From 8a4da141d6a4fa9eb84d0dadcc9c912a9357ff44 Mon Sep 17 00:00:00 2001 From: Edvard Date: Wed, 18 Feb 2026 22:50:35 -0500 Subject: [PATCH] fix(skills): inject skill prompts and tools into agent system prompt Skill prompts and tool definitions from SKILL.toml were parsed and stored correctly but never included in the agent's system prompt. Both prompt-building paths (channels/mod.rs and agent/prompt.rs) only emitted skill metadata (name, description, location), telling the LLM to "read" the SKILL.toml on demand. This caused the agent to attempt manual file reads that often failed, leaving skills effectively ignored. Now both paths inline and blocks inside each XML element, so the agent receives full skill context without extra tool calls. Closes #877 Co-Authored-By: Claude Opus 4.6 --- src/agent/prompt.rs | 30 +++++++++++++++++++++---- src/channels/mod.rs | 54 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/agent/prompt.rs b/src/agent/prompt.rs index 940d077..268af67 100644 --- a/src/agent/prompt.rs +++ b/src/agent/prompt.rs @@ -165,13 +165,35 @@ impl PromptSection for SkillsSection { .join(&skill.name) .join("SKILL.md") }); + let _ = writeln!(prompt, " "); + let _ = writeln!(prompt, " {}", skill.name); let _ = writeln!( prompt, - " \n {}\n {}\n {}\n ", - skill.name, - skill.description, - location.display() + " {}", + skill.description ); + let _ = writeln!(prompt, " {}", location.display()); + if !skill.tools.is_empty() { + let _ = writeln!(prompt, " "); + for tool in &skill.tools { + let _ = writeln!( + prompt, + " {}", + tool.name, tool.kind, tool.description + ); + } + let _ = writeln!(prompt, " "); + } + if !skill.prompts.is_empty() { + let _ = writeln!(prompt, " "); + for p in &skill.prompts { + prompt.push_str(" "); + prompt.push_str(p); + prompt.push('\n'); + } + let _ = writeln!(prompt, " "); + } + let _ = writeln!(prompt, " "); } prompt.push_str(""); Ok(prompt) diff --git a/src/channels/mod.rs b/src/channels/mod.rs index 0464924..41f3f95 100644 --- a/src/channels/mod.rs +++ b/src/channels/mod.rs @@ -1096,12 +1096,9 @@ pub fn build_system_prompt( - When in doubt, ask before acting externally.\n\n", ); - // ── 3. Skills (compact list — load on-demand) ─────────────── + // ── 3. Skills (inline prompts + tools) ────────────────────── if !skills.is_empty() { prompt.push_str("## Available Skills\n\n"); - prompt.push_str( - "Skills are loaded on demand. Use `read` on the skill path to get full instructions.\n\n", - ); prompt.push_str("\n"); for skill in skills { let _ = writeln!(prompt, " "); @@ -1118,6 +1115,26 @@ pub fn build_system_prompt( .join("SKILL.md") }); let _ = writeln!(prompt, " {}", location.display()); + if !skill.tools.is_empty() { + let _ = writeln!(prompt, " "); + for tool in &skill.tools { + let _ = writeln!( + prompt, + " {}", + tool.name, tool.kind, tool.description + ); + } + let _ = writeln!(prompt, " "); + } + if !skill.prompts.is_empty() { + let _ = writeln!(prompt, " "); + for p in &skill.prompts { + prompt.push_str(" "); + prompt.push_str(p); + prompt.push('\n'); + } + let _ = writeln!(prompt, " "); + } let _ = writeln!(prompt, " "); } prompt.push_str("\n\n"); @@ -3263,7 +3280,7 @@ mod tests { } #[test] - fn prompt_skills_compact_list() { + fn prompt_skills_inline_prompts_and_tools() { let ws = make_workspace(); let skills = vec![crate::skills::Skill { name: "code-review".into(), @@ -3271,8 +3288,14 @@ mod tests { version: "1.0.0".into(), author: None, tags: vec![], - tools: vec![], - prompts: vec!["Long prompt content that should NOT appear in system prompt".into()], + tools: vec![crate::skills::SkillTool { + name: "run_linter".into(), + description: "Run linter on code".into(), + kind: "shell".into(), + command: "cargo clippy".into(), + args: std::collections::HashMap::new(), + }], + prompts: vec!["When reviewing code, check for common bugs and style issues.".into()], location: None, }]; @@ -3282,12 +3305,21 @@ mod tests { assert!(prompt.contains("code-review")); assert!(prompt.contains("Review code for bugs")); assert!(prompt.contains("SKILL.md")); + // Skill prompts should be inlined assert!( - prompt.contains("loaded on demand"), - "should mention on-demand loading" + prompt.contains("When reviewing code, check for common bugs"), + "skill prompt should be inlined in system prompt" ); - // Full prompt content should NOT be dumped - assert!(!prompt.contains("Long prompt content that should NOT appear")); + assert!( + prompt.contains(""), + "should have instructions block" + ); + // Skill tools should be inlined + assert!( + prompt.contains("run_linter"), + "skill tool should be inlined" + ); + assert!(prompt.contains(""), "should have tools block"); } #[test]