diff --git a/.changeset/bump-otel-sdk-2x.md b/.changeset/bump-otel-sdk-2x.md new file mode 100644 index 000000000..06d0af4dd --- /dev/null +++ b/.changeset/bump-otel-sdk-2x.md @@ -0,0 +1,5 @@ +--- +"@bradygaster/squad-sdk": patch +--- + +deps: bump @opentelemetry/sdk-metrics and @opentelemetry/sdk-trace-node from 1.x to 2.8.0 diff --git a/.changeset/bundle-missing-skills-1289-1264.md b/.changeset/bundle-missing-skills-1289-1264.md new file mode 100644 index 000000000..87d4ad480 --- /dev/null +++ b/.changeset/bundle-missing-skills-1289-1264.md @@ -0,0 +1,39 @@ +--- +"@bradygaster/squad-cli": patch +"@bradygaster/squad-sdk": patch +--- + +Fix #1289, #1264: Bundle missing skills on `squad init` and strip fabricated provenance from tiered-memory SKILL.md + +**Problem** + +Two regressions shipped in v0.10.0 caused several skills to silently never reach users: + +1. **#1289** — `squad-commands` and `squad-version-check` were in `MANIFEST_SKILL_NAMES` (sdk-init.ts) but missing from `packages/squad-sdk/templates/skills/`. The install loop silently skipped them with `if (storage.existsSync(srcSkill))`, so every `squad init` produced an install with these two skills absent. The trigger phrase `squad commands` was a dead no-op for every v0.10.0 user. + +2. **#1264** — `tiered-memory`, `iterative-retrieval`, and `reflect` skill files existed in both templates dirs but were never added to `MANIFEST_SKILL_NAMES`, so they never installed either. Additionally, `tiered-memory/SKILL.md` claimed `confidence: high` and `source: earned (production measurements in tamirdresher/tamresearch1, 34-74KB baseline payloads)` — the referenced repository does not exist and the measurement table at the bottom of the SKILL contained fabricated numbers. The SKILL also referenced `docs/tiered-memory-guide.md`, which does not exist. + +3. The previously-merged cross-squad fix (#1291) updated `cross-squad/SKILL.md` content but never added it to `MANIFEST_SKILL_NAMES`, so it also never installed. + +**Fix** + +- **Source of truth.** Added the 5 missing skill directories to `.squad/skills/` (`squad-commands`, `squad-version-check`, `tiered-memory`, `iterative-retrieval`, `reflect`). The pre-existing `sync-skill-templates.mjs` runs in `prebuild` and propagates them to both `packages/squad-cli/templates/skills/` and `packages/squad-sdk/templates/skills/`. + +- **`MANIFEST_SKILL_NAMES`** grew from 10 → 14 entries: added `tiered-memory`, `iterative-retrieval`, `reflect`, `cross-squad`. + +- **Anti-regression guard.** The install loop in `sdk-init.ts` now collects missing skill source dirs and `throw`s with a clear remediation message (`Run \`node scripts/sync-skill-templates.mjs\``) instead of silently skipping. This is what would have surfaced #1289 at build time instead of in user installs. + +- **Provenance honesty.** `tiered-memory/SKILL.md` rewritten to: + - Frontmatter: `confidence: design (runtime not yet implemented)` + `source: design proposal` + - Added prominent "Status (v0.10.0)" callout linking to #1264 for the runtime gap + - Removed the fabricated `tamirdresher/tamresearch1` measurement table + - Removed reference to non-existent `docs/tiered-memory-guide.md` + - References section now points to real issues (#1264, #686, #600) + +- **Test guard.** New regression test in `test/init.test.ts` (`should install every manifest-curated skill`) asserts every entry in the expected manifest list ends up at `.copilot/skills/{name}/SKILL.md` after `initSquad()`. + +**Out of scope (tracked separately)** + +The tiered-memory runtime (storage scaffolding under `.squad/memory/hot|cold|wiki/`, Scribe promotion logic, spawn-template tier-aware reads) remains tracked in #1264. This change lands the install-time fixes; runtime work follows in a separate PR. + +The comprehensive `cross-squad-communication` plugin from `tamirdresher/squad-skills` is also separate; this change just adds the existing upstream `cross-squad` skill to the manifest so users get the registry-aware version that landed with #1291. diff --git a/.changeset/casting-identity-not-state.md b/.changeset/casting-identity-not-state.md new file mode 100644 index 000000000..569abea80 --- /dev/null +++ b/.changeset/casting-identity-not-state.md @@ -0,0 +1,5 @@ +--- +"@bradygaster/squad-sdk": patch +--- + +docs: clarify in `init.ts` and `.gitattributes` / `.gitignore` generators that `.squad/casting/*` files are authoritative identity (registry + history), NOT mutable two-layer state. They belong on `main`, must be committed, and should NOT receive a `merge=union` driver or be added to `.gitignore`. Pure comment additions — no runtime behavior change. diff --git a/.changeset/cli-upgrade-fixes.md b/.changeset/cli-upgrade-fixes.md new file mode 100644 index 000000000..159088d0a --- /dev/null +++ b/.changeset/cli-upgrade-fixes.md @@ -0,0 +1,12 @@ +--- +"@bradygaster/squad-cli": patch +"@bradygaster/squad-sdk": patch +--- + +Fix CLI and upgrade bugs: +- `squad externalize` and `squad internalize` now appear in `squad --help` output (#1050) +- `squad build` writes files to the externalized state directory when applicable (#1048) +- `squad new agent` correctly adds agent definitions to `squad.config.ts` (#1047) +- `squad upgrade` backs up customized `squad.agent.md` before overwriting; supports `--dry-run` (#1052) +- Explicit `@agent` mentions bypass direct-response handler and route to named agent (#1029) +- `state-mcp` server uses lazy initialization to avoid blocking multi-MCP startup (#1353) diff --git a/.changeset/conditional-state-gitignore.md b/.changeset/conditional-state-gitignore.md new file mode 100644 index 000000000..48b5f0db6 --- /dev/null +++ b/.changeset/conditional-state-gitignore.md @@ -0,0 +1,10 @@ +--- +"@bradygaster/squad-sdk": patch +"@bradygaster/squad-cli": patch +--- + +fix: conditional .gitignore entries for two-layer/orphan state backends + +When the state backend is `two-layer` or `orphan`, `squad init` and `squad upgrade` now add `.squad/decisions.md` and `.squad/agents/*/history.md` to `.gitignore` (delimited by `# Squad: state owned by squad-state branch` marker comments). When the backend is switched back to `local`, the marker block is removed so those files become committable again. + +Defense-in-depth complement to the existing pre-commit hook — prevents `git add .`, IDE "stage all", and `git add -A` from silently staging two-layer state into the working tree. diff --git a/.changeset/docs-platform-path-fixes.md b/.changeset/docs-platform-path-fixes.md new file mode 100644 index 000000000..d2be7342c --- /dev/null +++ b/.changeset/docs-platform-path-fixes.md @@ -0,0 +1,6 @@ +--- +"@bradygaster/squad-sdk": patch +"@bradygaster/squad-cli": patch +--- + +Fix platform-specific path examples in personal squad docs, JSDoc, and bundled skill templates. diff --git a/.changeset/feat-preset-install.md b/.changeset/feat-preset-install.md new file mode 100644 index 000000000..4c47d0c29 --- /dev/null +++ b/.changeset/feat-preset-install.md @@ -0,0 +1,60 @@ +--- +"@bradygaster/squad-cli": minor +"@bradygaster/squad-sdk": minor +--- + +feat: add `squad preset install ` for sharing presets via repo URL or local path (#1224) + +Closes #1224. Adds a new subcommand that installs a single preset from a GitHub URL or local path into `$SQUAD_HOME/presets//` — the peer-to-peer preset sharing flow that was missing in v0.10.0. + +### CLI + +```bash +# From a GitHub repo (preset at /preset.json or /presets//) +squad preset install https://github.com/tamir/my-presets#my-awesome-team + +# From a sub-path +squad preset install https://github.com/tamir/my-presets/tree/main/presets/my-awesome-team + +# From SSH URL +squad preset install git@github.com:tamir/my-presets.git#my-awesome-team + +# From a local path +squad preset install ./my-awesome-team + +# Override the installed name +squad preset install https://github.com/tamir/my-presets#my-awesome-team --name corp-team + +# Overwrite an existing preset +squad preset install --force +``` + +After install, the preset is a normal entry in `$SQUAD_HOME/presets/` — `squad preset list`, `squad preset apply `, and `squad init --preset ` all work as today. + +### Why + +The existing `squad preset init --remote` flow is for syncing your *whole* `$SQUAD_HOME` repo across machines (single-user, multi-machine use case). There was no built-in way to install just *one* preset from someone else's repo. Users had to: +1. Manually clone the repo to a temp dir +2. `cp -r /presets/ $SQUAD_HOME/presets/` +3. Then `squad preset apply ` + +…or hijack `SQUAD_HOME` (which collides with their own personal squad config). + +### What it does + +1. Resolves source — GitHub URL → shallow `git clone --depth 1` to a temp dir; local path → use as-is +2. Locates the preset within the source via 3 patterns: + - Source dir contains `preset.json` → single-preset source + - Source dir contains a `presets/` collection → require `--name` (or `#name` fragment) to pick + - Source dir IS the `presets/` dir → require `--name`, or auto-pick if only one preset +3. Validates the preset's `preset.json` manifest (name, agents[]) before any destructive action +4. Verifies `agents/` directory exists +5. Copies `preset.json` (with optional rename) + `agents/` into `$SQUAD_HOME/presets//` +6. Fail-stops if destination exists, unless `--force` is passed +7. Cleans up the temp clone whether install succeeds or fails + +### What's NOT in scope (deferred to follow-ups) + +- `squad preset uninstall` — `rm -rf $SQUAD_HOME/presets/` works for now +- `squad preset update ` to pull a fresh version from origin +- Public preset registry / discovery catalog diff --git a/.changeset/fix-1126-skills-to-github-skills.md b/.changeset/fix-1126-skills-to-github-skills.md new file mode 100644 index 000000000..e44599011 --- /dev/null +++ b/.changeset/fix-1126-skills-to-github-skills.md @@ -0,0 +1,68 @@ +--- +"@bradygaster/squad-sdk": minor +"@bradygaster/squad-cli": minor +--- + +Move bundled skills from `.copilot/skills/` to `.github/skills/` so they're visible to all Copilot surfaces (closes #1126) + +## Symptom (per #1126) + +Squad-bundled skills installed at `.copilot/skills/` are **invisible to every Copilot surface except Squad itself**: +- ❌ GitHub Copilot cloud agent +- ❌ Copilot CLI (outside Squad sessions) +- ❌ VS Code Copilot extension (agent mode) +- ❌ `@copilot` coding agent on issues +- ❌ Any future Copilot surface + +Per the [official Agent Skills docs](https://docs.github.com/en/copilot/concepts/agents/about-agent-skills), [add-skills docs](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/cloud-agent/add-skills), and [VS Code docs](https://code.visualstudio.com/docs/copilot/customization/agent-skills), the canonical **project-level** custom-skills location is `.github/skills/`. `.copilot/skills/` at repo root is not recognized by any Copilot surface — the home-directory equivalent `~/.copilot/skills/` IS recognized for **personal** skills, which is the source of the original mistake. + +## Fix + +1. **`squad init`** writes bundled skills to `.github/skills/{name}/SKILL.md` (was `.copilot/skills/`). +2. **`squad upgrade`** does the same AND auto-migrates legacy `.copilot/skills/{manifest-skill}/` into `.github/skills/{manifest-skill}/` (best-effort, preserves user-added non-manifest skills at `.copilot/skills/`, tombstones the legacy dir when empty). +3. **`TEMPLATE_MANIFEST`** destinations rewritten: all 10 skill entries now target `../.github/skills/` instead of `../.copilot/skills/`. +4. **`ENSURE_DIRECTORIES`** (upgrade.ts) updated so existing squads get `.github/skills/` created on upgrade. +5. **squad.agent.md** narrative updated: 5-path scan order now lists `.github/skills/` as path #2 (Copilot CLI's canonical custom-skills location) and `.copilot/skills/` as path #3 (Legacy install path; `squad upgrade` migrates). Personal scope (`~/.copilot/skills/`) preserved as-is. +6. **All other docs** (`spawn-reference.md`, `README.md`, `squad-commands` skill, `release-process` skill, `build.ts`, SDK type comments) updated to reference `.github/skills/` as the install destination. + +## Migration semantics (`squad upgrade`) + +`migrateLegacyCopilotSkills()` runs **before** `syncAllSkills`: + +| Scenario | Migration action | User-added skills at `.copilot/skills/` | +|---|---|---| +| `.copilot/skills/{manifest-skill}/` exists, `.github/skills/{manifest-skill}/` does NOT | Move legacy → new; remove legacy | Untouched | +| `.copilot/skills/{manifest-skill}/` exists AND `.github/skills/{manifest-skill}/` exists | Tombstone legacy (new wins) | Untouched | +| `.copilot/skills/my-custom-skill/` (NOT in TEMPLATE_MANIFEST) | Left alone | Preserved | +| `.copilot/skills/` becomes empty after migration | Directory removed | n/a | + +All migration steps are best-effort with try/catch — disk-write failures don't block upgrade. + +## Tests + +New regression tests: +- `test/init.test.ts > should install Squad-bundled skills at .github/skills/...` — asserts canonical path, asserts legacy path is NOT created +- `test/cli/upgrade.test.ts > should migrate manifest skills from .copilot/skills/ to .github/skills/` — asserts manifest skill moves, user-added skill preserved +- `test/cli/upgrade.test.ts > should NOT clobber a customized .github/skills/{name} if the legacy copy exists` — asserts both-locations case tombstones legacy without losing the new + +Updated existing tests: +- `test/builtin-skills.test.ts` regex now matches `.github/skills/` +- `test/cli/init.test.ts`, `test/init.test.ts`, `test/init-sdk.test.ts`, `test/cli/upgrade.test.ts`, `test/human-journeys.test.ts`, `test/repl-ux-fixes.test.ts`, `test/cli/init-upgrade-parity.test.ts` — install-path assertions updated to `.github/skills/` + +**188/188 init/upgrade/builtin tests pass; `npm run lint` clean.** + +## What's NOT changed (intentional) + +- **`.copilot/skills/` scan path stays in `squad.agent.md`'s 5-path skill discovery** — the coordinator still discovers user-added skills at the legacy location for backward compat; only Squad-installed (manifest) skills migrate. +- **`~/.copilot/skills/` (personal scope) is unchanged** — that's Copilot CLI's official personal-skills location and remains valid. +- **`test/skill-source.test.ts`, `test/skills-export-import.test.cjs`, `test/tools.test.ts`, `test/skill-script-loader.test.ts`** are NOT touched — they test the runtime skill loader and tool behavior, which still supports `.copilot/skills/` as a valid scan path. + +## Composability + +- **Disjoint from #1292, #1293, #1295, #1297, #1298, #1300, #1301, #1302, #1303** — touches the same `TEMPLATE_MANIFEST` array as #1292/#1295/#1303 but only modifies the destination path, not the source or entry list. Conflicts on `MANIFEST_SKILL_NAMES` are trivial: take the union of skill names with the `../.github/skills/` destination from this PR. +- This is the **canonical fix** for #1126; closes that issue. + +## Out of scope (separate follow-ups) + +- Backward-compat shim that adds a `.copilot/skills -> .github/skills` symlink. Not needed because users won't be looking at `.copilot/skills/` anymore once their tools find skills at `.github/skills/`. Filed as a follow-up if anyone reports broken muscle memory. +- A `squad doctor --skills` check that warns when `.copilot/skills/` still has manifest skills after upgrade (suggesting the migration silently failed). Worth adding to the next maintenance pass. diff --git a/.changeset/fix-1296-stop-mcp-home-pollution.md b/.changeset/fix-1296-stop-mcp-home-pollution.md new file mode 100644 index 000000000..cf8f38fec --- /dev/null +++ b/.changeset/fix-1296-stop-mcp-home-pollution.md @@ -0,0 +1,40 @@ +--- +"@bradygaster/squad-cli": patch +--- + +Fix #1296: stop writing `squad_state_` to `~/.copilot/mcp-config.json` on every `squad init` / `squad upgrade` + +`squad init` (line 408 of `packages/squad-cli/src/cli/core/init.ts`) and `squad upgrade` (line 738 of `upgrade.ts`) unconditionally called `ensureSquadStateMcpInUserConfig`, which wrote a `squad_state_` entry to `~/.copilot/mcp-config.json` keyed by a stable hash of the project path. Every new `squad init` accumulated another entry in HOME with no garbage collection. + +This contradicted the explicit iter-8 design intent documented at `packages/squad-cli/src/cli/core/mcp-root.ts:1-27`: + +> *iter-7: wrote `squad_state_` into the user's HOME `~/.copilot/mcp-config.json`. That polluted HOME with one entry per Squad project and required a stale-entry GC that we never built. It also touched a file outside the project, which is surprising for `squad init` / `squad upgrade`.* +> +> *iter-8 flips it back inside the project: we write `squad_state` to a repo-root `.mcp.json` ... **No HOME modifications.*** + +The repo-root `.mcp.json` writes (init.ts:403 / upgrade.ts:728) already cover all documented Copilot CLI launch modes: + +- ``copilot`` from the project root → walks up from cwd to git root, auto-loads `.mcp.json` +- ``copilot -p`` from the project root → same `.mcp.json` is found + +For ``copilot -p`` invocations launched from **outside** the project root, the right pattern is `--additional-mcp-config @.mcp.json` (already documented as the recommended pattern at init.ts:494). + +**Changes** + +- Removed the unconditional `ensureSquadStateMcpInUserConfig` call from `init.ts:408` and `upgrade.ts:738`. Replaced both with comments explaining the iter-8 design and pointing at #1296. +- Removed the now-unused import from both files. +- The function definition itself (`mcp-root.ts:178-228`) is **kept** — a future `squad doctor --mcp-prune` cleanup helper may want to inspect HOME for orphan entries. It's just no longer called from the init/upgrade auto-flow. + +**Tests** + +New regression test in `test/cli/init.test.ts`: + +> `should NOT write any squad_state entries to ~/.copilot/mcp-config.json (regression: #1296)` + +Isolates the developer's real HOME by setting `USERPROFILE`/`HOME` to a temp dir before init, then asserts no `squad_state*` keys appear under that temp HOME after init. The test passes only because the unconditional write is gone — previously this assertion would have failed. + +All 40 existing init tests still pass; `npm run lint` clean. + +**Cleanup** + +A follow-up could add `squad doctor --mcp-prune` to walk `~/.copilot/mcp-config.json`, find `squad_state_` entries whose target directory no longer exists, and remove them. Out of scope for this fix. diff --git a/.changeset/fix-1299-fact-checker-full-plumbing.md b/.changeset/fix-1299-fact-checker-full-plumbing.md new file mode 100644 index 000000000..bbe9ff5c2 --- /dev/null +++ b/.changeset/fix-1299-fact-checker-full-plumbing.md @@ -0,0 +1,77 @@ +--- +"@bradygaster/squad-cli": minor +"@bradygaster/squad-sdk": minor +--- + +Fix #1299 (deep): Fact Checker gets the same plumbing as Rai — rich charter at init + state directory + policy template + +PR #1300 (also #1299) fixed the documentation gap so the coordinator knows to roster Fact Checker. This PR fixes the **structural** gap behind it. Per user testing on 2026-06-13, even after #1300 the actual agent on disk is still a "name on disk with a 21-line placeholder" — three concrete problems: + +| Piece | Rai before this PR | Fact Checker before this PR | +|-------|--------------------|------------------------------| +| `charter.md` at init | Generic 478-byte stub from `generateCharter()` | Generic 523-byte stub from `generateCharter()` | +| Rich charter template usage | Only used by `squad upgrade` (never by `squad init`) | Same — never used by `squad init` | +| `.squad/{name}/policy.md` | ✅ Seeded from `rai-policy.md` (4160 bytes) | ❌ Directory does not exist | +| `.squad/{name}/audit-trail.md` | ✅ Seeded with append-only header | ❌ Directory does not exist | +| `merge=union` in `.gitattributes` | ✅ `.squad/rai/audit-trail.md` | ❌ No entry for fact-checker | +| `fact-checker-charter.md` distribution | n/a | Only in `packages/squad-cli/templates/` — missing from `.squad-templates/` (canonical source) AND `packages/squad-sdk/templates/`, so the SDK init path could never find it | + +## Fix (4 parts) + +### Part 1 — Rich charter at init (benefits BOTH Rai and Fact Checker) + +`packages/squad-sdk/src/config/init.ts` agent loop now looks up `{templatesDir}/{role}-charter.md` for each agent and uses that as `charter.md` content if it exists. Falls back to `generateCharter()` for user-defined agents that have no rich template (everyone except the built-ins). + +Result: a fresh `squad init` produces `.squad/agents/Rai/charter.md` with the full Rai charter (4525 bytes) and `.squad/agents/fact-checker/charter.md` with the full Fact Checker charter (3024 bytes). Previously both were 478-byte stubs. + +### Part 2 — `.squad/fact-checker/` state dir, mirroring `.squad/rai/` + +Added a new block in `init.ts` (right after the Rai seeding at lines 879–941) that creates: + +- `.squad/fact-checker/policy.md` — seeded from `templates/fact-checker-policy.md` (or a minimal inline fallback if the template is stripped) +- `.squad/fact-checker/audit-trail.md` — seeded with an append-only header + +The policy template (`.squad-templates/fact-checker-policy.md`, ~6 KB) is the canonical authority for the dual-mode operating rules per #789 + #1254: + +- **Mode 1 Verification:** confidence rating taxonomy (✅/⚠️/❌/🔍), what gets checked (URLs, packages, APIs, file paths, signatures, quotes, statistics, cross-references) +- **Mode 2 Devil's Advocate:** required brief structure (steelman → assumptions → pre-mortem → alternatives → risk acceptance) +- **Hard rules:** anti-fabrication guarantees — never cite unverified URL/package/API, never invent measurement data, never fabricate counter-hypotheses, never block on opinion +- **Advisory by default** with two narrow blocking exceptions (❌ at Pre-Ship; coordinator-escalated DA risk) +- **Opt-out model** mirroring Rai's +- **Audit trail rules** — succinct (verdict + citation, never raw source material) +- **Reviewer Rejection Protocol integration** for ❌ Contradicted verdicts + +### Part 3 — Fix the `.squad-templates/` distribution gap + +The existing `fact-checker-charter.md` had been added directly to `packages/squad-cli/templates/` only, bypassing the canonical `.squad-templates/` source. That meant `sync-templates.mjs` couldn't propagate it to `packages/squad-sdk/templates/` (which `getSDKTemplatesDir()` resolves at runtime), so the SDK init code path could never find the rich charter even if it tried. + +Fix: copied `fact-checker-charter.md` to `.squad-templates/` and re-synced. Now all 4 mirror targets have it. This unblocks Part 1. + +### Part 4 — Plumbing updates + +- `.gitattributes` block: added `.squad/fact-checker/audit-trail.md merge=union` alongside Rai's existing entry +- `packages/squad-cli/src/cli/core/templates.ts`: new `TEMPLATE_MANIFEST` entry for `fact-checker-policy.md → templates/fact-checker-policy.md` so `squad upgrade` propagates it +- `.squad-templates/squad.agent.md` Files Catalog table: 2 new rows for `.squad/fact-checker/policy.md` (authoritative) and `.squad/fact-checker/audit-trail.md` (derived/append-only) + +## Tests + +`test/init.test.ts` gains 3 regression tests (28/28 pass total): + +1. `should seed .squad/fact-checker/{policy,audit-trail}.md (regression: bradygaster/squad#1299)` — asserts policy declares both modes + anti-fabrication rules + confidence ratings; audit trail is append-only. +2. `should use the rich fact-checker-charter.md template for built-in agents at init (#1299)` — asserts the rendered charter is > 1 KB (not the 478-byte stub) and contains Verification Methodology + Confidence Ratings. +3. `should use the rich Rai-charter.md template at init (companion to fact-checker fix, #1299)` — asserts Rai gets the same treatment; charter references `.squad/rai/policy.md` and `.squad/rai/audit-trail.md`. + +`npm run lint` clean. + +## Composability + +This PR builds on #1300 (which adds the `## Fact Checker` section to `squad.agent.md` and the "team size" line fix). Both modify `.squad-templates/squad.agent.md` but in **disjoint regions**: + +- #1300 touches the team-size line at L56 and inserts a new `## Fact Checker` section before `## PRD Mode` +- #1301 touches the Files Catalog table at L710-711 + +They will merge cleanly in either order. The user-facing experience requires BOTH to land for full plumbing. + +## Closes / refs + +Closes #1299 (deep fix; #1300 was the surface fix). diff --git a/.changeset/fix-1299-fact-checker-roster-instructions.md b/.changeset/fix-1299-fact-checker-roster-instructions.md new file mode 100644 index 000000000..46ca9339e --- /dev/null +++ b/.changeset/fix-1299-fact-checker-roster-instructions.md @@ -0,0 +1,54 @@ +--- +"@bradygaster/squad-cli": patch +"@bradygaster/squad-sdk": patch +--- + +Fix #1299: tell the coordinator to roster Fact Checker (and Ralph + Rai explicitly) in the casting flow + +When a user runs `squad init` followed by `copilot --agent squad`, the coordinator's first-time casting flow correctly creates `.squad/agents/fact-checker/` on disk (per merged PR #1223) but **omits Fact Checker from the `## Members` table** in `.squad/team.md`. Rai is included, Ralph is included, Scribe is included — Fact Checker is silently dropped. + +## Root cause + +`.squad-templates/squad.agent.md` (the canonical coordinator instructions, mirrored to all package templates) had two gaps: + +1. **Line 56** said *"Determine team size (typically 4–5 + Scribe)"* — naming only Scribe among the always-on built-ins. +2. The Rai section (line 884) explicitly told the coordinator *"Rai always appears in team.md: `| Rai | RAI Reviewer | ...`"*, but **no equivalent section existed for Fact Checker**. + +So the model added Rai correctly (saw the roster-entry instruction) but had no instruction to add Fact Checker, even though the agent directory was scaffolded. + +## Fix + +1. Updated the team-size line to *"typically 4–5 + Scribe + Ralph + Rai + Fact Checker — the 4 always-on built-ins, see their dedicated sections below"*. +2. Added a full `## Fact Checker — Verification & Devil's Advocate` section that mirrors the Rai structure: + - Declares the single-agent dual-mode design (per #789 + #1254) — *"single agent, two modes"* + - Explicit **Roster Entry** line: *"Fact Checker always appears in `team.md`: `| Fact Checker | Fact Checker | .squad/agents/fact-checker/charter.md | 🔍 Verifier |`"* + - Trigger phrase table for both Verification mode and Devil's Advocate mode + - Confidence rating taxonomy (✅/⚠️/❌/🔍) + - DA brief structure (steelman, assumptions, pre-mortem, alternatives, risk acceptance) + - Boundaries, background-mode default, state location + +## Changes + +- `.squad-templates/squad.agent.md` (canonical source) updated. +- `sync-templates.mjs --sync` propagates to all 4 mirror targets: + - `templates/squad.agent.md.template` + - `packages/squad-cli/templates/squad.agent.md.template` + - `packages/squad-sdk/templates/squad.agent.md.template` + - `.github/agents/squad.agent.md` (this repo's own coordinator file) + +## Test coverage + +New `test/squad-agent-roster.test.ts` runs against **all 4 template targets** and asserts: + +- The "Determine team size" line mentions Scribe, Ralph, Rai, **and** Fact Checker +- A `## Fact Checker` section exists with an explicit "always appears in team.md" roster-entry line +- The Fact Checker section declares dual operating mode (Verification + Devil's Advocate) — anchors the design from #789 + #1254 so a future PR can't accidentally split them again (cf. closed PR #1294) +- The Ralph + Rai sections are still present and unmodified + +All 40 existing init/cli/init tests still pass; `npm run lint` clean. + +## Verification + +After this PR ships, a fresh `squad init` + `copilot --agent squad` produces a `team.md ## Members` table that includes Fact Checker alongside Scribe, Ralph, and Rai. + +Closes #1299 diff --git a/.changeset/fix-1305-state-backend-handshake.md b/.changeset/fix-1305-state-backend-handshake.md new file mode 100644 index 000000000..40af92ac4 --- /dev/null +++ b/.changeset/fix-1305-state-backend-handshake.md @@ -0,0 +1,67 @@ +--- +"@bradygaster/squad-cli": minor +"@bradygaster/squad-sdk": minor +--- + +Fix #1305: coordinator must probe for `squad_state` / `memory.*` tools before writing state, and hard-refuse writes when the bridge isn't reachable on non-local backends + +## Symptom + +A coordinator session against a Squad with `stateBackend: "two-layer"` wrote `.squad/decisions.md` + four `.squad/agents/{name}/history.md` files + a `.squad/decisions/inbox/copilot-directive-…md` via raw `create`/`edit` tools — never calling any `squad_state_*` or `memory.*` tool. The pre-commit hook caught the contract violation; the agent treated it as a "git problem" instead of the symptom it was. + +## Root cause (two failure modes stacked) + +**Mechanical (Copilot CLI):** Copilot CLI loads MCP server tools lazily — they're registered (via `.mcp.json`) but not always advertised in the model's initial function list. Models have to use `tool_search_tool_regex` to find them or guess they exist. The `squad_state` MCP server is correctly registered with `tools: ["*"]` and responds to `initialize`/`tools/list` over stdio — but the model's tool block at session start may not include them. This is a Copilot CLI architectural choice (lazy discovery vs. preload), not something Squad can fix server-side. + +**Behavioral (squad.agent.md):** The pre-1305 prompt said *"When memory tools are available, use them before writing durable memory by hand"* and *"If memory tools are not available, fall back to squad_decide or squad_state_write"*. Models read `"available"` as `"listed in my tool prompt"` instead of `"after probing to find out"`. There was no hard refusal clause for the case where the agent is about to violate the state-backend contract. + +## Fix + +Two changes to `.squad-templates/squad.agent.md` (synced to all 4 mirror targets): + +### 1. New "State-backend handshake" section (MANDATORY, runs once per session) + +Inserted right after the `stateBackend` resolution at L131. Steps: + +1. If `STATE_BACKEND ∈ {"local", "worktree"}`: file ops on `.squad/` are valid; skip the probe. +2. Otherwise: probe for `squad_state_health` via `tool_search_tool_regex` (or equivalent tool-discovery mechanism). On success, call `squad_state_health` once to confirm the bridge answers. +3. **If the probe fails**: HALT before any state write. Output a precise error to the user (verbatim text in the template): *"Squad's runtime state bridge is missing for backend `{STATE_BACKEND}`. The `squad_state` MCP server in `.mcp.json` is not reachable in this Copilot session. Restart Copilot CLI so `.mcp.json` is loaded, or change `stateBackend` to `local` in `.squad/config.json`."* + +### 2. Replaced soft "if not available" language with a HARD RULE in `### Memory Governance Tools` + +Lists the runtime-owned paths that are FORBIDDEN to write via `create`/`edit`/`write_file` on non-local backends when the bridge isn't reachable: + +- `.squad/decisions.md` +- `.squad/decisions/inbox/**` +- `.squad/agents/*/history.md` +- `.squad/casting/*.json` +- `.squad/identity/*.md` +- `.squad/memory/**` +- `.squad/orchestration-log/**` +- `.squad/log/**` +- `.squad/rai/audit-trail.md` +- `.squad/fact-checker/audit-trail.md` + +Clarified the local-backend carve-out so `STATE_BACKEND ∈ {"local", "worktree"}` users still freely use `create`/`edit`/`write_file` on `.squad/`. + +Also clarified that `memory.*` and `squad_state_*` share the same MCP server (they're aliases in the same registry — see `packages/squad-cli/src/cli/commands/state-mcp.ts`) so models stop treating them as separate availability checks. + +## Tests + +New `test/state-backend-handshake.test.ts` runs against all 4 template mirror targets and asserts (20 tests = 5 assertions × 4 files): + +- Mandatory handshake section exists with "every session" + "before any state mutation" timing +- Probe step exists with `squad_state_health` + tool-discovery mechanism +- HALT step exists with restart-CLI + change-stateBackend remediation +- HARD RULE exists listing the forbidden paths + `create`/`edit`/`write_file` tools by name +- Local/worktree carve-out preserved + +20/20 pass; `npm run lint` clean. + +## What's NOT in scope (filed/tracked separately) + +**Server-side fix (auto-preload MCP tools):** The mechanical root cause is a Copilot CLI feature — preload MCP server tools into the model's function list at session start instead of behind lazy discovery. Out of Squad's control. If maintainers agree it's worth pursuing, file as a feature request against `github/copilot-cli`. With this PR's prompt-level enforcement in place, the server-side fix becomes a nice-to-have rather than load-bearing. + +**Skill reinforcement (init-mode, agent-conduct):** Could repeat the rules in `.squad/skills/init-mode/SKILL.md` and `.squad/skills/agent-conduct/SKILL.md` as defense-in-depth for users who skip `squad upgrade`. Worth a small follow-up PR; not blocking. + +Closes #1305 diff --git a/.changeset/fix-cli-sdk-workspace-pin.md b/.changeset/fix-cli-sdk-workspace-pin.md new file mode 100644 index 000000000..70050760a --- /dev/null +++ b/.changeset/fix-cli-sdk-workspace-pin.md @@ -0,0 +1,5 @@ +--- +"@bradygaster/squad-cli": patch +--- + +Force the CLI's squad-sdk dependency through the repo workspace by using a root file dependency plus an npm override for the CLI package. This keeps fresh installs linked to the local SDK prerelease instead of pulling the stale published SDK. Closes #1405. diff --git a/.changeset/fix-coordinator-inline-dispatch-gate.md b/.changeset/fix-coordinator-inline-dispatch-gate.md new file mode 100644 index 000000000..5871a8941 --- /dev/null +++ b/.changeset/fix-coordinator-inline-dispatch-gate.md @@ -0,0 +1,24 @@ +--- +"@bradygaster/squad-cli": patch +"@bradygaster/squad-sdk": patch +--- + +Fix coordinator inline-dispatch regression: restore always-on dispatch gate in the Squad coordinator template + +**Problem** + +In v0.10.0 the Squad coordinator started doing domain work itself instead of dispatching to roster agents (it worked in v0.9.4). Root cause: commit `afe78188` (#1035, "context overflow sentinel + coordinator size reduction") relocated the concrete inline-dispatch gate and the dispatch mechanics out of the always-on coordinator prompt (`.squad-templates/squad.agent.md`) into lazy-loaded reference files (`client-compatibility-reference.md`, `spawn-reference.md`). The remaining inline one-liner ("inline work is last-resort fallback only") was too soft, and the concrete "when may I work inline?" rule + the VS Code `runSubagent` how-to were no longer in-context, so the coordinator defaulted to executing work itself. + +**Fix** + +Three minimal edits to the canonical coordinator template (`.squad-templates/squad.agent.md`), synced to all mirrors via `npm run sync-templates`: + +1. **Client Compatibility** — replaced the soft one-liner with an explicit **Inline-dispatch gate**: doing domain work inline is permitted ONLY in Direct Mode, or when NEITHER `task` NOR `runSubagent` is available; otherwise the coordinator MUST dispatch. +2. **How to Spawn an Agent** — added a one-line **STOP gate**: about to produce a domain artifact with no `task` / `runSubagent` call this turn → STOP and dispatch (exceptions: Direct Mode, or no spawn tool exists). +3. Re-inlined a ~5-line **VS Code `runSubagent` micro-playbook** so the how-to-dispatch mechanics are always-on instead of lazy-loaded. + +The legitimate Direct/Lightweight response modes are preserved (Lightweight still spawns one agent; Direct still answers from context without spawning). + +**Validation** + +New deterministic, subprocess-free regression test `test/coordinator-inline-dispatch-gate.test.ts` asserts the gate/STOP-gate/micro-playbook exist in canonical and that the gate is present in all 5 synced copies (parity). Red before the fix (8/8 failing), green after. Existing requires a `squad upgrade` for existing installs to pick up the new template. diff --git a/.changeset/fix-copilot-sdk-type-compat.md b/.changeset/fix-copilot-sdk-type-compat.md new file mode 100644 index 000000000..05fd158c7 --- /dev/null +++ b/.changeset/fix-copilot-sdk-type-compat.md @@ -0,0 +1,12 @@ +--- +"@bradygaster/squad-sdk": patch +--- + +fix: update adapter/client.ts for @github/copilot-sdk 1.0.4 API changes + +Fixes 5 TypeScript errors caused by breaking changes in `@github/copilot-sdk@1.0.4`: + +- Replace removed `cliPath`/`cliArgs`/`useStdio`/`port`/`cliUrl` options with the new `RuntimeConnection` pattern +- Update `ping()` return type from `timestamp: number` to `timestamp: string` +- Make `SquadModelBilling.multiplier` optional to match upstream `ModelBilling` +- Replace `client.on()` with `client.onLifecycle()` for session lifecycle events diff --git a/.changeset/fix-dep0190-shell-spawn.md b/.changeset/fix-dep0190-shell-spawn.md new file mode 100644 index 000000000..8cb081f75 --- /dev/null +++ b/.changeset/fix-dep0190-shell-spawn.md @@ -0,0 +1,11 @@ +--- +"@bradygaster/squad-cli": patch +--- + +Fix Windows shell spawn issues and gh auth false-negative + +- **DEP0190 fix:** Added `escapeForCmd()`/`escapeArgs()` to properly quote args when `shell: true` is used on Windows. Changed `shell: true` → `shell: IS_WINDOWS`. +- **Capability migration:** Moved monitor-email, monitor-teams, retro, decision-hygiene from inline `buildAgentCommand`/`spawnWithTimeout` to shared `agent-spawn.ts` module. +- **gh auth fix:** Use `gh auth token` instead of `gh auth status` — the latter returns non-zero when any keyring entry is stale, even if the active account works fine. +- **Copilot flag fix:** Use `-p` (correct) instead of `--message` (non-existent) for copilot CLI prompt flag. +- **Default --yolo:** When `--execute` is active and no `copilotFlags`/`agentCmd` are set, default to `--yolo` so copilot doesn't hang waiting for permission prompts. \ No newline at end of file diff --git a/.changeset/fix-fact-checker-auto-scaffold.md b/.changeset/fix-fact-checker-auto-scaffold.md new file mode 100644 index 000000000..899c60d98 --- /dev/null +++ b/.changeset/fix-fact-checker-auto-scaffold.md @@ -0,0 +1,16 @@ +--- +"@bradygaster/squad-cli": patch +--- + +Fix #1222: Auto-scaffold Fact Checker agent during `squad init`, `squad cast`, and `squad upgrade` + +The Fact Checker role was added in v0.10.0 (#789) with its catalog entry, charter template, skill, AGENT_TEMPLATES map entry, and template manifest entry — but it was never wired into the user-facing onboarding flow. Users running `squad init` got Scribe/Ralph/Rai but never saw Fact Checker. Users running `squad upgrade` from older versions never got Rai or Fact Checker scaffolded either (upgrade was intentionally silent on agents). + +This change wires Fact Checker (and Rai, as a defensive backfill) into three code paths: + +- **`init.ts`** — adds `fact-checker` to the default `agents:` array passed to `sdkInitSquad()`. Fresh `squad init` now produces `.squad/agents/fact-checker/`. +- **`cast.ts`** — adds `factCheckerMember()`, `factCheckerCharter()`, `hasFactChecker` branches in `castTeam()`, and the roster banner line. Interactive `squad cast` now offers Fact Checker as an always-on background agent. +- **`upgrade.ts`** — new `ensureBuiltinAgents()` runs in `runEnsureChecks()`. Idempotently scaffolds `.squad/agents/Rai/` and `.squad/agents/fact-checker/` from shipped charter templates if missing. Never overwrites existing charters or history files. Scribe and Ralph are intentionally NOT scaffolded by upgrade (they predate this fix in all squads, and their charters are inlined in cast.ts). + +Result: any squad — fresh init, interactive cast, or upgrade from any prior version — now ends up with Fact Checker available. + diff --git a/.changeset/fix-green-suite.md b/.changeset/fix-green-suite.md new file mode 100644 index 000000000..7e7911126 --- /dev/null +++ b/.changeset/fix-green-suite.md @@ -0,0 +1,5 @@ +--- +"@bradygaster/squad-sdk": patch +--- + +Fix local test-suite failures by aligning personal squad path expectations, stabilizing observer file classification, and skipping environment-dependent docs/Aspire checks when required local dependencies are unavailable. diff --git a/.changeset/fix-permission-contract.md b/.changeset/fix-permission-contract.md new file mode 100644 index 000000000..c40727410 --- /dev/null +++ b/.changeset/fix-permission-contract.md @@ -0,0 +1,6 @@ +--- +"@bradygaster/squad-sdk": patch +"@bradygaster/squad-cli": patch +--- + +Fix permission handler to use `approve-once` instead of deprecated `approved` kind, aligning with Copilot CLI v1.0.54+ permission contract diff --git a/.changeset/fix-release-pipeline-versions.md b/.changeset/fix-release-pipeline-versions.md new file mode 100644 index 000000000..cc918ff55 --- /dev/null +++ b/.changeset/fix-release-pipeline-versions.md @@ -0,0 +1,14 @@ +--- +"@bradygaster/squad-cli": patch +--- + +fix: release pipeline version pinning (#1203, #1204) + +- Lower SDK dependency floor from `>=0.10.0` to `>=0.9.0` so the CLI tarball + resolves against the last published SDK when the current version isn't yet on + the registry. +- Add `isLocalOrUnpublishedVersion` guard to `buildMcpServerSpecs` so local dev + builds and versions with build metadata (`+`) fall back to `@insider` instead + of writing an unresolvable version string into MCP config. +- Extend `resolveSquadStateMcpSpec` to short-circuit for build-metadata versions. +- Add CI step to verify SDK dependency is resolvable before publishing the CLI. diff --git a/.changeset/fix-routing-strip-example-quotes.md b/.changeset/fix-routing-strip-example-quotes.md new file mode 100644 index 000000000..365753800 --- /dev/null +++ b/.changeset/fix-routing-strip-example-quotes.md @@ -0,0 +1,9 @@ +--- +"@bradygaster/squad-sdk": patch +--- + +Fix: strip surrounding quotes from routing.md example phrases in `parseRoutingMarkdown` + +`parseRoutingMarkdown` split the Examples cell on commas but never stripped the surrounding quotes, so a quoted example like `"unit tests"` was stored verbatim (quotes included). When `compileRoutingRules` / `matchRoute` later tokenized it, the quote characters glued onto the boundary words (`"unit`, `tests"`), producing regex patterns that could never match — so routing tables that used quoted examples silently routed every message to the fallback agent. + +Leading/trailing quotes (`"`, `'`, `` ` ``) are now stripped from each example, so quoted and unquoted examples behave identically. Adds regression tests covering both the parse step and end-to-end `matchRoute`. diff --git a/.changeset/fix-sdk-export-gitignore-state.md b/.changeset/fix-sdk-export-gitignore-state.md new file mode 100644 index 000000000..0f23309b8 --- /dev/null +++ b/.changeset/fix-sdk-export-gitignore-state.md @@ -0,0 +1,11 @@ +--- +"@bradygaster/squad-sdk": patch +--- + +fix(sdk): export `addSquadStateGitignoreBlock` / `removeSquadStateGitignoreBlock` from the package root + +`packages/squad-cli/src/cli/commands/migrate-backend.ts` imports these helpers from +`@bradygaster/squad-sdk`, but they were never re-exported from `src/index.ts`. This broke +the CLI TypeScript build (`TS2305: has no exported member`). Re-export the two functions +(plus their marker constants) so the CLI compiles and the state-backend migration command +resolves them. Unblocks cutting a stable release that carries the #1378 inline-dispatch-gate fix. diff --git a/.changeset/fix-skill-squad-rename-to-squad-help.md b/.changeset/fix-skill-squad-rename-to-squad-help.md new file mode 100644 index 000000000..29907db3c --- /dev/null +++ b/.changeset/fix-skill-squad-rename-to-squad-help.md @@ -0,0 +1,49 @@ +--- +"@bradygaster/squad-sdk": patch +"@bradygaster/squad-cli": patch +--- + +Fix `skill(Squad)` discovery — rename disambiguation skill to `squad-help` (supersedes #1297) + +PR #1297 added a disambiguation skill named `squad` so models calling `skill(Squad)` would get a redirect instead of "Skill not found". After local end-to-end testing on 2026-06-13: **the skill ships to disk correctly but never shows up in Copilot CLI's `/skills` list**. + +## Root cause (verified against Copilot CLI source 1.0.62-2) + +Decompiling `~/.copilot/pkg/win32-x64/1.0.62-2/app.js`: + +1. **Copilot CLI's skill schema is `{name, description, source, baseDir, allowedTools, pluginName, pluginVersion}`** (line 989). Frontmatter fields like `triggers:`, `domain:`, `confidence:`, `license:` are silently ignored. +2. **Skill loader returns `{skills, warnings, errors}`** (line 4427). Skills that fail to load are reported as errors. +3. **A skill named `squad` collides with the Copilot agent named `Squad`** (registered at `.github/agents/squad.agent.md`). The agent wins; the skill is hidden from `/skills`. + +The original `triggers:` frontmatter from #1297 was based on a wrong assumption — Copilot CLI doesn't read that field. Triggering happens via natural-language match against `description:`, and the skill name is what `skill(X)` looks up. + +## Fix + +1. **Rename** the disambiguation skill `squad` → `squad-help`. Avoids the agent-name collision, descriptive enough that the model can find it via description match when a user says *"how do I use squad"* or *"squad help"*. +2. **Update SKILL.md content:** + - `name: "squad-help"` (was `"squad"`) + - Removed `triggers:`, `domain:`, `confidence:`, `source:`, `license:` (all ignored by Copilot CLI) + - Added `allowedTools: []` (matches Copilot CLI's schema) + - `description:` rewritten to be self-explanatory so natural-language match works + - Body still explains the agent-vs-skill distinction and routes to `task(agent_type="Squad", …)` for misdirected `skill(Squad)` attempts + - Added explicit note about `/squad` slash command: it does not exist (slash commands are built-in CLI keywords, not auto-mapped from skills) and there's no way to create one without a Copilot CLI feature change +3. **Update** `MANIFEST_SKILL_NAMES` in `packages/squad-sdk/src/config/init.ts`: `'squad'` → `'squad-help'`. +4. **Add** `TEMPLATE_MANIFEST` entry in `packages/squad-cli/src/cli/core/templates.ts` for `squad-help` so `squad upgrade` also propagates the skill (independent of #1297 which only updated MANIFEST_SKILL_NAMES — `squad upgrade` uses a different code path that reads TEMPLATE_MANIFEST). + +## Test coverage + +New `test/init.test.ts > should install the squad-help disambiguation skill`: +- Asserts `.copilot/skills/squad-help/SKILL.md` exists after `initSquad()` +- Asserts the frontmatter says `name: "squad-help"` (not `"squad"`) — regression guard against re-introducing the collision +- Asserts content references `agent_type="Squad"` (the correct invocation path) +- Asserts content references `squad-commands` (the right next-step skill) + +26/26 init tests pass; `npm run lint` clean. + +## Supersedes #1297 + +PR #1297 added a colliding-name skill. This PR is the correct version. Close #1297 in favor of this one; the consolidated changeset will be picked up here. + +## Out of scope (separate issue worth filing) + +`squad upgrade` synced only 10 of 16 installed skills in local testing — `TEMPLATE_MANIFEST` (used by upgrade) is out of sync with `MANIFEST_SKILL_NAMES` (used by init). Specifically, `tiered-memory`, `iterative-retrieval`, `reflect`, `cross-squad`, `cross-squad-communication` (added in PRs #1292 + #1295) have entries in `MANIFEST_SKILL_NAMES` but not in `TEMPLATE_MANIFEST`. Will file a follow-up to add those to `TEMPLATE_MANIFEST` so both code paths agree. diff --git a/.changeset/fix-squad-home-env-bugs.md b/.changeset/fix-squad-home-env-bugs.md new file mode 100644 index 000000000..e5bd0da9d --- /dev/null +++ b/.changeset/fix-squad-home-env-bugs.md @@ -0,0 +1,12 @@ +--- +"@bradygaster/squad-sdk": patch +"@bradygaster/squad-cli": patch +--- + +fix: respect SQUAD_HOME in capabilities.ts and comms-teams.ts, implement SQUAD_PERSONAL_DIR env var, fix Windows shell flag in loop preflight, link personal squad during init + +- capabilities.ts: use `resolveSquadHome()` instead of hardcoded `~/.squad/` for machine-capabilities.json (#1280) +- comms-teams.ts: use `resolveSquadHome()` instead of hardcoded `~/.squad/` for Teams OAuth token storage (#1279) +- resolution.ts: implement `SQUAD_PERSONAL_DIR` env var override in `resolvePersonalSquadDir()` (#1278) +- loop.ts: add `shell: process.platform === 'win32'` to `checkCopilotCli()` execFile call (#1372) +- init.ts: set `teamRoot` in config.json to personal squad directory when one exists (#1010, #984) diff --git a/.changeset/fix-squad-slash-command.md b/.changeset/fix-squad-slash-command.md new file mode 100644 index 000000000..dc4e364d6 --- /dev/null +++ b/.changeset/fix-squad-slash-command.md @@ -0,0 +1,76 @@ +--- +"@bradygaster/squad-sdk": minor +"@bradygaster/squad-cli": minor +--- + +Make `/squad` a real slash command — rename `squad-commands` skill to `squad` with `user-invocable: true` + +## What this enables + +Users can type `/squad` in any Copilot CLI session in a squad-initialized project and get the categorized command catalog (Install & Upgrade, Team Management, Issues & PRs, Plugins & Skills, Model & Cost, Sessions & State). This is what users naturally expect — Squad ships an in-chat command surface, and the slash key is the muscle memory. + +## Verified mechanism (Copilot CLI 1.0.62-2) + +Decompiling `~/.copilot/pkg/win32-x64/1.0.62-2/sdk/index.js` line 2618: + +```js +getLoadedSkills().filter(e => e.userInvocable) + .map(e => ({name: `/${eF(e)}`, isSkill: true, skill: e})) +``` + +Any skill with frontmatter `user-invocable: true` is auto-registered by Copilot CLI as a slash command at `/`. The built-in `customize-cloud-agent` skill shipped with Copilot CLI uses this exact pattern with `user-invocable: false`; setting it to `true` makes the skill appear in `/skills` AND as a slash keystroke. + +The previous skill `squad-commands` had no `user-invocable` field (Copilot CLI defaults to false), so `/squad-commands` did not exist. Users either typed natural language ("squad commands", "what can squad do") or never discovered the menu. + +## Changes + +1. **Renamed** the `squad-commands` skill to `squad` (canonical source in `.squad/skills/squad/`). +2. **Frontmatter rewritten** to match Copilot CLI's actual schema (verified against decompiled source): + - `name: squad` (was `squad-commands` — `/squad` is shorter, intuitive, and matches the agent name without colliding because slash commands and skill lookups are separate namespaces) + - `user-invocable: true` ← **the load-bearing change** + - `description:` rewritten to be self-explanatory so natural-language match also still works + - `allowedTools: []` (matches Copilot CLI's schema) + - Removed unused fields: `domain:`, `confidence:`, `source:`, `triggers:` (all silently ignored by Copilot CLI) +3. **Body text** updated to reference `/squad` as the primary invocation path. +4. **`MANIFEST_SKILL_NAMES`** in `packages/squad-sdk/src/config/init.ts`: `'squad-commands'` → `'squad'`. +5. **`TEMPLATE_MANIFEST`** in `packages/squad-cli/src/cli/core/templates.ts`: updated `source` + `destination` + `description` to reflect the rename. +6. **Removed** stale `packages/{squad-cli,squad-sdk}/templates/skills/squad-commands/` directories. + +## Test coverage + +New `test/init.test.ts > should install the squad slash-command skill with user-invocable: true`: +- Asserts `.copilot/skills/squad/SKILL.md` exists after `initSquad()` +- Asserts frontmatter contains `user-invocable: true` (load-bearing — without this, no slash command) +- Asserts frontmatter `name: squad` (load-bearing — slash command is `/`) +- Asserts the menu presentation rules are still in the body + +26/26 init tests pass; `npm run lint` clean. + +## How users will experience this + +After `squad init` + opening Copilot CLI in the project: + +``` +> /squad +📋 Squad Commands — pick a category: + 1. Install & Upgrade + 2. Team Management + 3. Issues & PRs + 4. Plugins & Skills + 5. Model & Cost + 6. Sessions & State +``` + +## Composability with other open PRs + +- **Disjoint from #1292** (skills bundling, 4 new entries). Both modify `MANIFEST_SKILL_NAMES` but `squad-commands → squad` is a rename, not an add. Merge order doesn't matter — the renamer wins. +- **Disjoint from #1302** (`squad-help` disambiguation skill). `squad-help` covers the `skill(Squad)` misdirect case; this PR covers the `/squad` slash command UX. They're complementary. + +## Out of scope (future, mentioned in user request) + +The user wants to eventually install the `squad` skill **machine-wide** (e.g., `~/.copilot/skills/squad/`) so `/squad init` works in any folder even before `squad` is initialized in that project. That's a separate piece of work because: +1. It requires a `squad install --global-skill` command or similar (none exists today) +2. The `/squad` menu currently calls into `squad` CLI subcommands that assume `.squad/` exists in cwd — the menu would need a "Init Mode" branch for the "no `.squad/` yet" case +3. Copilot CLI's `~/.copilot/skills/` is a personal scope, so it'd need to be opt-in per-user + +Will file a follow-up issue tracking that work after this lands. diff --git a/.changeset/fix-squad-spawning-routing.md b/.changeset/fix-squad-spawning-routing.md new file mode 100644 index 000000000..5e86ee042 --- /dev/null +++ b/.changeset/fix-squad-spawning-routing.md @@ -0,0 +1,40 @@ +--- +"@bradygaster/squad-cli": patch +"@bradygaster/squad-sdk": patch +--- + +Coordinator now recognises "spawn a squad" / "another squad" / "two squads" as Squad-PRODUCT vocabulary + +## Symptom + +A coordinator initialised by `squad init` saw prompts like *"spawn two squads of designers and devs"* and fanned out raw `task` agents inside its own context, treating "squad" as generic English for "team / group". It never invoked the bundled `cross-squad` or `cross-squad-communication` skills, so the peer-squad delegation protocol (registry / manifest / sync CLI / git-async / GitHub-issue patterns) was bypassed entirely. + +Two structural holes in `squad.agent.md` allowed this: + +1. The Routing table had no row that mapped "spawn a squad" phrasing to the Squad-PRODUCT concept. +2. The Skill-aware-routing block was process discipline ("check skill directories by domain relevance") with no hard "if the user's word matches a skill name, MUST load the skill" trigger. + +## Fix + +Two surgical edits to the canonical `.squad-templates/squad.agent.md` (synced to all 4 mirror copies via `scripts/sync-templates.mjs --sync`): + +1. **New routing-table row** — matches "spawn a squad", "another squad", "two squads", "second squad", "fan out to squads", "delegate to a squad", or any phrasing that treats "squad" as a unit to spawn or address. Action: invoke the `skill` tool on `cross-squad` AND `cross-squad-communication` BEFORE any `task` spawn, then delegate via Pattern 0/1/2/3 — never fan out raw `task` agents in the coordinator's own context. + +2. **New "Hard trigger — keyword-to-skill match" paragraph** at the top of the Skill-aware-routing block — if any word in the request matches an installed skill name (e.g., "squad" → `cross-squad`, "reflect" → `reflect`, "ceremony" → matching ceremony skill, "fact-check" → `fact-checking`, "release" → `release-process`), the coordinator MUST invoke the `skill` tool to fully load that skill BEFORE designing its approach. Includes a one-line "failure mode this closes" pointer so the rule survives future paraphrasing. + +3. **Companion `cross-squad/SKILL.md` opener strengthened** — added a one-line "Read this FIRST any time the user says 'squad' as a thing to spawn / delegate to / address" callout above the existing `## Context` paragraph, so even a coordinator that skips the routing-table row still hits the trigger when it does eventually load the skill. + +## Regression test + +`test/template-sync.test.ts` now asserts, for every mirrored copy of `squad.agent.md` (5 locations — canonical + 3 template mirrors + `.github/agents/`), that: + +* The routing-table row exists and contains all three trigger phrases ("spawn a squad", "another squad", "two squads"). +* The row's action cell references the `cross-squad` skill AND the `skill` tool. +* The "Hard trigger" + "keyword-to-skill match" markers are both present in the Skill-aware-routing block. +* The hard-trigger paragraph names the worked example `"squad" → cross-squad` so future edits can't drop the concrete mapping. + +223/223 template-sync tests pass. + +## Composability + +Disjoint from all other open PRs in the v0.10 stabilisation set (#1292, #1293, #1295, #1298, #1300, #1301, #1302, #1303, #1304, #1306). Only modifies `.squad-templates/squad.agent.md` + its 4 mirrors + `cross-squad/SKILL.md` + the template-sync test. Existing squads can pick up the fix two ways: (a) `squad upgrade` once this lands, or (b) hand-patch `.squad/squad.agent.md` against the upstream diff and restart the Copilot CLI session. diff --git a/.changeset/fix-yaml-escaping-skill.md b/.changeset/fix-yaml-escaping-skill.md new file mode 100644 index 000000000..3c2623fd9 --- /dev/null +++ b/.changeset/fix-yaml-escaping-skill.md @@ -0,0 +1,8 @@ +--- +"@bradygaster/squad-cli": patch +--- + +Fix YAML escaping in skill command apm.yml generation + +Use `JSON.stringify()` for skill descriptions in generated apm.yml files +to properly escape quotes and newlines, preventing invalid YAML output. diff --git a/.changeset/help-externalize-internalize.md b/.changeset/help-externalize-internalize.md new file mode 100644 index 000000000..4d71e7b76 --- /dev/null +++ b/.changeset/help-externalize-internalize.md @@ -0,0 +1,5 @@ +--- +"@bradygaster/squad-cli": patch +--- + +Add `externalize` and `internalize` to the top-level `squad --help` command list so they are discoverable (#1050). diff --git a/.changeset/init-prompt-copilot-member.md b/.changeset/init-prompt-copilot-member.md new file mode 100644 index 000000000..eb3dc7a95 --- /dev/null +++ b/.changeset/init-prompt-copilot-member.md @@ -0,0 +1,10 @@ +--- +"@bradygaster/squad-cli": minor +--- + +Add @copilot team member prompt during `squad init` + +During interactive `squad init`, users are now prompted to add @copilot +as an autonomous team member. Answering yes adds the Coding Agent section +to team.md and copies copilot-instructions.md into the project. Non-interactive +mode skips silently with a hint to run `squad copilot enable` later. diff --git a/.changeset/ink7-adaptation.md b/.changeset/ink7-adaptation.md new file mode 100644 index 000000000..626297867 --- /dev/null +++ b/.changeset/ink7-adaptation.md @@ -0,0 +1,12 @@ +--- +"@bradygaster/squad-cli": patch +--- + +chore: adapt CLI to ink 7.0.6 + +Updates the ink dependency from ^6.8.0 to ^7.0.6 and adapts keyboard handling in InputPrompt.tsx to ink 7's revised API: + +- key.meta no longer fires on bare Escape; guard updated to use key.escape +- useInput callbacks are now wrapped in reconciler.discreteUpdates(), which flushes React state synchronously at end of callback; rewrote Enter/paste handler to call setValue('') inside the flush rather than in a deferred timer + +Closes #1322, #1335. diff --git a/.changeset/memory-tools-mcp-exposure.md b/.changeset/memory-tools-mcp-exposure.md new file mode 100644 index 000000000..7a6a30528 --- /dev/null +++ b/.changeset/memory-tools-mcp-exposure.md @@ -0,0 +1,5 @@ +--- +"@bradygaster/squad-cli": patch +--- + +Expose memory.* tools (classify, write, search, promote, delete, audit) through the squad_state MCP server so agents can discover and call them. Also pin squad_state to user-level ~/.copilot/mcp-config.json for external `copilot -p` mode compatibility. diff --git a/.changeset/observer-hardening.md b/.changeset/observer-hardening.md new file mode 100644 index 000000000..e71207b54 --- /dev/null +++ b/.changeset/observer-hardening.md @@ -0,0 +1,5 @@ +--- +"@bradygaster/squad-sdk": patch +--- + +Harden squad observer change resolution against directory events, symlinks, and filesystem scan errors. diff --git a/.changeset/otel-resource-api-migration.md b/.changeset/otel-resource-api-migration.md new file mode 100644 index 000000000..8b6b1082f --- /dev/null +++ b/.changeset/otel-resource-api-migration.md @@ -0,0 +1,5 @@ +--- +"@bradygaster/squad-sdk": patch +--- + +Migrate OTel Resource API to `resourceFromAttributes` for compatibility with `@opentelemetry/resources` 2.x (replaces the removed `Resource` class constructor in `packages/squad-sdk/src/runtime/otel.ts` and `test/aspire-integration.test.ts`) \ No newline at end of file diff --git a/.changeset/preset-apply-wires-team-1288.md b/.changeset/preset-apply-wires-team-1288.md new file mode 100644 index 000000000..0f2df1982 --- /dev/null +++ b/.changeset/preset-apply-wires-team-1288.md @@ -0,0 +1,38 @@ +--- +"@bradygaster/squad-sdk": patch +"@bradygaster/squad-cli": patch +--- + +Fix #1288: `squad preset apply` now wires team.md, routing.md, and casting state + +`squad preset apply ` used to copy only the preset's agent charters into `.squad/agents/`. It left: + +- `.squad/team.md` `## Members` table empty +- `.squad/routing.md` with no Work Type rows for the preset agents +- `.squad/casting/registry.json`, `history.json`, and `policy.json` not created + +Net result: the coordinator's mode-switch check saw an empty `## Members` table and treated every session as **Init Mode**, proposing to re-scaffold a team the user already applied — defeating the entire purpose of presets. + +This change adds a new merge-friendly scaffold module (`packages/squad-sdk/src/presets/scaffold.ts`) that, after `applyPreset` copies the charters, wires the preset agents into: + +- **team.md** `## Members` — creates the file from scratch if missing, or merges new rows into an existing `## Members` table while preserving the surrounding content (Coordinator section, Project Context, etc.). Idempotent: a second apply does not duplicate rows. +- **routing.md** `## Work Type → Agent` — creates from scratch or appends new rows after the existing routing table. Each preset agent becomes `| | | — |`. +- **casting/registry.json** — merges new agents into an existing registry without clobbering pre-existing entries. Universe = `preset:` so future casts can distinguish preset-provided agents. +- **casting/history.json** — appends a preset-application snapshot and a universe_usage_history entry. +- **casting/policy.json** — created with sensible defaults only if missing; never overwrites an existing policy. + +Failure modes: + +- Agents with `status: 'error'` in the per-agent results (e.g., source dir missing) are excluded from the wiring step. +- Agents with `status: 'skipped'` (already exist in target) ARE wired into team.md/registry so the team reflects user intent. +- If the scaffolding itself throws (e.g., disk error), a synthetic error result is appended to the return value so the CLI can surface it; per-agent install results are preserved. + +Out of scope (tracked separately): deduplicating these writers with the equivalent fresh-write versions in `packages/squad-cli/src/cli/core/cast.ts`. A future refactor can move both call sites to the shared SDK module. + +Test coverage in `test/presets.test.ts`: +- `wires preset agents into team.md ## Members (#1288)` +- `merges preset agents into an existing team.md without duplicating rows (#1288)` — includes idempotency assertion +- `writes casting registry.json, history.json, and policy.json (#1288)` +- `appends routing rows for preset agents to routing.md (#1288)` + +Closes #1288 diff --git a/.changeset/reasoning-effort.md b/.changeset/reasoning-effort.md new file mode 100644 index 000000000..54ed26a13 --- /dev/null +++ b/.changeset/reasoning-effort.md @@ -0,0 +1,17 @@ +--- +"@bradygaster/squad-sdk": minor +--- + +Add reasoning effort support to agent spawning pipeline + +Thread `reasoningEffort` through the full agent lifecycle: + +- **Charter**: Parse `**Reasoning Effort:**` from `## Model` section in `charter.md` +- **Config**: Read/write `defaultReasoningEffort` and `agentReasoningEffortOverrides` in `.squad/config.json` +- **Resolution**: New `resolveReasoningEffort()` with layered priority (per-agent config override > global config > spawn override > charter preference > undefined) +- **Clamping**: New `clampReasoningEffort()` caps effort to model's max supported level +- **Lifecycle**: `SpawnAgentOptions.reasoningEffortOverride` passes through to `SquadSessionConfig` +- **Fan-Out**: `AgentSpawnConfig.reasoningEffortOverride` passes through to `createSession()` +- **Builders**: `defineAgent()` and `defineDefaults()` accept `reasoningEffort: "low" | "medium" | "high" | "xhigh"` +- **Template**: Updated charter template with `**Reasoning Effort:** auto` +- **Validation**: `"auto"` and invalid values are normalized to `undefined` at parse time diff --git a/.changeset/registry-subcommand.md b/.changeset/registry-subcommand.md new file mode 100644 index 000000000..08be83809 --- /dev/null +++ b/.changeset/registry-subcommand.md @@ -0,0 +1,20 @@ +--- +"@bradygaster/squad-cli": minor +"@bradygaster/squad-sdk": minor +--- + +Add `squad registry add/list/remove` for discovery-only peer squads (no inheritance) + +The existing `squad upstream add` triggers inheritance (skills/decisions/wisdom flow from the other squad into yours at session start). For peer relationships where you want discovery and delegation but NOT inheritance, you previously had to hand-edit `.squad/squad-registry.json` — the `squad discover` error message even told you to "create a squad-registry.json" manually. + +This adds a real CLI surface for the registry, symmetric to `squad upstream`: + +- `squad registry add ` — registers a peer, validates its manifest, refuses on duplicate name +- `squad registry list` — shows all registered peers +- `squad registry remove ` — removes by name + +It also fixes a subtle path-semantics confusion: `readManifest()` now accepts BOTH `repo-root` and `repo-root/.squad` paths (the docs/SKILL showed the `.squad`-suffixed form, but the code previously joined `.squad/manifest.json` onto whatever you gave it, so the suffixed form silently failed). The SDK helpers (`readSquadRegistry`, `writeSquadRegistry`, `addRegistryEntry`, `removeRegistryEntry`) are exported for tooling. + +The `cross-squad` SKILL and the `squad discover` empty-state hint are updated to reflect the new command. + +Closes #1290. diff --git a/.changeset/release-0.11.0.md b/.changeset/release-0.11.0.md new file mode 100644 index 000000000..225502b45 --- /dev/null +++ b/.changeset/release-0.11.0.md @@ -0,0 +1,6 @@ +--- +"@bradygaster/squad-cli": minor +"@bradygaster/squad-sdk": minor +--- + +Stage the 0.11.0 release by cleaning the committed package versions and publishing the 0.11.0 changelog notes. diff --git a/.changeset/rename-dotnet-aspire-to-aspire.md b/.changeset/rename-dotnet-aspire-to-aspire.md new file mode 100644 index 000000000..c083185a4 --- /dev/null +++ b/.changeset/rename-dotnet-aspire-to-aspire.md @@ -0,0 +1,5 @@ +--- +"@bradygaster/squad-cli": patch +--- + +Rename user-facing Aspire terminology in CLI help text and command output. Aspire is a standalone OTLP dashboard, not limited to .NET apps. diff --git a/.changeset/rename-hire-to-cast.md b/.changeset/rename-hire-to-cast.md new file mode 100644 index 000000000..41eab8b42 --- /dev/null +++ b/.changeset/rename-hire-to-cast.md @@ -0,0 +1,5 @@ +--- +"squad-cli": minor +--- + +Rename `hire` command to `cast` in CLI help and documentation. The `hire` command continues to work as a silent alias (like `cls`/`clear` in PowerShell) — no deprecation, no warnings, it just does the same thing. All user-facing text now presents `cast` as the canonical verb because we're casting agents, not hiring humans. diff --git a/.changeset/slim-squad-agent-md-phase1.md b/.changeset/slim-squad-agent-md-phase1.md new file mode 100644 index 000000000..1b4b05ebf --- /dev/null +++ b/.changeset/slim-squad-agent-md-phase1.md @@ -0,0 +1,90 @@ +--- +"@bradygaster/squad-sdk": minor +"@bradygaster/squad-cli": minor +--- + +Slim squad.agent.md by extracting 3 sections to satellite skills (closes #1308 phase 1) + +## Symptom + +`squad.agent.md` is loaded as the agent prompt at every Copilot session start. As of v0.10.0-insider.1 it sits at **81 KB / 1,137 lines** and was steadily growing — the v0.10 stabilisation pass alone added +10.6 KB / +114 lines. Every byte of that file is paid at every session. + +## Phase 1 fix (this PR) + +Extract three **low-cross-reference, high-byte** sections into satellite skills the coordinator loads on demand via the `skill` tool. Same pattern that already worked for `cross-squad-communication` (#1295). + +| Extracted to satellite skill | Bytes saved in main file | +|---|---| +| `coordinator-init-mode` (Init Mode Phase 1 + Phase 2) | 5.4 KB | +| `coordinator-source-of-truth` (Source of Truth Hierarchy table) | 4.4 KB | +| `coordinator-response-mode` (Response Mode Selection + Lightweight Spawn Template) | 3.9 KB | + +### Result + +- `squad.agent.md`: **81 KB → 70 KB** (-13.9%, -140 lines) +- Three new bundled skills (`coordinator-source-of-truth`, `coordinator-response-mode`, `coordinator-init-mode`) installed alongside the existing 16 — total 19 skills at `.github/skills/` +- Behaviour unchanged: each removed section is replaced with a stub that names the trigger condition and instructs the coordinator to `skill(coordinator-X)` before acting + +## Stubs left in `squad.agent.md` + +Each extracted section keeps a short stub (4-12 lines) so the coordinator still knows the section exists and what triggers it. The full algorithm/table/template lives in the satellite skill. + +Example (Source of Truth, was 30+ lines, now 9): + +```markdown +## Source of Truth Hierarchy + +Squad files split into **authoritative** (governance, roster, charters — static) and +**derived / append-only** (decisions, history, logs — runtime-owned). The four +governing rules: + +1. **`squad.agent.md` wins** any conflict with another file. +2. **Append-only files** are never retroactively edited. +3. **Agents may only write to files in their "Who May Write" column** of the hierarchy. +4. **Only Squad (Coordinator)** records accepted decisions in `.squad/decisions.md`. + +**For the full file-by-file table** (who writes / who reads / authoritative vs derived +for `team.md`, `decisions.md`, `routing.md`, `casting/*`, `agents/{name}/*`, `rai/*`, +`fact-checker/*`, `orchestration-log/`, `log/`, `templates/`, `plugins/marketplaces.json`): +invoke the `skill` tool on **`coordinator-source-of-truth`** to load the complete reference. +``` + +## What is NOT extracted (intentional) + +These sections stay in `squad.agent.md` because they're loaded on EVERY prompt and / or are load-bearing for first-touch behaviour: + +- **Team Mode + state-backend handshake + HARD RULE** (#1306) — handshake must fire before any state write +- **Routing table** — the trigger table for every user prompt +- **Hard trigger — keyword-to-skill match** paragraph (#1307) — load-bearing for the spawn-routing fix +- **How to Spawn an Agent** — referenced from every routing-table action +- **Coordinator Identity / Personal Squad / Memory Governance Tools** — frequently re-read inline + +Follow-up issues will tackle these one at a time if/when needed. + +## Wired changes + +- New: `.squad/skills/coordinator-{source-of-truth,response-mode,init-mode}/SKILL.md` are the **canonical source files** kept in the squad repo itself (the same convention every other bundled skill follows). At install/upgrade time they are copied to `.github/skills/coordinator-{x}/SKILL.md` in the user's repo — that's the Copilot CLI custom-skills location adopted by #1304 (formerly `.copilot/skills/`). +- Mirrored at build time to `packages/squad-cli/templates/skills/` and `packages/squad-sdk/templates/skills/` via `scripts/sync-skill-templates.mjs` (same path every other manifest skill takes). +- `MANIFEST_SKILL_NAMES` in `packages/squad-sdk/src/config/init.ts` grows by 3 entries (now 19) +- `TEMPLATE_MANIFEST` in `packages/squad-cli/src/cli/core/templates.ts` grows by 3 entries with `../.github/skills/` destinations (matching the post-#1304 install location) +- `.squad-templates/squad.agent.md` replaces each extracted section with a stub +- 4 mirrored copies re-synced via `scripts/sync-templates.mjs --sync` + +## Tests + +- `test/init.test.ts > should install every manifest-curated skill (regression: bradygaster/squad#1289, #1264)` — already imports `MANIFEST_SKILL_NAMES` and iterates it, so it automatically asserts the 3 new skills install. **287/287 tests pass.** +- Smoke test: fresh `squad init` produces 19 skills at `.github/skills/`, `squad.agent.md` is 70 KB. + +## Composability + +Disjoint from all other open PRs. Pure file moves + a few line replacements + 3 manifest entries. + +## Follow-ups (separate PRs) + +The remaining high-bloat sections in `squad.agent.md` are stickier and need careful design to extract: + +- **Routing table** (9.2 KB) — needed on every prompt; can't be lazy-loaded as-is. May be slimmable by moving the action-cell verbiage into satellite skills (the trigger table stays, the action prose moves). +- **Team Mode** (5.5 KB) — contains the state-backend handshake, which MUST fire on every session. Maybe extract the worktree-awareness / casting-migration sub-sections. +- **How to Spawn an Agent** (3.2 KB) — could extract the role-emoji catalog. + +Target: get the coordinator down to **~45 KB** in 2-3 follow-up PRs. diff --git a/.changeset/spawn-backend-followups-1377.md b/.changeset/spawn-backend-followups-1377.md new file mode 100644 index 000000000..590d9d2ed --- /dev/null +++ b/.changeset/spawn-backend-followups-1377.md @@ -0,0 +1,26 @@ +--- +"@bradygaster/squad-cli": patch +"@bradygaster/squad-sdk": patch +--- + +Fix #1377 follow-ups: spawn-backend fallback, slot-leak guards, timeouts, prompt sanitization + +Addresses the six deferred review items from the #1385 review (sub-sessions in the Copilot App). All are hardening fixes to the spawn coordinator (`packages/squad-sdk/src/coordinator/`) plus a template probe-order correction. + +**Blockers** + +1. **App→task fallback (`fan-out.ts`).** When the platform `SpawnBackend` (e.g. App sub-sessions) returns `success: false` — concurrency cap, unavailable tool, transient error — `spawnSingle()` no longer throws and fails the agent. It emits a `session.spawn_fallback` event and falls through to the direct `createSession` path, honoring the template contract ("if `create_session` fails, retry with `task`"). The direct path is now extracted into a shared `spawnViaCreateSession()` helper. + +2. **Concurrency slot leak (`fan-out.ts`).** `registerSpawnRelease()` now (a) treats `completed` as a terminal status (previously only `idle`/`error`/`destroyed` released the slot), and (b) installs an unref'd max-lifetime safety timer (default 1h) that force-releases the slot if a silently-crashed sub-session emits no terminal event. The timer is cleared on normal release. + +3. **Template detection-order drift.** Re-synced the canonical `.squad-templates/squad.agent.md` probe order (`create_session` → `runSubagent` → `task` → inline) to all mirror copies, which had `task` and `runSubagent` swapped. + +**Risks** + +4. **Prompt-injection hardening (`fan-out.ts`).** `buildInitialPrompt()` now runs caller-supplied `task`/`context` through `sanitizePromptValue()` (defense-in-depth): strips control characters, neutralizes forged `**Marker:**` headers, and caps length. Not a complete prompt-injection defense, but it stops trivial structural-marker spoofing. + +5. **`createSession` timeout (`spawn-backend.ts`).** Both backends wrap the injected `createSession` call in a timeout (`createSessionTimeoutMs`, default 60s, 0 disables) so a hung factory cannot pin `pendingSpawnCount` / a concurrency slot forever. `SessionSpawnBackend`'s `finally` still decrements the pending counter on timeout. + +6. **Honest `isAvailable()` (`spawn-backend.ts`).** Both backends previously returned `true` unconditionally. They now return a real heuristic (`typeof createSession === 'function'`) and accept an injectable `availabilityCheck` predicate via options. + +Adds vitest coverage for the fallback path, `completed`-status release, the safety-timeout release, both createSession timeouts, prompt sanitization, and `isAvailable()`. diff --git a/.changeset/spoiler-aware-casting.md b/.changeset/spoiler-aware-casting.md new file mode 100644 index 000000000..f352e57a8 --- /dev/null +++ b/.changeset/spoiler-aware-casting.md @@ -0,0 +1,29 @@ +--- +"@bradygaster/squad-cli": patch +"@bradygaster/squad-sdk": patch +--- + +docs(casting): make agent name allocation spoiler-aware + +Agent names are drawn from fictional universes as easter eggs and are +shown in plain text across `team.md`, prompts, logs, and generated files. +Previously the casting rules optimized only for theme fit and uniqueness, +so a freshly set-up squad could surface a character name that encodes a +future title, role, transformation, or fate — spoiling the source +material for a user who is only part-way through it. + +This change updates the canonical naming guidance so that name allocation +now: + +- Prefers the name a character has when first introduced. +- Avoids titles or epithets a character only earns later. +- Avoids names that reveal a transformation, fate, hidden identity, or + later reveal. +- Falls back to a safer character from the same universe when unsure. +- Keeps existing name mappings stable — only the next/new allocation + picks a different spoiler-safe character, so already-named agents are + never renamed. + +Updates the `squad.agent.md` name-allocation rule and the +`casting-reference.md` reference (new "Spoiler Awareness" section), +propagated to all template copies via `scripts/sync-templates.mjs`. diff --git a/.changeset/sub-sessions-spawn-backend.md b/.changeset/sub-sessions-spawn-backend.md new file mode 100644 index 000000000..b9d1c7f9e --- /dev/null +++ b/.changeset/sub-sessions-spawn-backend.md @@ -0,0 +1,22 @@ +--- +"@bradygaster/squad-sdk": minor +"@bradygaster/squad-cli": minor +--- + +Add sub-session spawn backend for Copilot App integration + +Spawn cast members as sub-sessions when running in the Copilot App (Tauri desktop), +giving users richer UX with each squad member visible in the left navigation: + +- **SpawnBackend interface**: Thin abstraction with `TaskSpawnBackend` (CLI) and + `SessionSpawnBackend` (App) implementations +- **Detection**: `detectSpawnBackend()` probes for `create_session` tool availability + at coordinator startup; `detectSpawnPlatform()` returns the platform type +- **Session naming**: `"{Name} {verb}ing {noun}"` convention with 40-char limit + via `truncateSessionName()` and `buildSessionName()` helpers +- **Concurrency cap**: Maximum 4-5 simultaneous sub-sessions with queuing +- **Depth limit**: No sub-sub-sessions — max depth 1 +- **Fallback**: Graceful degradation to `task` tool if `create_session` fails +- **Zero CLI impact**: Behavior unchanged when `create_session` is absent +- **Template updates**: `squad.agent.md` and `spawn-reference.md` updated with + App dispatch mechanism, platform detection probe, and sub-session rules diff --git a/.changeset/types-node-25.md b/.changeset/types-node-25.md new file mode 100644 index 000000000..0dde9d78e --- /dev/null +++ b/.changeset/types-node-25.md @@ -0,0 +1,6 @@ +--- +"@bradygaster/squad-sdk": patch +"@bradygaster/squad-cli": patch +--- + +Bump @types/node from ^22.0.0 to ^25.9.3 in cli and sdk packages. Removes a no-longer-needed @ts-expect-error directive in cli-entry.ts now that process.emit is properly typed. \ No newline at end of file diff --git a/.changeset/typescript-6.md b/.changeset/typescript-6.md new file mode 100644 index 000000000..ae8a4fb80 --- /dev/null +++ b/.changeset/typescript-6.md @@ -0,0 +1,10 @@ +--- +"@bradygaster/squad-sdk": patch +"@bradygaster/squad-cli": patch +--- + +chore: upgrade to TypeScript 6 (closes #1325, #1328, #1334) + +- Bumps `typescript` devDependency from `^5.7.0` to `^6.0.3` in packages/squad-sdk, packages/squad-cli, and the root workspace. +- Bumps `@typescript-eslint/parser` in root from `^8.57.1` to `^8.61.1` to align with the already-present `^8.61.1` plugin (v8.61.1 peer-deps cover `typescript >=4.8.4 <6.1.0`). +- Adds `"types": ["node"]` to `compilerOptions` in `packages/squad-cli/tsconfig.json` and `packages/squad-sdk/tsconfig.json`; TypeScript 6 no longer auto-injects Node globals without an explicit `types` declaration. diff --git a/.changeset/vitest-4-upgrade.md b/.changeset/vitest-4-upgrade.md new file mode 100644 index 000000000..0eac58616 --- /dev/null +++ b/.changeset/vitest-4-upgrade.md @@ -0,0 +1,6 @@ +--- +"@bradygaster/squad-sdk": patch +"@bradygaster/squad-cli": patch +--- + +Bump vitest from v3 to v4 alongside @vitest/coverage-v8 v4 to resolve peer-dep mismatch. Both packages now align at ^4.1.9 across root and all sample workspaces. No API adaptations required — vitest 4's spying rewrite is backwards-compatible for the mock/spyOn patterns used in this codebase. diff --git a/.changeset/vscode-jsonrpc-9-sdk.md b/.changeset/vscode-jsonrpc-9-sdk.md new file mode 100644 index 000000000..7ccaa4ae9 --- /dev/null +++ b/.changeset/vscode-jsonrpc-9-sdk.md @@ -0,0 +1,5 @@ +--- +"@bradygaster/squad-sdk": patch +--- + +Sync lockfile for the vscode-jsonrpc 8→9 bump in packages/squad-sdk. v9 adds the missing `exports` field that v8 lacked, making `vscode-jsonrpc/node` resolvable under strict ESM without `patch-esm-imports.mjs` runtime workarounds. \ No newline at end of file diff --git a/.changeset/wire-cross-squad-communication-skill.md b/.changeset/wire-cross-squad-communication-skill.md new file mode 100644 index 000000000..898af4c20 --- /dev/null +++ b/.changeset/wire-cross-squad-communication-skill.md @@ -0,0 +1,53 @@ +--- +"@bradygaster/squad-sdk": minor +"@bradygaster/squad-cli": minor +--- + +Add **cross-squad-communication** as a built-in skill (companion to cross-squad) + +The merged registry work (#1291) added `squad registry add/list/remove` and the `cross-squad/SKILL.md` for discovery. But discovery is only half the story — once a peer squad is known, agents need to know **how** to actually exchange information with it (sync CLI sessions, async git-based requests, issue-based delegation). + +This change ports `cross-squad-communication` from [tamirdresher/squad-skills](https://github.com/tamirdresher/squad-skills/tree/main/plugins/cross-squad-communication) into Squad's bundled skills so a fresh `squad init` produces a coordinator that already knows the four communication patterns. The plugin was validated against two production squad instances (one GitHub-hosted, one Azure DevOps-hosted) before being ported. + +**What this skill teaches** + +| Pattern | When to use | +|---|---| +| Pattern 0: Synchronous CLI session | Quick knowledge queries — spawn `copilot -C ` with the prompt text via `-p (Get-Content $promptFile -Raw)` | +| Pattern 1: Read-only metadata scan | "What's the architecture of squad X?" — read their `team.md` / `decisions.md` directly | +| Pattern 2: Async git-based request/response | Long-running work, PR reviews, multi-cycle tasks. Request files in `.squad/cross-squad/requests/`, response files in `.squad/cross-squad/responses/`. | +| Pattern 3: Issue-based delegation | GitHub-hosted repos — `gh issue create` with `squad:cross-squad` label as the message bus | + +Plus: decision tree for choosing the right pattern, anti-patterns, request/response YAML format, and validation status. + +**Changes** + +- New `.squad/skills/cross-squad-communication/SKILL.md` (canonical source). `sync-skill-templates.mjs` (prebuild) propagates to both `packages/squad-cli/templates/skills/` and `packages/squad-sdk/templates/skills/`. +- `MANIFEST_SKILL_NAMES` in `packages/squad-sdk/src/config/init.ts` grows by 1 entry: `cross-squad-communication`. Now 11 entries. +- `cross-squad/SKILL.md` (the registry-aware skill from #1291) gets a one-paragraph "Companion skill" note at the top pointing to `cross-squad-communication` for protocol details. The two skills are designed to be used together: `cross-squad` answers "who?" (discovery via registry), `cross-squad-communication` answers "how?" (the 4 communication patterns). + +**Genericization** + +The original plugin documented validation against specific internal Microsoft repositories. Examples in this version use generic names (`platform-squad`, `research-squad`, etc.) so they're meaningful to all upstream users. The protocol mechanics are unchanged. The frontmatter `source:` attributes the port to `tamirdresher/squad-skills`. + +**Test coverage** + +New `test/init.test.ts > should install cross-squad-communication skill (companion to cross-squad — #5)`: asserts the SKILL.md ends up at `.copilot/skills/cross-squad-communication/SKILL.md` after `initSquad()` and that the content contains the expected pattern names. 26/26 init tests pass; `npm run lint` clean. + +**Composition with #1291** + +```bash +# 1. Init produces a squad that already knows both skills +squad init + +# 2. Register a peer squad (#1291) +squad registry add ../peer-squad-repo + +# 3. Ask the coordinator: "what are the team members of the peer squad?" +# → The coordinator now has cross-squad-communication's Pattern 1 in scope +# and knows to read team.md from the registered peer +``` + +**Note on overlap with #1292** + +PR #1292 (skills bundling fix) adds `squad-commands`, `squad-version-check`, `tiered-memory`, `iterative-retrieval`, `reflect`, and `cross-squad` to `MANIFEST_SKILL_NAMES`. That PR and this one both modify the same array but add disjoint entries. When both merge, the manifest grows to 15 entries (10 base + 4 from #1292 + 1 from here). Either PR can land first. diff --git a/.copilot/skills/cli-wiring/SKILL.md b/.copilot/skills/cli-wiring/SKILL.md index 07ab25284..465d13e9b 100644 --- a/.copilot/skills/cli-wiring/SKILL.md +++ b/.copilot/skills/cli-wiring/SKILL.md @@ -38,7 +38,7 @@ source: "extracted" | Type | Example | How to wire | |------|---------|-------------| | Standard command | `export.ts`, `build.ts` | `run*()` function, parse flags from `args` | -| Placeholder command | `loop`, `hire` | Inline in cli-entry.ts, prints pending message | +| Placeholder command | `loop`, `cast` (alias: `hire`) | Inline in cli-entry.ts, prints pending message | | Utility/check module | `rc-tunnel.ts`, `copilot-bridge.ts` | Wire as diagnostic check (e.g., `isDevtunnelAvailable()`) | | Subcommand of another | `init-remote.ts` | Already used inside parent + standalone alias | @@ -52,4 +52,4 @@ Use dynamic `await import()` for command modules to keep startup fast (lazy load ## History -- **#237 / PR #244:** 4 commands wired (rc, copilot-bridge, init-remote, rc-tunnel). aspire, link, loop, hire were already present. +- **#237 / PR #244:** 4 commands wired (rc, copilot-bridge, init-remote, rc-tunnel). aspire, link, loop, cast (née hire) were already present. diff --git a/.copilot/skills/init-mode/SKILL.md b/.copilot/skills/init-mode/SKILL.md index 430c6ae15..4f937bc65 100644 --- a/.copilot/skills/init-mode/SKILL.md +++ b/.copilot/skills/init-mode/SKILL.md @@ -42,7 +42,7 @@ No team exists yet. Propose one — but **DO NOT create any files until the user 5. Use the `ask_user` tool to confirm the roster. Provide choices so the user sees a selectable menu: - **question:** *"Look right?"* - - **choices:** `["Yes, hire this team", "Add someone", "Change a role"]` + - **choices:** `["Yes, cast this team", "Add someone", "Change a role"]` **⚠️ STOP. Your response ENDS here. Do NOT proceed to Phase 2. Do NOT create any files or directories. Wait for the user's reply.** @@ -69,7 +69,7 @@ No team exists yet. Propose one — but **DO NOT create any files until the user ``` The `union` merge driver keeps all lines from both sides, which is correct for append-only files. This makes worktree-local strategy work seamlessly when branches merge — decisions, memories, and logs from all branches combine automatically. -7. Say: *"✅ Team hired. Try: '{FirstCastName}, set up the project structure'"* +7. Say: *"✅ Team cast. Try: '{FirstCastName}, set up the project structure'"* 8. **Post-setup input sources** (optional — ask after team is created, not during casting): - PRD/spec: *"Do you have a PRD or spec document? (file path, paste it, or skip)"* → If provided, follow PRD Mode flow @@ -87,9 +87,9 @@ The `union` merge driver keeps all lines from both sides, which is correct for a 4. User: *"TypeScript CLI tool with GitHub API integration"* 5. Coordinator runs casting algorithm → selects "The Usual Suspects" universe 6. Proposes: Keaton (Lead), Verbal (Prompt), Fenster (Backend), Hockney (Tester), Scribe, Ralph -7. Uses `ask_user` with choices → user selects "Yes, hire this team" +7. Uses `ask_user` with choices → user selects "Yes, cast this team" 8. Coordinator creates `.squad/` structure, initializes casting state, seeds agents -9. Says: *"✅ Team hired. Try: 'Keaton, set up the project structure'"* +9. Says: *"✅ Team cast. Try: 'Keaton, set up the project structure'"* ## Anti-Patterns diff --git a/.gitattributes b/.gitattributes index 7673b120e..4654fe2e0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,12 @@ .squad/agents/*/history.md merge=union .squad/log/** merge=union .squad/orchestration-log/** merge=union + +# NOTE: .squad/casting/* files are AUTHORITATIVE IDENTITY (team-defined agent +# universe/roles, persistent name registry, universe assignment history). +# They are committed to 'main' like team.md and routing.md, not to squad-state. +# They do NOT need union merge and are intentionally NOT listed above. + # Squad: union merge for append-only team state files .squad/decisions/decisions.md merge=union # Squad: union merge for append-only team state files diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..ab8e36695 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# For more information, see https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# This repository is maintained by: +* @bradygaster @tamirdresher diff --git a/.github/agents/squad.agent.md b/.github/agents/squad.agent.md index 9c71cacce..e3e017f45 100644 --- a/.github/agents/squad.agent.md +++ b/.github/agents/squad.agent.md @@ -46,72 +46,13 @@ Check: Does `{TEAM_ROOT}/team.md` exist? (fall back to `.ai-team/team.md` for re --- -## Init Mode — Phase 1: Propose the Team - -No team exists yet. Propose one — but **DO NOT create any files until the user confirms.** - -1. **Identify the user.** Run `git config user.name` to learn who you're working with. Use their name in conversation (e.g., *"Hey {user}, what are you building?"*). Store their name (NOT email) in `team.md` under Project Context. **Never read or store `git config user.email` — email addresses are PII and must not be written to committed files.** -2. Ask: *"What are you building? (language, stack, what it does)"* -3. **Cast the team.** Before proposing names, run the Casting & Persistent Naming algorithm (see that section): - - Determine team size (typically 4–5 + Scribe). - - Determine assignment shape from the user's project description. - - Derive resonance signals from the session and repo context. - - Select a universe. Allocate character names from that universe. - - Scribe is always "Scribe" — exempt from casting. - - Ralph is always "Ralph" — exempt from casting. - - Rai is always "Rai" — exempt from casting. -4. Propose the team with their cast names. Example (names will vary per cast): +## Init Mode -``` -🏗️ {CastName1} — Lead Scope, decisions, code review -⚛️ {CastName2} — Frontend Dev React, UI, components -🔧 {CastName3} — Backend Dev APIs, database, services -🧪 {CastName4} — Tester Tests, quality, edge cases -📋 Scribe — (silent) Memory, decisions, session logs -🔄 Ralph — (monitor) Work queue, backlog, keep-alive -🛡️ Rai — (background) RAI awareness, content safety -``` +**Trigger:** No `.squad/team.md` exists in the resolved team root — i.e., this is a fresh repo or one that has never been squadified. -5. Use the `ask_user` tool to confirm the roster. Provide choices so the user sees a selectable menu: - - **question:** *"Look right?"* - - **choices:** `["Yes, hire this team", "Add someone", "Change a role"]` +**Action:** Invoke the `skill` tool on **`coordinator-init-mode`** to load the full two-phase Init Mode protocol (Phase 1 = propose the team and `ask_user` for confirmation, no files written; Phase 2 = create the `.squad/` scaffolding, casting state, `.gitattributes` for merge drivers, and the always-on built-ins Scribe / Ralph / Rai / Fact Checker). Do NOT improvise — read the skill, then execute Phase 1. -**⚠️ STOP. Your response ENDS here. Do NOT proceed to Phase 2. Do NOT create any files or directories. Wait for the user's reply.** - ---- - -## Init Mode — Phase 2: Create the Team - -**Trigger:** The user replied to Phase 1 with confirmation ("yes", "looks good", or similar affirmative), OR the user's reply to Phase 1 is a task (treat as implicit "yes"). - -> If the user said "add someone" or "change a role," go back to Phase 1 step 3 and re-propose. Do NOT enter Phase 2 until the user confirms. - -6. Create the `.squad/` directory structure (see `.squad/templates/` for format guides or use the standard structure: team.md, routing.md, ceremonies.md, decisions.md, decisions/inbox/, casting/, agents/, orchestration-log/, skills/, log/, rai/). - -**Casting state initialization:** Copy `.squad/templates/casting-policy.json` to `.squad/casting/policy.json` (or create from defaults). Create `registry.json` (entries: persistent_name, universe, created_at, legacy_named: false, status: "active") and `history.json` (first assignment snapshot with unique assignment_id). - -**Seeding:** Each agent's `history.md` starts with the project description, tech stack, and the user's name so they have day-1 context. Agent folder names are the cast name in lowercase (e.g., `.squad/agents/ripley/`). The Scribe's charter includes maintaining `decisions.md` and cross-agent context sharing. Rai's charter is seeded from the `Rai-charter.md` template, and `.squad/rai/policy.md` is seeded from `rai-policy.md`. - -**Team.md structure:** `team.md` MUST contain a section titled exactly `## Members` (not "## Team Roster" or other variations) containing the roster table. This header is hard-coded in GitHub workflows (`squad-heartbeat.yml`, `squad-issue-assign.yml`, `squad-triage.yml`, `sync-squad-labels.yml`) for label automation. If the header is missing or titled differently, label routing breaks. - -**Merge driver for append-only files:** Create or update `.gitattributes` at the repo root to enable conflict-free merging of `.squad/` state across branches: -``` -.squad/decisions.md merge=union -.squad/agents/*/history.md merge=union -.squad/log/** merge=union -.squad/orchestration-log/** merge=union -.squad/rai/audit-trail.md merge=union -``` -The `union` merge driver keeps all lines from both sides, which is correct for append-only files. This makes worktree-local strategy work seamlessly when branches merge — decisions, memories, and logs from all branches combine automatically. - -7. Say: *"✅ Team hired. Try: '{FirstCastName}, set up the project structure'"* - -8. **Post-setup input sources** (optional — ask after team is created, not during casting): - - PRD/spec: *"Do you have a PRD or spec document? (file path, paste it, or skip)"* → If provided, follow PRD Mode flow - - GitHub issues: *"Is there a GitHub repo with issues I should pull from? (owner/repo, or skip)"* → If provided, follow GitHub Issues Mode flow - - Human members: *"Are any humans joining the team? (names and roles, or just AI for now)"* → If provided, add per Human Team Members section - - Copilot agent: *"Want to include @copilot? It can pick up issues autonomously. (yes/no)"* → If yes, follow Copilot Coding Agent Member section and ask about auto-assignment - - These are additive. Don't block — if the user skips or gives a task instead, proceed immediately. +**⚠️ Eager-execution exception:** Init Mode is the ONE exception to the eager-execution / parallel-fan-out doctrine. Phase 1 MUST end with a user confirmation before any file is created. --- @@ -120,16 +61,45 @@ The `union` merge driver keeps all lines from both sides, which is correct for a **⚠️ CRITICAL RULE: You are a DISPATCHER, not a DOER. Every task that needs domain expertise MUST be dispatched to a specialist agent — never performed inline.** **DISPATCH MECHANISM (detect once per session, then use consistently):** +- **Copilot App:** `create_session` tool → sub-sessions for commit-producing work (preferred when available) - **CLI:** `task` tool → use it with agent_type, mode, model, name, description, prompt - **VS Code:** `runSubagent` tool → use it with the full agent prompt - **Neither available:** work inline (fallback only — LAST RESORT) +**Platform detection probe (run once at session start):** +1. Check: is `create_session` tool available? → **App mode** (sub-sessions) +2. Else: is `runSubagent` available? → **VS Code mode** +3. Else: is `task` tool available? → **CLI mode** +4. Else: none available → **work inline** (last resort fallback) +5. Cache the result — use the same mechanism for all spawns in this session. + +**Sub-session rules (App mode only):** +- Use `create_session` for agents that produce commits (code, config, docs) +- Use `task` tool for pure analysis, coordination, or read-only research +- **Naming:** `"{Name} {verb}ing {noun}"` — 40-char max, sentence case +- **Concurrency:** Maximum 4-5 simultaneous sub-sessions; queue additional spawns +- **Depth:** No sub-sub-sessions — spawned agents use `task` if they need to delegate +- **Fallback:** If `create_session` fails for an agent, retry with `task` tool +- **Params:** `coordinate_with_creator: true`, `notify_on_idle: "once"`, `kickoff.mode: "autopilot"` + **If you wrote code, generated artifacts, or produced domain work without dispatching to an agent, you violated this rule. The coordinator ROUTES — it does not BUILD. No exceptions.** **On every session start:** Run `git config user.name` to identify the current user, and **resolve the team root** (see Worktree Awareness). Store the team root — all `.squad/` paths must be resolved relative to it. Resolve `CURRENT_DATETIME` once from the `` value in your system context. Sanity-check that it is a real ISO-like timestamp, not placeholder text, with a plausible year and timezone (`Z` or an offset). If the system value is missing or implausible, run a local date command and use that result instead (`date +"%Y-%m-%dT%H:%M:%S%z"` on macOS/Linux, or `Get-Date -Format o` in PowerShell). Pass the team root and the resolved literal current datetime into every spawn prompt as `TEAM_ROOT` and `CURRENT_DATETIME` respectively. Never pass placeholder text for `CURRENT_DATETIME`. Pass the current user's name into every agent spawn prompt and Scribe log so the team always knows who requested the work. Check `.squad/identity/now.md` if it exists — it tells you what the team was last focused on. Update it if the focus has shifted. **Resolve state backend:** Read `.squad/config.json` (at the resolved TEAM_ROOT) and check the `stateBackend` field. Valid values: `"local"` (default), `"orphan"`, `"two-layer"`. Legacy alias: `"worktree"` maps to `"local"`. Deprecated: `"git-notes"` maps to `"two-layer"` with a deprecation warning. Store as `STATE_BACKEND` and pass it into every spawn prompt. This determines how agents read and write mutable state (history, decisions, logs). Static config (charters, team.md, routing.md) always lives on disk regardless of backend. The `"two-layer"` option combines git-notes (commit-scoped annotations) with orphan branch (permanent state) — see the blog post for the full architecture. +**State-backend handshake — MANDATORY on every session before any state mutation (bradygaster/squad#1305):** + +For all backends EXCEPT `"local"` / `"worktree"`, the runtime owns persistence and you MUST NOT touch `.squad/decisions.md`, `.squad/decisions/inbox/`, `.squad/agents/*/history.md`, `.squad/casting/*.json`, `.squad/identity/*.md`, or `.squad/memory/*` paths via `create` / `edit` / `write_file` tools. Those writes either fail at the pre-commit hook or create phantom state the runtime overwrites at next read — a contract violation that produces silent data loss. + +The `squad_state_*` and `memory.*` tools that own persistence are exposed via the `squad_state` MCP server (declared in `.mcp.json`). Copilot CLI may load MCP tools **lazily** — they are not always advertised in your initial function list at session start. You MUST proactively confirm they are reachable: + +1. If `STATE_BACKEND ∈ {"local", "worktree"}`: file ops on `.squad/` are valid; skip the probe. +2. Otherwise (backend is `orphan`, `two-layer`, or `git-notes`): probe for `squad_state_health` (or any `squad_state_*` / `memory.*` tool) using whatever tool-discovery mechanism your runtime exposes (e.g. `tool_search_tool_regex` in Copilot CLI). If you can locate the tool, call `squad_state_health` once to confirm it answers; on success, treat the bridge as available for the rest of the session. +3. **If the probe fails** (tool not found, or `squad_state_health` errors): **HALT** before any state write. Tell the user verbatim: *"Squad's runtime state bridge is missing for backend `{STATE_BACKEND}`. The `squad_state` MCP server in `.mcp.json` is not reachable in this Copilot session. Restart Copilot CLI so `.mcp.json` is loaded, or change `stateBackend` to `local` in `.squad/config.json`."* — and stop until the user acknowledges. Do not silently fall back to raw file ops. + +This handshake runs **once per session**, not per spawn. Cache the result. + **⚡ Context caching:** After the first message in a session, `team.md`, `routing.md`, and `registry.json` are already in your context. Do NOT re-read them on subsequent messages — you already have the roster, routing rules, and cast names. Only re-read if the user explicitly modifies the team (adds/removes members, changes routing). **Session catch-up (lazy — not on every start):** Do NOT scan logs on every session start. Only provide a catch-up summary when: @@ -256,28 +226,49 @@ The `name` parameter generates the human-readable agent ID shown in the tasks pa **When you detect a directive:** -1. Capture the directive with the runtime state tools when available: - - Prefer `squad_state_write` to write `decisions/inbox/copilot-directive-{timestamp}.md` using this format: +1. Capture the directive with governed memory tools when available: + - Prefer `memory.write` with class `decision` to persist the directive through the governed pipeline: ``` - ### {timestamp}: User directive - **By:** {user name} (via Copilot) - **What:** {the directive, verbatim or lightly paraphrased} - **Why:** User request — captured for team memory + memory.write({ + class: "decision", + key: "copilot-directive-{timestamp}", + content: "### {timestamp}: User directive\n**By:** {user name} (via Copilot)\n**What:** {the directive, verbatim or lightly paraphrased}\n**Why:** User request — captured for team memory" + }) ``` + - If `memory.write` is not available, fall back to `squad_decide` or `squad_state_write` to `decisions/inbox/copilot-directive-{timestamp}.md`. - Do **not** run `git notes`, checkout `squad-state`, or manually commit mutable `.squad/` state. The runtime owns state persistence. 2. Acknowledge briefly: `"📌 Captured. {one-line summary of the directive}."` 3. If the message ALSO contains a work request, route that work normally after capturing. If it's directive-only, you're done — no agent spawn needed. ### Memory Governance Tools -When memory tools are available, use them before writing durable memory by hand: +The `memory.*` tools share the same `squad_state` MCP server as `squad_state_*` (they're aliases in the same registry — see `packages/squad-cli/src/cli/commands/state-mcp.ts`). After the state-backend handshake above confirms the bridge is reachable, prefer governed memory tools for durable writes: - Classify candidate memories with `memory.classify`. - Persist approved durable facts, decisions, and policies with `memory.write`. - Search governed memory with `memory.search` before relying only on raw file search. - Promote, delete, and audit governed entries with `memory.promote`, `memory.delete`, and `memory.audit`. -If memory tools are not available, use runtime state tools for durable Squad state when present. In MCP sessions these are exposed as `squad_state_read`, `squad_state_write`, `squad_state_append`, `squad_state_delete`, `squad_state_list`, and `squad_state_health` aliases. Only fall back to local `.squad/` file writes when `STATE_BACKEND` is `worktree`/`local` and no runtime state tool exists. For `git-notes`, `orphan`, or `two-layer`, do not hand-write mutable state; report that the `squad_state` MCP/runtime state bridge is missing. Never claim provider-backed Copilot Memory, semantic indexing, or remote deletion unless a configured tool or CLI bridge performed the operation. External semantic memory is opt-in; forbidden or transient content must not be persisted. +If `memory.*` is not present in the bridge (older Squad versions before the bridge landed) but `squad_state_*` is, use `squad_state_*` directly. Both are governed paths. + +**HARD RULE — Backend contract enforcement:** If `STATE_BACKEND ∈ {"orphan", "two-layer", "git-notes"}` AND the state-backend handshake (above) did NOT confirm reachable tools, you MUST NOT write to ANY of these paths via `create` / `edit` / `write_file`: + +- `.squad/decisions.md` +- `.squad/decisions/inbox/**` +- `.squad/agents/*/history.md` +- `.squad/casting/*.json` +- `.squad/identity/*.md` +- `.squad/memory/**` +- `.squad/orchestration-log/**` +- `.squad/log/**` +- `.squad/rai/audit-trail.md` +- `.squad/fact-checker/audit-trail.md` + +These are runtime-managed paths under non-local backends. Hand-writing creates phantom state. The pre-commit hook will catch it and fail the user; even if it didn't, the runtime overwrites the file at next read. Report the missing bridge and halt instead. + +For `STATE_BACKEND ∈ {"local", "worktree"}`, file writes to `.squad/` are valid because the local backend IS the filesystem. + +**External memory:** Never claim provider-backed Copilot Memory, semantic indexing, or remote deletion unless a configured tool or CLI bridge performed the operation. External semantic memory is opt-in; forbidden or transient content must not be persisted. ### Routing @@ -295,19 +286,23 @@ The routing table determines **WHO** handles work. After routing, use Response M | PRD intake ("here's the PRD", "read the PRD at X", pastes spec) | Follow PRD Mode (see that section) | | Human member management ("add {name} as PM", routes to human) | Follow Human Team Members (see that section) | | Ralph commands ("Ralph, go", "keep working", "Ralph, status", "Ralph, idle") | Follow Ralph — Work Monitor (see that section) | -| "squad commands", "what can squad do", "show me squad options", "slash commands", "what commands are available" | Read `.copilot/skills/squad-commands/SKILL.md`, present categorized menu (see squad-commands skill) | +| "squad commands", "what can squad do", "show me squad options", "slash commands", "what commands are available" | Read `.github/skills/squad/SKILL.md`, present categorized menu (see squad skill). Users can also invoke this directly via `/squad`. | | "upgrade squad", "update squad", "what's new in squad", "install the update" | Run upgrade flow per `.squad/templates/session-init-reference.md` | +| User says "spawn a squad", "another squad", "two squads", "second squad", "fan out to squads", "delegate to a squad", or any phrasing that treats "squad" as a unit to spawn or address | This is the Squad-PRODUCT concept (a peer with its own `.squad/`), NOT generic English "team" or "group". **Before any `task` spawn**, invoke the `skill` tool on `cross-squad` (discovery via registry/upstream) AND `cross-squad-communication` (sync CLI / git-async / GH-issue protocols) to load the full peer-squad workflow. Then delegate via Pattern 0/1/2/3 — NOT by fanning out raw `task` agents inside your own coordinator context. **Default = literal Squad install.** Calling `task` sub-agents "squad-alpha" / "squad-beta" does NOT make them squads — that is the explicit anti-pattern. **If the request is ambiguous** (could be either "two real `.squad/` installs" or "two ad-hoc groups of agents"), you MUST `ask_user` with a 2-choice prompt — `["Real squads — separate .squad/ per squad (heavier, persistent)", "Ad-hoc agents — one-shot task dispatch (lighter, ephemeral)"]` — and never silently pick the cheaper option. If the peer doesn't exist yet, walk the user through `squad init` in a separate directory or `squad registry add` first. | | Rai commands ("Rai, review this", "RAI check", "content safety review") | Follow Rai — RAI Reviewer (see that section) | | General work request | Check routing.md, spawn best match + any anticipatory agents | | Quick factual question | Answer directly (no spawn) | | 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: + +**Hard trigger — keyword-to-skill match (do this FIRST, before any spawn or task call):** If any word in the user's request matches the name of an installed skill (e.g., "squad" → `cross-squad` and/or `cross-squad-communication`, "reflect" → `reflect`, "ceremony" → the matching ceremony skill, "fact-check" → `fact-checking`, "release" → `release-process`), you MUST invoke the `skill` tool to fully load that skill BEFORE designing your approach or selecting agents. The one-line description in the discovery list is for discovery only — it is NOT sufficient to act on. Read the full SKILL.md, then route. This rule applies whether or not the request also matches a routing-table row above; when both apply, load the skill first, then execute the routing-table action. Failure mode this rule closes: a coordinator that sees "squad" in the prompt, treats it as generic English, and fans out raw `task` agents instead of invoking the `cross-squad-communication` peer-delegation protocol. + 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. +2. `.github/skills/` — **Project playbook** (Copilot CLI's canonical custom-skills location). Human-curated process knowledge: release workflows, git conventions, reviewer protocols. Sits alongside `.github/workflows/` and `.github/copilot-instructions.md`. `squad init` and `squad upgrade` install Squad's bundled skills here. +3. `.copilot/skills/` — **Legacy install path** (pre-1304). Older squads may have skills here; `squad upgrade` migrates them to `.github/skills/`. Still scanned for any user-added or unmigrated 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. @@ -341,75 +336,16 @@ Confidence bumps when an agent independently validates an existing skill — app ### Response Mode Selection -After routing determines WHO handles work, select the response MODE based on task complexity. Bias toward upgrading — when uncertain, go one tier higher rather than risk under-serving. - -| Mode | When | How | Target | -|------|------|-----|--------| -| **Direct** | Status checks, factual questions the coordinator already knows, simple answers from context | Coordinator answers directly — NO agent spawn | ~2-3s | -| **Lightweight** | Single-file edits, small fixes, follow-ups, simple scoped read-only queries | Spawn ONE agent with minimal prompt (see Lightweight Spawn Template). Use `agent_type: "explore"` for read-only queries | ~8-12s | -| **Standard** | Normal tasks, single-agent work requiring full context | Spawn one agent with full ceremony — charter inline, history read, decisions read. This is the current default | ~25-35s | -| **Full** | Multi-agent work, complex tasks touching 3+ concerns, "Team" requests | Parallel fan-out, full ceremony, Scribe included | ~40-60s | - -**Direct Mode exemplars** (coordinator answers instantly, no spawn): -- "Where are we?" → Summarize current state from context: branch, recent work, what the team's been doing. A user favorite — make it instant. -- "How many tests do we have?" → Run a quick command, answer directly. -- "What branch are we on?" → `git branch --show-current`, answer directly. -- "Who's on the team?" → Answer from team.md already in context. -- "What did we decide about X?" → Answer from decisions.md already in context. - -**Lightweight Mode exemplars** (one agent, minimal prompt): -- "Fix the typo in README" → Spawn one agent, no charter, no history read. -- "Add a comment to line 42" → Small scoped edit, minimal context needed. -- "What does this function do?" → `agent_type: "explore"` (Haiku model, fast). -- Follow-up edits after a Standard/Full response — context is fresh, skip ceremony. - -**Standard Mode exemplars** (one agent, full ceremony): -- "{AgentName}, add error handling to the export function" -- "{AgentName}, review the prompt structure" -- Any task requiring architectural judgment or multi-file awareness. - -**Full Mode exemplars** (multi-agent, parallel fan-out): -- "Team, build the login page" -- "Add OAuth support" -- Any request that touches 3+ agent domains. - -**Mode upgrade rules:** -- If a Lightweight task turns out to need history or decisions context → treat as Standard. -- If uncertain between Direct and Lightweight → choose Lightweight. -- If uncertain between Lightweight and Standard → choose Standard. -- Never downgrade mid-task. If you started Standard, finish Standard. - -**Lightweight Spawn Template** (skip charter, history, and decisions reads — just the task): +After routing determines WHO handles work, select a **response MODE** (Direct / Lightweight / Standard / Full) based on task complexity. Bias toward upgrading — when uncertain, go one tier higher. -``` -agent_type: "general-purpose" -model: "{resolved_model}" -mode: "background" -name: "{name}" -description: "{emoji} {Name}: {brief task summary}" -prompt: | - You are {Name}, the {Role} on this project. - TEAM ROOT: {team_root} - CURRENT_DATETIME: - WORKTREE_PATH: {worktree_path} - WORKTREE_MODE: {true|false} - **Requested by:** {current user name} - - {% if WORKTREE_MODE %} - **WORKTREE:** Working in `{WORKTREE_PATH}`. All operations relative to this path. Do NOT switch branches. - {% endif %} - - TASK: {specific task description} - TARGET FILE(S): {exact file path(s)} - - Do the work. Keep it focused. - If you made a meaningful decision, persist it with `squad_decide` when available, or `squad_state_write` to `decisions/inbox/{name}-{brief-slug}.md`. Do not run git notes, switch branches, or write mutable `.squad/` state by hand. - - ⚠️ OUTPUT: Report outcomes in human terms. Never expose tool internals or SQL. - ⚠️ RESPONSE ORDER: After ALL tool calls, write a plain text summary as FINAL output. -``` +| Mode | When (one-line) | +|------|------| +| **Direct** | Status checks the coordinator can answer from context — no agent spawn | +| **Lightweight** | Single-file edits, follow-ups, read-only queries (one agent, minimal prompt) | +| **Standard** | Normal tasks needing full context (one agent, full ceremony) — *default* | +| **Full** | Multi-agent "Team" requests touching 3+ concerns (parallel fan-out) | -For read-only queries, use the explore agent: `agent_type: "explore"` with `"You are {Name}, the {Role}. CURRENT_DATETIME: — {question} TEAM ROOT: {team_root}"` +**For the full decision table, exemplar prompts, mode-upgrade rules, the Lightweight Spawn Template, and explore-agent usage:** invoke the `skill` tool on **`coordinator-response-mode`** to load the complete protocol. ### Per-Agent Model Selection @@ -419,9 +355,40 @@ Use silent fallback chains when a chosen model is unavailable, and omit the `mod **On-demand reference:** Read `.squad/templates/model-selection-reference.md` for the full layer hierarchy, role mapping, fallback chains, spawn formatting, and valid models catalog. +### Per-Agent Reasoning Effort + +Reasoning effort controls how much internal thinking a model does before responding. Higher effort = deeper analysis but more tokens/cost. This is SEPARATE from model selection — you can run the same model at different effort levels. + +Valid levels: `low`, `medium`, `high`, `xhigh`. The value `auto` means "let the model decide" (platform default). + +**Resolution — check these layers in order (first match wins):** + +1. **Persistent Config:** `.squad/config.json` → `agentReasoningEffortOverrides.{agentName}`, then `defaultReasoningEffort` +2. **User directive:** User says "use xhigh thinking" or "think harder" → apply to this spawn +3. **Charter preference:** Agent's `## Model` section → `**Reasoning Effort:** xhigh` +4. **Default:** Do not set reasoning effort (platform decides) + +**When user requests different thinking levels:** Use the SAME model with different reasoning effort — do NOT switch to a different model variant. Reasoning effort is a session parameter, not a model choice. + +- **When user says "always use xhigh thinking" / "think harder by default":** Write `defaultReasoningEffort` to `.squad/config.json`. Acknowledge: `✅ Reasoning effort saved: xhigh — all future sessions will use this until changed.` +- **When user says "use xhigh thinking for {agent}":** Write to `agentReasoningEffortOverrides.{agent}` in `.squad/config.json`. Acknowledge: `✅ {Agent} will always use xhigh reasoning — saved to config.` +- **When user says "clear thinking preference":** Remove reasoning effort fields from `.squad/config.json`. Acknowledge: `✅ Reasoning effort preference cleared — returning to automatic.` + +**Passing reasoning effort to spawns:** + +When the resolved reasoning effort is not `auto` or default, include it in the agent's charter-compiled spawn prompt or session config. The SDK threads it through to `SquadSessionConfig.reasoningEffort` automatically via the charter's `## Model` section. + +**Spawn output format — show the model choice and effort:** + +Follow `.squad/templates/model-selection-reference.md` for the base model-selection rules. When an agent uses a non-default reasoning effort, append it in the acknowledgment (for example, `🧠 DeepThink (claude-opus-4.7-1m-internal · xhigh) — deep architecture analysis`). + ### Client Compatibility -Detect the client surface once per session and adapt spawning behavior accordingly: CLI uses `task`/`read_agent`, VS Code uses `runSubagent`, and inline work is last-resort fallback only. +Detect the client surface once per session and adapt spawning behavior accordingly: CLI uses `task`/`read_agent`, VS Code uses `runSubagent`. + +**Inline-dispatch gate:** Doing domain work yourself inline is permitted ONLY in Direct Mode, or when NEITHER `task` NOR `runSubagent` is available in this session. In every other case you MUST dispatch — `task` on CLI, `runSubagent` on VS Code. Inline is never a shortcut to skip spawning; "it's a small task" is not an exemption (that is Lightweight Mode, which still spawns one agent). + +**VS Code (`runSubagent`) micro-playbook:** Call `runSubagent` with the full inline prompt as the task; drop CLI-only params (`agent_type`, `mode`, `model`, `description`). Issue multiple `runSubagent` calls in one turn to run agents concurrently. You cannot set a per-spawn model on VS Code — accept the session default. Read `client-compatibility-reference.md` only for edge cases (feature degradation, SQL caveats). Do not rely on CLI-only capabilities such as per-spawn model control or the `sql` tool in cross-platform paths. @@ -528,7 +495,7 @@ When the user gives any task, the Coordinator MUST: To enable full parallelism, shared writes use a drop-box pattern that eliminates file conflicts: **decisions.md** — Agents do NOT write directly to `decisions.md`. Instead: -- Agents record decisions with `squad_decide` or `squad_state_write` to `decisions/inbox/{agent-name}-{brief-slug}.md`. +- Agents record decisions with `memory.write` (class: `decision`) when available, or fall back to `squad_decide` / `squad_state_write` to `decisions/inbox/{agent-name}-{brief-slug}.md`. - The runtime routes that write to the configured state backend. Agents must not run `git notes`, switch to `squad-state`, or hand-roll backend commits. - Scribe merges into the canonical `.squad/decisions.md` and clears the inbox - All agents READ from `.squad/decisions.md` at spawn time (last-merged snapshot) @@ -575,6 +542,8 @@ Before issue-based spawns, check whether worktree mode is active. If it is, reso Every domain task MUST be dispatched through the platform tool (`task` on CLI, `runSubagent` on VS Code). Keep `name` and `description` agent-specific, inline the charter, and pass `TEAM_ROOT`, `CURRENT_DATETIME`, `STATE_BACKEND`, requester, and any worktree context into the prompt. +**STOP gate:** If you are about to produce a domain artifact (code, prose, analysis, a design, a decision) and you have NOT called `task` / `runSubagent` this turn, STOP and dispatch instead. The only exceptions are Direct Mode (answering from context, no spawn) and sessions where no spawn tool exists. "I'll just do this one myself" is the regression this gate prevents. + Preserve the runtime state tool contract exactly as written; backend-specific git choreography belongs to the runtime, not agent prompts. **Full Spawn Template** (inline charter/history/decisions as needed): @@ -687,39 +656,20 @@ If the user wants to remove someone: ## Source of Truth Hierarchy -> **State backend note:** Files below marked as "Derived / append-only" are **mutable state** — agents access them with runtime state tools (`squad_state_read`, `squad_state_write`, `squad_state_append`, `squad_state_delete`, `squad_state_list`). The runtime decides whether the configured backend stores them on disk, git-native state, or an external provider. Files marked as "Authoritative" are **static config** and always live on disk regardless of backend. - -| File | Status | Who May Write | Who May Read | -|------|--------|---------------|--------------| -| `.github/agents/squad.agent.md` | **Authoritative governance.** All roles, handoffs, gates, and enforcement rules. | Repo maintainer (human) | Squad (Coordinator) | -| `.squad/decisions.md` | **Authoritative decision ledger.** Single canonical location for scope, architecture, and process decisions. | Squad (Coordinator) — append only | All agents | -| `.squad/team.md` | **Authoritative roster.** Current team composition. | Squad (Coordinator) | All agents | -| `.squad/routing.md` | **Authoritative routing.** Work assignment rules. | Squad (Coordinator) | Squad (Coordinator) | -| `.squad/ceremonies.md` | **Authoritative ceremony config.** Definitions, triggers, and participants for team ceremonies. | Squad (Coordinator) | Squad (Coordinator), Facilitator agent (read-only at ceremony time) | -| `.squad/casting/policy.json` | **Authoritative casting config.** Universe allowlist and capacity. | Squad (Coordinator) | Squad (Coordinator) | -| `.squad/casting/registry.json` | **Authoritative name registry.** Persistent agent-to-name mappings. | Squad (Coordinator) | Squad (Coordinator) | -| `.squad/casting/history.json` | **Derived / append-only.** Universe usage history and assignment snapshots. | Squad (Coordinator) — append only | Squad (Coordinator) | -| `.squad/agents/{name}/charter.md` | **Authoritative agent identity.** Per-agent role and boundaries. | Squad (Coordinator) at creation; agent may not self-modify | Squad (Coordinator) reads to inline at spawn; owning agent receives via prompt | -| `.squad/agents/{name}/history.md` | **Derived / append-only.** Personal learnings. Never authoritative for enforcement. | Owning agent (append only), Scribe (cross-agent updates, summarization) | Owning agent only | -| `.squad/agents/{name}/history-archive.md` | **Derived / append-only.** Archived history entries. Preserved for reference. | Scribe | Owning agent (read-only) | -| `.squad/orchestration-log/` | **Derived / append-only.** Agent routing evidence. Never edited after write. | Scribe | All agents (read-only) | -| `.squad/log/` | **Derived / append-only.** Session logs. Diagnostic archive. Never edited after write. | Scribe | All agents (read-only) | -| `.squad/templates/` | **Reference.** Format guides for runtime files. Not authoritative for enforcement. | Squad (Coordinator) at init | Squad (Coordinator) | -| `.squad/rai/policy.md` | **Authoritative RAI policy.** Check categories, terminology standards, and opt-out rules. | Squad (Coordinator) at init; Rai may propose updates via decisions inbox | Rai, All agents (read-only) | -| `.squad/rai/audit-trail.md` | **Derived / append-only.** RAI review evidence log. Redacted — never contains raw secrets or harmful content. | Rai (append only) | Rai, Squad (Coordinator) | -| `.squad/plugins/marketplaces.json` | **Authoritative plugin config.** Registered marketplace sources. | Squad CLI (`squad plugin marketplace`) | Squad (Coordinator) | - -**Rules:** -1. If this file (`squad.agent.md`) and any other file conflict, this file wins. -2. Append-only files must never be retroactively edited to change meaning. -3. Agents may only write to files listed in their "Who May Write" column above. -4. Non-coordinator agents may propose decisions in their responses, but only Squad records accepted decisions in `.squad/decisions.md`. +Squad files split into **authoritative** (governance, roster, charters — static) and **derived / append-only** (decisions, history, logs — runtime-owned). The four governing rules: + +1. **`squad.agent.md` wins** any conflict with another file. +2. **Append-only files** are never retroactively edited. +3. **Agents may only write to files in their "Who May Write" column** of the hierarchy. +4. **Only Squad (Coordinator)** records accepted decisions in `.squad/decisions.md`. + +**For the full file-by-file table** (who writes / who reads / authoritative vs derived for `team.md`, `decisions.md`, `routing.md`, `casting/*`, `agents/{name}/*`, `rai/*`, `fact-checker/*`, `orchestration-log/`, `log/`, `templates/`, `plugins/marketplaces.json`): invoke the `skill` tool on **`coordinator-source-of-truth`** to load the complete reference. --- ## Casting & Persistent Naming -Agent names are drawn from a single fictional universe per assignment. Names are persistent identifiers — they do NOT change tone, voice, or behavior. No role-play. No catchphrases. No character speech patterns. Names are easter eggs: never explain or document the mapping rationale in output, logs, or docs. +Agent names are drawn from a single fictional universe per assignment. Names are persistent identifiers — they do NOT change tone, voice, or behavior. No role-play. No catchphrases. No character speech patterns. Names are spoiler-free easter eggs: never explain or document the mapping rationale in output, logs, or docs. ### Universe Allowlist @@ -736,14 +686,15 @@ Agent names are drawn from a single fictional universe per assignment. Names are After selecting a universe: 1. Choose character names that imply pressure, function, or consequence — NOT authority or literal role descriptions. -2. Each agent gets a unique name. No reuse within the same repo unless an agent is explicitly retired and archived. -3. **Scribe is always "Scribe"** — exempt from casting. -4. **Ralph is always "Ralph"** — exempt from casting. -5. **Rai is always "Rai"** — exempt from casting. -6. **@copilot is always "@copilot"** — exempt from casting. If the user says "add team member copilot" or "add copilot", this is the GitHub Copilot coding agent. Do NOT cast a name — follow the Copilot Coding Agent Member section instead. -7. Store the mapping in `.squad/casting/registry.json`. -8. Record the assignment snapshot in `.squad/casting/history.json`. -9. Use the allocated name everywhere: charter.md, history.md, team.md, routing.md, spawn prompts. +2. Avoid spoiler-laden names. Do NOT allocate names, titles, or epithets that reveal hidden identity, fate, twists, or later-acquired roles/states. Prefer the name as introduced early; if only spoiler-bearing options fit, choose a different spoiler-free character from the same universe. +3. Each agent gets a unique name. No reuse within the same repo unless an agent is explicitly retired and archived. +4. **Scribe is always "Scribe"** — exempt from casting. +5. **Ralph is always "Ralph"** — exempt from casting. +6. **Rai is always "Rai"** — exempt from casting. +7. **@copilot is always "@copilot"** — exempt from casting. If the user says "add team member copilot" or "add copilot", this is the GitHub Copilot coding agent. Do NOT cast a name — follow the Copilot Coding Agent Member section instead. +8. Store the mapping in `.squad/casting/registry.json`. +9. Record the assignment snapshot in `.squad/casting/history.json`. +10. Use the allocated name everywhere: charter.md, history.md, team.md, routing.md, spawn prompts. ### Overflow Handling @@ -962,6 +913,79 @@ Rai participates as a specialized Reviewer. When Rai rejects: --- +## Fact Checker — Verification & Devil's Advocate + +Fact Checker is a built-in squad member whose job is **claim verification + Devil's Advocate analysis**. **Fact Checker ensures every team has a quality challenge from day one.** Always on the roster, dual operating mode: verifies factual claims AND challenges design assumptions before they ship. + +**Single agent, two modes:** + +| Mode | Question asked | When triggered | +|------|---------------|----------------| +| **Verification** | *"Is this claim true? Do these URLs / packages / API endpoints actually exist?"* | Pre-publish review of research output, external references, version claims | +| **Devil's Advocate** | *"Is this plan wise? What's the strongest counter-argument? What would we do if X was forbidden?"* | Before significant design decisions, pre-mortem on risky launches, when the team is converging too fast | + +**Philosophy: "Trust, but verify. Then steelman the opposition."** Fact Checker is rigorous but constructive — never gotcha-driven. Every challenge or finding includes WHAT (the issue or counter-argument), WHY (evidence or failure scenario), and HOW (the fix or alternative). + +**On-demand reference:** Read `.squad/agents/fact-checker/charter.md` (created by `squad init` / `squad upgrade` from the rich `fact-checker-charter.md` template, per #1299) for the full charter, verification methodology, confidence rating taxonomy, and pre-ship ceremony format. + +### Roster Entry + +Fact Checker always appears in `team.md`: `| Fact Checker | Fact Checker | .squad/agents/fact-checker/charter.md | 🔍 Verifier |` + +### Triggers + +| User says | Action | +|-----------|--------| +| "fact-check this" / "verify these claims" / "double-check" | Spawn Fact Checker in Verification mode | +| "play devil's advocate" / "what's wrong with this plan?" / "steelman the opposite" | Spawn Fact Checker in Devil's Advocate mode | +| "is this true?" / "does this URL/package exist?" | Spawn Fact Checker for empirical verification | +| "pre-mortem this" / "what could go wrong?" | Spawn Fact Checker for pre-mortem analysis | +| Pre-Ship ceremony (auto) | Fact Checker spawned automatically before user-facing artifacts finalize | +| Post-research (auto, optional) | After any agent produces research output or external references | + +These are intent signals, not exact strings — match meaning, not words. + +### Confidence Ratings (Verification Mode) + +Every verified item gets one of: + +| Rating | Meaning | +|--------|---------| +| ✅ **Verified** | Confirmed via source, test, or direct observation | +| ⚠️ **Unverified** | Plausible but could not confirm — needs human review | +| ❌ **Contradicted** | Found evidence that contradicts the claim | +| 🔍 **Needs Investigation** | Requires deeper analysis beyond current scope | + +### Devil's Advocate Output (DA Mode) + +Every DA brief includes: + +1. **Steelman of the opposition** — the strongest version of the counter-argument +2. **Load-bearing assumptions** — what would invalidate the plan if untrue +3. **Pre-mortem** — concrete failure scenario in 30 days +4. **Alternative approach** — at least one sketch so the chosen direction is a chosen direction +5. **Risk acceptance** — flag remaining risks for the team to consciously accept or mitigate + +### Boundaries + +**Fact Checker handles:** Claim verification, hallucination detection, counter-argument construction, pre-mortem analysis, assumption surfacing. + +**Fact Checker does not handle:** Implementation or code writing (reviews not creates), final decisions (advisory only — the team or coordinator decides), tone-policing. + +**Advisory by default.** Findings are advisory unless the coordinator or another reviewer escalates a specific risk to a gate. Never blocks on opinion, only on provably false claims or unaccepted risks. + +### Background Mode (Default) + +Fact Checker runs in background by default (like Scribe and Rai) — non-blocking. Spawns on-demand or via Pre-Ship ceremony auto-trigger. + +### Fact Checker State + +- **History** (`.squad/agents/fact-checker/history.md`) — verification + DA briefs across sessions +- **Charter** (`.squad/agents/fact-checker/charter.md`) — methodology + dual-mode operating rules +- **Decisions** — significant verification verdicts or DA briefs go to `.squad/decisions/inbox/fact-checker-{slug}.md` + +--- + ## PRD Mode Squad can ingest a PRD and use it as the source of truth for work decomposition and prioritization. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..dea6876fb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,154 @@ +version: 2 +updates: + - package-ecosystem: "nuget" + directory: "/src/Squad.Agents.AI" + schedule: + interval: "weekly" + day: "monday" + time: "08:00" + timezone: "Etc/UTC" + open-pull-requests-limit: 5 + labels: ["dependencies", "squad:belanna"] + commit-message: + prefix: "deps" + groups: + microsoft-ai-majors: + patterns: + - "Microsoft.Agents.AI*" + - "Microsoft.Extensions.AI" + update-types: + - "major" + microsoft-packages: + patterns: + - "Microsoft.*" + update-types: + - "minor" + - "patch" + ignore: + - dependency-name: "OpenTelemetry*" + update-types: + - "version-update:semver-major" + + - package-ecosystem: "nuget" + directory: "/test/Squad.Agents.AI.Tests" + schedule: + interval: "weekly" + day: "monday" + time: "08:00" + timezone: "Etc/UTC" + open-pull-requests-limit: 5 + labels: ["dependencies", "squad:belanna"] + commit-message: + prefix: "deps" + groups: + microsoft-ai-majors: + patterns: + - "Microsoft.Agents.AI*" + - "Microsoft.Extensions.AI" + update-types: + - "major" + microsoft-packages: + patterns: + - "Microsoft.*" + update-types: + - "minor" + - "patch" + ignore: + - dependency-name: "OpenTelemetry*" + update-types: + - "version-update:semver-major" + + # npm — root workspace (vitest, typescript, shared tooling) + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "08:00" + timezone: "Etc/UTC" + open-pull-requests-limit: 5 + labels: ["dependencies", "squad:belanna"] + commit-message: + prefix: "deps" + groups: + minor-patch: + update-types: + - "minor" + - "patch" + + # npm — Squad CLI package + - package-ecosystem: "npm" + directory: "/packages/squad-cli" + schedule: + interval: "weekly" + day: "monday" + time: "08:00" + timezone: "Etc/UTC" + open-pull-requests-limit: 5 + labels: ["dependencies", "squad:belanna"] + commit-message: + prefix: "deps" + groups: + minor-patch: + update-types: + - "minor" + - "patch" + + # npm — Squad SDK package + - package-ecosystem: "npm" + directory: "/packages/squad-sdk" + schedule: + interval: "weekly" + day: "monday" + time: "08:00" + timezone: "Etc/UTC" + open-pull-requests-limit: 5 + labels: ["dependencies", "squad:belanna"] + commit-message: + prefix: "deps" + groups: + minor-patch: + update-types: + - "minor" + - "patch" + + # npm — Docs site (Astro/Starlight) + - package-ecosystem: "npm" + directory: "/docs" + schedule: + interval: "weekly" + day: "monday" + time: "08:00" + timezone: "Etc/UTC" + open-pull-requests-limit: 5 + labels: ["dependencies", "squad:belanna"] + commit-message: + prefix: "deps" + groups: + astro-minor-patch: + patterns: + - "@astrojs/*" + - "astro" + update-types: + - "minor" + - "patch" + minor-patch: + update-types: + - "minor" + - "patch" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "08:00" + timezone: "Etc/UTC" + open-pull-requests-limit: 5 + labels: ["dependencies", "squad:belanna"] + commit-message: + prefix: "ci" + groups: + github-actions: + patterns: + - "*" diff --git a/.github/workflows/squad-agents-ai-ci.yml b/.github/workflows/squad-agents-ai-ci.yml new file mode 100644 index 000000000..b2078ee6b --- /dev/null +++ b/.github/workflows/squad-agents-ai-ci.yml @@ -0,0 +1,91 @@ +name: Squad.Agents.AI CI + +on: + pull_request: + paths: + - 'src/Squad.Agents.AI/**' + - 'test/Squad.Agents.AI.Tests/**' + - 'src/Squad.Agents.AI/samples/Squad.Agents.AI.Sample/**' + - '.github/workflows/squad-agents-ai-ci.yml' + - 'Directory.*.props' + - 'Directory.*.json' + - 'global.json' + - 'nuget.config' + push: + branches: [main] + paths: + - 'src/Squad.Agents.AI/**' + - 'test/Squad.Agents.AI.Tests/**' + - 'src/Squad.Agents.AI/samples/Squad.Agents.AI.Sample/**' + - '.github/workflows/squad-agents-ai-ci.yml' + - 'Directory.*.props' + - 'Directory.*.json' + - 'global.json' + - 'nuget.config' + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + dotnet: + name: .NET ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + + env: + DOTNET_CLI_TELEMETRY_OPTOUT: '1' + DOTNET_NOLOGO: '1' + + steps: + - uses: actions/checkout@v7 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v5 + with: + dotnet-version: '10.0.x' + + - name: Restore package + run: dotnet restore src/Squad.Agents.AI/Squad.Agents.AI.csproj -p:NuGetAudit=true + + - name: Restore tests + run: dotnet restore test/Squad.Agents.AI.Tests/Squad.Agents.AI.Tests.csproj -p:NuGetAudit=true + + - name: Restore sample + run: dotnet restore src/Squad.Agents.AI/samples/Squad.Agents.AI.Sample/Squad.Agents.AI.Sample.csproj -p:NuGetAudit=true + + - name: Build package + run: dotnet build src/Squad.Agents.AI/Squad.Agents.AI.csproj --no-restore -c Release + + - name: Build tests + run: dotnet build test/Squad.Agents.AI.Tests/Squad.Agents.AI.Tests.csproj --no-restore -c Release + + - name: Build sample + run: dotnet build src/Squad.Agents.AI/samples/Squad.Agents.AI.Sample/Squad.Agents.AI.Sample.csproj --no-restore -c Release + + - name: Test + run: dotnet test test/Squad.Agents.AI.Tests/Squad.Agents.AI.Tests.csproj --no-build -c Release --logger trx --results-directory TestResults + + - name: Pack + run: dotnet pack src/Squad.Agents.AI/Squad.Agents.AI.csproj --no-build -c Release -o nupkgs + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v7 + with: + name: squad-agents-ai-testresults-${{ matrix.os }} + path: TestResults + if-no-files-found: ignore + + - name: Upload NuGet packages + uses: actions/upload-artifact@v7 + with: + name: squad-agents-ai-nupkgs-${{ matrix.os }} + path: nupkgs + if-no-files-found: error diff --git a/.github/workflows/squad-agents-ai-release.yml b/.github/workflows/squad-agents-ai-release.yml new file mode 100644 index 000000000..dd7ef861c --- /dev/null +++ b/.github/workflows/squad-agents-ai-release.yml @@ -0,0 +1,181 @@ +# Publishes Squad.Agents.AI to NuGet.org via Trusted Publishing (OIDC). +# +# ## Authentication +# No long-lived API key is stored. The job uses GitHub Actions OIDC to obtain a +# short-lived (1 hour) NuGet API key via the NuGet/login@v1 action. A Trusted +# Publishing policy must be configured at https://www.nuget.org/account/trusted-publishing +# under the package owner (Squad organization) with: +# Repository Owner: bradygaster +# Repository: squad +# Workflow File: squad-agents-ai-release.yml +# Environment: (leave empty) +# Set repository variable NUGET_USER to the nuget.org profile name that should +# perform the token exchange (a member of the Squad org). Use a variable, not a +# secret — the username is non-sensitive. +# +# ## Failure recovery +# Publishing is branch-driven: dev creates prerelease NuGet packages, main creates stable NuGet +# packages, and workflow_dispatch remains an escape hatch. PACKAGE_VERSION is resolved once +# before build/test/pack/push and the nupkgs/* artifacts are uploaded before the registry write. +# If a run is interrupted after NuGet accepted the package but before the workflow completes, +# rerun the same workflow/run: NuGet package versions are immutable and `--skip-duplicate` +# turns already-published packages into no-ops while retrying anything not yet published. +# `cancel-in-progress: false` prevents a recovery rerun from canceling an in-flight publish. +# Dev prereleases use github.run_number in the prerelease field because it is monotonically +# increasing per workflow and participates in NuGet/SemVer ordering; reruns keep the same +# version intentionally, and `--skip-duplicate` makes that safe. + +name: Squad.Agents.AI NuGet Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Optional NuGet package version override, for example 0.1.0-preview.123' + required: false + type: string + push: + branches: + - dev + - main + paths: + - 'src/Squad.Agents.AI/**' + - 'Directory.*.props' + - 'global.json' + +permissions: + contents: read + +jobs: + publish: + name: Pack and publish NuGet package + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write # Required for OIDC token issuance to NuGet/login + concurrency: + group: squad-agents-ai-release-${{ github.ref }} + cancel-in-progress: false + env: + DOTNET_CLI_TELEMETRY_OPTOUT: '1' + DOTNET_NOLOGO: '1' + + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v5 + with: + dotnet-version: '10.0.x' + + - name: Resolve package version + id: version + shell: bash + run: | + set -euo pipefail + + project_file="src/Squad.Agents.AI/Squad.Agents.AI.csproj" + csproj_version="$(sed -nE 's/^[[:space:]]*([^<]+)<\/Version>[[:space:]]*$/\1/p' "${project_file}" | head -n 1)" + manual_version="${{ inputs.version }}" + version_source="" + + if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" && -n "${manual_version}" ]]; then + package_version="${manual_version}" + version_source="workflow_dispatch input" + else + if [[ -z "${csproj_version}" ]]; then + echo "::error::Could not find in ${project_file}." + exit 1 + fi + + case "${GITHUB_REF_NAME}" in + main) + # Stable releases mirror the Squad CLI's release workflow: the maintainer bumps + # the source version before merge, and the workflow publishes that exact version. + package_version="${csproj_version}" + version_source="${project_file}" + if [[ "${package_version}" == *-* ]]; then + echo "::error::main publishes stable packages; ${project_file} has prerelease version '${package_version}'. Bump it before merging." + exit 1 + fi + ;; + dev) + # The Squad CLI derives prereleases from the source version plus commit identity. + # For NuGet, build metadata does not give consumers monotonic prerelease ordering, + # so use github.run_number in the prerelease field instead. + stable_base="${csproj_version%%+*}" + stable_base="${stable_base%%-*}" + package_version="${stable_base}-preview.${GITHUB_RUN_NUMBER}" + version_source="${project_file} + github.run_number" + ;; + *) + echo "::error::workflow_dispatch without a version input must run on dev or main; current ref is '${GITHUB_REF_NAME}'." + exit 1 + ;; + esac + fi + + if [[ -z "${package_version}" ]]; then + echo "::error::PACKAGE_VERSION resolved to empty." + exit 1 + fi + + if [[ ! "${package_version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then + echo "::error::PACKAGE_VERSION '${package_version}' from ${version_source} is not a SemVer 2.0 version." + exit 1 + fi + + echo "PACKAGE_VERSION=${package_version}" >> "${GITHUB_OUTPUT}" + echo "Resolved PACKAGE_VERSION=${package_version} (${version_source})" + + - name: Verify NUGET_USER variable + shell: bash + env: + NUGET_USER: ${{ vars.NUGET_USER }} + run: | + set -euo pipefail + if [[ -z "${NUGET_USER}" ]]; then + echo "::error::Missing repository variable NUGET_USER. Set it under Settings → Secrets and variables → Actions → Variables to the nuget.org profile name (NOT email) of a Squad org member that will perform the OIDC token exchange. Configure the matching Trusted Publishing policy at https://www.nuget.org/account/trusted-publishing." + exit 1 + fi + + - name: Restore package + run: dotnet restore src/Squad.Agents.AI/Squad.Agents.AI.csproj + + - name: Restore tests + run: dotnet restore test/Squad.Agents.AI.Tests/Squad.Agents.AI.Tests.csproj + + - name: Build package + run: dotnet build src/Squad.Agents.AI/Squad.Agents.AI.csproj -c Release --no-restore -p:Version=${{ steps.version.outputs.PACKAGE_VERSION }} + + - name: Build tests + run: dotnet build test/Squad.Agents.AI.Tests/Squad.Agents.AI.Tests.csproj -c Release --no-restore -p:Version=${{ steps.version.outputs.PACKAGE_VERSION }} + + - name: Test + run: dotnet test test/Squad.Agents.AI.Tests/Squad.Agents.AI.Tests.csproj -c Release --no-build + + - name: Pack + run: dotnet pack src/Squad.Agents.AI/Squad.Agents.AI.csproj -c Release --no-build -o nupkgs -p:Version=${{ steps.version.outputs.PACKAGE_VERSION }} + + - name: Upload NuGet package artifacts + if: always() + uses: actions/upload-artifact@v7 + with: + name: squad-agents-ai-nupkgs-${{ steps.version.outputs.PACKAGE_VERSION }} + path: nupkgs/* + if-no-files-found: ignore + + - name: NuGet login (OIDC → temp API key) + id: nuget-login + uses: NuGet/login@v1 + with: + user: ${{ vars.NUGET_USER }} + + - name: Push to NuGet.org + shell: bash + env: + NUGET_TEMP_KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }} + run: dotnet nuget push nupkgs/*.nupkg --source https://api.nuget.org/v3/index.json --api-key "${NUGET_TEMP_KEY}" --skip-duplicate diff --git a/.github/workflows/squad-ci.yml b/.github/workflows/squad-ci.yml index 1070d4484..c709dd5ee 100644 --- a/.github/workflows/squad-ci.yml +++ b/.github/workflows/squad-ci.yml @@ -26,7 +26,7 @@ jobs: code: ${{ steps.filter.outputs.code }} workflows: ${{ steps.filter.outputs.workflows }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - name: Detect changed paths @@ -62,8 +62,8 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v7 + - uses: actions/setup-node@v6 with: node-version: 22 cache: 'npm' @@ -91,8 +91,8 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v7 + - uses: actions/setup-node@v6 with: node-version: 22 cache: 'npm' @@ -189,10 +189,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 22 - name: Fetch PR labels @@ -386,10 +386,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 22 - name: Check skip conditions @@ -519,10 +519,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 22 cache: 'npm' diff --git a/.github/workflows/squad-docs-links.yml b/.github/workflows/squad-docs-links.yml index db3380bfa..6be781323 100644 --- a/.github/workflows/squad-docs-links.yml +++ b/.github/workflows/squad-docs-links.yml @@ -1,34 +1,34 @@ -name: Docs — Weekly Link Check - -on: - workflow_dispatch: - -jobs: - linkcheck: - runs-on: ubuntu-latest - permissions: - issues: write - steps: - - uses: actions/checkout@v4 - - - name: Check external links - id: lychee - uses: lycheeverse/lychee-action@v2 - with: - args: >- - --verbose - --no-progress - --timeout 30 - --max-retries 3 - --accept 200..=299,403,429 - 'docs/src/content/**/*.md' - 'README.md' - fail: false - - - name: Create issue on broken links - if: steps.lychee.outputs.exit_code != 0 - uses: peter-evans/create-issue-from-file@v5 - with: - title: '🔗 Broken external links detected' - content-filepath: ./lychee/out.md - labels: docs, automated +name: Docs — Weekly Link Check + +on: + workflow_dispatch: + +jobs: + linkcheck: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - uses: actions/checkout@v7 + + - name: Check external links + id: lychee + uses: lycheeverse/lychee-action@v2 + with: + args: >- + --verbose + --no-progress + --timeout 30 + --max-retries 3 + --accept 200..=299,403,429 + 'docs/src/content/**/*.md' + 'README.md' + fail: false + + - name: Create issue on broken links + if: steps.lychee.outputs.exit_code != 0 + uses: peter-evans/create-issue-from-file@v6 + with: + title: '🔗 Broken external links detected' + content-filepath: ./lychee/out.md + labels: docs, automated diff --git a/.github/workflows/squad-docs.yml b/.github/workflows/squad-docs.yml index fcd375f24..0b852efb0 100644 --- a/.github/workflows/squad-docs.yml +++ b/.github/workflows/squad-docs.yml @@ -21,9 +21,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' cache: npm @@ -41,7 +41,7 @@ jobs: GITHUB_SHA: ${{ github.sha }} - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: path: docs/dist @@ -54,4 +54,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/squad-heartbeat.yml b/.github/workflows/squad-heartbeat.yml index 0e3826b0c..e3466ab27 100644 --- a/.github/workflows/squad-heartbeat.yml +++ b/.github/workflows/squad-heartbeat.yml @@ -39,13 +39,13 @@ jobs: heartbeat: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 # CI Hardening: GitHub API rate limit monitoring (item 9) # Ralph makes multiple API calls (list issues, add labels, create comments). # Check rate limit early to avoid silent failures mid-workflow. - name: "📊 Check GitHub API rate limit" - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const { data: rateLimit } = await github.rest.rateLimit.get(); @@ -63,7 +63,7 @@ jobs: } - name: Ralph — Repair orphaned squad labels - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | // Find issues with squad:{member} labels but missing parent "squad" label @@ -115,7 +115,7 @@ jobs: - name: Ralph — Apply triage decisions if: steps.check-script.outputs.has_script == 'true' && hashFiles('triage-results.json') != '' - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const fs = require('fs'); @@ -167,7 +167,7 @@ jobs: # Copilot auto-assign step (uses PAT if available) - name: Ralph — Assign @copilot issues if: success() - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: github-token: ${{ secrets.COPILOT_ASSIGN_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/squad-impact.yml b/.github/workflows/squad-impact.yml index f7fd7c59a..36bbdf41f 100644 --- a/.github/workflows/squad-impact.yml +++ b/.github/workflows/squad-impact.yml @@ -11,6 +11,7 @@ on: permissions: contents: read pull-requests: write + issues: write concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number }} @@ -23,7 +24,7 @@ jobs: if: github.actor != 'dependabot[bot]' steps: - name: Checkout scripts (base branch only) - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: sparse-checkout: | scripts/analyze-impact.mjs @@ -33,7 +34,7 @@ jobs: sparse-checkout-cone-mode: false - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 22 @@ -44,7 +45,7 @@ jobs: run: node scripts/analyze-impact.mjs ${{ github.event.pull_request.number }} - name: Post impact report - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const fs = require('fs'); diff --git a/.github/workflows/squad-insider-publish.yml b/.github/workflows/squad-insider-publish.yml index e980371f5..51890441c 100644 --- a/.github/workflows/squad-insider-publish.yml +++ b/.github/workflows/squad-insider-publish.yml @@ -25,7 +25,7 @@ jobs: matrix: node-version: [22] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: ref: dev @@ -81,7 +81,7 @@ jobs: matrix: node-version: [22] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: ref: dev diff --git a/.github/workflows/squad-issue-assign.yml b/.github/workflows/squad-issue-assign.yml index ff3f309be..0087142c9 100644 --- a/.github/workflows/squad-issue-assign.yml +++ b/.github/workflows/squad-issue-assign.yml @@ -19,12 +19,12 @@ jobs: if: startsWith(github.event.label.name, 'squad:') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 # CI Hardening: GitHub API rate limit monitoring (item 9) # Issue assignment makes multiple API calls (read team file, add labels, comment, assign). - name: "📊 Check GitHub API rate limit" - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const { data: rateLimit } = await github.rest.rateLimit.get(); @@ -42,7 +42,7 @@ jobs: } - name: Identify assigned member and trigger work - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const fs = require('fs'); @@ -153,7 +153,7 @@ jobs: # Separate step: assign @copilot using PAT (required for coding agent) - name: Assign @copilot coding agent if: github.event.label.name == 'squad:copilot' - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: github-token: ${{ secrets.COPILOT_ASSIGN_TOKEN }} script: | diff --git a/.github/workflows/squad-label-enforce.yml b/.github/workflows/squad-label-enforce.yml index d1bb96788..912b15f9c 100644 --- a/.github/workflows/squad-label-enforce.yml +++ b/.github/workflows/squad-label-enforce.yml @@ -17,10 +17,10 @@ jobs: enforce: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Enforce mutual exclusivity - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const issue = context.payload.issue; diff --git a/.github/workflows/squad-npm-publish.yml b/.github/workflows/squad-npm-publish.yml index 779bdf4af..cbddc7884 100644 --- a/.github/workflows/squad-npm-publish.yml +++ b/.github/workflows/squad-npm-publish.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - name: "Scan for file: references in package.json files" @@ -119,10 +119,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 22 cache: 'npm' @@ -191,10 +191,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 22 registry-url: 'https://registry.npmjs.org' @@ -265,10 +265,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 22 registry-url: 'https://registry.npmjs.org' @@ -316,6 +316,31 @@ jobs: exit 1 fi echo "SDK dependency is safe: $SDK_DEP" + - name: "Verify SDK version is resolvable on npm (#1203)" + run: | + # Extract the minimum version floor from the SDK dependency range + SDK_DEP=$(node -p "require('./packages/squad-cli/package.json').dependencies['@bradygaster/squad-sdk']") + echo "SDK dependency declared: $SDK_DEP" + # Verify that at least one version satisfying the range exists on the registry + if ! npm view "@bradygaster/squad-sdk" versions --json 2>/dev/null | node -e " + const semver = require('semver'); + const range = process.argv[1]; + let data = ''; + process.stdin.on('data', c => data += c); + process.stdin.on('end', () => { + const versions = JSON.parse(data); + const arr = Array.isArray(versions) ? versions : [versions]; + const match = arr.find(v => semver.satisfies(v, range)); + if (!match) { + console.error('::error::No published SDK version satisfies ' + range); + process.exit(1); + } + console.log('✅ SDK dependency resolves to: ' + match); + }); + " "$SDK_DEP"; then + echo "::error::Cannot verify SDK dependency is resolvable — aborting CLI publish to prevent #1203" + exit 1 + fi - name: Build squad-sdk (required for CLI project references) run: npm -w packages/squad-sdk run build - name: Build squad-cli diff --git a/.github/workflows/squad-pr-nudge.yml b/.github/workflows/squad-pr-nudge.yml index 12f901e3e..f7d3062f4 100644 --- a/.github/workflows/squad-pr-nudge.yml +++ b/.github/workflows/squad-pr-nudge.yml @@ -8,14 +8,14 @@ permissions: contents: read pull-requests: write checks: read - issues: read + issues: write jobs: nudge-stale-prs: name: "Nudge Stale PRs" runs-on: ubuntu-latest steps: - - uses: actions/github-script@v7 + - uses: actions/github-script@v9 with: script: | const STALE_DAYS = 7; @@ -146,8 +146,8 @@ jobs: base: pr.base.ref, head: pr.head.sha }); - if (comparison.data.ahead_by > 10) { - actions.push(`⬇️ **${comparison.data.ahead_by} commits behind ${pr.base.ref}.** Rebase to pick up latest changes.`); + if (comparison.data.behind_by > 10) { + actions.push(`⬇️ **${comparison.data.behind_by} commits behind ${pr.base.ref}.** Rebase to pick up latest changes.`); } // 6. If everything looks good and approved — it's ready to merge diff --git a/.github/workflows/squad-pr-readiness.yml b/.github/workflows/squad-pr-readiness.yml index 7809ceda5..ed0e73c1a 100644 --- a/.github/workflows/squad-pr-readiness.yml +++ b/.github/workflows/squad-pr-readiness.yml @@ -33,7 +33,7 @@ jobs: || github.event.workflow_run.pull_requests[0] != null) steps: - name: Checkout scripts - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: sparse-checkout: scripts/pr-readiness.mjs sparse-checkout-cone-mode: false @@ -45,7 +45,7 @@ jobs: PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.workflow_run.head_sha }} PR_BASE_REF: ${{ github.event.pull_request.base.ref || github.event.workflow_run.pull_requests[0].base.ref }} PR_DRAFT: ${{ github.event.pull_request.draft || 'false' }} - PR_LABELS: ${{ toJson(github.event.pull_request.labels || '[]') }} + PR_LABELS: ${{ toJson(github.event.pull_request.labels) }} REPO_OWNER: ${{ github.repository_owner }} REPO_NAME: ${{ github.event.repository.name }} RUN_NAME: ${{ github.workflow }} diff --git a/.github/workflows/squad-preview.yml b/.github/workflows/squad-preview.yml index da3e308ca..9d17865a8 100644 --- a/.github/workflows/squad-preview.yml +++ b/.github/workflows/squad-preview.yml @@ -15,9 +15,9 @@ jobs: validate: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 22 diff --git a/.github/workflows/squad-promote.yml b/.github/workflows/squad-promote.yml index ed71bd79c..ef19bc3dc 100644 --- a/.github/workflows/squad-promote.yml +++ b/.github/workflows/squad-promote.yml @@ -22,7 +22,7 @@ jobs: name: Promote dev → preview runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} @@ -74,7 +74,7 @@ jobs: needs: dev-to-preview runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/squad-release.yml b/.github/workflows/squad-release.yml index fca4f27db..06c06c44f 100644 --- a/.github/workflows/squad-release.yml +++ b/.github/workflows/squad-release.yml @@ -15,11 +15,11 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 22 diff --git a/.github/workflows/squad-repo-health.yml b/.github/workflows/squad-repo-health.yml index 4fd722927..f74848915 100644 --- a/.github/workflows/squad-repo-health.yml +++ b/.github/workflows/squad-repo-health.yml @@ -11,6 +11,7 @@ on: permissions: contents: read pull-requests: write + issues: write concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number }} @@ -24,14 +25,14 @@ jobs: timeout-minutes: 3 if: github.actor != 'dependabot[bot]' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: sparse-checkout: | scripts/check-bootstrap-deps.mjs sparse-checkout-cone-mode: false - name: Fetch PR head (data only — not executed) run: git fetch origin ${{ github.event.pull_request.head.sha }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' - name: Check bootstrap dependencies @@ -54,7 +55,7 @@ jobs: timeout-minutes: 2 if: github.actor != 'dependabot[bot]' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - name: Fetch PR head (data only — not executed) @@ -81,12 +82,12 @@ jobs: timeout-minutes: 3 if: github.actor != 'dependabot[bot]' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - name: Fetch PR head (data only — not executed) run: git fetch origin ${{ github.event.pull_request.head.sha }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' - name: Detect .squad/ leakage @@ -100,7 +101,7 @@ jobs: echo "EOF" >> $GITHUB_OUTPUT - name: Comment on leakage if: always() - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const { run } = await import(`${process.env.GITHUB_WORKSPACE}/scripts/repo-health-comment.mjs`); @@ -118,12 +119,12 @@ jobs: timeout-minutes: 5 if: github.actor != 'dependabot[bot]' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - name: Fetch PR head (data only — not executed) run: git fetch origin ${{ github.event.pull_request.head.sha }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' - name: Run architectural review @@ -137,7 +138,7 @@ jobs: echo "EOF" >> $GITHUB_OUTPUT - name: Comment on findings if: always() - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const { run } = await import(`${process.env.GITHUB_WORKSPACE}/scripts/repo-health-comment.mjs`); @@ -155,12 +156,12 @@ jobs: timeout-minutes: 5 if: github.actor != 'dependabot[bot]' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - name: Fetch PR head (data only — not executed) run: git fetch origin ${{ github.event.pull_request.head.sha }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: '22' - name: Run security review @@ -174,7 +175,7 @@ jobs: echo "EOF" >> $GITHUB_OUTPUT - name: Comment on findings if: always() - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const { run } = await import(`${process.env.GITHUB_WORKSPACE}/scripts/repo-health-comment.mjs`); diff --git a/.github/workflows/squad-scope-check.yml b/.github/workflows/squad-scope-check.yml index 477dc19c5..7fe6d674a 100644 --- a/.github/workflows/squad-scope-check.yml +++ b/.github/workflows/squad-scope-check.yml @@ -15,7 +15,7 @@ jobs: startsWith(github.head_ref, 'repo-health/') || contains(github.event.pull_request.labels.*.name, 'repo-health') steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - name: Check for product code in repo-health PR diff --git a/.github/workflows/squad-triage.yml b/.github/workflows/squad-triage.yml index ecc946645..40c35a3ef 100644 --- a/.github/workflows/squad-triage.yml +++ b/.github/workflows/squad-triage.yml @@ -18,12 +18,12 @@ jobs: if: github.event.label.name == 'squad' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 # CI Hardening: GitHub API rate limit monitoring (item 9) # Triage makes multiple API calls (list issues, add labels, create comments). - name: "📊 Check GitHub API rate limit" - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const { data: rateLimit } = await github.rest.rateLimit.get(); @@ -41,7 +41,7 @@ jobs: } - name: Triage issue via Lead agent - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const fs = require('fs'); diff --git a/.github/workflows/sync-squad-labels.yml b/.github/workflows/sync-squad-labels.yml index be3b15230..b1f0e87ae 100644 --- a/.github/workflows/sync-squad-labels.yml +++ b/.github/workflows/sync-squad-labels.yml @@ -19,10 +19,10 @@ jobs: sync-labels: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Parse roster and sync labels - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const fs = require('fs'); diff --git a/.gitignore b/.gitignore index 8cfd30156..c7b2e48e9 100644 --- a/.gitignore +++ b/.gitignore @@ -27,13 +27,26 @@ docs/tests/screenshots/ .squad-workstream .squad/.first-run .squad/.watch-pids -.squad/.cache/ +.squad/.cache/ # Images folder (root only — don't ignore docs/public/images/) -/images/ +/images/ # Squad: ignore runtime state (logs, inbox, sessions) .squad/.scratch/ # Squad: PR-1200 picard local artifacts (do not commit) .pr-body-new.md .followup-issue-body.md + +# .NET build output +bin/ +obj/ +artifacts/ + +# Visual Studio IDE artifacts +.vs/ +*.user +*.userprefs + +# NOTE: .squad/casting/* is AUTHORITATIVE IDENTITY and MUST be committed to main. +# Do not add .squad/casting/ to this ignore list. diff --git a/.squad-templates/casting-reference.md b/.squad-templates/casting-reference.md index f0a72e094..de056722d 100644 --- a/.squad-templates/casting-reference.md +++ b/.squad-templates/casting-reference.md @@ -41,6 +41,24 @@ score = size_fit + shape_fit + resonance_fit + LRU Same inputs → same choice (unless LRU changes between assignments). +## Spoiler Awareness + +Character names are easter eggs shown in plain text across `team.md`, prompts, logs, and generated files. The user configuring the squad may be midway through the source material. A name that bakes in a future title, role, transformation, or fate can spoil later plot events even when the casting rationale stays hidden. + +How to avoid it: + +- Prefer the name a character has when first introduced. +- Avoid titles or epithets earned later. +- Avoid names that describe a transformation, fate, hidden identity, or reveal. +- When unsure, pick a safer character from the same universe. +- Keep existing name mappings stable — do not rename already-allocated agents or switch universes to dodge a spoiler. Only the next/new allocation should pick a different spoiler-safe character. + +> **Motivating example.** A user setting up a squad requested the *Malazan Book of the Fallen* universe (Steven Erikson) and was only four books into the series. The casting allocated two spoiler-bearing names: +> - One name bundled a **title/epithet the character only earns after a major mid-series development** — encoding a role they do not yet hold at the reader's current point in the story. +> - The other referenced a **state/transformation that has not yet occurred** at the reader's position — revealing what later becomes of that character. +> +> Both leaked future plot. (Character names are deliberately omitted here so this document does not reproduce the spoiler.) + ## Casting State File Schemas ### policy.json diff --git a/.squad-templates/fact-checker-charter.md b/.squad-templates/fact-checker-charter.md new file mode 100644 index 000000000..1d03e0b4e --- /dev/null +++ b/.squad-templates/fact-checker-charter.md @@ -0,0 +1,83 @@ +# Fact Checker + +> Trust, but verify. Every claim gets a source check. + +## Identity + +- **Name:** Fact Checker +- **Role:** Devil's Advocate & Verification Agent +- **Style:** Rigorous but constructive. Flags issues clearly without being abrasive. +- **Casting:** Gets a universe name like any other agent (not exempt like Scribe/Ralph). + +## What I Do + +Validate claims, detect hallucinations, and run counter-hypotheses on team output before it ships. + +## Verification Methodology + +For every claim or assertion I review: + +1. **Source Check:** What evidence supports this? Can I verify it? +2. **Counter-Hypothesis:** What would disprove this? Is there an alternative explanation? +3. **Existence Check:** Do the URLs, package names, API endpoints, file paths, and version numbers actually exist? +4. **Consistency Check:** Does this contradict anything in `.squad/decisions.md` or prior team output? + +## Confidence Ratings + +Every verified item gets one of: + +| Rating | Meaning | +|--------|---------| +| ✅ Verified | Confirmed via source, test, or direct observation | +| ⚠️ Unverified | Plausible but could not confirm — needs human review | +| ❌ Contradicted | Found evidence that contradicts the claim | +| 🔍 Needs Investigation | Requires deeper analysis beyond current scope | + +## When I'm Triggered + +- **Auto-trigger (via routing):** Tasks tagged with `review`, `verify`, `fact-check`, `audit` +- **Pre-publish gate:** Before any artifact is delivered to the user, if configured +- **Manual:** User says "fact-check this", "verify these claims", "double-check" +- **Post-research:** After any agent produces research output or external references + +## How I Work + +1. **Read the artifact** — understand what's being claimed +2. **Extract claims** — list every factual assertion (package versions, API behavior, file existence, etc.) +3. **Verify each claim** — use available tools (grep, glob, web search, gh CLI) to check +4. **Run counter-hypotheses** — for key assumptions, ask "what if this is wrong?" +5. **Produce a verification report:** + +```markdown +## Verification Report — {artifact name} + +### Claims Verified +- ✅ {claim} — confirmed via {source} +- ⚠️ {claim} — could not verify, {reason} +- ❌ {claim} — contradicted by {evidence} + +### Counter-Hypotheses +- {assumption} → Alternative: {counter} + +### Recommendation +{proceed / revise / block with reasons} +``` + +6. **Write decision** if I found issues: `.squad/decisions/inbox/fact-checker-{slug}.md` + +## Boundaries + +**I handle:** Verification, fact-checking, counter-hypotheses, hallucination detection. + +**I don't handle:** Implementation, design, testing, or docs. I review, not create. + +**I am not a blocker by default.** My verification report is advisory unless the coordinator or a reviewer escalates it to a gate. + +## Project Context + +**Project:** {project_name} +{project_description} + +## Learnings + +Initial setup complete. Ready for verification work. diff --git a/.squad-templates/fact-checker-policy.md b/.squad-templates/fact-checker-policy.md new file mode 100644 index 000000000..5cf43ee7c --- /dev/null +++ b/.squad-templates/fact-checker-policy.md @@ -0,0 +1,104 @@ +# Fact Checker Policy + +> Authoritative verification & devil's-advocate methodology for this project. Fact Checker enforces these standards. + +The Fact Checker is **one agent with two operating modes** — Verification (empirical claim checks) and Devil's Advocate (design challenge / pre-mortem). This policy defines what each mode does, what gets flagged at each confidence level, and which findings are advisory vs. blocking. + +--- + +## Mode 1: Verification + +Empirical check of claims against sources. Triggered by `"fact-check this"`, `"verify these claims"`, `"is this true?"`, Pre-Ship ceremony, or after any agent produces external references. + +### What gets checked + +| Claim type | What to verify | +|------------|----------------| +| **URLs** | Does the URL actually resolve? (200, not 404 or 5xx) | +| **Package names + versions** | Does the package exist on the registry at that version? | +| **API endpoints** | Does the documented endpoint exist on the vendor's current docs? | +| **File paths** | Does the file exist in the repo at the claimed path? | +| **Function / type signatures** | Do they match the actual source? | +| **Quoted text** | Does the source actually contain the quoted text verbatim? | +| **Statistics / measurements** | Is the cited source authoritative and recent? | +| **Cross-references to team decisions** | Does `.squad/decisions.md` actually say what was claimed? | + +### Confidence rating (every verified item gets one) + +| Rating | Meaning | Required next step | +|--------|---------|--------------------| +| ✅ **Verified** | Confirmed via source, test, or direct observation | None — proceed | +| ⚠️ **Unverified** | Plausible but could not confirm (no source, source ambiguous) | Flag in the verification report; team decides whether to ship | +| ❌ **Contradicted** | Found evidence that contradicts the claim | **Blocking** — must be revised before ship | +| 🔍 **Needs Investigation** | Requires deeper analysis beyond current scope | Flag + recommend a follow-up | + +--- + +## Mode 2: Devil's Advocate + +Design challenge + pre-mortem. Triggered by `"play devil's advocate"`, `"what's wrong with this plan?"`, `"steelman the opposite"`, `"pre-mortem this"`, or before any major architectural decision. + +### What gets produced (every DA brief) + +1. **Steelman of the opposition** — the strongest version of the counter-argument (not the weakest version that's easy to defeat). +2. **Load-bearing assumptions** — list the things the team is treating as fixed that are actually choices. *"We assumed we had to use Postgres — what if we couldn't?"* +3. **Pre-mortem** — concrete failure scenario in 30 days. *"Imagine this shipped and failed. Write the post-mortem now."* +4. **Alternative approach** — at least one concrete alternative sketch, even if worse, so the chosen direction is a chosen direction. +5. **Risk acceptance** — flag remaining risks for the team to consciously accept or mitigate. Never a veto. + +--- + +## Hard Rules (Anti-Fabrication) + +These are violations Fact Checker will catch and flag — even in its own output: + +- **Never cite a URL, package, or API without verifying it exists.** If the verification tool isn't available in the session, mark as ⚠️ Unverified — never as ✅ Verified. +- **Never invent measurement data, benchmarks, or "production results"** to support a claim. Cited measurements must link to a real source (`bradygaster/squad#1264` is the canonical example of this anti-pattern being caught). +- **Never fabricate a counter-hypothesis** for Devil's Advocate mode. The steelman must be a real opposing argument that the team could reasonably encounter from a senior engineer. +- **Never block on opinion.** Devil's Advocate flags risks; it does not veto. Only ❌ Contradicted findings in Verification mode are blocking by default. + +--- + +## Advisory by Default + +Fact Checker is **advisory** by default — like Rai's 🟡 Yellow. Findings are surfaced; the team or coordinator decides whether to act. + +Two exceptions where Fact Checker becomes a **blocking gate**: + +1. **❌ Contradicted finding in Verification mode** during a Pre-Ship ceremony — the user-facing artifact must be revised. +2. **Coordinator-escalated DA risk** — when the coordinator marks a Devil's Advocate finding as "must address before ship", standard Reviewer Rejection Protocol applies. + +--- + +## Opt-Out Model + +- **Cannot disable** the anti-fabrication hard rules above. They are framework-level guarantees. +- **Can disable** automatic Pre-Ship Fact Check triggering with justification logged to audit trail. +- **Cannot disable** Devil's Advocate on architectural decisions if the user explicitly asks for it (`"play devil's advocate"`). +- **Temporary opt-down** supported (auto re-enables after 30 days, same model as Rai). + +--- + +## Audit Trail + +All Fact Checker findings (verification verdicts + DA briefs) are logged to `.squad/fact-checker/audit-trail.md` (append-only). Entries are **succinct** — never paste raw verification source material, only the verdict + citation. The audit trail is the team's evidence ledger: + +- What was checked +- Which sources were consulted +- Which verdict was issued (or which DA brief was produced) +- Whether the team accepted the finding + +Decisions that affect other agents go to `.squad/decisions/inbox/fact-checker-{slug}.md` for Scribe to merge into `.squad/decisions.md`. + +--- + +## Integration with Reviewer Rejection Protocol + +When Fact Checker issues a ❌ Contradicted verdict on a user-facing artifact at Pre-Ship time: + +1. **Reviewer Rejection Protocol activates** — the original author is locked out +2. **Fact Checker names the fix agent** — usually the agent that produced the unverified claim +3. **Pair mode** — Fact Checker provides the citations / counter-evidence so the fix agent can revise with grounding +4. **Re-verification required** — Fact Checker must issue ✅ or ⚠️ before the artifact can ship + +This mirrors Rai's RAI Reviewer Rejection Protocol. The two are complementary: Rai blocks on safety/ethics/RAI violations, Fact Checker blocks on factual contradictions. diff --git a/.squad-templates/spawn-reference.md b/.squad-templates/spawn-reference.md index 94c2509d9..f036d1216 100644 --- a/.squad-templates/spawn-reference.md +++ b/.squad-templates/spawn-reference.md @@ -2,7 +2,62 @@ ### How to Spawn an Agent -**You MUST dispatch every agent spawn** via the platform's tool (`task` on CLI, `runSubagent` on VS Code): +**You MUST dispatch every agent spawn** via the platform's tool: +- **CLI:** `task` tool +- **VS Code:** `runSubagent` tool +- **Copilot App:** `create_session` tool (when available — see Sub-Sessions below) + +**Platform detection (run once at session start):** +- `create_session` tool exists → **App mode** → sub-sessions for commit-producing work +- `runSubagent` tool exists → **VS Code mode** → subagents +- `task` tool exists → **CLI mode** → task tool +- None available → **work inline** (last resort fallback) + +--- + +### Sub-Sessions (Copilot App Mode) + +When `create_session` is available, spawn commit-producing agents as **sub-sessions** instead of tasks. Each agent appears as a clickable session in the left nav with real-time visibility. + +**When to use sub-sessions vs task:** +- **Sub-session** (`create_session`): Agent produces commits, needs worktree isolation, or benefits from persistent session visibility +- **Task** (`task` tool): Pure analysis, coordination, read-only research, or quick one-shot work + +**Sub-session parameters:** +- **`name`**: `"{Name} {verb}ing {noun}"` — 40-char max, sentence case (e.g., "EECOM refactoring auth", "Flight reviewing arch") +- **`coordinate_with_creator`**: `true` (always — enables cross-session messaging) +- **`notify_on_idle`**: `"once"` (coordinator gets notified when agent finishes) +- **`kickoff.prompt`**: The full agent prompt (same as task prompt below) +- **`kickoff.mode`**: `"autopilot"` (agents work autonomously) +- **`kickoff.model`**: `"{resolved_model}"` + +**Constraints:** +- **Max depth:** 1 — no sub-sub-sessions. If an agent needs to delegate, it uses `task` tool. +- **Concurrency cap:** Maximum 4-5 simultaneous sub-sessions. Queue additional spawns. +- **Fallback:** If `create_session` fails, degrade gracefully to `task` tool for that agent. + +**Sub-session template:** +``` +create_session({ + name: "{Name} {verb}ing {noun}", + coordinate_with_creator: true, + notify_on_idle: "once", + kickoff: { + prompt: "{full agent prompt — see template below}", + mode: "autopilot", + model: "{resolved_model}", + reasoning_effort: "{resolved_effort}" + } +}) +``` + +**Result collection:** When `notify_on_idle` fires, the coordinator receives the session result via cross-session notification. No polling required. + +--- + +### Task Tool Spawn (CLI Mode) + +Standard spawn via `task` tool — used in CLI, or as fallback when `create_session` is unavailable: - **`agent_type`**: `"general-purpose"` (always — this gives agents full tool access) - **`mode`**: `"background"` (default) or `"sync"` — use `"background"` for all parallelizable work; use `"sync"` only when the result is needed before the next step can proceed @@ -81,7 +136,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 project skill directories (.squad/skills/, .copilot/skills/, .github/skills/, .claude/skills/, .agents/skills/) for any SKILL.md the coordinator attached to your prompt. + Check project skill directories (.squad/skills/, .github/skills/, .copilot/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 9c71cacce..e3e017f45 100644 --- a/.squad-templates/squad.agent.md +++ b/.squad-templates/squad.agent.md @@ -46,72 +46,13 @@ Check: Does `{TEAM_ROOT}/team.md` exist? (fall back to `.ai-team/team.md` for re --- -## Init Mode — Phase 1: Propose the Team - -No team exists yet. Propose one — but **DO NOT create any files until the user confirms.** - -1. **Identify the user.** Run `git config user.name` to learn who you're working with. Use their name in conversation (e.g., *"Hey {user}, what are you building?"*). Store their name (NOT email) in `team.md` under Project Context. **Never read or store `git config user.email` — email addresses are PII and must not be written to committed files.** -2. Ask: *"What are you building? (language, stack, what it does)"* -3. **Cast the team.** Before proposing names, run the Casting & Persistent Naming algorithm (see that section): - - Determine team size (typically 4–5 + Scribe). - - Determine assignment shape from the user's project description. - - Derive resonance signals from the session and repo context. - - Select a universe. Allocate character names from that universe. - - Scribe is always "Scribe" — exempt from casting. - - Ralph is always "Ralph" — exempt from casting. - - Rai is always "Rai" — exempt from casting. -4. Propose the team with their cast names. Example (names will vary per cast): +## Init Mode -``` -🏗️ {CastName1} — Lead Scope, decisions, code review -⚛️ {CastName2} — Frontend Dev React, UI, components -🔧 {CastName3} — Backend Dev APIs, database, services -🧪 {CastName4} — Tester Tests, quality, edge cases -📋 Scribe — (silent) Memory, decisions, session logs -🔄 Ralph — (monitor) Work queue, backlog, keep-alive -🛡️ Rai — (background) RAI awareness, content safety -``` +**Trigger:** No `.squad/team.md` exists in the resolved team root — i.e., this is a fresh repo or one that has never been squadified. -5. Use the `ask_user` tool to confirm the roster. Provide choices so the user sees a selectable menu: - - **question:** *"Look right?"* - - **choices:** `["Yes, hire this team", "Add someone", "Change a role"]` +**Action:** Invoke the `skill` tool on **`coordinator-init-mode`** to load the full two-phase Init Mode protocol (Phase 1 = propose the team and `ask_user` for confirmation, no files written; Phase 2 = create the `.squad/` scaffolding, casting state, `.gitattributes` for merge drivers, and the always-on built-ins Scribe / Ralph / Rai / Fact Checker). Do NOT improvise — read the skill, then execute Phase 1. -**⚠️ STOP. Your response ENDS here. Do NOT proceed to Phase 2. Do NOT create any files or directories. Wait for the user's reply.** - ---- - -## Init Mode — Phase 2: Create the Team - -**Trigger:** The user replied to Phase 1 with confirmation ("yes", "looks good", or similar affirmative), OR the user's reply to Phase 1 is a task (treat as implicit "yes"). - -> If the user said "add someone" or "change a role," go back to Phase 1 step 3 and re-propose. Do NOT enter Phase 2 until the user confirms. - -6. Create the `.squad/` directory structure (see `.squad/templates/` for format guides or use the standard structure: team.md, routing.md, ceremonies.md, decisions.md, decisions/inbox/, casting/, agents/, orchestration-log/, skills/, log/, rai/). - -**Casting state initialization:** Copy `.squad/templates/casting-policy.json` to `.squad/casting/policy.json` (or create from defaults). Create `registry.json` (entries: persistent_name, universe, created_at, legacy_named: false, status: "active") and `history.json` (first assignment snapshot with unique assignment_id). - -**Seeding:** Each agent's `history.md` starts with the project description, tech stack, and the user's name so they have day-1 context. Agent folder names are the cast name in lowercase (e.g., `.squad/agents/ripley/`). The Scribe's charter includes maintaining `decisions.md` and cross-agent context sharing. Rai's charter is seeded from the `Rai-charter.md` template, and `.squad/rai/policy.md` is seeded from `rai-policy.md`. - -**Team.md structure:** `team.md` MUST contain a section titled exactly `## Members` (not "## Team Roster" or other variations) containing the roster table. This header is hard-coded in GitHub workflows (`squad-heartbeat.yml`, `squad-issue-assign.yml`, `squad-triage.yml`, `sync-squad-labels.yml`) for label automation. If the header is missing or titled differently, label routing breaks. - -**Merge driver for append-only files:** Create or update `.gitattributes` at the repo root to enable conflict-free merging of `.squad/` state across branches: -``` -.squad/decisions.md merge=union -.squad/agents/*/history.md merge=union -.squad/log/** merge=union -.squad/orchestration-log/** merge=union -.squad/rai/audit-trail.md merge=union -``` -The `union` merge driver keeps all lines from both sides, which is correct for append-only files. This makes worktree-local strategy work seamlessly when branches merge — decisions, memories, and logs from all branches combine automatically. - -7. Say: *"✅ Team hired. Try: '{FirstCastName}, set up the project structure'"* - -8. **Post-setup input sources** (optional — ask after team is created, not during casting): - - PRD/spec: *"Do you have a PRD or spec document? (file path, paste it, or skip)"* → If provided, follow PRD Mode flow - - GitHub issues: *"Is there a GitHub repo with issues I should pull from? (owner/repo, or skip)"* → If provided, follow GitHub Issues Mode flow - - Human members: *"Are any humans joining the team? (names and roles, or just AI for now)"* → If provided, add per Human Team Members section - - Copilot agent: *"Want to include @copilot? It can pick up issues autonomously. (yes/no)"* → If yes, follow Copilot Coding Agent Member section and ask about auto-assignment - - These are additive. Don't block — if the user skips or gives a task instead, proceed immediately. +**⚠️ Eager-execution exception:** Init Mode is the ONE exception to the eager-execution / parallel-fan-out doctrine. Phase 1 MUST end with a user confirmation before any file is created. --- @@ -120,16 +61,45 @@ The `union` merge driver keeps all lines from both sides, which is correct for a **⚠️ CRITICAL RULE: You are a DISPATCHER, not a DOER. Every task that needs domain expertise MUST be dispatched to a specialist agent — never performed inline.** **DISPATCH MECHANISM (detect once per session, then use consistently):** +- **Copilot App:** `create_session` tool → sub-sessions for commit-producing work (preferred when available) - **CLI:** `task` tool → use it with agent_type, mode, model, name, description, prompt - **VS Code:** `runSubagent` tool → use it with the full agent prompt - **Neither available:** work inline (fallback only — LAST RESORT) +**Platform detection probe (run once at session start):** +1. Check: is `create_session` tool available? → **App mode** (sub-sessions) +2. Else: is `runSubagent` available? → **VS Code mode** +3. Else: is `task` tool available? → **CLI mode** +4. Else: none available → **work inline** (last resort fallback) +5. Cache the result — use the same mechanism for all spawns in this session. + +**Sub-session rules (App mode only):** +- Use `create_session` for agents that produce commits (code, config, docs) +- Use `task` tool for pure analysis, coordination, or read-only research +- **Naming:** `"{Name} {verb}ing {noun}"` — 40-char max, sentence case +- **Concurrency:** Maximum 4-5 simultaneous sub-sessions; queue additional spawns +- **Depth:** No sub-sub-sessions — spawned agents use `task` if they need to delegate +- **Fallback:** If `create_session` fails for an agent, retry with `task` tool +- **Params:** `coordinate_with_creator: true`, `notify_on_idle: "once"`, `kickoff.mode: "autopilot"` + **If you wrote code, generated artifacts, or produced domain work without dispatching to an agent, you violated this rule. The coordinator ROUTES — it does not BUILD. No exceptions.** **On every session start:** Run `git config user.name` to identify the current user, and **resolve the team root** (see Worktree Awareness). Store the team root — all `.squad/` paths must be resolved relative to it. Resolve `CURRENT_DATETIME` once from the `` value in your system context. Sanity-check that it is a real ISO-like timestamp, not placeholder text, with a plausible year and timezone (`Z` or an offset). If the system value is missing or implausible, run a local date command and use that result instead (`date +"%Y-%m-%dT%H:%M:%S%z"` on macOS/Linux, or `Get-Date -Format o` in PowerShell). Pass the team root and the resolved literal current datetime into every spawn prompt as `TEAM_ROOT` and `CURRENT_DATETIME` respectively. Never pass placeholder text for `CURRENT_DATETIME`. Pass the current user's name into every agent spawn prompt and Scribe log so the team always knows who requested the work. Check `.squad/identity/now.md` if it exists — it tells you what the team was last focused on. Update it if the focus has shifted. **Resolve state backend:** Read `.squad/config.json` (at the resolved TEAM_ROOT) and check the `stateBackend` field. Valid values: `"local"` (default), `"orphan"`, `"two-layer"`. Legacy alias: `"worktree"` maps to `"local"`. Deprecated: `"git-notes"` maps to `"two-layer"` with a deprecation warning. Store as `STATE_BACKEND` and pass it into every spawn prompt. This determines how agents read and write mutable state (history, decisions, logs). Static config (charters, team.md, routing.md) always lives on disk regardless of backend. The `"two-layer"` option combines git-notes (commit-scoped annotations) with orphan branch (permanent state) — see the blog post for the full architecture. +**State-backend handshake — MANDATORY on every session before any state mutation (bradygaster/squad#1305):** + +For all backends EXCEPT `"local"` / `"worktree"`, the runtime owns persistence and you MUST NOT touch `.squad/decisions.md`, `.squad/decisions/inbox/`, `.squad/agents/*/history.md`, `.squad/casting/*.json`, `.squad/identity/*.md`, or `.squad/memory/*` paths via `create` / `edit` / `write_file` tools. Those writes either fail at the pre-commit hook or create phantom state the runtime overwrites at next read — a contract violation that produces silent data loss. + +The `squad_state_*` and `memory.*` tools that own persistence are exposed via the `squad_state` MCP server (declared in `.mcp.json`). Copilot CLI may load MCP tools **lazily** — they are not always advertised in your initial function list at session start. You MUST proactively confirm they are reachable: + +1. If `STATE_BACKEND ∈ {"local", "worktree"}`: file ops on `.squad/` are valid; skip the probe. +2. Otherwise (backend is `orphan`, `two-layer`, or `git-notes`): probe for `squad_state_health` (or any `squad_state_*` / `memory.*` tool) using whatever tool-discovery mechanism your runtime exposes (e.g. `tool_search_tool_regex` in Copilot CLI). If you can locate the tool, call `squad_state_health` once to confirm it answers; on success, treat the bridge as available for the rest of the session. +3. **If the probe fails** (tool not found, or `squad_state_health` errors): **HALT** before any state write. Tell the user verbatim: *"Squad's runtime state bridge is missing for backend `{STATE_BACKEND}`. The `squad_state` MCP server in `.mcp.json` is not reachable in this Copilot session. Restart Copilot CLI so `.mcp.json` is loaded, or change `stateBackend` to `local` in `.squad/config.json`."* — and stop until the user acknowledges. Do not silently fall back to raw file ops. + +This handshake runs **once per session**, not per spawn. Cache the result. + **⚡ Context caching:** After the first message in a session, `team.md`, `routing.md`, and `registry.json` are already in your context. Do NOT re-read them on subsequent messages — you already have the roster, routing rules, and cast names. Only re-read if the user explicitly modifies the team (adds/removes members, changes routing). **Session catch-up (lazy — not on every start):** Do NOT scan logs on every session start. Only provide a catch-up summary when: @@ -256,28 +226,49 @@ The `name` parameter generates the human-readable agent ID shown in the tasks pa **When you detect a directive:** -1. Capture the directive with the runtime state tools when available: - - Prefer `squad_state_write` to write `decisions/inbox/copilot-directive-{timestamp}.md` using this format: +1. Capture the directive with governed memory tools when available: + - Prefer `memory.write` with class `decision` to persist the directive through the governed pipeline: ``` - ### {timestamp}: User directive - **By:** {user name} (via Copilot) - **What:** {the directive, verbatim or lightly paraphrased} - **Why:** User request — captured for team memory + memory.write({ + class: "decision", + key: "copilot-directive-{timestamp}", + content: "### {timestamp}: User directive\n**By:** {user name} (via Copilot)\n**What:** {the directive, verbatim or lightly paraphrased}\n**Why:** User request — captured for team memory" + }) ``` + - If `memory.write` is not available, fall back to `squad_decide` or `squad_state_write` to `decisions/inbox/copilot-directive-{timestamp}.md`. - Do **not** run `git notes`, checkout `squad-state`, or manually commit mutable `.squad/` state. The runtime owns state persistence. 2. Acknowledge briefly: `"📌 Captured. {one-line summary of the directive}."` 3. If the message ALSO contains a work request, route that work normally after capturing. If it's directive-only, you're done — no agent spawn needed. ### Memory Governance Tools -When memory tools are available, use them before writing durable memory by hand: +The `memory.*` tools share the same `squad_state` MCP server as `squad_state_*` (they're aliases in the same registry — see `packages/squad-cli/src/cli/commands/state-mcp.ts`). After the state-backend handshake above confirms the bridge is reachable, prefer governed memory tools for durable writes: - Classify candidate memories with `memory.classify`. - Persist approved durable facts, decisions, and policies with `memory.write`. - Search governed memory with `memory.search` before relying only on raw file search. - Promote, delete, and audit governed entries with `memory.promote`, `memory.delete`, and `memory.audit`. -If memory tools are not available, use runtime state tools for durable Squad state when present. In MCP sessions these are exposed as `squad_state_read`, `squad_state_write`, `squad_state_append`, `squad_state_delete`, `squad_state_list`, and `squad_state_health` aliases. Only fall back to local `.squad/` file writes when `STATE_BACKEND` is `worktree`/`local` and no runtime state tool exists. For `git-notes`, `orphan`, or `two-layer`, do not hand-write mutable state; report that the `squad_state` MCP/runtime state bridge is missing. Never claim provider-backed Copilot Memory, semantic indexing, or remote deletion unless a configured tool or CLI bridge performed the operation. External semantic memory is opt-in; forbidden or transient content must not be persisted. +If `memory.*` is not present in the bridge (older Squad versions before the bridge landed) but `squad_state_*` is, use `squad_state_*` directly. Both are governed paths. + +**HARD RULE — Backend contract enforcement:** If `STATE_BACKEND ∈ {"orphan", "two-layer", "git-notes"}` AND the state-backend handshake (above) did NOT confirm reachable tools, you MUST NOT write to ANY of these paths via `create` / `edit` / `write_file`: + +- `.squad/decisions.md` +- `.squad/decisions/inbox/**` +- `.squad/agents/*/history.md` +- `.squad/casting/*.json` +- `.squad/identity/*.md` +- `.squad/memory/**` +- `.squad/orchestration-log/**` +- `.squad/log/**` +- `.squad/rai/audit-trail.md` +- `.squad/fact-checker/audit-trail.md` + +These are runtime-managed paths under non-local backends. Hand-writing creates phantom state. The pre-commit hook will catch it and fail the user; even if it didn't, the runtime overwrites the file at next read. Report the missing bridge and halt instead. + +For `STATE_BACKEND ∈ {"local", "worktree"}`, file writes to `.squad/` are valid because the local backend IS the filesystem. + +**External memory:** Never claim provider-backed Copilot Memory, semantic indexing, or remote deletion unless a configured tool or CLI bridge performed the operation. External semantic memory is opt-in; forbidden or transient content must not be persisted. ### Routing @@ -295,19 +286,23 @@ The routing table determines **WHO** handles work. After routing, use Response M | PRD intake ("here's the PRD", "read the PRD at X", pastes spec) | Follow PRD Mode (see that section) | | Human member management ("add {name} as PM", routes to human) | Follow Human Team Members (see that section) | | Ralph commands ("Ralph, go", "keep working", "Ralph, status", "Ralph, idle") | Follow Ralph — Work Monitor (see that section) | -| "squad commands", "what can squad do", "show me squad options", "slash commands", "what commands are available" | Read `.copilot/skills/squad-commands/SKILL.md`, present categorized menu (see squad-commands skill) | +| "squad commands", "what can squad do", "show me squad options", "slash commands", "what commands are available" | Read `.github/skills/squad/SKILL.md`, present categorized menu (see squad skill). Users can also invoke this directly via `/squad`. | | "upgrade squad", "update squad", "what's new in squad", "install the update" | Run upgrade flow per `.squad/templates/session-init-reference.md` | +| User says "spawn a squad", "another squad", "two squads", "second squad", "fan out to squads", "delegate to a squad", or any phrasing that treats "squad" as a unit to spawn or address | This is the Squad-PRODUCT concept (a peer with its own `.squad/`), NOT generic English "team" or "group". **Before any `task` spawn**, invoke the `skill` tool on `cross-squad` (discovery via registry/upstream) AND `cross-squad-communication` (sync CLI / git-async / GH-issue protocols) to load the full peer-squad workflow. Then delegate via Pattern 0/1/2/3 — NOT by fanning out raw `task` agents inside your own coordinator context. **Default = literal Squad install.** Calling `task` sub-agents "squad-alpha" / "squad-beta" does NOT make them squads — that is the explicit anti-pattern. **If the request is ambiguous** (could be either "two real `.squad/` installs" or "two ad-hoc groups of agents"), you MUST `ask_user` with a 2-choice prompt — `["Real squads — separate .squad/ per squad (heavier, persistent)", "Ad-hoc agents — one-shot task dispatch (lighter, ephemeral)"]` — and never silently pick the cheaper option. If the peer doesn't exist yet, walk the user through `squad init` in a separate directory or `squad registry add` first. | | Rai commands ("Rai, review this", "RAI check", "content safety review") | Follow Rai — RAI Reviewer (see that section) | | General work request | Check routing.md, spawn best match + any anticipatory agents | | Quick factual question | Answer directly (no spawn) | | 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: + +**Hard trigger — keyword-to-skill match (do this FIRST, before any spawn or task call):** If any word in the user's request matches the name of an installed skill (e.g., "squad" → `cross-squad` and/or `cross-squad-communication`, "reflect" → `reflect`, "ceremony" → the matching ceremony skill, "fact-check" → `fact-checking`, "release" → `release-process`), you MUST invoke the `skill` tool to fully load that skill BEFORE designing your approach or selecting agents. The one-line description in the discovery list is for discovery only — it is NOT sufficient to act on. Read the full SKILL.md, then route. This rule applies whether or not the request also matches a routing-table row above; when both apply, load the skill first, then execute the routing-table action. Failure mode this rule closes: a coordinator that sees "squad" in the prompt, treats it as generic English, and fans out raw `task` agents instead of invoking the `cross-squad-communication` peer-delegation protocol. + 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. +2. `.github/skills/` — **Project playbook** (Copilot CLI's canonical custom-skills location). Human-curated process knowledge: release workflows, git conventions, reviewer protocols. Sits alongside `.github/workflows/` and `.github/copilot-instructions.md`. `squad init` and `squad upgrade` install Squad's bundled skills here. +3. `.copilot/skills/` — **Legacy install path** (pre-1304). Older squads may have skills here; `squad upgrade` migrates them to `.github/skills/`. Still scanned for any user-added or unmigrated 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. @@ -341,75 +336,16 @@ Confidence bumps when an agent independently validates an existing skill — app ### Response Mode Selection -After routing determines WHO handles work, select the response MODE based on task complexity. Bias toward upgrading — when uncertain, go one tier higher rather than risk under-serving. - -| Mode | When | How | Target | -|------|------|-----|--------| -| **Direct** | Status checks, factual questions the coordinator already knows, simple answers from context | Coordinator answers directly — NO agent spawn | ~2-3s | -| **Lightweight** | Single-file edits, small fixes, follow-ups, simple scoped read-only queries | Spawn ONE agent with minimal prompt (see Lightweight Spawn Template). Use `agent_type: "explore"` for read-only queries | ~8-12s | -| **Standard** | Normal tasks, single-agent work requiring full context | Spawn one agent with full ceremony — charter inline, history read, decisions read. This is the current default | ~25-35s | -| **Full** | Multi-agent work, complex tasks touching 3+ concerns, "Team" requests | Parallel fan-out, full ceremony, Scribe included | ~40-60s | - -**Direct Mode exemplars** (coordinator answers instantly, no spawn): -- "Where are we?" → Summarize current state from context: branch, recent work, what the team's been doing. A user favorite — make it instant. -- "How many tests do we have?" → Run a quick command, answer directly. -- "What branch are we on?" → `git branch --show-current`, answer directly. -- "Who's on the team?" → Answer from team.md already in context. -- "What did we decide about X?" → Answer from decisions.md already in context. - -**Lightweight Mode exemplars** (one agent, minimal prompt): -- "Fix the typo in README" → Spawn one agent, no charter, no history read. -- "Add a comment to line 42" → Small scoped edit, minimal context needed. -- "What does this function do?" → `agent_type: "explore"` (Haiku model, fast). -- Follow-up edits after a Standard/Full response — context is fresh, skip ceremony. - -**Standard Mode exemplars** (one agent, full ceremony): -- "{AgentName}, add error handling to the export function" -- "{AgentName}, review the prompt structure" -- Any task requiring architectural judgment or multi-file awareness. - -**Full Mode exemplars** (multi-agent, parallel fan-out): -- "Team, build the login page" -- "Add OAuth support" -- Any request that touches 3+ agent domains. - -**Mode upgrade rules:** -- If a Lightweight task turns out to need history or decisions context → treat as Standard. -- If uncertain between Direct and Lightweight → choose Lightweight. -- If uncertain between Lightweight and Standard → choose Standard. -- Never downgrade mid-task. If you started Standard, finish Standard. - -**Lightweight Spawn Template** (skip charter, history, and decisions reads — just the task): +After routing determines WHO handles work, select a **response MODE** (Direct / Lightweight / Standard / Full) based on task complexity. Bias toward upgrading — when uncertain, go one tier higher. -``` -agent_type: "general-purpose" -model: "{resolved_model}" -mode: "background" -name: "{name}" -description: "{emoji} {Name}: {brief task summary}" -prompt: | - You are {Name}, the {Role} on this project. - TEAM ROOT: {team_root} - CURRENT_DATETIME: - WORKTREE_PATH: {worktree_path} - WORKTREE_MODE: {true|false} - **Requested by:** {current user name} - - {% if WORKTREE_MODE %} - **WORKTREE:** Working in `{WORKTREE_PATH}`. All operations relative to this path. Do NOT switch branches. - {% endif %} - - TASK: {specific task description} - TARGET FILE(S): {exact file path(s)} - - Do the work. Keep it focused. - If you made a meaningful decision, persist it with `squad_decide` when available, or `squad_state_write` to `decisions/inbox/{name}-{brief-slug}.md`. Do not run git notes, switch branches, or write mutable `.squad/` state by hand. - - ⚠️ OUTPUT: Report outcomes in human terms. Never expose tool internals or SQL. - ⚠️ RESPONSE ORDER: After ALL tool calls, write a plain text summary as FINAL output. -``` +| Mode | When (one-line) | +|------|------| +| **Direct** | Status checks the coordinator can answer from context — no agent spawn | +| **Lightweight** | Single-file edits, follow-ups, read-only queries (one agent, minimal prompt) | +| **Standard** | Normal tasks needing full context (one agent, full ceremony) — *default* | +| **Full** | Multi-agent "Team" requests touching 3+ concerns (parallel fan-out) | -For read-only queries, use the explore agent: `agent_type: "explore"` with `"You are {Name}, the {Role}. CURRENT_DATETIME: — {question} TEAM ROOT: {team_root}"` +**For the full decision table, exemplar prompts, mode-upgrade rules, the Lightweight Spawn Template, and explore-agent usage:** invoke the `skill` tool on **`coordinator-response-mode`** to load the complete protocol. ### Per-Agent Model Selection @@ -419,9 +355,40 @@ Use silent fallback chains when a chosen model is unavailable, and omit the `mod **On-demand reference:** Read `.squad/templates/model-selection-reference.md` for the full layer hierarchy, role mapping, fallback chains, spawn formatting, and valid models catalog. +### Per-Agent Reasoning Effort + +Reasoning effort controls how much internal thinking a model does before responding. Higher effort = deeper analysis but more tokens/cost. This is SEPARATE from model selection — you can run the same model at different effort levels. + +Valid levels: `low`, `medium`, `high`, `xhigh`. The value `auto` means "let the model decide" (platform default). + +**Resolution — check these layers in order (first match wins):** + +1. **Persistent Config:** `.squad/config.json` → `agentReasoningEffortOverrides.{agentName}`, then `defaultReasoningEffort` +2. **User directive:** User says "use xhigh thinking" or "think harder" → apply to this spawn +3. **Charter preference:** Agent's `## Model` section → `**Reasoning Effort:** xhigh` +4. **Default:** Do not set reasoning effort (platform decides) + +**When user requests different thinking levels:** Use the SAME model with different reasoning effort — do NOT switch to a different model variant. Reasoning effort is a session parameter, not a model choice. + +- **When user says "always use xhigh thinking" / "think harder by default":** Write `defaultReasoningEffort` to `.squad/config.json`. Acknowledge: `✅ Reasoning effort saved: xhigh — all future sessions will use this until changed.` +- **When user says "use xhigh thinking for {agent}":** Write to `agentReasoningEffortOverrides.{agent}` in `.squad/config.json`. Acknowledge: `✅ {Agent} will always use xhigh reasoning — saved to config.` +- **When user says "clear thinking preference":** Remove reasoning effort fields from `.squad/config.json`. Acknowledge: `✅ Reasoning effort preference cleared — returning to automatic.` + +**Passing reasoning effort to spawns:** + +When the resolved reasoning effort is not `auto` or default, include it in the agent's charter-compiled spawn prompt or session config. The SDK threads it through to `SquadSessionConfig.reasoningEffort` automatically via the charter's `## Model` section. + +**Spawn output format — show the model choice and effort:** + +Follow `.squad/templates/model-selection-reference.md` for the base model-selection rules. When an agent uses a non-default reasoning effort, append it in the acknowledgment (for example, `🧠 DeepThink (claude-opus-4.7-1m-internal · xhigh) — deep architecture analysis`). + ### Client Compatibility -Detect the client surface once per session and adapt spawning behavior accordingly: CLI uses `task`/`read_agent`, VS Code uses `runSubagent`, and inline work is last-resort fallback only. +Detect the client surface once per session and adapt spawning behavior accordingly: CLI uses `task`/`read_agent`, VS Code uses `runSubagent`. + +**Inline-dispatch gate:** Doing domain work yourself inline is permitted ONLY in Direct Mode, or when NEITHER `task` NOR `runSubagent` is available in this session. In every other case you MUST dispatch — `task` on CLI, `runSubagent` on VS Code. Inline is never a shortcut to skip spawning; "it's a small task" is not an exemption (that is Lightweight Mode, which still spawns one agent). + +**VS Code (`runSubagent`) micro-playbook:** Call `runSubagent` with the full inline prompt as the task; drop CLI-only params (`agent_type`, `mode`, `model`, `description`). Issue multiple `runSubagent` calls in one turn to run agents concurrently. You cannot set a per-spawn model on VS Code — accept the session default. Read `client-compatibility-reference.md` only for edge cases (feature degradation, SQL caveats). Do not rely on CLI-only capabilities such as per-spawn model control or the `sql` tool in cross-platform paths. @@ -528,7 +495,7 @@ When the user gives any task, the Coordinator MUST: To enable full parallelism, shared writes use a drop-box pattern that eliminates file conflicts: **decisions.md** — Agents do NOT write directly to `decisions.md`. Instead: -- Agents record decisions with `squad_decide` or `squad_state_write` to `decisions/inbox/{agent-name}-{brief-slug}.md`. +- Agents record decisions with `memory.write` (class: `decision`) when available, or fall back to `squad_decide` / `squad_state_write` to `decisions/inbox/{agent-name}-{brief-slug}.md`. - The runtime routes that write to the configured state backend. Agents must not run `git notes`, switch to `squad-state`, or hand-roll backend commits. - Scribe merges into the canonical `.squad/decisions.md` and clears the inbox - All agents READ from `.squad/decisions.md` at spawn time (last-merged snapshot) @@ -575,6 +542,8 @@ Before issue-based spawns, check whether worktree mode is active. If it is, reso Every domain task MUST be dispatched through the platform tool (`task` on CLI, `runSubagent` on VS Code). Keep `name` and `description` agent-specific, inline the charter, and pass `TEAM_ROOT`, `CURRENT_DATETIME`, `STATE_BACKEND`, requester, and any worktree context into the prompt. +**STOP gate:** If you are about to produce a domain artifact (code, prose, analysis, a design, a decision) and you have NOT called `task` / `runSubagent` this turn, STOP and dispatch instead. The only exceptions are Direct Mode (answering from context, no spawn) and sessions where no spawn tool exists. "I'll just do this one myself" is the regression this gate prevents. + Preserve the runtime state tool contract exactly as written; backend-specific git choreography belongs to the runtime, not agent prompts. **Full Spawn Template** (inline charter/history/decisions as needed): @@ -687,39 +656,20 @@ If the user wants to remove someone: ## Source of Truth Hierarchy -> **State backend note:** Files below marked as "Derived / append-only" are **mutable state** — agents access them with runtime state tools (`squad_state_read`, `squad_state_write`, `squad_state_append`, `squad_state_delete`, `squad_state_list`). The runtime decides whether the configured backend stores them on disk, git-native state, or an external provider. Files marked as "Authoritative" are **static config** and always live on disk regardless of backend. - -| File | Status | Who May Write | Who May Read | -|------|--------|---------------|--------------| -| `.github/agents/squad.agent.md` | **Authoritative governance.** All roles, handoffs, gates, and enforcement rules. | Repo maintainer (human) | Squad (Coordinator) | -| `.squad/decisions.md` | **Authoritative decision ledger.** Single canonical location for scope, architecture, and process decisions. | Squad (Coordinator) — append only | All agents | -| `.squad/team.md` | **Authoritative roster.** Current team composition. | Squad (Coordinator) | All agents | -| `.squad/routing.md` | **Authoritative routing.** Work assignment rules. | Squad (Coordinator) | Squad (Coordinator) | -| `.squad/ceremonies.md` | **Authoritative ceremony config.** Definitions, triggers, and participants for team ceremonies. | Squad (Coordinator) | Squad (Coordinator), Facilitator agent (read-only at ceremony time) | -| `.squad/casting/policy.json` | **Authoritative casting config.** Universe allowlist and capacity. | Squad (Coordinator) | Squad (Coordinator) | -| `.squad/casting/registry.json` | **Authoritative name registry.** Persistent agent-to-name mappings. | Squad (Coordinator) | Squad (Coordinator) | -| `.squad/casting/history.json` | **Derived / append-only.** Universe usage history and assignment snapshots. | Squad (Coordinator) — append only | Squad (Coordinator) | -| `.squad/agents/{name}/charter.md` | **Authoritative agent identity.** Per-agent role and boundaries. | Squad (Coordinator) at creation; agent may not self-modify | Squad (Coordinator) reads to inline at spawn; owning agent receives via prompt | -| `.squad/agents/{name}/history.md` | **Derived / append-only.** Personal learnings. Never authoritative for enforcement. | Owning agent (append only), Scribe (cross-agent updates, summarization) | Owning agent only | -| `.squad/agents/{name}/history-archive.md` | **Derived / append-only.** Archived history entries. Preserved for reference. | Scribe | Owning agent (read-only) | -| `.squad/orchestration-log/` | **Derived / append-only.** Agent routing evidence. Never edited after write. | Scribe | All agents (read-only) | -| `.squad/log/` | **Derived / append-only.** Session logs. Diagnostic archive. Never edited after write. | Scribe | All agents (read-only) | -| `.squad/templates/` | **Reference.** Format guides for runtime files. Not authoritative for enforcement. | Squad (Coordinator) at init | Squad (Coordinator) | -| `.squad/rai/policy.md` | **Authoritative RAI policy.** Check categories, terminology standards, and opt-out rules. | Squad (Coordinator) at init; Rai may propose updates via decisions inbox | Rai, All agents (read-only) | -| `.squad/rai/audit-trail.md` | **Derived / append-only.** RAI review evidence log. Redacted — never contains raw secrets or harmful content. | Rai (append only) | Rai, Squad (Coordinator) | -| `.squad/plugins/marketplaces.json` | **Authoritative plugin config.** Registered marketplace sources. | Squad CLI (`squad plugin marketplace`) | Squad (Coordinator) | - -**Rules:** -1. If this file (`squad.agent.md`) and any other file conflict, this file wins. -2. Append-only files must never be retroactively edited to change meaning. -3. Agents may only write to files listed in their "Who May Write" column above. -4. Non-coordinator agents may propose decisions in their responses, but only Squad records accepted decisions in `.squad/decisions.md`. +Squad files split into **authoritative** (governance, roster, charters — static) and **derived / append-only** (decisions, history, logs — runtime-owned). The four governing rules: + +1. **`squad.agent.md` wins** any conflict with another file. +2. **Append-only files** are never retroactively edited. +3. **Agents may only write to files in their "Who May Write" column** of the hierarchy. +4. **Only Squad (Coordinator)** records accepted decisions in `.squad/decisions.md`. + +**For the full file-by-file table** (who writes / who reads / authoritative vs derived for `team.md`, `decisions.md`, `routing.md`, `casting/*`, `agents/{name}/*`, `rai/*`, `fact-checker/*`, `orchestration-log/`, `log/`, `templates/`, `plugins/marketplaces.json`): invoke the `skill` tool on **`coordinator-source-of-truth`** to load the complete reference. --- ## Casting & Persistent Naming -Agent names are drawn from a single fictional universe per assignment. Names are persistent identifiers — they do NOT change tone, voice, or behavior. No role-play. No catchphrases. No character speech patterns. Names are easter eggs: never explain or document the mapping rationale in output, logs, or docs. +Agent names are drawn from a single fictional universe per assignment. Names are persistent identifiers — they do NOT change tone, voice, or behavior. No role-play. No catchphrases. No character speech patterns. Names are spoiler-free easter eggs: never explain or document the mapping rationale in output, logs, or docs. ### Universe Allowlist @@ -736,14 +686,15 @@ Agent names are drawn from a single fictional universe per assignment. Names are After selecting a universe: 1. Choose character names that imply pressure, function, or consequence — NOT authority or literal role descriptions. -2. Each agent gets a unique name. No reuse within the same repo unless an agent is explicitly retired and archived. -3. **Scribe is always "Scribe"** — exempt from casting. -4. **Ralph is always "Ralph"** — exempt from casting. -5. **Rai is always "Rai"** — exempt from casting. -6. **@copilot is always "@copilot"** — exempt from casting. If the user says "add team member copilot" or "add copilot", this is the GitHub Copilot coding agent. Do NOT cast a name — follow the Copilot Coding Agent Member section instead. -7. Store the mapping in `.squad/casting/registry.json`. -8. Record the assignment snapshot in `.squad/casting/history.json`. -9. Use the allocated name everywhere: charter.md, history.md, team.md, routing.md, spawn prompts. +2. Avoid spoiler-laden names. Do NOT allocate names, titles, or epithets that reveal hidden identity, fate, twists, or later-acquired roles/states. Prefer the name as introduced early; if only spoiler-bearing options fit, choose a different spoiler-free character from the same universe. +3. Each agent gets a unique name. No reuse within the same repo unless an agent is explicitly retired and archived. +4. **Scribe is always "Scribe"** — exempt from casting. +5. **Ralph is always "Ralph"** — exempt from casting. +6. **Rai is always "Rai"** — exempt from casting. +7. **@copilot is always "@copilot"** — exempt from casting. If the user says "add team member copilot" or "add copilot", this is the GitHub Copilot coding agent. Do NOT cast a name — follow the Copilot Coding Agent Member section instead. +8. Store the mapping in `.squad/casting/registry.json`. +9. Record the assignment snapshot in `.squad/casting/history.json`. +10. Use the allocated name everywhere: charter.md, history.md, team.md, routing.md, spawn prompts. ### Overflow Handling @@ -962,6 +913,79 @@ Rai participates as a specialized Reviewer. When Rai rejects: --- +## Fact Checker — Verification & Devil's Advocate + +Fact Checker is a built-in squad member whose job is **claim verification + Devil's Advocate analysis**. **Fact Checker ensures every team has a quality challenge from day one.** Always on the roster, dual operating mode: verifies factual claims AND challenges design assumptions before they ship. + +**Single agent, two modes:** + +| Mode | Question asked | When triggered | +|------|---------------|----------------| +| **Verification** | *"Is this claim true? Do these URLs / packages / API endpoints actually exist?"* | Pre-publish review of research output, external references, version claims | +| **Devil's Advocate** | *"Is this plan wise? What's the strongest counter-argument? What would we do if X was forbidden?"* | Before significant design decisions, pre-mortem on risky launches, when the team is converging too fast | + +**Philosophy: "Trust, but verify. Then steelman the opposition."** Fact Checker is rigorous but constructive — never gotcha-driven. Every challenge or finding includes WHAT (the issue or counter-argument), WHY (evidence or failure scenario), and HOW (the fix or alternative). + +**On-demand reference:** Read `.squad/agents/fact-checker/charter.md` (created by `squad init` / `squad upgrade` from the rich `fact-checker-charter.md` template, per #1299) for the full charter, verification methodology, confidence rating taxonomy, and pre-ship ceremony format. + +### Roster Entry + +Fact Checker always appears in `team.md`: `| Fact Checker | Fact Checker | .squad/agents/fact-checker/charter.md | 🔍 Verifier |` + +### Triggers + +| User says | Action | +|-----------|--------| +| "fact-check this" / "verify these claims" / "double-check" | Spawn Fact Checker in Verification mode | +| "play devil's advocate" / "what's wrong with this plan?" / "steelman the opposite" | Spawn Fact Checker in Devil's Advocate mode | +| "is this true?" / "does this URL/package exist?" | Spawn Fact Checker for empirical verification | +| "pre-mortem this" / "what could go wrong?" | Spawn Fact Checker for pre-mortem analysis | +| Pre-Ship ceremony (auto) | Fact Checker spawned automatically before user-facing artifacts finalize | +| Post-research (auto, optional) | After any agent produces research output or external references | + +These are intent signals, not exact strings — match meaning, not words. + +### Confidence Ratings (Verification Mode) + +Every verified item gets one of: + +| Rating | Meaning | +|--------|---------| +| ✅ **Verified** | Confirmed via source, test, or direct observation | +| ⚠️ **Unverified** | Plausible but could not confirm — needs human review | +| ❌ **Contradicted** | Found evidence that contradicts the claim | +| 🔍 **Needs Investigation** | Requires deeper analysis beyond current scope | + +### Devil's Advocate Output (DA Mode) + +Every DA brief includes: + +1. **Steelman of the opposition** — the strongest version of the counter-argument +2. **Load-bearing assumptions** — what would invalidate the plan if untrue +3. **Pre-mortem** — concrete failure scenario in 30 days +4. **Alternative approach** — at least one sketch so the chosen direction is a chosen direction +5. **Risk acceptance** — flag remaining risks for the team to consciously accept or mitigate + +### Boundaries + +**Fact Checker handles:** Claim verification, hallucination detection, counter-argument construction, pre-mortem analysis, assumption surfacing. + +**Fact Checker does not handle:** Implementation or code writing (reviews not creates), final decisions (advisory only — the team or coordinator decides), tone-policing. + +**Advisory by default.** Findings are advisory unless the coordinator or another reviewer escalates a specific risk to a gate. Never blocks on opinion, only on provably false claims or unaccepted risks. + +### Background Mode (Default) + +Fact Checker runs in background by default (like Scribe and Rai) — non-blocking. Spawns on-demand or via Pre-Ship ceremony auto-trigger. + +### Fact Checker State + +- **History** (`.squad/agents/fact-checker/history.md`) — verification + DA briefs across sessions +- **Charter** (`.squad/agents/fact-checker/charter.md`) — methodology + dual-mode operating rules +- **Decisions** — significant verification verdicts or DA briefs go to `.squad/decisions/inbox/fact-checker-{slug}.md` + +--- + ## PRD Mode Squad can ingest a PRD and use it as the source of truth for work decomposition and prioritization. diff --git a/.squad-templates/workflows/squad-heartbeat.yml b/.squad-templates/workflows/squad-heartbeat.yml index 1b75fda3e..3ab647c89 100644 --- a/.squad-templates/workflows/squad-heartbeat.yml +++ b/.squad-templates/workflows/squad-heartbeat.yml @@ -1,167 +1,164 @@ -name: Squad Heartbeat (Ralph) -# ⚠️ SYNC: This workflow is maintained in 4 locations. Changes must be applied to all: -# - templates/workflows/squad-heartbeat.yml (source template) -# - packages/squad-cli/templates/workflows/squad-heartbeat.yml (CLI package) -# - .squad/templates/workflows/squad-heartbeat.yml (installed template) -# - .github/workflows/squad-heartbeat.yml (active workflow) -# Run 'squad upgrade' to sync installed copies from source templates. - -on: - # React to completed work or new squad work - issues: - types: [closed, labeled] - pull_request: - types: [closed] - - # Manual trigger - workflow_dispatch: - -permissions: - issues: write - contents: read - pull-requests: read - -jobs: - heartbeat: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Check triage script - id: check-script - run: | - if [ -f ".squad/templates/ralph-triage.js" ]; then - echo "has_script=true" >> $GITHUB_OUTPUT - else - echo "has_script=false" >> $GITHUB_OUTPUT - echo "⚠️ ralph-triage.js not found — run 'squad upgrade' to install" - fi - - - name: Ralph — Smart triage - if: steps.check-script.outputs.has_script == 'true' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - node .squad/templates/ralph-triage.js \ - --squad-dir .squad \ - --output triage-results.json - - - name: Ralph — Apply triage decisions - if: steps.check-script.outputs.has_script == 'true' && hashFiles('triage-results.json') != '' - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs'); - const path = 'triage-results.json'; - if (!fs.existsSync(path)) { - core.info('No triage results — board is clear'); - return; - } - - const results = JSON.parse(fs.readFileSync(path, 'utf8')); - if (results.length === 0) { - core.info('📋 Board is clear — Ralph found no untriaged issues'); - return; - } - - for (const decision of results) { - try { - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: decision.issueNumber, - labels: [decision.label] - }); - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: decision.issueNumber, - body: [ - '### 🔄 Ralph — Auto-Triage', - '', - `**Assigned to:** ${decision.assignTo}`, - `**Reason:** ${decision.reason}`, - `**Source:** ${decision.source}`, - '', - '> Ralph auto-triaged this issue using routing rules.', - '> To reassign, swap the `squad:*` label.' - ].join('\n') - }); - - core.info(`Triaged #${decision.issueNumber} → ${decision.assignTo} (${decision.source})`); - } catch (e) { - core.warning(`Failed to triage #${decision.issueNumber}: ${e.message}`); - } - } - - core.info(`🔄 Ralph triaged ${results.length} issue(s)`); - - # Copilot auto-assign step (uses PAT if available) - - name: Ralph — Assign @copilot issues - if: success() - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.COPILOT_ASSIGN_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const fs = require('fs'); - - let teamFile = '.squad/team.md'; - if (!fs.existsSync(teamFile)) { - teamFile = '.ai-team/team.md'; - } - if (!fs.existsSync(teamFile)) return; - - const content = fs.readFileSync(teamFile, 'utf8'); - - // Check if @copilot is on the team with auto-assign - const hasCopilot = content.includes('🤖 Coding Agent') || content.includes('@copilot'); - const autoAssign = content.includes(''); - if (!hasCopilot || !autoAssign) return; - - // Find issues labeled squad:copilot with no assignee - try { - const { data: copilotIssues } = await github.rest.issues.listForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - labels: 'squad:copilot', - state: 'open', - per_page: 5 - }); - - const unassigned = copilotIssues.filter(i => - !i.assignees || i.assignees.length === 0 - ); - - if (unassigned.length === 0) { - core.info('No unassigned squad:copilot issues'); - return; - } - - // Get repo default branch - const { data: repoData } = await github.rest.repos.get({ - owner: context.repo.owner, - repo: context.repo.repo - }); - - for (const issue of unassigned) { - try { - await github.request('POST /repos/{owner}/{repo}/issues/{issue_number}/assignees', { - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - assignees: ['copilot-swe-agent[bot]'], - agent_assignment: { - target_repo: `${context.repo.owner}/${context.repo.repo}`, - base_branch: repoData.default_branch, - custom_instructions: `Read .squad/team.md (or .ai-team/team.md) for team context and .squad/routing.md (or .ai-team/routing.md) for routing rules.` - } - }); - core.info(`Assigned copilot-swe-agent[bot] to #${issue.number}`); - } catch (e) { - core.warning(`Failed to assign @copilot to #${issue.number}: ${e.message}`); - } - } - } catch (e) { - core.info(`No squad:copilot label found or error: ${e.message}`); - } +name: Squad Heartbeat (Ralph) +# ⚠️ SYNC: This workflow is maintained in 4 locations. Changes must be applied to all: +# - templates/workflows/squad-heartbeat.yml (source template) +# - packages/squad-cli/templates/workflows/squad-heartbeat.yml (CLI package) +# - .squad/templates/workflows/squad-heartbeat.yml (installed template) +# - .github/workflows/squad-heartbeat.yml (active workflow) +# Run 'squad upgrade' to sync installed copies from source templates. + +on: + # React to completed work or new squad work + issues: + types: [closed, labeled] + pull_request: + types: [closed] + + # Manual trigger + workflow_dispatch: + +permissions: + issues: write + contents: read + pull-requests: read + +jobs: + heartbeat: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Check triage script + id: check-script + run: | + if [ -f ".squad/templates/ralph-triage.js" ]; then + echo "has_script=true" >> $GITHUB_OUTPUT + else + echo "has_script=false" >> $GITHUB_OUTPUT + echo "⚠️ ralph-triage.js not found — run 'squad upgrade' to install" + fi + + - name: Ralph — Smart triage + if: steps.check-script.outputs.has_script == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + node .squad/templates/ralph-triage.js \ + --squad-dir .squad \ + --output triage-results.json + + - name: Ralph — Apply triage decisions + if: steps.check-script.outputs.has_script == 'true' && hashFiles('triage-results.json') != '' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const path = 'triage-results.json'; + if (!fs.existsSync(path)) { + core.info('No triage results — board is clear'); + return; + } + + const results = JSON.parse(fs.readFileSync(path, 'utf8')); + if (results.length === 0) { + core.info('📋 Board is clear — Ralph found no untriaged issues'); + return; + } + + for (const decision of results) { + try { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: decision.issueNumber, + labels: [decision.label] + }); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: decision.issueNumber, + body: [ + '### 🔄 Ralph — Auto-Triage', + '', + `**Assigned to:** ${decision.assignTo}`, + `**Reason:** ${decision.reason}`, + `**Source:** ${decision.source}`, + '', + '> Ralph auto-triaged this issue using routing rules.', + '> To reassign, swap the `squad:*` label.' + ].join('\n') + }); + + core.info(`Triaged #${decision.issueNumber} → ${decision.assignTo} (${decision.source})`); + } catch (e) { + core.warning(`Failed to triage #${decision.issueNumber}: ${e.message}`); + } + } + + core.info(`🔄 Ralph triaged ${results.length} issue(s)`); + + # Copilot auto-assign step (uses PAT if available) + - name: Ralph — Assign @copilot issues + if: success() + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.COPILOT_ASSIGN_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + + const teamFile = '.squad/team.md'; + if (!fs.existsSync(teamFile)) return; + + const content = fs.readFileSync(teamFile, 'utf8'); + + // Check if @copilot is on the team with auto-assign + const hasCopilot = content.includes('🤖 Coding Agent') || content.includes('@copilot'); + const autoAssign = content.includes(''); + if (!hasCopilot || !autoAssign) return; + + // Find issues labeled squad:copilot with no assignee + try { + const { data: copilotIssues } = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + labels: 'squad:copilot', + state: 'open', + per_page: 5 + }); + + const unassigned = copilotIssues.filter(i => + !i.assignees || i.assignees.length === 0 + ); + + if (unassigned.length === 0) { + core.info('No unassigned squad:copilot issues'); + return; + } + + // Get repo default branch + const { data: repoData } = await github.rest.repos.get({ + owner: context.repo.owner, + repo: context.repo.repo + }); + + for (const issue of unassigned) { + try { + await github.request('POST /repos/{owner}/{repo}/issues/{issue_number}/assignees', { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + assignees: ['copilot-swe-agent[bot]'], + agent_assignment: { + target_repo: `${context.repo.owner}/${context.repo.repo}`, + base_branch: repoData.default_branch, + custom_instructions: `Read .squad/team.md for team context and .squad/routing.md for routing rules.` + } + }); + core.info(`Assigned copilot-swe-agent[bot] to #${issue.number}`); + } catch (e) { + core.warning(`Failed to assign @copilot to #${issue.number}: ${e.message}`); + } + } + } catch (e) { + core.info(`No squad:copilot label found or error: ${e.message}`); + } diff --git a/.squad-templates/workflows/squad-issue-assign.yml b/.squad-templates/workflows/squad-issue-assign.yml index ad140f42d..f6b664bd8 100644 --- a/.squad-templates/workflows/squad-issue-assign.yml +++ b/.squad-templates/workflows/squad-issue-assign.yml @@ -27,13 +27,9 @@ jobs: // Extract member name from label (e.g., "squad:ripley" → "ripley") const memberName = label.replace('squad:', '').toLowerCase(); - // Read team roster — check .squad/ first, fall back to .ai-team/ - let teamFile = '.squad/team.md'; + const teamFile = '.squad/team.md'; if (!fs.existsSync(teamFile)) { - teamFile = '.ai-team/team.md'; - } - if (!fs.existsSync(teamFile)) { - core.warning('No .squad/team.md or .ai-team/team.md found — cannot assign work'); + core.warning('No .squad/team.md found — cannot assign work'); return; } @@ -72,7 +68,7 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, - body: `⚠️ No squad member found matching label \`${label}\`. Check \`.squad/team.md\` (or \`.ai-team/team.md\`) for valid member names.` + body: `⚠️ No squad member found matching label \`${label}\`. Check \`.squad/team.md\` for valid member names.` }); return; } diff --git a/.squad-templates/workflows/squad-triage.yml b/.squad-templates/workflows/squad-triage.yml index d118a2813..8eaf51f65 100644 --- a/.squad-templates/workflows/squad-triage.yml +++ b/.squad-templates/workflows/squad-triage.yml @@ -22,13 +22,9 @@ jobs: const fs = require('fs'); const issue = context.payload.issue; - // Read team roster — check .squad/ first, fall back to .ai-team/ - let teamFile = '.squad/team.md'; + const teamFile = '.squad/team.md'; if (!fs.existsSync(teamFile)) { - teamFile = '.ai-team/team.md'; - } - if (!fs.existsSync(teamFile)) { - core.warning('No .squad/team.md or .ai-team/team.md found — cannot triage'); + core.warning('No .squad/team.md found — cannot triage'); return; } @@ -88,11 +84,7 @@ jobs: } } - // Read routing rules — check .squad/ first, fall back to .ai-team/ - let routingFile = '.squad/routing.md'; - if (!fs.existsSync(routingFile)) { - routingFile = '.ai-team/routing.md'; - } + const routingFile = '.squad/routing.md'; let routingContent = ''; if (fs.existsSync(routingFile)) { routingContent = fs.readFileSync(routingFile, 'utf8'); diff --git a/.squad-templates/workflows/sync-squad-labels.yml b/.squad-templates/workflows/sync-squad-labels.yml index 699fc680f..f3799e793 100644 --- a/.squad-templates/workflows/sync-squad-labels.yml +++ b/.squad-templates/workflows/sync-squad-labels.yml @@ -4,7 +4,6 @@ on: push: paths: - '.squad/team.md' - - '.ai-team/team.md' workflow_dispatch: permissions: @@ -22,13 +21,9 @@ jobs: with: script: | const fs = require('fs'); - let teamFile = '.squad/team.md'; + const teamFile = '.squad/team.md'; if (!fs.existsSync(teamFile)) { - teamFile = '.ai-team/team.md'; - } - - if (!fs.existsSync(teamFile)) { - core.info('No .squad/team.md or .ai-team/team.md found — skipping label sync'); + core.info('No .squad/team.md found — skipping label sync'); return; } diff --git a/.squad/agents/_alumni/saul/charter.md b/.squad/agents/_alumni/saul/charter.md index aef3a1f01..db79b87ea 100644 --- a/.squad/agents/_alumni/saul/charter.md +++ b/.squad/agents/_alumni/saul/charter.md @@ -18,7 +18,7 @@ ## How I Work -- It's "Aspire" — not ".NET Aspire." Aspire is a standalone dashboard for any OTLP app. +- Use "Aspire" without a ".NET" prefix. Aspire is a standalone dashboard for any OTLP app. - Reference aspire.dev for documentation, NOT learn.microsoft.com - Use the very latest Aspire bits (mcr.microsoft.com/dotnet/aspire-dashboard:latest) - Aspire dashboard: port 18888 (UI), port 18889→4317 (OTLP/gRPC) diff --git a/.squad/decisions-archive.md b/.squad/decisions-archive.md index d44f3a31e..5888002d4 100644 --- a/.squad/decisions-archive.md +++ b/.squad/decisions-archive.md @@ -75,7 +75,7 @@ ### 2026-02-22: User directive — Aspire testing requirements **By:** Brady (via Copilot) -**What:** Integration tests must launch the Aspire dashboard and validate OTel telemetry shows up. Use Playwright. Use latest Aspire bits. Reference aspire.dev (NOT learn.microsoft.com). It's "Aspire" not ".NET Aspire". +**What:** Integration tests must launch the Aspire dashboard and validate OTel telemetry shows up. Use Playwright. Use latest Aspire bits. Reference aspire.dev (NOT learn.microsoft.com). Use "Aspire" without a ".NET" prefix. ### 2026-02-23: User directive — code fences **By:** Brady (via Copilot) diff --git a/.squad/decisions/inbox/procedures-fix-coordinator-inline-dispatch-gate.md b/.squad/decisions/inbox/procedures-fix-coordinator-inline-dispatch-gate.md new file mode 100644 index 000000000..e65fee749 --- /dev/null +++ b/.squad/decisions/inbox/procedures-fix-coordinator-inline-dispatch-gate.md @@ -0,0 +1,45 @@ +# Decision: Restore always-on coordinator inline-dispatch gate (v0.10.0 regression) + +- **Author:** Procedures (Prompt Engineer) +- **Date:** 2026-06-24 +- **Requested by:** Tamir Dresher +- **Branch:** squad/fix-coordinator-inline-dispatch-gate (worktree) +- **Status:** Proposed — applied in working tree, awaiting Lead (Flight) review; NOT committed. + +## Context + +Matthew Wan reported the main Squad coordinator "does a lot of work on its own instead of using his roster of agents." Worked in v0.9.4, broke in v0.10.0. Dina Berry worked around it by hand-editing the coordinator charter. + +## Root cause + +Commit `afe78188` (#1035, "context overflow sentinel + coordinator size reduction") cut the coordinator template and moved the concrete inline-dispatch gate and dispatch mechanics out of the always-on prompt into lazy-loaded reference files (`client-compatibility-reference.md`, `spawn-reference.md`). The leftover inline guidance ("inline work is last-resort fallback only") was too soft to hold the line, so the coordinator began executing domain work itself. + +## Decision + +Re-establish a strong, **always-on** dispatch gate without breaking the legitimate Direct/Lightweight response modes or the size-reduction spirit. Three minimal edits (~14 lines) to the canonical template `.squad-templates/squad.agent.md`, propagated to all 5 synced copies via `npm run sync-templates`: + +1. **Inline-dispatch gate** (Client Compatibility): inline permitted ONLY in Direct Mode, or when NEITHER `task` NOR `runSubagent` exists; otherwise MUST dispatch. +2. **STOP gate** (How to Spawn an Agent): about to emit a domain artifact with no spawn call this turn → STOP and dispatch (exceptions: Direct Mode / no spawn tool). +3. **VS Code `runSubagent` micro-playbook** re-inlined (~5 lines) so how-to-dispatch is always-on. + +## The 5 synced copies (canonical first) + +1. `.squad-templates/squad.agent.md` (canonical — edited here) +2. `templates/squad.agent.md.template` +3. `.github/agents/squad.agent.md` +4. `packages/squad-cli/templates/squad.agent.md.template` +5. `packages/squad-sdk/templates/squad.agent.md.template` + +## Validation + +- New regression/parity test `test/coordinator-inline-dispatch-gate.test.ts` (deterministic, subprocess-free): RED before (8/8 fail), GREEN after (8/8 pass). +- Full `npm run test`: 18 failed files vs 31 baseline (fewer); all failures are pre-existing flaky subprocess/timeout e2e tests (3 of 4 "new vs baseline" pass in isolation; the 4th is `squad doctor` at 40x10 exiting null = timeout, unrelated to template content). No new deterministic failures. +- `npm run lint` (tsc noEmit both packages): clean. + +## Changeset + +Required. The `changelog-gate` regex governs `.squad-templates/`, `templates/`, and `packages/squad-(sdk|cli)/templates/` — not just `src/`. Added `.changeset/fix-coordinator-inline-dispatch-gate.md` (patch, squad-cli + squad-sdk). + +## Rollout + +Existing installs require `squad upgrade` to pick up the regenerated coordinator agent. diff --git a/.squad/skills/coordinator-init-mode/SKILL.md b/.squad/skills/coordinator-init-mode/SKILL.md new file mode 100644 index 000000000..efc61ba3a --- /dev/null +++ b/.squad/skills/coordinator-init-mode/SKILL.md @@ -0,0 +1,83 @@ +--- +name: "coordinator-init-mode" +description: "The complete two-phase Init Mode protocol the Squad coordinator runs when no team exists yet in the current repo. Phase 1 = propose the team (no files created, wait for user confirm). Phase 2 = create .squad/ scaffolding, casting state, .gitattributes for merge drivers, and the always-on built-ins (Scribe, Ralph, Rai, Fact Checker). Loaded on demand when the coordinator detects no .squad/team.md exists." +allowedTools: [] +confidence: high +domain: squad-internals +source: "Extracted from squad.agent.md as part of the slimming effort (bradygaster/squad#1308). Behaviour unchanged — coordinator loads this satellite skill when init mode is detected (no .squad/team.md present)." +--- + +> **Load this skill when:** you detect that no `.squad/team.md` exists in the resolved team root — that means this is a fresh repo or a repo that has never been squadified, and Init Mode is the right path. The short stub in `squad.agent.md` tells you to load this skill; the full two-phase protocol lives here. +> +> **⚠️ Eager-execution exception:** Init Mode is the ONE exception to the eager-execution / parallel-fan-out doctrine. Phase 1 MUST end with a user confirmation before any file is created. Do not bypass. + +## Phase 1: Propose the Team + +No team exists yet. **Propose one — but DO NOT create any files until the user confirms.** + +1. **Identify the user.** Run `git config user.name` to learn who you're working with. Use their name in conversation (e.g., *"Hey {user}, what are you building?"*). Store their name (NOT email) in `team.md` under Project Context. **Never read or store `git config user.email`** — email addresses are PII and must not be written to committed files. +2. Ask: *"What are you building? (language, stack, what it does)"* +3. **Cast the team.** Before proposing names, run the Casting & Persistent Naming algorithm (see the canonical Casting reference at `.squad/templates/casting-reference.md`): + - Determine team size: pick **4–5 cast (user-domain) agents**, then add the **4 always-on built-ins** (Scribe + Ralph + Rai + Fact Checker — see their dedicated sections in `squad.agent.md`). A typical fresh squad has **8–9 total roster entries**, not 4–5. + - Determine assignment shape from the user's project description. + - Derive resonance signals from the session and repo context. + - Select a universe. Allocate character names from that universe. + - Scribe is always "Scribe" — exempt from casting. + - Ralph is always "Ralph" — exempt from casting. + - Rai is always "Rai" — exempt from casting. + - Fact Checker is always "Fact Checker" — exempt from casting (same pattern as Scribe / Ralph / Rai). +4. Propose the team with their cast names. Example (names will vary per cast): + +``` +🏗️ {CastName1} — Lead Scope, decisions, code review +⚛️ {CastName2} — Frontend Dev React, UI, components +🔧 {CastName3} — Backend Dev APIs, database, services +🧪 {CastName4} — Tester Tests, quality, edge cases +📋 Scribe — (silent) Memory, decisions, session logs +🔄 Ralph — (monitor) Work queue, backlog, keep-alive +🛡️ Rai — (background) RAI awareness, content safety +🔍 Fact Checker — (verifier) Verification + Devil's Advocate +``` + +5. Use the `ask_user` tool to confirm the roster. Provide choices so the user sees a selectable menu: + - **question:** *"Look right?"* + - **choices:** `["Yes, hire this team", "Add someone", "Change a role"]` + +**⚠️ STOP. Your response ENDS here. Do NOT proceed to Phase 2. Do NOT create any files or directories. Wait for the user's reply.** + +--- + +## Phase 2: Create the Team + +**Trigger:** The user replied to Phase 1 with confirmation ("yes", "looks good", or similar affirmative), OR the user's reply to Phase 1 is a task (treat as implicit "yes"). + +> If the user said "add someone" or "change a role," go back to Phase 1 step 3 and re-propose. **Do NOT enter Phase 2 until the user confirms.** + +6. Create the `.squad/` directory structure (see `.squad/templates/` for format guides or use the standard structure: `team.md`, `routing.md`, `ceremonies.md`, `decisions.md`, `decisions/inbox/`, `casting/`, `agents/`, `orchestration-log/`, `skills/`, `log/`, `rai/`). + +**Casting state initialization:** Copy `.squad/templates/casting-policy.json` to `.squad/casting/policy.json` (or create from defaults). Create `registry.json` (entries: persistent_name, universe, created_at, legacy_named: false, status: "active") and `history.json` (first assignment snapshot with unique assignment_id). + +**Seeding:** Each agent's `history.md` starts with the project description, tech stack, and the user's name so they have day-1 context. Agent folder names are the cast name in lowercase (e.g., `.squad/agents/ripley/`). The Scribe's charter includes maintaining `decisions.md` and cross-agent context sharing. Rai's charter is seeded from the `Rai-charter.md` template, and `.squad/rai/policy.md` is seeded from `rai-policy.md`. Fact Checker's charter is seeded from `fact-checker-charter.md` and `.squad/fact-checker/policy.md` is seeded from `fact-checker-policy.md`. + +**Team.md structure:** `team.md` MUST contain a section titled exactly `## Members` (not "## Team Roster" or other variations) containing the roster table. This header is hard-coded in GitHub workflows (`squad-heartbeat.yml`, `squad-issue-assign.yml`, `squad-triage.yml`, `sync-squad-labels.yml`) for label automation. If the header is missing or titled differently, label routing breaks. + +**Merge driver for append-only files:** Create or update `.gitattributes` at the repo root to enable conflict-free merging of `.squad/` state across branches: + +``` +.squad/decisions.md merge=union +.squad/agents/*/history.md merge=union +.squad/log/** merge=union +.squad/orchestration-log/** merge=union +.squad/rai/audit-trail.md merge=union +``` + +The `union` merge driver keeps all lines from both sides, which is correct for append-only files. This makes worktree-local strategy work seamlessly when branches merge — decisions, memories, and logs from all branches combine automatically. + +7. Say: *"✅ Team hired. Try: '{FirstCastName}, set up the project structure'"* + +8. **Post-setup input sources** (optional — ask after team is created, not during casting): + - **PRD/spec:** *"Do you have a PRD or spec document? (file path, paste it, or skip)"* → If provided, follow PRD Mode flow. + - **GitHub issues:** *"Is there a GitHub repo with issues I should pull from? (owner/repo, or skip)"* → If provided, follow GitHub Issues Mode flow. + - **Human members:** *"Are any humans joining the team? (names and roles, or just AI for now)"* → If provided, add per Human Team Members section. + - **Copilot agent:** *"Want to include @copilot? It can pick up issues autonomously. (yes/no)"* → If yes, follow Copilot Coding Agent Member section and ask about auto-assignment. + - These are additive. **Don't block** — if the user skips or gives a task instead, proceed immediately. diff --git a/.squad/skills/coordinator-response-mode/SKILL.md b/.squad/skills/coordinator-response-mode/SKILL.md new file mode 100644 index 000000000..4dff3b800 --- /dev/null +++ b/.squad/skills/coordinator-response-mode/SKILL.md @@ -0,0 +1,97 @@ +--- +name: "coordinator-response-mode" +description: "Selecting WHO handles work is the Routing table; selecting HOW they handle it (Direct, Lightweight, Standard, Full) is Response Mode. This skill contains the complete decision table, exemplar prompts for each mode, the Lightweight spawn template, and the upgrade rules. Squad coordinator loads this on demand once routing has identified the agent — to pick the right ceremony level for the task." +allowedTools: [] +confidence: high +domain: squad-internals +source: "Extracted from squad.agent.md as part of the slimming effort (bradygaster/squad#1308). Behaviour unchanged — coordinator loads this satellite skill after Routing, before spawn." +--- + +> **Load this skill when:** you have routed work to an agent and need to pick the response mode (Direct / Lightweight / Standard / Full). The 1-line stub in `squad.agent.md` is for awareness; this skill is the full decision table + templates. + +## Response Mode Selection + +After routing determines WHO handles work, select the response MODE based on task complexity. **Bias toward upgrading** — when uncertain, go one tier higher rather than risk under-serving. + +| Mode | When | How | Target | +|------|------|-----|--------| +| **Direct** | Status checks, factual questions the coordinator already knows, simple answers from context | Coordinator answers directly — NO agent spawn | ~2-3s | +| **Lightweight** | Single-file edits, small fixes, follow-ups, simple scoped read-only queries | Spawn ONE agent with minimal prompt (see Lightweight Spawn Template below). Use `agent_type: "explore"` for read-only queries | ~8-12s | +| **Standard** | Normal tasks, single-agent work requiring full context | Spawn one agent with full ceremony — charter inline, history read, decisions read. This is the current default | ~25-35s | +| **Full** | Multi-agent work, complex tasks touching 3+ concerns, "Team" requests | Parallel fan-out, full ceremony, Scribe included | ~40-60s | + +## Direct Mode exemplars + +Coordinator answers instantly, no spawn: + +- *"Where are we?"* → Summarize current state from context: branch, recent work, what the team's been doing. A user favorite — make it instant. +- *"How many tests do we have?"* → Run a quick command, answer directly. +- *"What branch are we on?"* → `git branch --show-current`, answer directly. +- *"Who's on the team?"* → Answer from `team.md` already in context. +- *"What did we decide about X?"* → Answer from `decisions.md` already in context. + +## Lightweight Mode exemplars + +One agent, minimal prompt: + +- *"Fix the typo in README"* → Spawn one agent, no charter, no history read. +- *"Add a comment to line 42"* → Small scoped edit, minimal context needed. +- *"What does this function do?"* → `agent_type: "explore"` (Haiku model, fast). +- Follow-up edits after a Standard/Full response — context is fresh, skip ceremony. + +## Standard Mode exemplars + +One agent, full ceremony: + +- *"{AgentName}, add error handling to the export function"* +- *"{AgentName}, review the prompt structure"* +- Any task requiring architectural judgment or multi-file awareness. + +## Full Mode exemplars + +Multi-agent, parallel fan-out: + +- *"Team, build the login page"* +- *"Add OAuth support"* +- Any request that touches 3+ agent domains. + +## Mode upgrade rules + +- If a Lightweight task turns out to need history or decisions context → treat as Standard. +- If uncertain between Direct and Lightweight → choose Lightweight. +- If uncertain between Lightweight and Standard → choose Standard. +- **Never downgrade mid-task.** If you started Standard, finish Standard. + +## Lightweight Spawn Template + +Skip charter, history, and decisions reads — just the task: + +``` +agent_type: "general-purpose" +model: "{resolved_model}" +mode: "background" +name: "{name}" +description: "{emoji} {Name}: {brief task summary}" +prompt: | + You are {Name}, the {Role} on this project. + TEAM ROOT: {team_root} + CURRENT_DATETIME: + WORKTREE_PATH: {worktree_path} + WORKTREE_MODE: {true|false} + **Requested by:** {current user name} + + {% if WORKTREE_MODE %} + **WORKTREE:** Working in `{WORKTREE_PATH}`. All operations relative to this path. Do NOT switch branches. + {% endif %} + + TASK: {specific task description} + TARGET FILE(S): {exact file path(s)} + + Do the work. Keep it focused. + If you made a meaningful decision, persist it with `memory.write` (class: `decision`) when available, or fall back to `squad_decide` / `squad_state_write` to `decisions/inbox/{name}-{brief-slug}.md`. Do not run git notes, switch branches, or write mutable `.squad/` state by hand. + + ⚠️ OUTPUT: Report outcomes in human terms. Never expose tool internals or SQL. + ⚠️ RESPONSE ORDER: After ALL tool calls, write a plain text summary as FINAL output. +``` + +For **read-only queries**, use the explore agent: `agent_type: "explore"` with `"You are {Name}, the {Role}. CURRENT_DATETIME: — {question} TEAM ROOT: {team_root}"` diff --git a/.squad/skills/coordinator-source-of-truth/SKILL.md b/.squad/skills/coordinator-source-of-truth/SKILL.md new file mode 100644 index 000000000..3a992ba0a --- /dev/null +++ b/.squad/skills/coordinator-source-of-truth/SKILL.md @@ -0,0 +1,45 @@ +--- +name: "coordinator-source-of-truth" +description: "The complete file-by-file source-of-truth hierarchy for Squad: which files are authoritative, which are derived/append-only, who may write each one, who may read each one, and the precedence rules when they conflict. Squad coordinator loads this on demand when it needs to resolve a write conflict, decide where a piece of state belongs, or answer a 'who owns this file' question." +allowedTools: [] +confidence: high +domain: squad-internals +source: "Extracted from squad.agent.md as part of the slimming effort (bradygaster/squad#1308). Behaviour unchanged — coordinator loads this satellite skill when a routing decision needs the full hierarchy." +--- + +> **Load this skill when:** the coordinator (or any agent) needs to resolve a "where does this state belong?" or "who is allowed to write this file?" question — e.g., when about to write `.squad/decisions.md`, when reviewing whether an agent broke the append-only rule, when answering a user question about Squad's file layout, or when adjudicating a conflict between two files. The short summary in `squad.agent.md` is for routing; this skill is the full reference. + +## State backend note + +Files below marked as **"Derived / append-only"** are **mutable state** — agents access them with runtime state tools (`squad_state_read`, `squad_state_write`, `squad_state_append`, `squad_state_delete`, `squad_state_list`). The runtime decides whether the configured backend stores them on disk, git-native state, or an external provider. Files marked as **"Authoritative"** are **static config** and always live on disk regardless of backend. + +## File hierarchy + +| File | Status | Who May Write | Who May Read | +|------|--------|---------------|--------------| +| `.github/agents/squad.agent.md` | **Authoritative governance.** All roles, handoffs, gates, and enforcement rules. | Repo maintainer (human) | Squad (Coordinator) | +| `.squad/decisions.md` | **Authoritative decision ledger.** Single canonical location for scope, architecture, and process decisions. | Squad (Coordinator) — append only | All agents | +| `.squad/team.md` | **Authoritative roster.** Current team composition. | Squad (Coordinator) | All agents | +| `.squad/routing.md` | **Authoritative routing.** Work assignment rules. | Squad (Coordinator) | Squad (Coordinator) | +| `.squad/ceremonies.md` | **Authoritative ceremony config.** Definitions, triggers, and participants for team ceremonies. | Squad (Coordinator) | Squad (Coordinator), Facilitator agent (read-only at ceremony time) | +| `.squad/casting/policy.json` | **Authoritative casting config.** Universe allowlist and capacity. | Squad (Coordinator) | Squad (Coordinator) | +| `.squad/casting/registry.json` | **Authoritative name registry.** Persistent agent-to-name mappings. | Squad (Coordinator) | Squad (Coordinator) | +| `.squad/casting/history.json` | **Derived / append-only.** Universe usage history and assignment snapshots. | Squad (Coordinator) — append only | Squad (Coordinator) | +| `.squad/agents/{name}/charter.md` | **Authoritative agent identity.** Per-agent role and boundaries. | Squad (Coordinator) at creation; agent may not self-modify | Squad (Coordinator) reads to inline at spawn; owning agent receives via prompt | +| `.squad/agents/{name}/history.md` | **Derived / append-only.** Personal learnings. Never authoritative for enforcement. | Owning agent (append only), Scribe (cross-agent updates, summarization) | Owning agent only | +| `.squad/agents/{name}/history-archive.md` | **Derived / append-only.** Archived history entries. Preserved for reference. | Scribe | Owning agent (read-only) | +| `.squad/orchestration-log/` | **Derived / append-only.** Agent routing evidence. Never edited after write. | Scribe | All agents (read-only) | +| `.squad/log/` | **Derived / append-only.** Session logs. Diagnostic archive. Never edited after write. | Scribe | All agents (read-only) | +| `.squad/templates/` | **Reference.** Format guides for runtime files. Not authoritative for enforcement. | Squad (Coordinator) at init | Squad (Coordinator) | +| `.squad/rai/policy.md` | **Authoritative RAI policy.** Check categories, terminology standards, and opt-out rules. | Squad (Coordinator) at init; Rai may propose updates via decisions inbox | Rai, All agents (read-only) | +| `.squad/rai/audit-trail.md` | **Derived / append-only.** RAI review evidence log. Redacted — never contains raw secrets or harmful content. | Rai (append only) | Rai, Squad (Coordinator) | +| `.squad/fact-checker/policy.md` | **Authoritative verification + Devil's Advocate policy.** Confidence rating taxonomy, hard anti-fabrication rules, mode triggers, opt-out model. | Squad (Coordinator) at init; Fact Checker may propose updates via decisions inbox | Fact Checker, All agents (read-only) | +| `.squad/fact-checker/audit-trail.md` | **Derived / append-only.** Verification verdicts + DA brief evidence log. Succinct — verdict + citation, never raw source material. | Fact Checker (append only) | Fact Checker, Squad (Coordinator) | +| `.squad/plugins/marketplaces.json` | **Authoritative plugin config.** Registered marketplace sources. | Squad CLI (`squad plugin marketplace`) | Squad (Coordinator) | + +## Rules + +1. **If `squad.agent.md` and any other file conflict, `squad.agent.md` wins.** It is the only file with hard governance authority. +2. **Append-only files must never be retroactively edited** to change meaning. They are diagnostic and audit-trail material. Corrections go in a new entry that references the prior one. +3. **Agents may only write to files listed in their "Who May Write" column above.** Violations are a contract bug; runtime state-backends will refuse the write on non-local backends. +4. **Non-coordinator agents may propose decisions** in their responses, but only Squad (Coordinator) records accepted decisions in `.squad/decisions.md`. diff --git a/.squad/skills/cross-squad-communication/SKILL.md b/.squad/skills/cross-squad-communication/SKILL.md new file mode 100644 index 000000000..9286d5fa2 --- /dev/null +++ b/.squad/skills/cross-squad-communication/SKILL.md @@ -0,0 +1,399 @@ +--- +name: "cross-squad-communication" +description: "Protocol for sending queries, delegating tasks, and sharing context between independent Squad instances across different repositories" +domain: "multi-repo coordination" +confidence: "medium" +source: "Ported from tamirdresher/squad-skills (plugins/cross-squad-communication). Companion to the registry-aware cross-squad skill — this one teaches the actual communication protocols once a peer is discovered. Pattern 0 (synchronous CLI) is the only end-to-end-validated pattern; Patterns 1, 2, 3 are documented from design but require live validation against your own setup before relying on them in production. See the Validation Status section at the bottom of this skill." +--- + +## Context + +When multiple repositories each have their own Squad (AI team), they need to exchange information: knowledge queries, PR reviews, task delegation, and dependency analysis. Each squad has its own agents, MCP tools, and issue tracker — there is no shared runtime. + +> **Companion skill — read first:** `cross-squad/SKILL.md` covers **discovery** of peer squads via `squad registry add/list/remove`. This skill picks up after a peer is known and covers the **communication protocols** themselves — the four numbered patterns below: Pattern 0 (synchronous CLI), Pattern 1 (read-only knowledge query), Pattern 2 (git-based async), and Pattern 3 (GitHub-issue-based delegation). A separate non-numbered appendix (Cross-Repo Dependency Scan) is provided as a related analysis tool, not a communication pattern. The two skills are designed to be used together. + +**When this skill applies:** +- A squad agent needs information from another squad-enabled repo +- A task needs to be delegated to another squad +- Cross-repo dependency analysis is needed +- PR review requests span repo boundaries + +**Key constraint:** Each squad has its own runtime, MCP tools, and issue tracker. Cross-squad communication can be **synchronous** (via CLI session targeting the other repo) or **asynchronous** (file-based or issue-based). The coordinator decides which approach fits. + +--- + +## Patterns + +### Decision Tree: Choosing the Right Pattern + +``` +Is the target repo cloned locally? +├─ NO → Use Pattern 3 (Issue-Based) or Pattern 2 (Git-Based Async) +└─ YES + ├─ Is this a quick query / knowledge lookup? + │ └─ YES → Use Pattern 0 (Synchronous CLI) — fastest + ├─ Does the work need to persist as artifacts? + │ └─ YES → Use Pattern 2 (Git-Based Async) + ├─ Is it a long-running analysis or multi-cycle task? + │ └─ YES → Use Pattern 2 (Git-Based Async) + └─ Is the target squad's Ralph running? + ├─ YES → Pattern 2 or 3 (async processing available) + └─ NO → Pattern 0 (Synchronous CLI) or Pattern 1 (Read-Only) +``` + +--- + +## Universal rule: every `copilot` spawn into a peer squad MUST pass `--agent squad` + +The `copilot` CLI accepts `--agent ` to select a custom agent (see `copilot --help`). Squad installs ship `.github/agents/squad.agent.md`, which is loaded **only when `--agent squad` is specified**. Without it the spawned session runs as a generic Copilot CLI session that does NOT load the peer's `team.md`, routing, MCP tools, casting, or coordinator behaviour — so you get an off-the-shelf model answering, not the peer's Squad. **Every command example in this skill that spawns `copilot` into a peer repo includes `--agent squad`; do not strip it.** + +This rule also applies anywhere else you spawn `copilot` into a Squad-initialised repo (not just cross-squad protocols) — e.g., `squad init`'s post-init tip and any automation that invokes the CLI on a squadified folder. The only case where you may omit `--agent` is when resuming an existing session (`copilot --resume `) — the resumed session preserves its original agent context. + +--- + +### Pattern 0: Synchronous CLI Session (Fastest for Interactive Queries) + +For quick knowledge queries, decision lookups, or short analyses — spawn a Copilot CLI session with the working directory set to the target squad's repo. This lets you send a prompt and get a response within the same session, using the target repo's full context. + +This is the same technique used by `ralph-watch.ps1`: write the prompt to a temp file, then invoke the CLI with that file as input. The key insight is that setting the working directory to the target repo gives the CLI session access to that squad's `.squad/` metadata, codebase, and conventions. + +**Protocol:** +1. Write prompt to a temp file (avoids argument-splitting issues, as learned in `ralph-watch.ps1`) +2. Read the file into a string and invoke `copilot -p ` with `-C ` set to the target repo (`-p` takes prompt text, NOT a file path) AND `--agent squad` so the spawned session uses the peer squad's coordinator (without `--agent` you get a generic Copilot CLI session that doesn't load the peer's `team.md`, MCP tools, or skills) +3. Receive response in the same session + +**Invocation:** +```powershell +# Spawn a Copilot CLI session targeting another squad's repo +$targetRepo = "C:\repos\platform-squad-repo" +$promptFile = New-TemporaryFile +@" +You are working in a Squad-enabled repository. +Read .squad/team.md and .squad/decisions.md first. + +[CROSS-SQUAD REQUEST] +From: research-squad +Request Type: knowledge_query +Query: What is the current architecture of the platform? What services does it expose? +Response Format: Brief structured summary +"@ | Out-File $promptFile -Encoding utf8 + +# Option A: copilot with prompt file (read file into string; -p takes text, not a path) +# --agent squad is REQUIRED: the target is another Squad install, so the spawned +# session must use that squad's coordinator (not a generic Copilot CLI session). +copilot -C $targetRepo --agent squad -p (Get-Content $promptFile -Raw) --allow-all-tools + +# Option B: Start-Process for non-blocking (ralph-watch.ps1 style) +Start-Process pwsh -ArgumentList "-NoProfile -Command `"copilot -C '$targetRepo' --agent squad -p (Get-Content '$promptFile' -Raw) --allow-all-tools`"" -Wait + +# Option C: Pipe directly (stdin is the prompt text) +"What is the platform architecture?" | copilot -C $targetRepo --agent squad --allow-all-tools +``` + +**When to use synchronous vs async:** + +| Scenario | Pattern | Why | +|----------|---------|-----| +| Quick knowledge query | Synchronous CLI (Pattern 0) | Fast answer, no overhead | +| "What did you decide about X?" | Synchronous CLI (Pattern 0) | Read decisions.md via the target squad's context | +| PR review request | Either (Pattern 0 or 2/3) | Sync for quick feedback, async for thorough review | +| Task delegation (do work in their repo) | Async (Pattern 2 or 3) | Work needs to persist beyond the session | +| Long-running analysis | Async (Pattern 2) | May take multiple cycles | +| Target repo not locally cloned | Async (Pattern 3) | Can't set working directory to a remote repo | + +**The coordinator decides which pattern to use based on:** +1. Is the target repo cloned locally? → If yes, sync CLI is available +2. Is this a quick query or a long task? → Quick = sync, long = async +3. Does the work need to persist? → If yes, use async (creates artifacts) +4. Is the target squad's Ralph running? → Needed for async processing + +**Requirements:** +- Target repo must be cloned locally (for `copilot -C `) +- Target repo must be Squad-initialised (`.squad/config.json` + `.github/agents/squad.agent.md` present), so `--agent squad` resolves to the peer's coordinator +- Prompt file avoids argument-splitting bugs (see `ralph-watch.ps1` lines 2166-2184) + +**Response quality:** ⭐⭐⭐⭐⭐ — the CLI session has full context of the target repo, including code, squad metadata, and MCP tools. + +### Liveness Protocol for Pattern 0 + +The synchronous CLI session requires monitoring to avoid false timeouts. With 7+ MCP servers initializing and `.squad/` metadata being read, startup can take 30-60 seconds. A hard timeout kills valid sessions before they complete. Instead, monitor the agency session's activity log directory. + +**Health Check Approach:** + +Instead of a fixed wall-clock timeout, monitor the agency session log directory for activity: + +```powershell +# The Copilot CLI creates a session log directory at ~/.copilot/logs/. +# Older `agency` runtimes wrote to ~/.agency/logs/; fall back to that +# location if the new path doesn't exist yet on the user's machine. +# e.g., ~/.copilot/logs/session_20260325_071211_57824 +$copilotLogs = "$env:USERPROFILE\.copilot\logs" +$agencyLogs = "$env:USERPROFILE\.agency\logs" +$logRoot = if (Test-Path $copilotLogs) { $copilotLogs } elseif (Test-Path $agencyLogs) { $agencyLogs } else { $null } +if ($logRoot) { + $logDir = Get-ChildItem $logRoot -Directory | Sort-Object LastWriteTime -Descending | Select-Object -First 1 +} +$lastSize = 0 +$stallCount = 0 + +while ($proc -and -not $proc.HasExited) { + Start-Sleep -Seconds 15 + $currentSize = (Get-ChildItem $logDir -Recurse -File | Measure-Object -Property Length -Sum).Sum + + if ($currentSize -eq $lastSize) { + $stallCount++ + if ($stallCount -ge 4) { # 60s with no progress + Write-Warning "Session stalled — no log activity for 60s" + break + } + } else { + $stallCount = 0 + $lastSize = $currentSize + } +} +``` + +**Progress Indicators (What Counts as "Alive"):** + +- New files appearing in the session log directory (e.g., `transcript.log`, `mcp-server-logs/`) +- Log file size increasing (indicates active processing) +- New or modified `.squad/` files in the target repo (e.g., `decisions/inbox.md`, `identity/history.md`) +- Process still running and consuming non-idle CPU time + +**Stall Detection (When to Intervene):** + +- **No log activity for 60s** → Issue a warning; session may be slow but not hung +- **No log activity for 120s** → Likely stuck; consider terminating and checking logs +- **Process exited with non-zero exit code** → Failed; examine `transcript.log` and `stderr` for errors +- **MCP server connection timeout** → Session blocked waiting for an MCP server response + +**Recovery Actions When Stalled:** + +1. **Check for user input waiting:** Inspect logs for prompts or dialogs (shouldn't happen with `--autopilot`) +2. **Check MCP server health:** Review `mcp-server-logs/` for connection errors or timeouts +3. **Retry with `--disable-builtin-mcps` flag:** For lightweight queries that don't require MCP tools + ```powershell + # Retry without MCP servers — faster startup, limited capability + copilot -C $targetRepo --agent squad -p (Get-Content $promptFile -Raw) --disable-builtin-mcps --allow-all-tools + ``` +4. **Increase timeout threshold:** If MCP server initialization is consistently slow (>90s), raise threshold before declaring stall + +--- + +### Pattern 1: Read-Only Knowledge Query (No CLI Needed) + +For questions about another squad's architecture, decisions, or current state — read their `.squad/` metadata directly. + +**Protocol:** +1. Read target repo's `.squad/team.md` → get stack, members, issue source +2. Read `.squad/decisions.md` → get architectural decisions +3. Read `.squad/routing.md` → understand who handles what +4. Read `.squad/identity/now.md` → get current focus +5. Scan code structure if needed (csproj files, directory layout) + +**Requirements:** +- Target repo must be cloned locally or accessible via git +- No authentication needed beyond git read access + +**Example:** +```powershell +# Query another squad's architecture +$targetRepo = "C:\repos\platform-squad-repo" +Get-Content "$targetRepo\.squad\team.md" +Get-Content "$targetRepo\.squad\decisions.md" +Get-Content "$targetRepo\.squad\identity\now.md" +``` + +**Response quality:** ⭐⭐⭐⭐ — excellent for structural/architectural questions. + +--- + +### Pattern 2: Async Task Request (Git-Based) + +For work that needs the target squad to execute (PR reviews, issue analysis, code changes). + +**Protocol:** +1. Create request file in YOUR repo: `.squad/cross-squad/requests/{timestamp}-{target}-{id}.yaml` +2. Commit and push +3. Target squad's Ralph detects on next cycle +4. Target squad processes and writes response to their `.squad/cross-squad/responses/` +5. Your Ralph picks up the response + +**Request File Format:** +```yaml +id: req-2026-06-13-001 +source_squad: research-squad +source_repo: your-org/research-squad-repo +target_squad: platform-squad +target_repo: your-org/platform-squad-repo +request_type: knowledge_query | pr_review | task_delegation | dependency_check +priority: high | normal | low +created_at: 2026-06-13T10:00:00Z +query: "What is the current architecture of the platform?" +routing_hint: "lead" # optional — which agent should handle this +status: pending +``` + +**Response File Format:** +```yaml +id: req-2026-06-13-001 +responding_squad: platform-squad +responding_agent: lead +responded_at: 2026-06-13T10:15:00Z +status: completed | partial | rejected +response: | + The platform architecture consists of... +artifacts: [] # optional file paths +``` + +--- + +### Pattern 3: Issue-Based Delegation (For GitHub-Hosted Repos) + +For repos on GitHub, use issues with labels as the message bus. + +**Protocol:** +1. Create issue in target repo with label `squad:cross-squad` +2. Include source squad identifier and routing hint in issue body +3. Target squad's Ralph picks up and routes to appropriate agent +4. Response posted as issue comment +5. Issue closed when complete + +**Example:** +```bash +gh issue create \ + --repo your-org/platform-squad-repo \ + --title "[Cross-Squad] Architecture query from research-squad" \ + --body "Source: research-squad\nQuery: What services does the platform expose?\nRouting: lead" \ + --label "squad:cross-squad" +``` + +**Limitation:** Only works for repos on GitHub. Other platforms (Azure DevOps, GitLab, etc.) need different approach. + +--- + +### Appendix: Cross-Repo Dependency Scan (Related Analysis Tool — Not a Communication Pattern) + +> This section is intentionally listed as an appendix rather than "Pattern 4" — it is a one-off analysis utility for discovering how two repos relate, not a protocol the coordinator picks from the decision tree above. The four numbered communication patterns are 0–3. + +For discovering how two repos relate to each other. + +**Protocol:** +1. Search both repos for mutual references: + ```powershell + Select-String -Path (Get-ChildItem $repoA -Recurse -Include "*.md","*.cs","*.json","*.csproj") ` + -Pattern $repoB_name + Select-String -Path (Get-ChildItem $repoB -Recurse -Include "*.md","*.cs","*.json","*.csproj") ` + -Pattern $repoA_name + ``` +2. Check shared NuGet packages / npm packages +3. Check shared ADO project or GitHub org +4. Document relationship type: code dependency, operational coupling, shared infra + +--- + +## Discovery Protocol + +Before sending any cross-squad request, verify the target: + +``` +1. Does .squad/team.md exist? → Squad is installed +2. What is the issue_source? → GitHub Issues | ADO | Planner +3. What agents are active? → Check member status column +4. What is the routing table? → Read routing.md +5. What is the current focus? → Read identity/now.md +6. Is Ralph running? → Check for recent commits by Ralph +``` + +If `.squad/team.md` doesn't exist, the repo is not squad-enabled. Fall back to standard human communication. + +--- + +## Platform Compatibility Matrix + +| Source Issue Tracker | Target Issue Tracker | Mechanism | +|---------------------|---------------------|-----------| +| GitHub Issues | GitHub Issues | Issue-based (Pattern 3) | +| GitHub Issues | ADO Work Items | Git-based (Pattern 2) | +| GitHub Issues | Planner | Git-based (Pattern 2) | +| ADO Work Items | GitHub Issues | Issue-based (Pattern 3) via `gh` CLI | +| ADO Work Items | ADO Work Items | ADO cross-project work items | +| Any | Any | Git-based (Pattern 2) — universal fallback | + +--- + +## Examples + +### Example 1: research-squad queries platform-squad architecture + +```powershell +# Step 1: Read metadata (Pattern 1) +$target = "C:\repos\platform-squad-repo" +$team = Get-Content "$target\.squad\team.md" -Raw +$decisions = Get-Content "$target\.squad\decisions.md" -Raw + +# Step 2: Extract answer from metadata +# team.md reveals tech stack and member roles +# decisions.md reveals architectural choices + +# Step 3: If deeper analysis needed, create async request (Pattern 2) +``` + +### Example 2: Request PR review from another squad + +```yaml +# .squad/cross-squad/requests/2026-06-13-platform-squad-pr-review.yaml +id: pr-review-001 +source_squad: research-squad +target_squad: platform-squad +request_type: pr_review +priority: normal +query: "Review PR #54 — package version fix. Check for correctness." +routing_hint: "lead" +status: pending +``` + +--- + +## Anti-Patterns + +### ⚠️ Know when synchronous CLI is NOT the right choice +```powershell +# WRONG — don't use sync CLI for long-running tasks that need artifacts +copilot -C $targetRepo --agent squad -p (Get-Content $promptFile -Raw) --allow-all-tools +# If the task creates files, PRs, or takes multiple cycles → use async (Pattern 2 or 3) + +# WRONG — don't use sync CLI when the target repo isn't cloned locally +copilot -C "C:\not\cloned\yet" --agent squad --allow-all-tools +# If the repo isn't available locally → use issue-based delegation (Pattern 3) +``` +Synchronous CLI sessions (Pattern 0) are valid for quick queries and knowledge lookups. Use async patterns for work that needs to persist or where the target repo isn't available locally. + +### ❌ Don't assume shared MCP tools +Each squad has its own MCP server instances. You cannot invoke another squad's ADO tools or GitHub tools from your session. + +### ❌ Don't skip the discovery step +Always read `team.md` first. The target squad may use a different issue tracker, have different agents, or be in a different state than expected. + +### ❌ Don't send requests to squads without Ralph +If the target squad doesn't have Ralph (Work Monitor) running, async requests will never be processed. Check for recent Ralph activity first. + +### ❌ Don't mix up repo platforms +Different repos may use GitHub Issues vs Azure DevOps Work Items vs Jira. Check `team.md` / repository metadata for the right tooling before sending requests. + +--- + +## Validation Status + +This skill was originally drafted against two prototype squad setups (a GitHub-hosted platform squad with ~10 agents and an Azure DevOps-hosted automation squad with ~4 agents). The protocols are platform-agnostic; the examples in this document use generic names so you can substitute your own repos. Patterns 0 and 1 have been exercised end-to-end in those prototypes; Patterns 2 and 3 are documented from design but have not been end-to-end-validated against a live target repo. + +| Scenario | Result | +|----------|--------| +| Knowledge query (read-only) | ✅ Works via Pattern 1 | +| Step handler discovery | ✅ Works via file scan | +| PR review (basic) | ⚠️ Partial — git log only, no API | +| Backlog enumeration | ⚠️ Partial — depends on issue platform | +| Dependency analysis | ✅ Works via cross-reference scan | +| CLI invocation (sync) + Liveness Protocol | ✅ Works — session launches successfully; log monitoring prevents false timeouts | + +**Confidence: MEDIUM** — Synchronous CLI pattern (Pattern 0) validated end-to-end. Liveness protocol provides operational robustness against slow MCP initialization. Git-based async (Pattern 2) and issue-based (Pattern 3) untested end-to-end. Production readiness requires Ralph integration on both sides. diff --git a/.squad/skills/cross-squad/SKILL.md b/.squad/skills/cross-squad/SKILL.md index 1d4e3a251..d008e85b8 100644 --- a/.squad/skills/cross-squad/SKILL.md +++ b/.squad/skills/cross-squad/SKILL.md @@ -1,9 +1,20 @@ --- name: "cross-squad" -description: "Coordinating work across multiple Squad instances" +description: "Coordinating work across multiple Squad instances — discovery, delegation, and disambiguation when the user says 'squad' (the product) vs casual English 'group of agents'." domain: "orchestration" confidence: "medium" source: "manual" +triggers: + - "spawn N squads" + - "spawn a squad" + - "another squad" + - "two squads of" + - "second squad" + - "fan out to squads" + - "delegate to a squad" + - "set up squads for" + - "create a squad to review" + - "ask the other squad" tools: - name: "squad-discover" description: "List known squads and their capabilities" @@ -14,21 +25,60 @@ tools: --- ## Context + +> **Read this FIRST any time the user says "squad" as a thing to spawn, delegate to, address, or fan out to** — e.g., *"spawn two squads of designers and devs"*, *"ask the other squad"*, *"delegate to a squad"*. In Squad-PRODUCT vocabulary, "squad" is a **peer** (an independent installation with its own `.squad/`, `team.md`, MCP server, and agents) — NOT a generic English synonym for "team" or "group". Do not fan out raw `task` agents inside your own coordinator context when the user means "another squad". Use the discovery and communication patterns below (and the companion `cross-squad-communication` skill for the actual protocols). + When an organization runs multiple Squad instances (e.g., platform-squad, frontend-squad, data-squad), those squads need to discover each other, share context, and hand off work across repository boundaries. This skill teaches agents how to coordinate across squads without creating tight coupling. +> **Companion skill — for protocol details:** `cross-squad-communication/SKILL.md` covers the four communication patterns (synchronous CLI, read-only knowledge query, git-based async, and GitHub-issue-based delegation) once a peer squad is discovered via the registry below. This skill answers "who?" — the companion answers "how?". + Cross-squad orchestration applies when: - A task requires capabilities owned by another squad - An architectural decision affects multiple squads - A feature spans multiple repositories with different squads - A squad needs to request infrastructure, tooling, or support from another squad +## Disambiguation: "squad" vs ad-hoc agents + +When the user uses the word **"squad" / "squads"** or asks to **"spawn a team"**, the coordinator MUST treat it as a literal reference to a Squad install (a `.squad/` directory with its own roster, casting, and coordinator) — NOT as a casual synonym for "a group of sub-agents". + +### Default behaviour (apply unless explicitly told otherwise) + +| User says | Coordinator does | +|---|---| +| *"spawn two squads of X and Y"* / *"set up squads for X, Y, Z"* | Bootstrap N **real** Squad installs — separate folder + `git init` + `squad init` per squad — then use the cross-squad patterns below (`.squad/manifest.json`, `squad registry add`, `squad delegate`) and the protocols in the `cross-squad-communication` skill | +| *"ask the other squad about X"* / *"delegate to the data squad"* | Discover the peer via `squad registry list` (or by reading a known `.squad/manifest.json`), then use `cross-squad-communication` Pattern 0 / 1 / 2 / 3 — never re-implement the protocol with `task` | +| *"spawn a few agents to do X"* / *"have some agents review X"* / *"in parallel, get sub-agents to..."* | Ad-hoc `task` fan-out is fine — no `.squad/` bootstrap needed. This is the only path where raw `task` is appropriate when the user mentioned a multi-agent activity | + +### Ambiguous? `ask_user`, never silently downgrade + +If the request **could** be either interpretation AND bootstrapping real squads is non-trivial (more than one or two `squad init` runs), you MUST use the `ask_user` tool with a 2-choice prompt before proceeding: + +``` +question: "Should I create separate Squad installs or just dispatch ad-hoc agents?" +choices: + - "Real squads — separate .squad/ per squad (heavier, persistent, can be re-engaged later)" + - "Ad-hoc agents — one-shot `task` dispatch (lighter, ephemeral, no .squad/ created)" +``` + +The cost of asking is one `ask_user`. The cost of getting it wrong is the user has to redo the work. **Never silently pick the cheaper option just because it feels disproportionate for the task size — surface the trade-off and let the user pick.** + +### Anti-patterns (every one of these is a real failure mode observed in production) + +- **Calling `task` sub-agents "squad-alpha" / "squad-beta"** and treating them as squads. Naming something a squad doesn't make it one — a squad has its own `.squad/`, `team.md`, MCP server, and coordinator. If those aren't there, it's not a squad. +- **Matching a prior session's ad-hoc pattern without re-checking current intent.** If you see existing `reviews/squad-alpha/` folders from a previous run, that's a hint, NOT a contract — the user may have meant something different this time. Re-evaluate from scratch. +- **Silently choosing the cheaper interpretation because "bootstrapping two real squads for a 30-line app feels disproportionate".** That's a judgment call for the USER to make, not the coordinator. Use `ask_user`. +- **Loading the `cross-squad` skill, reading it, then doing `task` fan-out anyway** because the eager-execution / parallel-fan-out doctrine pulled you back. The disambiguation rule on this page OVERRIDES the generic fan-out doctrine when "squad" was the trigger. + ## Patterns ### Discovery via Manifest -Each squad publishes a `.squad/manifest.json` declaring its name, capabilities, and contact information. Squads discover each other through: -1. **Well-known paths**: Check `.squad/manifest.json` in known org repos -2. **Upstream config**: Squads already listed in `.squad/upstream.json` are checked for manifests -3. **Explicit registry**: A central `squad-registry.json` can list all squads in an org +Each squad publishes a `.squad/manifest.json` declaring its name, capabilities, and contact information. Squads discover each other through two mechanisms: + +1. **`.squad/squad-registry.json`** — **discovery-only.** Peer squads are findable via `squad discover` and addressable via `squad delegate`, but their skills/decisions/wisdom are NOT loaded into your coordinator. Manage with `squad registry add/list/remove`. +2. **`.squad/upstream.json`** — **discovery + inheritance.** Squads listed here are also discoverable, AND your coordinator inherits their skills/decisions/wisdom/routing at session start. Manage with `squad upstream add/list/remove/sync`. + +Both forms read the peer's manifest via the same code path. The `path` field is the **repository root** (e.g. `../friend-repo`), and Squad appends `.squad/manifest.json` internally. Pointing at the `.squad/` directory works too — Squad accepts both forms (`readManifest` strips a trailing `.squad` if present). ```json { @@ -73,9 +123,19 @@ Track delegated work completion: ## Examples +### Registering a peer squad (no inheritance) +```bash +# Friend's repo is checked out at ../friend-platform/ +squad registry add platform-squad ../friend-platform + +# Verify +squad registry list +squad discover +``` + ### Discovering squads ```bash -# List all squads discoverable from upstreams and known repos +# List all squads discoverable from registry + upstreams squad discover # Output: diff --git a/.squad/skills/iterative-retrieval/SKILL.md b/.squad/skills/iterative-retrieval/SKILL.md new file mode 100644 index 000000000..4d8eea993 --- /dev/null +++ b/.squad/skills/iterative-retrieval/SKILL.md @@ -0,0 +1,165 @@ +--- +name: "iterative-retrieval" +description: "Max-3-cycle protocol for agent sub-tasks with WHY context and coordinator validation. Use when spawning sub-agents to complete scoped work." +domain: "agent-coordination" +confidence: "high" +license: MIT +--- + +# Iterative Retrieval Skill + +Squad agents frequently spawn sub-agents to complete scoped work. Without structure, these +handoffs become vague, cycles multiply, and outputs land without being checked. The +**Iterative Retrieval Pattern** caps cycles at 3, mandates WHY context in every spawn, and +requires the coordinator to validate agent output before closing an issue. + +--- + +## Spawn Prompt Template + +Every agent spawn must include the following four sections. Copy and fill in the template: + +``` +## Task +{What you need done — concrete and bounded} + +## WHY this matters +{The motivation and context. What system or user goal does this serve? What breaks if skipped?} + +## Success criteria +{How you will know the output is correct. Be explicit — list acceptance criteria, not vibes.} +Example: +- [ ] File X exists and contains Y +- [ ] No regressions in existing tests +- [ ] PR is open targeting main with description matching the issue + +## Escalation path +{What the agent should do if uncertain or stuck. "Stop and ask me" is valid.} +Example: +- If requirements are ambiguous → stop, comment on the issue, set label status:needs-decision +- If blocked by a dependency → label status:blocked, explain in a comment +- If 3 cycles exhausted without resolution → write a summary to inbox and surface to coordinator +``` + +--- + +## 3-Cycle Protocol + +| Cycle | Description | Exit condition | +|-------|-------------|----------------| +| **1** | Initial attempt | Done → coordinator validates. Incomplete → surface delta. | +| **2** | Targeted retry with specific corrections | Done → coordinator validates. Incomplete → one more. | +| **3** | Final attempt with all context from cycles 1–2 | Done or escalate — no cycle 4. | + +### Rules + +1. **After each cycle**, the coordinator evaluates the output against the success criteria + before accepting it or spawning the next cycle. +2. **Objective context forward**: each subsequent spawn includes a summary of what was tried + and what is still missing — not just a repeat of the original task. +3. **Cycle 3 exhausted** → escalate: write a summary to `.squad/decisions/inbox/`, label the + issue `status:needs-decision`, and notify the user. + +--- + +## Coordinator Validation Checklist + +Before accepting agent output and closing an issue, the coordinator must check: + +- [ ] All success criteria from the spawn prompt are met +- [ ] PR exists and description matches the issue (if code work) +- [ ] No obvious regressions (grep for TODO/FIXME introduced, build passes) +- [ ] Agent did not silently skip parts of the task +- [ ] If the agent reported uncertainty — was it resolved or escalated? + +If any item fails → do **not** accept. Spawn cycle N+1 (up to cycle 3) with specific deltas. + +--- + +## When to Escalate vs Retry + +**Retry (cycle N+1)** when: +- Output is structurally correct but missing specific items +- Agent misunderstood scope (provide more context and re-run) +- Partial success — clearly identified remaining delta + +**Escalate** when: +- Requirements are fundamentally unclear (decision needed) +- 3 cycles complete without convergence +- Agent returned conflicting results across cycles +- Task requires elevated permissions or external action +- The work depends on another issue that isn't done yet + +--- + +## Issue Dedup Check (Mandatory) + +Before any agent creates a GitHub issue, it **must** search for existing open issues to avoid +duplicates. + +```bash +# Check for existing open issues before creating a new one +gh issue list --search "" --state open +``` + +- If an open issue already covers the same problem → **comment on it** instead of creating a new one. +- If no duplicate → proceed to create the issue. +- Use 2–3 representative keywords from the planned issue title as the search query. + +--- + +## Mandatory Output Requirement (Research-Then-Execute) + +Every research or analysis task completed under this protocol **MUST** end with at least one +concrete action before the cycle is closed. Acceptable follow-up actions: + +- GitHub issue created documenting the findings and next steps +- PR opened implementing a recommendation +- Decision recorded in `.squad/decisions/inbox/` +- Documented recommendation with a named assignee and due date + +**Pure analysis reports without actionable follow-up will be rejected during triage.** +If no action is warranted, the agent must explicitly state why and get coordinator sign-off. + +--- + +## Anti-Patterns + +- **Spawning without WHY** — agents can't prioritise trade-offs without motivation context. +- **Accepting output without validating** — one failed check avoids merging broken work. +- **Cycle 4+** — if 3 cycles haven't converged, the problem is in the requirements, not the agent. +- **Vague success criteria** — "looks good" is not a criterion. Use checkboxes. +- **Forwarding WHAT without delta** — cycle 2+ prompts must include what cycle 1 got wrong. +- **Creating issues without dedup check** — always search before creating. +- **Research without action** — delivering analysis with no issue, PR, decision, or assignee is incomplete work. + +--- + +## Examples + +### Good spawn prompt +``` +## Task +Add an "Iterative Retrieval Protocol" section to `.squad/agents/coordinator/charter.md` explaining +the 3-cycle rule, WHY format, and validation checklist. + +## WHY this matters +The coordinator spawns sub-agents on every round. Without a documented protocol, agents run unbounded +cycles and outputs go unvalidated — leading to stale issues and silent failures. + +## Success criteria +- [ ] Section "Iterative Retrieval Protocol" exists in charter.md +- [ ] Section documents max-3-cycles rule +- [ ] Section documents WHY format requirement +- [ ] Section contains validation checklist (at least 4 items) +- [ ] No other sections of charter.md are modified + +## Escalation path +If the charter.md format is unclear, check another agent charter as a reference. +If uncertain about content, stop and surface to coordinator. +``` + +### Bad spawn prompt (don't do this) +``` +Update the coordinator charter with the iterative retrieval stuff. +``` diff --git a/.squad/skills/personal-squad/SKILL.md b/.squad/skills/personal-squad/SKILL.md index b300600d3..958031e39 100644 --- a/.squad/skills/personal-squad/SKILL.md +++ b/.squad/skills/personal-squad/SKILL.md @@ -15,8 +15,9 @@ A personal squad is a user-level collection of AI agents that travel with you ac ## Directory Structure ``` -~/.config/squad/personal-squad/ # Linux/macOS -%APPDATA%/squad/personal-squad/ # Windows +~/Library/Application Support/squad/personal-squad/ # macOS +~/.config/squad/personal-squad/ # Linux +%APPDATA%/squad/personal-squad/ # Windows ├── agents/ │ ├── {agent-name}/ │ │ ├── charter.md diff --git a/.squad/skills/reflect/SKILL.md b/.squad/skills/reflect/SKILL.md new file mode 100644 index 000000000..6a85b5190 --- /dev/null +++ b/.squad/skills/reflect/SKILL.md @@ -0,0 +1,229 @@ +--- +name: reflect +description: Learning capture system that extracts HIGH/MED/LOW confidence patterns from conversations to prevent repeating mistakes. Use after user corrections ("no", "wrong"), praise ("perfect", "exactly"), or when discovering edge cases. Complements .squad/agents/{agent}/history.md and .squad/decisions.md. +license: MIT +version: 1.0.0-squad +domain: team-memory, learning +confidence: high +--- + +# Reflect Skill + +**Critical learning capture system** for Squad. Prevents repeating mistakes and preserves successful patterns across sessions. + +Analyze conversations and propose improvements to squad knowledge based on what worked, what didn't, and edge cases discovered. **Every correction is a learning opportunity.** + +--- + +## Integration with Squad Architecture + +**Reflect complements existing Squad knowledge systems:** + +1. **`.squad/agents/{agent}/history.md`** — Permanent learnings from completed work (append-only; each agent updates their own file; Scribe propagates cross-agent updates) +2. **`.squad/decisions.md`** — Team-wide decisions that all agents respect +3. **`reflect` skill** — Captures in-flight learnings from conversations that may graduate to history.md or decisions.md + +**Workflow:** +- Use `reflect` during work to capture learnings +- At session end, review captured learnings +- Promote HIGH confidence patterns → lead agent for decision.md review +- Promote agent-specific patterns → `{agent}/history.md` updates + +--- + +## Triggers + +### 🔴 HIGH Priority (Invoke Immediately) + +| Trigger | Example | Why Critical | +|---------|---------|--------------| +| User correction | "no", "wrong", "not like that", "never do" | Captures mistakes to prevent repetition | +| Architectural insight | "you removed that without understanding why" | Documents design decisions (Chesterton's Fence) | +| Immediate fixes | "debug", "root cause", "fix all" | Learns from errors in real-time | + +### 🟡 MEDIUM Priority (Invoke After Multiple) + +| Trigger | Example | Why Important | +|---------|---------|---------------| +| User praise | "perfect", "exactly", "great" | Reinforces successful patterns | +| Tool preferences | "use X instead of Y", "prefer" | Builds workflow preferences | +| Edge cases | "what if X happens?", "don't forget", "ensure" | Captures scenarios to handle | + +### 🟢 LOW Priority (Invoke at Session End) + +| Trigger | Example | Why Useful | +|---------|---------|------------| +| Repeated patterns | Frequent use of specific commands/tools | Identifies workflow preferences | +| Session end | After complex work | Consolidates all session learnings | + +--- + +## Process + +### Phase 1: Identify Learning Target + +Determine what knowledge system should be updated: + +1. **Agent-specific learning** → `.squad/agents/{agent}/history.md` +2. **Team-wide decision** → `.squad/decisions/inbox/{agent}-{topic}.md` +3. **Skill-specific improvement** → Document in session, recommend to skill owner + +### Phase 2: Analyze Conversation + +Scan for learning signals with confidence levels: + +#### HIGH Confidence: Corrections + +User actively steered or corrected output. + +**Detection patterns:** +- Explicit rejection: "no", "not like that", "that's wrong" +- Strong directives: "never do", "always do", "don't ever" +- User provided alternative implementation + +**Example:** +```text +User: "No, use the azure-devops MCP tool instead of raw API calls" +→ [HIGH] + Add constraint: "Prefer azure-devops MCP tools over REST API" +``` + +#### MEDIUM Confidence: Success Patterns + +Output was accepted or praised. + +**Detection patterns:** +- Explicit praise: "perfect", "great", "yes", "exactly" +- User built on output without modification +- Output was committed without changes + +**Example:** +```text +User: "Perfect, that's exactly what I needed" +→ [MED] + Add preference: "Include usage examples in documentation" +``` + +#### MEDIUM Confidence: Edge Cases + +Scenarios not anticipated. + +**Detection patterns:** +- Questions not answered +- Workarounds user had to apply +- Error handling gaps discovered + +#### LOW Confidence: Preferences + +Accumulated patterns over time. + +--- + +### Phase 3: Propose Learnings + +Present findings: + +```text +┌─────────────────────────────────────────────────────────────┐ +│ REFLECTION: {target (agent/decision/skill)} │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ [HIGH] + Add constraint: "{specific constraint}" │ +│ Source: "{quoted user correction}" │ +│ Target: .squad/decisions/inbox/{agent}-{topic}.md │ +│ │ +│ [MED] + Add preference: "{specific preference}" │ +│ Source: "{evidence from conversation}" │ +│ Target: .squad/agents/{agent}/history.md │ +│ │ +│ [LOW] ~ Note for review: "{observation}" │ +│ Source: "{pattern observed}" │ +│ Target: Session notes only │ +│ │ +├─────────────────────────────────────────────────────────────┤ +│ Apply changes? [Y/n/edit] │ +└─────────────────────────────────────────────────────────────┘ +``` + +**Confidence Threshold:** + +| Threshold | Action | +|-----------|--------| +| ≥1 HIGH signal | Always propose (user explicitly corrected) | +| ≥2 MED signals | Propose (sufficient pattern) | +| ≥3 LOW signals | Propose (accumulated evidence) | +| 1-2 LOW only | Skip (insufficient evidence) | + +### Phase 4: Persist Learnings + +**ALWAYS show changes before applying.** + +After user approval: + +1. **For Agent History:** + - Append to `.squad/agents/{agent}/history.md` under `## Learnings` section + - Format: Date, assignment context, key learning + +2. **For Team Decisions:** + - Create `.squad/decisions/inbox/{agent}-{topic}.md` + - Lead agent reviews and merges to `decisions.md` if appropriate + +3. **For Skills:** + - Document recommendation in session notes + - Squad lead reviews and routes to skill owner + +--- + +## Usage Examples + +### Example 1: User Correction + +**Conversation:** +``` +Agent: "I'll use grep to search the repository" +User: "No, use the code search tools first, grep is too slow" +``` + +**Reflection Output:** +``` +[HIGH] + Add constraint: "Use code intelligence tools before grep" + Source: "No, use the code search tools first, grep is too slow" + Target: .squad/agents/{agent}/history.md +``` + +### Example 2: Success Pattern + +**Conversation:** +``` +Agent: [Creates PR with detailed description and test plan] +User: "Perfect! This is exactly the format I want for all PRs" +``` + +**Reflection Output:** +``` +[MED] + Add preference: "Include test plan in PR descriptions" + Source: User praised detailed PR format + Target: .squad/decisions/inbox/pr-format.md (for team adoption) +``` + +--- + +## When to Use + +✅ **Use reflect when:** +- User says "no", "wrong", "not like that" (HIGH priority) +- User says "perfect", "exactly", "great" (MED priority) +- You discover edge cases or gaps +- Complex work session with multiple learnings +- At end of sprint/milestone to consolidate patterns + +❌ **Don't use reflect when:** +- Simple one-off questions with no pattern +- User is just exploring ideas (no concrete decisions) +- Learning is already captured in history.md/decisions.md + +--- + +## See Also + +- `.squad/decisions.md` — Team-wide decisions +- `.squad/agents/*/history.md` — Agent-specific learnings +- `.squad/routing.md` — Work assignment patterns diff --git a/.squad/skills/release-process/SKILL.md b/.squad/skills/release-process/SKILL.md index c80603986..18e0e84b1 100644 --- a/.squad/skills/release-process/SKILL.md +++ b/.squad/skills/release-process/SKILL.md @@ -9,7 +9,7 @@ source: "earned" # Release Process > Earned knowledge from the v0.9.0→v0.9.1 and v0.9.4 incidents. Every agent involved in releases MUST read this before starting release work. -> See also: `.copilot/skills/release-process/SKILL.md` for the Copilot-facing runbook. +> See also: `.github/skills/release-process/SKILL.md` for the Copilot-facing runbook. ## SCOPE @@ -213,5 +213,5 @@ See `.github/workflows/squad-ci.yml` → `publish-policy` job for implementation - v0.9.4 fixes: PR #1042 (CHANGELOG), PR #1043 (root package.json), PR #1044 (lockfile integrity) - Retro: `.squad/decisions/inbox/surgeon-v091-retrospective.md` - CI audit: `.squad/decisions/inbox/booster-ci-audit.md` -- Copilot-level skill: `.copilot/skills/release-process/SKILL.md` +- Copilot-level skill: `.github/skills/release-process/SKILL.md` - Playbook: `PUBLISH-README.md` (repo root) diff --git a/packages/squad-cli/templates/skills/squad-commands/SKILL.md b/.squad/skills/squad-commands/SKILL.md similarity index 100% rename from packages/squad-cli/templates/skills/squad-commands/SKILL.md rename to .squad/skills/squad-commands/SKILL.md diff --git a/.squad/skills/squad-help/SKILL.md b/.squad/skills/squad-help/SKILL.md new file mode 100644 index 000000000..81bc4ae2a --- /dev/null +++ b/.squad/skills/squad-help/SKILL.md @@ -0,0 +1,97 @@ +--- +name: "squad-help" +description: "How to actually use Squad — Squad is a custom Copilot agent (invoked via the task tool with agent_type='Squad'), not a skill. This file explains the right invocation paths for setting up a team, listing squad commands, and initializing Squad in a new project." +allowedTools: [] +confidence: high +domain: squad-onboarding +--- + +# Skill: squad-help + +> **Quick reference.** If you're reading this because a user said "use squad" or "squad" or "set up a squad", you're in the right place — read on for the correct invocation paths. + +--- + +## Squad is a custom agent, not a skill + +The Squad framework registers a **custom Copilot CLI agent** at `.github/agents/squad.agent.md`. The agent is named **`Squad`** and its description is *"Your AI team. Describe what you're building, get a team of specialists that live in your repo."* + +Copilot CLI agents and skills are different things: + +| Thing | How to invoke | Example | +|---|---|---| +| **Skill** | `skill(name)` tool call or natural-language match | `skill(squad-commands)` | +| **Agent** | `task` tool with `agent_type=` | `task(name="...", agent_type="Squad", prompt="...")` | +| **Slash command** | Built-in CLI keyword | `/agent`, `/skills`, `/mcp` | + +Calling `skill(Squad)` will fail with *"Skill not found: Squad"* because Squad is the agent, not a skill. (`/squad` as a slash command also does not exist — only built-in CLI keywords like `/agent`, `/skills`, `/mcp` are slash commands. There's no way to map a skill name to a slash command without a Copilot CLI feature change.) + +--- + +## How to actually use Squad + +Pick the path that matches the user's intent: + +### A) Invoke the Squad coordinator agent (most common) + +The Squad coordinator orchestrates a team of specialists. It routes work to the right agent, scaffolds a team if none exists, and enforces handoffs. + +```text +task( + name="", + agent_type="Squad", + prompt="" +) +``` + +Use this when the user says things like: +- *"Use Squad to build X"* +- *"Set up an AI team for this project"* +- *"Have the Squad coordinator design Y"* +- *"Spawn Squad"* / *"Squad, help me with ..."* + +### B) See what Squad commands exist + +The `squad-commands` skill is a categorized catalog of common Squad operations. The coordinator presents it as an interactive menu. + +Trigger by natural-language match: `"squad commands"`, `"what can squad do"`, `"show me squad options"`, `"slash commands"`, `"what commands are available"`. + +Use this when the user says things like: +- *"What can Squad do?"* +- *"Show me the squad commands"* +- *"squad help"* + +### C) Initialize Squad in a fresh project + +`squad init` is a **shell command**, not a tool call. The user runs it in their terminal in a project that has no `.squad/` directory yet. + +```bash +squad init +``` + +Do **not** try to invoke this from inside an existing Copilot session — `.squad/` is already initialized if you're reading this file. + +--- + +## What NOT to do + +- ❌ Do not call `skill(Squad)`, `skill(squad)`, or `skill(squad-coordinator)` — Squad is not a skill. +- ❌ Do not type `/squad` expecting a slash command — slash commands are CLI keywords, not skill names. Use `/agent` (browse) or invoke the `Squad` agent via the `task` tool. +- ❌ Do not call `task(agent_type="Squad", …)` for tiny tasks the current agent can handle directly. Squad is for work that needs orchestration; trivial edits do not. + +--- + +## How this skill was discovered + +This skill ships from the Squad SDK templates and is wired into `MANIFEST_SKILL_NAMES`. It lives at `.copilot/skills/squad-help/SKILL.md` so the Copilot CLI's `/skills` loader picks it up alongside the other bundled Squad skills. + +If you removed this skill on purpose, the model will fall back to its own reasoning and may make the lookup mistakes described above. + +--- + +## See also + +- `.github/agents/squad.agent.md` — the actual Squad coordinator agent +- `.copilot/skills/squad-commands/SKILL.md` — the command catalog +- `.copilot/skills/squad-conventions/SKILL.md` — conventions for working on the Squad codebase itself +- `.copilot/skills/squad-version-check/SKILL.md` — version-stamping mechanics diff --git a/.squad/skills/squad-version-check/SKILL.md b/.squad/skills/squad-version-check/SKILL.md new file mode 100644 index 000000000..3f3aebb0a --- /dev/null +++ b/.squad/skills/squad-version-check/SKILL.md @@ -0,0 +1,169 @@ +--- +name: "squad-version-check" +description: "Internals of how @bradygaster/squad-cli stamps its version, how `squad upgrade` works (what it preserves vs overwrites), and how to probe the npm registry for the latest version from a coordinator prompt." +allowedTools: [] +confidence: medium +domain: squad-internals +source: "Discovered by Data; validated in bradygaster/squad#1173 recon (2026-05-26)." +--- + +# SKILL: Squad CLI Internals — Version Stamping & Upgrade Mechanics + +**Confidence:** medium +**Discovered by:** Data +**Date:** 2026-05-26 +**Validated in:** Issue #1173 recon (bradygaster/squad) + +--- + +## What This Skill Covers + +Reusable knowledge about how `@bradygaster/squad-cli` stamps its version into `squad.agent.md`, how `squad upgrade` works, what it preserves vs. overwrites, and how to probe the npm registry for the latest version from a coordinator prompt. + +--- + +## Package & Registry Facts + +- **Package name:** `@bradygaster/squad-cli` +- **Registry:** npm (public) +- **CLI binary:** `squad` (registered via `package.json#bin.squad`) +- **Node version requirement:** Node ≥22.5.0 (ESM-only codebase) + +--- + +## Version Stamping Mechanism + +**Source file:** `dist/cli/core/version.js` + +Three functions: + +### `getPackageVersion()` +Walks up from the compiled JS file to find `package.json`. Returns `pkg.version`. Works from both `dist/cli/core/version.js` and a bundled root `cli.js`. Returns `'0.0.0'` as fallback if not found. + +### `stampVersion(filePath, version)` +Mutates `squad.agent.md` in three places: +1. HTML comment: `` (must be on the line immediately after frontmatter `---`) +2. Identity line: `- **Version:** {version}` +3. Greeting instruction: backtick-quoted `` `Squad v{version}` `` + +**Called by:** both `init` and `upgrade` — after copying the template to the destination. + +### `readInstalledVersion(filePath)` +Reads the stamped version back from `squad.agent.md`: +1. First tries HTML comment format: `//` +2. Falls back to old frontmatter format: `/^version:\s*"([^"]+)"/m` +3. Returns `'0.0.0'` on any error + +--- + +## `squad upgrade` Behavior + +**Source file:** `dist/cli/core/upgrade.js` + +### What gets overwritten: +- `squad.agent.md` — full overwrite from template, then `stampVersion()` +- Files with `overwriteOnUpgrade: true` in `TEMPLATE_MANIFEST`: casting JSON files, template .md files, `copilot-instructions.md` (if @copilot enabled) +- GitHub Actions workflows — from `templates/workflows/`; non-npm projects get type-aware stubs +- Runs `runMigrations()` after file copy + +### What is PRESERVED: +- `team.md`, `routing.md`, `decisions.md`, `ceremonies.md` (user-owned) +- `agents/*/history.md` (individual agent memory) +- `.squad/config.json` — **never touched**; `stateBackend` survives intact +- User-added files not in TEMPLATE_MANIFEST + +### Self-upgrade path (`selfUpgradeCli()`): +Detects npm/pnpm/yarn via `npm_execpath` and `npm_config_user_agent`. Runs: +- npm: `npm install -g @bradygaster/squad-cli@latest` +- pnpm: `pnpm add -g @bradygaster/squad-cli@latest` +- yarn: `yarn global add @bradygaster/squad-cli@latest` +Use `@insider` tag for insider builds. + +### `compareSemver(a, b)` utility (in upgrade.js): +Returns -1/0/1. Handles pre-release: strips pre-release for base comparison, then treats pre-release as less than release (e.g., `0.9.5-insider.1` < `0.9.5`). Can be ported directly if needed in prompt logic. + +--- + +## `.squad/config.json` — What It Holds + +```json +{ + "version": 1, + "stateBackend": "worktree" +} +``` + +Other optional fields added by the coordinator at runtime: +- `defaultModel` — global model override for all agent spawns +- `agentModelOverrides.{agentName}` — per-agent model override + +The file is read-only from the upgrade path's perspective. Only the coordinator writes to it (for model preferences). + +--- + +## Version-Check Probe (npm Registry) + +Use this one-liner from inside a coordinator prompt to fetch dist-tags: + +``` +npm view @bradygaster/squad-cli dist-tags --json +``` + +- Timeout: **5 seconds.** If no response within 5 seconds, abandon and show normal greeting. +- On success: extract `dist-tags[channel]` (e.g., `dist-tags["insider"]`). +- On any error (network failure, registry unreachable, parse error): show normal greeting. + +--- + +## Upstream OS-Specific Cache + +The CLI (`self-update.ts`) writes `latest` version info to an OS-specific path with a 24h TTL. + +**One-liner to read the upstream cache:** +``` +node -e "const p=require('path'),o=require('os');const b=process.env.APPDATA||(process.platform==='darwin'?p.join(o.homedir(),'Library','Application Support'):p.join(o.homedir(),'.config'));const f=p.join(b,'squad-cli','update-check.json');try{const d=JSON.parse(require('fs').readFileSync(f,'utf8'));const age=Date.now()-d.checkedAt;if(age<86400000)console.log(JSON.stringify(d));else console.log('STALE')}catch{console.log('MISS')}" +``` + +Output semantics: +- Valid JSON `{"latestVersion":"X.Y.Z","checkedAt":N}` → cache hit; use `latestVersion` +- `STALE` → cache expired (older than 24h); treat as no data +- `MISS` → cache missing or corrupt; treat as no data + +**OS-specific cache path:** +- Windows: `%APPDATA%\squad-cli\update-check.json` +- Linux: `~/.config/squad-cli/update-check.json` +- macOS: `~/Library/Application Support/squad-cli/update-check.json` + +--- + +## Repo-Local Cache Convention: `.squad/.cache/version-check.json` + +Used by coordinator for `insider`/`preview` channels (the upstream cache only stores `latest`). + +**Schema:** +```json +{ + "checkedAt": "2026-05-26T14:13:28.492Z", + "currentVersion": "0.9.6-insider.2", + "channel": "insider", + "channelVersion": "0.9.7-insider.1" +} +``` + +**TTL:** 24 hours from `checkedAt`. +**Gitignore:** `.squad/.cache/` is listed in `.gitignore` — cache files are never committed. + +--- + +## Key File Paths (installed CLI) + +| Purpose | Path | +|---|---| +| Version utilities | `dist/cli/core/version.js` | +| Upgrade logic | `dist/cli/core/upgrade.js` | +| Init logic | `dist/cli/core/init.js` | +| Template manifest | `dist/cli/core/templates.js` | +| Copilot install helper | `dist/cli/copilot-install.js` | +| squad.agent.md template | `templates/squad.agent.md.template` | +| Session init reference | `templates/session-init-reference.md` | +| All templates | `templates/` | diff --git a/.squad/skills/squad/SKILL.md b/.squad/skills/squad/SKILL.md new file mode 100644 index 000000000..e67cd9710 --- /dev/null +++ b/.squad/skills/squad/SKILL.md @@ -0,0 +1,299 @@ +--- +name: squad +description: >- + Squad's command catalog and interactive menu. Invoke via /squad (slash command) or natural language ("squad commands", "what can squad do", "show me squad options"). Presents categorized operations (Install & Upgrade, Team Management, Issues & PRs, Plugins & Skills, Model & Cost, Sessions & State) as an interactive picker. Routes to the right squad CLI command or the Squad coordinator agent. +user-invocable: true +allowedTools: [] +--- + +## Menu Presentation Rules + +When the user triggers this skill (via `/squad` slash command, "squad commands", "help", "what can squad do", etc.): + +1. **Category-level menu first.** Present category names as an `ask_user` choice list: + ``` + 📋 Squad Commands — pick a category: + 1. Install & Upgrade + 2. Team Management + 3. Issues & PRs + 4. Plugins & Skills + 5. Model & Cost + 6. Sessions & State + ``` +2. **Drill-down.** After selection, show operation titles in that category as a second `ask_user` list. +3. **Direct match skips the menu.** If the user says "how do I upgrade with state backend," match to the specific entry and go straight to argument collection. +4. **Compact fallback.** If `ask_user` is unavailable, render as a markdown table instead. +5. **Back / Cancel.** Include "← Back to categories" in sub-menus. Include "Cancel" in confirmation prompts. Respect "never mind" / "cancel" at any point. + +**Argument collection:** For entries with `args`, iterate the list sequentially. Use `ask_user` with choices when `choices` is provided; free-text prompt otherwise. If the user says "just do it" or "defaults are fine," skip remaining args and use their defaults. + +**Confirmation template:** +``` +⚠️ This will {action-description}. +{what will change} +Proceed? (yes / no) +``` + +--- + +## Install & Upgrade + +### Upgrade Squad CLI + +- **intent:** upgrade squad, update squad, install latest version, get new version +- **summary:** Upgrade Squad CLI to the latest version for your channel +- **action:** shell +- **command:** squad upgrade +- **args:** + - `state-backend`: Which state backend? | choices: {worktree, git-notes, orphan, two-layer} | default: (keep current) +- **confirm:** false +- **platform_caveats:** Requires terminal. In VS Code, open the integrated terminal and run the command directly. + +### Initialize Squad + +- **intent:** set up squad, initialize squad, create team, start squad in this project +- **summary:** Scaffold Squad in the current directory (idempotent) +- **action:** shell +- **command:** squad init +- **args:** (none) +- **confirm:** false +- **platform_caveats:** Requires terminal. Recommend a standalone terminal for best results. + +### Switch State Backend + +- **intent:** switch state backend, change state storage, use git-notes, use orphan branch +- **summary:** Change where Squad stores mutable state (config.json) +- **action:** file-edit +- **command:** .squad/config.json → stateBackend +- **args:** + - `stateBackend`: Which state backend? | choices: {worktree, git-notes, orphan, two-layer} | default: (keep current) +- **confirm:** true +- **platform_caveats:** May require migration if switching away from worktree. Show current value and new value before confirming. + +--- + +## Team Management + +### Add Team Member + +- **intent:** add team member, hire agent, add agent, add developer, recruit +- **summary:** Add a new agent to the team roster +- **action:** coordinator +- **command:** Add Team Member flow (Init Mode / Team Mode) +- **args:** + - `role`: What role should this agent fill? (e.g., Frontend Dev, Backend Dev, QA Engineer) + - `name`: Preferred name or casting universe? | default: (auto-cast from active universe) +- **confirm:** false + +### Remove Team Member + +- **intent:** remove team member, fire agent, delete agent, remove developer +- **summary:** Remove an agent and delete their charter and history files +- **action:** coordinator +- **command:** Remove Team Member flow +- **args:** + - `member`: Which team member to remove? (name or role) +- **confirm:** true + +### Reassign Roles + +- **intent:** reassign role, change role, swap roles, update team member role +- **summary:** Update a team member's role in team.md and their charter +- **action:** coordinator +- **command:** Update team.md roster + charter.md +- **args:** + - `member`: Which team member? + - `newRole`: New role? +- **confirm:** false + +### Show Roster + +- **intent:** show roster, who is on the team, list team members, show team, capability profile +- **summary:** Display the current team roster and capability profile +- **action:** coordinator +- **command:** Direct Mode — read team.md, answer +- **args:** (none) +- **confirm:** false + +--- + +## Issues & PRs + +### Connect GitHub Repo + +- **intent:** connect github, enable issues, set up issues, link repository, github issues mode +- **summary:** Connect this project to GitHub Issues via gh auth +- **action:** coordinator +- **command:** GitHub Issues Mode (connection flow) +- **args:** (none) +- **confirm:** false +- **platform_caveats:** Requires `gh auth login` to have been run in the terminal. + +### Triage Issues + +- **intent:** triage issues, review issues, assign issues, label issues +- **summary:** Run the Lead triage flow on open GitHub issues +- **action:** coordinator +- **command:** GitHub Issues Mode → Lead triage +- **args:** (none) +- **confirm:** false + +### Activate Ralph + +- **intent:** activate ralph, start ralph, ralph go, start work monitor, start auto-work +- **summary:** Activate Ralph — Work Monitor — to pick up and run queued issues +- **action:** coordinator +- **command:** Ralph — Work Monitor triggers +- **args:** (none) +- **confirm:** false + +### Set Ralph Polling Interval + +- **intent:** set ralph interval, change ralph timing, how often does ralph check, ralph every N minutes +- **summary:** Tell Ralph how frequently to poll for new work +- **action:** coordinator +- **command:** Ralph trigger: "Ralph, check every N minutes" +- **args:** + - `interval`: How often should Ralph poll? (in minutes) | default: 10 +- **confirm:** false + +### Start Squad Watch + +- **intent:** start watch, squad watch, monitor issues, watch for issues, auto-triage +- **summary:** Start squad watch to continuously poll and triage issues +- **action:** shell +- **command:** squad watch +- **args:** + - `interval`: Poll interval in minutes | default: 10 +- **confirm:** false +- **platform_caveats:** CLI-only — long-running foreground process. Not viable in VS Code without an integrated terminal. Run: `squad watch --interval {n}` in your terminal. + +--- + +## Plugins & Skills + +### Browse Plugin Marketplace + +- **intent:** browse plugins, explore plugins, what plugins are available, plugin marketplace +- **summary:** Browse available plugins in the Squad marketplace +- **action:** shell +- **command:** squad plugin marketplace browse +- **args:** + - `name`: Plugin name to search for | default: (browse all) +- **confirm:** false + +### Add Marketplace Plugin + +- **intent:** add plugin, install plugin, get plugin from marketplace +- **summary:** Add a plugin from the marketplace to this Squad +- **action:** shell +- **command:** squad plugin marketplace add +- **args:** + - `plugin`: Plugin owner/repo (e.g., owner/plugin-name) +- **confirm:** false + +### Remove Marketplace Plugin + +- **intent:** remove plugin, uninstall plugin, delete plugin +- **summary:** Remove an installed marketplace plugin +- **action:** shell +- **command:** squad plugin marketplace remove +- **args:** + - `name`: Plugin name to remove +- **confirm:** true + +### List Marketplace Plugins + +- **intent:** list plugins, show installed plugins, what plugins do I have +- **summary:** List all plugins registered in this Squad +- **action:** shell +- **command:** squad plugin marketplace list +- **args:** (none) +- **confirm:** false + +### List Installed Skills + +- **intent:** list skills, show skills, what skills are installed, skill catalog +- **summary:** List all skills installed in .squad/skills/ and .github/skills/ +- **action:** coordinator +- **command:** Direct Mode — list .squad/skills/ and .github/skills/ directories +- **args:** (none) +- **confirm:** false + +--- + +## Model & Cost + +### Set Default Model + +- **intent:** set default model, change model, use gpt-4, use claude, switch model +- **summary:** Set the default model for all agents in config.json +- **action:** file-edit +- **command:** .squad/config.json → defaultModel +- **args:** + - `model`: Model name (e.g., gpt-4o, claude-sonnet-4.5, o3) +- **confirm:** false + +### Override Per-Agent Model + +- **intent:** set model for agent, agent model override, use different model for one agent +- **summary:** Set a model override for a specific agent in config.json +- **action:** file-edit +- **command:** .squad/config.json → agentModelOverrides.{agentName} +- **args:** + - `agent`: Agent name (must match name in team.md) + - `model`: Model name (e.g., gpt-4o, claude-sonnet-4.5) +- **confirm:** false + +### Clear Model Preference + +- **intent:** clear model, reset model, remove model preference, use default model +- **summary:** Remove a model override from config.json (reverts to system default) +- **action:** file-edit +- **command:** .squad/config.json → remove defaultModel or agentModelOverrides.{agentName} +- **args:** + - `scope`: Clear default or a specific agent? | choices: {default model, specific agent} | default: default model + - `agent`: Agent name (only if scope = specific agent) +- **confirm:** false + +--- + +## Sessions & State + +### Catch-Up Summary + +- **intent:** catch me up, what happened, status, what did the team do, session summary +- **summary:** Summarize recent agent activity and key decisions +- **action:** coordinator +- **command:** Session catch-up flow (lazy scan) +- **args:** (none) +- **confirm:** false + +### Show Recent Decisions + +- **intent:** show decisions, recent decisions, what decisions were made, decision log +- **summary:** Display recent entries from .squad/decisions.md +- **action:** coordinator +- **command:** Direct Mode — read decisions.md, answer +- **args:** (none) +- **confirm:** false + +### Archive Old Decisions + +- **intent:** archive decisions, clean up decisions, move old decisions, compact decisions +- **summary:** Move old decisions from decisions.md to decisions-archive.md +- **action:** coordinator +- **command:** Move entries older than threshold from .squad/decisions.md → .squad/decisions-archive.md +- **args:** + - `olderThan`: Archive decisions older than how many days? | default: 30 +- **confirm:** true + +### Summarize Agent History + +- **intent:** summarize history, what did agent do, agent history, compress history +- **summary:** Spawn an agent to summarize and compress a team member's history file +- **action:** coordinator +- **command:** Spawn agent with history.md summarization task +- **args:** + - `member`: Which team member's history to summarize? +- **confirm:** false diff --git a/.squad/skills/tiered-memory/SKILL.md b/.squad/skills/tiered-memory/SKILL.md new file mode 100644 index 000000000..5921eb80a --- /dev/null +++ b/.squad/skills/tiered-memory/SKILL.md @@ -0,0 +1,221 @@ +--- +name: tiered-memory +description: Three-tier agent memory model (hot/cold/wiki) for context reduction per spawn +domain: memory-management, performance +confidence: design (runtime not yet implemented) +source: design proposal +--- + +# Skill: Tiered Agent Memory + +> **Status (v0.10.0):** This skill describes a **design proposal**, not a shipped runtime. Skill files install via `squad init`/`upgrade`, but the underlying tier scaffolding (`.squad/memory/hot/`, `cold/`, `wiki/`), Scribe promotion logic, and spawn-template tier-aware reads are tracked in [bradygaster/squad#1264](https://github.com/bradygaster/squad/issues/1264). Until those land, agents continue to load full `history.md` + `decisions.md` on every spawn. + +## Overview + +Squad agents today load their full context history on every spawn, which grows unboundedly across sessions. The Tiered Agent Memory model proposes a three-tier separation so agents only load the bytes that are actually relevant to the current task, with older context kept available on demand. + +--- + +## Memory Tiers + +### 🔥 Hot Tier — Current Session Context +- **Size target:** keep small (~2–4KB typical) +- **Load policy:** Always loaded. Every spawn includes hot memory by default. +- **Contents:** Current task description, active decisions made this session, immediate blockers, last 3–5 actions taken, who you are talking to right now. +- **Lifetime:** Current session only. Discarded after session ends (Scribe promotes relevant parts to Cold). +- **Purpose:** Provide immediate task context without any latency or load decision. + +### ❄️ Cold Tier — Summarized Cross-Session History +- **Size target:** larger summary, not full transcript (~8–12KB typical) +- **Load policy:** Load on demand. Include only when the task explicitly needs history. +- **Contents:** Summarized past sessions (compressed by Scribe), cross-session decisions, recurring patterns, unresolved issues from prior work. +- **Lifetime:** Rolling window (default proposal: 30 days). Eligible entries are then promoted to Wiki. +- **Purpose:** Answer "what have we tried before?" and "what was decided?" without replaying full transcripts. +- **How to include:** Pass `--include-cold` in spawn template or add `## Cold Memory` section. + +### 📚 Wiki Tier — Durable Structured Knowledge +- **Size target:** variable, structured reference docs +- **Load policy:** Async write, selective read. Load only when task requires domain knowledge. +- **Contents:** Architecture decisions (ADRs), agent charters, routing rules, stable conventions, external API contracts, known platform constraints. +- **Lifetime:** Permanent until explicitly deprecated. +- **Purpose:** Authoritative reference. Not history — structured facts. +- **How to include:** Pass `--include-wiki` or reference specific wiki doc paths in spawn template. + +--- + +## When to Load Each Tier + +| Situation | Hot | Cold | Wiki | +|-----------|-----|------|------| +| New task, no prior context needed | ✅ | ❌ | ❌ | +| Resuming interrupted work | ✅ | ✅ | ❌ | +| Debugging a recurring issue | ✅ | ✅ | ❌ | +| Implementing against a spec/ADR | ✅ | ❌ | ✅ | +| Onboarding to unfamiliar subsystem | ✅ | ❌ | ✅ | +| Post-incident review | ✅ | ✅ | ✅ | + +--- + +## Spawn Template Pattern + +The default spawn prompt should include **Hot tier only**: + +``` +## Memory Context + +### Hot (current session) +{hot_context} +``` + +Add `--include-cold` when the task needs history: +``` +## Memory Context + +### Hot (current session) +{hot_context} + +### Cold (summarized history — load on demand) +See: .squad/memory/cold/{agent-name}.md +``` + +Add `--include-wiki` when the task needs domain knowledge: +``` +## Memory Context + +### Hot (current session) +{hot_context} + +### Wiki (durable reference) +See: .squad/memory/wiki/{topic}.md +``` + +--- + +## Integration with Scribe Agent (design — not yet implemented) + +Scribe is the proposed memory coordinator for this system. Once the runtime lands, Scribe will: + +1. **End of session:** Compress Hot → Cold summary (target: ~10% of session verbosity) +2. **Aged cold entries:** Promote Cold → Wiki for decisions/facts that aged into stable knowledge +3. **On-demand wiki writes:** Any agent can request Scribe to write a wiki entry mid-session + +Until then, see the Scribe charter for current behavior: `.squad/agents/scribe/charter.md` + +--- + +## Implementation Checklist (tracked in #1264) + +- [ ] Scribe writes Hot context file at session start (`.squad/memory/hot/{agent}.md`) +- [ ] Scribe compresses and writes Cold summary at session end +- [ ] Spawn templates default to Hot-only +- [ ] Coordinators add `--include-cold` / `--include-wiki` flags as needed +- [ ] Wiki entries stored in `.squad/memory/wiki/` +- [ ] Cold entries stored in `.squad/memory/cold/` with rolling TTL + +--- + +## References + +- Tracking issue: [bradygaster/squad#1264](https://github.com/bradygaster/squad/issues/1264) — installation gap + runtime status +- Original design spike: [bradygaster/squad#686](https://github.com/bradygaster/squad/issues/686) — tiered memory implementation plan +- Related: [bradygaster/squad#600](https://github.com/bradygaster/squad/issues/600) — context payload growth + +--- + +## Spawn Template + +# Spawn Template: Agent with Tiered Memory + +Use this template when spawning any Squad agent. By default it loads **Hot tier only**. Add optional sections as needed. + +--- + +## Task + +{task_description} + +## WHY + +{why_this_matters} + +## Success Criteria + +- [ ] {criterion_1} +- [ ] {criterion_2} + +--- + +## Memory Context + +### 🔥 Hot (always included) + +> Paste current session context here (~2–4KB target): + +``` +Current task: {task_description} +Active decisions: {decisions_this_session} +Last actions: {last_3_to_5_actions} +Blockers: {current_blockers_or_none} +Talking to: {current_interlocutor} +``` + +--- + +### ❄️ Cold (include when task needs history — add `--include-cold`) + +> Load on demand. Do not inline unless specifically needed. + +Summarized cross-session history is at: +`.squad/memory/cold/{agent-name}.md` + +Include when: +- Resuming interrupted work +- Debugging a recurring issue +- "What have we tried before?" + +**To load cold memory, add this section and fetch the file before spawning:** + +``` +## Cold Memory Summary +{contents_of_.squad/memory/cold/{agent-name}.md} +``` + +--- + +### 📚 Wiki (include when task needs domain knowledge — add `--include-wiki`) + +> Load on demand. Reference specific wiki docs by path. + +Wiki entries are at: `.squad/memory/wiki/` + +Include when: +- Implementing against an ADR or spec +- Onboarding to unfamiliar subsystem +- Need stable conventions or API contracts + +**To load wiki, add this section and reference the specific doc:** + +``` +## Wiki Reference +{contents_of_.squad/memory/wiki/{topic}.md} +``` + +--- + +## Escalation + +If blocked or uncertain: +- Architecture questions → @picard +- Security concerns → @worf +- Infrastructure/deployment → @belanna +- Memory/history questions → @scribe + +--- + +## Notes + +- Hot tier is always included; keep it focused +- Cold adds a summary; only include when history is relevant +- Wiki adds variable size; only include specific relevant docs +- Runtime backing is tracked in [bradygaster/squad#1264](https://github.com/bradygaster/squad/issues/1264) — until those changes land, this skill is design-only and agents continue to load full history.md + decisions.md on every spawn + diff --git a/CHANGELOG.md b/CHANGELOG.md index dbb2fbc6a..47af4259c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [0.11.0] - 2026-06-29 + +### Added +- (cli) Add `squad preset install ` for installing shared presets from GitHub URLs or local paths (#1224). +- (cli) Add `squad registry add/list/remove` for discovery-only peer squads, plus the bundled cross-squad communication skill. +- (cli) Add `cast` terminology across help and docs while keeping `hire` as a silent compatibility alias (#1394). +- (sdk+cli) Add Copilot App sub-session spawning for cast members and thread reasoning-effort settings through agent dispatch (#1385). +- (sdk+cli) Expose memory tools through the `squad_state` MCP server and improve Fact Checker auto-scaffolding/plumbing. + +### Changed +- (sdk+cli) Slim the bundled `squad.agent.md` by extracting long guidance into satellite skills (#1311). +- (cli) Move bundled skills from `.copilot/skills/` to `.github/skills/` so they are visible to all Copilot surfaces. +- (cli) Rename user-facing ".NET Aspire" references to "Aspire" in CLI help and command output (#1239). +- (sdk+cli) Refresh the toolchain and integrations, including `@github/copilot-sdk` 1.0.4, TypeScript 6, Vitest 4, Ink 7, OpenTelemetry 2.x, and Node type updates. + +### Fixed +- (cli) Pin CLI-to-SDK workspace resolution so local builds and runtime use the workspace SDK instead of a stale published package (#1406). +- (sdk+cli) Restore the green test suite by reconciling personal squad paths, stabilizing observer category handling, guarding storage case-insensitivity, and skipping environment-dependent tests when prerequisites are missing (#1416). +- (sdk+cli) Guard symlink-sensitive tests on Windows or restricted CI filesystems (#1418). +- (sdk+cli) Harden observer change resolution for directory events, symlinks, and filesystem scan errors. +- (sdk+cli) Fix state-backend handshake, SQUAD_HOME/SQUAD_PERSONAL_DIR resolution, preset apply wiring, routing example quote stripping, YAML escaping, and MCP config pollution regressions. +- (cli) Fix Windows command escaping, top-level help coverage for `externalize`/`internalize`, slash-command discovery, and upgrade/self-repair flows. +- (sdk) Export gitignore-state helpers, update OTel resource APIs for 2.x, and align the adapter client with `@github/copilot-sdk` 1.0.4. + ## [0.10.0] — 2026-06-07 First stable release since v0.9.4 (April 25). Consumes 97 changesets (sdk: 50, cli: 71). @@ -108,6 +132,14 @@ First stable release since v0.9.4 (April 25). Consumes 97 changesets (sdk: 50, c ### Internal / Tests / Docs - (cli) docs: point README migration guide links to the published docs site +## [0.1.0-preview] - 2026-06-02 + +### Added — Squad.Agents.AI NuGet preview +- Added `Squad.Agents.AI`, a `net10.0` Microsoft Agent Framework package that exposes a Squad team as an `AIAgent`. +- Public surface: `SquadAgent`, `SquadAgentOptions`, `SquadConnectionFactory`, and `SquadServiceCollectionExtensions`. +- Added DI registration, PATH/URI connection-string parsing, GitHub token/provider options, and XML documentation for the public preview surface. +- Added README documentation for the `Squad.Agents.AI` package in `src/Squad.Agents.AI/README.md`. + ## [0.9.0] - 2026-03-23 ### Added — Personal Squad Governance Layer @@ -391,4 +423,3 @@ First stable release since v0.9.4 (April 25). Consumes 97 changesets (sdk: 50, c - Changesets infrastructure for independent package versioning - diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..6dc4b1210 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at . All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/README.md b/README.md index 5ee2e61ec..339a7a44a 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,10 @@ Squad proposes a team — each member named from a persistent thematic cast. You --- +## .NET package preview + +Building a .NET app that should call a Squad team as a Microsoft Agent Framework agent? `Squad.Agents.AI` is a preview NuGet package under [`src/Squad.Agents.AI`](src/Squad.Agents.AI/README.md). It registers a Squad-backed `AIAgent` in DI and targets early `0.1.0-preview` consumers. + ## Upgrading Upgrading Squad is a two-step process. @@ -134,7 +138,7 @@ Say **"squad commands"** in chat to see a categorized menu of common operations | Command | What it does | |---------|-------------| -| `squad init` | **Init** — scaffold Squad in the current directory (idempotent — safe to run multiple times); alias: `hire`; use `--global` to init in personal squad directory, `--mode remote ` for dual-root mode | +| `squad init` | **Init** — scaffold Squad in the current directory (idempotent — safe to run multiple times); alias: `cast`; use `--global` to init in personal squad directory, `--mode remote ` for dual-root mode | | `squad upgrade` | Update Squad-owned files to latest; never touches your team state; use `--global` to upgrade personal squad, `--migrate-directory` to rename `.ai-team/` → `.squad/` | | `squad upgrade --self` | Update the Squad CLI package itself; add `--insider` for dev-channel prerelease builds | | `squad status` | Show which squad is active and why | @@ -518,3 +522,35 @@ The SDK provides programmatic control over agent orchestration — custom tools, - [Samples](samples/README.md) — eight working examples from beginner to advanced For SDK installation: `npm install @bradygaster/squad-sdk` + +--- + +## Requirements + +- **Node.js** ≥22.5.0 +- **npm** ≥10.0.0 +- **Git** with SSH agent +- **GitHub CLI** (`gh`) for GitHub integration + +## License + +This project is licensed under the terms of the MIT open source license. Please refer to the [LICENSE](./LICENSE) file for the full terms. + +## Maintainers + +- [@bradygaster](https://github.com/bradygaster) +- [@tamirdresher](https://github.com/tamirdresher) + +See [CODEOWNERS](.github/CODEOWNERS) for the full list. + +## Support + +For help or questions about using Squad, please use [GitHub Discussions](https://github.com/github/squad/discussions). See [SUPPORT.md](./SUPPORT.md) for details. + +## Contributing + +We welcome contributions! See [CONTRIBUTING.md](./CONTRIBUTING.md) for build setup, monorepo structure, and guidelines. + +## Code of Conduct + +This project has adopted the [Contributor Covenant Code of Conduct](./CODE_OF_CONDUCT.md). See the full text for details on expected behavior and reporting. diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 000000000..0316949a8 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,13 @@ +# Support + +## How to file issues and get help + +This project uses GitHub Issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new issue. + +For help or questions about using Squad, please use [GitHub Discussions](https://github.com/bradygaster/squad/discussions) on this repository. + +**Squad** is under active development and maintained by GitHub staff and the community. We will do our best to respond to support, feature requests, and community questions in a timely manner. + +## GitHub Support Policy + +Support for this project is limited to the resources listed above. diff --git a/Squad.Agents.AI.slnx b/Squad.Agents.AI.slnx new file mode 100644 index 000000000..907725a98 --- /dev/null +++ b/Squad.Agents.AI.slnx @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/cspell.json b/cspell.json index 1030c0872..2f1fc0a0c 100644 --- a/cspell.json +++ b/cspell.json @@ -39,7 +39,9 @@ "benleane", "TELMU", "Automator", "kedacore", "DEVBOX", "myaccount", "graphify", "Graphify", "graphifyy", "safishamsi", "jagilber", "benchmarkfn", "filterissuesbystream", "filterissuesbyworkstream", "Recognised", "recognised", - "squadified", "TOCTOU", "unflushed", "unparseable", "pluggability" + "squadified", "TOCTOU", "unflushed", "unparseable", "pluggability", + "PKCE", "MSAL", "AKIA", "runas", + "typoo" ], "dictionaries": ["en_US", "typescript", "node", "npm", "bash"], "allowCompoundWords": true diff --git a/docs/.astro/collections/blog.schema.json b/docs/.astro/collections/blog.schema.json index bf04f626c..04f161b81 100644 --- a/docs/.astro/collections/blog.schema.json +++ b/docs/.astro/collections/blog.schema.json @@ -1,52 +1,34 @@ { - "$ref": "#/definitions/blog", - "definitions": { - "blog": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "date": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "author": { - "type": "string" - }, - "updatedAt": { - "anyOf": [ - { - "type": "string", - "format": "date-time" - }, - { - "type": "string", - "format": "date" - }, - { - "type": "integer", - "format": "unix-time" - } - ] - }, - "status": { - "type": "string" - }, - "$schema": { - "type": "string" - } - }, - "additionalProperties": false + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "date": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "author": { + "type": "string" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string" + }, + "$schema": { + "type": "string" } - }, - "$schema": "http://json-schema.org/draft-07/schema#" + } } \ No newline at end of file diff --git a/docs/.astro/collections/docs.schema.json b/docs/.astro/collections/docs.schema.json index 8564032b5..df651bbf7 100644 --- a/docs/.astro/collections/docs.schema.json +++ b/docs/.astro/collections/docs.schema.json @@ -1,52 +1,34 @@ { - "$ref": "#/definitions/docs", - "definitions": { - "docs": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "order": { - "type": "number" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "author": { - "type": "string" - }, - "updatedAt": { - "anyOf": [ - { - "type": "string", - "format": "date-time" - }, - { - "type": "string", - "format": "date" - }, - { - "type": "integer", - "format": "unix-time" - } - ] - }, - "status": { - "type": "string" - }, - "$schema": { - "type": "string" - } - }, - "additionalProperties": false + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "order": { + "type": "number" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "author": { + "type": "string" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string" + }, + "$schema": { + "type": "string" } - }, - "$schema": "http://json-schema.org/draft-07/schema#" + } } \ No newline at end of file diff --git a/docs/.astro/content.d.ts b/docs/.astro/content.d.ts index 4aa3e1b42..c88a60137 100644 --- a/docs/.astro/content.d.ts +++ b/docs/.astro/content.d.ts @@ -15,21 +15,13 @@ declare module 'astro:content' { [key: string]: unknown; }; } -} -declare module 'astro:content' { type Flatten = T extends { [K: string]: infer U } ? U : never; - export type CollectionKey = keyof AnyEntryMap; - export type CollectionEntry = Flatten; - - export type ContentCollectionKey = keyof ContentEntryMap; - export type DataCollectionKey = keyof DataEntryMap; + export type CollectionKey = keyof DataEntryMap; + export type CollectionEntry = Flatten; type AllValuesOf = T extends any ? T[keyof T] : never; - type ValidContentEntrySlug = AllValuesOf< - ContentEntryMap[C] - >['slug']; export type ReferenceDataEntry< C extends CollectionKey, @@ -38,41 +30,17 @@ declare module 'astro:content' { collection: C; id: E; }; - export type ReferenceContentEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}) = string, - > = { - collection: C; - slug: E; - }; + export type ReferenceLiveEntry = { collection: C; id: string; }; - /** @deprecated Use `getEntry` instead. */ - export function getEntryBySlug< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - // Note that this has to accept a regular string too, for SSR - entrySlug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - - /** @deprecated Use `getEntry` instead. */ - export function getDataEntryById( - collection: C, - entryId: E, - ): Promise>; - - export function getCollection>( + export function getCollection>( collection: C, filter?: (entry: CollectionEntry) => entry is E, ): Promise; - export function getCollection( + export function getCollection( collection: C, filter?: (entry: CollectionEntry) => unknown, ): Promise[]>; @@ -84,14 +52,6 @@ declare module 'astro:content' { import('astro').LiveDataCollectionResult, LiveLoaderErrorType> >; - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - entry: ReferenceContentEntry, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; export function getEntry< C extends keyof DataEntryMap, E extends keyof DataEntryMap[C] | (string & {}), @@ -100,15 +60,6 @@ declare module 'astro:content' { ): E extends keyof DataEntryMap[C] ? Promise : Promise | undefined>; - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - slug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; export function getEntry< C extends keyof DataEntryMap, E extends keyof DataEntryMap[C] | (string & {}), @@ -126,40 +77,47 @@ declare module 'astro:content' { ): Promise, LiveLoaderErrorType>>; /** Resolve an array of entry references from the same collection */ - export function getEntries( - entries: ReferenceContentEntry>[], - ): Promise[]>; export function getEntries( entries: ReferenceDataEntry[], ): Promise[]>; - export function render( - entry: AnyEntryMap[C][string], + export function render( + entry: DataEntryMap[C][string], ): Promise; - export function reference( + export function reference< + C extends + | keyof DataEntryMap + // Allow generic `string` to avoid excessive type errors in the config + // if `dev` is not running to update as you edit. + // Invalid collection names will be caught at build time. + | (string & {}), + >( collection: C, - ): import('astro/zod').ZodEffects< + ): import('astro/zod').ZodPipe< import('astro/zod').ZodString, - C extends keyof ContentEntryMap - ? ReferenceContentEntry> - : ReferenceDataEntry + import('astro/zod').ZodTransform< + C extends keyof DataEntryMap + ? { + collection: C; + id: string; + } + : never, + string + > >; - // Allow generic `string` to avoid excessive type errors in the config - // if `dev` is not running to update as you edit. - // Invalid collection names will be caught at build time. - export function reference( - collection: C, - ): import('astro/zod').ZodEffects; type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T; - type InferEntrySchema = import('astro/zod').infer< + type InferEntrySchema = import('astro/zod').infer< ReturnTypeOrOriginal['schema']> >; - - type ContentEntryMap = { - - }; + type ExtractLoaderConfig = T extends { loader: infer L } ? L : never; + type InferLoaderSchema< + C extends keyof DataEntryMap, + L = ExtractLoaderConfig, + > = L extends { schema: import('astro/zod').ZodSchema } + ? import('astro/zod').infer + : any; type DataEntryMap = { "blog": Record = T extends import('astro/loaders').LiveLoader< infer TData, infer TEntryFilter, @@ -191,10 +147,10 @@ declare module 'astro:content' { > ? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError } : { data: never; entryFilter: never; collectionFilter: never; error: never }; - type ExtractDataType = ExtractLoaderTypes['data']; type ExtractEntryFilterType = ExtractLoaderTypes['entryFilter']; type ExtractCollectionFilterType = ExtractLoaderTypes['collectionFilter']; type ExtractErrorType = ExtractLoaderTypes['error']; + type ExtractDataType = ExtractLoaderTypes['data']; type LiveLoaderDataType = LiveContentConfig['collections'][C]['schema'] extends undefined @@ -210,6 +166,6 @@ declare module 'astro:content' { LiveContentConfig['collections'][C]['loader'] >; - export type ContentConfig = typeof import("./../src/content/config.js"); + export type ContentConfig = typeof import("./../src/content.config.js"); export type LiveContentConfig = never; } diff --git a/docs/package-lock.json b/docs/package-lock.json index ee1c24006..03198bb08 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,44 +8,52 @@ "name": "squad-docs", "version": "0.0.1", "dependencies": { - "@astrojs/rss": "^4.0.17", - "@astrojs/sitemap": "^3.7.1", - "@tailwindcss/vite": "^4.1.0", - "astro": "^5.7.0", - "sharp": "^0.34.5", + "@astrojs/rss": "^4.0.18", + "@astrojs/sitemap": "^3.7.3", + "@tailwindcss/vite": "^4.3.1", + "astro": "^6.4.7", + "sharp": "^0.35.2", "tailwindcss": "^4.1.0", "unist-util-visit": "^5.1.0" }, "devDependencies": { - "@playwright/test": "^1.58.2", - "pagefind": "^1.4.0" + "@playwright/test": "^1.61.0", + "pagefind": "^1.5.2" } }, "node_modules/@astrojs/compiler": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", - "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-4.0.0.tgz", + "integrity": "sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA==", "license": "MIT" }, "node_modules/@astrojs/internal-helpers": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.5.tgz", - "integrity": "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA==", - "license": "MIT" + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.10.0.tgz", + "integrity": "sha512-Ry2R3VPeIN4uPCSA4xQc+e+vsJXkalKpEbDc07hV+a/o5Bs2N/s/uDcPJH/05L19DKh9tAy7e6JM3YZ6Cxfezw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "js-yaml": "^4.1.1", + "picomatch": "^4.0.4", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "unified": "^11.0.5" + } }, "node_modules/@astrojs/markdown-remark": { - "version": "6.3.10", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.10.tgz", - "integrity": "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.2.0.tgz", + "integrity": "sha512-+YxmVQu1Bd+MFfSzjq1rOJvD9+nIOJzz5YIIhdIH01RrxRkKbyKoEgyIqP3yv51MhzMDgd79QaPv+kCVPT8vHw==", "license": "MIT", "dependencies": { - "@astrojs/internal-helpers": "0.7.5", - "@astrojs/prism": "3.3.0", + "@astrojs/internal-helpers": "0.10.0", + "@astrojs/prism": "4.0.2", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", - "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", @@ -53,51 +61,40 @@ "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", - "shiki": "^3.19.0", - "smol-toml": "^1.5.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.0.0", + "unist-util-visit": "^5.1.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "node_modules/@astrojs/prism": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz", - "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz", + "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==", "license": "MIT", "dependencies": { "prismjs": "^1.30.0" }, "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" + "node": ">=22.12.0" } }, "node_modules/@astrojs/rss": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-4.0.17.tgz", - "integrity": "sha512-eV+wdMbeVKC9+sPaV0LN8JL1LGo9YAh3GKl4Ou4nzMNLmXM/aswYpSGxVEAuHilgBZ6/++/Pv08ICmuOqX107w==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-4.0.18.tgz", + "integrity": "sha512-wc5DwKlbTEdgVAWnHy8krFTeQ42t1v/DJqeq5HtulYK3FYHE4krtRGjoyhS3eXXgfdV6Raoz2RU3wrMTFAitRg==", "license": "MIT", "dependencies": { - "fast-xml-parser": "5.4.1", + "fast-xml-parser": "^5.5.7", "piccolore": "^0.1.3", "zod": "^4.3.6" } }, - "node_modules/@astrojs/rss/node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@astrojs/sitemap": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.1.tgz", - "integrity": "sha512-IzQqdTeskaMX+QDZCzMuJIp8A8C1vgzMBp/NmHNnadepHYNHcxQdGLQZYfkbd2EbRXUfOS+UDIKx8sKg0oWVdw==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.3.tgz", + "integrity": "sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==", "license": "MIT", "dependencies": { "sitemap": "^9.0.0", @@ -105,27 +102,16 @@ "zod": "^4.3.6" } }, - "node_modules/@astrojs/sitemap/node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@astrojs/telemetry": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz", - "integrity": "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.2.tgz", + "integrity": "sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ==", "license": "MIT", "dependencies": { - "ci-info": "^4.2.0", - "debug": "^4.4.0", - "dlv": "^1.1.3", + "ci-info": "^4.4.0", "dset": "^3.1.4", - "is-docker": "^3.0.0", - "is-wsl": "^3.1.0", + "is-docker": "^4.0.0", + "is-wsl": "^3.1.1", "which-pm-runs": "^1.1.0" }, "engines": { @@ -190,10 +176,38 @@ "node": ">=18" } }, + "node_modules/@clack/core": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.1.tgz", + "integrity": "sha512-FILJa1gGKEFTGZAJE9RpVhrjKz3c3h4ar60dSv6cGuDqufQ84YEIS3GAGvZiN+H6yaLbbvTFNejjCC4tXpZEuw==", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.5.1.tgz", + "integrity": "sha512-zccHj2z2oCCO4yrDiRSlFOxWerGqRiysP7a5jPK6uoI9URKAquwY42Dd/iUP8JWHxEzdRe4TlbvZCo8z1/mhrw==", + "license": "MIT", + "dependencies": { + "@clack/core": "1.4.1", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "license": "MIT", "optional": true, "dependencies": { @@ -626,9 +640,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.2.tgz", + "integrity": "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==", "cpu": [ "arm64" ], @@ -638,19 +652,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.1" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.2.tgz", + "integrity": "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==", "cpu": [ "x64" ], @@ -660,19 +674,38 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.1" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.2.tgz", + "integrity": "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.2" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.1.tgz", + "integrity": "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==", "cpu": [ "arm64" ], @@ -686,9 +719,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.1.tgz", + "integrity": "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==", "cpu": [ "x64" ], @@ -702,12 +735,15 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.1.tgz", + "integrity": "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==", "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -718,12 +754,15 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.1.tgz", + "integrity": "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -734,12 +773,15 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.1.tgz", + "integrity": "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==", "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -750,12 +792,15 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.1.tgz", + "integrity": "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==", "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -766,12 +811,15 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.1.tgz", + "integrity": "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==", "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -782,12 +830,15 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.1.tgz", + "integrity": "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -798,12 +849,15 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.1.tgz", + "integrity": "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -814,12 +868,15 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.1.tgz", + "integrity": "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -830,204 +887,244 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.2.tgz", + "integrity": "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==", "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.1" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.2.tgz", + "integrity": "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.1" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.2.tgz", + "integrity": "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==", "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.1" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.2.tgz", + "integrity": "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==", "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.1" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.2.tgz", + "integrity": "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==", "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.1" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.2.tgz", + "integrity": "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.1" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.2.tgz", + "integrity": "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.2.tgz", + "integrity": "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.1" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz", + "integrity": "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.2.tgz", + "integrity": "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==", "cpu": [ "wasm32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.2" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.2.tgz", + "integrity": "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==", "cpu": [ "arm64" ], @@ -1037,16 +1134,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.2.tgz", + "integrity": "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==", "cpu": [ "ia32" ], @@ -1056,16 +1153,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.2.tgz", + "integrity": "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==", "cpu": [ "x64" ], @@ -1075,7 +1172,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -1126,6 +1223,18 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@nodable/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, "node_modules/@oslojs/encoding": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", @@ -1133,9 +1242,9 @@ "license": "MIT" }, "node_modules/@pagefind/darwin-arm64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.4.0.tgz", - "integrity": "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.5.2.tgz", + "integrity": "sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ==", "cpu": [ "arm64" ], @@ -1147,9 +1256,9 @@ ] }, "node_modules/@pagefind/darwin-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.4.0.tgz", - "integrity": "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.5.2.tgz", + "integrity": "sha512-IojxFWMEJe0RQ7PQ3KXQsPIImNsbpPYpoZ+QUDrL8fAl/O27IX+LVLs74/UzEZy5uA2LD8Nz1AiwKr72vrkZQw==", "cpu": [ "x64" ], @@ -1161,9 +1270,9 @@ ] }, "node_modules/@pagefind/freebsd-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.4.0.tgz", - "integrity": "sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.5.2.tgz", + "integrity": "sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA==", "cpu": [ "x64" ], @@ -1175,9 +1284,9 @@ ] }, "node_modules/@pagefind/linux-arm64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.4.0.tgz", - "integrity": "sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.5.2.tgz", + "integrity": "sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw==", "cpu": [ "arm64" ], @@ -1189,9 +1298,9 @@ ] }, "node_modules/@pagefind/linux-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.4.0.tgz", - "integrity": "sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.5.2.tgz", + "integrity": "sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA==", "cpu": [ "x64" ], @@ -1202,10 +1311,24 @@ "linux" ] }, + "node_modules/@pagefind/windows-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.5.2.tgz", + "integrity": "sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@pagefind/windows-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.4.0.tgz", - "integrity": "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.5.2.tgz", + "integrity": "sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg==", "cpu": [ "x64" ], @@ -1217,13 +1340,13 @@ ] }, "node_modules/@playwright/test": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", - "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.0.tgz", + "integrity": "sha512-cKA5B6lpFEMyMGjxF54QihfYpB4FkEGH+qZhtArDEG+wezQAJY8Pq6C7T1SjWz+FFzt3TbyoXBQYk/0292TdJA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.58.2" + "playwright": "1.61.0" }, "bin": { "playwright": "cli.js" @@ -1586,64 +1709,97 @@ ] }, "node_modules/@shikijs/core": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz", - "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.2.0.tgz", + "integrity": "sha512-Hc87Ab1Ld/vEbZRCbwx344I5v+4RU8CVToUTRkqXL1+TjbuOp9U5Xa0M23V4GEWHxVn+yO5otb+HkQVm3ptWQQ==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.23.0", + "@shikijs/primitive": "4.2.0", + "@shikijs/types": "4.2.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/engine-javascript": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", - "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.2.0.tgz", + "integrity": "sha512-fjETeq1k5ffyXqRgS6+3hpvqseLalp1kjNfRbXpUgWR8FpZ1CmQfiNHovc5lncYjt/Vg5JK/WJEmLahjwMa0og==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.23.0", + "@shikijs/types": "4.2.0", "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.4" + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", - "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.2.0.tgz", + "integrity": "sha512-hTorK1dffPkpbMUk6Z+828PgRo7d07HbnizoP0hNPFjhxMHctj0Px/qoHeGMYafc6ju+u9iMldN4JbVzNQM++g==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.23.0", + "@shikijs/types": "4.2.0", "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/langs": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", - "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.2.0.tgz", + "integrity": "sha512-bwrVRlJ0wUhZxAbVdvBbv2TTC9yLsh4C/IO5Ofz0T8MQntgDvyVnkbjw9vi50r1kx7RCIJdnJnjZAwmAsXFLZQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.2.0.tgz", + "integrity": "sha512-NOq+DtUkVBJtZMVXL5A0vI0Xk8nvDYaXetFHSJFlOqjDZIVhIPRYFdGkSoElDqNuegikcc3A76SNUa8dTqtAYA==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.23.0" + "@shikijs/types": "4.2.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/themes": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", - "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.2.0.tgz", + "integrity": "sha512-RX8IHYeLv8Cu2W6ruc3RxUqWn0IYCqSrMBzi/uRGAmfyDNOnNO5BF/Px7o97n4XTpmFTo5GbRaazuOWj+2ak2w==", "license": "MIT", "dependencies": { - "@shikijs/types": "3.23.0" + "@shikijs/types": "4.2.0" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/types": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", - "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.2.0.tgz", + "integrity": "sha512-VT/MKtlpOhEPZloSH3Pb9WCZEBDoQVMa9jedp5UAwmJOar1DVc9DRODAxmYPW9M93IK4ryuqRejFfmlvlVDemw==", "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" } }, "node_modules/@shikijs/vscode-textmate": { @@ -1653,47 +1809,47 @@ "license": "MIT" }, "node_modules/@tailwindcss/node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz", - "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.1.tgz", + "integrity": "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.19.0", - "jiti": "^2.6.1", - "lightningcss": "1.31.1", + "enhanced-resolve": "5.21.6", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.2.1" + "tailwindcss": "4.3.1" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz", - "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.1.tgz", + "integrity": "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==", "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.1", - "@tailwindcss/oxide-darwin-arm64": "4.2.1", - "@tailwindcss/oxide-darwin-x64": "4.2.1", - "@tailwindcss/oxide-freebsd-x64": "4.2.1", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", - "@tailwindcss/oxide-linux-x64-musl": "4.2.1", - "@tailwindcss/oxide-wasm32-wasi": "4.2.1", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" + "@tailwindcss/oxide-android-arm64": "4.3.1", + "@tailwindcss/oxide-darwin-arm64": "4.3.1", + "@tailwindcss/oxide-darwin-x64": "4.3.1", + "@tailwindcss/oxide-freebsd-x64": "4.3.1", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.1", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.1", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.1", + "@tailwindcss/oxide-linux-x64-musl": "4.3.1", + "@tailwindcss/oxide-wasm32-wasi": "4.3.1", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.1", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.1" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz", - "integrity": "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.1.tgz", + "integrity": "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==", "cpu": [ "arm64" ], @@ -1707,9 +1863,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz", - "integrity": "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.1.tgz", + "integrity": "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==", "cpu": [ "arm64" ], @@ -1723,9 +1879,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz", - "integrity": "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.1.tgz", + "integrity": "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==", "cpu": [ "x64" ], @@ -1739,9 +1895,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz", - "integrity": "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.1.tgz", + "integrity": "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==", "cpu": [ "x64" ], @@ -1755,9 +1911,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz", - "integrity": "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.1.tgz", + "integrity": "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==", "cpu": [ "arm" ], @@ -1771,9 +1927,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz", - "integrity": "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.1.tgz", + "integrity": "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==", "cpu": [ "arm64" ], @@ -1787,9 +1943,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz", - "integrity": "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.1.tgz", + "integrity": "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==", "cpu": [ "arm64" ], @@ -1803,9 +1959,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz", - "integrity": "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.1.tgz", + "integrity": "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==", "cpu": [ "x64" ], @@ -1819,9 +1975,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz", - "integrity": "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.1.tgz", + "integrity": "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==", "cpu": [ "x64" ], @@ -1835,9 +1991,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz", - "integrity": "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.1.tgz", + "integrity": "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -1852,67 +2008,127 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.8.1", - "@emnapi/runtime": "^1.8.1", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.1", - "@tybys/wasm-util": "^0.10.1", + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz", - "integrity": "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==", - "cpu": [ - "arm64" - ], + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.10.0", + "inBundle": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" } }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz", - "integrity": "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==", - "cpu": [ - "x64" - ], + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.10.0", + "inBundle": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@tailwindcss/vite": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.1.tgz", - "integrity": "sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w==", + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "inBundle": true, "license": "MIT", + "optional": true, "dependencies": { - "@tailwindcss/node": "4.2.1", - "@tailwindcss/oxide": "4.2.1", - "tailwindcss": "4.2.1" - }, + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "inBundle": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.1.tgz", + "integrity": "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.1.tgz", + "integrity": "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.1.tgz", + "integrity": "sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.1", + "@tailwindcss/oxide": "4.3.1", + "tailwindcss": "4.3.1" + }, "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7" + "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", "license": "MIT", "dependencies": { "@types/ms": "*" @@ -1987,92 +2203,6 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -2087,9 +2217,9 @@ } }, "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -2098,6 +2228,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/anynum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.0.tgz", + "integrity": "sha512-xjR9/zBVnUOP6ztMIIgShjsxui80nQUQH+5xJnvrYLs+90bF25/KJqaAi8mk+B4RDtX1Nspi6fmp4YTEts8SfA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -2130,80 +2272,72 @@ } }, "node_modules/astro": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.18.0.tgz", - "integrity": "sha512-CHiohwJIS4L0G6/IzE1Fx3dgWqXBCXus/od0eGUfxrZJD2um2pE7ehclMmgL/fXqbU7NfE1Ze2pq34h2QaA6iQ==", + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/astro/-/astro-6.4.7.tgz", + "integrity": "sha512-5vsXx0H52u23Jpshs9tM81D03Tb3Oh2Vt2Zo0bpqjXN+njkAWjFyGjTfmWJLAcrCQd9Q+iWB1eqfhR1sZJEaUA==", "license": "MIT", "dependencies": { - "@astrojs/compiler": "^2.13.0", - "@astrojs/internal-helpers": "0.7.5", - "@astrojs/markdown-remark": "6.3.10", - "@astrojs/telemetry": "3.3.0", + "@astrojs/compiler": "^4.0.0", + "@astrojs/internal-helpers": "0.10.0", + "@astrojs/markdown-remark": "7.2.0", + "@astrojs/telemetry": "3.3.2", "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", - "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", - "boxen": "8.0.1", - "ci-info": "^4.3.1", + "ci-info": "^4.4.0", "clsx": "^2.1.1", - "common-ancestor-path": "^1.0.1", + "common-ancestor-path": "^2.0.0", "cookie": "^1.1.1", - "cssesc": "^3.0.0", - "debug": "^4.4.3", - "deterministic-object-hash": "^2.0.2", - "devalue": "^5.6.2", + "devalue": "^5.8.1", "diff": "^8.0.3", - "dlv": "^1.1.3", "dset": "^3.1.4", - "es-module-lexer": "^1.7.0", + "es-module-lexer": "^2.0.0", "esbuild": "^0.27.3", - "estree-walker": "^3.0.3", "flattie": "^1.1.1", - "fontace": "~0.4.0", + "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", - "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", + "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", - "magicast": "^0.5.1", + "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", - "p-limit": "^6.2.0", - "p-queue": "^8.1.1", + "obug": "^2.1.1", + "p-limit": "^7.3.0", + "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", - "picomatch": "^4.0.3", - "prompts": "^2.4.2", + "picomatch": "^4.0.4", "rehype": "^13.0.2", - "semver": "^7.7.3", - "shiki": "^3.21.0", + "semver": "^7.7.4", + "shiki": "^4.0.2", "smol-toml": "^1.6.0", - "svgo": "^4.0.0", - "tinyexec": "^1.0.2", + "svgo": "^4.0.1", + "tinyclip": "^0.1.12", + "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", - "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", - "unifont": "~0.7.3", - "unist-util-visit": "^5.0.0", - "unstorage": "^1.17.4", + "unifont": "~0.7.4", + "unist-util-visit": "^5.1.0", + "unstorage": "^1.17.5", "vfile": "^6.0.3", - "vite": "^6.4.1", - "vitefu": "^1.1.1", + "vite": "^7.3.2", + "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", - "yargs-parser": "^21.1.1", - "yocto-spinner": "^0.2.3", - "zod": "^3.25.76", - "zod-to-json-schema": "^3.25.1", - "zod-to-ts": "^1.2.0" + "yargs-parser": "^22.0.0", + "zod": "^4.3.6" }, "bin": { - "astro": "astro.js" + "astro": "bin/astro.mjs" }, "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0", + "node": ">=22.12.0", "npm": ">=9.6.5", "pnpm": ">=7.1.0" }, @@ -2215,535 +2349,505 @@ "sharp": "^0.34.0" } }, - "node_modules/astro/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "node_modules/astro/node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", "cpu": [ "arm64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "android" + "darwin" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, - "node_modules/astro/node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "node_modules/astro/node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", "cpu": [ "x64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "android" + "darwin" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" } }, - "node_modules/astro/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "node_modules/astro/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", "cpu": [ "arm64" ], - "license": "MIT", + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" ], - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "node_modules/astro/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", "cpu": [ "x64" ], - "license": "MIT", + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "node_modules/astro/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", "cpu": [ - "x64" + "arm" ], - "license": "MIT", + "license": "LGPL-3.0-or-later", "optional": true, "os": [ - "freebsd" + "linux" ], - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "node_modules/astro/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", "cpu": [ - "arm" + "arm64" ], - "license": "MIT", + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "node_modules/astro/node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", "cpu": [ - "arm64" + "ppc64" ], - "license": "MIT", + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "node_modules/astro/node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", "cpu": [ - "ia32" + "riscv64" ], - "license": "MIT", + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "node_modules/astro/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", "cpu": [ - "loong64" + "s390x" ], - "license": "MIT", + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "node_modules/astro/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", "cpu": [ - "mips64el" + "x64" ], - "license": "MIT", + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "node_modules/astro/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", "cpu": [ - "ppc64" + "arm64" ], - "license": "MIT", + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "node_modules/astro/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", "cpu": [ - "riscv64" + "x64" ], - "license": "MIT", + "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], - "engines": { - "node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "node_modules/astro/node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", "cpu": [ - "s390x" + "arm" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" } }, - "node_modules/astro/node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "node_modules/astro/node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", "cpu": [ - "x64" + "arm64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" } }, - "node_modules/astro/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "node_modules/astro/node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", "cpu": [ - "arm64" + "ppc64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "netbsd" + "linux" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" } }, - "node_modules/astro/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "node_modules/astro/node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", "cpu": [ - "x64" + "riscv64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "netbsd" + "linux" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" } }, - "node_modules/astro/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "node_modules/astro/node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", "cpu": [ - "arm64" + "s390x" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "openbsd" + "linux" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" } }, - "node_modules/astro/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "node_modules/astro/node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", "cpu": [ "x64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "openbsd" + "linux" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" } }, - "node_modules/astro/node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "node_modules/astro/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", "cpu": [ "arm64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "openharmony" + "linux" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" } }, - "node_modules/astro/node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "node_modules/astro/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", "cpu": [ "x64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ - "sunos" + "linux" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/astro/node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "node_modules/astro/node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", "cpu": [ "arm64" ], - "license": "MIT", + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "node_modules/astro/node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", "cpu": [ "ia32" ], - "license": "MIT", + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "node_modules/astro/node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", "cpu": [ "x64" ], - "license": "MIT", + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } + "url": "https://opencollective.com/libvips" } }, - "node_modules/astro/node_modules/vite/node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "node_modules/astro/node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" }, "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" } }, "node_modules/axobject-query": { @@ -2765,52 +2869,12 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/base-64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", - "license": "MIT" - }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "license": "ISC" }, - "node_modules/boxen": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", - "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^8.0.0", - "chalk": "^5.3.0", - "cli-boxes": "^3.0.0", - "string-width": "^7.2.0", - "type-fest": "^4.21.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -2821,18 +2885,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -2893,18 +2945,6 @@ "node": ">=8" } }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2934,10 +2974,13 @@ } }, "node_modules/common-ancestor-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", - "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", - "license": "ISC" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } }, "node_modules/cookie": { "version": "1.1.1", @@ -2953,9 +2996,9 @@ } }, "node_modules/cookie-es": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", - "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", "license": "MIT" }, "node_modules/crossws": { @@ -3008,18 +3051,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/csso": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", @@ -3084,9 +3115,9 @@ } }, "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", "license": "MIT" }, "node_modules/dequal": { @@ -3113,22 +3144,10 @@ "node": ">=8" } }, - "node_modules/deterministic-object-hash": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", - "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", - "license": "MIT", - "dependencies": { - "base-64": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/devalue": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.3.tgz", - "integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", + "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", "license": "MIT" }, "node_modules/devlop": { @@ -3153,12 +3172,6 @@ "node": ">=0.3.1" } }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -3235,20 +3248,14 @@ "node": ">=4" } }, - "node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, "node_modules/enhanced-resolve": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", - "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", + "version": "5.21.6", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", + "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" @@ -3267,9 +3274,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "license": "MIT" }, "node_modules/esbuild": { @@ -3321,17 +3328,8 @@ "engines": { "node": ">=12" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eventemitter3": { @@ -3346,10 +3344,34 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, "node_modules/fast-xml-builder": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", - "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", "funding": [ { "type": "github", @@ -3358,13 +3380,14 @@ ], "license": "MIT", "dependencies": { - "path-expression-matcher": "^1.1.3" + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" } }, "node_modules/fast-xml-parser": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz", - "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==", + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.9.0.tgz", + "integrity": "sha512-duBuXbyIhEeNO4GjFuVqr0nF047oNwr18aum+zJyqo0MUG/n7Afgs3Qv3D6VN3ONedUKxiuFlPiMGIa0Z11chA==", "funding": [ { "type": "github", @@ -3373,8 +3396,12 @@ ], "license": "MIT", "dependencies": { - "fast-xml-builder": "^1.0.0", - "strnum": "^2.1.2" + "@nodable/entities": "^2.2.0", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^1.0.1", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.4.0", + "xml-naming": "^0.1.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -3441,16 +3468,19 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/get-east-asian-width": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", - "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", + "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, "engines": { - "node": ">=18" + "node": ">=20.20.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, "node_modules/github-slugger": { @@ -3466,14 +3496,14 @@ "license": "ISC" }, "node_modules/h3": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.5.tgz", - "integrity": "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", "license": "MIT", "dependencies": { - "cookie-es": "^1.2.2", + "cookie-es": "^1.2.3", "crossws": "^0.3.5", - "defu": "^6.1.4", + "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", @@ -3681,16 +3711,6 @@ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", "license": "BSD-2-Clause" }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/iron-webcrypto": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", @@ -3701,29 +3721,20 @@ } }, "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz", + "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==", "license": "MIT", "bin": { "is-docker": "cli.js" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -3742,6 +3753,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -3754,6 +3780,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-unsafe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-1.0.1.tgz", + "integrity": "sha512-CLK2+VdgERgD96EYm5lUQssZYlRg2tkZnbsxZoacmSiRxiFJ4Nk4SzjCl+Ur+v3kXIY9dTIdb3IH22y1mZ56LA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/is-wsl": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", @@ -3770,18 +3808,28 @@ } }, "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -3790,19 +3838,16 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", - "engines": { - "node": ">=6" - } + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" }, "node_modules/lightningcss": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", - "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -3815,23 +3860,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.31.1", - "lightningcss-darwin-arm64": "1.31.1", - "lightningcss-darwin-x64": "1.31.1", - "lightningcss-freebsd-x64": "1.31.1", - "lightningcss-linux-arm-gnueabihf": "1.31.1", - "lightningcss-linux-arm64-gnu": "1.31.1", - "lightningcss-linux-arm64-musl": "1.31.1", - "lightningcss-linux-x64-gnu": "1.31.1", - "lightningcss-linux-x64-musl": "1.31.1", - "lightningcss-win32-arm64-msvc": "1.31.1", - "lightningcss-win32-x64-msvc": "1.31.1" + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz", - "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", "cpu": [ "arm64" ], @@ -3849,9 +3894,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz", - "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", "cpu": [ "arm64" ], @@ -3869,9 +3914,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz", - "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", "cpu": [ "x64" ], @@ -3889,9 +3934,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz", - "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", "cpu": [ "x64" ], @@ -3909,9 +3954,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz", - "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", "cpu": [ "arm" ], @@ -3929,9 +3974,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz", - "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", "cpu": [ "arm64" ], @@ -3949,9 +3994,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz", - "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", "cpu": [ "arm64" ], @@ -3969,9 +4014,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz", - "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" ], @@ -3989,9 +4034,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz", - "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" ], @@ -4009,9 +4054,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz", - "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", "cpu": [ "arm64" ], @@ -4029,9 +4074,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz", - "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", "cpu": [ "x64" ], @@ -4059,9 +4104,9 @@ } }, "node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -4979,6 +5024,19 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/obug": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/ofetch": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", @@ -4997,60 +5055,60 @@ "license": "MIT" }, "node_modules/oniguruma-parser": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", - "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", "license": "MIT" }, "node_modules/oniguruma-to-es": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz", - "integrity": "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", "license": "MIT", "dependencies": { - "oniguruma-parser": "^0.12.1", - "regex": "^6.0.1", + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "node_modules/p-limit": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", - "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.0.tgz", + "integrity": "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==", "license": "MIT", "dependencies": { - "yocto-queue": "^1.1.1" + "yocto-queue": "^1.2.1" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-queue": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz", - "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.0.tgz", + "integrity": "sha512-7NED7xhQ74Ngp4JP/2e0VZHp7vSWfJfqeiR92jPgxsz6m0Se4P03YoTKa9dDXyZ3r6P616gUXttrB6nnHYKang==", "license": "MIT", "dependencies": { - "eventemitter3": "^5.0.1", - "p-timeout": "^6.1.2" + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-timeout": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", - "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", "license": "MIT", "engines": { - "node": ">=14.16" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5063,21 +5121,22 @@ "license": "MIT" }, "node_modules/pagefind": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.4.0.tgz", - "integrity": "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.5.2.tgz", + "integrity": "sha512-XTUaK0hXMCu2jszWE584JGQT7y284TmMV9l/HX3rnG5uo3rHI/uHU56XTyyyPFjeWEBxECbAi0CaFDJOONtG0Q==", "dev": true, "license": "MIT", "bin": { "pagefind": "lib/runner/bin.cjs" }, "optionalDependencies": { - "@pagefind/darwin-arm64": "1.4.0", - "@pagefind/darwin-x64": "1.4.0", - "@pagefind/freebsd-x64": "1.4.0", - "@pagefind/linux-arm64": "1.4.0", - "@pagefind/linux-x64": "1.4.0", - "@pagefind/windows-x64": "1.4.0" + "@pagefind/darwin-arm64": "1.5.2", + "@pagefind/darwin-x64": "1.5.2", + "@pagefind/freebsd-x64": "1.5.2", + "@pagefind/linux-arm64": "1.5.2", + "@pagefind/linux-x64": "1.5.2", + "@pagefind/windows-arm64": "1.5.2", + "@pagefind/windows-x64": "1.5.2" } }, "node_modules/parse-latin": { @@ -5111,9 +5170,9 @@ } }, "node_modules/path-expression-matcher": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz", - "integrity": "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", "funding": [ { "type": "github", @@ -5138,9 +5197,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" @@ -5150,13 +5209,13 @@ } }, "node_modules/playwright": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", - "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz", + "integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.58.2" + "playwright-core": "1.61.0" }, "bin": { "playwright": "cli.js" @@ -5169,9 +5228,9 @@ } }, "node_modules/playwright-core": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", - "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.0.tgz", + "integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5233,19 +5292,6 @@ "node": ">=6" } }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/property-information": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", @@ -5441,6 +5487,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/retext": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", @@ -5556,9 +5611,9 @@ } }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5568,63 +5623,66 @@ } }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.2.tgz", + "integrity": "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==", "license": "Apache-2.0", "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.4" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.2", + "@img/sharp-darwin-x64": "0.35.2", + "@img/sharp-freebsd-wasm32": "0.35.2", + "@img/sharp-libvips-darwin-arm64": "1.3.1", + "@img/sharp-libvips-darwin-x64": "1.3.1", + "@img/sharp-libvips-linux-arm": "1.3.1", + "@img/sharp-libvips-linux-arm64": "1.3.1", + "@img/sharp-libvips-linux-ppc64": "1.3.1", + "@img/sharp-libvips-linux-riscv64": "1.3.1", + "@img/sharp-libvips-linux-s390x": "1.3.1", + "@img/sharp-libvips-linux-x64": "1.3.1", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", + "@img/sharp-libvips-linuxmusl-x64": "1.3.1", + "@img/sharp-linux-arm": "0.35.2", + "@img/sharp-linux-arm64": "0.35.2", + "@img/sharp-linux-ppc64": "0.35.2", + "@img/sharp-linux-riscv64": "0.35.2", + "@img/sharp-linux-s390x": "0.35.2", + "@img/sharp-linux-x64": "0.35.2", + "@img/sharp-linuxmusl-arm64": "0.35.2", + "@img/sharp-linuxmusl-x64": "0.35.2", + "@img/sharp-webcontainers-wasm32": "0.35.2", + "@img/sharp-win32-arm64": "0.35.2", + "@img/sharp-win32-ia32": "0.35.2", + "@img/sharp-win32-x64": "0.35.2" } }, "node_modules/shiki": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz", - "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.2.0.tgz", + "integrity": "sha512-hjNax6o/ylDy9lefQEaSDtzaT3iVNtZ3WmpQnbuQNoG4xvnSKf2kSKbihZVO4JRG1TTMejs7CmNRYlWgAL66pQ==", "license": "MIT", "dependencies": { - "@shikijs/core": "3.23.0", - "@shikijs/engine-javascript": "3.23.0", - "@shikijs/engine-oniguruma": "3.23.0", - "@shikijs/langs": "3.23.0", - "@shikijs/themes": "3.23.0", - "@shikijs/types": "3.23.0", + "@shikijs/core": "4.2.0", + "@shikijs/engine-javascript": "4.2.0", + "@shikijs/engine-oniguruma": "4.2.0", + "@shikijs/langs": "4.2.0", + "@shikijs/themes": "4.2.0", + "@shikijs/types": "4.2.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" } }, "node_modules/sisteransi": { @@ -5653,9 +5711,9 @@ } }, "node_modules/smol-toml": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", - "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz", + "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==", "license": "BSD-3-Clause", "engines": { "node": ">= 18" @@ -5689,23 +5747,6 @@ "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", "license": "MIT" }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -5720,32 +5761,20 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/strnum": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.1.tgz", - "integrity": "sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.0.tgz", + "integrity": "sha512-sHrVyWWdq28RbhjuJdZsA1SnGRJV6NiXbk6AXBxDOsgAcA+lmpUZCYjOdLBxkXMwis6RRe7dlZt4VlIWFVzkmg==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT" + "license": "MIT", + "dependencies": { + "anynum": "^1.0.0" + } }, "node_modules/svgo": { "version": "4.0.1", @@ -5773,15 +5802,15 @@ } }, "node_modules/tailwindcss": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", - "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.1.tgz", + "integrity": "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==", "license": "MIT" }, "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "license": "MIT", "engines": { "node": ">=6" @@ -5797,10 +5826,19 @@ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", "license": "MIT" }, + "node_modules/tinyclip": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.14.tgz", + "integrity": "sha512-F1oWdz8tjT17qe1d5JgDK6z03WGOhYYAN0lK3/D/fzNiy93xswLLEw7pk+3g05onhAy6Bsc6PLNUGhdgVjemMQ==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "license": "MIT", "engines": { "node": ">=18" @@ -5842,26 +5880,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -5869,32 +5887,6 @@ "license": "0BSD", "optional": true }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/ufo": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", @@ -6073,16 +6065,16 @@ } }, "node_modules/unstorage": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.4.tgz", - "integrity": "sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw==", + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", "license": "MIT", "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", - "h3": "^1.15.5", - "lru-cache": "^11.2.0", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" @@ -6211,11 +6203,10 @@ } }, "node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.5.tgz", + "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -6323,36 +6314,19 @@ "node": ">=4" } }, - "node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "license": "MIT", - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "node_modules/xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=16.0.0" } }, "node_modules/xxhash-wasm": { @@ -6362,12 +6336,12 @@ "license": "MIT" }, "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", "license": "ISC", "engines": { - "node": ">=12" + "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/yocto-queue": { @@ -6382,60 +6356,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yocto-spinner": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz", - "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==", - "license": "MIT", - "dependencies": { - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18.19" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", - "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/zod-to-json-schema": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", - "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25 || ^4" - } - }, - "node_modules/zod-to-ts": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", - "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", - "peerDependencies": { - "typescript": "^4.9.4 || ^5.0.2", - "zod": "^3" - } - }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/docs/package.json b/docs/package.json index 488a4890d..55f893b7e 100644 --- a/docs/package.json +++ b/docs/package.json @@ -13,16 +13,16 @@ "test:e2e": "npx playwright test" }, "dependencies": { - "@astrojs/rss": "^4.0.17", - "@astrojs/sitemap": "^3.7.1", - "@tailwindcss/vite": "^4.1.0", - "astro": "^5.7.0", - "sharp": "^0.34.5", + "@astrojs/rss": "^4.0.18", + "@astrojs/sitemap": "^3.7.3", + "@tailwindcss/vite": "^4.3.1", + "astro": "^6.4.7", + "sharp": "^0.35.2", "tailwindcss": "^4.1.0", "unist-util-visit": "^5.1.0" }, "devDependencies": { - "@playwright/test": "^1.58.2", - "pagefind": "^1.4.0" + "@playwright/test": "^1.61.0", + "pagefind": "^1.5.2" } } diff --git a/docs/proposals/memory-governance-provider.md b/docs/proposals/memory-governance-provider.md index 4408d00b2..8089703cc 100644 --- a/docs/proposals/memory-governance-provider.md +++ b/docs/proposals/memory-governance-provider.md @@ -93,7 +93,7 @@ For `squad init` and Copilot custom agents, local worktree memory remains the de - `.squad/decisions.md` - `.squad/decisions/inbox/` - `.squad/agents/{name}/history.md` -- `.squad/skills/{name}/SKILL.md` +- `.copilot/skills/{name}/SKILL.md` This keeps Squad usable offline, reviewable in Git, and compatible with existing teams. It also preserves the current prompt-only behavior for Copilot custom agents: when no diff --git a/docs/src/content/config.ts b/docs/src/content.config.ts similarity index 100% rename from docs/src/content/config.ts rename to docs/src/content.config.ts diff --git a/docs/src/content/blog/001b-meet-the-squad.md b/docs/src/content/blog/001b-meet-the-squad.md index bc8d2b07f..57fe72d62 100644 --- a/docs/src/content/blog/001b-meet-the-squad.md +++ b/docs/src/content/blog/001b-meet-the-squad.md @@ -69,8 +69,8 @@ You won't hear from Scribe. Ever. Scribe works in the background — logging ses | Active agents | 8 | | Silent agents | 1 | | Total roster | 9 | -| Original hires (day one) | 5 | -| Hired in session 2+ | 3 | +| Original cast (day one) | 5 | +| Cast in session 2+ | 3 | | Newest member | Redfoot | ## What's Next diff --git a/docs/src/content/blog/004-v020-release.md b/docs/src/content/blog/004-v020-release.md index 2eb3bf9b1..74d8ea616 100644 --- a/docs/src/content/blog/004-v020-release.md +++ b/docs/src/content/blog/004-v020-release.md @@ -18,7 +18,7 @@ hero: "Export your squad. Import it somewhere else. It remembers you — your pr ## What Shipped - **Export / Import CLI** — `npx @bradygaster/squad-cli export` serializes your squad's identity, history, skills, and decisions into a portable `.squad` package. `npx @bradygaster/squad-cli import` reconstitutes it in a new project. Your squad remembers YOU, not the repo it came from. _(Built by Fenster)_ -- **Skills Phase 1: Template + Read** — Agents read `SKILL.md` files from `.squad/skills/` before working. Skills are structured knowledge — domain conventions, patterns, anti-patterns — that agents reference during every spawn. _(Built by Verbal)_ +- **Skills Phase 1: Template + Read** — Agents read `SKILL.md` files from `.copilot/skills/` before working. Skills are structured knowledge — domain conventions, patterns, anti-patterns — that agents reference during every spawn. _(Built by Verbal)_ - **Skills Phase 2: Earned Skills** — Agents write `SKILL.md` files from real work. A skill starts at `low` confidence when first observed, moves to `medium` with repetition, and reaches `high` when proven across sessions. Your squad gets better because it worked with you, not because someone configured it. _(Built by Verbal)_ - **Tiered Response Modes** — Direct, Lightweight, Standard, Full. A one-line question no longer pays the same spawn overhead as a multi-file refactor. The coordinator picks the tier based on complexity. _(Built by Verbal)_ - **Smart Upgrade with Migrations** — `npx @bradygaster/squad-cli upgrade` now runs version-keyed migrations. Upgrading from v0.1.0 to v0.2.0 applies only the migrations for versions you haven't seen. Your team state is never touched. _(Built by Fenster)_ diff --git a/docs/src/content/blog/009-v040-sprint-progress.md b/docs/src/content/blog/009-v040-sprint-progress.md index a2488a7f7..7f8da90f1 100644 --- a/docs/src/content/blog/009-v040-sprint-progress.md +++ b/docs/src/content/blog/009-v040-sprint-progress.md @@ -73,7 +73,7 @@ Coordinator: 3. Extracts new milestones, relays to user in real-time 4. Falls back to "still working" if no milestones (graceful degradation) -**For v0.4.0:** Coordinator polling loop + `.squad/skills/progress-signals/SKILL.md` documentation. +**For v0.4.0:** Coordinator polling loop + `.copilot/skills/progress-signals/SKILL.md` documentation. **For v0.5.0+:** Customizable polling cadence, emoji matching to agent persona, milestone filtering for quiet mode. diff --git a/docs/src/content/blog/011-skills-system-learning-from-work.md b/docs/src/content/blog/011-skills-system-learning-from-work.md index 41ad5f56d..e9bc3d77b 100644 --- a/docs/src/content/blog/011-skills-system-learning-from-work.md +++ b/docs/src/content/blog/011-skills-system-learning-from-work.md @@ -25,7 +25,7 @@ That changed in v0.2.0 with the skills system. ## How It Works -Skills are **earned domain knowledge** that changes how agents approach work. After completing a task, agents extract reusable patterns and write them to `.squad/skills/{skill-name}/SKILL.md` using the Anthropic SKILL.md open standard. +Skills are **earned domain knowledge** that changes how agents approach work. After completing a task, agents extract reusable patterns and write them to `.copilot/skills/{skill-name}/SKILL.md` using the Anthropic SKILL.md open standard. Three categories exist: @@ -71,15 +71,15 @@ Verbal designed the skill lifecycle (acquisition → reinforcement → correctio Kujan validated that: - Skills stored separately from history enable clean export (history is project-specific, skills are portable) - The `store_memory` tool (Anthropic's skill persistence API) was the wrong model for Squad — filesystem persistence is Squad's architecture -- File paths in agent charters are frozen API contracts (changing `.squad/agents/{name}/skills.md` to `.squad/skills/` requires migration) +- File paths in agent charters are frozen API contracts (changing `.squad/agents/{name}/skills.md` to `.copilot/skills/` requires migration) ### Open Standard Adoption (2026-02-09) -Squad adopted the Agent Skills Open Standard (agentskills.io) and the SKILL.md YAML frontmatter format. Directory structure changed from per-agent files to a flat `.squad/skills/` directory. Skills are **team knowledge**, not agent-specific. +Squad adopted the Agent Skills Open Standard (agentskills.io) and the SKILL.md YAML frontmatter format. Directory structure changed from per-agent files to a flat `.copilot/skills/` directory. Skills are **team knowledge**, not agent-specific. The final decision (Verbal, 2026-02-09): -> _"Skills in `.squad/skills/{skill-name}/SKILL.md`. Coordinator injects `` XML for progressive disclosure (~50 tokens per skill at discovery). Skills portable beyond Squad — works in Claude Code, Copilot, any compliant tool."_ +> _"Skills in `.copilot/skills/{skill-name}/SKILL.md`. Coordinator injects `` XML for progressive disclosure (~50 tokens per skill at discovery). Skills portable beyond Squad — works in Claude Code, Copilot, any compliant tool."_ ## Technical Details @@ -113,9 +113,9 @@ What to avoid ### Discovery and Application -1. **Coordinator reads** `.squad/skills/` directory at session start +1. **Coordinator reads** `.copilot/skills/` directory at session start 2. **Progressive disclosure**: Only skill names and descriptions are loaded initially (~50 tokens per skill) -3. **Agent spawns with context**: Spawn template says "check `.squad/skills/{skill-name}/SKILL.md` if relevant" +3. **Agent spawns with context**: Spawn template says "check `.copilot/skills/{skill-name}/SKILL.md` if relevant" 4. **Agent reads full skill** when applicable to the task 5. **Agent applies pattern** from the skill 6. **Agent updates or extracts**: Bump confidence if validated, extract new skill if pattern discovered @@ -136,7 +136,7 @@ Skills travel via the `squad-export.json` manifest: ``` When imported into a new squad: -- Skill files are written to `.squad/skills/{skill-name}/SKILL.md` +- Skill files are written to `.copilot/skills/{skill-name}/SKILL.md` - Agents read them before first spawn - Team arrives at the new project already competent @@ -161,7 +161,7 @@ squad plugin marketplace add github:squad-plugins/official squad plugin install aws-deployment-patterns ``` -The skill appears at `.squad/skills/aws-deployment-patterns/SKILL.md` and agents apply it on their next spawn. +The skill appears at `.copilot/skills/aws-deployment-patterns/SKILL.md` and agents apply it on their next spawn. ### Cross-Tool Compatibility @@ -179,7 +179,7 @@ Users aren't locked into Squad. The knowledge is portable. As of v0.2.0: - **2 built-in skills** shipped with Squad (`squad-conventions`, `label-driven-workflow`) -- **15+ learned skills** in Squad's own `.squad/skills/` directory earned during dogfooding (GitHub Actions automation, Jekyll site deployment, Jest testing patterns, MCP tool discovery) +- **15+ learned skills** in Squad's own `.copilot/skills/` directory earned during dogfooding (GitHub Actions automation, Jekyll site deployment, Jest testing patterns, MCP tool discovery) - **0 npm dependencies** — pure markdown with YAML frontmatter - **~50 tokens per skill** at discovery (name + description only) - **Full content (~500-2000 tokens)** loaded only when agent needs it diff --git a/docs/src/content/blog/014-wave-1-otel-and-aspire.md b/docs/src/content/blog/014-wave-1-otel-and-aspire.md index 7a447380b..0eaecea3a 100644 --- a/docs/src/content/blog/014-wave-1-otel-and-aspire.md +++ b/docs/src/content/blog/014-wave-1-otel-and-aspire.md @@ -47,7 +47,7 @@ Every file mutation by every agent becomes a traceable event. In the Aspire dash ## Aspire Dashboard Integration -The `squad aspire` command (#265) wires Squad's OTLP exporter to a .NET Aspire dashboard. Aspire gives you: +The `squad aspire` command (#265) wires Squad's OTLP exporter to a Aspire dashboard. Aspire gives you: - **Trace waterfall** — See agent spawns, tool calls, and file writes as a timeline - **Metrics** — Agent spawn counts, tool call durations, event bus throughput diff --git a/docs/src/content/blog/022-welcome-to-the-new-squad.md b/docs/src/content/blog/022-welcome-to-the-new-squad.md index ee2ff28ec..2fcbb1029 100644 --- a/docs/src/content/blog/022-welcome-to-the-new-squad.md +++ b/docs/src/content/blog/022-welcome-to-the-new-squad.md @@ -238,7 +238,7 @@ squad upgrade This updates Squad-owned files (templates, CLI scripts, SDK code) but never touches: - `.squad/agents/**` (your agent charters) - `.squad/decisions.md` (architectural decisions) -- `.squad/skills/**` (learned patterns) +- `.copilot/skills/**` (learned patterns) - `.squad/history/**` (session logs) Your team's memory is sacred. Upgrades respect that. @@ -486,7 +486,7 @@ npm install && npm start **What you'll see:** ``` -📚 Loading skills from .squad/skills/ +📚 Loading skills from .copilot/skills/ ✅ Loaded: TypeScript Patterns (confidence: high 🟢) Triggers: typescript, types, generics @@ -671,7 +671,7 @@ Squad exports traces and metrics via OpenTelemetry. Three-layer API: - Cost accumulation in real time **Export to:** -- .NET Aspire Dashboard (`squad aspire`) +- Aspire Dashboard (`squad aspire`) - Jaeger - Zipkin - Any OTLP-compatible backend diff --git a/docs/src/content/blog/026-whats-new-ado-comms-subsquads.md b/docs/src/content/blog/026-whats-new-ado-comms-subsquads.md index 30cf45d3d..8c0e22323 100644 --- a/docs/src/content/blog/026-whats-new-ado-comms-subsquads.md +++ b/docs/src/content/blog/026-whats-new-ado-comms-subsquads.md @@ -111,7 +111,7 @@ Every platform adapter went through a community-driven 5-model security review ( - Runtime `Module._resolveFilename` intercept for Node 24+ ESM compatibility - 5-layer secret defense architecture -- `.squad/skills/secret-handling/SKILL.md` team reference +- `.copilot/skills/secret-handling/SKILL.md` team reference - 59 TDD security hook tests - Charter hardening for Trejo (Git & Release) and Drucker (CI/CD) diff --git a/docs/src/content/docs/concepts/memory-and-knowledge.md b/docs/src/content/docs/concepts/memory-and-knowledge.md index e0cad2ff8..f5fda72c6 100644 --- a/docs/src/content/docs/concepts/memory-and-knowledge.md +++ b/docs/src/content/docs/concepts/memory-and-knowledge.md @@ -29,7 +29,7 @@ Memory lives in three layers, each serving a different purpose: ```mermaid graph TD - A["Skills Layer
.squad/skills/{name}/SKILL.md
Reusable patterns • Portable
How to set up CI with GitHub Actions"] + A["Skills Layer
.copilot/skills/{name}/SKILL.md
Reusable patterns • Portable
How to set up CI with GitHub Actions"] B["Shared Decisions Layer
.squad/decisions.md
Team-wide rules • Every agent reads
Use PostgreSQL • No Friday deploys"] C["Personal History Layer
.squad/agents/{name}/history.md
Per-agent memory • Only reads own
Auth uses JWT • Config in src/config/"] @@ -173,7 +173,7 @@ Directives are context-aware guidelines, not hard constraints. If an agent viola ## Skills -Skills are reusable knowledge files that live at `.squad/skills/{skill-name}/SKILL.md`. Unlike decisions (project policies like "use PostgreSQL"), skills are transferable techniques ("how to set up CI with GitHub Actions"). +Skills are reusable knowledge files that live at `.copilot/skills/{skill-name}/SKILL.md`. Unlike decisions (project policies like "use PostgreSQL"), skills are transferable techniques ("how to set up CI with GitHub Actions"). **All agents can read any skill.** Skills are team-wide knowledge, not per-agent. @@ -227,7 +227,7 @@ Not all knowledge in `.squad/` lasts forever. When files grow large, Squad compa Knowledge that needs to survive compaction belongs in **skills**. Reusable patterns, code conventions, and technical techniques live here because they grow without limits. Team rules and preferences go in directives (stored in `decisions.md`) — they persist through compaction cycles because directives are preserved when decisions get summarized. -> 💡 **Where to store permanent knowledge:** Put reusable patterns and techniques in `.squad/skills/`. Put team rules and preferences in directives (they persist in `decisions.md`). For org-wide knowledge that multiple teams need, use [upstream inheritance](/features/upstream-inheritance) to share a skills library. +> 💡 **Where to store permanent knowledge:** Put reusable patterns and techniques in `.copilot/skills/`. Put team rules and preferences in directives (they persist in `decisions.md`). For org-wide knowledge that multiple teams need, use [upstream inheritance](/features/upstream-inheritance) to share a skills library. --- diff --git a/docs/src/content/docs/concepts/portability.md b/docs/src/content/docs/concepts/portability.md index 457109c97..c03ce760f 100644 --- a/docs/src/content/docs/concepts/portability.md +++ b/docs/src/content/docs/concepts/portability.md @@ -64,7 +64,7 @@ squad export --out ./backups/team.json # custom path | Agent charters | ✅ | | Agent histories | ✅ (split into portable vs project-specific) | | Casting state | ✅ | -| Skills | ✅ All earned skills from `.squad/skills/` | +| Skills | ✅ All earned skills from `.copilot/skills/` | | Decisions | ✅ | Skills are fully portable — they export and import with perfect fidelity. @@ -101,7 +101,7 @@ Plugins are community-curated bundles of agent templates, skills, and best pract ### What's in a Plugin - **Agent templates** — specialized role charters (e.g., "AWS DevOps", "Python Data Science") -- **Skills** — reusable `.squad/skills/SKILL.md` files +- **Skills** — reusable `.copilot/skills/SKILL.md` files - **Instructions** — `decisions.md` snippets for conventions and routing - **Sample prompts** — ready-to-use prompts that activate plugin capabilities @@ -126,7 +126,7 @@ Or use the command: /plugin install awesome-copilot/react-component-library ``` -Squad downloads the bundle, merges agent templates into `.squad/agents/`, adds skills to `.squad/skills/`, updates `decisions.md`, and seeds agents with the new knowledge. +Squad downloads the bundle, merges agent templates into `.squad/agents/`, adds skills to `.copilot/skills/`, updates `decisions.md`, and seeds agents with the new knowledge. ### Managing Marketplaces @@ -175,7 +175,7 @@ Declare external Squad sources and automatically inherit their context at sessio ### What Gets Inherited -- **Skills** — all `.squad/skills/*/SKILL.md` files +- **Skills** — all `.copilot/skills/*/SKILL.md` files - **Decisions** — `.squad/decisions.md` - **Wisdom** — `.squad/identity/wisdom.md` - **Casting Policy** — `.squad/casting/policy.json` diff --git a/docs/src/content/docs/concepts/your-team.md b/docs/src/content/docs/concepts/your-team.md index 78b6ce188..e144b2485 100644 --- a/docs/src/content/docs/concepts/your-team.md +++ b/docs/src/content/docs/concepts/your-team.md @@ -109,7 +109,7 @@ The coordinator routes work automatically using three strategies. First match wi |----------|-------------|---------| | **Named** | You say who does it | `"Fenster, fix the login bug"` | | **Domain** | Pattern matching in `.squad/routing.md` | `src/api/**` → Backend | -| **Skill-aware** | Capability check in `.squad/skills/` | Auth expertise → Backend or Lead | +| **Skill-aware** | Capability check in `.copilot/skills/` | Auth expertise → Backend or Lead | **Routing priority:** Named > Domain > Skill-aware. If nothing matches, the Lead triages. diff --git a/docs/src/content/docs/cookbook/recipes.md b/docs/src/content/docs/cookbook/recipes.md index d1f4f13ab..5ebfc2e7c 100644 --- a/docs/src/content/docs/cookbook/recipes.md +++ b/docs/src/content/docs/cookbook/recipes.md @@ -35,7 +35,7 @@ Each agent gets its own 200K context window. Routing ensures only the right agen > "Export skills from my React project and import them into this new project" -Import one full squad, then cherry-pick skills from others. Skills are standalone markdown files — just copy them into `.squad/skills/`. Best practice: merge knowledge, don't run parallel squads. +Import one full squad, then cherry-pick skills from others. Skills are standalone markdown files — just copy them into `.copilot/skills/`. Best practice: merge knowledge, don't run parallel squads. ### Moving a Team Between Repos diff --git a/docs/src/content/docs/features/cleanup.md b/docs/src/content/docs/features/cleanup.md index a19be7234..8830bff9c 100644 --- a/docs/src/content/docs/features/cleanup.md +++ b/docs/src/content/docs/features/cleanup.md @@ -107,7 +107,7 @@ Run cleanup every round (aggressive), keep 14 days: ## What Cleanup Does NOT Touch -- Earned skills in `.squad/skills/` — never deleted +- Earned skills in `.copilot/skills/` — never deleted - Decision log in `.squad/decisions/log.md` — never deleted - Active session data - Router state, team config, and other core Squad files diff --git a/docs/src/content/docs/features/consult-mode.md b/docs/src/content/docs/features/consult-mode.md index caaf2edf5..4c0b8e3af 100644 --- a/docs/src/content/docs/features/consult-mode.md +++ b/docs/src/content/docs/features/consult-mode.md @@ -9,7 +9,7 @@ Consult mode lets you bring your personal squad to projects you don't own — OS ## The Problem -You have a personal squad at your global path (e.g., `~/.config/squad/.squad` on Linux) with agents, skills, and decisions refined over time. When you contribute to someone else's project, you face a dilemma: +You have a personal squad at your global path (e.g., `~/Library/Application Support/squad/personal-squad` on macOS, `~/.config/squad/personal-squad` on Linux) with agents, skills, and decisions refined over time. When you contribute to someone else's project, you face a dilemma: - **Pollute the project?** Running `squad init` creates a `.squad/` folder they didn't ask for - **Pollute your squad?** Project-specific knowledge bleeds into your global squad diff --git a/docs/src/content/docs/features/context-hygiene.md b/docs/src/content/docs/features/context-hygiene.md index bef518244..9850e863f 100644 --- a/docs/src/content/docs/features/context-hygiene.md +++ b/docs/src/content/docs/features/context-hygiene.md @@ -62,7 +62,7 @@ In the interactive shell, use `/compact` for the same effect. When you tell the team to "reskill," agents: -1. Review existing skill files in `.squad/skills/` +1. Review existing skill files in `.copilot/skills/` 2. Validate that documented patterns still apply 3. Look for new reusable patterns from recent work 4. Update skill confidence levels based on current evidence diff --git a/docs/src/content/docs/features/coordinator-as-agent-export.md b/docs/src/content/docs/features/coordinator-as-agent-export.md new file mode 100644 index 000000000..8ad8179c5 --- /dev/null +++ b/docs/src/content/docs/features/coordinator-as-agent-export.md @@ -0,0 +1,237 @@ +--- +title: Coordinator-as-Agent Export +description: Compile your squad's coordinator into a repo-native Copilot custom agent file with squad export agent. +--- + +# Coordinator-as-Agent Export + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +**Try this to generate a coordinator agent:** +```bash +squad export agent +``` + +**Try this for CI drift detection:** +```bash +squad export agent --check +``` + +**Try this for live development:** +```bash +squad export agent --watch +``` + +`squad export agent` compiles your `.squad/` state — team roster, routing rules, ceremony triggers, agent charters — into a single repository-native Copilot custom agent at `.github/agents/squad.md`. The generated file works across **every** Copilot surface (CLI, VS Code, GitHub Desktop, github.com) without requiring the Squad runtime installed. + +This is the "ship Squad as a portable agent" path. Use it when you want collaborators or downstream repos to get the benefit of your squad's setup with **zero install** — they just check out the repo and the agent is available. + +--- + +## When to use it + +| Scenario | Use `squad export agent`? | +|----------|-----------------------| +| You want collaborators to use your team's coordinator without installing the CLI | ✅ Yes | +| You want a portable, version-controlled snapshot of your coordinator behavior | ✅ Yes | +| You want CI to enforce that `.github/agents/squad.md` stays in sync with `.squad/` | ✅ Yes — use `--check` | +| You need full Squad runtime features (Scribe, Ralph, MCP state tools, ceremonies) | ❌ No — install the CLI | +| You want to share state (decisions, history) not just coordinator behavior | ❌ No — use [`squad export`](/squad/docs/features/export-import/) (snapshot mode) | + +The exported coordinator agent has access to the team's roster and routing logic but does NOT include the live Squad runtime. Sub-agents in the exported coordinator will be dispatched via Copilot's native `task` tool, not via Squad's full spawn machinery. + +--- + +## Commands + +### `squad export agent` + +Generate or update `.github/agents/squad.md` from your current `.squad/` state: + +```bash +$ squad export agent + +🔧 Compiling coordinator agent... + - Read team.md (8 members) + - Read routing.md (24 work-type entries) + - Read ceremonies.md (3 ceremonies) + - Loaded 8 agent charters + - Compiled prompt: 12,847 tokens (under 14k soft budget — full mode) + - Wrote .github/agents/squad.md (38,294 bytes) + +✓ Coordinator exported to .github/agents/squad.md +``` + +The output is a self-contained Copilot custom-agent file with proper YAML frontmatter and a compiled coordinator prompt. Anyone in the repo can now run `copilot --agent squad` and get the coordinator's behavior. + +### `squad export agent --watch` + +Re-export on every change to `.squad/`. Useful during active team development when you want the exported agent file to track your edits: + +```bash +$ squad export agent --watch +👀 Watching .squad/ for changes... +✓ .github/agents/squad.md up to date + +[edit .squad/routing.md] +🔄 .squad/routing.md changed — re-exporting... +✓ .github/agents/squad.md updated (38,401 bytes) +``` + +Press `Ctrl+C` to stop. + +### `squad export agent --check` + +Verify that `.github/agents/squad.md` is in sync with the current `.squad/` state. Exits with non-zero if drift is detected. Use this in CI to enforce "if you change `.squad/`, you must re-run `squad export agent`": + +```bash +$ squad export agent --check + +✓ .github/agents/squad.md is up to date + +# Or, on drift: + +✗ Drift detected: + .squad/routing.md changed but .github/agents/squad.md not regenerated. + Run 'squad export agent' to update. +exit 1 +``` + +### `squad export agent --dry-run` + +Preview what would be written without actually writing the file: + +```bash +$ squad export agent --dry-run + +🔍 DRY RUN — would write to .github/agents/squad.md: + Size: 38,294 bytes + Prompt tokens: ~12,847 + Mode: full + Frontmatter: + name: squad + description: ... + No changes made. +``` + +### `squad export agent --compact` + +Force compact mode even if the prompt fits within the soft budget. Useful for keeping the generated file lean intentionally: + +```bash +squad export agent --compact +``` + +Compact mode omits some optional sections (extended examples, on-demand reference pointers) and is the default when the prompt would otherwise exceed the soft token budget. + +--- + +## Token budget modes + +The exporter adapts automatically to your team size: + +| Mode | Trigger | What's in the prompt | +|------|---------|---------------------| +| **Full** | ≤8 members AND prompt < 14k tokens | Full charters inlined, all routing tables, complete ceremony definitions | +| **Compact** | Prompt 14k–20k tokens OR `--compact` flag | Condensed charters, abbreviated examples, on-demand references | +| **Lazy-load** | >8 members OR roster > 3k tokens | Coordinator instructed to load charters on demand at dispatch time | +| **(fails)** | Prompt > 20k hard budget | Fails with diagnostics — suggests removing rarely-used members or splitting the squad | + +These thresholds protect against generating a coordinator file that's too large for the LLM's context budget. If you hit the hard limit, the exporter prints actionable diagnostics: + +``` +✗ Coordinator prompt exceeds hard budget (22,841 / 20,000 tokens). + Top contributors: + - 8 large agent charters (avg 1,800 tokens each) + - Routing table: 24 entries + + Suggestions: + - Remove rarely-spawned members from .squad/team.md + - Trim agent charter narrative sections (target: 1,500 tokens/charter) + - Split into multiple squads (see Multiple Squads docs) + - Use --compact to drop ~2k tokens +``` + +--- + +## Safety guarantees + +The exporter is conservative about what it writes: + +- **Won't overwrite user-owned agent files.** If `.github/agents/squad.md` exists and lacks the generated-file marker comment header, the export fails unless you pass `--force`. +- **Detects legacy `squad.agent.md` collisions.** The classic Squad CLI installation puts the coordinator at `.github/agents/squad.agent.md`. The export warns if both files would exist, and `squad init`/`squad upgrade` skip writing `squad.agent.md` when an exported `squad.md` is present. +- **Generated files are marked.** The output starts with `` so the file is unambiguous. +- **`--check` mode never mutates.** Safe to run on every CI build. + +--- + +## CI integration + +The pattern most teams use: + +```yaml +# .github/workflows/squad-drift-check.yml +name: Squad drift check +on: + pull_request: + paths: ['.squad/**', '.github/agents/squad.md'] +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: { node-version: '22' } + - run: npm install -g @bradygaster/squad-cli + - run: squad export agent --check +``` + +If anyone changes `.squad/` without re-exporting, the PR fails CI with a clear message. + +--- + +## What gets compiled into the exported agent + +The exporter loads these sources from your `.squad/`: + +| File | Used for | +|------|----------| +| `.squad/team.md` | Roster — agent names, roles, charter paths | +| `.squad/routing.md` | Work-type → agent and module-ownership mappings | +| `.squad/ceremonies.md` | Auto-trigger definitions for design review, retro, etc. | +| `.squad/config.json` | State backend selection, model preferences | +| `.squad/agents/{name}/charter.md` | Per-agent role definitions (inlined or referenced based on budget mode) | + +These get rendered into the output as: + +- **YAML frontmatter** — `name`, `description`, `model`, `tools` declarations +- **Coordinator prompt body** — dispatch rules, routing table, ceremony triggers, agent identities, response mode selection +- **On-demand reference markers** — pointers to charters in lazy-load mode + +What's NOT compiled in: +- Decisions ledger (use snapshot export to share state) +- Agent histories (personal learnings, owner-only) +- Skills (live as separate files in `.copilot/skills/`) +- Casting state (registry, history) + +--- + +## Architecture (for the curious) + +The export pipeline lives at `packages/squad-sdk/src/repo-native/`: + +1. **Context loader** — parses `.squad/` files into a typed IR +2. **Prompt compiler** — renders the coordinator prompt with budget enforcement +3. **Frontmatter renderer** — emits valid custom-agent YAML +4. **File writer** — handles safety checks and atomic write + +You can use the SDK module directly if you want to embed coordinator export into your own tooling — see `@bradygaster/squad-sdk/repo-native`. + +--- + +## See also + +- [Export & Import](/squad/docs/features/export-import/) — full state snapshots (different from this) +- [Self Upgrade](/squad/docs/features/self-upgrade/) — keeping the CLI itself updated +- [Multiple Squads](/squad/docs/scenarios/multiple-squads/) — when one team gets too large +- [Team Setup](/squad/docs/features/team-setup/) — composing the team that gets exported diff --git a/docs/src/content/docs/features/copilot-coding-agent.md b/docs/src/content/docs/features/copilot-coding-agent.md index f9b10bba8..095819c68 100644 --- a/docs/src/content/docs/features/copilot-coding-agent.md +++ b/docs/src/content/docs/features/copilot-coding-agent.md @@ -46,7 +46,7 @@ gh issue edit --add-label "squad:copilot" Say something like: - **"I want to add copilot to the squad"** -- **"hire copilot to the squad"** +- **"cast copilot to the squad"** - **"add team member copilot"** The coordinator will add @copilot to the roster and ask about auto-assign. diff --git a/docs/src/content/docs/features/cross-squad-discover.md b/docs/src/content/docs/features/cross-squad-discover.md new file mode 100644 index 000000000..6912a14e0 --- /dev/null +++ b/docs/src/content/docs/features/cross-squad-discover.md @@ -0,0 +1,158 @@ +--- +title: Cross-Squad Discover & Delegate +description: Discover other squads across repository boundaries and delegate work to them via squad discover and squad delegate. +--- + +# Cross-Squad Discover & Delegate + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +**Try this to see what squads you can reach:** +```bash +squad discover +``` + +**Try this to send work to another squad:** +```bash +squad delegate platform-squad "Add monitoring dashboard for the auth service" +``` + +When you have multiple Squad-enabled repositories — a platform squad, a frontend squad, a data squad — you often need to ask the other squad to do something for you. Cross-squad orchestration lets a squad **discover** other squads and **delegate** issues to them, with proper labels and contact info, so the right team picks it up automatically. + +Both commands work via GitHub issues — no shared infrastructure required. Each squad's manifest declares what it accepts and how to reach it. + +--- + +## How it works + +Each squad publishes a `.squad/manifest.json` file declaring: +- Its name and capabilities (e.g., `kubernetes`, `helm`, `monitoring`) +- Its GitHub repo (the contact) +- The labels to apply to cross-squad issues +- What work types it accepts (`issues`, `prs`) +- Named skills it offers + +When you run `squad discover`, Squad reads manifests from: +- **Upstream** — repos declared in your `.squad/upstreams/` +- **Registry** — any registry sources configured +- **Local** — manifests inside the current repo + +When you run `squad delegate ""`, Squad finds the target manifest, creates a properly-labeled GitHub issue in its repo, and includes structured cross-squad metadata so the other squad's coordinator picks it up correctly. + +--- + +## Commands + +### `squad discover` + +List known squads and their capabilities: + +```bash +$ squad discover + +Discovered Squads (3): + + Name Capabilities Repo Accepts + ────────────── ────────────────────────── ────────────────────── ──────── + platform-squad kubernetes, helm, infra myorg/platform issues + frontend-squad react, design-system, ui myorg/web-app issues + data-squad etl, dbt, pipelines, ml myorg/data-platform issues, prs +``` + +If no squads are discovered, the output reminds you to configure upstreams or check that other repos have published `manifest.json` files. + +### `squad delegate ""` + +Create a cross-squad work request in another squad's repository: + +```bash +squad delegate platform-squad "Add Grafana dashboards for the auth service's p95 latency" +``` + +This creates an issue in `myorg/platform` titled `[cross-squad] Add Grafana dashboards...` with: +- Squad's discovered labels applied automatically +- A structured body with the originating repo, target squad, description, and acceptance criteria +- The created issue's URL printed to your terminal + +Required: +- The target squad's manifest must include the work type — `accepts: ["issues"]` for the default delegation flow +- GitHub CLI (`gh`) installed and authenticated with permission to create issues in the target repo + +--- + +## Publishing a manifest for your own squad + +To make your squad discoverable by others, create `.squad/manifest.json`: + +```json +{ + "name": "platform-squad", + "version": "1.0.0", + "description": "Infrastructure, Kubernetes, and platform services", + "capabilities": ["kubernetes", "helm", "monitoring", "infra"], + "contact": { + "repo": "myorg/platform", + "labels": ["cross-squad", "needs-triage"] + }, + "accepts": ["issues"], + "skills": ["k8s-deployment", "helm-chart-authoring", "prometheus-alerting"] +} +``` + +| Field | Required | Notes | +|-------|----------|-------| +| `name` | ✅ | Human-readable name (e.g., `platform-squad`). Used in `squad delegate `. | +| `version` | optional | Schema version for forward compatibility | +| `description` | optional | One-line summary shown in `discover` output | +| `capabilities` | ✅ | Tags other squads use to find you (`squad discover` filters here in future versions) | +| `contact.repo` | ✅ | GitHub repo in `owner/repo` format where issues get created | +| `contact.labels` | optional | Labels applied to every cross-squad issue (so your triage automation can find them) | +| `accepts` | ✅ | Work types: `["issues"]`, `["prs"]`, or both | +| `skills` | optional | Named skills your squad offers — informational today, filterable in future | + +Commit `manifest.json` to your repo's `.squad/` directory and push. Other squads that have your repo in their upstream list will pick it up on next `squad discover`. + +--- + +## What the delegated issue looks like + +When `squad delegate` creates an issue, it uses this structured body so the receiving squad's coordinator can recognize and route it correctly: + +```markdown +## Cross-Squad Work Request + +**From:** this repository +**To:** platform-squad (myorg/platform) + +### Description + +Add Grafana dashboards for the auth service's p95 latency + +### Acceptance Criteria + +- [ ] Work completed and verified +- [ ] Originating squad notified of completion + +*Created by squad cross-squad orchestration* +``` + +The receiving squad sees an issue with their `cross-squad` (and any custom) labels, structured metadata in the body, and clear acceptance criteria. + +--- + +## Limitations in v0.10 + +- **No automatic completion notification.** When the receiving squad closes the issue, the originating squad doesn't get notified back automatically. Today you watch the issue manually or via standard GitHub notifications. +- **Discovery is upstream-driven.** A squad has to know about another squad (via an upstream declaration) before `discover` can see it. There's no global registry. +- **No capability filtering on delegate.** `squad delegate ` requires the exact squad name. You can't say "delegate this to any squad that has the `kubernetes` capability" — yet. +- **PRs aren't supported in the default flow.** Even though manifests can declare `accepts: ["prs"]`, the v0.10 `delegate` command only creates issues. + +These are tracked for follow-up. For now, cross-squad orchestration is a useful but minimal MVP — it removes the "where do I file this?" friction and ensures the right team gets a properly-formatted request. + +--- + +## See also + +- [Distributed Mesh](/squad/docs/features/distributed-mesh/) — the broader cross-repo coordination architecture +- [Multiple Squads](/squad/docs/scenarios/multiple-squads/) — running several squads in one organization +- [Upstream Inheritance](/squad/docs/features/upstream-inheritance/) — how upstreams get discovered diff --git a/docs/src/content/docs/features/dual-mode-deployment.md b/docs/src/content/docs/features/dual-mode-deployment.md new file mode 100644 index 000000000..ca31f2752 --- /dev/null +++ b/docs/src/content/docs/features/dual-mode-deployment.md @@ -0,0 +1,132 @@ +--- +title: Dual-Mode Deployment — Pod-Aware Capabilities +description: Run Squad in either agent-per-node or squad-per-pod deployment modes with pod-specific machine capability manifests, controlled by SQUAD_POD_ID and SQUAD_DEPLOYMENT_MODE env vars. +--- + +# Dual-Mode Deployment — Pod-Aware Capabilities + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +Dual-mode deployment extends [Capability Routing](/squad/docs/features/capability-routing/) to support both classic single-machine setups and modern containerized/Kubernetes deployments where multiple Squad pods may share an organization's workload — each with potentially different machine capabilities. + +It introduces two environment variables and a pod-specific manifest lookup pattern so the same Squad config can run identically in either deployment shape. + +--- + +## The two deployment modes + +| Mode | What it means | Capability manifest | +|------|---------------|---------------------| +| **`agent-per-node`** (default) | One Squad instance per machine; the machine's capabilities are the squad's capabilities | `.squad/machine-capabilities.json` (shared) | +| **`squad-per-pod`** | Multiple Squad pods may run on different machines/containers, each with potentially different capabilities | `.squad/machine-capabilities-{podId}.json` (pod-specific) with fallback chain | + +Choose the mode via the `SQUAD_DEPLOYMENT_MODE` environment variable: + +```bash +# Classic single-machine setup (default) +export SQUAD_DEPLOYMENT_MODE=agent-per-node + +# Kubernetes / multi-pod setup +export SQUAD_DEPLOYMENT_MODE=squad-per-pod +export SQUAD_POD_ID=worker-1 +``` + +If neither is set, the SDK defaults to `agent-per-node` for backward compatibility. + +--- + +## Environment variables + +### `SQUAD_DEPLOYMENT_MODE` + +| Value | Behavior | +|-------|----------| +| `agent-per-node` | Single shared `machine-capabilities.json` | +| `squad-per-pod` | Pod-specific manifests with fallback chain | +| (unset) | Same as `agent-per-node` | + +### `SQUAD_POD_ID` + +Pod identifier used to construct the pod-specific manifest path. Required when `SQUAD_DEPLOYMENT_MODE=squad-per-pod`; ignored otherwise. + +```bash +SQUAD_POD_ID=worker-1 # → .squad/machine-capabilities-worker-1.json +SQUAD_POD_ID=gpu-pool-node-3 # → .squad/machine-capabilities-gpu-pool-node-3.json +``` + +--- + +## The fallback chain (squad-per-pod mode) + +When `SQUAD_DEPLOYMENT_MODE=squad-per-pod` AND `SQUAD_POD_ID` is set, the SDK looks up capabilities in this order: + +1. **`.squad/machine-capabilities-{podId}.json`** — pod-specific (highest priority) +2. **`.squad/machine-capabilities.json`** — shared fallback for capabilities that apply to all pods +3. **`~/.squad/machine-capabilities.json`** — user-home fallback (rarely useful in container deployments) +4. **`null`** — opt-out; capability routing falls back to label-only routing + +The first manifest that exists is loaded; the search stops there (no merging). If you need different pods to see different capability sets, give each its own pod-specific file. If you need a shared baseline plus pod-specific additions, merge at the deployment-config level (Helm, Kustomize, etc.) — the SDK doesn't merge automatically. + +--- + +## SDK programmatic access + +The new exports from `@bradygaster/squad-sdk/ralph/capabilities`: + +```typescript +import { + getDeploymentMode, + getPodId, + type DeploymentMode, +} from '@bradygaster/squad-sdk/ralph/capabilities'; + +const mode: DeploymentMode = getDeploymentMode(); // 'agent-per-node' | 'squad-per-pod' +const podId: string | undefined = getPodId(); // e.g. 'worker-1', or undefined +``` + +These are pure env-var readers. They don't cache or memoize — each call reads `process.env` directly so changes between reads are visible. + +--- + +## Typical Kubernetes deployment shape + +In a KEDA-scaled deployment (see [KEDA Scaling](/squad/docs/features/keda-scaling/)), each scaled pod gets a unique `SQUAD_POD_ID` from the pod's name or hash: + +```yaml +# Deployment env block +env: + - name: SQUAD_DEPLOYMENT_MODE + value: squad-per-pod + - name: SQUAD_POD_ID + valueFrom: + fieldRef: + fieldPath: metadata.name +``` + +The pod's mounted volume contains per-pod manifests baked in by the image build or pulled from a ConfigMap, e.g.: + +``` +/app/.squad/ +├── machine-capabilities.json # shared baseline (CPU, memory) +├── machine-capabilities-gpu-pool-node-1.json # extends baseline with GPU +├── machine-capabilities-gpu-pool-node-2.json # same shape +└── machine-capabilities-cpu-pool-node-1.json # no GPU declaration +``` + +Pods scheduled onto GPU nodes load a manifest declaring GPU capability; pods on CPU-only nodes get a manifest without GPU. Ralph's issue dispatcher routes `needs:gpu`-labeled work only to pods with the GPU capability. + +--- + +## Limitations + +- **No automatic pod discovery.** The SDK reads env vars to know who it is; it doesn't enumerate sibling pods or coordinate work distribution. That's the deployment orchestrator's job (KEDA, scheduler). +- **No central capability registry.** Pods don't publish their capabilities back to anything; each pod evaluates issues against its own loaded manifest independently. If you need a central view, your orchestrator must aggregate. +- **Manifest changes require redeploy or restart.** The fallback lookup happens on capability resolution; manifest content is read from disk each time but the manifest *path* is decided by env vars set at process start. + +--- + +## See also + +- [Capability Routing](/squad/docs/features/capability-routing/) — the broader machine-capability system +- [KEDA Scaling](/squad/docs/features/keda-scaling/) — autoscaling Squad pods on demand +- [Labels](/squad/docs/features/labels/) — `needs:*` label conventions used for capability matching diff --git a/docs/src/content/docs/features/error-recovery.md b/docs/src/content/docs/features/error-recovery.md new file mode 100644 index 000000000..e9ebea5ce --- /dev/null +++ b/docs/src/content/docs/features/error-recovery.md @@ -0,0 +1,95 @@ +--- +title: Error Recovery — Standard Failure Patterns +description: Built-in skill teaching agents to adapt when things fail — retry with backoff, fallback alternatives, diagnose-and-fix, and escalation patterns. +--- + +# Error Recovery — Standard Failure Patterns + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +The `error-recovery` skill teaches every squad agent to **adapt** when something fails, not just report the failure. It ships as a built-in skill at `.copilot/skills/error-recovery/SKILL.md` and is available to every spawned agent. + +Without this skill, agents tend to encounter a failure (CI test red, API timeout, missing dependency) and stop. With it, they apply standard patterns to diagnose, retry, or escalate the right way. + +--- + +## The five recovery patterns + +### 1. Retry with Backoff + +**When:** Transient failures — API timeouts, rate limits, network errors, temporary service unavailability. + +**Pattern:** +1. Wait briefly, then retry (start at 2s, double each attempt) +2. Maximum 3 retries before escalating +3. Log each attempt with the error received + +**Example:** API call returns `429 Too Many Requests` → wait 2s → retry → wait 4s → retry → wait 8s → retry → escalate if still failing. + +### 2. Fallback Alternatives + +**When:** Primary tool or approach fails and an alternative exists. + +**Pattern:** +1. Attempt primary approach +2. On failure, identify alternative tool/method +3. Try the alternative with the same intent +4. Document which alternative was used and why + +**Example:** Primary CLI tool fails → fall back to direct API call for the same operation. Or: `gh pr comment` rate-limited → fall back to `gh api -X POST .../issues/{n}/comments`. + +### 3. Diagnose-and-Fix + +**When:** Build failures, test failures, linting errors — structured errors with actionable output. + +**Pattern:** +1. Read the full error output carefully (not just the last line) +2. Identify the root cause from error messages +3. Attempt a targeted fix +4. Re-run to verify the fix +5. If 3 fix attempts fail, escalate with a diagnostic summary + +**Example:** TypeScript build fails with `Cannot find module '@x/y'` → check `package.json`, run `npm install`, re-run build. + +### 4. Reframe-and-Retry + +**When:** The approach itself is wrong (not just the implementation). User feedback like *"that won't work because..."* or *"try a different way"*. + +**Pattern:** +1. Stop the current approach immediately +2. Re-read the original task description +3. Identify what assumption was wrong +4. Propose 2 alternative approaches before picking one +5. Get user confirmation if the cost of being wrong again is high + +### 5. Escalation + +**When:** Three attempts have failed, OR the failure is outside the agent's domain, OR fixing it would violate a team decision. + +**Pattern:** +1. Stop attempting fixes +2. Summarize: what was tried, what failed, what's known +3. Surface to coordinator with a clear ask (*"need lead's call on architecture"* vs. *"need human approval"* vs. *"need access to X system"*) +4. Document the escalation in `decisions/inbox/` if it's a recurring pattern + +--- + +## When NOT to apply these patterns + +- **Don't retry on user-input errors.** If the user typed `gh repo create my-typo`, don't retry with `my-typoo`. Surface and ask. +- **Don't fall back silently on security-sensitive operations.** If `git push origin main` fails because of branch protection, do NOT fall back to `--force`. +- **Don't escalate without context.** *"It failed"* isn't an escalation; *"three attempts, each with `EACCES`, suggests user lacks write to `.squad/`, recommend chmod or different storage path"* is. + +--- + +## Integration with Reviewer Rejection Protocol + +When the failure is a Reviewer rejection (a Reviewer agent rejects an artifact), the [Reviewer Rejection Protocol](/squad/docs/features/reviewer-protocol/) takes precedence. The original author is locked out and a different agent must own the revision. Error-recovery patterns apply within that constraint — the revision agent can use retry/fallback/diagnose patterns freely. + +--- + +## See also + +- [Reflect](/squad/docs/features/reflect/) — learning from corrections +- [Reviewer Protocol](/squad/docs/features/reviewer-protocol/) — when a Reviewer rejects work +- [Skills](/squad/docs/features/skills/) — how built-in skills work diff --git a/docs/src/content/docs/features/export-import.md b/docs/src/content/docs/features/export-import.md index 7e739941f..789cdf316 100644 --- a/docs/src/content/docs/features/export-import.md +++ b/docs/src/content/docs/features/export-import.md @@ -31,6 +31,28 @@ Creates `squad-export.json` in the current directory — a portable snapshot of squad export --out ./backups/my-team.json ``` +### Push directly to a GitHub repository + +Instead of writing to a local file, you can push the export straight to a GitHub repo via the GitHub Contents API. This is the easiest way to back up your team to a private repo or share it with collaborators without sending a file. + +```bash +# Export to a GitHub repo (uses default branch) +squad export --repo myorg/squad-backups + +# Export to a specific branch +squad export --repo myorg/squad-backups --branch nightly +``` + +Requirements: +- GitHub CLI (`gh`) installed and authenticated with permission to push to the target repo +- The repo must exist (the export does NOT create it) + +The export lands at the repo root as `squad-export.json` by default. Combine with `--out` to control the filename inside the repo: + +```bash +squad export --repo myorg/squad-backups --out my-team-2026-06-11.json +``` + ### What's included | Data | Included | @@ -41,7 +63,7 @@ squad export --out ./backups/my-team.json | **Skills** | ✅ **All earned skills export with the team** | | Decisions | ✅ | -> **Skills are portable**: When you export a team, all earned skills from `.squad/skills/` are included in the JSON manifest. After importing, skills are immediately available to all agents — no loss of knowledge. +> **Skills are portable**: When you export a team, all earned skills from `.copilot/skills/` are included in the JSON manifest. After importing, skills are immediately available to all agents — no loss of knowledge. --- @@ -53,6 +75,25 @@ squad import squad-export.json Imports the snapshot into the current repo's `.squad/` directory. +### Pull directly from a GitHub repository + +You can import a snapshot directly from a GitHub repo without downloading the file first: + +```bash +# Import from default branch of a repo +squad import --repo myorg/squad-backups + +# Import a specific filename or branch +squad import --repo myorg/squad-backups --branch nightly +squad import --repo myorg/squad-backups --out my-team-2026-06-11.json +``` + +Requirements: +- GitHub CLI (`gh`) installed and authenticated with read access to the source repo +- The export file must exist at the named path in the repo (default: `squad-export.json` at repo root) + +Use `--force` together with `--repo` for the same archive-then-replace behavior as the file-based import. + ### Collision detection If `.squad/` already exists, Squad warns you and stops. To archive the existing team and replace it: diff --git a/docs/src/content/docs/features/external-state.md b/docs/src/content/docs/features/external-state.md index 3a2f592a6..925b0e9a4 100644 --- a/docs/src/content/docs/features/external-state.md +++ b/docs/src/content/docs/features/external-state.md @@ -54,7 +54,7 @@ If you commit `.squad/` to preserve it, every branch includes squad state in PRs ```diff + .squad/decisions/log.md -+ .squad/skills/ci-setup/SKILL.md ++ .copilot/skills/ci-setup/SKILL.md + .squad/team.md ``` diff --git a/docs/src/content/docs/features/fleet-dispatch.md b/docs/src/content/docs/features/fleet-dispatch.md new file mode 100644 index 000000000..cd197d75d --- /dev/null +++ b/docs/src/content/docs/features/fleet-dispatch.md @@ -0,0 +1,132 @@ +--- +title: Fleet Dispatch — Parallel Issue Triage +description: Hybrid dispatch mode for squad watch that batches read-heavy issues into a single Copilot /fleet session for 2.9x faster parallel analysis. +--- + +# Fleet Dispatch — Parallel Issue Triage + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +**Try this for parallel read-heavy issue triage:** +```bash +squad watch --execute --dispatch-mode fleet +``` + +**Try this for mixed read + write workloads:** +```bash +squad watch --execute --dispatch-mode hybrid +``` + +Fleet Dispatch enables `squad watch --execute` to batch **read-heavy issues** (research, review, audit, triage) into a single Copilot CLI `/fleet` session that analyzes them in parallel tracks. The published measurement: **2.9× faster** than sequential dispatch for read-heavy workloads. + +It's a `WatchCapability` that runs in the `post-execute` phase of the watch loop, so it composes with the existing per-issue dispatch logic rather than replacing it. + +--- + +## Three dispatch modes + +| Mode | What gets parallelized | Best for | +|------|------------------------|----------| +| **`sequential`** (default) | One issue at a time, full agent spawn each | Mixed workloads, debugging | +| **`fleet`** | All issues batched into one `/fleet` Copilot session, parallel analysis tracks | Pure triage/review rounds where all issues are read-only | +| **`hybrid`** | Read-heavy issues go to fleet; write-heavy issues go sequential | Realistic backlogs with both kinds | + +`hybrid` is the recommended mode for most teams — it gets the speedup on the analysis-heavy issues without trying to fleet-dispatch issues that need to write code or modify state. + +--- + +## What counts as "read-heavy" + +The fleet-dispatch capability classifies issues using the same `classifyIssue` logic used elsewhere in `squad watch`. Read-heavy classification is based on labels and title keywords: + +- **Labels:** `triage`, `review`, `audit`, `analyze`, `research`, `investigate`, `discuss`, `question` +- **Title keywords:** *"review"*, *"audit"*, *"analyze"*, *"investigate"*, *"why does"*, *"how does"* + +Anything that touches code, files, or external systems is **write-heavy** and stays in sequential dispatch — even in `hybrid` mode. + +--- + +## How a fleet round works + +When `squad watch` decides to dispatch (work items present, no rate-limit hold), and `dispatchMode` is `fleet` or `hybrid`: + +1. Watch's executor calls `findExecutableIssues` to get the work batch +2. FleetDispatch capability runs in `post-execute` phase +3. Read-heavy issues are filtered out of the sequential queue +4. A multi-track `/fleet` prompt is built — one track per issue +5. Each track names the appropriate agent (from the roster + labels) and instructs it to: + - Read the issue body + - Analyze, assess urgency (P0/P1/P2) + - Recommend next step + - Write findings as an issue comment + - **NOT** create branches or modify files +6. The prompt is sent as a single `copilot --fleet` invocation +7. Copilot runs all tracks in parallel, posts comments per issue, exits +8. Watch logs the fleet dispatch result and continues to its next round + +A typical fleet prompt looks like: + +``` +/fleet Execute these 6 read-only analysis tracks in parallel: + +Track 1 (PAO): Issue #421: Triage user-reported bug in login flow + Read the issue body. Analyze, assess urgency (P0/P1/P2), recommend next step. + Write findings as an issue comment. + Do NOT create branches or modify files. + +Track 2 (FIDO): Issue #428: Review PR #427's test coverage + ... + +Rules: All tracks READ-ONLY. Write findings as issue comments. Run in parallel. +``` + +--- + +## Measurement methodology + +The 2.9× speedup citation comes from comparing 6 read-heavy issues: + +- Sequential mode: 6 separate `copilot --agent {role}` invocations → ~18 minutes total (each ~3 min for cold-start + analysis) +- Fleet mode: 1 `copilot --fleet` invocation with 6 tracks → ~6 minutes total (one cold-start, parallel analysis tracks) + +Speedup is dominated by avoiding 5 cold-starts. It does NOT extend to write-heavy issues because Copilot's `/fleet` doesn't currently support parallel write operations safely (commits would conflict). + +--- + +## Configuration + +Set the dispatch mode in `.squad/watch-config.json`: + +```json +{ + "execute": true, + "dispatchMode": "hybrid", + "interval": 300, + "copilotFlags": "--allow-all-tools --no-color" +} +``` + +Or via CLI flag (overrides config): + +```bash +squad watch --execute --dispatch-mode fleet +squad watch --execute --dispatch-mode hybrid +squad watch --execute --dispatch-mode sequential +``` + +--- + +## Limitations + +- **Read-only only.** Fleet tracks must not modify files or create branches. The capability builds prompts that explicitly forbid this; if your team needs parallel write workflows, sequential dispatch remains the safer choice. +- **One track per issue.** No batching of multiple issues into one track — each issue gets its own analysis context. +- **Track count limit.** Copilot CLI `/fleet` has its own track-count ceiling. For backlogs with >10 read-heavy issues per round, the capability splits across multiple fleet calls. +- **Classification is conservative.** If `classifyIssue` is unsure, it defaults to write-heavy (sequential). Better to lose the speedup than to fleet-dispatch a write-heavy issue accidentally. + +--- + +## See also + +- [Ralph](/squad/docs/features/ralph/) — the watch loop's broader behavior +- [Capability Routing](/squad/docs/features/capability-routing/) — how watch matches work to agents +- [Rate Limiting](/squad/docs/features/rate-limiting/) — cooperative rate limiting (composes with fleet dispatch) diff --git a/docs/src/content/docs/features/mcp-frontmatter.md b/docs/src/content/docs/features/mcp-frontmatter.md new file mode 100644 index 000000000..ae2c41122 --- /dev/null +++ b/docs/src/content/docs/features/mcp-frontmatter.md @@ -0,0 +1,102 @@ +--- +title: MCP Frontmatter — squad init --mcp-frontmatter +description: Write MCP server configuration directly into the Squad agent file's frontmatter instead of .copilot/mcp-config.json, for harnesses that read agent-level MCP config. +--- + +# MCP Frontmatter — `squad init --mcp-frontmatter` + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +**Try this when your agent harness reads frontmatter-level MCP config:** +```bash +squad init --mcp-frontmatter +``` + +By default, `squad init` writes MCP server configuration to two places: +- `.copilot/mcp-config.json` (workspace-level for Copilot CLI) +- `~/.copilot/mcp-config.json` (user-level, ensures `copilot -p` non-interactive mode also sees the MCP — see [#1247](https://github.com/bradygaster/squad/issues/1247)) + +The `--mcp-frontmatter` flag changes this: instead of writing JSON config files, MCP server declarations go directly into the YAML frontmatter of `.github/agents/squad.agent.md` (or `.github/agents/squad.md` if you've exported with [Coordinator-as-Agent Export](/squad/docs/features/coordinator-as-agent-export/)). + +--- + +## When to use it + +| Your setup | Use `--mcp-frontmatter`? | +|------------|-------------------------| +| Standard Copilot CLI users | ❌ No — default config files work fine | +| Custom agent harness that reads MCP from agent frontmatter | ✅ Yes | +| Building or distributing a Squad agent as a self-contained file | ✅ Yes — keeps MCP config inline with the agent | +| Some VS Code extensions / custom IDE plugins that prefer per-agent MCP declarations | ✅ Yes | + +If you're not sure, you don't need this flag. It's specifically for environments where the agent file itself is the source of truth for MCP configuration. + +--- + +## What the output looks like + +Without `--mcp-frontmatter` (default), the agent file frontmatter is: + +```yaml +--- +name: squad +description: Squad coordinator +model: claude-opus-4.5 +tools: ["*"] +--- +``` + +And `.copilot/mcp-config.json` separately contains: + +```json +{ + "mcpServers": { + "squad_state": { + "command": "npx", + "args": ["-y", "@bradygaster/squad-cli@latest", "state-mcp"], + "tools": ["*"] + } + } +} +``` + +With `--mcp-frontmatter`, the MCP server moves into the frontmatter: + +```yaml +--- +name: squad +description: Squad coordinator +model: claude-opus-4.5 +tools: ["*"] +mcpServers: + squad_state: + command: npx + args: ["-y", "@bradygaster/squad-cli@latest", "state-mcp"] + tools: ["*"] +--- +``` + +And the standalone `.copilot/mcp-config.json` is not written (or contains only non-squad servers). + +--- + +## Effect on `squad upgrade` + +`squad upgrade` detects which mode the project is using (looks for the `mcpServers` key in agent frontmatter vs. presence of `.copilot/mcp-config.json`) and preserves the choice. You don't need to re-pass `--mcp-frontmatter` on every upgrade. + +To switch modes after init, re-run `squad init --mcp-frontmatter` (or run `squad init` without the flag to switch back). The previous MCP config is migrated. + +--- + +## Limitations + +- **Less robust for `copilot -p` non-interactive mode.** Standard mode pins MCP at user level too, which solves the workspace-only loading gap (PR [#1251](https://github.com/bradygaster/squad/pull/1251)). Frontmatter mode skips that user-level write — so `copilot -p` may not see the squad MCP unless the harness reads frontmatter directly. +- **No second-layer fallback.** If the harness that reads frontmatter MCP fails to load it correctly, there's no `.copilot/mcp-config.json` to fall back to. Test in your specific harness before adopting. +- **Schema is harness-specific.** The frontmatter `mcpServers` key follows the Copilot CLI convention, but other harnesses may expect different key names (`mcp_servers`, `mcp.servers`, etc.). Check your harness's spec. + +--- + +## See also + +- [MCP Integration](/squad/docs/features/mcp/) — the broader MCP system +- [Coordinator-as-Agent Export](/squad/docs/features/coordinator-as-agent-export/) — bundling MCP config into a self-contained agent file diff --git a/docs/src/content/docs/features/mcp.md b/docs/src/content/docs/features/mcp.md index 597134e6b..55a7c14f9 100644 --- a/docs/src/content/docs/features/mcp.md +++ b/docs/src/content/docs/features/mcp.md @@ -269,7 +269,7 @@ Agents will now automatically propose Trello tasks for tracking work items. ## Example: Aspire Dashboard MCP (Deployment Monitoring) -For .NET Aspire projects, configure the Aspire Dashboard MCP for deployment monitoring: +For Aspire projects, configure the Aspire Dashboard MCP for deployment monitoring: **Config template:** ```json diff --git a/docs/src/content/docs/features/memory.md b/docs/src/content/docs/features/memory.md index 98dfc9cb8..f84d14e05 100644 --- a/docs/src/content/docs/features/memory.md +++ b/docs/src/content/docs/features/memory.md @@ -169,7 +169,7 @@ Active decisions (ongoing policies, user preferences, current architecture) stay ## Skills -Reusable knowledge files at `.squad/skills/{skill-name}/SKILL.md`. See [Skills System](skills.md) for details. +Reusable knowledge files at `.copilot/skills/{skill-name}/SKILL.md`. See [Skills System](skills.md) for details. Skills differ from decisions — decisions are project policies ("use PostgreSQL"), while skills are transferable techniques ("how to set up CI with GitHub Actions"). diff --git a/docs/src/content/docs/features/notifications.md b/docs/src/content/docs/features/notifications.md index f07fd20f5..d3044e8d2 100644 --- a/docs/src/content/docs/features/notifications.md +++ b/docs/src/content/docs/features/notifications.md @@ -359,7 +359,7 @@ Use the `NOTIFY_*` environment variables (see Configuration above) to disable no ## Architecture Notes -The `human-notification` skill lives in `.squad/skills/squad-human-notification/SKILL.md`. Agents read it before working and decide whether to ping you. You can edit the skill directly if you want to: +The `human-notification` skill lives in `.copilot/skills/squad-human-notification/SKILL.md`. Agents read it before working and decide whether to ping you. You can edit the skill directly if you want to: - Add custom notification logic for your team - Change when agents decide to ping (e.g., always notify on errors) diff --git a/docs/src/content/docs/features/preset.md b/docs/src/content/docs/features/preset.md new file mode 100644 index 000000000..e39d7d3c8 --- /dev/null +++ b/docs/src/content/docs/features/preset.md @@ -0,0 +1,205 @@ +--- +title: Presets — Curated Agent Collections +description: Save, share, and apply pre-configured agent rosters across projects with squad preset commands. +--- + +# Presets — Curated Agent Collections + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +**Try this to see available presets:** +```bash +squad preset list +``` + +**Try this to apply a preset to a new project:** +```bash +squad init --preset backend-team +``` + +Presets are reusable, named bundles of agent charters you can apply to any squad. Built-in presets ship with Squad; you can save your own from any project's current agents and (optionally) sync them across machines via a private GitHub repo. + +Each preset is a directory at `~/.squad/presets//` containing a `preset.json` manifest and `agents/` charter files. The preset name is the directory name. + +--- + +## What presets capture + +Presets capture **agents only** (charters). For full squad snapshots including casting state, skills, routing rules, and decisions — for example to share a configured squad or publish to an agent toolbox — use [`squad export`](/squad/docs/features/export-import/) instead. + +| Captured by preset | NOT captured by preset | +|---|---| +| Agent charters (role, expertise, prompt style) | Casting state (registry, history) | +| Agent count + composition | `routing.md` rules | +| Built-in agent names + descriptions | `decisions.md` ledger | +| | Skills (`.copilot/skills/`) | +| | Ceremonies | +| | Memory (`.squad/memory/`) | + +This split is intentional. Presets are about the **shape** of a team. Skills, decisions, and history are about the **work** that team did. + +--- + +## Commands + +### `squad preset list` + +Show every preset available in your squad home (`~/.squad/presets/`): + +```bash +$ squad preset list + +Available Presets (3): + + Name Agents Description + ─────────────── ────── ──────────────────────────────────────── + backend-team 4 Backend-focused squad: lead, API, DB, QA + full-stack 6 Full-stack web app: lead, FE, BE, design, QA, devops + data-engineering 4 Data pipeline squad: lead, ETL, ML, QA +``` + +If no presets directory exists yet, you'll be prompted to run `squad preset init`. + +### `squad preset show ` + +Inspect a preset's manifest and agents before applying: + +```bash +$ squad preset show backend-team + +backend-team v1.0.0 + Backend-focused squad: lead, API, DB, QA + Author: bradygaster + Tags: backend, api + + Agents (4): + • lead (Lead) — owns architecture, scope, and code review + • api (Backend Dev) — REST endpoints, validation, error handling + • db (Database Engineer) — schema, migrations, query optimization + • qa (Tester) — test coverage, edge cases, CI/CD +``` + +### `squad preset apply [--force]` + +Install the preset's agents into the current squad. The current directory must be a Squad project (have a `.squad/` directory). + +```bash +squad preset apply backend-team +``` + +This copies the preset's `agents/` directory into your project's `.squad/agents/`. Existing agents with the same names are **NOT** overwritten unless you pass `--force`. + +### `squad preset save [--force] [--description "..."]` + +Save your current project's agents as a new preset: + +```bash +squad preset save my-team --description "My favorite roster for greenfield projects" +``` + +This snapshots the agents from the current squad's `.squad/agents/` directory into `~/.squad/presets/my-team/` along with a `preset.json` manifest. Pass `--force` to overwrite an existing preset of the same name. + +### `squad preset init [--remote]` + +Initialize the presets directory in your squad home (`~/.squad/presets/`). + +- **Local-only** (`squad preset init`) — creates the directory and seeds the built-in presets +- **Remote-synced** (`squad preset init --remote`) — creates the directory, seeds built-ins, AND creates a private GitHub repo (`{your-gh-user}/squad-home`) backing the directory so presets sync across machines + +Requirements for `--remote`: +- GitHub CLI (`gh`) installed and authenticated (`gh auth login`) +- Permission to create private repos + +On a second machine, run `squad preset init --remote` again and it will detect and clone your existing `squad-home` repo automatically. + +--- + +## Applying a preset at init time + +The most common usage — bootstrap a new project with a preset team: + +```bash +mkdir my-new-project +cd my-new-project +git init +squad init --preset backend-team +``` + +This creates the standard `.squad/` scaffold AND applies the preset's agents. If `~/.squad/presets/` doesn't exist yet, `squad init` auto-runs `squad preset init` first to seed the built-in presets. + +--- + +## Cross-machine workflow + +Use the remote-backed setup if you want presets to follow you to new machines or shared dev environments: + +```bash +# Machine A — initial setup +squad preset init --remote +squad preset save my-team --description "My standard team" + +# Machine B — same user, same GitHub account +squad preset init --remote # detects existing squad-home repo, clones it +squad preset list # my-team is already here +``` + +The remote repo lives at `https://github.com//squad-home` (private by default). + +--- + +## Sharing presets between users + +Today, the simplest path to share a preset with someone else is: + +1. They run `squad preset init --remote` to set up their own squad home +2. You manually copy the preset directory across (or clone yours, copy the directory in, push) + +A formal "publish/install from another user's repo" flow is on the roadmap but not in v0.10. + +For collaborative team rosters that go beyond just agents (skills, decisions, routing), use [`squad export`](/squad/docs/features/export-import/) instead. + +--- + +## What's in a preset directory + +``` +~/.squad/presets/backend-team/ +├── preset.json # manifest (name, version, description, tags, agents list) +└── agents/ + ├── lead/ + │ └── charter.md + ├── api/ + │ └── charter.md + ├── db/ + │ └── charter.md + └── qa/ + └── charter.md +``` + +The `preset.json` manifest format: + +```json +{ + "name": "backend-team", + "version": "1.0.0", + "description": "Backend-focused squad: lead, API, DB, QA", + "author": "bradygaster", + "tags": ["backend", "api"], + "agents": [ + { "name": "lead", "role": "Lead", "description": "owns architecture, scope, and code review" }, + { "name": "api", "role": "Backend Dev", "description": "REST endpoints, validation, error handling" }, + { "name": "db", "role": "Database Engineer", "description": "schema, migrations, query optimization" }, + { "name": "qa", "role": "Tester", "description": "test coverage, edge cases, CI/CD" } + ] +} +``` + +You can hand-edit this file to refine descriptions or add/remove agents — but the corresponding `agents//charter.md` files must match. + +--- + +## See also + +- [Export & Import](/squad/docs/features/export-import/) — full squad snapshots including state +- [Skills](/squad/docs/features/skills/) — earned knowledge (separate from presets) +- [Plugin Marketplace](/squad/docs/features/plugins/) — community-curated bundles diff --git a/docs/src/content/docs/features/reflect.md b/docs/src/content/docs/features/reflect.md new file mode 100644 index 000000000..a23c2e9bc --- /dev/null +++ b/docs/src/content/docs/features/reflect.md @@ -0,0 +1,91 @@ +--- +title: Reflect — In-Session Learning Capture +description: Built-in skill that extracts HIGH/MED/LOW confidence patterns from conversations to prevent repeating mistakes and reinforce successful patterns. +--- + +# Reflect — In-Session Learning Capture + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +The `reflect` skill is a built-in capability that turns every user correction into a learning opportunity. Agents invoke `reflect` after critical conversation moments — corrections, praise, edge-case discoveries — to capture patterns that prevent repeating mistakes across sessions. + +It ships at `.copilot/skills/reflect/SKILL.md` and is automatically available to every spawned agent. The skill complements the existing knowledge layers (`history.md`, `decisions.md`) by capturing **in-flight** learnings that may later graduate to permanent memory. + +--- + +## How it fits the memory architecture + +Squad has three layers for what agents know: + +| Layer | Lifetime | Audience | Reflect's relationship | +|-------|----------|----------|------------------------| +| `.squad/agents/{name}/history.md` | Permanent | Owner agent + Scribe-propagated cross-updates | Reflect captures candidates; HIGH-confidence ones graduate here | +| `.squad/decisions.md` | Permanent | All agents | Reflect surfaces candidates; lead promotes after review | +| `reflect` skill | In-flight | Captured during the active session | Working memory for patterns not yet ready to commit | + +Workflow: +1. During the session, agents invoke `reflect` to capture learnings +2. At session end, the agent or Scribe reviews captured learnings +3. HIGH-confidence patterns → lead reviews for `decisions.md` promotion +4. Agent-specific patterns → `{agent}/history.md` append + +--- + +## Triggers — when to invoke reflect + +### 🔴 HIGH Priority (invoke immediately) + +| Trigger | Example phrase | Why critical | +|---------|---------------|--------------| +| User correction | *"no"*, *"wrong"*, *"not like that"*, *"never do"* | Captures mistakes to prevent repetition | +| Architectural insight | *"you removed that without understanding why"* | Documents the *why* behind a design (Chesterton's Fence) | +| Immediate fixes | *"debug"*, *"root cause"*, *"fix all"* | Learns from errors in real-time | + +### 🟡 MEDIUM Priority (invoke after multiple instances) + +| Trigger | Example phrase | Why important | +|---------|---------------|--------------| +| User praise | *"perfect"*, *"exactly"*, *"great"* | Reinforces successful patterns | +| Tool preferences | *"use X instead of Y"*, *"prefer"* | Builds workflow preferences | +| Edge cases | *"what if X happens?"*, *"don't forget"*, *"ensure"* | Captures scenarios to handle | + +### 🟢 LOW Priority (invoke at natural breakpoints) + +| Trigger | Example phrase | Why useful | +|---------|---------------|--------------| +| Workflow refinements | *"better if you..."*, *"next time"* | Iterative improvement | +| Style preferences | *"prefer this format"*, *"like this approach"* | Personal style learning | + +--- + +## Capture format + +Reflect produces structured entries the lead or Scribe can review at session end: + +```markdown +## Reflection — 2026-06-11T16:42:00Z + +**Trigger:** User correction — "no, never auto-merge without explicit approval" +**Confidence:** HIGH +**Pattern:** Auto-merge gating +**Learning:** Even when CI is green and reviews pass, do not invoke `gh pr merge` without an explicit user confirmation. The user wants the final merge action to be human-driven. +**Promote to:** `decisions.md` (team-wide rule) — surface to lead next ceremony +**Cited:** Coordinator session 2026-06-11, user message ~16:41 +``` + +--- + +## Anti-patterns + +- **Don't capture every interaction.** Reflect is for inflection points — corrections, surprises, breakthroughs. A capture rate >1 per ~10 messages is too high. +- **Don't promote LOW-confidence patterns to decisions.md.** Decisions are binding for the whole team; LOW captures are personal preferences and should live in the agent's `history.md` if anywhere. +- **Don't reflect on user instructions you already executed correctly.** That's not learning, that's logging. +- **Don't paraphrase the user's words when capturing HIGH-priority items.** Verbatim quotes preserve nuance. + +--- + +## See also + +- [Memory & Knowledge](/squad/docs/concepts/memory-and-knowledge/) — the three-layer model +- [Directives](/squad/docs/features/directives/) — how the coordinator captures explicit team rules +- [Error Recovery](/squad/docs/features/error-recovery/) — the companion skill for handling failures diff --git a/docs/src/content/docs/features/routing.md b/docs/src/content/docs/features/routing.md index 3e8478108..69f631faf 100644 --- a/docs/src/content/docs/features/routing.md +++ b/docs/src/content/docs/features/routing.md @@ -56,7 +56,7 @@ When work involves `src/api/auth.ts`, it routes to Backend automatically. ### 3. Skill-Aware Routing -If no domain match, the coordinator checks `.squad/skills/` for capability fit: +If no domain match, the coordinator checks `.copilot/skills/` for capability fit: ```markdown # authentication.md diff --git a/docs/src/content/docs/features/skill-security-scanner.md b/docs/src/content/docs/features/skill-security-scanner.md new file mode 100644 index 000000000..5e3fe415a --- /dev/null +++ b/docs/src/content/docs/features/skill-security-scanner.md @@ -0,0 +1,118 @@ +--- +title: Skill Security Scanner +description: Markdown-aware security scanner that catches embedded credentials, download-and-execute patterns, and privilege escalation in skill files before they ship. +--- + +# Skill Security Scanner + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +The skill security scanner is a markdown-aware safety check that runs as part of `scripts/security-review.mjs` to inspect every SKILL.md file in `.copilot/skills/` and `.squad/skills/`. It catches three classes of problem before a skill gets installed or merged: + +1. **Embedded credentials** — API keys, tokens, passwords pasted into skill text +2. **Download-and-execute patterns** — `curl ... | bash`, `Invoke-Expression`, and friends +3. **Privilege escalation commands** — `sudo`, `Set-ExecutionPolicy Bypass`, `chmod 777`, etc. + +It ships as Phase 1 — focused on the highest-signal issues with **zero false positives on the existing 35 skill files** at the time of release. + +--- + +## How it integrates + +The scanner is invoked by the existing security-review pipeline (`scripts/security-review.mjs`), which is triggered: + +- On every PR that touches `.copilot/skills/**` or `.squad/skills/**` (via the Security Review CI workflow) +- Manually: `node scripts/security-review.mjs --scope skills` +- As part of [Plugin Marketplace](/squad/docs/features/plugins/) install (skills from external sources get scanned before landing on disk) + +A finding produces a CI failure with the file path, line number, pattern type, and the matched substring (redacted for credentials). + +--- + +## What it catches + +### Credentials + +| Pattern type | Example match | +|--------------|---------------| +| Generic API key | `API_KEY=` | +| GitHub PAT | `ghp_<40-character-token>` | +| AWS access key | `AKIA<16-character-key>` | +| Bearer tokens | `Authorization: Bearer ` | +| Database connection strings with embedded passwords | `postgres://user:@host/db` | + +### Download-and-execute patterns + +| Pattern type | Example match | +|--------------|---------------| +| Curl-to-bash | `curl https://... \| bash`, `curl ... \| sh`, `wget ... \| sh` | +| PowerShell invoke-expression | `iex (irm https://...)`, `Invoke-Expression $downloaded` | +| Unsafe eval | `eval $(curl ...)`, `eval $(wget ...)` | + +### Privilege escalation + +| Pattern type | Example match | +|--------------|---------------| +| `sudo` invocations | `sudo apt install`, `sudo -i`, `sudo bash` | +| Permissive chmod | `chmod 777`, `chmod a+rwx`, `chmod -R 777` | +| PowerShell policy bypass | `Set-ExecutionPolicy Bypass`, `Set-ExecutionPolicy Unrestricted` | +| Windows admin escalation | `Start-Process ... -Verb RunAs`, `runas /user:Administrator` | + +--- + +## Suppression — the false-positive guardrails + +The scanner is markdown-aware, which means it understands when a "dangerous" pattern is actually in a code block being **shown as an anti-pattern** vs. in prose advising users to run something: + +| Where pattern appears | Action | +|----------------------|--------| +| Inside a fenced code block (three backticks) | **Suppressed** — treated as documentation, not advice | +| Inside an inline code span (single backtick) | **Suppressed** — treated as a reference | +| In prose with a placeholder token (``, ``, `xxx`, `***`) | **Suppressed** — clearly an example | +| In prose without any of the above | **Flagged** as a finding | + +The placeholder-token list covers common safe markers: ``, ``, ``, `xxx`, `***`, `placeholder`, `example`, `PLACEHOLDER`. + +This is why the existing 35 skill files have zero false positives — most discuss security patterns inside fenced code blocks or with placeholder tokens. + +--- + +## Local invocation + +```bash +# Scan all skills in the current repo +node scripts/security-review.mjs --scope skills + +# Scan a single skill file +node scripts/security-review.mjs --file .copilot/skills/my-skill/SKILL.md + +# JSON output for tooling integration +node scripts/security-review.mjs --scope skills --format json +``` + +Exit codes: +- `0` — no findings +- `1` — findings detected (CI fails the build) +- `2` — scanner error (couldn't read file, malformed markdown, etc.) + +--- + +## What it doesn't catch + +This is **Phase 1**. The scanner is deliberately conservative — it would rather miss something than false-positive a legitimate skill. Things NOT in scope today: + +- **Obfuscated patterns** — base64-encoded credentials, character-class regex tricks, etc. +- **Multi-line patterns** — the scanner is line-oriented; a credential split across lines won't match +- **Skill scripts (`.js`/`.mjs` files in `scripts/`)** — only the SKILL.md narrative is scanned; executable handlers need their own audit +- **Semantic context** — the scanner doesn't understand whether a `sudo` example is contextually safe; if it's in prose without a placeholder marker, it flags +- **Hooks beyond `.copilot/skills/` and `.squad/skills/`** — other markdown files (charters, decisions, README) aren't scanned by this rule + +Phase 2 work tracked in the issue tracker would extend coverage to scripts and add an LLM-based semantic pass. + +--- + +## See also + +- [Skills](/squad/docs/features/skills/) — the broader skills system +- [Plugin Marketplace](/squad/docs/features/plugins/) — how external skills get installed +- [Secret Handling](/squad/docs/features/skills/) — see also the `secret-handling` built-in skill diff --git a/docs/src/content/docs/features/skills.md b/docs/src/content/docs/features/skills.md index c4c4f6e08..d3f7cc7b8 100644 --- a/docs/src/content/docs/features/skills.md +++ b/docs/src/content/docs/features/skills.md @@ -25,11 +25,13 @@ Agents learn from real work and write skill files — reusable patterns, convent ## Where Skills Live ``` -.squad/skills/{skill-name}/SKILL.md +.copilot/skills/{skill-name}/SKILL.md ``` Each skill is a directory containing a `SKILL.md` file. Skills are **team-wide knowledge** — not tied to individual agents. All agents can read and use any skill. +> **Legacy path**: Skills are also discovered at `.squad/skills/` for backward compatibility. If both paths contain a skill with the same name, the `.squad/skills/` version takes precedence. New skills are always written to `.copilot/skills/`. + > **Portable across projects**: Skills export and import with your team. When you move a trained team to a new repo, all their earned knowledge comes with them. --- @@ -57,7 +59,7 @@ Legacy term for built-in skills. Previously called "starter skills" and prefixed ### Session Recovery -The `session-recovery` skill teaches agents to find and resume interrupted Copilot CLI sessions. When a session is interrupted (terminal crash, network drop, machine restart), in-progress work may be left incomplete. This skill uses `session_store` SQL queries to detect abandoned sessions, inspect checkpoint progress, and resume work. See [`.squad/skills/session-recovery/SKILL.md`](https://github.com/bradygaster/squad/blob/dev/.squad/skills/session-recovery/SKILL.md) for query patterns and examples. +The `session-recovery` skill teaches agents to find and resume interrupted Copilot CLI sessions. When a session is interrupted (terminal crash, network drop, machine restart), in-progress work may be left incomplete. This skill uses `session_store` SQL queries to detect abandoned sessions, inspect checkpoint progress, and resume work. See [`.copilot/skills/session-recovery/SKILL.md`](https://github.com/bradygaster/squad/blob/dev/.copilot/skills/session-recovery/SKILL.md) for query patterns and examples. ### Earned skills @@ -92,7 +94,7 @@ Confidence only goes up, never down. A skill that reaches `high` stays there. After successfully setting up a CI pipeline, an agent might create: ``` -.squad/skills/ci-github-actions/SKILL.md +.copilot/skills/ci-github-actions/SKILL.md ``` ```markdown @@ -117,7 +119,7 @@ After successfully setting up a CI pipeline, an agent might create: - Skills compound over time. A mature project has skills covering testing patterns, deployment procedures, API conventions, and more. - Built-in skills are overwritten on upgrade. Earned skills are never touched. -- **Skills are shared across the whole team** — any agent can read any skill. They're stored in a flat `.squad/skills/` directory, not per-agent files. +- **Skills are shared across the whole team** — any agent can read any skill. They're stored in a flat `.copilot/skills/` directory, not per-agent files. - You can manually edit skill files if you want to seed knowledge (e.g., paste your team's existing conventions into a `SKILL.md`). - **Skills survive export/import** — your team's accumulated knowledge is fully portable across projects. @@ -127,7 +129,7 @@ After successfully setting up a CI pipeline, an agent might create: list all skills ``` -Shows all skill files in `.squad/skills/` with confidence levels for earned skills. +Shows all skill files in `.copilot/skills/` with confidence levels for earned skills. ``` what's the confidence level for the CI skill? diff --git a/docs/src/content/docs/features/state-backends.md b/docs/src/content/docs/features/state-backends.md index 0009e0fb2..6d8a5ed87 100644 --- a/docs/src/content/docs/features/state-backends.md +++ b/docs/src/content/docs/features/state-backends.md @@ -49,13 +49,15 @@ squad upgrade --state-backend two-layer # Or: squad upgrade --state-backend orphan ``` -This migrates existing state, creates the orphan branch, and installs git hooks for automatic multi-user sync. No manual steps needed. +This migrates existing state, creates the orphan branch, and installs git hooks for automatic multi-user sync. ### What gets installed automatically When you choose `orphan` or `two-layer`: -- **Git hooks** (pre-push, post-merge, post-checkout, post-rewrite) are installed in `.git/hooks/` -- These hooks sync the `squad-state` branch automatically when you push/pull — no manual sync needed +- **Git hooks** (pre-push, post-merge, post-checkout, post-rewrite, pre-commit, post-commit) are installed in `.git/hooks/` +- The sync hooks (pre-push, post-merge, post-checkout, post-rewrite) keep the `squad-state` branch in sync automatically when you push/pull +- The **pre-commit** hook guards against accidentally staging two-layer mutable state (decisions, histories, casting, routing) into a working-tree commit — it refuses with an explanation if detected +- The **post-commit** hook flushes any pending two-layer state onto the orphan branch after each commit (best-effort, never blocks) - Hooks chain with existing hooks (husky, etc.) — nothing is overwritten --- @@ -162,7 +164,7 @@ Warning: State backend 'two-layer' failed: . Falling back to 'local'. ```bash cat .squad/decisions.md -ls .squad/skills/ +ls .copilot/skills/ ``` ### Git Notes @@ -406,7 +408,7 @@ This will: 3. Install git hooks for automatic sync 4. Preserve all existing state -**What happens:** Existing `.squad/` files remain on disk as a read-only reference. New decisions and state writes go to the orphan branch (and git notes for two-layer). Over time, the on-disk state files become stale (they're the snapshot from before migration), while the orphan branch and notes contain the latest state. +**What happens:** Existing `.squad/` files are migrated to the orphan branch and may be removed from the working tree on subsequent commits. New decisions and state writes go to the orphan branch (and git notes for two-layer). The pre-commit hook prevents you from accidentally re-committing mutable state files into the working tree. ### Switching between orphan and two-layer @@ -414,8 +416,65 @@ Change `stateBackend` in `.squad/config.json`. The coordinator adapts on the nex --- +## Steady-state safety net + +Once you've migrated to `orphan` or `two-layer`, two additional git hooks enforce the invariant that mutable state never lands in your working branch. + +### `pre-commit` — blocks state from entering the working tree + +Before every commit, this hook scans the staged index for files that belong on the orphan branch: + +- `.squad/decisions.md` +- `.squad/agents/*/history.md` +- `.squad/casting/` +- `.squad/routing/` + +If any of those paths are staged, the commit is refused with: + +``` +⚠ squad pre-commit: refusing to commit two-layer state into the working tree. + Unstage the state files and let the post-commit hook sync them: + git restore --staged .squad/decisions.md .squad/agents/*/history.md +``` + +**Why files might reappear:** A tool, editor save, or agent code path that writes directly via `fs.writeFile` (bypassing `StateBackend`) will recreate the file on disk. Staging it and attempting a commit triggers this hook. + +For the full recovery flow see [troubleshooting](#squad-pre-commit-refusing-to-commit-two-layer-state-into-the-working-tree). + +### `post-commit` — keeps the orphan branch current + +After every successful commit, `squad sync --quiet` is called automatically. This pushes any pending state from the in-memory queue onto the `squad-state` branch, so the orphan branch stays up to date without manual intervention. + +### `SQUAD_SYNC_ACTIVE=1` bypass + +Setting `SQUAD_SYNC_ACTIVE=1` in the environment causes both hooks to exit immediately without running. This is used **internally** by `squad sync` itself to avoid hook recursion. + +> ⚠️ **Do not use `SQUAD_SYNC_ACTIVE=1` routinely.** Bypassing the pre-commit hook lets state files land in your working branch commits — exactly the situation `two-layer` is designed to prevent. Any PR created from that branch will carry squad state in the diff, defeating the clean-PR promise of the two-layer backend. Use the recovery flow instead. + ## Troubleshooting +### "Pre-commit hook refused my commit" + +**Cause:** You staged `.squad/` files that belong on the `squad-state` orphan branch (decisions.md, agent histories, casting/, routing/). The pre-commit hook blocks these to keep mutable state off your working-tree branches. + +**Fix:** + +```bash +# Unstage the offending paths +git restore --staged .squad/decisions.md .squad/agents/*/history.md .squad/casting/ .squad/routing/ + +# Then commit normally — only your code changes go through +git commit +``` + +**If you need to bypass** (e.g., during initial migration or manual repair): + +```bash +SQUAD_SYNC_ACTIVE=1 git commit -m "manual state repair" +``` + +> ⚠️ Only bypass when you understand why — the hook exists to prevent state from leaking into PRs. + ### "My state disappeared after switching branches" **Cause:** You're using the default `local` backend. State files are branch-local. diff --git a/docs/src/content/docs/features/teams-comms.md b/docs/src/content/docs/features/teams-comms.md new file mode 100644 index 000000000..533cefddb --- /dev/null +++ b/docs/src/content/docs/features/teams-comms.md @@ -0,0 +1,127 @@ +--- +title: Microsoft Teams Comms Adapter +description: Bidirectional chat integration between Squad and Microsoft Teams via Microsoft Graph API — 1:1 chats and channel messaging with PKCE browser auth or device code fallback. +--- + +# Microsoft Teams Comms Adapter + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +The Teams adapter lets your squad post updates and read replies through Microsoft Teams, alongside the existing file-based, email, and other comm channels. It ships in `@bradygaster/squad-sdk` as a `CommunicationAdapter` implementation and uses Microsoft Graph API for both 1:1 chats and channel messaging. + +> **⚠️ Breaking change in v0.10:** `createCommunicationAdapter` is now async (returns `Promise`). Callers must `await` the result. + +--- + +## What you can do with it + +| Action | Supported | +|--------|-----------| +| Post a message to a 1:1 chat | ✅ | +| Post a message to a Teams channel | ✅ | +| Read replies / new messages from a chat | ✅ | +| Post rich content (Adaptive Cards, attachments) | Partial (text + basic formatting) | +| Notify on agent-completed work | ✅ (via squad watch / notification routing) | +| Two-way conversation with an agent in Teams | ✅ (poll-based, not push) | + +The adapter is one of several `CommunicationAdapter` implementations — see [Notifications](/squad/docs/features/notifications/) for the broader notification system. + +--- + +## Authentication flow + +The adapter tries auth methods in this order, falling through on failure: + +1. **Cached token** — looks for a previously-saved token in the OS credential store +2. **Refresh token** — if cached refresh token is valid, silently re-issues an access token +3. **Browser PKCE** — opens a browser for the user to sign in; uses Authorization Code with PKCE; 120-second timeout +4. **Device code** — fallback when no browser is available (CI, remote shell); user enters a code on a different device + +``` +$ squad notify teams --to user@example.com --message "Build complete" +🔑 No cached token — opening browser for sign-in... +[browser opens, user signs in] +✓ Token cached. Sending message... +✓ Posted to user@example.com +``` + +The token cache persists across sessions. After the first sign-in, subsequent runs are silent unless the refresh token expires. + +--- + +## Configuration + +The adapter requires a Microsoft Entra (Azure AD) app registration with permissions for: + +- `Chat.ReadWrite` (1:1 chat operations) +- `ChannelMessage.Send` (channel posts) +- `ChannelMessage.Read.All` (read channel replies) +- `User.Read` (basic profile) + +Configure in `.squad/config.json`: + +```json +{ + "comms": { + "teams": { + "tenantId": "00000000-0000-0000-0000-000000000000", + "clientId": "00000000-0000-0000-0000-000000000000", + "redirectUri": "http://localhost:8400/auth", + "tokenCachePath": "~/.squad/.cache/teams-token.json" + } + } +} +``` + +The `redirectUri` is the local-only OAuth callback for browser PKCE — it never leaves your machine. + +--- + +## Usage from the SDK + +```typescript +import { createCommunicationAdapter } from '@bradygaster/squad-sdk/platform'; + +// IMPORTANT: this is async now (breaking change in v0.10) +const teams = await createCommunicationAdapter({ channel: 'teams' }); + +// Post a message +const post = await teams.postUpdate({ + title: 'CI passed', + body: 'PR #1234 is green and ready for review.', + category: 'pr-status', + author: 'Squad', +}); + +// Poll for replies +const replies = await teams.pollForReplies({ + threadId: post.id, + since: new Date(Date.now() - 60_000), +}); +``` + +--- + +## Limitations + +- **Polling, not push.** The adapter polls for replies; it doesn't subscribe to a websocket. Reply latency is the poll interval (default 30s). +- **No Adaptive Card builder.** You can send plain text and basic formatting today; for rich cards, use the underlying Graph SDK directly. +- **No bot-framework integration.** This adapter uses delegated user permissions, not a bot account. Each user sees the message as posted by themselves (or the configured app identity), not by a "Squad bot". +- **MSAL token cache shared across processes.** If you run multiple squads simultaneously with the same Entra app, they share the same cached token. Use distinct `tokenCachePath` if you need isolation. + +--- + +## Security notes + +- Tokens are stored in the OS credential store (Windows Credential Manager / macOS Keychain / Linux libsecret) where available, with a JSON file fallback at `tokenCachePath` +- The browser PKCE callback listens on `127.0.0.1` only — never exposed to the network +- The device code flow shows a verification URL + code; both are short-lived +- The adapter does NOT log message content; only metadata (post id, recipient, timestamp) is recorded in any audit trail + +--- + +## See also + +- [Notifications](/squad/docs/features/notifications/) — the broader notification system +- [Enterprise Platforms](/squad/docs/features/enterprise-platforms/) — Teams + ADO + other enterprise integrations +- [Notification Level](/squad/docs/features/notification-level/) — controlling noise across all channels diff --git a/docs/src/content/docs/features/tiered-memory.md b/docs/src/content/docs/features/tiered-memory.md new file mode 100644 index 000000000..acdfd6a61 --- /dev/null +++ b/docs/src/content/docs/features/tiered-memory.md @@ -0,0 +1,98 @@ +--- +title: Tiered Memory — Hot / Cold / Wiki +description: Three-tier agent memory model that cuts spawn context cost by 20-55% by separating fresh task context from archived history and durable reference docs. +--- + +# Tiered Memory — Hot / Cold / Wiki + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +**Problem:** Squad agents load their full `history.md` on every spawn. Production measurements show 34–74KB payloads per agent (8.8K–18.5K tokens), with 82–96% of that being "old noise" — context the current task doesn't need. + +**Solution:** A three-tier memory model that loads only what each task actually requires, achieving 20–55% context reduction per spawn. + +Tiered Memory ships as a built-in skill at `.copilot/skills/tiered-memory/SKILL.md` and pairs with Scribe's existing 15KB-summarization rule (see [Memory & Knowledge](/squad/docs/concepts/memory-and-knowledge/)) to give large, long-running squads predictable context budgets. + +--- + +## The three tiers + +### 🔥 Hot — Current Session Context + +- **Size target:** ~2–4KB +- **Loaded:** Always, on every spawn +- **Contents:** Current task, active decisions made this session, immediate blockers, last 3–5 actions, who's being talked to +- **Lifetime:** Current session only — Scribe promotes relevant parts to Cold at session end +- **Purpose:** Immediate task context with zero latency and zero decision + +### ❄️ Cold — Summarized Cross-Session History + +- **Size target:** ~8–12KB +- **Loaded:** On demand — include only when the task explicitly needs history +- **Contents:** Summarized past sessions, cross-session decisions, recurring patterns, unresolved issues +- **Lifetime:** 30-day rolling window — older entries promoted to Wiki +- **Purpose:** Answer *"what have we tried before?"* and *"what was decided?"* without replaying full transcripts +- **How to include:** Pass `--include-cold` in the spawn template, or add a `## Cold Memory` section to the agent's instructions + +### 📚 Wiki — Durable Structured Knowledge + +- **Size target:** variable (structured reference docs) +- **Loaded:** Async write, selective read — only when the task requires domain knowledge +- **Contents:** ADRs, agent charters, routing rules, stable conventions, external API contracts, platform constraints +- **Lifetime:** Permanent until explicitly deprecated +- **Purpose:** Authoritative reference (not history) — structured facts +- **How to include:** Pass `--include-wiki` or reference specific wiki doc paths in the spawn template + +--- + +## When to load each tier + +| Situation | Hot | Cold | Wiki | +|-----------|-----|------|------| +| New task, no prior context needed | ✅ | ❌ | ❌ | +| Resuming interrupted work | ✅ | ✅ | ❌ | +| Debugging a recurring issue | ✅ | ✅ | ❌ | +| Designing something new in an established area | ✅ | ❌ | ✅ | +| Onboarding a new team member | ✅ | ❌ | ✅ | +| Investigating an architectural drift | ✅ | ✅ | ✅ | + +The bias is to load LESS, not more. Cold and Wiki should be opt-in for each spawn based on whether the task description references the past or domain conventions. + +--- + +## How Scribe maintains the tiers + +Scribe's existing maintenance cycle (see [Memory & Knowledge](/squad/docs/concepts/memory-and-knowledge/)) is extended: + +1. **Hot drained at session end** — Scribe scans the session's hot memory, summarizes meaningful entries, appends them to Cold +2. **Cold aged into Wiki** — entries older than 30 days that contain structured facts (decisions, conventions, contracts) get promoted to Wiki +3. **Wiki authored deliberately** — Scribe never auto-creates Wiki entries from scratch; it only promotes Cold content that's already structured + +--- + +## Production measurements + +The skill's documentation cites measurements from a large production squad: + +| Squad size | Before tiered | After tiered | Reduction | +|------------|--------------|--------------|-----------| +| 8 agents, 34KB total history | 8,800 tokens/spawn | 4,400 tokens/spawn | ~50% | +| 14 agents, 74KB total history | 18,500 tokens/spawn | 8,300 tokens/spawn | ~55% | + +The exact savings depend on what fraction of each agent's history is task-relevant. The 20–55% range is the measured spread across different team configurations. + +--- + +## Caveats + +- **The tier split is currently advisory** — the skill defines hot/cold/wiki semantics, but the spawn template doesn't yet enforce `--include-cold` / `--include-wiki` flags as part of the runtime contract. Adoption is per-team via spawn-template edits. +- **Wiki has no UI** — there's no `squad wiki list` command yet. Entries live as files in `.squad/wiki/` (when teams create that directory) and the coordinator references them by path. +- **Issue [#1268](https://github.com/bradygaster/squad/issues/1268) and [#1269](https://github.com/bradygaster/squad/issues/1269)** propose making Scribe enforce these tiers via the governed memory pipeline. Until those land, tier maintenance is best-effort. + +--- + +## See also + +- [Memory & Knowledge](/squad/docs/concepts/memory-and-knowledge/) — the broader memory architecture +- [Skills](/squad/docs/features/skills/) — how built-in skills work +- [Context Hygiene](/squad/docs/features/context-hygiene/) — related practices for keeping spawn context small diff --git a/docs/src/content/docs/features/upstream-inheritance.md b/docs/src/content/docs/features/upstream-inheritance.md index d6288a92e..d9247c0e9 100644 --- a/docs/src/content/docs/features/upstream-inheritance.md +++ b/docs/src/content/docs/features/upstream-inheritance.md @@ -8,7 +8,7 @@ Upstream inheritance lets you declare external Squad sources (from repositories, At session start, the coordinator reads all declared upstreams from `upstream.json` and makes their context available to every agent: -- **Skills** — `.squad/skills/*/SKILL.md` +- **Skills** — `.copilot/skills/*/SKILL.md` - **Decisions** — `.squad/decisions.md` - **Wisdom** — `.squad/identity/wisdom.md` - **Casting policy** — `.squad/casting/policy.json` diff --git a/docs/src/content/docs/guide.md b/docs/src/content/docs/guide.md index 98ccaeb47..01e0f6a87 100644 --- a/docs/src/content/docs/guide.md +++ b/docs/src/content/docs/guide.md @@ -276,7 +276,7 @@ When a universe runs out of names, the overflow strategy determines what happens ## Skills system -Skills are reusable knowledge patterns that agents load on demand. They live in `.squad/skills/{name}/SKILL.md` and teach agents how to handle specific tasks — branching workflows, deployment strategies, testing patterns, or domain expertise. +Skills are reusable knowledge patterns that agents load on demand. They live in `.copilot/skills/{name}/SKILL.md` and teach agents how to handle specific tasks — branching workflows, deployment strategies, testing patterns, or domain expertise. Skills have a confidence lifecycle: `low` → `medium` → `high`, and track their source: `manual` (you wrote it), `observed` (agent saw a pattern), `earned` (validated through use), or `extracted` (imported from another project). diff --git a/docs/src/content/docs/guide/build-autonomous-agent.md b/docs/src/content/docs/guide/build-autonomous-agent.md index 66e0ac0c4..12832df78 100644 --- a/docs/src/content/docs/guide/build-autonomous-agent.md +++ b/docs/src/content/docs/guide/build-autonomous-agent.md @@ -375,7 +375,7 @@ import { recordTokenUsage, } from '@bradygaster/squad-sdk'; -// Initialize OTel (connects to .NET Aspire dashboard if endpoint is set) +// Initialize OTel (connects to Aspire dashboard if endpoint is set) const otelEndpoint = process.env['OTEL_EXPORTER_OTLP_ENDPOINT']; if (otelEndpoint) { initSquadTelemetry({ @@ -413,7 +413,7 @@ const summary = costTracker.getSummary(); console.log(`Total: $${summary.totalEstimatedCost.toFixed(4)}`); ``` -To view traces and metrics in the .NET Aspire dashboard, see the [Aspire dashboard scenario](/scenarios/aspire-dashboard/). +To view traces and metrics in the Aspire dashboard, see the [Aspire dashboard scenario](/scenarios/aspire-dashboard/). --- diff --git a/docs/src/content/docs/guide/personal-squad.md b/docs/src/content/docs/guide/personal-squad.md index 5f71faf3a..08479686b 100644 --- a/docs/src/content/docs/guide/personal-squad.md +++ b/docs/src/content/docs/guide/personal-squad.md @@ -124,6 +124,14 @@ This is your **team identity**. It contains: This is the stuff that makes your agents *yours*. It persists across sessions. It grows as you work. It follows you from project to project. +### The global `squad.agent.md` — a reference, not a live file + +You'll also see `.github/agents/squad.agent.md` inside your global config directory. This file is **not** discoverable by GitHub Copilot — Copilot only reads agent files inside a git repository's `.github/agents/` folder. + +So why is it there? It serves as a **local reference** of the Squad agent instructions that were applied to your projects. The actual source for `squad init` is the packaged template bundled with the CLI (`squad.agent.md.template`), so the global copy is not used as input for future inits. It exists so you can see what Squad generated without opening a project. + +If you want Copilot to use Squad's agent instructions in a project, run `squad init` in that project's root — that creates the "real" `.github/agents/squad.agent.md` inside the git repo where Copilot can discover it. + ### The project pointer: `.squad/config.json` Inside each connected project, `.squad/config.json` looks like this: diff --git a/docs/src/content/docs/guide/sample-prompts.md b/docs/src/content/docs/guide/sample-prompts.md index 7d546a321..bbf02fe5c 100644 --- a/docs/src/content/docs/guide/sample-prompts.md +++ b/docs/src/content/docs/guide/sample-prompts.md @@ -65,10 +65,10 @@ Set up the team and start with the backend data layer. --- -### .NET Aspire Cloud-Native App +### Aspire Cloud-Native App ``` -Build a cloud-native distributed app with .NET Aspire. I want: +Build a cloud-native distributed app with Aspire. I want: - An AppHost that orchestrates all services - A Blazor frontend with interactive server components - A minimal API backend with OpenAPI endpoints diff --git a/docs/src/content/docs/reference/glossary.md b/docs/src/content/docs/reference/glossary.md index 7a5ef32f9..5642dd0ba 100644 --- a/docs/src/content/docs/reference/glossary.md +++ b/docs/src/content/docs/reference/glossary.md @@ -28,7 +28,7 @@ Key terms defined in one sentence each. Alphabetical order. **Scribe** — The silent agent that tracks decisions and logs sessions, merging proposals from all agents into `.squad/decisions.md`. -**Skill** — A reusable capability stored in `.squad/skills/` that agents can learn and execute. +**Skill** — A reusable capability stored in `.copilot/skills/` that agents can learn and execute. **Spawn** — The act of starting an agent as an independent subprocess with its own context window, tools, and memory. diff --git a/docs/src/content/docs/sample-prompts.md b/docs/src/content/docs/sample-prompts.md index b3a73fee5..2112eda8c 100644 --- a/docs/src/content/docs/sample-prompts.md +++ b/docs/src/content/docs/sample-prompts.md @@ -212,10 +212,10 @@ Read SDK docs carefully. One agent owns SDK integration/streaming, another owns --- -### 9. .NET Aspire Cloud-Native App +### 9. Aspire Cloud-Native App ``` -Build a cloud-native app with .NET Aspire (read https://learn.microsoft.com/en-us/dotnet/aspire/): +Build a cloud-native app with Aspire (read https://aspire.dev/): - AppHost orchestrating all services - Blazor Server dashboard: current conditions + 5-day forecast for saved cities - Weather API service: wraps OpenWeatherMap with Redis caching diff --git a/docs/src/content/docs/scenarios/cross-org-auth.md b/docs/src/content/docs/scenarios/cross-org-auth.md index 34d4e823d..b0f54ba5f 100644 --- a/docs/src/content/docs/scenarios/cross-org-auth.md +++ b/docs/src/content/docs/scenarios/cross-org-auth.md @@ -138,7 +138,7 @@ Squad agents will read this instruction and switch accounts when they detect a c Capture the cross-org auth pattern as a Squad skill. When authentication fails, the skill detects the error and suggests or attempts account switching. -Create `.squad/skills/cross-org-auth-recovery.md`: +Create `.copilot/skills/cross-org-auth-recovery.md`: ```markdown # Cross-Organization Authentication Recovery diff --git a/docs/src/content/docs/scenarios/existing-repo.md b/docs/src/content/docs/scenarios/existing-repo.md index eac0535ea..09932181c 100644 --- a/docs/src/content/docs/scenarios/existing-repo.md +++ b/docs/src/content/docs/scenarios/existing-repo.md @@ -25,7 +25,7 @@ squad ✅ .github/agents/squad.agent.md (v0.2.0) ✅ .github/workflows/ (10 workflows) ✅ .squad/templates/ -✅ .squad/skills/ (starter skills) +✅ .copilot/skills/ (starter skills) ✅ .squad/ceremonies.md ✅ .gitattributes (merge=union rules) diff --git a/docs/src/content/docs/scenarios/monorepo.md b/docs/src/content/docs/scenarios/monorepo.md index 05cc6d4c8..eb70f0036 100644 --- a/docs/src/content/docs/scenarios/monorepo.md +++ b/docs/src/content/docs/scenarios/monorepo.md @@ -142,7 +142,7 @@ All four work in parallel, each in their own service directory. Some patterns apply **across all services**: -`.squad/skills/service-logging-pattern.md`: +`.copilot/skills/service-logging-pattern.md`: ```markdown # Service Logging Pattern @@ -166,7 +166,7 @@ Every service must log: This skill is read by **all agents**, regardless of which service they're working on. Consistent logging across the monorepo. -`.squad/skills/inter-service-communication.md`: +`.copilot/skills/inter-service-communication.md`: ```markdown # Inter-Service Communication diff --git a/docs/src/content/docs/scenarios/solo-dev.md b/docs/src/content/docs/scenarios/solo-dev.md index b770e53a3..340140480 100644 --- a/docs/src/content/docs/scenarios/solo-dev.md +++ b/docs/src/content/docs/scenarios/solo-dev.md @@ -77,7 +77,7 @@ This is the safety net you don't have as a solo dev. Michael catches issues befo After Michael rejects Fredo's code for missing rate limiting, **it gets encoded as a skill**: -`.squad/skills/auth-rate-limiting.md`: +`.copilot/skills/auth-rate-limiting.md`: ```markdown # Authentication Endpoints Must Be Rate-Limited @@ -98,7 +98,7 @@ Next time any agent builds an auth feature, they read this skill first. The mist As a solo dev, you juggle dozens of context switches. Squad doesn't forget: - **Decisions** are logged. "Why did I use PostgreSQL instead of MongoDB?" — check `.squad/decisions.md`. -- **Skills** capture patterns. "How do I structure FastAPI routes?" — check `.squad/skills/`. +- **Skills** capture patterns. "How do I structure FastAPI routes?" — check `.copilot/skills/`. - **Histories** track what each agent learned. Fredo knows your database schema after one session. You get back to a project after 3 weeks, and the team is still up to speed. @@ -161,6 +161,6 @@ Michael's not always right, but having a second opinion is invaluable. - **Start with 3–4 agents.** You don't need a frontend and backend specialist if you're only building one layer. - **The Lead is your reviewer.** Use them to review your own code before committing. -- **Skills are YOUR documentation.** After 10 sessions, `.squad/skills/` is a custom knowledge base. +- **Skills are YOUR documentation.** After 10 sessions, `.copilot/skills/` is a custom knowledge base. - **Agents work while you're thinking.** Give a vague task and agents research in parallel. - **You get testing discipline.** The Tester writes tests you'd skip. Coverage goes up without extra effort. diff --git a/docs/src/content/docs/scenarios/troubleshooting.md b/docs/src/content/docs/scenarios/troubleshooting.md index 36fae207f..54a2df64c 100644 --- a/docs/src/content/docs/scenarios/troubleshooting.md +++ b/docs/src/content/docs/scenarios/troubleshooting.md @@ -156,3 +156,50 @@ If below v20, upgrade to the latest LTS: - Use PowerShell or Git Bash (not cmd.exe) - Ensure git is in your PATH - Ensure `gh` CLI is in your PATH + +--- + +## "⚠ squad pre-commit: refusing to commit two-layer state into the working tree" + +**Problem:** A `git commit` is blocked with the message above. + +**Cause:** You're on the `orphan` or `two-layer` backend, and one or more state files (`.squad/decisions.md`, `.squad/agents/*/history.md`, `.squad/casting/`, `.squad/routing/`) were staged for commit. These files belong on the `squad-state` orphan branch, not in your working branch. Something wrote them back to disk after the migration — a direct `fs.writeFile` call, an editor auto-save, or an external tool — and you staged them unintentionally. + +**Recovery flow:** + +1. **Unstage the state files:** + ```bash + git restore --staged .squad/decisions.md + git restore --staged ".squad/agents/*/history.md" + ``` + +2. **Check whether the orphan branch already has the content** (it should, if `squad sync` has run): + ```bash + git show squad-state:decisions.md + git show squad-state:agents//history.md + ``` + +3. **If the working-tree copy contains new content not yet on the orphan branch**, lift it through Squad before deleting: + ```bash + squad memory write --file .squad/decisions.md + ``` + +4. **Remove the working-tree copies:** + ```bash + # PowerShell + Remove-Item .squad\decisions.md -ErrorAction SilentlyContinue + Get-ChildItem .squad\agents -Recurse -Filter history.md | Remove-Item + ``` + ```bash + # bash + rm -f .squad/decisions.md .squad/agents/*/history.md + ``` + +5. **Commit normally** — the `post-commit` hook will call `squad sync --quiet` automatically: + ```bash + git commit -m "your commit message" + ``` + +**When to use `SQUAD_SYNC_ACTIVE=1`:** Rarely. This env var bypasses both the pre-commit and post-commit hooks. It's intended for internal use by `squad sync` itself to prevent recursion. If you set it to unblock a commit, the state files will land in your working-branch history and appear in PRs — exactly what two-layer is designed to prevent. Use the recovery flow above instead. + +--- diff --git a/docs/src/content/docs/scenarios/upgrading.md b/docs/src/content/docs/scenarios/upgrading.md index c29acde17..567b92978 100644 --- a/docs/src/content/docs/scenarios/upgrading.md +++ b/docs/src/content/docs/scenarios/upgrading.md @@ -153,3 +153,40 @@ No changes to `.ai-team/` — the diff is limited to Squad-owned files. - **Upgrade is safe.** It only overwrites files that Squad owns. Your team state is never modified. - **Don't customize `squad.agent.md`.** Any changes you make will be overwritten on the next upgrade. If you need custom behavior, use directives in `decisions.md` instead. - **Re-running upgrade is harmless.** If you're not sure whether an upgrade completed, run it again. It's idempotent. + +--- + +## After upgrading to `two-layer` or `orphan` + +After running `squad upgrade --state-backend two-layer` (or `--state-backend orphan`), verify the migration completed cleanly: + +### 1. Working tree is clean + +```bash +git status +``` + +You should see no changes. The migration removes the working-tree state files after copying them to the orphan branch — if `.squad/decisions.md` or any `history.md` files still appear as untracked or modified, rerun the migration. + +### 2. Orphan branch contains your state + +```bash +git ls-tree --name-only -r squad-state +``` + +You should see your state files listed (e.g. `decisions.md`, `agents/scribe/history.md`). If the branch is missing or empty, the migration may not have completed. + +### 3. Push the orphan branch and notes once + +The orphan branch and any git notes need to be pushed to the remote so collaborators can access them: + +```bash +git push origin squad-state +git push origin 'refs/notes/squad*:refs/notes/squad*' +``` + +After this, the `post-commit` hook keeps both in sync automatically on every commit. + +### If state files reappear later + +If `.squad/decisions.md` or `history.md` files reappear in the working tree and a `git commit` is blocked with `⚠ squad pre-commit: refusing to commit two-layer state into the working tree`, see the [pre-commit troubleshooting entry](./troubleshooting#squad-pre-commit-refusing-to-commit-two-layer-state-into-the-working-tree) for the recovery flow. diff --git a/docs/src/content/docs/sdk-first-mode.md b/docs/src/content/docs/sdk-first-mode.md index f02c86228..8470a1f8f 100644 --- a/docs/src/content/docs/sdk-first-mode.md +++ b/docs/src/content/docs/sdk-first-mode.md @@ -398,7 +398,7 @@ const gitWorkflow = defineSkill({ | `content` | string | ✅ | The skill body (patterns, examples) | | `tools` | `SkillTool[]` | ❌ | MCP tools relevant to this skill | -Skills defined in `squad.config.ts` are generated to `.squad/skills/{name}/SKILL.md` when you run `squad build`. +Skills defined in `squad.config.ts` are generated to `.copilot/skills/{name}/SKILL.md` when you run `squad build`. --- diff --git a/docs/src/layouts/BaseLayout.astro b/docs/src/layouts/BaseLayout.astro index 26de6816d..8cc598de7 100644 --- a/docs/src/layouts/BaseLayout.astro +++ b/docs/src/layouts/BaseLayout.astro @@ -4,7 +4,7 @@ interface Props { description?: string; } -import { ViewTransitions } from 'astro:transitions'; +import { ClientRouter } from 'astro:transitions'; import squadLogo from '../assets/images/squad-logo.png'; const { title, description = 'Squad — Your AI Development Team. Describe what you\'re building. Get a team of specialists that live in your repo.' } = Astro.props; const fullTitle = `${title} — Squad Docs`; @@ -35,7 +35,7 @@ const ogImage = Astro.site ? new URL(squadLogo.src, Astro.site).href : squadLogo - +