From fbf3018ee4080928ba13a4c0c5d8b7eba3c5531e Mon Sep 17 00:00:00 2001 From: Bo Li Date: Thu, 19 Feb 2026 02:22:14 +0800 Subject: [PATCH] refactor(prompt): dedupe repeated skill guidance blocks --- src/agents/atlas/prompt-section-builder.ts | 37 +++---------------- .../dynamic-agent-prompt-builder.test.ts | 12 +++--- src/agents/dynamic-agent-prompt-builder.ts | 3 +- 3 files changed, 12 insertions(+), 40 deletions(-) diff --git a/src/agents/atlas/prompt-section-builder.ts b/src/agents/atlas/prompt-section-builder.ts index 194621650..50f6312de 100644 --- a/src/agents/atlas/prompt-section-builder.ts +++ b/src/agents/atlas/prompt-section-builder.ts @@ -6,7 +6,7 @@ */ import type { CategoryConfig } from "../../config/schema" -import { formatCustomSkillsBlock, type AvailableAgent, type AvailableSkill } from "../dynamic-agent-prompt-builder" +import type { AvailableAgent, AvailableSkill } from "../dynamic-agent-prompt-builder" import { CATEGORY_DESCRIPTIONS } from "../../tools/delegate-task/constants" import { mergeCategories } from "../../shared/merge-categories" import { truncateDescription } from "../../shared/truncate-description" @@ -58,43 +58,16 @@ export function buildSkillsSection(skills: AvailableSkill[]): string { const builtinSkills = skills.filter((s) => s.location === "plugin") const customSkills = skills.filter((s) => s.location !== "plugin") - const builtinRows = builtinSkills.map((s) => { - const shortDesc = truncateDescription(s.description) - return `- **\`${s.name}\`** — ${shortDesc}` - }) - - const customRows = customSkills.map((s) => { - const shortDesc = truncateDescription(s.description) - const source = s.location === "project" ? "project" : "user" - return `- **\`${s.name}\`** (${source}): ${shortDesc}` - }) - - const customSkillBlock = formatCustomSkillsBlock(customRows, customSkills, "**") - - let skillsTable: string - - if (customSkills.length > 0 && builtinSkills.length > 0) { - skillsTable = `**Built-in Skills:** - -${builtinRows.join("\n")} - -${customSkillBlock}` - } else if (customSkills.length > 0) { - skillsTable = customSkillBlock - } else { - skillsTable = `${builtinRows.join("\n")}` - } - return ` #### 3.2.2: Skill Selection (PREPEND TO PROMPT) -**Skills are specialized instructions that guide subagent behavior. Consider them alongside category selection.** - -${skillsTable} +**Use the \`Category + Skills Delegation System\` section below as the single source of truth for skill details.** +- Built-in skills available: ${builtinSkills.length} +- User-installed skills available: ${customSkills.length} **MANDATORY: Evaluate ALL skills (built-in AND user-installed) for relevance to your task.** -Read each skill's description and ask: "Does this skill's domain overlap with my task?" +Read each skill's description in the section below and ask: "Does this skill's domain overlap with my task?" - If YES: INCLUDE in load_skills=[...] - If NO: You MUST justify why in your pre-delegation declaration diff --git a/src/agents/dynamic-agent-prompt-builder.test.ts b/src/agents/dynamic-agent-prompt-builder.test.ts index 952c8912d..fcc99bad7 100644 --- a/src/agents/dynamic-agent-prompt-builder.test.ts +++ b/src/agents/dynamic-agent-prompt-builder.test.ts @@ -43,16 +43,16 @@ describe("buildCategorySkillsDelegationGuide", () => { expect(result).toContain("HIGH PRIORITY") }) - it("should include custom skill names in CRITICAL warning", () => { + it("should list custom skills and keep CRITICAL warning", () => { //#given: custom skills installed const allSkills = [...builtinSkills, ...customUserSkills] //#when: building the delegation guide const result = buildCategorySkillsDelegationGuide(categories, allSkills) - //#then: should mention custom skills by name in the warning - expect(result).toContain('"react-19"') - expect(result).toContain('"tailwind-4"') + //#then: should mention custom skills by name and include warning + expect(result).toContain("`react-19`") + expect(result).toContain("`tailwind-4`") expect(result).toContain("CRITICAL") }) @@ -180,8 +180,8 @@ describe("formatCustomSkillsBlock", () => { //#then: contains all expected elements expect(result).toContain("User-Installed Skills (HIGH PRIORITY)") expect(result).toContain("CRITICAL") - expect(result).toContain('"react-19"') - expect(result).toContain('"tailwind-4"') + expect(result).toContain("`react-19`") + expect(result).toContain("`tailwind-4`") expect(result).toContain("| user |") expect(result).toContain("| project |") }) diff --git a/src/agents/dynamic-agent-prompt-builder.ts b/src/agents/dynamic-agent-prompt-builder.ts index 163115903..c192054f9 100644 --- a/src/agents/dynamic-agent-prompt-builder.ts +++ b/src/agents/dynamic-agent-prompt-builder.ts @@ -167,7 +167,6 @@ export function formatCustomSkillsBlock( customSkills: AvailableSkill[], headerLevel: "####" | "**" = "####" ): string { - const customSkillNames = customSkills.map((s) => `"${s.name}"`).join(", ") const header = headerLevel === "####" ? `#### User-Installed Skills (HIGH PRIORITY)` : `**User-Installed Skills (HIGH PRIORITY):**` @@ -180,7 +179,7 @@ Subagents are STATELESS — they lose all custom knowledge unless you pass these ${customRows.join("\n")} > **CRITICAL**: Ignoring user-installed skills when they match the task domain is a failure. -> The user installed ${customSkillNames} for a reason — USE THEM when the task overlaps with their domain.` +> The user installed custom skills for a reason — USE THEM when the task overlaps with their domain.` } export function buildCategorySkillsDelegationGuide(categories: AvailableCategory[], skills: AvailableSkill[]): string {