阶段1拆解: stream 系外移 stream_relay.rs,mod.rs 3048→2607,loop_body 502→83 技能收尾: regenerate/edit 回溯补技能,新增 read_skill 工具,项目级技能扫描 DRY: emit 双写 46 处收敛 event_bus 正典,KV 引号归一收口,LOCK-SLOW 33 处单行化 警示骨架 4→1 与压缩兜底 2→1,噪音目录档位收敛,truncate 同名陷阱消除 工具元数据落 ToolMeta 八维,7 张 match 表改查 registry,parity 测试锁等价 命名: is_noise_entry/teardown_round/probe_missed_terminal_event 等 6 项纠偏
49 lines
2.7 KiB
Rust
49 lines
2.7 KiB
Rust
//! read_skill AI 工具声明式注册(按名读本机 Claude 技能规程正文,只读)。
|
|
//!
|
|
//! 工具职责:LLM 主动调 read_skill(name) → 查进程内技能缓存(含项目级,PROJECT_SKILLS_ROOT)
|
|
//! 命中后读 SKILL.md 剥 frontmatter 正文返回(含 source 来源标注);未命中返回明确错误文案
|
|
//! (LLM 可据此向用户澄清技能名,不静默空转)。
|
|
//!
|
|
//! 风险:Low(纯只读,读 ~/.claude 与项目 .claude 下 SKILL.md,无写副作用)。
|
|
//! handler 直调 skills.rs 的 `read_skill_content_stripped` / `skills_cached`(注入链同一
|
|
//! 真相源,与 / 技能 SkillResolver 读到相同内容)。
|
|
|
|
use std::sync::Arc;
|
|
|
|
use df_ai::ai_tools::{object_schema, AiToolRegistry, RiskLevel};
|
|
use df_ai::declare_tool;
|
|
|
|
/// 注册 read_skill 工具到 `$registry`(无外部捕获,读 skills.rs 进程内缓存 + 单文件)。
|
|
pub fn register(registry: &mut AiToolRegistry) {
|
|
// 无捕获:占位 Arc<()>(handler 只调 skills.rs 缓存/读文件,不持外部状态)。
|
|
let dummy: Arc<()> = Arc::new(());
|
|
// schema:name 必填(技能名,如 "review" / "swarm")。
|
|
let schema = object_schema(vec![("name", "string", true)]);
|
|
declare_tool!(
|
|
registry,
|
|
dummy: Arc<()>,
|
|
"read_skill",
|
|
"按名称读取本机已安装的 Claude 技能规程(SKILL.md)完整正文,只读查阅,不执行技能。参数 name 为技能名。命中返回 {name, source, content}(source 为来源 skill/project/command/plugin,content 已剥离 frontmatter);未命中返回明确错误。当用户 / 技能调用失败、用户提到某技能名、或你判断某技能与本任务相关时调用本工具读取其完整规程内容",
|
|
RiskLevel::Low,
|
|
schema: schema,
|
|
args => {
|
|
let name = args["name"].as_str().unwrap_or("").trim().to_string();
|
|
if name.is_empty() {
|
|
return Err(anyhow::anyhow!("参数 name 不能为空:请传入技能名,如 {{\"name\": \"review\"}}"));
|
|
}
|
|
let content = crate::commands::ai::skills::read_skill_content_stripped(name.clone())
|
|
.await
|
|
.ok_or_else(|| anyhow::anyhow!(
|
|
"技能 \"{name}\" 未找到(未安装/名称不匹配/user_invocable:false)。请核对技能名后重试,或请用户确认该技能是否已安装",
|
|
))?;
|
|
let source = crate::commands::ai::skills::skills_cached()
|
|
.await
|
|
.into_iter()
|
|
.find(|s| s.name == name)
|
|
.map(|s| s.source)
|
|
.unwrap_or_else(|| "skill".to_string());
|
|
Ok(serde_json::json!({ "name": name, "source": source, "content": content }))
|
|
}
|
|
);
|
|
}
|