From fe1e7e8c3bb9417fb60e64c511d8b2307810f8bd Mon Sep 17 00:00:00 2001 From: tamirdresher Date: Wed, 3 Jun 2026 07:04:56 +0300 Subject: [PATCH 1/3] feat(skills): expand coordinator skill-aware routing to all 5 Copilot CLI paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squad's coordinator previously scanned only .copilot/skills/ and .squad/skills/ — but Copilot CLI loads skills from 5 project paths AND 2 personal paths. Skills placed in .github/skills/ (a common location alongside other .github/ tooling) were loaded ambiently by the CLI but invisible to Squad's skill-aware routing, so agents didn't get them attached to spawn prompts. This PR ports Picard's 5-decision skill-discovery design from squad-squad's governance doc into the upstream templates that ship via `squad upgrade`: 1. Precedence: .squad/skills/ > .copilot/skills/ > .github/skills/ > .claude/skills/ > .agents/skills/ 2. Personal paths (~/.copilot/skills/, ~/.agents/skills/) excluded — Copilot CLI already injects them ambiently 3. One-level traversal, no symlinks, no per-session cache 4. Dedup by directory name (case-insensitive, NFC-normalized, whitespace-trimmed) 5. Invalid-char skip (null bytes, control chars, path separators) Updates 5 template-mirror files (squad.agent.md) and 4 mirror files (spawn-reference.md) via scripts/sync-templates.mjs --sync. Source: GitHub Copilot CLI docs — https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-skills Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/skill-discovery-paths.md | 13 +++++++++++++ .github/agents/squad.agent.md | 14 +++++++++++--- .squad-templates/spawn-reference.md | 3 +-- .squad-templates/squad.agent.md | 14 +++++++++++--- packages/squad-cli/templates/spawn-reference.md | 3 +-- .../squad-cli/templates/squad.agent.md.template | 14 +++++++++++--- packages/squad-sdk/templates/spawn-reference.md | 3 +-- .../squad-sdk/templates/squad.agent.md.template | 14 +++++++++++--- templates/spawn-reference.md | 3 +-- templates/squad.agent.md.template | 14 +++++++++++--- 10 files changed, 72 insertions(+), 23 deletions(-) create mode 100644 .changeset/skill-discovery-paths.md diff --git a/.changeset/skill-discovery-paths.md b/.changeset/skill-discovery-paths.md new file mode 100644 index 000000000..789b280e0 --- /dev/null +++ b/.changeset/skill-discovery-paths.md @@ -0,0 +1,13 @@ +--- +"@bradygaster/squad-cli": minor +"@bradygaster/squad-sdk": minor +--- + +Squad coordinator now scans all 5 official Copilot CLI project skill paths: +`.squad/skills/` > `.copilot/skills/` > `.github/skills/` > `.claude/skills/` +> `.agents/skills/` (in precedence order, dedup by directory name). Personal +paths (`~/.copilot/skills/`, `~/.agents/skills/`) are deliberately excluded +from explicit routing — Copilot CLI injects them ambiently. This closes a gap +where skills placed in `.github/skills/` (a common location alongside other +`.github/` tooling) were loaded by Copilot CLI but invisible to Squad's +coordinator-attached skill-aware routing. diff --git a/.github/agents/squad.agent.md b/.github/agents/squad.agent.md index aeade9aaf..c29a4757e 100644 --- a/.github/agents/squad.agent.md +++ b/.github/agents/squad.agent.md @@ -303,9 +303,17 @@ The routing table determines **WHO** handles work. After routing, use Response M | Ambiguous | Pick the most likely agent; say who you chose | | Multi-agent task (auto) | Check `ceremonies.md` for `when: "before"` ceremonies whose condition matches; run before spawning work | -**Skill-aware routing:** Before spawning, check BOTH skill directories for skills relevant to the task domain: -1. `.copilot/skills/` — **Copilot-level skills.** Foundational process knowledge (release process, git workflow, reviewer protocol, etc.). These are the coordinator's own playbook — check first. -2. `.squad/skills/` — **Team-level skills.** Patterns and practices agents discovered during work. + +**Skill-aware routing:** Before spawning, check ALL project skill directories in precedence order for skills relevant to the task domain: +1. `.squad/skills/` — **Team-earned skills** (highest precedence). Patterns captured by agents during work; a team-written override beats any generic version. +2. `.copilot/skills/` — **Project playbook.** Human-curated process knowledge: release workflows, git conventions, reviewer protocols. +3. `.github/skills/` — **Generic project skills.** Sits alongside `.github/workflows/` and `.github/copilot-instructions.md`; common location for shared-repo skills. +4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. +5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. + +**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI already injects them as ambient context for every agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. + +**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Normalize directory names to NFC Unicode form and trim trailing whitespace before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` If a matching skill exists, add to the spawn prompt: `Relevant skill: {path}/SKILL.md — read before starting.` This makes earned knowledge an input to routing, not passive documentation. diff --git a/.squad-templates/spawn-reference.md b/.squad-templates/spawn-reference.md index 591989228..94c2509d9 100644 --- a/.squad-templates/spawn-reference.md +++ b/.squad-templates/spawn-reference.md @@ -81,8 +81,7 @@ prompt: | Read `decisions.md` with `squad_state_read` when state tools are available; otherwise fall back to `.squad/decisions.md`. If .squad/identity/wisdom.md exists, read it before starting work. If .squad/identity/now.md exists, read it at spawn time. - Check .copilot/skills/ for copilot-level skills (process, workflow, protocol). - Check .squad/skills/ for team-level skills (patterns discovered during work). + Check project skill directories (.squad/skills/, .copilot/skills/, .github/skills/, .claude/skills/, .agents/skills/) for any SKILL.md the coordinator attached to your prompt. Read any relevant SKILL.md files before working. ⚠️ WORK FRESHNESS: When determining what to work on: diff --git a/.squad-templates/squad.agent.md b/.squad-templates/squad.agent.md index aeade9aaf..c29a4757e 100644 --- a/.squad-templates/squad.agent.md +++ b/.squad-templates/squad.agent.md @@ -303,9 +303,17 @@ The routing table determines **WHO** handles work. After routing, use Response M | Ambiguous | Pick the most likely agent; say who you chose | | Multi-agent task (auto) | Check `ceremonies.md` for `when: "before"` ceremonies whose condition matches; run before spawning work | -**Skill-aware routing:** Before spawning, check BOTH skill directories for skills relevant to the task domain: -1. `.copilot/skills/` — **Copilot-level skills.** Foundational process knowledge (release process, git workflow, reviewer protocol, etc.). These are the coordinator's own playbook — check first. -2. `.squad/skills/` — **Team-level skills.** Patterns and practices agents discovered during work. + +**Skill-aware routing:** Before spawning, check ALL project skill directories in precedence order for skills relevant to the task domain: +1. `.squad/skills/` — **Team-earned skills** (highest precedence). Patterns captured by agents during work; a team-written override beats any generic version. +2. `.copilot/skills/` — **Project playbook.** Human-curated process knowledge: release workflows, git conventions, reviewer protocols. +3. `.github/skills/` — **Generic project skills.** Sits alongside `.github/workflows/` and `.github/copilot-instructions.md`; common location for shared-repo skills. +4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. +5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. + +**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI already injects them as ambient context for every agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. + +**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Normalize directory names to NFC Unicode form and trim trailing whitespace before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` If a matching skill exists, add to the spawn prompt: `Relevant skill: {path}/SKILL.md — read before starting.` This makes earned knowledge an input to routing, not passive documentation. diff --git a/packages/squad-cli/templates/spawn-reference.md b/packages/squad-cli/templates/spawn-reference.md index 591989228..94c2509d9 100644 --- a/packages/squad-cli/templates/spawn-reference.md +++ b/packages/squad-cli/templates/spawn-reference.md @@ -81,8 +81,7 @@ prompt: | Read `decisions.md` with `squad_state_read` when state tools are available; otherwise fall back to `.squad/decisions.md`. If .squad/identity/wisdom.md exists, read it before starting work. If .squad/identity/now.md exists, read it at spawn time. - Check .copilot/skills/ for copilot-level skills (process, workflow, protocol). - Check .squad/skills/ for team-level skills (patterns discovered during work). + Check project skill directories (.squad/skills/, .copilot/skills/, .github/skills/, .claude/skills/, .agents/skills/) for any SKILL.md the coordinator attached to your prompt. Read any relevant SKILL.md files before working. ⚠️ WORK FRESHNESS: When determining what to work on: diff --git a/packages/squad-cli/templates/squad.agent.md.template b/packages/squad-cli/templates/squad.agent.md.template index aeade9aaf..c29a4757e 100644 --- a/packages/squad-cli/templates/squad.agent.md.template +++ b/packages/squad-cli/templates/squad.agent.md.template @@ -303,9 +303,17 @@ The routing table determines **WHO** handles work. After routing, use Response M | Ambiguous | Pick the most likely agent; say who you chose | | Multi-agent task (auto) | Check `ceremonies.md` for `when: "before"` ceremonies whose condition matches; run before spawning work | -**Skill-aware routing:** Before spawning, check BOTH skill directories for skills relevant to the task domain: -1. `.copilot/skills/` — **Copilot-level skills.** Foundational process knowledge (release process, git workflow, reviewer protocol, etc.). These are the coordinator's own playbook — check first. -2. `.squad/skills/` — **Team-level skills.** Patterns and practices agents discovered during work. + +**Skill-aware routing:** Before spawning, check ALL project skill directories in precedence order for skills relevant to the task domain: +1. `.squad/skills/` — **Team-earned skills** (highest precedence). Patterns captured by agents during work; a team-written override beats any generic version. +2. `.copilot/skills/` — **Project playbook.** Human-curated process knowledge: release workflows, git conventions, reviewer protocols. +3. `.github/skills/` — **Generic project skills.** Sits alongside `.github/workflows/` and `.github/copilot-instructions.md`; common location for shared-repo skills. +4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. +5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. + +**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI already injects them as ambient context for every agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. + +**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Normalize directory names to NFC Unicode form and trim trailing whitespace before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` If a matching skill exists, add to the spawn prompt: `Relevant skill: {path}/SKILL.md — read before starting.` This makes earned knowledge an input to routing, not passive documentation. diff --git a/packages/squad-sdk/templates/spawn-reference.md b/packages/squad-sdk/templates/spawn-reference.md index 591989228..94c2509d9 100644 --- a/packages/squad-sdk/templates/spawn-reference.md +++ b/packages/squad-sdk/templates/spawn-reference.md @@ -81,8 +81,7 @@ prompt: | Read `decisions.md` with `squad_state_read` when state tools are available; otherwise fall back to `.squad/decisions.md`. If .squad/identity/wisdom.md exists, read it before starting work. If .squad/identity/now.md exists, read it at spawn time. - Check .copilot/skills/ for copilot-level skills (process, workflow, protocol). - Check .squad/skills/ for team-level skills (patterns discovered during work). + Check project skill directories (.squad/skills/, .copilot/skills/, .github/skills/, .claude/skills/, .agents/skills/) for any SKILL.md the coordinator attached to your prompt. Read any relevant SKILL.md files before working. ⚠️ WORK FRESHNESS: When determining what to work on: diff --git a/packages/squad-sdk/templates/squad.agent.md.template b/packages/squad-sdk/templates/squad.agent.md.template index aeade9aaf..c29a4757e 100644 --- a/packages/squad-sdk/templates/squad.agent.md.template +++ b/packages/squad-sdk/templates/squad.agent.md.template @@ -303,9 +303,17 @@ The routing table determines **WHO** handles work. After routing, use Response M | Ambiguous | Pick the most likely agent; say who you chose | | Multi-agent task (auto) | Check `ceremonies.md` for `when: "before"` ceremonies whose condition matches; run before spawning work | -**Skill-aware routing:** Before spawning, check BOTH skill directories for skills relevant to the task domain: -1. `.copilot/skills/` — **Copilot-level skills.** Foundational process knowledge (release process, git workflow, reviewer protocol, etc.). These are the coordinator's own playbook — check first. -2. `.squad/skills/` — **Team-level skills.** Patterns and practices agents discovered during work. + +**Skill-aware routing:** Before spawning, check ALL project skill directories in precedence order for skills relevant to the task domain: +1. `.squad/skills/` — **Team-earned skills** (highest precedence). Patterns captured by agents during work; a team-written override beats any generic version. +2. `.copilot/skills/` — **Project playbook.** Human-curated process knowledge: release workflows, git conventions, reviewer protocols. +3. `.github/skills/` — **Generic project skills.** Sits alongside `.github/workflows/` and `.github/copilot-instructions.md`; common location for shared-repo skills. +4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. +5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. + +**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI already injects them as ambient context for every agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. + +**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Normalize directory names to NFC Unicode form and trim trailing whitespace before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` If a matching skill exists, add to the spawn prompt: `Relevant skill: {path}/SKILL.md — read before starting.` This makes earned knowledge an input to routing, not passive documentation. diff --git a/templates/spawn-reference.md b/templates/spawn-reference.md index 591989228..94c2509d9 100644 --- a/templates/spawn-reference.md +++ b/templates/spawn-reference.md @@ -81,8 +81,7 @@ prompt: | Read `decisions.md` with `squad_state_read` when state tools are available; otherwise fall back to `.squad/decisions.md`. If .squad/identity/wisdom.md exists, read it before starting work. If .squad/identity/now.md exists, read it at spawn time. - Check .copilot/skills/ for copilot-level skills (process, workflow, protocol). - Check .squad/skills/ for team-level skills (patterns discovered during work). + Check project skill directories (.squad/skills/, .copilot/skills/, .github/skills/, .claude/skills/, .agents/skills/) for any SKILL.md the coordinator attached to your prompt. Read any relevant SKILL.md files before working. ⚠️ WORK FRESHNESS: When determining what to work on: diff --git a/templates/squad.agent.md.template b/templates/squad.agent.md.template index aeade9aaf..c29a4757e 100644 --- a/templates/squad.agent.md.template +++ b/templates/squad.agent.md.template @@ -303,9 +303,17 @@ The routing table determines **WHO** handles work. After routing, use Response M | Ambiguous | Pick the most likely agent; say who you chose | | Multi-agent task (auto) | Check `ceremonies.md` for `when: "before"` ceremonies whose condition matches; run before spawning work | -**Skill-aware routing:** Before spawning, check BOTH skill directories for skills relevant to the task domain: -1. `.copilot/skills/` — **Copilot-level skills.** Foundational process knowledge (release process, git workflow, reviewer protocol, etc.). These are the coordinator's own playbook — check first. -2. `.squad/skills/` — **Team-level skills.** Patterns and practices agents discovered during work. + +**Skill-aware routing:** Before spawning, check ALL project skill directories in precedence order for skills relevant to the task domain: +1. `.squad/skills/` — **Team-earned skills** (highest precedence). Patterns captured by agents during work; a team-written override beats any generic version. +2. `.copilot/skills/` — **Project playbook.** Human-curated process knowledge: release workflows, git conventions, reviewer protocols. +3. `.github/skills/` — **Generic project skills.** Sits alongside `.github/workflows/` and `.github/copilot-instructions.md`; common location for shared-repo skills. +4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. +5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. + +**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI already injects them as ambient context for every agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. + +**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Normalize directory names to NFC Unicode form and trim trailing whitespace before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` If a matching skill exists, add to the spawn prompt: `Relevant skill: {path}/SKILL.md — read before starting.` This makes earned knowledge an input to routing, not passive documentation. From 8a62093ab5a1895a5b402b82bc2ac75c9ee0c7f0 Mon Sep 17 00:00:00 2001 From: tamirdresher Date: Wed, 3 Jun 2026 07:13:22 +0300 Subject: [PATCH 2/3] =?UTF-8?q?fix(skills):=20apply=20Worf=20review=20cond?= =?UTF-8?q?itions=20C-0..C-4=20=E2=80=94=20port=20traversal=20rule=20from?= =?UTF-8?q?=20design?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per Worf review on workstream skill-discovery-paths (.squad/workstreams/active/skill-discovery-paths/decisions/inbox/worf-skill-discovery-review.md): - C-0 BLOCKING: add Traversal rule paragraph (one-level only, skip symlinks AND reparse points, no per-session cache, with rationale) - C-1: dedup denylist is minimum contract; runtime MUST reject homoglyph separators (U+FF0F, U+2044); SHOULD reject Windows reserved names (CON, PRN, AUX, NUL, COM1-9, LPT1-9) - C-2: whitespace trim includes leading AND trailing AND zero-width chars (U+200B, U+200C, U+200D, U+FEFF) - C-3: case-insensitive comparison applies regardless of filesystem case sensitivity - C-4: personal-paths rationale scoped explicitly to CLI surface Source: Decision 3 in workstream decisions.md (one-level / no-symlinks / no-cache), which was omitted from the prior implementation commit. Reviewed by: Worf (re-review pending) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/agents/squad.agent.md | 6 ++++-- .squad-templates/squad.agent.md | 6 ++++-- packages/squad-cli/templates/squad.agent.md.template | 6 ++++-- packages/squad-sdk/templates/squad.agent.md.template | 6 ++++-- templates/squad.agent.md.template | 6 ++++-- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/.github/agents/squad.agent.md b/.github/agents/squad.agent.md index c29a4757e..7b19840a9 100644 --- a/.github/agents/squad.agent.md +++ b/.github/agents/squad.agent.md @@ -311,9 +311,11 @@ The routing table determines **WHO** handles work. After routing, use Response M 4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. 5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. -**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI already injects them as ambient context for every agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. +**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, use a hardlink or copy the directory into one of the 5 paths. -**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Normalize directory names to NFC Unicode form and trim trailing whitespace before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` +**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI injects them as ambient context for every CLI agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. (Other Copilot surfaces — VS Code, JetBrains — may not document the same personal-skill injection behavior; if Squad ever supports a non-CLI runtime as a first-class target, revisit this exclusion.) + +**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Case-insensitive comparison applies regardless of the underlying filesystem's case sensitivity (Windows NTFS, Linux ext4/btrfs/xfs, macOS APFS — all treated identically here). Normalize directory names to NFC Unicode form and trim leading and trailing whitespace, including zero-width characters (`U+200B`, `U+200C`, `U+200D`, `U+FEFF`), before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` (The listed denylist is the *minimum* contract. Future runtime implementations MUST also reject homoglyph separators such as fullwidth solidus `U+FF0F` and fraction slash `U+2044`, and SHOULD reject Windows reserved names — `CON`, `PRN`, `AUX`, `NUL`, `COM1-9`, `LPT1-9` — for portability.) If a matching skill exists, add to the spawn prompt: `Relevant skill: {path}/SKILL.md — read before starting.` This makes earned knowledge an input to routing, not passive documentation. diff --git a/.squad-templates/squad.agent.md b/.squad-templates/squad.agent.md index c29a4757e..7b19840a9 100644 --- a/.squad-templates/squad.agent.md +++ b/.squad-templates/squad.agent.md @@ -311,9 +311,11 @@ The routing table determines **WHO** handles work. After routing, use Response M 4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. 5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. -**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI already injects them as ambient context for every agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. +**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, use a hardlink or copy the directory into one of the 5 paths. -**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Normalize directory names to NFC Unicode form and trim trailing whitespace before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` +**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI injects them as ambient context for every CLI agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. (Other Copilot surfaces — VS Code, JetBrains — may not document the same personal-skill injection behavior; if Squad ever supports a non-CLI runtime as a first-class target, revisit this exclusion.) + +**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Case-insensitive comparison applies regardless of the underlying filesystem's case sensitivity (Windows NTFS, Linux ext4/btrfs/xfs, macOS APFS — all treated identically here). Normalize directory names to NFC Unicode form and trim leading and trailing whitespace, including zero-width characters (`U+200B`, `U+200C`, `U+200D`, `U+FEFF`), before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` (The listed denylist is the *minimum* contract. Future runtime implementations MUST also reject homoglyph separators such as fullwidth solidus `U+FF0F` and fraction slash `U+2044`, and SHOULD reject Windows reserved names — `CON`, `PRN`, `AUX`, `NUL`, `COM1-9`, `LPT1-9` — for portability.) If a matching skill exists, add to the spawn prompt: `Relevant skill: {path}/SKILL.md — read before starting.` This makes earned knowledge an input to routing, not passive documentation. diff --git a/packages/squad-cli/templates/squad.agent.md.template b/packages/squad-cli/templates/squad.agent.md.template index c29a4757e..7b19840a9 100644 --- a/packages/squad-cli/templates/squad.agent.md.template +++ b/packages/squad-cli/templates/squad.agent.md.template @@ -311,9 +311,11 @@ The routing table determines **WHO** handles work. After routing, use Response M 4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. 5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. -**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI already injects them as ambient context for every agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. +**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, use a hardlink or copy the directory into one of the 5 paths. -**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Normalize directory names to NFC Unicode form and trim trailing whitespace before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` +**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI injects them as ambient context for every CLI agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. (Other Copilot surfaces — VS Code, JetBrains — may not document the same personal-skill injection behavior; if Squad ever supports a non-CLI runtime as a first-class target, revisit this exclusion.) + +**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Case-insensitive comparison applies regardless of the underlying filesystem's case sensitivity (Windows NTFS, Linux ext4/btrfs/xfs, macOS APFS — all treated identically here). Normalize directory names to NFC Unicode form and trim leading and trailing whitespace, including zero-width characters (`U+200B`, `U+200C`, `U+200D`, `U+FEFF`), before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` (The listed denylist is the *minimum* contract. Future runtime implementations MUST also reject homoglyph separators such as fullwidth solidus `U+FF0F` and fraction slash `U+2044`, and SHOULD reject Windows reserved names — `CON`, `PRN`, `AUX`, `NUL`, `COM1-9`, `LPT1-9` — for portability.) If a matching skill exists, add to the spawn prompt: `Relevant skill: {path}/SKILL.md — read before starting.` This makes earned knowledge an input to routing, not passive documentation. diff --git a/packages/squad-sdk/templates/squad.agent.md.template b/packages/squad-sdk/templates/squad.agent.md.template index c29a4757e..7b19840a9 100644 --- a/packages/squad-sdk/templates/squad.agent.md.template +++ b/packages/squad-sdk/templates/squad.agent.md.template @@ -311,9 +311,11 @@ The routing table determines **WHO** handles work. After routing, use Response M 4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. 5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. -**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI already injects them as ambient context for every agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. +**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, use a hardlink or copy the directory into one of the 5 paths. -**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Normalize directory names to NFC Unicode form and trim trailing whitespace before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` +**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI injects them as ambient context for every CLI agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. (Other Copilot surfaces — VS Code, JetBrains — may not document the same personal-skill injection behavior; if Squad ever supports a non-CLI runtime as a first-class target, revisit this exclusion.) + +**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Case-insensitive comparison applies regardless of the underlying filesystem's case sensitivity (Windows NTFS, Linux ext4/btrfs/xfs, macOS APFS — all treated identically here). Normalize directory names to NFC Unicode form and trim leading and trailing whitespace, including zero-width characters (`U+200B`, `U+200C`, `U+200D`, `U+FEFF`), before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` (The listed denylist is the *minimum* contract. Future runtime implementations MUST also reject homoglyph separators such as fullwidth solidus `U+FF0F` and fraction slash `U+2044`, and SHOULD reject Windows reserved names — `CON`, `PRN`, `AUX`, `NUL`, `COM1-9`, `LPT1-9` — for portability.) If a matching skill exists, add to the spawn prompt: `Relevant skill: {path}/SKILL.md — read before starting.` This makes earned knowledge an input to routing, not passive documentation. diff --git a/templates/squad.agent.md.template b/templates/squad.agent.md.template index c29a4757e..7b19840a9 100644 --- a/templates/squad.agent.md.template +++ b/templates/squad.agent.md.template @@ -311,9 +311,11 @@ The routing table determines **WHO** handles work. After routing, use Response M 4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. 5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. -**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI already injects them as ambient context for every agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. +**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, use a hardlink or copy the directory into one of the 5 paths. -**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Normalize directory names to NFC Unicode form and trim trailing whitespace before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` +**Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI injects them as ambient context for every CLI agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. (Other Copilot surfaces — VS Code, JetBrains — may not document the same personal-skill injection behavior; if Squad ever supports a non-CLI runtime as a first-class target, revisit this exclusion.) + +**Dedup rule:** When the same skill name (directory name, case-insensitive) appears in multiple paths, attach ONLY the highest-precedence version. Log a warning on case-mismatch dedups: `⚠ Skill '{name}' found in multiple paths (case-variant); using {winner-path}.` Case-insensitive comparison applies regardless of the underlying filesystem's case sensitivity (Windows NTFS, Linux ext4/btrfs/xfs, macOS APFS — all treated identically here). Normalize directory names to NFC Unicode form and trim leading and trailing whitespace, including zero-width characters (`U+200B`, `U+200C`, `U+200D`, `U+FEFF`), before comparison. Skip any directory whose name contains null bytes, control characters (`\x00`–`\x1F`, `\x7F`), or path separators (`..`, `/`, `\`); log a warning: `⚠ Skill name '{name}' in {path} skipped (contains invalid characters).` (The listed denylist is the *minimum* contract. Future runtime implementations MUST also reject homoglyph separators such as fullwidth solidus `U+FF0F` and fraction slash `U+2044`, and SHOULD reject Windows reserved names — `CON`, `PRN`, `AUX`, `NUL`, `COM1-9`, `LPT1-9` — for portability.) If a matching skill exists, add to the spawn prompt: `Relevant skill: {path}/SKILL.md — read before starting.` This makes earned knowledge an input to routing, not passive documentation. From d38088aead17e4d1083911a124fbbf80f6b6405e Mon Sep 17 00:00:00 2001 From: tamirdresher Date: Wed, 3 Jun 2026 07:43:38 +0300 Subject: [PATCH 3/3] docs(skills): address Copilot-bot PR review nits on #1209 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drop hardlink suggestion (directory hardlinks not supported on Windows NTFS); recommend copy/vendor instead - Update HTML sync comment + changeset wording to clarify that Squad scans 5 directories total: Copilot CLI's 3 official project paths (.github/skills, .claude/skills, .agents/skills) plus Squad's 2 conventions (.squad/skills, .copilot/skills) — not all 5 are "official Copilot CLI paths" Reviewed by: copilot-pull-request-reviewer[bot] (PR #1209) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/skill-discovery-paths.md | 9 ++++++--- .github/agents/squad.agent.md | 4 ++-- .squad-templates/squad.agent.md | 4 ++-- packages/squad-cli/templates/squad.agent.md.template | 4 ++-- packages/squad-sdk/templates/squad.agent.md.template | 4 ++-- templates/squad.agent.md.template | 4 ++-- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/.changeset/skill-discovery-paths.md b/.changeset/skill-discovery-paths.md index 789b280e0..d5522bef1 100644 --- a/.changeset/skill-discovery-paths.md +++ b/.changeset/skill-discovery-paths.md @@ -3,9 +3,12 @@ "@bradygaster/squad-sdk": minor --- -Squad coordinator now scans all 5 official Copilot CLI project skill paths: -`.squad/skills/` > `.copilot/skills/` > `.github/skills/` > `.claude/skills/` -> `.agents/skills/` (in precedence order, dedup by directory name). Personal +Squad coordinator now scans all 5 project skill directories: Copilot CLI's 3 +official project paths — `.github/skills/`, `.claude/skills/`, `.agents/skills/` +(per https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-skills) +— plus Squad's existing conventions `.squad/skills/` and `.copilot/skills/`. +Precedence: `.squad/skills/` > `.copilot/skills/` > `.github/skills/` > +`.claude/skills/` > `.agents/skills/` (dedup by directory name). Personal paths (`~/.copilot/skills/`, `~/.agents/skills/`) are deliberately excluded from explicit routing — Copilot CLI injects them ambiently. This closes a gap where skills placed in `.github/skills/` (a common location alongside other diff --git a/.github/agents/squad.agent.md b/.github/agents/squad.agent.md index 7b19840a9..99e696794 100644 --- a/.github/agents/squad.agent.md +++ b/.github/agents/squad.agent.md @@ -303,7 +303,7 @@ The routing table determines **WHO** handles work. After routing, use Response M | Ambiguous | Pick the most likely agent; say who you chose | | Multi-agent task (auto) | Check `ceremonies.md` for `when: "before"` ceremonies whose condition matches; run before spawning work | - + **Skill-aware routing:** Before spawning, check ALL project skill directories in precedence order for skills relevant to the task domain: 1. `.squad/skills/` — **Team-earned skills** (highest precedence). Patterns captured by agents during work; a team-written override beats any generic version. 2. `.copilot/skills/` — **Project playbook.** Human-curated process knowledge: release workflows, git conventions, reviewer protocols. @@ -311,7 +311,7 @@ The routing table determines **WHO** handles work. After routing, use Response M 4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. 5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. -**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, use a hardlink or copy the directory into one of the 5 paths. +**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, copy or vendor the directory into one of the 5 paths (directory hardlinks are not portable — NTFS hardlinks are file-only on Windows). **Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI injects them as ambient context for every CLI agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. (Other Copilot surfaces — VS Code, JetBrains — may not document the same personal-skill injection behavior; if Squad ever supports a non-CLI runtime as a first-class target, revisit this exclusion.) diff --git a/.squad-templates/squad.agent.md b/.squad-templates/squad.agent.md index 7b19840a9..99e696794 100644 --- a/.squad-templates/squad.agent.md +++ b/.squad-templates/squad.agent.md @@ -303,7 +303,7 @@ The routing table determines **WHO** handles work. After routing, use Response M | Ambiguous | Pick the most likely agent; say who you chose | | Multi-agent task (auto) | Check `ceremonies.md` for `when: "before"` ceremonies whose condition matches; run before spawning work | - + **Skill-aware routing:** Before spawning, check ALL project skill directories in precedence order for skills relevant to the task domain: 1. `.squad/skills/` — **Team-earned skills** (highest precedence). Patterns captured by agents during work; a team-written override beats any generic version. 2. `.copilot/skills/` — **Project playbook.** Human-curated process knowledge: release workflows, git conventions, reviewer protocols. @@ -311,7 +311,7 @@ The routing table determines **WHO** handles work. After routing, use Response M 4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. 5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. -**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, use a hardlink or copy the directory into one of the 5 paths. +**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, copy or vendor the directory into one of the 5 paths (directory hardlinks are not portable — NTFS hardlinks are file-only on Windows). **Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI injects them as ambient context for every CLI agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. (Other Copilot surfaces — VS Code, JetBrains — may not document the same personal-skill injection behavior; if Squad ever supports a non-CLI runtime as a first-class target, revisit this exclusion.) diff --git a/packages/squad-cli/templates/squad.agent.md.template b/packages/squad-cli/templates/squad.agent.md.template index 7b19840a9..99e696794 100644 --- a/packages/squad-cli/templates/squad.agent.md.template +++ b/packages/squad-cli/templates/squad.agent.md.template @@ -303,7 +303,7 @@ The routing table determines **WHO** handles work. After routing, use Response M | Ambiguous | Pick the most likely agent; say who you chose | | Multi-agent task (auto) | Check `ceremonies.md` for `when: "before"` ceremonies whose condition matches; run before spawning work | - + **Skill-aware routing:** Before spawning, check ALL project skill directories in precedence order for skills relevant to the task domain: 1. `.squad/skills/` — **Team-earned skills** (highest precedence). Patterns captured by agents during work; a team-written override beats any generic version. 2. `.copilot/skills/` — **Project playbook.** Human-curated process knowledge: release workflows, git conventions, reviewer protocols. @@ -311,7 +311,7 @@ The routing table determines **WHO** handles work. After routing, use Response M 4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. 5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. -**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, use a hardlink or copy the directory into one of the 5 paths. +**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, copy or vendor the directory into one of the 5 paths (directory hardlinks are not portable — NTFS hardlinks are file-only on Windows). **Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI injects them as ambient context for every CLI agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. (Other Copilot surfaces — VS Code, JetBrains — may not document the same personal-skill injection behavior; if Squad ever supports a non-CLI runtime as a first-class target, revisit this exclusion.) diff --git a/packages/squad-sdk/templates/squad.agent.md.template b/packages/squad-sdk/templates/squad.agent.md.template index 7b19840a9..99e696794 100644 --- a/packages/squad-sdk/templates/squad.agent.md.template +++ b/packages/squad-sdk/templates/squad.agent.md.template @@ -303,7 +303,7 @@ The routing table determines **WHO** handles work. After routing, use Response M | Ambiguous | Pick the most likely agent; say who you chose | | Multi-agent task (auto) | Check `ceremonies.md` for `when: "before"` ceremonies whose condition matches; run before spawning work | - + **Skill-aware routing:** Before spawning, check ALL project skill directories in precedence order for skills relevant to the task domain: 1. `.squad/skills/` — **Team-earned skills** (highest precedence). Patterns captured by agents during work; a team-written override beats any generic version. 2. `.copilot/skills/` — **Project playbook.** Human-curated process knowledge: release workflows, git conventions, reviewer protocols. @@ -311,7 +311,7 @@ The routing table determines **WHO** handles work. After routing, use Response M 4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. 5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. -**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, use a hardlink or copy the directory into one of the 5 paths. +**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, copy or vendor the directory into one of the 5 paths (directory hardlinks are not portable — NTFS hardlinks are file-only on Windows). **Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI injects them as ambient context for every CLI agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. (Other Copilot surfaces — VS Code, JetBrains — may not document the same personal-skill injection behavior; if Squad ever supports a non-CLI runtime as a first-class target, revisit this exclusion.) diff --git a/templates/squad.agent.md.template b/templates/squad.agent.md.template index 7b19840a9..99e696794 100644 --- a/templates/squad.agent.md.template +++ b/templates/squad.agent.md.template @@ -303,7 +303,7 @@ The routing table determines **WHO** handles work. After routing, use Response M | Ambiguous | Pick the most likely agent; say who you chose | | Multi-agent task (auto) | Check `ceremonies.md` for `when: "before"` ceremonies whose condition matches; run before spawning work | - + **Skill-aware routing:** Before spawning, check ALL project skill directories in precedence order for skills relevant to the task domain: 1. `.squad/skills/` — **Team-earned skills** (highest precedence). Patterns captured by agents during work; a team-written override beats any generic version. 2. `.copilot/skills/` — **Project playbook.** Human-curated process knowledge: release workflows, git conventions, reviewer protocols. @@ -311,7 +311,7 @@ The routing table determines **WHO** handles work. After routing, use Response M 4. `.claude/skills/` — **Claude-ecosystem skills.** Vendor-specific path; less common in multi-tool projects. 5. `.agents/skills/` — **Generic agents path** (lowest project precedence). Least-specific convention. -**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, use a hardlink or copy the directory into one of the 5 paths. +**Traversal rule:** For each of the 5 directories above, (a) scan ONE level only — a skill is `{skill-dir}/{skill-name}/SKILL.md`; do NOT descend past a skill's top-level directory (nested `{skill-dir}/foo/bar/SKILL.md` is ignored); (b) SKIP symbolic links AND any other reparse points (NTFS junctions via `mklink /J`, mount points, and other Windows reparse-point types) — never follow them, even if the target appears to be inside the repo; (c) do NOT maintain a per-session cache — re-`readdir` on every spawn and rely on filesystem freshness (5 small directory listings is <5ms on any modern FS). **Rationale:** Windows compatibility (symlinks require elevated privileges or developer mode; reparse points are not POSIX symlinks and need a separate `FILE_ATTRIBUTE_REPARSE_POINT` check), defense against symlink-traversal attacks (a malicious or careless skill placing a symlink target like `../../.env` outside the repo would otherwise be read into a spawn prompt), and debugging simplicity (no stale-cache surprises when a user adds a skill mid-session). **Legitimate monorepo case:** a symlink like `.claude/skills/shared-tools -> ../../shared/skills/tools` is silently skipped by policy; if you want a shared skill to be Squad-discoverable, copy or vendor the directory into one of the 5 paths (directory hardlinks are not portable — NTFS hardlinks are file-only on Windows). **Personal paths not scanned:** `~/.copilot/skills/` and `~/.agents/skills/` are NOT scanned by Squad. Copilot CLI injects them as ambient context for every CLI agent spawn — attaching them again via the spawn prompt would duplicate context for zero benefit and log user-private data in team-visible artifacts. (Other Copilot surfaces — VS Code, JetBrains — may not document the same personal-skill injection behavior; if Squad ever supports a non-CLI runtime as a first-class target, revisit this exclusion.)