refactor: consolidate nvim crates, extract server library, and update workspace dependencies
This commit is contained in:
1 parent
a083719cf1
commit
533adfd41b
53 files changed
+5967
-1230
No files matched your search
@@ -247,6 +247,154 @@ impl McpTool for ReplaceAstNodeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FindSymbolReferencesHandler;
|
||||
|
||||
#[async_trait]
|
||||
impl McpTool for FindSymbolReferencesHandler {
|
||||
fn name(&self) -> &'static str {
|
||||
"find_symbol_references"
|
||||
}
|
||||
|
||||
fn schema(&self) -> Value {
|
||||
crate::mcp::tool_def::<crate::tools::FindSymbolReferencesTool>(
|
||||
"find_symbol_references",
|
||||
"Find all source locations and AST chunks where a specific symbol is referenced or called.",
|
||||
)
|
||||
}
|
||||
|
||||
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
|
||||
let req: crate::tools::FindSymbolReferencesTool =
|
||||
serde_json::from_value(args).map_err(|e| e.to_string())?;
|
||||
let limit = req.limit.unwrap_or(10);
|
||||
let target_sym = req.symbol;
|
||||
|
||||
let matches = state.code.snippets.read_with(|snippets| {
|
||||
let mut refs = Vec::new();
|
||||
for snippet in snippets {
|
||||
if snippet.code.contains(&target_sym) || snippet.name.contains(&target_sym) {
|
||||
refs.push(snippet.clone());
|
||||
if refs.len() >= limit {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok::<String, crate::error::AppError>(serde_json::to_string(&refs)?)
|
||||
})?;
|
||||
|
||||
Ok(matches)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GetCallersHandler;
|
||||
|
||||
#[async_trait]
|
||||
impl McpTool for GetCallersHandler {
|
||||
fn name(&self) -> &'static str {
|
||||
"get_callers"
|
||||
}
|
||||
|
||||
fn schema(&self) -> Value {
|
||||
crate::mcp::tool_def::<crate::tools::GetCallersTool>(
|
||||
"get_callers",
|
||||
"Find all caller functions or methods that invoke a specified target function name.",
|
||||
)
|
||||
}
|
||||
|
||||
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
|
||||
let req: crate::tools::GetCallersTool =
|
||||
serde_json::from_value(args).map_err(|e| e.to_string())?;
|
||||
let limit = req.limit.unwrap_or(10);
|
||||
let target_fn = req.function_name;
|
||||
|
||||
let callers = state.code.snippets.read_with(|snippets| {
|
||||
let mut matching = Vec::new();
|
||||
for snippet in snippets {
|
||||
if snippet.code.contains(&format!("{}(", target_fn))
|
||||
|| snippet.code.contains(&format!("{}.await", target_fn))
|
||||
{
|
||||
matching.push(snippet.clone());
|
||||
if matching.len() >= limit {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok::<String, crate::error::AppError>(serde_json::to_string(&matching)?)
|
||||
})?;
|
||||
|
||||
Ok(callers)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnalyzeImpactHandler;
|
||||
|
||||
#[async_trait]
|
||||
impl McpTool for AnalyzeImpactHandler {
|
||||
fn name(&self) -> &'static str {
|
||||
"analyze_impact"
|
||||
}
|
||||
|
||||
fn schema(&self) -> Value {
|
||||
crate::mcp::tool_def::<crate::tools::AnalyzeImpactTool>(
|
||||
"analyze_impact",
|
||||
"Analyze the potential downstream breaking impact of modifying a function, struct, or file.",
|
||||
)
|
||||
}
|
||||
|
||||
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
|
||||
let req: crate::tools::AnalyzeImpactTool =
|
||||
serde_json::from_value(args).map_err(|e| e.to_string())?;
|
||||
let sym = req.target_symbol.clone();
|
||||
|
||||
let mut callers = Vec::new();
|
||||
state.code.snippets.read_with(|snippets| {
|
||||
for snippet in snippets {
|
||||
if snippet.code.contains(&sym) {
|
||||
callers.push(snippet.name.clone());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut kg_connected = Vec::new();
|
||||
state.read_graph(|g| {
|
||||
for rel in &g.relations {
|
||||
if rel.from == sym {
|
||||
kg_connected.push(format!("Outgoing: {} -> {}", rel.relation_type, rel.to));
|
||||
} else if rel.to == sym {
|
||||
kg_connected.push(format!("Incoming: {} <- {}", rel.relation_type, rel.from));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let caller_count = callers.len();
|
||||
let graph_count = kg_connected.len();
|
||||
let risk_level = if caller_count > 10 || graph_count > 5 {
|
||||
"CRITICAL"
|
||||
} else if caller_count > 3 || graph_count > 2 {
|
||||
"HIGH"
|
||||
} else if caller_count > 0 || graph_count > 0 {
|
||||
"MEDIUM"
|
||||
} else {
|
||||
"LOW"
|
||||
};
|
||||
|
||||
let result = serde_json::json!({
|
||||
"target_symbol": sym,
|
||||
"risk_level": risk_level,
|
||||
"ast_callers_count": caller_count,
|
||||
"ast_callers_sample": callers.into_iter().take(5).collect::<Vec<_>>(),
|
||||
"graph_relations_count": graph_count,
|
||||
"graph_relations": kg_connected,
|
||||
"recommendation": match risk_level {
|
||||
"CRITICAL" | "HIGH" => "Requires comprehensive unit test verification and backwards compatibility checks before modifying.",
|
||||
"MEDIUM" => "Verify direct call sites and run affected module tests.",
|
||||
_ => "Safe to modify with standard unit test verification.",
|
||||
}
|
||||
});
|
||||
|
||||
Ok(serde_json::to_string_pretty(&result)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in new issue
Block a user