1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
pub mod ctx_agent;
pub mod ctx_analyze;
pub mod ctx_architecture;
pub mod ctx_artifacts;
pub mod ctx_benchmark;
pub mod ctx_cache;
pub mod ctx_callgraph;
pub mod ctx_compile;
pub mod ctx_compress;
pub mod ctx_compress_memory;
pub mod ctx_context;
pub mod ctx_control;
pub mod ctx_cost;
pub mod ctx_dedup;
pub mod ctx_delta;
pub mod ctx_discover;
pub mod ctx_discover_tools;
pub mod ctx_edit;
pub mod ctx_execute;
pub mod ctx_expand;
pub mod ctx_feedback;
pub mod ctx_fill;
pub mod ctx_gain;
pub mod ctx_graph;
pub mod ctx_handoff;
pub mod ctx_heatmap;
pub mod ctx_impact;
pub mod ctx_index;
pub mod ctx_intent;
pub mod ctx_knowledge;
pub mod ctx_load_tools;
pub mod ctx_metrics;
pub mod ctx_multi_read;
pub mod ctx_outline;
pub mod ctx_overview;
pub mod ctx_pack;
pub mod ctx_plan;
pub mod ctx_prefetch;
pub mod ctx_preload;
pub mod ctx_proof;
pub mod ctx_provider;
pub mod ctx_radar;
pub mod ctx_read;
pub mod ctx_refactor;
pub mod ctx_response;
pub mod ctx_retrieve;
pub mod ctx_review;
pub mod ctx_routes;
pub mod ctx_search;
pub mod ctx_semantic_search;
pub mod ctx_session;
pub mod ctx_share;
pub mod ctx_shell;
pub mod ctx_smart_read;
pub mod ctx_smells;
pub mod ctx_symbol;
pub mod ctx_task;
pub mod ctx_tree;
pub mod ctx_verify;
pub mod ctx_workflow;
/// Resolve a relative path against session state (sync version).
/// Replicates the core logic of `LeanCtxServer::resolve_path` without
/// the re-rooting fallback (which needs `startup_project_root`).
/// Must be called within `tokio::task::block_in_place`.
pub(crate) fn resolve_path_sync(
session: &crate::core::session::SessionState,
raw: &str,
) -> Result<String, String> {
let normalized = crate::core::pathutil::normalize_tool_path(raw);
if normalized.is_empty() || normalized == "." {
return Ok(normalized);
}
let p = std::path::Path::new(&normalized);
let jail_root = session
.project_root
.as_deref()
.or(session.shell_cwd.as_deref())
.unwrap_or(".")
.to_string();
let resolved = if p.is_absolute() || p.exists() {
std::path::PathBuf::from(&normalized)
} else if let Some(ref root) = session.project_root {
let joined = std::path::Path::new(root).join(&normalized);
if joined.exists() {
joined
} else if let Some(ref cwd) = session.shell_cwd {
std::path::Path::new(cwd).join(&normalized)
} else {
std::path::Path::new(&jail_root).join(&normalized)
}
} else if let Some(ref cwd) = session.shell_cwd {
std::path::Path::new(cwd).join(&normalized)
} else {
std::path::Path::new(&jail_root).join(&normalized)
};
let jail_root_path = std::path::Path::new(&jail_root);
let jailed = crate::core::pathjail::jail_path(&resolved, jail_root_path)?;
crate::core::io_boundary::check_secret_path_for_tool("resolve_path", &jailed)?;
Ok(crate::core::pathutil::normalize_tool_path(
&jailed.to_string_lossy().replace('\\', "/"),
))
}