feat: Coordinator-as-Agent export — compile .squad/ into .github/agents/squad.md#1180
feat: Coordinator-as-Agent export — compile .squad/ into .github/agents/squad.md#1180bradygaster wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a new “Coordinator-as-Agent” export mode that compiles .squad/ state into a repo-native Copilot custom agent at .github/agents/squad.md, including watch/check/dry-run safety features.
Changes:
- Introduces a repo-native export pipeline in
squad-sdk(IR loader, prompt compiler with token budget, YAML frontmatter renderer, file writer, watch mode). - Adds
squad export agentCLI subcommand and routessquad exportbetween snapshot (JSON) and coordinator (agent) exports. - Adds Vitest coverage for the coordinator export pipeline (context → prompt → frontmatter → write/check/legacy collision).
Show a summary per file
| File | Description |
|---|---|
| test/repo-native/coordinator-export.test.ts | New end-to-end-ish tests for loading context, compiling prompts, rendering frontmatter, and writing/checking output. |
| packages/squad-sdk/src/repo-native/load-export-context.ts | Loads .squad/ files into a typed IR, resolves skills/model/description, and summarizes charters. |
| packages/squad-sdk/src/repo-native/compile-coordinator-prompt.ts | Builds coordinator prompt markdown with soft/hard token budgets and compaction modes. |
| packages/squad-sdk/src/repo-native/render-frontmatter.ts | Renders Copilot custom-agent YAML frontmatter from coordinator metadata. |
| packages/squad-sdk/src/repo-native/write-coordinator-agent.ts | Writes/checks the generated agent file with safety and legacy collision handling. |
| packages/squad-sdk/src/repo-native/watch-export.ts | Implements --watch rebuild triggers for .squad/ (and skills) file changes. |
| packages/squad-sdk/src/repo-native/types.ts | Adds repo-native export types and public API contracts. |
| packages/squad-sdk/src/repo-native/index.ts | Exposes the repo-native export API. |
| packages/squad-sdk/src/index.ts | Re-exports the repo-native module from the SDK top-level entry. |
| packages/squad-sdk/package.json | Adds ./repo-native subpath export and updates package version. |
| packages/squad-cli/src/cli/commands/export.ts | Routes squad export to either snapshot export or coordinator agent export. |
| packages/squad-cli/src/cli/commands/export-coordinator.ts | New CLI entry point for generating .github/agents/squad.md (check/watch/dry-run/force/etc.). |
| packages/squad-cli/src/cli-entry.ts | Updates help text and forwards args to the new export router. |
Copilot's findings
- Files reviewed: 13/13 changed files
- Comments generated: 9
| const slug = row.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); | ||
| const charterPath = row.charterPath || `.squad/agents/${slug}/charter.md`; | ||
| const charterFullPath = path.join(root, charterPath); | ||
| const charterContent = readIfExists(charterFullPath); |
| lines.push(`name: ${coordinator.displayName}`); | ||
| lines.push(`description: "${escapeYamlString(coordinator.description)}"`); |
| if (coordinator.model) { | ||
| lines.push(`model: ${coordinator.model}`); | ||
| } |
| function renderDispatchRules(context: SquadExportContext): string { | ||
| const lines: string[] = []; | ||
|
|
||
| for (const rule of context.routing.rules) { | ||
| lines.push(`- If the request is mainly about ${rule.workType.toLowerCase()}, route to \`${rule.routeTo.toLowerCase()}\`.`); |
| // Watch top-level squad files | ||
| for (const file of WATCH_PATTERNS) { | ||
| const filePath = path.join(squadRoot, file); | ||
| try { | ||
| const watcher = fs.watch(filePath, { persistent: true }, () => { | ||
| scheduleRebuild(); | ||
| }); | ||
| watchers.push(watcher); | ||
| } catch { | ||
| // File may not exist; that's fine | ||
| } |
| const watcher = fs.watch(agentsDir, { recursive: true, persistent: true }, () => { | ||
| scheduleRebuild(); | ||
| }); |
| if (cleanLegacyAgent) { | ||
| const backupPath = legacyPath + '.bak'; | ||
| fs.renameSync(legacyPath, backupPath); | ||
| result.legacyCleaned = true; | ||
| } |
| // Check mode: compare without writing | ||
| if (check) { | ||
| try { | ||
| const existing = fs.readFileSync(fullOutputPath, 'utf-8'); | ||
| result.driftDetected = existing !== output; | ||
| } catch { | ||
| // File doesn't exist = drift | ||
| result.driftDetected = true; | ||
| } | ||
| return result; | ||
| } |
| * Start watching .squad/ source files for changes. | ||
| * Returns a cleanup function to stop watching. | ||
| */ | ||
| export function startWatchExport(options: WatchExportOptions): () => void { |
- Path traversal: validate charter paths stay within repo root - YAML safety: quote all frontmatter scalar values to prevent injection - Route resolution: resolve routeTo against team members by slug/name - Watch mode: watch directories (not files) for creation/rename detection - Cross-platform: replace recursive fs.watch with manual subdirectory enumeration - Backup safety: use timestamped backup names to prevent data loss - Check mode: distinguish user-owned files from drift with actionable errors - Tests: add coverage for path safety, YAML escaping, watch behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@copilot resolve the merge conflicts in this pull request |
Resolved. I merged |
|
@copilot you should have resolved the conflicts from dev, not from main |
Addressed in |
Extends the existing `squad export` command with a new `agent` branch that compiles .squad/ state into a repository-native Copilot custom agent at .github/agents/squad.md. The export family now supports two modes: - `squad export` / `squad export snapshot` — existing JSON snapshot (unchanged) - `squad export agent` — new coordinator agent export The coordinator export pipeline: 1. Reads .squad/team.md, routing.md, ceremonies.md, config.json, and agent charters 2. Parses into a typed intermediate representation (IR) 3. Compiles a coordinator prompt with token budget management 4. Renders YAML frontmatter + markdown body 5. Writes .github/agents/squad.md with safety checks Features: - Token budget with soft (14k) and hard (20k) limits - Automatic compaction passes for large teams - Lazy-load mode for teams > 8 members - --check mode for CI drift detection - --watch mode for live re-export on file changes - --dry-run for preview without writing - Legacy squad.agent.md collision detection - Deterministic, regenerable output New CLI flags for agent export: --out, --model, --description, --skills, --check, --watch, --dry-run, --force, --clean-legacy-agent, --max-prompt-tokens, --compact Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Path traversal: validate charter paths stay within repo root - YAML safety: quote all frontmatter scalar values to prevent injection - Route resolution: resolve routeTo against team members by slug/name - Watch mode: watch directories (not files) for creation/rename detection - Cross-platform: replace recursive fs.watch with manual subdirectory enumeration - Backup safety: use timestamped backup names to prevent data loss - Check mode: distinguish user-owned files from drift with actionable errors - Tests: add coverage for path safety, YAML escaping, watch behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
54349e4 to
c1bcadf
Compare
- Add changeset for coordinator-as-agent-export (minor for SDK + CLI) - Remove 'coordinator' from user-facing help text (repl-ux-e2e test) - Condense export help to stay within 130-line limit (speed-gates test) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
All code paths that write squad.agent.md now check for an existing exported coordinator (squad.md with generated marker) first. If one exists, squad.agent.md is skipped — the exported file supersedes it. Previously only the export direction was guarded (export renames legacy to .bak). This closes the reverse vectors: init, upgrade, and consult could blindly write squad.agent.md alongside an already-exported squad.md, causing a collision. Vectors fixed: - squad init: skips squad.agent.md when exported coordinator present - squad upgrade: skips refresh when exported coordinator present - squad consult: skips agent file creation when exported coordinator present Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
👋 Friendly nudge — this PR has had no activity for 11 days. What needs attention:
If this PR is abandoned, please close it. If it's blocked on something external, leave a comment so the team knows. |
Summary
Extends the
squad exportcommand family with a new coordinator agent export that compiles.squad/state into a repository-native Copilot custom agent at.github/agents/squad.md.This lets Squad teams export their roster, routing rules, ceremony triggers, and dispatch protocol into a format that works across all Copilot surfaces (CLI, VS Code, GitHub Desktop) without requiring runtime changes.
What's new
CLI
Architecture
The export pipeline:
.squad/team.md,routing.md,ceremonies.md,config.json, and agent charters into a typed IR.github/agents/squad.mdwith safety checks (collision detection, generated-file markers, legacy cleanup)Token budget & compaction
Safety
--forcesquad.agent.mdcollisions--checkmode enables CI enforcement without mutationFiles
New
packages/squad-sdk/src/repo-native/— types, IR loader, prompt compiler, frontmatter renderer, file writer, watch modepackages/squad-cli/src/cli/commands/export-coordinator.ts— CLI entry point for agent exporttest/repo-native/coordinator-export.test.ts— 15 tests covering the full pipelineModified
packages/squad-cli/src/cli/commands/export.ts— forked into a snapshot/agent routerpackages/squad-cli/src/cli-entry.ts— updated help text and arg passingpackages/squad-sdk/src/index.ts— re-exports repo-native modulepackages/squad-sdk/package.json— added./repo-nativesubpath exportTesting
.squad/directory — generates a valid coordinator for a 22-member team