diff --git a/.changeset/skill-discovery-paths.md b/.changeset/skill-discovery-paths.md new file mode 100644 index 000000000..d5522bef1 --- /dev/null +++ b/.changeset/skill-discovery-paths.md @@ -0,0 +1,16 @@ +--- +"@bradygaster/squad-cli": minor +"@bradygaster/squad-sdk": minor +--- + +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 +`.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..99e696794 100644 --- a/.github/agents/squad.agent.md +++ b/.github/agents/squad.agent.md @@ -303,9 +303,19 @@ 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. + +**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.) + +**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/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..99e696794 100644 --- a/.squad-templates/squad.agent.md +++ b/.squad-templates/squad.agent.md @@ -303,9 +303,19 @@ 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. + +**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.) + +**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/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..99e696794 100644 --- a/packages/squad-cli/templates/squad.agent.md.template +++ b/packages/squad-cli/templates/squad.agent.md.template @@ -303,9 +303,19 @@ 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. + +**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.) + +**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/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..99e696794 100644 --- a/packages/squad-sdk/templates/squad.agent.md.template +++ b/packages/squad-sdk/templates/squad.agent.md.template @@ -303,9 +303,19 @@ 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. + +**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.) + +**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/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..99e696794 100644 --- a/templates/squad.agent.md.template +++ b/templates/squad.agent.md.template @@ -303,9 +303,19 @@ 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. + +**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.) + +**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.