diff --git a/agents/gsd-codebase-mapper.md b/agents/gsd-codebase-mapper.md index 9762f15774..96cac0367e 100644 --- a/agents/gsd-codebase-mapper.md +++ b/agents/gsd-codebase-mapper.md @@ -3,6 +3,14 @@ name: gsd-codebase-mapper description: Explores codebase and writes structured analysis documents. Spawned by map-codebase with a focus area (tech, arch, quality, concerns). Writes documents directly to reduce orchestrator context load. tools: Read, Bash, Grep, Glob, Write color: cyan +skills: + - gsd-mapper-workflow +# hooks: +# PostToolUse: +# - matcher: "Write|Edit" +# hooks: +# - type: command +# command: "npx eslint --fix $FILE 2>/dev/null || true" --- @@ -148,7 +156,7 @@ Write document(s) to `.planning/codebase/` using the templates below. 3. If something is not found, use "Not detected" or "Not applicable" 4. Always include file paths with backticks -Use the Write tool to create each document. +**ALWAYS use the Write tool to create files** — never use `Bash(cat << 'EOF')` or heredoc commands for file creation. diff --git a/agents/gsd-debugger.md b/agents/gsd-debugger.md index 2635ee7d7d..4a0437e0a8 100644 --- a/agents/gsd-debugger.md +++ b/agents/gsd-debugger.md @@ -3,6 +3,14 @@ name: gsd-debugger description: Investigates bugs using scientific method, manages debug sessions, handles checkpoints. Spawned by /gsd:debug orchestrator. tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch color: orange +skills: + - gsd-debugger-workflow +# hooks: +# PostToolUse: +# - matcher: "Write|Edit" +# hooks: +# - type: command +# command: "npx eslint --fix $FILE 2>/dev/null || true" --- @@ -849,6 +857,8 @@ ls .planning/debug/*.md 2>/dev/null | grep -v resolved **Create debug file IMMEDIATELY.** +**ALWAYS use the Write tool to create files** — never use `Bash(cat << 'EOF')` or heredoc commands for file creation. + 1. Generate slug from user input (lowercase, hyphens, max 30 chars) 2. `mkdir -p .planning/debug` 3. Create file with initial state: diff --git a/agents/gsd-executor.md b/agents/gsd-executor.md index 7d95f26238..8ae18c353e 100644 --- a/agents/gsd-executor.md +++ b/agents/gsd-executor.md @@ -3,6 +3,14 @@ name: gsd-executor description: Executes GSD plans with atomic commits, deviation handling, checkpoint protocols, and state management. Spawned by execute-phase orchestrator or execute-plan command. tools: Read, Write, Edit, Bash, Grep, Glob color: yellow +skills: + - gsd-executor-workflow +# hooks: +# PostToolUse: +# - matcher: "Write|Edit" +# hooks: +# - type: command +# command: "npx eslint --fix $FILE 2>/dev/null || true" --- diff --git a/agents/gsd-integration-checker.md b/agents/gsd-integration-checker.md index 14e1a8c7ce..e0e1d54e5a 100644 --- a/agents/gsd-integration-checker.md +++ b/agents/gsd-integration-checker.md @@ -3,6 +3,8 @@ name: gsd-integration-checker description: Verifies cross-phase integration and E2E flows. Checks that phases connect properly and user workflows complete end-to-end. tools: Read, Bash, Grep, Glob color: blue +skills: + - gsd-integration-workflow --- diff --git a/agents/gsd-phase-researcher.md b/agents/gsd-phase-researcher.md index 461bc34824..d06bb23e66 100644 --- a/agents/gsd-phase-researcher.md +++ b/agents/gsd-phase-researcher.md @@ -3,6 +3,14 @@ name: gsd-phase-researcher description: Researches how to implement a phase before planning. Produces RESEARCH.md consumed by gsd-planner. Spawned by /gsd:plan-phase orchestrator. tools: Read, Write, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__* color: cyan +skills: + - gsd-researcher-workflow +# hooks: +# PostToolUse: +# - matcher: "Write|Edit" +# hooks: +# - type: command +# command: "npx eslint --fix $FILE 2>/dev/null || true" --- @@ -421,7 +429,7 @@ List missing test files, framework config, or shared fixtures needed before impl ## Step 6: Write RESEARCH.md -**ALWAYS use Write tool to persist to disk** — mandatory regardless of `commit_docs` setting. +**ALWAYS use the Write tool to create files** — never use `Bash(cat << 'EOF')` or heredoc commands for file creation. Mandatory regardless of `commit_docs` setting. **CRITICAL: If CONTEXT.md exists, FIRST content section MUST be ``:** diff --git a/agents/gsd-plan-checker.md b/agents/gsd-plan-checker.md index 3ef73ea366..eedc294fa2 100644 --- a/agents/gsd-plan-checker.md +++ b/agents/gsd-plan-checker.md @@ -3,6 +3,8 @@ name: gsd-plan-checker description: Verifies plans will achieve phase goal before execution. Goal-backward analysis of plan quality. Spawned by /gsd:plan-phase orchestrator. tools: Read, Bash, Glob, Grep color: green +skills: + - gsd-plan-checker-workflow --- diff --git a/agents/gsd-planner.md b/agents/gsd-planner.md index ffa1723f83..7757d1fafa 100644 --- a/agents/gsd-planner.md +++ b/agents/gsd-planner.md @@ -3,6 +3,14 @@ name: gsd-planner description: Creates executable phase plans with task breakdown, dependency analysis, and goal-backward verification. Spawned by /gsd:plan-phase orchestrator. tools: Read, Write, Bash, Glob, Grep, WebFetch, mcp__context7__* color: green +skills: + - gsd-planner-workflow +# hooks: +# PostToolUse: +# - matcher: "Write|Edit" +# hooks: +# - type: command +# command: "npx eslint --fix $FILE 2>/dev/null || true" --- diff --git a/agents/gsd-project-researcher.md b/agents/gsd-project-researcher.md index cbf5554bc6..b9c8422eb8 100644 --- a/agents/gsd-project-researcher.md +++ b/agents/gsd-project-researcher.md @@ -3,6 +3,14 @@ name: gsd-project-researcher description: Researches domain ecosystem before roadmap creation. Produces files in .planning/research/ consumed during roadmap creation. Spawned by /gsd:new-project or /gsd:new-milestone orchestrators. tools: Read, Write, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__* color: cyan +skills: + - gsd-researcher-workflow +# hooks: +# PostToolUse: +# - matcher: "Write|Edit" +# hooks: +# - type: command +# command: "npx eslint --fix $FILE 2>/dev/null || true" --- @@ -518,6 +526,8 @@ Run pre-submission checklist (see verification_protocol). ## Step 5: Write Output Files +**ALWAYS use the Write tool to create files** — never use `Bash(cat << 'EOF')` or heredoc commands for file creation. + In `.planning/research/`: 1. **SUMMARY.md** — Always 2. **STACK.md** — Always diff --git a/agents/gsd-research-synthesizer.md b/agents/gsd-research-synthesizer.md index b016557add..aa4cbe8fde 100644 --- a/agents/gsd-research-synthesizer.md +++ b/agents/gsd-research-synthesizer.md @@ -3,6 +3,14 @@ name: gsd-research-synthesizer description: Synthesizes research outputs from parallel researcher agents into SUMMARY.md. Spawned by /gsd:new-project after 4 researcher agents complete. tools: Read, Write, Bash color: purple +skills: + - gsd-synthesizer-workflow +# hooks: +# PostToolUse: +# - matcher: "Write|Edit" +# hooks: +# - type: command +# command: "npx eslint --fix $FILE 2>/dev/null || true" --- @@ -122,6 +130,8 @@ Identify gaps that couldn't be resolved and need attention during planning. ## Step 6: Write SUMMARY.md +**ALWAYS use the Write tool to create files** — never use `Bash(cat << 'EOF')` or heredoc commands for file creation. + Use template: ~/.claude/get-shit-done/templates/research-project/SUMMARY.md Write to `.planning/research/SUMMARY.md` diff --git a/agents/gsd-roadmapper.md b/agents/gsd-roadmapper.md index 97b5cd22fc..fc02333c45 100644 --- a/agents/gsd-roadmapper.md +++ b/agents/gsd-roadmapper.md @@ -3,6 +3,14 @@ name: gsd-roadmapper description: Creates project roadmaps with phase breakdown, requirement mapping, success criteria derivation, and coverage validation. Spawned by /gsd:new-project orchestrator. tools: Read, Write, Bash, Glob, Grep color: purple +skills: + - gsd-roadmapper-workflow +# hooks: +# PostToolUse: +# - matcher: "Write|Edit" +# hooks: +# - type: command +# command: "npx eslint --fix $FILE 2>/dev/null || true" --- @@ -449,7 +457,9 @@ If gaps found, include in draft for user decision. ## Step 7: Write Files Immediately -**Write files first, then return.** This ensures artifacts persist even if context is lost. +**ALWAYS use the Write tool to create files** — never use `Bash(cat << 'EOF')` or heredoc commands for file creation. + +Write files first, then return. This ensures artifacts persist even if context is lost. 1. **Write ROADMAP.md** using output format diff --git a/agents/gsd-verifier.md b/agents/gsd-verifier.md index b2b5ed03ee..641754fd24 100644 --- a/agents/gsd-verifier.md +++ b/agents/gsd-verifier.md @@ -3,6 +3,14 @@ name: gsd-verifier description: Verifies phase goal achievement through goal-backward analysis. Checks codebase delivers what phase promised, not just that tasks completed. Creates VERIFICATION.md report. tools: Read, Write, Bash, Grep, Glob color: green +skills: + - gsd-verifier-workflow +# hooks: +# PostToolUse: +# - matcher: "Write|Edit" +# hooks: +# - type: command +# command: "npx eslint --fix $FILE 2>/dev/null || true" --- diff --git a/commands/gsd/research-phase.md b/commands/gsd/research-phase.md index ad022bf006..40feedfdd7 100644 --- a/commands/gsd/research-phase.md +++ b/commands/gsd/research-phase.md @@ -135,8 +135,8 @@ Write to: .planning/phases/${PHASE}-{slug}/${PHASE}-RESEARCH.md ``` Task( - prompt="First, read ~/.claude/agents/gsd-phase-researcher.md for your role and instructions.\n\n" + filled_prompt, - subagent_type="general-purpose", + prompt=filled_prompt, + subagent_type="gsd-phase-researcher", model="{researcher_model}", description="Research Phase {phase}" ) @@ -171,8 +171,8 @@ Continue research for Phase {phase_number}: {phase_name} ``` Task( - prompt="First, read ~/.claude/agents/gsd-phase-researcher.md for your role and instructions.\n\n" + continuation_prompt, - subagent_type="general-purpose", + prompt=continuation_prompt, + subagent_type="gsd-phase-researcher", model="{researcher_model}", description="Continue research Phase {phase}" ) diff --git a/docs/MCP-FRONTMATTER-FINDINGS.md b/docs/MCP-FRONTMATTER-FINDINGS.md new file mode 100644 index 0000000000..9dce044fca --- /dev/null +++ b/docs/MCP-FRONTMATTER-FINDINGS.md @@ -0,0 +1,54 @@ +# mcpServers Frontmatter Test Findings + +**Date:** 2026-02-28 +**Claude Code Version:** Current (Opus 4.6) +**GSD Version:** v1.22.0 +**Source:** Claude Code official docs (code.claude.com/docs/en/sub-agents) + +## Summary + +Claude Code's `mcpServers:` frontmatter field DOES create independent MCP server connections in custom subagents. However, this does NOT solve GSD's MCP access problem because GSD agents are installed as generic templates — we cannot hardcode user-specific MCP server configurations. + +## Evidence from Official Documentation + +From code.claude.com/docs/en/sub-agents (accessed 2026-02-28): + +> **mcpServers** (optional): MCP servers available to this subagent. Each entry is either a server name referencing an already-configured server (e.g., "slack") or an inline definition with the server name as key and a full MCP server config as value. + +This confirms: +1. **Inline definitions work** — defining a full MCP server config in frontmatter creates an independent connection +2. **Named references work** — referencing an already-configured server by name makes it available +3. **Subagents don't inherit MCP by default** — you must explicitly list servers + +## Test Conditions (Logical Analysis) + +| Condition | Config | Agent Type | Frontmatter | Result | Reasoning | +|-----------|--------|------------|-------------|--------|-----------| +| A | .mcp.json (project) | Main session | N/A | PASS | Main session always has access | +| B | .mcp.json (project) | Custom agent | None | FAIL | Bug #13898 — project MCP doesn't propagate | +| C | Inline in frontmatter | Custom agent | Yes (inline) | PASS | Independent connection per docs | +| D | ~/.claude/mcp.json (global) | Custom agent | None | PASS | Global MCP propagates correctly | + +## Why This Doesn't Solve GSD's Problem + +GSD agents are **generic templates** installed to `~/.claude/get-shit-done/agents/`. They serve all users across all projects. We cannot: + +1. Hardcode user-specific MCP server commands in agent frontmatter +2. Know at install time what MCP servers a user will need +3. Dynamically modify frontmatter at spawn time (Claude Code loads .md files as-is) + +The `mcpServers:` frontmatter approach works for **project-specific custom agents** (in `.claude/agents/`) but NOT for globally-installed agent templates like GSD's. + +## Strategy Decision + +**Proceed with PR-6 (MCP migration helper + connectivity pre-check)** as the practical solution: + +1. `/gsd:migrate-mcp` helps users move project MCP to global scope (proven to work) +2. MCP connectivity pre-check prevents hallucination (defense-in-depth) +3. Users with project-specific agents can add `mcpServers:` to their own `.claude/agents/` definitions + +The general-purpose fallback (PR-5) is deprioritized — named agent types are better for role isolation, model control, and frontmatter features (skills, hooks). Users needing MCP should migrate to global scope. + +## Upstream Issue + +Claude Code #13898 remains OPEN. The fix should be at the Claude Code level — project-scoped MCP servers should propagate to custom subagents. Until then, global MCP or `mcpServers:` frontmatter in project-specific agents are the workarounds. diff --git a/get-shit-done/workflows/diagnose-issues.md b/get-shit-done/workflows/diagnose-issues.md index 27e40a5b7f..299cf72cc9 100644 --- a/get-shit-done/workflows/diagnose-issues.md +++ b/get-shit-done/workflows/diagnose-issues.md @@ -80,7 +80,7 @@ For each gap, fill the debug-subagent-prompt template and spawn: ``` Task( prompt=filled_debug_subagent_prompt + "\n\n\n- {phase_dir}/{phase_num}-UAT.md\n- .planning/STATE.md\n", - subagent_type="general-purpose", + subagent_type="gsd-debugger", description="Debug: {truth_short}" ) ``` diff --git a/get-shit-done/workflows/new-project.md b/get-shit-done/workflows/new-project.md index b925dbbdc6..da6beeae6f 100644 --- a/get-shit-done/workflows/new-project.md +++ b/get-shit-done/workflows/new-project.md @@ -544,9 +544,7 @@ Display spawning indicator: Spawn 4 parallel gsd-project-researcher agents with path references: ``` -Task(prompt="First, read ~/.claude/agents/gsd-project-researcher.md for your role and instructions. - - +Task(prompt=" Project Research — Stack dimension for [domain]. @@ -582,11 +580,9 @@ Your STACK.md feeds into roadmap creation. Be prescriptive: Write to: .planning/research/STACK.md Use template: ~/.claude/get-shit-done/templates/research-project/STACK.md -", subagent_type="general-purpose", model="{researcher_model}", description="Stack research") - -Task(prompt="First, read ~/.claude/agents/gsd-project-researcher.md for your role and instructions. +", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Stack research") - +Task(prompt=" Project Research — Features dimension for [domain]. @@ -622,11 +618,9 @@ Your FEATURES.md feeds into requirements definition. Categorize clearly: Write to: .planning/research/FEATURES.md Use template: ~/.claude/get-shit-done/templates/research-project/FEATURES.md -", subagent_type="general-purpose", model="{researcher_model}", description="Features research") +", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Features research") -Task(prompt="First, read ~/.claude/agents/gsd-project-researcher.md for your role and instructions. - - +Task(prompt=" Project Research — Architecture dimension for [domain]. @@ -662,11 +656,9 @@ Your ARCHITECTURE.md informs phase structure in roadmap. Include: Write to: .planning/research/ARCHITECTURE.md Use template: ~/.claude/get-shit-done/templates/research-project/ARCHITECTURE.md -", subagent_type="general-purpose", model="{researcher_model}", description="Architecture research") - -Task(prompt="First, read ~/.claude/agents/gsd-project-researcher.md for your role and instructions. +", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Architecture research") - +Task(prompt=" Project Research — Pitfalls dimension for [domain]. @@ -702,7 +694,7 @@ Your PITFALLS.md prevents mistakes in roadmap/planning. For each pitfall: Write to: .planning/research/PITFALLS.md Use template: ~/.claude/get-shit-done/templates/research-project/PITFALLS.md -", subagent_type="general-purpose", model="{researcher_model}", description="Pitfalls research") +", subagent_type="gsd-project-researcher", model="{researcher_model}", description="Pitfalls research") ``` After all 4 agents complete, spawn synthesizer to create SUMMARY.md: diff --git a/get-shit-done/workflows/plan-phase.md b/get-shit-done/workflows/plan-phase.md index 91a788719a..db7836270a 100644 --- a/get-shit-done/workflows/plan-phase.md +++ b/get-shit-done/workflows/plan-phase.md @@ -207,8 +207,8 @@ Write to: {phase_dir}/{phase_num}-RESEARCH.md ``` Task( - prompt="First, read ~/.claude/agents/gsd-phase-researcher.md for your role and instructions.\n\n" + research_prompt, - subagent_type="general-purpose", + prompt=research_prompt, + subagent_type="gsd-phase-researcher", model="{researcher_model}", description="Research Phase {phase}" ) @@ -320,8 +320,8 @@ Output consumed by /gsd:execute-phase. Plans need: ``` Task( - prompt="First, read ~/.claude/agents/gsd-planner.md for your role and instructions.\n\n" + filled_prompt, - subagent_type="general-purpose", + prompt=filled_prompt, + subagent_type="gsd-planner", model="{planner_model}", description="Plan Phase {phase}" ) @@ -417,8 +417,8 @@ Return what changed. ``` Task( - prompt="First, read ~/.claude/agents/gsd-planner.md for your role and instructions.\n\n" + revision_prompt, - subagent_type="general-purpose", + prompt=revision_prompt, + subagent_type="gsd-planner", model="{planner_model}", description="Revise Phase {phase} plans" ) diff --git a/get-shit-done/workflows/quick.md b/get-shit-done/workflows/quick.md index ba306eaac8..3901be5561 100644 --- a/get-shit-done/workflows/quick.md +++ b/get-shit-done/workflows/quick.md @@ -222,8 +222,8 @@ Return what changed. ``` Task( - prompt="First, read ~/.claude/agents/gsd-planner.md for your role and instructions.\n\n" + revision_prompt, - subagent_type="general-purpose", + prompt=revision_prompt, + subagent_type="gsd-planner", model="{planner_model}", description="Revise quick plan: ${DESCRIPTION}" ) diff --git a/tests/agent-frontmatter.test.cjs b/tests/agent-frontmatter.test.cjs new file mode 100644 index 0000000000..9518d3b2d2 --- /dev/null +++ b/tests/agent-frontmatter.test.cjs @@ -0,0 +1,181 @@ +/** + * GSD Agent Frontmatter Tests + * + * Validates that all agent .md files have correct frontmatter fields: + * - Anti-heredoc instruction present in file-writing agents + * - skills: field in all agents + * - Commented hooks: pattern in file-writing agents + * - Spawn type consistency across workflows + */ + +const { test, describe } = require('node:test'); +const assert = require('node:assert'); +const fs = require('fs'); +const path = require('path'); + +const AGENTS_DIR = path.join(__dirname, '..', 'agents'); +const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows'); +const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd'); + +const ALL_AGENTS = fs.readdirSync(AGENTS_DIR) + .filter(f => f.startsWith('gsd-') && f.endsWith('.md')) + .map(f => f.replace('.md', '')); + +const FILE_WRITING_AGENTS = ALL_AGENTS.filter(name => { + const content = fs.readFileSync(path.join(AGENTS_DIR, name + '.md'), 'utf-8'); + const toolsMatch = content.match(/^tools:\s*(.+)$/m); + return toolsMatch && toolsMatch[1].includes('Write'); +}); + +const READ_ONLY_AGENTS = ALL_AGENTS.filter(name => !FILE_WRITING_AGENTS.includes(name)); + +// ─── Anti-Heredoc Instruction ──────────────────────────────────────────────── + +describe('HDOC: anti-heredoc instruction', () => { + for (const agent of FILE_WRITING_AGENTS) { + test(`${agent} has anti-heredoc instruction`, () => { + const content = fs.readFileSync(path.join(AGENTS_DIR, agent + '.md'), 'utf-8'); + assert.ok( + content.includes("never use `Bash(cat << 'EOF')` or heredoc"), + `${agent} missing anti-heredoc instruction` + ); + }); + } + + test('no active heredoc patterns in any agent file', () => { + for (const agent of ALL_AGENTS) { + const content = fs.readFileSync(path.join(AGENTS_DIR, agent + '.md'), 'utf-8'); + // Match actual heredoc commands (not references in anti-heredoc instruction) + const lines = content.split('\n'); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + // Skip lines that are part of the anti-heredoc instruction or markdown code fences + if (line.includes('never use') || line.includes('NEVER') || line.trim().startsWith('```')) continue; + // Check for actual heredoc usage instructions + if (/^cat\s+<<\s*'?EOF'?\s*>/.test(line.trim())) { + assert.fail(`${agent}:${i + 1} has active heredoc pattern: ${line.trim()}`); + } + } + } + }); +}); + +// ─── Skills Frontmatter ────────────────────────────────────────────────────── + +describe('SKILL: skills frontmatter', () => { + for (const agent of ALL_AGENTS) { + test(`${agent} has skills: in frontmatter`, () => { + const content = fs.readFileSync(path.join(AGENTS_DIR, agent + '.md'), 'utf-8'); + const frontmatter = content.split('---')[1] || ''; + assert.ok( + frontmatter.includes('skills:'), + `${agent} missing skills: in frontmatter` + ); + }); + } + + test('skill references follow naming convention', () => { + for (const agent of ALL_AGENTS) { + const content = fs.readFileSync(path.join(AGENTS_DIR, agent + '.md'), 'utf-8'); + const frontmatter = content.split('---')[1] || ''; + const skillLines = frontmatter.split('\n').filter(l => l.trim().startsWith('- gsd-')); + for (const line of skillLines) { + const skillName = line.trim().replace('- ', ''); + assert.match(skillName, /^gsd-[\w-]+-workflow$/, `Invalid skill name: ${skillName}`); + } + } + }); +}); + +// ─── Hooks Frontmatter ─────────────────────────────────────────────────────── + +describe('HOOK: hooks frontmatter pattern', () => { + for (const agent of FILE_WRITING_AGENTS) { + test(`${agent} has commented hooks pattern`, () => { + const content = fs.readFileSync(path.join(AGENTS_DIR, agent + '.md'), 'utf-8'); + const frontmatter = content.split('---')[1] || ''; + assert.ok( + frontmatter.includes('# hooks:'), + `${agent} missing commented hooks: pattern in frontmatter` + ); + }); + } + + for (const agent of READ_ONLY_AGENTS) { + test(`${agent} (read-only) does not need hooks`, () => { + const content = fs.readFileSync(path.join(AGENTS_DIR, agent + '.md'), 'utf-8'); + const frontmatter = content.split('---')[1] || ''; + // Read-only agents may or may not have hooks — just verify they parse + assert.ok(frontmatter.includes('name:'), `${agent} has valid frontmatter`); + }); + } +}); + +// ─── Spawn Type Consistency ────────────────────────────────────────────────── + +describe('SPAWN: spawn type consistency', () => { + test('no "First, read agent .md" workaround pattern remains', () => { + const dirs = [WORKFLOWS_DIR, COMMANDS_DIR]; + for (const dir of dirs) { + if (!fs.existsSync(dir)) continue; + const files = fs.readdirSync(dir).filter(f => f.endsWith('.md')); + for (const file of files) { + const content = fs.readFileSync(path.join(dir, file), 'utf-8'); + const hasWorkaround = content.includes('First, read ~/.claude/agents/gsd-'); + assert.ok( + !hasWorkaround, + `${file} still has "First, read agent .md" workaround — use named subagent_type instead` + ); + } + } + }); + + test('named agent spawns use correct agent names', () => { + const validAgentTypes = new Set([ + ...ALL_AGENTS, + 'general-purpose', // Allowed for orchestrator spawns + ]); + + const dirs = [WORKFLOWS_DIR, COMMANDS_DIR]; + for (const dir of dirs) { + if (!fs.existsSync(dir)) continue; + const files = fs.readdirSync(dir).filter(f => f.endsWith('.md')); + for (const file of files) { + const content = fs.readFileSync(path.join(dir, file), 'utf-8'); + const matches = content.matchAll(/subagent_type="([^"]+)"/g); + for (const match of matches) { + const agentType = match[1]; + assert.ok( + validAgentTypes.has(agentType), + `${file} references unknown agent type: ${agentType}` + ); + } + } + } + }); + + test('diagnose-issues uses gsd-debugger (not general-purpose)', () => { + const content = fs.readFileSync( + path.join(WORKFLOWS_DIR, 'diagnose-issues.md'), 'utf-8' + ); + assert.ok( + content.includes('subagent_type="gsd-debugger"'), + 'diagnose-issues should spawn gsd-debugger, not general-purpose' + ); + }); +}); + +// ─── Required Frontmatter Fields ───────────────────────────────────────────── + +describe('AGENT: required frontmatter fields', () => { + for (const agent of ALL_AGENTS) { + test(`${agent} has name, description, tools, color`, () => { + const content = fs.readFileSync(path.join(AGENTS_DIR, agent + '.md'), 'utf-8'); + const frontmatter = content.split('---')[1] || ''; + assert.ok(frontmatter.includes('name:'), `${agent} missing name:`); + assert.ok(frontmatter.includes('description:'), `${agent} missing description:`); + assert.ok(frontmatter.includes('tools:'), `${agent} missing tools:`); + assert.ok(frontmatter.includes('color:'), `${agent} missing color:`); + }); + } +});