新增: AST代码智能Phase2(read_symbol drill下钻+调用点带行+prompt优先引导)
code_intel: read_symbol 加 drill(目标节点子树递归找嵌套子定义,命中走full)/calls 每项带 line号(与start_row同口径,Vue切段偏移自动对齐)/2 新测试(drill命中+drill_not_found兜底) prompt: 中英能力列表加 read_symbol 优先引导(治 read_file 全文回灌 prompt 爆)+ 修支持集标错bug(.rs/.ts等原标不支持实为支持集) tool_registry: read_symbol schema 加 drill 参数 + read_file desc 反向引导 密度: code_intel.rs 全文 25733B vs 骨架 1348B → 降 19.1x;16 单测全绿
This commit is contained in:
@@ -106,6 +106,9 @@ fn extract_vue_script(source: &str) -> Option<(String, usize)> {
|
|||||||
/// - `symbol`:目标符号名(精确匹配定义节点的 name field)
|
/// - `symbol`:目标符号名(精确匹配定义节点的 name field)
|
||||||
/// - `full`:true=完整定义体,false=骨架
|
/// - `full`:true=完整定义体,false=骨架
|
||||||
/// - `kind_hint`:可选 kind 过滤(如 "function"/"struct"),None 匹配任意定义 kind
|
/// - `kind_hint`:可选 kind 过滤(如 "function"/"struct"),None 匹配任意定义 kind
|
||||||
|
/// - `drill`:可选子定义名(如 "parse");Some(name)=在目标符号节点子树内递归找名为 name 的
|
||||||
|
/// 嵌套定义(局部函数/impl 方法),命中即返该子定义的 full 态;未命中走兜底(reason=drill_not_found)。
|
||||||
|
/// None=不下钻,走原 full/kind_hint 三态逻辑。
|
||||||
pub fn read_symbol(
|
pub fn read_symbol(
|
||||||
source: &str,
|
source: &str,
|
||||||
file_hash: &str,
|
file_hash: &str,
|
||||||
@@ -113,6 +116,7 @@ pub fn read_symbol(
|
|||||||
symbol: &str,
|
symbol: &str,
|
||||||
full: bool,
|
full: bool,
|
||||||
kind_hint: Option<&str>,
|
kind_hint: Option<&str>,
|
||||||
|
drill: Option<&str>,
|
||||||
) -> serde_json::Value {
|
) -> serde_json::Value {
|
||||||
let ext = path.rsplit('.').next().unwrap_or("").to_lowercase();
|
let ext = path.rsplit('.').next().unwrap_or("").to_lowercase();
|
||||||
let is_vue = ext == "vue";
|
let is_vue = ext == "vue";
|
||||||
@@ -154,6 +158,21 @@ pub fn read_symbol(
|
|||||||
let mut found: Option<Node> = None;
|
let mut found: Option<Node> = None;
|
||||||
find_definition(&root, symbol, kind_hint, parse_source.as_bytes(), &mut found);
|
find_definition(&root, symbol, kind_hint, parse_source.as_bytes(), &mut found);
|
||||||
|
|
||||||
|
// drill:在目标符号节点子树内递归找名为 drill 的嵌套定义(局部函数/impl 方法)。
|
||||||
|
// 命中即返该子定义 full 态;未命中走兜底(reason=drill_not_found,不 panic)。
|
||||||
|
if let Some(drill_name) = drill {
|
||||||
|
if let Some(sym_node) = found {
|
||||||
|
let mut drill_found: Option<Node> = None;
|
||||||
|
find_definition(&sym_node, drill_name, None, parse_source.as_bytes(), &mut drill_found);
|
||||||
|
return match drill_found {
|
||||||
|
Some(dn) => extract_symbol(dn, parse_source.as_bytes(), source, path, file_hash, true, line_offset),
|
||||||
|
None => fallback(drill_name, path, file_hash, "drill_not_found",
|
||||||
|
&format!("符号「{}」内未找到下钻子定义「{}」(可能非嵌套定义/作用域差异)", symbol, drill_name)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// found 本身为 None 时 drill 无意义,落入下方 not_found 兜底
|
||||||
|
}
|
||||||
|
|
||||||
match found {
|
match found {
|
||||||
Some(node) => extract_symbol(node, parse_source.as_bytes(), source, path, file_hash, full, line_offset),
|
Some(node) => extract_symbol(node, parse_source.as_bytes(), source, path, file_hash, full, line_offset),
|
||||||
None => fallback(symbol, path, file_hash, "not_found",
|
None => fallback(symbol, path, file_hash, "not_found",
|
||||||
@@ -237,7 +256,7 @@ fn extract_symbol(
|
|||||||
|
|
||||||
let node_src = node_text(node, source);
|
let node_src = node_text(node, source);
|
||||||
let signature = extract_signature(&node_src);
|
let signature = extract_signature(&node_src);
|
||||||
let calls = collect_calls(node, source);
|
let calls = collect_calls(node, source, line_offset);
|
||||||
|
|
||||||
if full {
|
if full {
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
@@ -265,7 +284,7 @@ fn extract_symbol(
|
|||||||
"signature": signature,
|
"signature": signature,
|
||||||
"calls": calls,
|
"calls": calls,
|
||||||
"mode": "skeleton",
|
"mode": "skeleton",
|
||||||
"hint": "骨架态:传 full=true 取完整定义体(语法边界精准);下钻局部分支 drill 待 Phase2",
|
"hint": "骨架态:传 full=true 取完整定义体(语法边界精准);传 drill=\"子定义名\" 下钻符号内嵌套定义(局部函数/impl 方法)",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,25 +308,34 @@ fn extract_signature(node_src: &str) -> String {
|
|||||||
///
|
///
|
||||||
/// Phase1:扁平收集去重(Phase2 按分支结构组织调用图)。
|
/// Phase1:扁平收集去重(Phase2 按分支结构组织调用图)。
|
||||||
/// Rust/TS/JS 的 call_expression 都有 "function" field(callee),统一提取。
|
/// Rust/TS/JS 的 call_expression 都有 "function" field(callee),统一提取。
|
||||||
fn collect_calls(node: Node, source: &[u8]) -> Vec<String> {
|
/// Phase2:每项带行号(call_expression 所在行,1-based + Vue 偏移,与 start_row 同口径)。
|
||||||
|
fn collect_calls(node: Node, source: &[u8], line_offset: usize) -> Vec<serde_json::Value> {
|
||||||
let mut calls = Vec::new();
|
let mut calls = Vec::new();
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
collect_calls_inner(node, source, &mut calls, &mut seen);
|
collect_calls_inner(node, source, line_offset, &mut calls, &mut seen);
|
||||||
calls
|
calls
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_calls_inner(node: Node, source: &[u8], out: &mut Vec<String>, seen: &mut HashSet<String>) {
|
fn collect_calls_inner(
|
||||||
|
node: Node,
|
||||||
|
source: &[u8],
|
||||||
|
line_offset: usize,
|
||||||
|
out: &mut Vec<serde_json::Value>,
|
||||||
|
seen: &mut HashSet<String>,
|
||||||
|
) {
|
||||||
let mut cursor = node.walk();
|
let mut cursor = node.walk();
|
||||||
for child in node.children(&mut cursor) {
|
for child in node.children(&mut cursor) {
|
||||||
if child.kind() == "call_expression" {
|
if child.kind() == "call_expression" {
|
||||||
if let Some(func) = child.child_by_field_name("function") {
|
if let Some(func) = child.child_by_field_name("function") {
|
||||||
let name = node_text(func, source).to_string();
|
let name = node_text(func, source).to_string();
|
||||||
if !name.is_empty() && seen.insert(name.clone()) {
|
if !name.is_empty() && seen.insert(name.clone()) {
|
||||||
out.push(name);
|
// 行号:调用语句所在行(call_expression 节点本身的 start_position),1-based + Vue 偏移
|
||||||
|
let line = child.start_position().row + 1 + line_offset;
|
||||||
|
out.push(serde_json::json!({ "name": name, "line": line }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
collect_calls_inner(child, source, out, seen);
|
collect_calls_inner(child, source, line_offset, out, seen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +403,7 @@ struct Config {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_symbol_rust_skeleton() {
|
fn test_read_symbol_rust_skeleton() {
|
||||||
let r = read_symbol(RUST_FIXTURE, "hash1", "test.rs", "read_symbol", false, None);
|
let r = read_symbol(RUST_FIXTURE, "hash1", "test.rs", "read_symbol", false, None, None);
|
||||||
assert_eq!(r["fallback"], serde_json::Value::Null);
|
assert_eq!(r["fallback"], serde_json::Value::Null);
|
||||||
assert_eq!(r["symbol"], "read_symbol");
|
assert_eq!(r["symbol"], "read_symbol");
|
||||||
assert_eq!(r["kind"], "function_item");
|
assert_eq!(r["kind"], "function_item");
|
||||||
@@ -384,15 +412,16 @@ struct Config {
|
|||||||
assert!(r["content"].is_null());
|
assert!(r["content"].is_null());
|
||||||
assert!(r["signature"].as_str().unwrap().contains("read_symbol"));
|
assert!(r["signature"].as_str().unwrap().contains("read_symbol"));
|
||||||
// calls 含同文件静态调用(parse/validate)+ load(跨文件也命中名,Phase1 扁平)
|
// calls 含同文件静态调用(parse/validate)+ load(跨文件也命中名,Phase1 扁平)
|
||||||
|
// Phase2:每项是 {name, line} 对象
|
||||||
let calls = r["calls"].as_array().unwrap();
|
let calls = r["calls"].as_array().unwrap();
|
||||||
assert!(calls.iter().any(|c| c == "parse"));
|
assert!(calls.iter().any(|c| c["name"] == "parse"));
|
||||||
assert!(calls.iter().any(|c| c == "validate"));
|
assert!(calls.iter().any(|c| c["name"] == "validate"));
|
||||||
assert_eq!(r["body_lines"].as_u64().unwrap(), 6);
|
assert_eq!(r["body_lines"].as_u64().unwrap(), 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_symbol_rust_full() {
|
fn test_read_symbol_rust_full() {
|
||||||
let r = read_symbol(RUST_FIXTURE, "hash1", "test.rs", "read_symbol", true, None);
|
let r = read_symbol(RUST_FIXTURE, "hash1", "test.rs", "read_symbol", true, None, None);
|
||||||
assert_eq!(r["mode"], "full");
|
assert_eq!(r["mode"], "full");
|
||||||
let content = r["content"].as_str().unwrap();
|
let content = r["content"].as_str().unwrap();
|
||||||
// full 态返完整定义体(含函数体 {})
|
// full 态返完整定义体(含函数体 {})
|
||||||
@@ -403,7 +432,7 @@ struct Config {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_symbol_struct() {
|
fn test_read_symbol_struct() {
|
||||||
let r = read_symbol(RUST_FIXTURE, "h", "test.rs", "Config", false, None);
|
let r = read_symbol(RUST_FIXTURE, "h", "test.rs", "Config", false, None, None);
|
||||||
assert_eq!(r["symbol"], "Config");
|
assert_eq!(r["symbol"], "Config");
|
||||||
assert_eq!(r["kind"], "struct_item");
|
assert_eq!(r["kind"], "struct_item");
|
||||||
}
|
}
|
||||||
@@ -411,14 +440,14 @@ struct Config {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_read_symbol_kind_hint_filter() {
|
fn test_read_symbol_kind_hint_filter() {
|
||||||
// kind_hint="struct" 时,"read_symbol"(function)不应被误匹配为 struct
|
// kind_hint="struct" 时,"read_symbol"(function)不应被误匹配为 struct
|
||||||
let r = read_symbol(RUST_FIXTURE, "h", "test.rs", "Config", false, Some("struct"));
|
let r = read_symbol(RUST_FIXTURE, "h", "test.rs", "Config", false, Some("struct"), None);
|
||||||
assert_eq!(r["symbol"], "Config");
|
assert_eq!(r["symbol"], "Config");
|
||||||
assert_eq!(r["kind"], "struct_item");
|
assert_eq!(r["kind"], "struct_item");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_symbol_not_found_fallback() {
|
fn test_read_symbol_not_found_fallback() {
|
||||||
let r = read_symbol(RUST_FIXTURE, "h", "test.rs", "nonexistent_fn", false, None);
|
let r = read_symbol(RUST_FIXTURE, "h", "test.rs", "nonexistent_fn", false, None, None);
|
||||||
assert_eq!(r["fallback"], true);
|
assert_eq!(r["fallback"], true);
|
||||||
assert_eq!(r["reason"], "not_found");
|
assert_eq!(r["reason"], "not_found");
|
||||||
assert!(r["suggestion"].as_str().unwrap().contains("grep"));
|
assert!(r["suggestion"].as_str().unwrap().contains("grep"));
|
||||||
@@ -427,7 +456,7 @@ struct Config {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_read_symbol_no_grammar_fallback() {
|
fn test_read_symbol_no_grammar_fallback() {
|
||||||
let src = "def hello():\n print('hi')\n";
|
let src = "def hello():\n print('hi')\n";
|
||||||
let r = read_symbol(src, "h", "test.py", "hello", false, None);
|
let r = read_symbol(src, "h", "test.py", "hello", false, None, None);
|
||||||
assert_eq!(r["fallback"], true);
|
assert_eq!(r["fallback"], true);
|
||||||
assert_eq!(r["reason"], "no_grammar");
|
assert_eq!(r["reason"], "no_grammar");
|
||||||
assert!(r["suggestion"].as_str().unwrap().contains("不支持"));
|
assert!(r["suggestion"].as_str().unwrap().contains("不支持"));
|
||||||
@@ -448,7 +477,7 @@ function helper() {}
|
|||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
"#;
|
"#;
|
||||||
let r = read_symbol(vue, "h", "App.vue", "greet", false, None);
|
let r = read_symbol(vue, "h", "App.vue", "greet", false, None, None);
|
||||||
assert_eq!(r["fallback"], serde_json::Value::Null);
|
assert_eq!(r["fallback"], serde_json::Value::Null);
|
||||||
assert_eq!(r["symbol"], "greet");
|
assert_eq!(r["symbol"], "greet");
|
||||||
assert_eq!(r["kind"], "function_declaration");
|
assert_eq!(r["kind"], "function_declaration");
|
||||||
@@ -456,17 +485,53 @@ function helper() {}
|
|||||||
let start = r["line_start"].as_u64().unwrap();
|
let start = r["line_start"].as_u64().unwrap();
|
||||||
assert!(start >= 4, "Vue 行号应映射回原文件(script 段偏移后),实际 {}", start);
|
assert!(start >= 4, "Vue 行号应映射回原文件(script 段偏移后),实际 {}", start);
|
||||||
let calls = r["calls"].as_array().unwrap();
|
let calls = r["calls"].as_array().unwrap();
|
||||||
assert!(calls.iter().any(|c| c == "helper"));
|
// Phase2:calls 每项是 {name, line} 对象
|
||||||
|
assert!(calls.iter().any(|c| c["name"] == "helper"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_symbol_vue_no_script_fallback() {
|
fn test_read_symbol_vue_no_script_fallback() {
|
||||||
let vue = "<template><div>hi</div></template>\n<style></style>";
|
let vue = "<template><div>hi</div></template>\n<style></style>";
|
||||||
let r = read_symbol(vue, "h", "App.vue", "greet", false, None);
|
let r = read_symbol(vue, "h", "App.vue", "greet", false, None, None);
|
||||||
assert_eq!(r["fallback"], true);
|
assert_eq!(r["fallback"], true);
|
||||||
assert_eq!(r["reason"], "vue_no_script");
|
assert_eq!(r["reason"], "vue_no_script");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Phase2 drill:下钻目标符号(read_symbol)内的嵌套子定义(parse)。
|
||||||
|
/// RUST_FIXTURE 中 read_symbol 函数体内调用了 parse,但 parse 是顶层函数(非 read_symbol 子树内),
|
||||||
|
/// 故用带嵌套局部函数的 fixture 验证:outer 内定义 inner,drill="inner" 应命中并返 full 态。
|
||||||
|
#[test]
|
||||||
|
fn test_read_symbol_drill_hits_nested() {
|
||||||
|
let src = r#"
|
||||||
|
fn outer() {
|
||||||
|
fn inner(x: i32) -> i32 {
|
||||||
|
x + 1
|
||||||
|
}
|
||||||
|
inner(42)
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
// 外层骨架态,确认 outer 能解析到
|
||||||
|
let skeleton = read_symbol(src, "h", "t.rs", "outer", false, None, None);
|
||||||
|
assert_eq!(skeleton["symbol"], "outer");
|
||||||
|
// 下钻 inner:应在 outer 子树内命中,返 full 态
|
||||||
|
let drill = read_symbol(src, "h", "t.rs", "outer", false, None, Some("inner"));
|
||||||
|
assert_eq!(drill["fallback"], serde_json::Value::Null, "drill 命中应返符号非兜底");
|
||||||
|
assert_eq!(drill["symbol"], "inner");
|
||||||
|
assert_eq!(drill["mode"], "full", "drill 命中走 full 态(完整定义体)");
|
||||||
|
let content = drill["content"].as_str().unwrap();
|
||||||
|
assert!(content.contains("fn inner"), "drill content 应含 inner 定义体");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase2 drill 未命中兜底:下钻外层符号内不存在的子定义名 → fallback reason=drill_not_found。
|
||||||
|
#[test]
|
||||||
|
fn test_read_symbol_drill_not_found_fallback() {
|
||||||
|
let src = "fn outer() {\n fn inner() {}\n}\n";
|
||||||
|
let drill = read_symbol(src, "h", "t.rs", "outer", false, None, Some("nonexistent"));
|
||||||
|
assert_eq!(drill["fallback"], true);
|
||||||
|
assert_eq!(drill["reason"], "drill_not_found");
|
||||||
|
assert!(drill["suggestion"].as_str().unwrap().contains("nonexistent"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_extract_vue_script() {
|
fn test_extract_vue_script() {
|
||||||
let vue = "a\nb\n<script>let x = 1;</script>";
|
let vue = "a\nb\n<script>let x = 1;</script>";
|
||||||
@@ -495,11 +560,13 @@ function helper() {}
|
|||||||
let mut parser = Parser::new();
|
let mut parser = Parser::new();
|
||||||
parser.set_language(&Language::from(tree_sitter_rust::LANGUAGE)).unwrap();
|
parser.set_language(&Language::from(tree_sitter_rust::LANGUAGE)).unwrap();
|
||||||
let tree = parser.parse(src, None).unwrap();
|
let tree = parser.parse(src, None).unwrap();
|
||||||
let calls = collect_calls(tree.root_node(), src.as_bytes());
|
let calls = collect_calls(tree.root_node(), src.as_bytes(), 0);
|
||||||
// b 去重只出现一次
|
// b 去重只出现一次(Phase2:每项是 {name, line} 对象)
|
||||||
assert!(calls.iter().any(|c| c == "b"));
|
assert!(calls.iter().any(|c| c["name"] == "b"));
|
||||||
assert_eq!(calls.iter().filter(|c| *c == "b").count(), 1);
|
assert_eq!(calls.iter().filter(|c| c["name"] == "b").count(), 1);
|
||||||
assert!(calls.iter().any(|c| c == "c"));
|
assert!(calls.iter().any(|c| c["name"] == "c"));
|
||||||
|
// line 应为调用所在行(src 单行,a 在第 1 行)
|
||||||
|
assert_eq!(calls[0]["line"].as_u64().unwrap(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 多角度验证(用户要求:实现后看效果 + 多角度测试):
|
/// 多角度验证(用户要求:实现后看效果 + 多角度测试):
|
||||||
@@ -514,7 +581,7 @@ function helper() {}
|
|||||||
let full_bytes = source.len();
|
let full_bytes = source.len();
|
||||||
|
|
||||||
// 骨架态:解析自身 read_symbol 函数(只给该符号,非整个文件)
|
// 骨架态:解析自身 read_symbol 函数(只给该符号,非整个文件)
|
||||||
let skeleton = read_symbol(&source, "h0", "code_intel.rs", "read_symbol", false, None);
|
let skeleton = read_symbol(&source, "h0", "code_intel.rs", "read_symbol", false, None, None);
|
||||||
assert_ne!(skeleton["fallback"], true, "真实文件应解析出 read_symbol 符号");
|
assert_ne!(skeleton["fallback"], true, "真实文件应解析出 read_symbol 符号");
|
||||||
assert_eq!(skeleton["symbol"], "read_symbol");
|
assert_eq!(skeleton["symbol"], "read_symbol");
|
||||||
assert_eq!(skeleton["kind"], "function_item");
|
assert_eq!(skeleton["kind"], "function_item");
|
||||||
@@ -531,7 +598,7 @@ function helper() {}
|
|||||||
assert!(ratio > 5.0, "骨架密度应显著高于全文(降 >5x),实际 {:.1}x", ratio);
|
assert!(ratio > 5.0, "骨架密度应显著高于全文(降 >5x),实际 {:.1}x", ratio);
|
||||||
|
|
||||||
// full 态:完整定义体(语法边界 100% 准)
|
// full 态:完整定义体(语法边界 100% 准)
|
||||||
let full_sym = read_symbol(&source, "h1", "code_intel.rs", "read_symbol", true, None);
|
let full_sym = read_symbol(&source, "h1", "code_intel.rs", "read_symbol", true, None, None);
|
||||||
let content = full_sym["content"].as_str().expect("full 态应返 content");
|
let content = full_sym["content"].as_str().expect("full 态应返 content");
|
||||||
assert!(content.contains("pub fn read_symbol"));
|
assert!(content.contains("pub fn read_symbol"));
|
||||||
// calls 应含 read_symbol 内部调用的函数(grammar_for/extract_symbol/fallback 等)
|
// calls 应含 read_symbol 内部调用的函数(grammar_for/extract_symbol/fallback 等)
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ fn system_prompt_parts(lang: &str) -> (&'static str, &'static str, &'static str)
|
|||||||
- Create/query projects, tasks, and ideas\n\
|
- Create/query projects, tasks, and ideas\n\
|
||||||
- Run workflows\n\
|
- Run workflows\n\
|
||||||
- Read file contents, list directories, create/write files\n\
|
- Read file contents, list directories, create/write files\n\
|
||||||
|
- To understand/locate a code symbol (function/struct/class) prefer read_symbol(path, symbol): returns a skeleton (signature + line range + internal calls, minimal and high-density), avoiding read_file dumping the whole file into the prompt; pass full=true for the full definition body; read_symbol supports .rs/.ts/.tsx/.js/.jsx/.vue — fall back to read_file only for other languages or when the symbol is not found\n\
|
||||||
- Get file metadata (size, line count, binary detection)\n\
|
- Get file metadata (size, line count, binary detection)\n\
|
||||||
- Append content to files, search files by name pattern\n\n\
|
- Append content to files, search files by name pattern\n\n\
|
||||||
## Guidelines\n\
|
## Guidelines\n\
|
||||||
@@ -92,6 +93,7 @@ fn system_prompt_parts(lang: &str) -> (&'static str, &'static str, &'static str)
|
|||||||
- 创建/查询项目、任务、灵感\n\
|
- 创建/查询项目、任务、灵感\n\
|
||||||
- 运行工作流\n\
|
- 运行工作流\n\
|
||||||
- 读取文件内容、列出目录、创建/写入文件\n\
|
- 读取文件内容、列出目录、创建/写入文件\n\
|
||||||
|
- 理解/定位代码符号(函数/结构体/类等)优先用 read_symbol(path, symbol):返回骨架(签名+行范围+内部调用,极小高密度),避免 read_file 全文回灌撑爆 prompt;需完整定义体传 full=true;read_symbol 支持 .rs/.ts/.tsx/.js/.jsx/.vue,其他语言或未找到符号时再退回 read_file\n\
|
||||||
- 获取文件元信息(大小、行数、二进制检测)\n\
|
- 获取文件元信息(大小、行数、二进制检测)\n\
|
||||||
- 追加写入文件、按名称模式搜索文件\n\n\
|
- 追加写入文件、按名称模式搜索文件\n\n\
|
||||||
## 行为准则\n\
|
## 行为准则\n\
|
||||||
|
|||||||
@@ -948,7 +948,7 @@ fn register_file_tools(
|
|||||||
|
|
||||||
// ── 文件系统 ──
|
// ── 文件系统 ──
|
||||||
registry.register(
|
registry.register(
|
||||||
"read_file", "读取文件内容,返回文本内容。支持 offset/limit 分页;传入 search 则在文件内容中搜索匹配行(大小写敏感,字符串包含匹配),返回 matches 数组(限50条)",
|
"read_file", "读取文件内容,返回文本内容。支持 offset/limit 分页;传入 search 则在文件内容中搜索匹配行(大小写敏感,字符串包含匹配),返回 matches 数组(限50条)。仅需函数/类型符号的签名或定义体时优先用 read_symbol(骨架/全文,避免 prompt 爆)",
|
||||||
df_ai::ai_tools::object_schema(vec![("path", "string", true), ("offset", "integer", false), ("limit", "integer", false), ("search", "string", false)]),
|
df_ai::ai_tools::object_schema(vec![("path", "string", true), ("offset", "integer", false), ("limit", "integer", false), ("search", "string", false)]),
|
||||||
RiskLevel::Low,
|
RiskLevel::Low,
|
||||||
{ let allowed_dirs = allowed_dirs.clone(); Box::new(move |args: serde_json::Value| {
|
{ let allowed_dirs = allowed_dirs.clone(); Box::new(move |args: serde_json::Value| {
|
||||||
@@ -1044,7 +1044,7 @@ fn register_file_tools(
|
|||||||
);
|
);
|
||||||
registry.register(
|
registry.register(
|
||||||
"read_symbol", "AST 符号解析(信息密度驱动,治 read_file 全文回灌 prompt 爆)。提取函数/结构体/类等符号:默认返回骨架(签名+行范围+内部调用,极小高密度);传 full=true 取完整定义体。不支持/未找到时回退提示用 grep+read_file。Phase1 支持 .rs/.ts/.tsx/.js/.jsx/.vue。",
|
"read_symbol", "AST 符号解析(信息密度驱动,治 read_file 全文回灌 prompt 爆)。提取函数/结构体/类等符号:默认返回骨架(签名+行范围+内部调用,极小高密度);传 full=true 取完整定义体。不支持/未找到时回退提示用 grep+read_file。Phase1 支持 .rs/.ts/.tsx/.js/.jsx/.vue。",
|
||||||
df_ai::ai_tools::object_schema(vec![("path", "string", true), ("symbol", "string", true), ("full", "boolean", false), ("kind", "string", false)]),
|
df_ai::ai_tools::object_schema(vec![("path", "string", true), ("symbol", "string", true), ("full", "boolean", false), ("kind", "string", false), ("drill", "string", false)]),
|
||||||
RiskLevel::Low,
|
RiskLevel::Low,
|
||||||
{ let allowed_dirs = allowed_dirs.clone(); Box::new(move |args: serde_json::Value| {
|
{ let allowed_dirs = allowed_dirs.clone(); Box::new(move |args: serde_json::Value| {
|
||||||
let allowed_dirs = allowed_dirs.clone();
|
let allowed_dirs = allowed_dirs.clone();
|
||||||
@@ -1058,6 +1058,7 @@ fn register_file_tools(
|
|||||||
let symbol = args["symbol"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 symbol 参数"))?;
|
let symbol = args["symbol"].as_str().ok_or_else(|| anyhow::anyhow!("缺少 symbol 参数"))?;
|
||||||
let full = args["full"].as_bool().unwrap_or(false);
|
let full = args["full"].as_bool().unwrap_or(false);
|
||||||
let kind_hint = args["kind"].as_str();
|
let kind_hint = args["kind"].as_str();
|
||||||
|
let drill = args["drill"].as_str().filter(|s| !s.is_empty());
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
let mut file = File::open(path).await
|
let mut file = File::open(path).await
|
||||||
@@ -1081,7 +1082,7 @@ fn register_file_tools(
|
|||||||
}
|
}
|
||||||
// 调 code_intel 纯函数(三态 + 兜底,不 panic)
|
// 调 code_intel 纯函数(三态 + 兜底,不 panic)
|
||||||
Ok(crate::commands::ai::code_intel::read_symbol(
|
Ok(crate::commands::ai::code_intel::read_symbol(
|
||||||
&content, &file_hash, path, symbol, full, kind_hint,
|
&content, &file_hash, path, symbol, full, kind_hint, drill,
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
})},
|
})},
|
||||||
|
|||||||
Reference in New Issue
Block a user