fix: YAML frontmatter 解析 + call_capability iPet 兼容桥接
- SKILL.md 解析器支持跳过 YAML frontmatter (---...---)
修复导入的 Claude Code / Agent Skills 的元数据头导致
description 混入元数据的问题
- call_capability 桥接工具:兼容 iPet 遗留的
{name, prompt} 调用格式
按 name 查找 SkillRegistry,委托执行
- 清理 parser 中未使用的 in_desc 变量
This commit is contained in:
+32
@@ -275,6 +275,23 @@ async fn cmd_listen(
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// call_capability 桥接:iPet 兼容,按 skill 名调用
|
||||||
|
tools_list.push(serde_json::json!({
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "call_capability",
|
||||||
|
"description": "按名称调用一个 skill(等同于直接使用该 skill 的工具名)。name 参数为 skill 的名称,prompt 为自然语言需求",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string", "description": "要调用的 skill 名称" },
|
||||||
|
"prompt": { "type": "string", "description": "给该 skill 的自然语言需求" }
|
||||||
|
},
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
cfg.tools = Some(tools_list);
|
cfg.tools = Some(tools_list);
|
||||||
|
|
||||||
// 创建 Conversation
|
// 创建 Conversation
|
||||||
@@ -331,6 +348,7 @@ async fn cmd_listen(
|
|||||||
let args = args_json.to_string();
|
let args = args_json.to_string();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
|
let user_id = current_user.lock().await.clone();
|
||||||
match n.as_str() {
|
match n.as_str() {
|
||||||
"read_memories" => return Ok(memory_store.read().await),
|
"read_memories" => return Ok(memory_store.read().await),
|
||||||
"write_memory" => {
|
"write_memory" => {
|
||||||
@@ -343,6 +361,20 @@ async fn cmd_listen(
|
|||||||
return Ok(memory_store.write(content).await);
|
return Ok(memory_store.write(content).await);
|
||||||
}
|
}
|
||||||
"read_summaries" => return Ok(context::tools::read_summaries(&session).await),
|
"read_summaries" => return Ok(context::tools::read_summaries(&session).await),
|
||||||
|
"call_capability" => {
|
||||||
|
let cap_params: serde_json::Value = serde_json::from_str(&args).unwrap_or_default();
|
||||||
|
let cap_name = cap_params.get("name").and_then(|v| v.as_str()).map(|s| s.to_string());
|
||||||
|
if let Some(name) = cap_name {
|
||||||
|
if let Some(ref reg) = reg {
|
||||||
|
let mut ctx = ExecutionContext::new(&user_id);
|
||||||
|
if let Some(a) = approval { ctx = ctx.with_approval(a); }
|
||||||
|
ctx = ctx.with_wechat(send);
|
||||||
|
let result = reg.execute(&name, cap_params, &ctx).await;
|
||||||
|
return Ok(result.output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok("请提供 name 参数(要调用的 skill 名称)".to_string());
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
let user_id = current_user.lock().await.clone();
|
let user_id = current_user.lock().await.clone();
|
||||||
|
|||||||
+11
-6
@@ -38,11 +38,20 @@ pub fn parse_skill(skill_dir: &Path) -> Result<Skill, String> {
|
|||||||
let mut params_fence = 0;
|
let mut params_fence = 0;
|
||||||
let mut execute_fence = 0;
|
let mut execute_fence = 0;
|
||||||
let mut desc_lines: Vec<&str> = Vec::new();
|
let mut desc_lines: Vec<&str> = Vec::new();
|
||||||
let mut in_desc = false;
|
let mut frontmatter_fences = 0;
|
||||||
|
|
||||||
for (i, line) in lines.iter().enumerate() {
|
for (_i, line) in lines.iter().enumerate() {
|
||||||
let trimmed = line.trim();
|
let trimmed = line.trim();
|
||||||
|
|
||||||
|
// 跳过 YAML frontmatter (---...---)
|
||||||
|
if trimmed == "---" && frontmatter_fences < 2 {
|
||||||
|
frontmatter_fences += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if frontmatter_fences == 1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 标题行 → 技能名
|
// 标题行 → 技能名
|
||||||
if trimmed.starts_with("# ") && !trimmed.starts_with("## ") && name.is_empty() {
|
if trimmed.starts_with("# ") && !trimmed.starts_with("## ") && name.is_empty() {
|
||||||
name = trimmed.trim_start_matches("# ").trim().to_string();
|
name = trimmed.trim_start_matches("# ").trim().to_string();
|
||||||
@@ -54,24 +63,20 @@ pub fn parse_skill(skill_dir: &Path) -> Result<Skill, String> {
|
|||||||
let section = trimmed.trim_start_matches("## ").trim().to_lowercase();
|
let section = trimmed.trim_start_matches("## ").trim().to_lowercase();
|
||||||
match section.as_str() {
|
match section.as_str() {
|
||||||
"risk level" | "risk_level" | "risk" => {
|
"risk level" | "risk_level" | "risk" => {
|
||||||
in_desc = false;
|
|
||||||
in_params = false;
|
in_params = false;
|
||||||
in_execute = false;
|
in_execute = false;
|
||||||
}
|
}
|
||||||
"parameters" | "param" => {
|
"parameters" | "param" => {
|
||||||
in_desc = false;
|
|
||||||
in_params = true;
|
in_params = true;
|
||||||
in_execute = false;
|
in_execute = false;
|
||||||
}
|
}
|
||||||
"execute" | "execution" | "command" | "run" => {
|
"execute" | "execution" | "command" | "run" => {
|
||||||
in_desc = false;
|
|
||||||
in_params = false;
|
in_params = false;
|
||||||
in_execute = true;
|
in_execute = true;
|
||||||
params_fence = 0;
|
params_fence = 0;
|
||||||
execute_fence = 0;
|
execute_fence = 0;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
in_desc = false;
|
|
||||||
in_params = false;
|
in_params = false;
|
||||||
in_execute = false;
|
in_execute = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user