diff --git a/.changeset/fix-skill-squad-rename-to-squad-help.md b/.changeset/fix-skill-squad-rename-to-squad-help.md new file mode 100644 index 000000000..29907db3c --- /dev/null +++ b/.changeset/fix-skill-squad-rename-to-squad-help.md @@ -0,0 +1,49 @@ +--- +"@bradygaster/squad-sdk": patch +"@bradygaster/squad-cli": patch +--- + +Fix `skill(Squad)` discovery — rename disambiguation skill to `squad-help` (supersedes #1297) + +PR #1297 added a disambiguation skill named `squad` so models calling `skill(Squad)` would get a redirect instead of "Skill not found". After local end-to-end testing on 2026-06-13: **the skill ships to disk correctly but never shows up in Copilot CLI's `/skills` list**. + +## Root cause (verified against Copilot CLI source 1.0.62-2) + +Decompiling `~/.copilot/pkg/win32-x64/1.0.62-2/app.js`: + +1. **Copilot CLI's skill schema is `{name, description, source, baseDir, allowedTools, pluginName, pluginVersion}`** (line 989). Frontmatter fields like `triggers:`, `domain:`, `confidence:`, `license:` are silently ignored. +2. **Skill loader returns `{skills, warnings, errors}`** (line 4427). Skills that fail to load are reported as errors. +3. **A skill named `squad` collides with the Copilot agent named `Squad`** (registered at `.github/agents/squad.agent.md`). The agent wins; the skill is hidden from `/skills`. + +The original `triggers:` frontmatter from #1297 was based on a wrong assumption — Copilot CLI doesn't read that field. Triggering happens via natural-language match against `description:`, and the skill name is what `skill(X)` looks up. + +## Fix + +1. **Rename** the disambiguation skill `squad` → `squad-help`. Avoids the agent-name collision, descriptive enough that the model can find it via description match when a user says *"how do I use squad"* or *"squad help"*. +2. **Update SKILL.md content:** + - `name: "squad-help"` (was `"squad"`) + - Removed `triggers:`, `domain:`, `confidence:`, `source:`, `license:` (all ignored by Copilot CLI) + - Added `allowedTools: []` (matches Copilot CLI's schema) + - `description:` rewritten to be self-explanatory so natural-language match works + - Body still explains the agent-vs-skill distinction and routes to `task(agent_type="Squad", …)` for misdirected `skill(Squad)` attempts + - Added explicit note about `/squad` slash command: it does not exist (slash commands are built-in CLI keywords, not auto-mapped from skills) and there's no way to create one without a Copilot CLI feature change +3. **Update** `MANIFEST_SKILL_NAMES` in `packages/squad-sdk/src/config/init.ts`: `'squad'` → `'squad-help'`. +4. **Add** `TEMPLATE_MANIFEST` entry in `packages/squad-cli/src/cli/core/templates.ts` for `squad-help` so `squad upgrade` also propagates the skill (independent of #1297 which only updated MANIFEST_SKILL_NAMES — `squad upgrade` uses a different code path that reads TEMPLATE_MANIFEST). + +## Test coverage + +New `test/init.test.ts > should install the squad-help disambiguation skill`: +- Asserts `.copilot/skills/squad-help/SKILL.md` exists after `initSquad()` +- Asserts the frontmatter says `name: "squad-help"` (not `"squad"`) — regression guard against re-introducing the collision +- Asserts content references `agent_type="Squad"` (the correct invocation path) +- Asserts content references `squad-commands` (the right next-step skill) + +26/26 init tests pass; `npm run lint` clean. + +## Supersedes #1297 + +PR #1297 added a colliding-name skill. This PR is the correct version. Close #1297 in favor of this one; the consolidated changeset will be picked up here. + +## Out of scope (separate issue worth filing) + +`squad upgrade` synced only 10 of 16 installed skills in local testing — `TEMPLATE_MANIFEST` (used by upgrade) is out of sync with `MANIFEST_SKILL_NAMES` (used by init). Specifically, `tiered-memory`, `iterative-retrieval`, `reflect`, `cross-squad`, `cross-squad-communication` (added in PRs #1292 + #1295) have entries in `MANIFEST_SKILL_NAMES` but not in `TEMPLATE_MANIFEST`. Will file a follow-up to add those to `TEMPLATE_MANIFEST` so both code paths agree. diff --git a/.squad/skills/squad-help/SKILL.md b/.squad/skills/squad-help/SKILL.md new file mode 100644 index 000000000..81bc4ae2a --- /dev/null +++ b/.squad/skills/squad-help/SKILL.md @@ -0,0 +1,97 @@ +--- +name: "squad-help" +description: "How to actually use Squad — Squad is a custom Copilot agent (invoked via the task tool with agent_type='Squad'), not a skill. This file explains the right invocation paths for setting up a team, listing squad commands, and initializing Squad in a new project." +allowedTools: [] +confidence: high +domain: squad-onboarding +--- + +# Skill: squad-help + +> **Quick reference.** If you're reading this because a user said "use squad" or "squad" or "set up a squad", you're in the right place — read on for the correct invocation paths. + +--- + +## Squad is a custom agent, not a skill + +The Squad framework registers a **custom Copilot CLI agent** at `.github/agents/squad.agent.md`. The agent is named **`Squad`** and its description is *"Your AI team. Describe what you're building, get a team of specialists that live in your repo."* + +Copilot CLI agents and skills are different things: + +| Thing | How to invoke | Example | +|---|---|---| +| **Skill** | `skill(name)` tool call or natural-language match | `skill(squad-commands)` | +| **Agent** | `task` tool with `agent_type=` | `task(name="...", agent_type="Squad", prompt="...")` | +| **Slash command** | Built-in CLI keyword | `/agent`, `/skills`, `/mcp` | + +Calling `skill(Squad)` will fail with *"Skill not found: Squad"* because Squad is the agent, not a skill. (`/squad` as a slash command also does not exist — only built-in CLI keywords like `/agent`, `/skills`, `/mcp` are slash commands. There's no way to map a skill name to a slash command without a Copilot CLI feature change.) + +--- + +## How to actually use Squad + +Pick the path that matches the user's intent: + +### A) Invoke the Squad coordinator agent (most common) + +The Squad coordinator orchestrates a team of specialists. It routes work to the right agent, scaffolds a team if none exists, and enforces handoffs. + +```text +task( + name="", + agent_type="Squad", + prompt="" +) +``` + +Use this when the user says things like: +- *"Use Squad to build X"* +- *"Set up an AI team for this project"* +- *"Have the Squad coordinator design Y"* +- *"Spawn Squad"* / *"Squad, help me with ..."* + +### B) See what Squad commands exist + +The `squad-commands` skill is a categorized catalog of common Squad operations. The coordinator presents it as an interactive menu. + +Trigger by natural-language match: `"squad commands"`, `"what can squad do"`, `"show me squad options"`, `"slash commands"`, `"what commands are available"`. + +Use this when the user says things like: +- *"What can Squad do?"* +- *"Show me the squad commands"* +- *"squad help"* + +### C) Initialize Squad in a fresh project + +`squad init` is a **shell command**, not a tool call. The user runs it in their terminal in a project that has no `.squad/` directory yet. + +```bash +squad init +``` + +Do **not** try to invoke this from inside an existing Copilot session — `.squad/` is already initialized if you're reading this file. + +--- + +## What NOT to do + +- ❌ Do not call `skill(Squad)`, `skill(squad)`, or `skill(squad-coordinator)` — Squad is not a skill. +- ❌ Do not type `/squad` expecting a slash command — slash commands are CLI keywords, not skill names. Use `/agent` (browse) or invoke the `Squad` agent via the `task` tool. +- ❌ Do not call `task(agent_type="Squad", …)` for tiny tasks the current agent can handle directly. Squad is for work that needs orchestration; trivial edits do not. + +--- + +## How this skill was discovered + +This skill ships from the Squad SDK templates and is wired into `MANIFEST_SKILL_NAMES`. It lives at `.copilot/skills/squad-help/SKILL.md` so the Copilot CLI's `/skills` loader picks it up alongside the other bundled Squad skills. + +If you removed this skill on purpose, the model will fall back to its own reasoning and may make the lookup mistakes described above. + +--- + +## See also + +- `.github/agents/squad.agent.md` — the actual Squad coordinator agent +- `.copilot/skills/squad-commands/SKILL.md` — the command catalog +- `.copilot/skills/squad-conventions/SKILL.md` — conventions for working on the Squad codebase itself +- `.copilot/skills/squad-version-check/SKILL.md` — version-stamping mechanics diff --git a/packages/squad-cli/src/cli/core/templates.ts b/packages/squad-cli/src/cli/core/templates.ts index 7917ca34c..403c6badd 100644 --- a/packages/squad-cli/src/cli/core/templates.ts +++ b/packages/squad-cli/src/cli/core/templates.ts @@ -257,6 +257,12 @@ export const TEMPLATE_MANIFEST: TemplateFile[] = [ overwriteOnUpgrade: true, description: 'Squad CLI internals — version stamping & upgrade mechanics', }, + { + source: 'skills/squad-help/SKILL.md', + destination: '../.copilot/skills/squad-help/SKILL.md', + overwriteOnUpgrade: true, + description: 'How to actually use Squad — agent vs skill vs slash command (#1297 redirect)', + }, // Session init reference (squad-owned, coordinator reads at session start) { diff --git a/packages/squad-cli/templates/skills/squad-help/SKILL.md b/packages/squad-cli/templates/skills/squad-help/SKILL.md new file mode 100644 index 000000000..81bc4ae2a --- /dev/null +++ b/packages/squad-cli/templates/skills/squad-help/SKILL.md @@ -0,0 +1,97 @@ +--- +name: "squad-help" +description: "How to actually use Squad — Squad is a custom Copilot agent (invoked via the task tool with agent_type='Squad'), not a skill. This file explains the right invocation paths for setting up a team, listing squad commands, and initializing Squad in a new project." +allowedTools: [] +confidence: high +domain: squad-onboarding +--- + +# Skill: squad-help + +> **Quick reference.** If you're reading this because a user said "use squad" or "squad" or "set up a squad", you're in the right place — read on for the correct invocation paths. + +--- + +## Squad is a custom agent, not a skill + +The Squad framework registers a **custom Copilot CLI agent** at `.github/agents/squad.agent.md`. The agent is named **`Squad`** and its description is *"Your AI team. Describe what you're building, get a team of specialists that live in your repo."* + +Copilot CLI agents and skills are different things: + +| Thing | How to invoke | Example | +|---|---|---| +| **Skill** | `skill(name)` tool call or natural-language match | `skill(squad-commands)` | +| **Agent** | `task` tool with `agent_type=` | `task(name="...", agent_type="Squad", prompt="...")` | +| **Slash command** | Built-in CLI keyword | `/agent`, `/skills`, `/mcp` | + +Calling `skill(Squad)` will fail with *"Skill not found: Squad"* because Squad is the agent, not a skill. (`/squad` as a slash command also does not exist — only built-in CLI keywords like `/agent`, `/skills`, `/mcp` are slash commands. There's no way to map a skill name to a slash command without a Copilot CLI feature change.) + +--- + +## How to actually use Squad + +Pick the path that matches the user's intent: + +### A) Invoke the Squad coordinator agent (most common) + +The Squad coordinator orchestrates a team of specialists. It routes work to the right agent, scaffolds a team if none exists, and enforces handoffs. + +```text +task( + name="", + agent_type="Squad", + prompt="" +) +``` + +Use this when the user says things like: +- *"Use Squad to build X"* +- *"Set up an AI team for this project"* +- *"Have the Squad coordinator design Y"* +- *"Spawn Squad"* / *"Squad, help me with ..."* + +### B) See what Squad commands exist + +The `squad-commands` skill is a categorized catalog of common Squad operations. The coordinator presents it as an interactive menu. + +Trigger by natural-language match: `"squad commands"`, `"what can squad do"`, `"show me squad options"`, `"slash commands"`, `"what commands are available"`. + +Use this when the user says things like: +- *"What can Squad do?"* +- *"Show me the squad commands"* +- *"squad help"* + +### C) Initialize Squad in a fresh project + +`squad init` is a **shell command**, not a tool call. The user runs it in their terminal in a project that has no `.squad/` directory yet. + +```bash +squad init +``` + +Do **not** try to invoke this from inside an existing Copilot session — `.squad/` is already initialized if you're reading this file. + +--- + +## What NOT to do + +- ❌ Do not call `skill(Squad)`, `skill(squad)`, or `skill(squad-coordinator)` — Squad is not a skill. +- ❌ Do not type `/squad` expecting a slash command — slash commands are CLI keywords, not skill names. Use `/agent` (browse) or invoke the `Squad` agent via the `task` tool. +- ❌ Do not call `task(agent_type="Squad", …)` for tiny tasks the current agent can handle directly. Squad is for work that needs orchestration; trivial edits do not. + +--- + +## How this skill was discovered + +This skill ships from the Squad SDK templates and is wired into `MANIFEST_SKILL_NAMES`. It lives at `.copilot/skills/squad-help/SKILL.md` so the Copilot CLI's `/skills` loader picks it up alongside the other bundled Squad skills. + +If you removed this skill on purpose, the model will fall back to its own reasoning and may make the lookup mistakes described above. + +--- + +## See also + +- `.github/agents/squad.agent.md` — the actual Squad coordinator agent +- `.copilot/skills/squad-commands/SKILL.md` — the command catalog +- `.copilot/skills/squad-conventions/SKILL.md` — conventions for working on the Squad codebase itself +- `.copilot/skills/squad-version-check/SKILL.md` — version-stamping mechanics diff --git a/packages/squad-sdk/src/config/init.ts b/packages/squad-sdk/src/config/init.ts index 9e37d7363..fd136ca49 100644 --- a/packages/squad-sdk/src/config/init.ts +++ b/packages/squad-sdk/src/config/init.ts @@ -39,6 +39,7 @@ const MANIFEST_SKILL_NAMES = [ 'agent-collaboration', 'squad-commands', 'squad-version-check', + 'squad-help', ] as const; // ============================================================================ diff --git a/packages/squad-sdk/templates/skills/squad-help/SKILL.md b/packages/squad-sdk/templates/skills/squad-help/SKILL.md new file mode 100644 index 000000000..81bc4ae2a --- /dev/null +++ b/packages/squad-sdk/templates/skills/squad-help/SKILL.md @@ -0,0 +1,97 @@ +--- +name: "squad-help" +description: "How to actually use Squad — Squad is a custom Copilot agent (invoked via the task tool with agent_type='Squad'), not a skill. This file explains the right invocation paths for setting up a team, listing squad commands, and initializing Squad in a new project." +allowedTools: [] +confidence: high +domain: squad-onboarding +--- + +# Skill: squad-help + +> **Quick reference.** If you're reading this because a user said "use squad" or "squad" or "set up a squad", you're in the right place — read on for the correct invocation paths. + +--- + +## Squad is a custom agent, not a skill + +The Squad framework registers a **custom Copilot CLI agent** at `.github/agents/squad.agent.md`. The agent is named **`Squad`** and its description is *"Your AI team. Describe what you're building, get a team of specialists that live in your repo."* + +Copilot CLI agents and skills are different things: + +| Thing | How to invoke | Example | +|---|---|---| +| **Skill** | `skill(name)` tool call or natural-language match | `skill(squad-commands)` | +| **Agent** | `task` tool with `agent_type=` | `task(name="...", agent_type="Squad", prompt="...")` | +| **Slash command** | Built-in CLI keyword | `/agent`, `/skills`, `/mcp` | + +Calling `skill(Squad)` will fail with *"Skill not found: Squad"* because Squad is the agent, not a skill. (`/squad` as a slash command also does not exist — only built-in CLI keywords like `/agent`, `/skills`, `/mcp` are slash commands. There's no way to map a skill name to a slash command without a Copilot CLI feature change.) + +--- + +## How to actually use Squad + +Pick the path that matches the user's intent: + +### A) Invoke the Squad coordinator agent (most common) + +The Squad coordinator orchestrates a team of specialists. It routes work to the right agent, scaffolds a team if none exists, and enforces handoffs. + +```text +task( + name="", + agent_type="Squad", + prompt="" +) +``` + +Use this when the user says things like: +- *"Use Squad to build X"* +- *"Set up an AI team for this project"* +- *"Have the Squad coordinator design Y"* +- *"Spawn Squad"* / *"Squad, help me with ..."* + +### B) See what Squad commands exist + +The `squad-commands` skill is a categorized catalog of common Squad operations. The coordinator presents it as an interactive menu. + +Trigger by natural-language match: `"squad commands"`, `"what can squad do"`, `"show me squad options"`, `"slash commands"`, `"what commands are available"`. + +Use this when the user says things like: +- *"What can Squad do?"* +- *"Show me the squad commands"* +- *"squad help"* + +### C) Initialize Squad in a fresh project + +`squad init` is a **shell command**, not a tool call. The user runs it in their terminal in a project that has no `.squad/` directory yet. + +```bash +squad init +``` + +Do **not** try to invoke this from inside an existing Copilot session — `.squad/` is already initialized if you're reading this file. + +--- + +## What NOT to do + +- ❌ Do not call `skill(Squad)`, `skill(squad)`, or `skill(squad-coordinator)` — Squad is not a skill. +- ❌ Do not type `/squad` expecting a slash command — slash commands are CLI keywords, not skill names. Use `/agent` (browse) or invoke the `Squad` agent via the `task` tool. +- ❌ Do not call `task(agent_type="Squad", …)` for tiny tasks the current agent can handle directly. Squad is for work that needs orchestration; trivial edits do not. + +--- + +## How this skill was discovered + +This skill ships from the Squad SDK templates and is wired into `MANIFEST_SKILL_NAMES`. It lives at `.copilot/skills/squad-help/SKILL.md` so the Copilot CLI's `/skills` loader picks it up alongside the other bundled Squad skills. + +If you removed this skill on purpose, the model will fall back to its own reasoning and may make the lookup mistakes described above. + +--- + +## See also + +- `.github/agents/squad.agent.md` — the actual Squad coordinator agent +- `.copilot/skills/squad-commands/SKILL.md` — the command catalog +- `.copilot/skills/squad-conventions/SKILL.md` — conventions for working on the Squad codebase itself +- `.copilot/skills/squad-version-check/SKILL.md` — version-stamping mechanics diff --git a/test/init.test.ts b/test/init.test.ts index 63b883ac5..86ba456f1 100644 --- a/test/init.test.ts +++ b/test/init.test.ts @@ -157,6 +157,41 @@ describe('Squad Initialization', () => { expect(content).toContain('.squad/decisions.md merge=union'); }); + it('should install the squad-help disambiguation skill (regression: #1297 / supersedes squad name collision)', async () => { + // The Squad framework registers a Copilot CLI agent named "Squad" at + // .github/agents/squad.agent.md. Coding models sometimes confuse that + // with a skill and call skill(Squad), which fails because: + // (a) Copilot CLI's skill loader uses {name, description} — the + // triggers field in our SKILL.md frontmatter is ignored + // (b) A skill named "squad" would collide with the agent name and + // hide the skill from /skills output + // + // Fix: name the disambiguation skill "squad-help" instead, which + // - avoids the agent-name collision (so /skills lists it) + // - is discoverable via natural-language match on its description + // + // Supersedes the original PR #1297 which used the colliding name. + const agents: InitAgentSpec[] = [{ name: 'lead', role: 'lead' }]; + const options: InitOptions = { + teamRoot: TEST_ROOT, + projectName: 'Test Project', + agents + }; + + await initSquad(options); + + const skillPath = join(TEST_ROOT, '.copilot', 'skills', 'squad-help', 'SKILL.md'); + expect(existsSync(skillPath)).toBe(true); + const content = await readFile(skillPath, 'utf-8'); + // The skill must NOT be named "squad" (collides with the agent named "Squad") + expect(content).toContain('name: "squad-help"'); + expect(content).not.toMatch(/^name:\s*"?squad"?\s*$/m); + // Must explain the agent-vs-skill distinction and route to task tool. + expect(content).toContain("agent_type=\"Squad\""); + expect(content).toContain('custom agent'); + expect(content).toContain('squad-commands'); + }); + it('should create initial decisions.md', async () => { const agents: InitAgentSpec[] = [{ name: 'lead', role: 'lead' }]; const options: InitOptions = {