From 01093e04bb47405d1e9f2f00ac8214e238c3605f Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Mon, 9 Mar 2026 07:06:11 -0700 Subject: [PATCH 01/20] docs: comprehensive new-user experience improvements Add 4 new docs pages and update 3 existing: - New: five-minute-start.md (5-minute quickstart) - New: architecture.md (how Squad works) - New: choosing-your-path.md (CLI vs SDK vs Copilot) - New: glossary.md (key terms reference) - Update: troubleshooting.md (common errors table) - Update: first-session.md (jargon definitions) - Update: navigation.ts (sidebar entries) Closes #301 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/content/docs/concepts/architecture.md | 70 +++++++++++++++ .../docs/get-started/choosing-your-path.md | 82 +++++++++++++++++ .../content/docs/get-started/first-session.md | 16 ++-- .../docs/get-started/five-minute-start.md | 87 +++++++++++++++++++ docs/src/content/docs/reference/glossary.md | 39 +++++++++ .../content/docs/scenarios/troubleshooting.md | 12 +++ docs/src/navigation.ts | 4 + test/docs-build.test.ts | 9 +- 8 files changed, 309 insertions(+), 10 deletions(-) create mode 100644 docs/src/content/docs/concepts/architecture.md create mode 100644 docs/src/content/docs/get-started/choosing-your-path.md create mode 100644 docs/src/content/docs/get-started/five-minute-start.md create mode 100644 docs/src/content/docs/reference/glossary.md diff --git a/docs/src/content/docs/concepts/architecture.md b/docs/src/content/docs/concepts/architecture.md new file mode 100644 index 000000000..991891e3b --- /dev/null +++ b/docs/src/content/docs/concepts/architecture.md @@ -0,0 +1,70 @@ +# Architecture + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +How Squad works — one page, no handwaving. + +--- + +## System diagram + +``` +User request + ↓ +Coordinator (routing engine) + ↓ +Spawns agents in parallel + ↓ +Agents read memory (.squad/) → work → write results + ↓ +Scribe merges decisions, Ralph tracks issues + ↓ +Results returned to user +``` + +--- + +## Components + +### Coordinator + +The coordinator is Squad's routing engine. It reads your request, checks routing rules in `.squad/routing.md`, and decides which agents to spawn. If you say "team," it decomposes the work and launches multiple agents in parallel. If you name an agent, it routes directly to them. + +### Agents + +Each agent is a specialist with a charter, role, and persistent memory. Agents are spawned as independent subprocesses with their own context windows and tools. They read `.squad/decisions.md` and their own history before working, then write results back. Agents never see each other's conversations — the coordinator orchestrates coordination. + +### Memory (.squad/) + +All team state lives in `.squad/`. This includes the roster (`team.md`), routing rules (`routing.md`), decisions (`decisions.md`), agent charters and histories (`agents/`), and ceremony schedules (`ceremonies.md`). Agents read this before every spawn. You own these files — edit them anytime. + +### Routing + +Routing rules in `.squad/routing.md` define which agent handles which work. The coordinator reads these rules before spawning. You can override routing by naming an agent directly in your request. + +### Scribe + +The Scribe is a silent agent that tracks decisions and logs sessions. Every team has a Scribe. You never talk to them directly — they work in the background, merging decisions from all agents into `.squad/decisions.md`. + +### Ralph + +Ralph is the work monitor. He watches your GitHub or GitLab issues, tracks work in progress, and alerts the team when something is ready. Every team has a Ralph. He's silent unless you ask him for status. + +--- + +## What happens when you say "Team, build X"? + +1. **Coordinator reads the request** and checks `.squad/routing.md` for decomposition rules. +2. **Coordinator spawns multiple agents in parallel** — one for frontend, one for backend, one for tests, etc. +3. **Each agent reads `.squad/decisions.md`** and their own history (`agents/{name}/history.md`), then works independently. +4. **Agents write results** to their history files and propose decisions. +5. **Scribe merges all decisions** into `.squad/decisions.md`. +6. **Coordinator returns labeled results** to you, tagged with each agent's name. + +--- + +## Learn more + +- [**Work routing**](../features/routing) — How the coordinator decides which agents to spawn +- [**Memory and knowledge**](memory-and-knowledge) — How decisions, skills, and history persist +- [**Parallel work**](parallel-work) — How agents work simultaneously without conflicts diff --git a/docs/src/content/docs/get-started/choosing-your-path.md b/docs/src/content/docs/get-started/choosing-your-path.md new file mode 100644 index 000000000..16eacec5e --- /dev/null +++ b/docs/src/content/docs/get-started/choosing-your-path.md @@ -0,0 +1,82 @@ +# Choose your path + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +CLI, Copilot agent, or SDK? Pick the right mode for your workflow. + +--- + +## Three modes + +### CLI mode + +Install Squad globally or per-project, then use terminal commands to initialize, route work, and manage your team. + +```bash +npm install -g @bradygaster/squad-cli +squad init +squad status +squad watch +``` + +**Use for:** Terminal workflows, automation scripts, CI/CD integration. + +--- + +### Copilot agent mode + +Talk to Squad in GitHub Copilot CLI or VS Code. Squad is built-in as an agent. Your `.squad/` directory works identically to CLI mode. + +```bash +copilot +> /agent Squad + +Squad: Hey Brady, what are you building? +``` + +**Use for:** Conversational workflows, exploratory work, VS Code users. + +--- + +### SDK mode + +Write TypeScript code that spawns agents, routes work, and coordinates teams programmatically. Full access to Squad's internals. + +```bash +npm install @bradygaster/squad-sdk +``` + +```typescript +import { Coordinator } from '@bradygaster/squad-sdk'; + +const coordinator = new Coordinator(); +const result = await coordinator.route('Build a login page'); +``` + +**Use for:** Building tools on Squad, custom integrations, advanced automation. + +--- + +## Decision table + +| **Your goal** | **Use** | +|---------------|---------| +| Try Squad quickly | **Copilot agent** — no install | +| Work in the terminal | **CLI** | +| Work in VS Code | **Copilot agent** | +| Automate repetitive tasks | **CLI** or **SDK** | +| Build custom tooling | **SDK** | +| CI/CD integration | **CLI** or **SDK** | + +--- + +## Can I use multiple modes? + +Yes. Your `.squad/` directory is the source of truth. CLI, Copilot agent, and SDK all read and write the same files. You can switch between modes anytime. + +Example workflow: +1. Use **Copilot agent** to form your team and do exploratory work +2. Use **CLI** (`squad watch`) to monitor issues in the background +3. Use **SDK** to build a custom deployment script that spawns agents + +All three modes share the same memory and decisions. diff --git a/docs/src/content/docs/get-started/first-session.md b/docs/src/content/docs/get-started/first-session.md index 030e70479..6c62bbebc 100644 --- a/docs/src/content/docs/get-started/first-session.md +++ b/docs/src/content/docs/get-started/first-session.md @@ -48,7 +48,7 @@ Start the Copilot CLI: copilot ``` -Select **Squad** from the `/agent` list (CLI) or `/agents` (VS Code). Squad greets you by name (pulled from `git config user.name`): +Select **Squad** from the `/agent` list (CLI) or `/agents` (VS Code). Squad greets you by name: ``` Hey Brady, what are you building? @@ -61,7 +61,7 @@ Describe your project: > post recipes, search by ingredient, and save favorites. ``` -Squad proposes a team. Names come from a fictional universe — the exact universe depends on your project shape and history: +Squad proposes a team. The coordinator analyzes your description and suggests specialists: ``` Here's your team: @@ -72,6 +72,8 @@ Here's your team: 🧪 Lambert — Tester Tests, quality, edge cases 📋 Scribe — (silent) Memory, decisions, session logs +**Scribe** and **Ralph** are always on every roster — you don't manage them. + Look right? Say yes, add someone, or change a role. (Or just give me a task to start!) ``` @@ -86,7 +88,7 @@ Say "yes" or skip straight to a task (which is implicit confirmation): > Yes. Dallas, set up the Express server with basic routing. ``` -Squad creates the `.squad/` directory structure — team roster, routing rules, casting state, ceremony config, agent charters, and histories — all seeded with your project context. +Squad creates the `.squad/` directory structure — team roster, routing rules, casting state, ceremony config, agent charters, and histories — all seeded with your project context. Each agent spawns to do their work. ### What's inside .squad/? @@ -177,7 +179,7 @@ At any point, check status: > Where are we? ``` -This is a Direct mode response — no agent spawn, just the coordinator reading recent logs: +The coordinator reads recent logs: ``` Last session: Recipe listing page (15 minutes ago) @@ -274,7 +276,7 @@ squad export 5 agents, 3 skills, 6 decisions ``` -This snapshot contains everything — charters, histories, casting state, skills, and decisions. Import into another repo anytime: +This snapshot contains charters, histories, casting state, skills, and decisions. Import into another repo anytime: ```bash cd ../other-project @@ -286,8 +288,8 @@ squad import ../my-app/squad-export.json ## Tips -- **First session is the slowest.** Agents have no history yet. After 2–3 sessions, they know your conventions and stop asking repeated questions. -- **Commit `.squad/`.** It's your team's brain. Anyone who clones the repo gets the team with all their knowledge. +- **First session is slowest.** Agents have no history yet. After 2–3 sessions, they know your conventions. +- **Commit `.squad/`** — your team's brain. Anyone who clones the repo gets the full team. - **Say "team" for big tasks.** The word "team" triggers parallel fan-out across multiple agents. - **Name an agent for focused work.** `"Dallas, fix the login bug"` sends work to one specific agent. - **Directives are sticky.** Once captured, they persist across all future sessions. diff --git a/docs/src/content/docs/get-started/five-minute-start.md b/docs/src/content/docs/get-started/five-minute-start.md new file mode 100644 index 000000000..29667ea95 --- /dev/null +++ b/docs/src/content/docs/get-started/five-minute-start.md @@ -0,0 +1,87 @@ +# Quick start + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +Your first 5 minutes with Squad. Prove it works before you learn anything. + +--- + +## Prerequisites + +- **Node.js 20+** — Check with `node --version` +- **Git repository** — New or existing + +--- + +## Install + +```bash +npm install --save-dev @bradygaster/squad-cli +``` + +Then initialize: + +```bash +npx squad init +``` + +You'll see: + +``` +✅ Squad installed. + .github/agents/squad.agent.md — coordinator agent + .squad/templates/ — 11 template files + +Open GitHub Copilot and select Squad from the agent list. +``` + +--- + +## Validate + +Check that Squad created your team directory: + +```bash +ls .squad/ +``` + +You should see: `team.md`, `routing.md`, `decisions.md`, `agents/`, and more. + +Confirm Squad is ready: + +```bash +npx squad status +``` + +--- + +## Try it + +Open GitHub Copilot in your terminal or VS Code. Select **Squad** from the agent list (`/agent Squad` in CLI or `/agents` in VS Code). + +Say something simple: + +``` +> I'm building a task management app with React and Node.js. +> Users can create, update, and delete tasks. +``` + +Squad forms your team and responds with agent names and roles. Say yes, or just give your first task: + +``` +> Team, create a basic Express server with a /health endpoint. +``` + +Squad spawns agents and does the work. + +--- + +## What just happened? + +Squad read your description, formed a team of specialists, wrote their charters to `.squad/agents/`, and coordinated parallel work. Check `.squad/decisions.md` to see what they decided. + +--- + +## Next steps + +[**Your first session**](first-session) — Step-by-step walkthrough of parallel work, decisions, and memory. diff --git a/docs/src/content/docs/reference/glossary.md b/docs/src/content/docs/reference/glossary.md new file mode 100644 index 000000000..7a5ef32f9 --- /dev/null +++ b/docs/src/content/docs/reference/glossary.md @@ -0,0 +1,39 @@ +# Glossary + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + +Key terms defined in one sentence each. Alphabetical order. + +--- + +**Agent** — A specialist AI team member with a role, charter, and persistent memory that handles specific types of work. + +**Casting** — The process of forming your team by proposing agents, confirming roles, and writing their charters to `.squad/`. + +**Ceremony** — A scheduled team event like retrospectives, reviews, or planning sessions defined in `.squad/ceremonies.md`. + +**Coordinator** — Squad's routing engine that reads your request, checks routing rules, and spawns the right agents. + +**Decisions** — Architectural choices, conventions, and directives captured in `.squad/decisions.md` that all agents read before working. + +**Directive** — A persistent rule or convention you give the team (like "Always use Zod for validation") that gets written to `decisions.md`. + +**History** — Each agent's memory of past work, stored in `.squad/agents/{name}/history.md` and read before every spawn. + +**Memory** — All persistent team state stored in the `.squad/` directory, including roster, routing rules, decisions, and agent histories. + +**Ralph** — The silent work monitor agent that watches your GitHub or GitLab issues and tracks work in progress. + +**Routing** — Rules in `.squad/routing.md` that define which agent handles which type of work, read by the coordinator before spawning. + +**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. + +**Spawn** — The act of starting an agent as an independent subprocess with its own context window, tools, and memory. + +**Squad** — Your AI development team, coordinated through the Squad framework. + +**.squad/ directory** — The root directory containing all team state: roster, routing, decisions, agent charters and histories, and ceremony config. + +**Team** — The collection of agents working on your project, defined in `.squad/team.md`. diff --git a/docs/src/content/docs/scenarios/troubleshooting.md b/docs/src/content/docs/scenarios/troubleshooting.md index 921dc617f..913e4c3c2 100644 --- a/docs/src/content/docs/scenarios/troubleshooting.md +++ b/docs/src/content/docs/scenarios/troubleshooting.md @@ -4,6 +4,18 @@ Common issues and fixes for Squad installation and usage. --- +## Quick fixes + +| Error | Cause | Fix | +|-------|-------|-----| +| `squad: command not found` | Squad CLI not installed or not in PATH | Run `npm install -g @bradygaster/squad-cli` or use `npx @bradygaster/squad-cli` | +| `No .squad/ directory found` | Not in a git repo or Squad not initialized | Run `git init` then `npx squad init` | +| `Cannot find agent "{name}"` | Agent doesn't exist in `.squad/agents/` | Check `.squad/team.md` for roster, or re-run casting | +| `gh: command not found` | GitHub CLI not installed | Install from [cli.github.com](https://cli.github.com/) then `gh auth login` | +| `Node.js version error` | Node.js version below v20 | Upgrade Node.js to v20+ (see below) | + +--- + ## `npx github:bradygaster/squad` appears to hang **Problem:** Running the install command shows a frozen npm spinner. Nothing happens. diff --git a/docs/src/navigation.ts b/docs/src/navigation.ts index 56d5a88dd..5e975ec0b 100644 --- a/docs/src/navigation.ts +++ b/docs/src/navigation.ts @@ -14,7 +14,9 @@ export const NAV_SECTIONS: NavSection[] = [ title: 'Get Started', dir: 'get-started', items: [ + { title: 'Quick start', slug: 'get-started/five-minute-start' }, { title: 'Installation', slug: 'get-started/installation' }, + { title: 'Choose your path', slug: 'get-started/choosing-your-path' }, { title: 'Your First Session', slug: 'get-started/first-session' }, { title: 'Migration Guide', slug: 'get-started/migration' }, ], @@ -78,6 +80,7 @@ export const NAV_SECTIONS: NavSection[] = [ { title: 'SDK Integration', slug: 'reference/integration' }, { title: 'Tools & Hooks', slug: 'reference/tools-and-hooks' }, { title: 'Config', slug: 'reference/config' }, + { title: 'Glossary', slug: 'reference/glossary' }, ], }, { @@ -115,6 +118,7 @@ export const NAV_SECTIONS: NavSection[] = [ title: 'Concepts', dir: 'concepts', items: [ + { title: 'Architecture', slug: 'concepts/architecture' }, { title: 'Your Team', slug: 'concepts/your-team' }, { title: 'Memory & Knowledge', slug: 'concepts/memory-and-knowledge' }, { title: 'Parallel Work', slug: 'concepts/parallel-work' }, diff --git a/test/docs-build.test.ts b/test/docs-build.test.ts index 5a14ce65f..a1982bdd8 100644 --- a/test/docs-build.test.ts +++ b/test/docs-build.test.ts @@ -15,16 +15,18 @@ const BLOG_CONTENT_DIR = join(CONTENT_DIR, 'blog'); const DIST_DIR = join(DOCS_DIR, 'dist'); // Expected content directories in src/content/docs/ -const EXPECTED_GET_STARTED = ['installation', 'first-session']; +const EXPECTED_GET_STARTED = ['installation', 'first-session', 'five-minute-start', 'choosing-your-path', 'migration']; const EXPECTED_GUIDES = ['tips-and-tricks', 'sample-prompts', 'personal-squad', 'contributing', 'contributors', 'shell']; -const EXPECTED_REFERENCE = ['cli', 'sdk', 'config', 'api-reference', 'integration', 'tools-and-hooks']; +const EXPECTED_REFERENCE = ['cli', 'sdk', 'config', 'api-reference', 'integration', 'tools-and-hooks', 'glossary']; const EXPECTED_SCENARIOS = [ 'issue-driven-dev', 'existing-repo', 'ci-cd-integration', 'solo-dev', 'monorepo', 'team-of-humans', ]; +const EXPECTED_CONCEPTS = ['architecture', 'your-team', 'memory-and-knowledge', 'parallel-work', 'github-workflow', 'portability']; + // Blog posts are discovered dynamically to avoid breaking tests when posts change const EXPECTED_BLOG = existsSync(BLOG_CONTENT_DIR) ? readdirSync(BLOG_CONTENT_DIR) @@ -43,7 +45,7 @@ function getMarkdownFiles(section: string): string[] { } function getAllMarkdownFiles(): string[] { - const sections = ['get-started', 'guide', 'reference', 'scenarios']; + const sections = ['get-started', 'guide', 'reference', 'scenarios', 'concepts']; const allFiles: string[] = []; for (const section of sections) { allFiles.push(...getMarkdownFiles(section)); @@ -169,6 +171,7 @@ describe('Docs Build Script (Astro)', () => { ...EXPECTED_GUIDES.map(n => ({ dir: 'guide', name: n })), ...EXPECTED_REFERENCE.map(n => ({ dir: 'reference', name: n })), ...EXPECTED_SCENARIOS.map(n => ({ dir: 'scenarios', name: n })), + ...EXPECTED_CONCEPTS.map(n => ({ dir: 'concepts', name: n })), ]; for (const { dir, name } of allExpected) { const htmlPath = join(DIST_DIR, 'docs', dir, name, 'index.html'); From a8020c72c62d457ea0b2bc24469c7929fcffbfe1 Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Mon, 9 Mar 2026 14:44:35 -0700 Subject: [PATCH 02/20] fix(cli): eliminate terminal scroll flicker and rerender storm Root cause: four independent issues combined into a scroll storm: 1. Ink fullscreen clearTerminal path firing every render cycle 2. ~16 unsynchronized animation re-renders/sec from 3 timers 3. Unstable Static component keys causing Ink remounts 4. Layout shift from height toggling between processing states Changes: - Patch Ink fullscreen path (disable clearTerminal, incrementalRendering, trailing newline) - Widen spinner/animation intervals (80->150ms spinner, 500->800ms pulse) - Share terminal dimension hook with 150ms debounce - Pin root height to prevent logUpdate cursor drift - Keep conversation in live viewport (not Static scrollback) - Stable UUID-based Static keys, responsive maxVisible - Fix bump-build.mjs to produce valid semver prerelease format - Fix marketplace test for Windows path compat Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CHANGELOG.md | 8 ++ packages/squad-cli/package.json | 2 +- .../squad-cli/scripts/patch-ink-rendering.mjs | 115 ++++++++++++++++++ .../src/cli/shell/components/AgentPanel.tsx | 27 +++- .../src/cli/shell/components/App.tsx | 65 ++++++---- .../src/cli/shell/components/InputPrompt.tsx | 2 +- .../cli/shell/components/MessageStream.tsx | 74 +++++------ packages/squad-cli/src/cli/shell/index.ts | 6 + packages/squad-cli/src/cli/shell/terminal.ts | 64 +++++----- scripts/bump-build.mjs | 6 +- test/bump-build.test.ts | 12 +- test/cli-packaging-smoke.test.ts | 8 +- test/marketplace.test.ts | 4 +- test/repl-ux.test.ts | 10 +- 14 files changed, 301 insertions(+), 102 deletions(-) create mode 100644 packages/squad-cli/scripts/patch-ink-rendering.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d9e54fbe..06fbf739c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. +## [Unreleased] + +### Fixed — CLI Terminal Rendering +- Eliminated scroll-to-top flicker caused by Ink's fullscreen `clearTerminal` path firing on every render cycle +- Reduced re-render churn via memoized elapsed-time display (one-second granularity gate) and consolidated animation intervals +- Stabilized component keys (timestamp-based instead of shifting array indices) to prevent Ink remounts +- Pinned live viewport height to keep input prompt above fold on all terminal sizes + ## [0.8.24] - 2026-03-08 ### Added — Azure DevOps Platform Adapter diff --git a/packages/squad-cli/package.json b/packages/squad-cli/package.json index 752984d19..3b98ffb33 100644 --- a/packages/squad-cli/package.json +++ b/packages/squad-cli/package.json @@ -148,7 +148,7 @@ "README.md" ], "scripts": { - "postinstall": "node scripts/patch-esm-imports.mjs", + "postinstall": "node scripts/patch-esm-imports.mjs && node scripts/patch-ink-rendering.mjs", "prepublishOnly": "npm run build", "build": "tsc -p tsconfig.json && npm run postbuild", "postbuild": "node -e \"require('fs').cpSync('src/remote-ui', 'dist/remote-ui', {recursive: true})\"" diff --git a/packages/squad-cli/scripts/patch-ink-rendering.mjs b/packages/squad-cli/scripts/patch-ink-rendering.mjs new file mode 100644 index 000000000..a545a03e6 --- /dev/null +++ b/packages/squad-cli/scripts/patch-ink-rendering.mjs @@ -0,0 +1,115 @@ +#!/usr/bin/env node + +/** + * Ink Rendering Patcher for Squad CLI + * + * Patches ink/build/ink.js to fix scroll flicker on Windows Terminal. + * Three patches are applied: + * + * 1. Remove trailing newline — the extra '\n' appended to output causes + * logUpdate's previousLineCount to be off by one, pushing the bottom of + * the UI below the viewport. + * + * 2. Disable clearTerminal fullscreen path — when output fills the terminal, + * Ink clears the entire screen, causing violent scroll-to-top flicker. + * We force the condition to `false` so logUpdate's incremental + * erase-and-rewrite is always used instead. + * + * 3. Verify incrementalRendering passthrough — confirms that Ink forwards + * the incrementalRendering option to logUpdate.create(). No code change + * needed if already wired up. + * + * All patches are idempotent (safe to run multiple times). + */ + +import { readFileSync, writeFileSync, existsSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +function patchInkRendering() { + // Try multiple possible locations (npm workspaces can hoist dependencies) + const possiblePaths = [ + // squad-cli package node_modules + join(__dirname, '..', 'node_modules', 'ink', 'build', 'ink.js'), + // Workspace root node_modules (common with npm workspaces) + join(__dirname, '..', '..', '..', 'node_modules', 'ink', 'build', 'ink.js'), + // Global install location (node_modules at parent of package) + join(__dirname, '..', '..', 'ink', 'build', 'ink.js'), + ]; + + const inkJsPath = possiblePaths.find(p => existsSync(p)) ?? null; + + if (!inkJsPath) { + // ink not installed yet — exit silently + return false; + } + + try { + let content = readFileSync(inkJsPath, 'utf8'); + let patchCount = 0; + + // --- Patch 1: Remove trailing newline --- + // Original: const outputToRender = output + '\n'; + // Patched: const outputToRender = output; + const trailingNewlineSearch = "const outputToRender = output + '\\n';"; + const trailingNewlineReplace = 'const outputToRender = output;'; + if (content.includes(trailingNewlineSearch)) { + content = content.replace(trailingNewlineSearch, trailingNewlineReplace); + console.log(' ✅ Patch 1/3: Removed trailing newline from outputToRender'); + patchCount++; + } else if (content.includes(trailingNewlineReplace)) { + console.log(' ⏭️ Patch 1/3: Trailing newline already removed'); + } else { + console.warn(' ⚠️ Patch 1/3: Could not find outputToRender pattern — Ink version may have changed'); + } + + // --- Patch 2: Disable clearTerminal fullscreen path --- + // Original: if (isFullscreen) { + // const sync = shouldSynchronize(this.options.stdout); + // ... + // this.options.stdout.write(ansiEscapes.clearTerminal + ... + // Patched: if (false) { + // + // We match `if (isFullscreen) {` only when followed by the clearTerminal + // usage to avoid replacing unrelated isFullscreen references. + const fullscreenSearch = /if \(isFullscreen\) \{\s*\n\s*const sync = shouldSynchronize/; + const fullscreenAlreadyPatched = /if \(false\) \{\s*\n\s*const sync = shouldSynchronize/; + if (fullscreenSearch.test(content)) { + content = content.replace( + /if \(isFullscreen\) (\{\s*\n\s*const sync = shouldSynchronize)/, + 'if (false) $1' + ); + console.log(' ✅ Patch 2/3: Disabled clearTerminal fullscreen path'); + patchCount++; + } else if (fullscreenAlreadyPatched.test(content)) { + console.log(' ⏭️ Patch 2/3: clearTerminal path already disabled'); + } else { + console.warn(' ⚠️ Patch 2/3: Could not find isFullscreen pattern — Ink version may have changed'); + } + + // --- Patch 3: Verify incrementalRendering passthrough --- + const incrementalPattern = 'incremental: options.incrementalRendering'; + if (content.includes(incrementalPattern)) { + console.log(' ✅ Patch 3/3: incrementalRendering passthrough verified (no change needed)'); + } else { + console.warn(' ⚠️ Patch 3/3: incrementalRendering passthrough not found — Ink version may have changed'); + } + + if (patchCount > 0) { + writeFileSync(inkJsPath, content, 'utf8'); + console.log(`✅ Patched ink.js with ${patchCount} rendering fix(es) for scroll flicker`); + return true; + } + + return false; + } catch (err) { + console.warn('⚠️ Failed to patch ink.js rendering:', err.message); + console.warn(' Scroll flicker may occur on Windows Terminal.'); + return false; + } +} + +// Run patch +patchInkRendering(); diff --git a/packages/squad-cli/src/cli/shell/components/AgentPanel.tsx b/packages/squad-cli/src/cli/shell/components/AgentPanel.tsx index 61a710541..8681b9019 100644 --- a/packages/squad-cli/src/cli/shell/components/AgentPanel.tsx +++ b/packages/squad-cli/src/cli/shell/components/AgentPanel.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { Box, Text } from 'ink'; import { getRoleEmoji } from '../lifecycle.js'; import { isNoColor, useLayoutTier } from '../terminal.js'; @@ -21,10 +21,10 @@ const PulsingDot: React.FC = () => { useEffect(() => { if (noColor) return; - // 500ms interval reduces re-renders vs 300ms (#206) + // 800ms interval reduces re-renders vs 500ms (fix-cli-scroll-rerender-storm) const timer = setInterval(() => { setFrame(f => (f + 1) % PULSE_FRAMES.length); - }, 500); + }, 800); return () => clearInterval(timer); }, [noColor]); @@ -49,12 +49,27 @@ export const AgentPanel: React.FC = ({ agents, streamingContent const noColor = isNoColor(); const tier = useLayoutTier(); - // Tick every second to update elapsed times - const [, setTick] = useState(0); + // Re-render gate: store elapsed strings in a ref so the timer only triggers + // a React re-render (via the tick counter) when a visible value changes. + const elapsedRef = useRef(new Map()); + const [, setElapsedTick] = useState(0); + useEffect(() => { const hasActive = agents.some(a => a.status === 'working' || a.status === 'streaming'); if (!hasActive) return; - const timer = setInterval(() => setTick(t => t + 1), 1000); + const timer = setInterval(() => { + let changed = false; + for (const a of agents) { + if (a.status === 'working' || a.status === 'streaming') { + const display = formatElapsed(agentElapsedSec(a)); + if (elapsedRef.current.get(a.name) !== display) { + elapsedRef.current.set(a.name, display); + changed = true; + } + } + } + if (changed) setElapsedTick(t => t + 1); + }, 1000); return () => clearInterval(timer); }, [agents]); diff --git a/packages/squad-cli/src/cli/shell/components/App.tsx b/packages/squad-cli/src/cli/shell/components/App.tsx index 69bb2c42e..ab252a507 100644 --- a/packages/squad-cli/src/cli/shell/components/App.tsx +++ b/packages/squad-cli/src/cli/shell/components/App.tsx @@ -1,5 +1,5 @@ import React, { useState, useCallback, useEffect, useRef, useMemo } from 'react'; -import { Box, Text, Static, useApp, useInput } from 'ink'; +import { Box, Text, Static, useApp, useInput, useStdout } from 'ink'; import { AgentPanel } from './AgentPanel.js'; import { MessageStream, renderMarkdownInline, formatDuration } from './MessageStream.js'; import { InputPrompt } from './InputPrompt.js'; @@ -282,12 +282,16 @@ export const App: React.FC = ({ registry, renderer, teamRoot, version, const width = useTerminalWidth(); const tier = useLayoutTier(); const terminalHeight = useTerminalHeight(); - const contentWidth = tier === 'wide' ? Math.min(width, 120) : tier === 'normal' ? Math.min(width, 80) : width; - - // Budget live region height so InputPrompt is never pushed off-screen. - // Reserve 3 rows for InputPrompt (prompt line + hint + padding). - const INPUT_RESERVED_ROWS = 3; - const liveContentHeight = Math.max(terminalHeight - INPUT_RESERVED_ROWS, 4); + // Cap contentWidth at Ink's stdout columns to prevent text overflow/clipping. + // In tests, Ink renders at 100 columns while process.stdout.columns may differ. + const { stdout: inkStdout } = useStdout(); + const renderWidth = inkStdout && 'columns' in inkStdout + ? (inkStdout as { columns?: number }).columns ?? width + : width; + const contentWidth = Math.min( + tier === 'wide' ? Math.min(width, 120) : tier === 'normal' ? Math.min(width, 80) : width, + renderWidth, + ); // Prefer lead/coordinator for first-run hint, fall back to first agent const leadAgent = welcome?.agents.find(a => @@ -299,8 +303,7 @@ export const App: React.FC = ({ registry, renderer, teamRoot, version, // Determine ThinkingIndicator phase based on SDK connection state const thinkingPhase: ThinkingPhase = !onDispatch ? 'connecting' : 'routing'; - // Derive @mention hint from last user message (needed because MessageStream - // receives messages=[] after the Static scrollback refactor). + // Derive @mention hint from last user message. const mentionHint = useMemo(() => { if (!processing) return undefined; const lastUser = [...messages].reverse().find(m => m.role === 'user'); @@ -311,13 +314,12 @@ export const App: React.FC = ({ registry, renderer, teamRoot, version, return undefined; }, [messages, processing]); - // Combine archived + current messages for Static rendering. - // This array only grows — archival moves items between the two source arrays - // but the combined list stays stable, which is required by Ink's Static tracking. - const staticMessages = useMemo( - () => [...archivedMessages, ...messages], - [archivedMessages, messages], - ); + // Only archived (overflow) messages go to Static scrollback. + // Current messages stay in the live region so the user can always see + // the recent conversation without scrolling. This prevents the + // "conversation vanishes" problem where every re-render forced the + // viewport to the bottom, hiding Static scrollback content. + const staticMessages = archivedMessages; const roleMap = useMemo(() => new Map((agents ?? []).map(a => [a.name, a.role])), [agents]); // Memoize the header box — rendered once into Static scroll buffer at the top. @@ -381,13 +383,28 @@ export const App: React.FC = ({ registry, renderer, teamRoot, version, const allStaticItems = useMemo((): StaticItem[] => { const items: StaticItem[] = [{ kind: 'header', key: 'welcome-header' }]; for (let i = 0; i < staticMessages.length; i++) { - items.push({ kind: 'msg', key: `${sessionId}-${i}`, msg: staticMessages[i]!, idx: i }); + // Use timestamp + index-at-creation for stable keys that don't shift + // when new messages are added (array only grows via append) + const msg = staticMessages[i]!; + const stableKey = `${sessionId}-${msg.timestamp.getTime()}-${i}`; + items.push({ kind: 'msg', key: stableKey, msg, idx: i }); } return items; }, [staticMessages, sessionId]); + // Fill the entire viewport. Ink's fullscreen clearTerminal path and + // trailing-newline behavior have been patched out of ink.js, so we can + // safely use the full terminal height without triggering scroll-to-top. + // logUpdate tracks exactly rootHeight lines and erases/rewrites them + // on each render cycle without cursor drift. + const rootHeight = Math.max(terminalHeight, 8); + + // Derive maxVisible from terminal height so taller terminals show more + // conversation context. Reserve ~8 rows for header/input/agent-panel chrome. + const maxVisible = Math.max(Math.floor((terminalHeight - 8) / 3), 3); + return ( - + {/* Static block: header first (stays at top of scroll buffer), then messages */} {(item) => { @@ -443,16 +460,20 @@ export const App: React.FC = ({ registry, renderer, teamRoot, version, }} - {/* Live region: bounded height only while processing so InputPrompt stays in viewport; - auto-sized when idle to avoid blank space below the agent panel. */} - + {/* Live region: always height-constrained to prevent layout shift flicker + when processing state toggles. InputPrompt stays pinned at bottom. + Messages are kept here (not in Static) so the user can always see the + recent conversation without scrolling. maxVisible caps the message + count to prevent overflow into the InputPrompt area. */} + - + {/* Fixed input box at bottom — Copilot/Claude style */} a.name)} messageCount={messages.length} /> + {/* version is shown in the Static header — no footer duplicate needed */} ); }; diff --git a/packages/squad-cli/src/cli/shell/components/InputPrompt.tsx b/packages/squad-cli/src/cli/shell/components/InputPrompt.tsx index 7b0e9b1af..7c926ba22 100644 --- a/packages/squad-cli/src/cli/shell/components/InputPrompt.tsx +++ b/packages/squad-cli/src/cli/shell/components/InputPrompt.tsx @@ -82,7 +82,7 @@ export const InputPrompt: React.FC = ({ if (!disabled || noColor) return; const timer = setInterval(() => { setSpinFrame(f => (f + 1) % SPINNER_FRAMES.length); - }, 80); + }, 150); return () => clearInterval(timer); }, [disabled, noColor]); diff --git a/packages/squad-cli/src/cli/shell/components/MessageStream.tsx b/packages/squad-cli/src/cli/shell/components/MessageStream.tsx index 94aae89bc..0ee0731f7 100644 --- a/packages/squad-cli/src/cli/shell/components/MessageStream.tsx +++ b/packages/squad-cli/src/cli/shell/components/MessageStream.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef, useMemo } from 'react'; import { Box, Text } from 'ink'; import { getRoleEmoji } from '../lifecycle.js'; import { isNoColor, useTerminalWidth, useLayoutTier, type LayoutTier } from '../terminal.js'; @@ -201,39 +201,41 @@ export const MessageStream: React.FC = ({ maxVisible = 50, }) => { const visible = messages.slice(-maxVisible); - const roleMap = new Map((agents ?? []).map(a => [a.name, a.role])); + const visibleOffset = Math.max(0, messages.length - maxVisible); + const roleMap = useMemo(() => new Map((agents ?? []).map(a => [a.name, a.role])), [agents]); // Message fade-in: new messages start dim for 200ms const fadingCount = useMessageFade(messages.length); - // Elapsed time tracking for the ThinkingIndicator + // Elapsed time tracking for the ThinkingIndicator. + // Only update state when the rounded seconds value changes to avoid + // unnecessary re-renders that cause terminal scroll flicker. const [elapsedMs, setElapsedMs] = useState(0); const processingStartRef = useRef(Date.now()); + const lastElapsedSecRef = useRef(0); useEffect(() => { if (processing) { processingStartRef.current = Date.now(); + lastElapsedSecRef.current = 0; setElapsedMs(0); - // Update once per second — reduces re-renders that cause flicker (#206) const timer = setInterval(() => { - setElapsedMs(Date.now() - processingStartRef.current); + const now = Date.now() - processingStartRef.current; + const sec = Math.floor(now / 1000); + if (sec !== lastElapsedSecRef.current) { + lastElapsedSecRef.current = sec; + setElapsedMs(now); + } }, 1000); return () => clearInterval(timer); } else { setElapsedMs(0); + lastElapsedSecRef.current = 0; } }, [processing]); - // Build activity hint: prefer explicit hint, then infer from agent @mention - const resolvedHint = (() => { - if (activityHint) return activityHint; - const lastUser = [...messages].reverse().find(m => m.role === 'user'); - if (lastUser) { - const atMatch = lastUser.content.match(/^@(\w+)/); - if (atMatch?.[1]) return `${atMatch[1]} is thinking...`; - } - return undefined; - })(); + // Activity hint comes from the parent (App.tsx derives @mention hints + // via `mentionHint` and passes them through `activityHint`). // Compute response duration: time from previous user message to this agent message const getResponseDuration = (index: number): string | null => { @@ -263,26 +265,28 @@ export const MessageStream: React.FC = ({ const isFading = fadingCount > 0 && i >= visible.length - fadingCount; return ( - + {isNewTurn && } - - {msg.role === 'user' ? ( - <> - ❯ - {msg.content} - - ) : msg.role === 'system' ? ( - <> - {msg.content} - - ) : ( - <> - {emoji ? `${emoji} ` : ''}{(msg.agentName === 'coordinator' ? 'Squad' : msg.agentName) ?? 'agent'}: - {renderMarkdownInline(wrapTableContent(msg.content, contentWidth, tier))} - {duration && ({duration})} - - )} - + {msg.role === 'system' ? ( + + {msg.content} + + ) : ( + + {msg.role === 'user' ? ( + <> + ❯ + {msg.content} + + ) : ( + <> + {emoji ? `${emoji} ` : ''}{(msg.agentName === 'coordinator' ? 'Squad' : msg.agentName) ?? 'agent'}: + {renderMarkdownInline(wrapTableContent(msg.content, contentWidth, tier))} + {duration && ({duration})} + + )} + + )} ); })} @@ -321,7 +325,7 @@ export const MessageStream: React.FC = ({ )} diff --git a/packages/squad-cli/src/cli/shell/index.ts b/packages/squad-cli/src/cli/shell/index.ts index d17f9e397..b9960c417 100644 --- a/packages/squad-cli/src/cli/shell/index.ts +++ b/packages/squad-cli/src/cli/shell/index.ts @@ -1139,6 +1139,7 @@ export async function runShell(): Promise { // Clear terminal and scrollback — prevents old scaffold output from // bleeding through above the header box in extended sessions. + // Also ensures we start from a clean viewport before Ink renders. process.stdout.write('\x1b[2J\x1b[3J\x1b[H'); const { waitUntilExit } = render( @@ -1205,6 +1206,11 @@ export async function runShell(): Promise { onRestoreSession, }), ), + // NOTE: Both incrementalRendering AND Ink's trailing-newline have been + // patched via scripts/patch-ink-rendering.mjs (runs on postinstall). + // This means: (a) logUpdate uses standard erase-and-rewrite, (b) no + // trailing '\n' is appended to output, (c) no clearTerminal scroll-to-top. + // patchConsole: false ensures console.log doesn't corrupt Ink's rendering. { exitOnCtrlC: false, patchConsole: false }, ); diff --git a/packages/squad-cli/src/cli/shell/terminal.ts b/packages/squad-cli/src/cli/shell/terminal.ts index 6bed88479..0d40042ab 100644 --- a/packages/squad-cli/src/cli/shell/terminal.ts +++ b/packages/squad-cli/src/cli/shell/terminal.ts @@ -18,49 +18,50 @@ export function getTerminalWidth(): number { return Math.max(process.stdout.columns || 80, 40); } -/** React hook — returns live terminal width, updates on resize. */ -export function useTerminalWidth(): number { - const [width, setWidth] = useState(getTerminalWidth()); - - useEffect(() => { - const onResize = () => setWidth(getTerminalWidth()); - // Avoid MaxListenersExceededWarning in test environments with many renders - const prev = process.stdout.getMaxListeners?.() ?? 10; - if (prev <= 20) process.stdout.setMaxListeners?.(prev + 10); - process.stdout.on('resize', onResize); - return () => { - process.stdout.off('resize', onResize); - }; - }, []); - - return width; -} +/** + * Default row count used when `process.stdout.rows` is undefined + * (e.g. piped output, test harnesses). 50 rows ensures the live + * viewport has enough room for content like /help. + */ +const DEFAULT_TERMINAL_ROWS = 50; -/** Current terminal height, clamped to a minimum of 10. */ +/** Current terminal height, clamped to a minimum of 10. + * Fallback of DEFAULT_TERMINAL_ROWS when rows is undefined (test/pipe environments) + * ensures the live viewport has enough room for content like /help. */ export function getTerminalHeight(): number { - return Math.max(process.stdout.rows || 24, 10); + return Math.max(process.stdout.rows || DEFAULT_TERMINAL_ROWS, 10); } -/** React hook — returns live terminal height, updates on resize. */ -export function useTerminalHeight(): number { - const [height, setHeight] = useState(getTerminalHeight()); - +/** + * Shared hook that subscribes to `process.stdout` resize events and + * returns the current value of `getter()`, debounced at 150 ms. + * Extracted from the formerly-duplicated useTerminalWidth / useTerminalHeight hooks. + */ +function useTerminalDimension(getter: () => number): number { + const [value, setValue] = useState(getter()); useEffect(() => { - const onResize = () => setHeight(getTerminalHeight()); + let timer: ReturnType | null = null; + const onResize = () => { + if (timer) clearTimeout(timer); + timer = setTimeout(() => setValue(getter()), 150); + }; const prev = process.stdout.getMaxListeners?.() ?? 10; if (prev <= 20) process.stdout.setMaxListeners?.(prev + 10); process.stdout.on('resize', onResize); return () => { process.stdout.off('resize', onResize); + if (timer) clearTimeout(timer); }; }, []); - - return height; + return value; } -/** - * Detect terminal capabilities for cross-platform compatibility. - */ +/** React hook — returns live terminal width, updates on resize. */ +export function useTerminalWidth(): number { return useTerminalDimension(getTerminalWidth); } + +/** React hook — returns live terminal height, updates on resize. */ +export function useTerminalHeight(): number { return useTerminalDimension(getTerminalHeight); } + /** * Returns true when the environment requests no color output. * Respects the NO_COLOR standard (https://no-color.org/) and TERM=dumb. @@ -72,6 +73,7 @@ export function isNoColor(): boolean { ); } +/** Detect terminal capabilities for cross-platform compatibility. */ export function detectTerminal(): TerminalCapabilities { const plat = platform(); const isTTY = Boolean(process.stdout.isTTY); @@ -81,6 +83,10 @@ export function detectTerminal(): TerminalCapabilities { supportsColor: !noColor && isTTY && (process.env['FORCE_COLOR'] !== '0'), supportsUnicode: plat !== 'win32' || Boolean(process.env['WT_SESSION']), columns: process.stdout.columns || 80, + // detectTerminal uses 24 (standard VT100 default) rather than + // DEFAULT_TERMINAL_ROWS because this is a capability snapshot — not + // a live viewport sizing decision — and 24 is the safer assumption + // when advertising rows to callers that need a conservative baseline. rows: process.stdout.rows || 24, platform: plat, isWindows: plat === 'win32', diff --git a/scripts/bump-build.mjs b/scripts/bump-build.mjs index 63a1cfb4c..ac0a975ec 100644 --- a/scripts/bump-build.mjs +++ b/scripts/bump-build.mjs @@ -6,7 +6,7 @@ * e.g. 0.8.6-preview.1 → 0.8.6-preview.2 * * If no build number exists (e.g. 0.8.6-preview), starts at 1. - * Non-prerelease versions use: major.minor.patch.build + * Non-prerelease versions use: major.minor.patch-build.N (valid semver) * Updates all 3 package.json files (root + both workspaces) in lockstep. * * Skip this script by setting SKIP_BUILD_BUMP=1 (used in CI/CD publish). @@ -32,6 +32,7 @@ const PACKAGE_PATHS = [ ]; // Parse version: "major.minor.patch-prerelease.build" or "major.minor.patch.build" +// Non-prerelease bumps now produce "major.minor.patch-build.N" (valid semver) function parseVersion(version) { // Try prerelease format: "1.2.3-tag" or "1.2.3-tag.N" let match = version.match(/^(\d+\.\d+\.\d+)(-[a-zA-Z][a-zA-Z0-9-]*)(?:\.(\d+))?$/); @@ -58,7 +59,8 @@ function formatVersion({ base, build, prerelease }) { if (prerelease) { return `${base}${prerelease}.${build}`; } - return `${base}.${build}`; + // Use prerelease tag for valid semver (npm rejects 4-part versions like 0.8.25.4) + return `${base}-build.${build}`; } // Read the canonical version from root package.json diff --git a/test/bump-build.test.ts b/test/bump-build.test.ts index f1b282adb..70ca0698a 100644 --- a/test/bump-build.test.ts +++ b/test/bump-build.test.ts @@ -69,7 +69,17 @@ describe('bump-build.mjs', () => { workspace = makeTempWorkspace('1.0.0.3'); execSync(`node ${join(workspace.dir, 'scripts', 'bump-build.mjs')}`, execOpts); for (const p of workspace.paths) { - expect(readVersion(p)).toBe('1.0.0.4'); + // Old 4-part format (1.0.0.3) is parsed as base=1.0.0, build=3 + // New format uses valid semver prerelease tag + expect(readVersion(p)).toBe('1.0.0-build.4'); + } + }); + + it('bumps clean release version to semver prerelease format', () => { + workspace = makeTempWorkspace('1.0.0'); + execSync(`node ${join(workspace.dir, 'scripts', 'bump-build.mjs')}`, execOpts); + for (const p of workspace.paths) { + expect(readVersion(p)).toBe('1.0.0-build.1'); } }); diff --git a/test/cli-packaging-smoke.test.ts b/test/cli-packaging-smoke.test.ts index 2ffaa6059..3b7ba78c4 100644 --- a/test/cli-packaging-smoke.test.ts +++ b/test/cli-packaging-smoke.test.ts @@ -31,14 +31,18 @@ describe('CLI packaging smoke test', { timeout: 120_000 }, () => { const sdkDist = join(sdkDir, 'dist'); const cliDist = join(cliDir, 'dist'); + // SKIP_BUILD_BUMP prevents bump-build.mjs from mutating versions to + // invalid 4-part semver (e.g. 0.8.25.4) which npm install rejects. + const buildEnv = { ...process.env, SKIP_BUILD_BUMP: '1' }; + if (!existsSync(sdkDist)) { console.log('Building squad-sdk...'); - execSync('npm run build', { cwd: sdkDir, stdio: 'inherit' }); + execSync('npm run build', { cwd: sdkDir, stdio: 'inherit', env: buildEnv }); } if (!existsSync(cliDist)) { console.log('Building squad-cli...'); - execSync('npm run build', { cwd: cliDir, stdio: 'inherit' }); + execSync('npm run build', { cwd: cliDir, stdio: 'inherit', env: buildEnv }); } // Pack both packages diff --git a/test/marketplace.test.ts b/test/marketplace.test.ts index be48897da..d940b61d2 100644 --- a/test/marketplace.test.ts +++ b/test/marketplace.test.ts @@ -327,8 +327,10 @@ describe('packageForMarketplace', () => { }); it('should throw if project directory does not exist', () => { + // Use a UUID-based path to guarantee it doesn't exist on any OS + const fakePath = path.join(tmpDir, `nonexistent-${randomUUID()}`); expect(() => - packageForMarketplace('/nonexistent', makeManifest()), + packageForMarketplace(fakePath, makeManifest()), ).toThrow('not found'); }); diff --git a/test/repl-ux.test.ts b/test/repl-ux.test.ts index 80ffc74a8..c5f6e3893 100644 --- a/test/repl-ux.test.ts +++ b/test/repl-ux.test.ts @@ -62,12 +62,15 @@ describe('ThinkingIndicator visibility', () => { expect(frame).toMatch(/[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/); }); - it('spinner text includes agent name from @mention', () => { + it('spinner text shows agent name from explicit activityHint', () => { + // After removing the redundant @mention fallback from MessageStream, + // the hint must come from the parent via activityHint (as App.tsx does). const { lastFrame } = render( h(MessageStream, { messages: [makeMessage({ role: 'user', content: '@Kovash fix the bug' })], processing: true, streamingContent: new Map(), + activityHint: 'Kovash is thinking...', }) ); const frame = lastFrame()!; @@ -742,12 +745,15 @@ describe('ThinkingIndicator integration with MessageStream', () => { expect(frame).toContain('Routing to agent'); }); - it('shows agent-specific hint when @mention present', () => { + it('shows agent-specific hint when activityHint provided', () => { + // After removing the redundant @mention fallback from MessageStream, + // the hint must come from the parent via activityHint (as App.tsx does). const { lastFrame } = render( h(MessageStream, { messages: [makeMessage({ role: 'user', content: '@Kovash fix the bug' })], processing: true, streamingContent: new Map(), + activityHint: 'Kovash is thinking...', }) ); const frame = lastFrame()!; From f0b1d51b2ab321977e644ff5d7e7a2a6402b31c9 Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Tue, 10 Mar 2026 19:24:11 -0700 Subject: [PATCH 03/20] fix(docs): replace ~/.squad/ with platform-specific path references Closes #343 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../content/docs/get-started/installation.md | 10 ++++- docs/src/content/docs/guide/personal-squad.md | 38 +++++++++++-------- .../content/docs/reference/api-reference.md | 2 +- docs/src/content/docs/reference/cli.md | 4 +- docs/src/content/docs/reference/config.md | 2 +- docs/src/content/docs/reference/sdk.md | 4 +- packages/squad-sdk/src/sharing/consult.ts | 8 ++-- 7 files changed, 42 insertions(+), 26 deletions(-) diff --git a/docs/src/content/docs/get-started/installation.md b/docs/src/content/docs/get-started/installation.md index 65d916532..9b1e35e11 100644 --- a/docs/src/content/docs/get-started/installation.md +++ b/docs/src/content/docs/get-started/installation.md @@ -108,7 +108,15 @@ Want the same agents across all your projects? squad init --global ``` -This creates `~/.squad/` — a personal team root that any project can inherit from. See [Upstream Inheritance](../features/upstream-inheritance.md) for details. +This creates your personal squad directory — a personal team root that any project can inherit from. See [Upstream Inheritance](../features/upstream-inheritance.md) for details. + +**Personal squad location by platform:** + +| Platform | Path | +|----------|------| +| Linux | `~/.config/squad/` | +| macOS | `~/Library/Application Support/squad/` | +| Windows | `%APPDATA%\squad\` | --- diff --git a/docs/src/content/docs/guide/personal-squad.md b/docs/src/content/docs/guide/personal-squad.md index 92d7adec3..36f4e1676 100644 --- a/docs/src/content/docs/guide/personal-squad.md +++ b/docs/src/content/docs/guide/personal-squad.md @@ -18,7 +18,15 @@ This tutorial walks you through setup, explains what's happening behind the scen Normally, Squad lives inside a single project — `.squad/` in your repo root. Your agents know that project. They don't know your other ones. -A personal squad flips that. Your team identity — agents, charters, skills, casting history — moves to a global directory (`~/.squad/`). Every project you work in can point to it. +A personal squad flips that. Your team identity — agents, charters, skills, casting history — moves to your personal squad directory. Every project you work in can point to it. + +**Personal squad location by platform:** + +| Platform | Path | +|----------|------| +| Linux | `~/.config/squad/` | +| macOS | `~/Library/Application Support/squad/` | +| Windows | `%APPDATA%\squad\` | What that means in practice: @@ -51,7 +59,7 @@ You'll see: ``` ✅ Personal squad initialized. - ~/.squad/ — your global team root + {personal squad directory} — your global team root Agents, skills, and casting will be shared across projects. ``` @@ -65,7 +73,7 @@ squad status ``` Squad Status - Global squad: ~/.squad/ + Global squad: {personal squad directory} Agents: 0 (none cast yet — start a session to form your team) Skills: 0 ``` @@ -85,7 +93,7 @@ Squad detects your global team root and writes a pointer: ``` ✅ Squad initialized. - .squad/config.json → teamRoot: ~/.squad/ + .squad/config.json → teamRoot: {personal squad directory} Team identity inherited from personal squad. Project-local state (decisions, logs) stays here. ``` @@ -100,12 +108,12 @@ Repeat for any project you want connected. Two things were created. Understanding the split is the key to personal squads. -### The global directory: `~/.squad/` +### The global directory: your personal squad This is your **team identity**. It contains: ``` -~/.squad/ +{personal squad directory}/ agents/ — your agent charters and histories casting/ — who's been cast, role assignments skills/ — accumulated knowledge ("always use Zod", "prefer Tailwind") @@ -120,7 +128,7 @@ Inside each connected project, `.squad/config.json` looks like this: ```json { "version": 1, - "teamRoot": "~/.squad/", + "teamRoot": "{personal squad directory}", "projectKey": null } ``` @@ -129,14 +137,14 @@ That `teamRoot` field is the magic. When Squad's resolution system sees it, the | | **Local mode** (default) | **Remote mode** (personal squad) | |---|---|---| -| Team identity | `.squad/` in project | `~/.squad/` (global) | +| Team identity | `.squad/` in project | Personal squad directory (global) | | Decisions & logs | `.squad/` in project | `.squad/` in project | | Agents shared? | No — project only | Yes — across all connected projects | | Skills shared? | No | Yes | In remote mode: -- **Team identity** (agents, charters, skills, casting) → loaded from `~/.squad/` +- **Team identity** (agents, charters, skills, casting) → loaded from your personal squad directory - **Project-local state** (decisions, logs, orchestration-log) → stays in this project's `.squad/` The resolution system walks up directories looking for `.squad/`. When it finds one with a `teamRoot` in `config.json`, it switches to remote mode — pulling team identity from the external path while keeping project state local. @@ -251,7 +259,7 @@ Set a directive once: 📌 Captured. Linting required before task completion. ``` -That directive is now in `~/.squad/` — every project, every session. Your agents enforce it everywhere. You set the standard once and it sticks. +That directive is now in your personal squad directory — every project, every session. Your agents enforce it everywhere. You set the standard once and it sticks. Over time, your personal squad becomes an opinionated workflow engine. Not because you configured it that way — because you worked with it and it learned. @@ -259,12 +267,12 @@ Over time, your personal squad becomes an opinionated workflow engine. Not becau ## 8. Use Case: Skills That Grow Everywhere -Skills accumulate in `~/.squad/skills/`. Every project contributes. +Skills accumulate in your personal squad directory under `skills/`. Every project contributes. After a few weeks: ``` -~/.squad/skills/ +{personal squad directory}/skills/ always-use-zod.md prefer-tailwind.md cursor-pagination.md @@ -292,7 +300,7 @@ What works well today: - **Consult mode** — bring your team to projects you don't own, invisibly ([docs](../features/consult-mode.md)) What's still rough: -- No sync mechanism between machines yet — `~/.squad/` is local to your machine +- No sync mechanism between machines yet — your personal squad directory is local to your machine - Project keys aren't used for anything yet (that `null` in config.json) - No UI for browsing your global skills or agent histories (it's files for now) @@ -303,10 +311,10 @@ We're building in the open. If something feels off, [open an issue](https://gith ## Tips - **Start with one project.** Get comfortable with the personal squad on one repo before connecting others. The value compounds, but so does confusion if something's misconfigured. -- **Commit project `.squad/` but not global `~/.squad/`.** The project-local state (decisions, logs) belongs in version control. Your global identity is personal — keep it out of repos. +- **Commit project `.squad/` but not personal squad directory.** The project-local state (decisions, logs) belongs in version control. Your global identity is personal — keep it out of repos. - **Check status anytime.** `squad status` shows your global squad directory and which projects are connected. - **Skills are the payoff.** The more projects you work across, the more skills accumulate. After a month, your agents have a real knowledge base tailored to how *you* build software. -- **It's just files.** `~/.squad/` is a directory on your machine. You can browse it, edit it, back it up, copy it to another machine manually. No magic, no cloud, no lock-in. +- **It's just files.** Your personal squad directory is a folder on your machine. You can browse it, edit it, back it up, copy it to another machine manually. No magic, no cloud, no lock-in. - **Global install matters.** `npm install -g @bradygaster/squad-cli` gives you the `squad` command everywhere. Without it, you'd need `npx` in each project. Global CLI + global squad = full portability. --- diff --git a/docs/src/content/docs/reference/api-reference.md b/docs/src/content/docs/reference/api-reference.md index 5815e8764..5e82a47fa 100644 --- a/docs/src/content/docs/reference/api-reference.md +++ b/docs/src/content/docs/reference/api-reference.md @@ -51,7 +51,7 @@ const squadPath = resolveSquad('/home/user/project/src'); ### `resolveGlobalSquadPath(): string` -Get path to global personal squad (`~/.squad/` on Unix, `%USERPROFILE%\.squad\` on Windows). +Get path to global personal squad. Returns platform-specific path: `~/.config/squad/` on Linux, `~/Library/Application Support/squad/` on macOS, `%APPDATA%\squad\` on Windows. ### `ensureSquadPath(startPath?: string): string` diff --git a/docs/src/content/docs/reference/cli.md b/docs/src/content/docs/reference/cli.md index c88d42f0e..2e1df542c 100644 --- a/docs/src/content/docs/reference/cli.md +++ b/docs/src/content/docs/reference/cli.md @@ -28,7 +28,7 @@ squad init |---------|-------------|:------------------:| | `squad` | Enter interactive shell (no args) | No | | `squad init` | Initialize Squad in the current repo (idempotent — safe to run multiple times) | No | -| `squad init --global` | Create a personal squad at `~/.squad/` | No | +| `squad init --global` | Create a personal squad in your platform-specific directory | No | | `squad init --mode remote ` | Initialize linked to a remote team root (dual-root mode) | No | | `squad start [--tunnel] [--port N] [--command cmd]` | Start Copilot with remote phone access via PTY and WebSocket | No | | `squad status` | Show which squad is active and why | Yes | @@ -239,7 +239,7 @@ When Squad starts, it looks for `.squad/` in this order: 1. Current directory (`./.squad/`) 2. Parent directories (walk up to project root) -3. Home directory (`~/.squad/`) +3. Personal squad directory (platform-specific: `~/.config/squad/` on Linux, `~/Library/Application Support/squad/` on macOS, `%APPDATA%\squad\` on Windows) 4. Global CLI default (fallback only) First match wins. diff --git a/docs/src/content/docs/reference/config.md b/docs/src/content/docs/reference/config.md index 359c1d250..0b6ffc1ea 100644 --- a/docs/src/content/docs/reference/config.md +++ b/docs/src/content/docs/reference/config.md @@ -145,7 +145,7 @@ Squad finds `.squad/` by walking up: 1. Current directory (`./.squad/`) 2. Parent directories (up to project root) -3. Home directory (`~/.squad/`) +3. Personal squad directory (platform-specific: `~/.config/squad/` on Linux, `~/Library/Application Support/squad/` on macOS, `%APPDATA%\squad\` on Windows) 4. Global CLI default (fallback) First match wins. diff --git a/docs/src/content/docs/reference/sdk.md b/docs/src/content/docs/reference/sdk.md index bff98e66c..a45744c9c 100644 --- a/docs/src/content/docs/reference/sdk.md +++ b/docs/src/content/docs/reference/sdk.md @@ -24,12 +24,12 @@ Find `.squad/` directories on disk. | Function | Description | |----------|-------------| | `resolveSquad(startPath?)` | Find `.squad/` walking up from `startPath` (throws if not found) | -| `resolveGlobalSquadPath()` | Get `~/.squad/` path (`%USERPROFILE%\.squad\` on Windows) | +| `resolveGlobalSquadPath()` | Get personal squad directory path (platform-specific) | | `ensureSquadPath(startPath?)` | Like `resolveSquad`, but creates `.squad/` if missing | ```typescript const squadPath = resolveSquad(); // '/home/user/project/.squad' -const globalPath = resolveGlobalSquadPath(); // '/home/user/.squad' +const globalPath = resolveGlobalSquadPath(); // Platform-specific: ~/.config/squad/ (Linux), ~/Library/Application Support/squad/ (macOS), %APPDATA%\squad\ (Windows) const safePath = ensureSquadPath(); // Creates if needed ``` diff --git a/packages/squad-sdk/src/sharing/consult.ts b/packages/squad-sdk/src/sharing/consult.ts index 01ecca243..03b761f81 100644 --- a/packages/squad-sdk/src/sharing/consult.ts +++ b/packages/squad-sdk/src/sharing/consult.ts @@ -1006,8 +1006,8 @@ function extractSkillName(content: string): string | null { /** * Merge staged learnings into personal squad. * - * Routes skills to ~/.squad/skills/{name}/SKILL.md - * Routes decisions to ~/.squad/decisions.md (with smart merge) + * Routes skills to personal squad directory via resolveGlobalSquadPath() to skills/{name}/SKILL.md + * Routes decisions to decisions.md in personal squad directory (with smart merge) * * @param learnings - Staged learnings to merge * @param personalSquadRoot - Path to personal squad root @@ -1035,7 +1035,7 @@ export async function mergeToPersonalSquad( } } - // Route skills to ~/.squad/skills/{name}/SKILL.md + // Route skills to personal squad directory (via resolveGlobalSquadPath()) at skills/{name}/SKILL.md const skillsDir = path.join(personalSquadRoot, 'skills'); for (const skill of skills) { const skillName = extractSkillName(skill.content) || skill.filename.replace('.md', ''); @@ -1053,7 +1053,7 @@ export async function mergeToPersonalSquad( skillsAdded++; } - // Route decisions to ~/.squad/decisions.md + // Route decisions to personal squad directory at decisions.md if (decisions.length > 0) { const decisionsPath = path.join(personalSquadRoot, 'decisions.md'); const newContent = decisions.map(d => d.content.trim()).join('\n\n'); From 4c4f06dcee143702ff337a32756c333542c476d8 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Mon, 9 Mar 2026 16:09:36 -0700 Subject: [PATCH 04/20] docs: update PAO history with first-session guide structure Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/agents/pao/history.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.squad/agents/pao/history.md b/.squad/agents/pao/history.md index 85b620eb5..dede225bc 100644 --- a/.squad/agents/pao/history.md +++ b/.squad/agents/pao/history.md @@ -18,3 +18,6 @@ Release blog posts use YAML frontmatter with: title, date, author, wave, tags, s ### Roster & Contributor Recognition (v0.8.25) Squad moved to Apollo 13/NASA Mission Control naming scheme (Flight, Procedures, EECOM, FIDO, PAO, CAPCOM, CONTROL, Surgeon, Booster, GNC, Network, RETRO, INCO, GUIDO, Telemetry, VOX, DSKY, Sims, Handbook). CONTRIBUTORS.md tracks both team roster and community contributors; contributor table entries grow with PRs (append PR counts rather than replace, maintaining attribution history). + +### Getting Started Guide Structure +docs/src/content/docs/get-started/first-session.md explains Squad's first-run experience. Line 89 introduces .squad/ directory creation, followed by "What's inside .squad/?" section (line 91) with table documenting team.md, routing.md, decisions.md, agents/, ceremonies.md, casting/, skills/, templates/. Includes ownership statement ("You own these files") and commit guidance ("Commit .squad/ to version control"). From 8d0e1b38123af99287a6b2d7a82e5a1ab82b56d9 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 17:07:22 -0700 Subject: [PATCH 05/20] chore: remove pao/history.md session artifact from PR diff Reverts .squad/agents/pao/history.md to upstream/main so it no longer appears in the PR diff. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/agents/pao/history.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/.squad/agents/pao/history.md b/.squad/agents/pao/history.md index dede225bc..85b620eb5 100644 --- a/.squad/agents/pao/history.md +++ b/.squad/agents/pao/history.md @@ -18,6 +18,3 @@ Release blog posts use YAML frontmatter with: title, date, author, wave, tags, s ### Roster & Contributor Recognition (v0.8.25) Squad moved to Apollo 13/NASA Mission Control naming scheme (Flight, Procedures, EECOM, FIDO, PAO, CAPCOM, CONTROL, Surgeon, Booster, GNC, Network, RETRO, INCO, GUIDO, Telemetry, VOX, DSKY, Sims, Handbook). CONTRIBUTORS.md tracks both team roster and community contributors; contributor table entries grow with PRs (append PR counts rather than replace, maintaining attribution history). - -### Getting Started Guide Structure -docs/src/content/docs/get-started/first-session.md explains Squad's first-run experience. Line 89 introduces .squad/ directory creation, followed by "What's inside .squad/?" section (line 91) with table documenting team.md, routing.md, decisions.md, agents/, ceremonies.md, casting/, skills/, templates/. Includes ownership statement ("You own these files") and commit guidance ("Commit .squad/ to version control"). From 802dddc33dc891823af30b8762c4a492a41e3bbd Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 05:57:40 -0700 Subject: [PATCH 06/20] docs(ai-team): Merge adoption tracking decisions and governance rules Session: 2026-03-10-adoption-tracking Requested by: Scribe Changes: - Merged 4 decisions from inbox to decisions.md: adoption tracking 3-tier architecture, implementation, append-only governance, ampersand style guide - Updated Flight and EECOM history.md with team cross-agent updates - Created orchestration logs for PAO (PR #303 rebase), Flight (adoption proposal), EECOM (Tier 1 implementation) - Created session log documenting adoption tracking architecture finalization - Deleted decision inbox files after merge --- .squad/agents/eecom/history.md | 4 ++ .squad/agents/flight/history.md | 3 + .squad/agents/pao/history.md | 3 + .squad/decisions.md | 98 +++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+) diff --git a/.squad/agents/eecom/history.md b/.squad/agents/eecom/history.md index 6c1c3da08..6ae77f2a4 100644 --- a/.squad/agents/eecom/history.md +++ b/.squad/agents/eecom/history.md @@ -24,3 +24,7 @@ CLI completeness audit (2026-03-08) confirmed: 26 primary commands routed in cli 📌 **Team update (2026-03-08T21:18:00Z):** FIDO + EECOM released unanimous GO verdict for v0.8.24. Smoke test approved as release gate. FIDO confirmed 32/32 pass + publish.yml wired correctly. EECOM confirmed 26/26 commands + packaging complete (minor gap: "streams" alias untested, non-blocking). +### Adoption Tracking Tier 1 Implementation (2026-03-10) +Implemented Flight's privacy-first adoption monitoring strategy on PR #326 branch. Moved `.squad/adoption/` → `.github/adoption/` for better GitHub integration. Stripped tracking.md to aggregate-only metrics (removed all individual repo names/URLs). Updated GitHub Action workflow (adoption-report.yml) and monitoring script (scripts/adoption-monitor.mjs) to write reports to `.github/adoption/reports/`. Removed "Built with Squad" showcase link from README.md (deferred to Tier 2 opt-in feature). This honors the principle: collect aggregate metrics via public APIs, but never publish individual repo lists without explicit consent. Test discipline: verified npm run build passes; docs-build.test.ts passed structure tests (Astro build failure unrelated to changes). Committed with clear message explaining privacy rationale. + +📌 **Team update (2026-03-10T12-55-49Z):** Adoption tracking Tier 1 complete and merged to decisions.md. Privacy-first architecture confirmed: aggregate metrics only, opt-in for individual repos, public showcase only when 5+ projects opt in. Append-only file governance enforced (no deletions in history.md or decisions.md). Microsoft ampersand style guide adopted for documentation. diff --git a/.squad/agents/flight/history.md b/.squad/agents/flight/history.md index 3756a8af3..126f3acea 100644 --- a/.squad/agents/flight/history.md +++ b/.squad/agents/flight/history.md @@ -9,3 +9,6 @@ **Updated now.md to reflect post-v0.8.24 state:** Apollo 13 team, 3931 tests, Tamir's active branches across 5 feature streams (remote-control, hierarchical-squad-inheritance, ralph-watch, project-type-detection, prevent-git-checkout-data-loss). **Updated wisdom.md with 4 patterns + 2 anti-patterns from recent work:** Test name-agnosticism for team rebirths, dynamic filesystem discovery for evolving content, cli-entry.ts unwired command bug pattern, bump-build.mjs version mutation timing, invalid semver formats, git reset data loss. + +📌 **Team update (2026-03-10T12-55-49Z):** Adoption tracking architecture finalized. Three-tier system approved: Tier 1 (aggregate-only, `.github/adoption/`) shipping with PR #326; Tier 2 (opt-in registry) designed for next PR; Tier 3 (public showcase) launches when ≥5 projects opt in. Append-only file governance rule enforced to prevent data loss. Microsoft ampersand style guide adopted for all user-facing documentation. + diff --git a/.squad/agents/pao/history.md b/.squad/agents/pao/history.md index 85b620eb5..6aaa1804d 100644 --- a/.squad/agents/pao/history.md +++ b/.squad/agents/pao/history.md @@ -18,3 +18,6 @@ Release blog posts use YAML frontmatter with: title, date, author, wave, tags, s ### Roster & Contributor Recognition (v0.8.25) Squad moved to Apollo 13/NASA Mission Control naming scheme (Flight, Procedures, EECOM, FIDO, PAO, CAPCOM, CONTROL, Surgeon, Booster, GNC, Network, RETRO, INCO, GUIDO, Telemetry, VOX, DSKY, Sims, Handbook). CONTRIBUTORS.md tracks both team roster and community contributors; contributor table entries grow with PRs (append PR counts rather than replace, maintaining attribution history). + +### Git Rebase for Doc Merges +When rebasing doc PRs with conflicts from other merged doc PRs, the main branch version (already merged) should generally take priority. For Node.js version references, maintain LTS terminology when present (e.g., `nvm install --lts` over specific version numbers like `nvm install 20`). Conflict resolution pattern: preserve new content from PR branch only where it doesn't duplicate or contradict already-merged changes. Use `git -c core.editor=true rebase --continue` to bypass interactive editor issues on Windows. diff --git a/.squad/decisions.md b/.squad/decisions.md index 3f4bd4bd9..cd515051f 100644 --- a/.squad/decisions.md +++ b/.squad/decisions.md @@ -58,6 +58,104 @@ --- +## Adoption & Community + +### Adoption Tracking — Opt-In Architecture +**By:** Flight +**Date:** 2026-03-09 + +Privacy-first adoption monitoring using a three-tier system: + +**Tier 1: Aggregate monitoring (SHIPPED)** +- GitHub Action + monitoring script collect metrics +- Reports moved to `.github/adoption/reports/{YYYY-MM-DD}.md` +- Reports show ONLY aggregate numbers (no individual repo names): + - "78+ repositories found via code search" + - Total stars/forks across all discovered repos + - npm weekly downloads + +**Tier 2: Opt-in registry (DESIGN NEXT)** +- Create `SHOWCASE.md` in repo root with submission instructions +- Opted-in projects listed in `.github/adoption/registry.json` +- Monitoring script reads registry, reports only on opted-in repos + +**Tier 3: Public showcase (LAUNCH LATER)** +- `docs/community/built-with-squad.md` shows opted-in projects only +- README link added when ≥5 opted-in projects exist + +**Rationale:** +- Aggregate metrics safe (public code search results) +- Individual projects only listed with explicit owner consent +- Prevents surprise listings, respects privacy +- Incremental rollout maintains team capacity + +**Implementation (PR #326):** +- ✅ Moved `.squad/adoption/` → `.github/adoption/` +- ✅ Stripped tracking.md to aggregate-only metrics +- ✅ Removed individual repo names, URLs, metadata +- ✅ Updated adoption-report.yml and scripts/adoption-monitor.mjs +- ✅ Removed "Built with Squad" showcase link from README (Tier 2 feature) + +--- + +### Adoption Tracking Location & Privacy +**By:** EECOM +**Date:** 2026-03-10 + +Implementation decision confirming Tier 1 adoption tracking changes. + +**What:** Move adoption tracking from `.squad/adoption/` to `.github/adoption/` + +**Why:** +1. **GitHub integration:** `.github/adoption/` aligns with GitHub convention (workflows, CODEOWNERS, issue templates) +2. **Privacy-first:** Aggregate metrics only; defer individual repo showcase to Tier 2 (opt-in) +3. **Clear separation:** `.squad/` = team internal; `.github/` = GitHub platform integration +4. **Future-proof:** When Tier 2 opt-in launches, `.github/adoption/` is the natural home + +**Impact:** +- GitHub Action reports write to `.github/adoption/reports/{YYYY-MM-DD}.md` +- No individual repo information published until Tier 2 +- Monitoring continues collecting aggregate metrics via public APIs +- Team sees trends without publishing sensitive adoption data + +--- + +### Append-Only File Governance +**By:** Flight +**Date:** 2026-03-09 + +Feature branches must never modify append-only team state files except to append new content. + +**What:** If a PR diff shows deletions in `.squad/agents/*/history.md` or `.squad/decisions.md`, the PR is blocked until deletions are reverted. + +**Why:** Session state drift causes agents to reset append-only files to stale branch state, destroying team knowledge. PR #326 deleted entire history files and trimmed ~75 lines of decisions, causing data loss. + +**Enforcement:** Code review + future CI check candidate. + +--- + +### Documentation Style: No Ampersands +**By:** PAO +**Date:** 2026-03-09 + +Ampersands (&) are prohibited in user-facing documentation headings and body text, per Microsoft Style Guide. + +**Rule:** Use "and" instead. + +**Why:** Microsoft Style Guide prioritizes clarity and professionalism. Ampersands feel informal and reduce accessibility. + +**Exceptions:** +- Brand names (AT&T, Barnes & Noble) +- UI element names matching exact product text +- Code samples and technical syntax +- Established product naming conventions + +**Scope:** Applies to docs pages, README files, blog posts, community-facing content. Internal files (.squad/** memory files, decision docs, agent history) have flexibility. + +**Reference:** https://learn.microsoft.com/en-us/style-guide/punctuation/ampersands + +--- + ## Sprint Directives ### Secret handling — agents must never persist secrets From 5f2d3802abb754ad4a47bd5ac241bd28c95bb540 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 10:52:00 -0700 Subject: [PATCH 07/20] docs: add scenario and feature guides from Tamir blog analysis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New docs pages derived from public blog post analysis: - scenarios/ralph-operations.md: outer loop deployment, mutex, logging, alerting - scenarios/proactive-communication.md: two-way Teams webhooks and scanning - features/issue-templates.md: squad-aware issue templates, routing labels - features/reviewer-protocol.md: trust levels section (full/selective/self-managing) - test/docs-build.test.ts: assertions updated for new pages All content follows Microsoft Style Guide. No individual repo names — aggregate references only per owner privacy directive. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/agents/pao/history.md | 13 + .../content/docs/features/issue-templates.md | 346 ++++++++++++++++++ .../docs/features/reviewer-protocol.md | 44 ++- .../docs/scenarios/proactive-communication.md | 219 +++++++++++ .../docs/scenarios/ralph-operations.md | 295 +++++++++++++++ test/docs-build.test.ts | 4 +- 6 files changed, 915 insertions(+), 6 deletions(-) create mode 100644 docs/src/content/docs/features/issue-templates.md create mode 100644 docs/src/content/docs/scenarios/proactive-communication.md create mode 100644 docs/src/content/docs/scenarios/ralph-operations.md diff --git a/.squad/agents/pao/history.md b/.squad/agents/pao/history.md index 6aaa1804d..455cef958 100644 --- a/.squad/agents/pao/history.md +++ b/.squad/agents/pao/history.md @@ -21,3 +21,16 @@ Squad moved to Apollo 13/NASA Mission Control naming scheme (Flight, Procedures, ### Git Rebase for Doc Merges When rebasing doc PRs with conflicts from other merged doc PRs, the main branch version (already merged) should generally take priority. For Node.js version references, maintain LTS terminology when present (e.g., `nvm install --lts` over specific version numbers like `nvm install 20`). Conflict resolution pattern: preserve new content from PR branch only where it doesn't duplicate or contradict already-merged changes. Use `git -c core.editor=true rebase --continue` to bypass interactive editor issues on Windows. + +### Astro Docs Format (v0.8.26) +Squad docs use plain markdown without Astro frontmatter. Structure: title (H1), experimental warning callout, "Try this" code blocks at top, overview paragraph, horizontal rule, then content sections with H2 headings. Microsoft Style Guide enforced: sentence-case headings, active voice, second person ("you"), present tense, no ampersands except in code/brand names. Features and scenarios directories added to test coverage in docs-build.test.ts. Reference implementations linked where available (e.g., ralph-watch.ps1 for operational patterns). + +### Proactive Communication Patterns (v0.8.26) +Two-way communication layer between Squad and work environment. Outbound: Teams webhook notifications (breaking, briefings, recaps, flashes) sent via Adaptive Cards — only when newsworthy. Inbound: WorkIQ/Playwright scanning of Teams channels and email → auto-create GitHub issues with teams-bridge label, anti-duplicate logic enforced. Loop: inbound creates issues → Ralph dispatches → agents work → outbound notifies results. Human stays informed on mobile. Prerequisites are enhancements, not requirements. + +### PR Trust Model Documentation (v0.8.26) +Three trust levels for PR management: (1) Full review (default, team repos) — human gate on every merge; (2) Selective review (personal projects with patterns) — human reviews only critical paths; (3) Self-managing (solo personal repos only) — Squad merges own PRs, human reviews retroactively. Added to reviewer-protocol.md as new section. Important: self-managing ≠ unmonitored; use Ralph work monitoring and Teams notifications for awareness. Decision matrix included for when to use each level. + +### Final Docs Review Pattern (v0.8.26) +Pre-PR quality reviews check: (1) Microsoft Style Guide compliance (sentence-case headings, active voice, no ampersands, present tense, second person); (2) Tone consistency (practical, developer-focused, no hype); (3) Technical accuracy (code examples, file paths, commands); (4) Cross-reference integrity (valid links between pages); (5) DOCS-TEST SYNC (test assertions match new pages); (6) Privacy directive compliance (no individual repos without consent). Fixed duplicate section heading in reviewer-protocol.md (merge artifact). All staged docs passed review and are ready to commit. + diff --git a/docs/src/content/docs/features/issue-templates.md b/docs/src/content/docs/features/issue-templates.md new file mode 100644 index 000000000..e31c403a1 --- /dev/null +++ b/docs/src/content/docs/features/issue-templates.md @@ -0,0 +1,346 @@ +# Issue Templates for Squad + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + + +**Try this after setting up templates:** +``` +Ralph, show me untriaged issues +``` + +**Then watch Ralph auto-triage based on labels.** + +When GitHub Issues are your work queue, creating tasks should be frictionless. Issue templates pre-fill labels, structure task descriptions, and work beautifully on mobile — making it possible to add tasks in 10 seconds from anywhere. + +--- + +## Why Issue Templates Matter for Squad + +Squad operates best when work is captured as GitHub Issues. But creating an issue from scratch takes time: you need to remember the right labels, format the description consistently, and ensure the structure matches what agents expect. + +Issue templates solve this: + +- **Pre-filled labels** — `squad` label applied automatically +- **Structured format** — Task description, acceptance criteria, priority fields +- **Mobile-friendly** — Works in the GitHub mobile app +- **Fast task creation** — Add work while walking the dog, waiting for coffee, or during a meeting + +With templates, creating a Squad task takes 10 seconds instead of 2 minutes. + +--- + +## Basic Squad Task Template + +Create `.github/ISSUE_TEMPLATE/squad-task.yml` in your repository: + +```yaml +name: Squad Task +description: Create a task for the Squad team +title: "[Task]: " +labels: ["squad"] +body: + - type: markdown + attributes: + value: | + Thanks for creating a Squad task! Fill in the details below. + + - type: textarea + id: description + attributes: + label: Task Description + description: What needs to be done? + placeholder: | + Add dark mode support to the settings page. + + Current behavior: Settings page uses light theme only. + Expected behavior: Theme switcher in settings, respects system preference. + validations: + required: true + + - type: textarea + id: acceptance-criteria + attributes: + label: Acceptance Criteria + description: How will we know this is complete? + placeholder: | + - [ ] Theme switcher toggle added to settings + - [ ] Dark mode CSS applied when enabled + - [ ] Preference saved to localStorage + - [ ] System theme preference detected on first load + validations: + required: false + + - type: dropdown + id: priority + attributes: + label: Priority + description: How urgent is this task? + options: + - Low + - Medium + - High + - Critical + validations: + required: false +``` + +### What This Template Does + +- **Applies `squad` label** — Ralph sees it in the untriaged queue +- **Structured sections** — Description, acceptance criteria, priority +- **Markdown support** — Use checklists, code blocks, links +- **Works on mobile** — GitHub app renders forms beautifully + +--- + +## Custom Labels for Routing + +Ralph uses `.squad/routing.md` to route work to agents. Add `squad:{member}` labels to your template for pre-triaging: + +```yaml +name: Documentation Task +description: Create a docs task (auto-routed to PAO) +title: "[Docs]: " +labels: ["squad", "squad:pao"] +body: + - type: textarea + id: description + attributes: + label: What needs documenting? + placeholder: | + Add a guide for setting up Ralph in production. +``` + +When Ralph scans the board, this issue is already labeled `squad:pao` — no triage needed, work goes straight to PAO. + +### Setting Up Squad Member Labels + +Create labels in your repository for each squad member: + +```bash +# Using gh CLI +gh label create "squad:pao" --description "DevRel tasks" --color "1d76db" +gh label create "squad:flight" --description "Architecture and planning" --color "d73a4a" +gh label create "squad:fido" --description "Testing and quality" --color "0e8a16" +``` + +Or use the [label sync workflow](../features/labels.md) to automate label management across repositories. + +--- + +## Template Variants + +Different work types need different structures: + +### Bug Report Template + +`.github/ISSUE_TEMPLATE/bug-report.yml`: + +```yaml +name: Bug Report +description: Report a bug for Squad to fix +title: "[Bug]: " +labels: ["squad", "bug"] +body: + - type: textarea + id: description + attributes: + label: Bug Description + description: What went wrong? + validations: + required: true + + - type: textarea + id: repro-steps + attributes: + label: Steps to Reproduce + placeholder: | + 1. Run `squad init` + 2. Create a team with 3 agents + 3. Try to export the configuration + 4. See error: "Cannot read property 'name' of undefined" + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: What should have happened? + validations: + required: false + + - type: input + id: version + attributes: + label: Squad Version + placeholder: "0.8.24" + validations: + required: false +``` + +### Feature Request Template + +`.github/ISSUE_TEMPLATE/feature-request.yml`: + +```yaml +name: Feature Request +description: Suggest a new feature for Squad +title: "[Feature]: " +labels: ["squad", "enhancement"] +body: + - type: textarea + id: problem + attributes: + label: Problem Statement + description: What problem does this feature solve? + placeholder: "As a solo developer, I want to track time spent on tasks so I can invoice clients accurately." + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed Solution + description: How should this feature work? + validations: + required: false + + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered + description: What other approaches did you think about? + validations: + required: false +``` + +### Doc Update Template + +`.github/ISSUE_TEMPLATE/doc-update.yml`: + +```yaml +name: Documentation Update +description: Suggest a docs improvement +title: "[Docs]: " +labels: ["squad", "squad:pao", "documentation"] +body: + - type: textarea + id: what + attributes: + label: What needs updating? + placeholder: "The Ralph deployment guide doesn't mention log rotation." + validations: + required: true + + - type: input + id: page + attributes: + label: Page URL or Path + placeholder: "docs/scenarios/ralph-operations.md" + validations: + required: false +``` + +--- + +## Mobile Workflow + +GitHub Issues + templates work from anywhere: + +**On your phone:** +1. Open GitHub app +2. Navigate to repository +3. Tap **Issues** → **New Issue** +4. Select template +5. Fill form (voice-to-text works!) +6. Tap **Submit new issue** + +**10 seconds later:** +- Issue created with `squad` label +- Ralph sees it in the next scan +- Agent picks it up autonomously + +This workflow enables "capture anywhere, process later" — add tasks while commuting, exercising, or in meetings without context-switching to a laptop. + +--- + +## Template Configuration + +GitHub supports multiple templates. Create a config file to customize the issue creation experience: + +`.github/ISSUE_TEMPLATE/config.yml`: + +```yaml +blank_issues_enabled: false +contact_links: + - name: Squad Community Discussions + url: https://github.com/bradygaster/squad/discussions + about: Ask questions or share ideas in Discussions + - name: Squad Documentation + url: https://squad.dev + about: Read the full Squad documentation +``` + +This disables blank issues (forcing template use) and provides helpful links when users click "New Issue." + +--- + +## Template Best Practices + +- **Keep templates short** — Long forms reduce completion rates +- **Make most fields optional** — Only require what's absolutely necessary +- **Use placeholders** — Show examples of good descriptions +- **Pre-fill smart defaults** — Priority: Medium, Type: Task +- **Test on mobile** — Ensure forms render well in the GitHub app +- **Use dropdown for enums** — Priority, Type, Severity (reduces typos) +- **Add markdown help** — Link to GitHub markdown guide in template + +--- + +## Integration with Ralph + +Ralph's heartbeat workflow (`.github/workflows/squad-heartbeat.yml`) scans for untriaged issues: + +1. Issue created with `squad` label (from template) +2. Heartbeat workflow runs (every 30 min or on issue create) +3. Ralph reads `.squad/routing.md` to determine agent +4. Ralph adds `squad:{member}` label +5. Next heartbeat run (or in-session Ralph) assigns agent + +If your template pre-fills `squad:{member}`, Ralph skips triage and goes straight to assignment. + +--- + +## Sample Prompts + +``` +Show me untriaged squad issues +``` + +Lists all issues with `squad` label but no `squad:{member}` assignment. + +``` +Ralph, triage and assign the backlog +``` + +Ralph reads routing rules, applies member labels, and prepares work for agents. + +--- + +## Notes + +- Templates don't prevent manual issue creation — users can still click "Open a blank issue" +- Templates are stored in `.github/ISSUE_TEMPLATE/` (note the underscore, not dash) +- Use `.yml` or `.yaml` extension (both work) +- Test templates by creating issues yourself before announcing to the team +- Mobile workflow requires GitHub app (iOS or Android) — works on tablets too + +--- + +## See Also + +- [GitHub Issues Mode](./github-issues.md) — Issue-driven development workflow +- [Ralph — Work Monitor](./ralph.md) — Ralph's work monitoring behavior +- [Labels](./labels.md) — Label management and sync workflow +- [Routing](./routing.md) — How Ralph triages work to agents diff --git a/docs/src/content/docs/features/reviewer-protocol.md b/docs/src/content/docs/features/reviewer-protocol.md index 44fa21324..fa69b5e6b 100644 --- a/docs/src/content/docs/features/reviewer-protocol.md +++ b/docs/src/content/docs/features/reviewer-protocol.md @@ -19,10 +19,6 @@ When a reviewer (Lead, Tester) rejects work, the original agent is locked out fr ## How It Works -When a reviewer (Lead, Tester) rejects an agent's work, the agent is **locked out** from self-revising. This prevents endless fix-retry loops and forces human oversight or escalation. The protocol ensures rejected work doesn't slip through without proper review. - -## How It Works - 1. **Agent submits work** — Creates draft PR, requests review from Lead or Tester. 2. **Reviewer evaluates** — Checks code quality, test coverage, adherence to directives. 3. **Reviewer decision:** @@ -133,6 +129,46 @@ Lockouts are recorded in `.squad/orchestration-log/`: [2024-01-15 16:20:11] UNLOCK: Fenster unlocked (issue #42 resolved) ``` +## Trust Levels for PR Management + +This section covers the spectrum of human oversight for Squad-created PRs: + +### 1. Full Review (Default) + +Every PR requires human approval before merge. This is the default and recommended for team repos, shared codebases, and anything with external collaborators. + +**When to use:** Team repositories, public packages, shared codebases where multiple people depend on stability. + +**Risk:** Low — human gate on every change. + +### 2. Selective Review + +Squad creates and reviews PRs, but the human only reviews PRs that touch specific paths or domains they care about. Everything else merges after agent review. + +**When to use:** Personal projects with established patterns where you trust Squad's judgment on routine changes (dependency updates, test fixes, doc improvements). + +**Risk:** Medium — some changes skip human eyes. + +### 3. Self-Managing (Personal Repos Only) + +Squad creates, reviews, approves, and merges its own PRs. The human only jumps in when an issue is explicitly flagged for review. + +**When to use:** Solo personal projects where you're the sole maintainer and experimentation speed matters more than pre-merge safety. + +**Risk:** Higher — but fast; review PRs retroactively. + +### Decision Matrix + +| Trust Level | When | Risk | +|-------------|------|------| +| Full review | Team repos, shared codebases, public packages | Low — human gate on every change | +| Selective review | Personal projects with established patterns | Medium — some changes skip human eyes | +| Self-managing | Solo personal projects, experimentation | Higher — but fast; review PRs retroactively | + +**Important:** Self-managing mode doesn't mean unmonitored. Use Ralph's work monitoring, Teams notifications, and periodic code review to stay informed. The difference is that you review *after* merge rather than *before*. + +--- + ## Sample Prompts ``` diff --git a/docs/src/content/docs/scenarios/proactive-communication.md b/docs/src/content/docs/scenarios/proactive-communication.md new file mode 100644 index 000000000..e10edda7c --- /dev/null +++ b/docs/src/content/docs/scenarios/proactive-communication.md @@ -0,0 +1,219 @@ +# Proactive Communication Patterns + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + + +**Try this to set up outbound notifications:** +```bash +# Store your Teams webhook URL +echo "https://outlook.office.com/webhook/..." > ~/.squad/teams-webhook.url +``` + +**Try this to scan for external work:** +``` +Ralph, check Teams and email for new work +``` + +Squad can both push notifications to you AND pull information from your environment. These patterns work independently but are most powerful together, creating a two-way communication layer between Squad and your work environment. + +--- + +## Outbound: Teams Webhook Notifications + +Any agent can send notifications to you via a Teams webhook by reading a stored URL and POSTing an Adaptive Card. This keeps you informed on mobile without being at the terminal. + +### Setup + +Store your webhook URL at a known path: + +```bash +# Create webhook URL file +mkdir -p ~/.squad +echo "https://outlook.office.com/webhook/..." > ~/.squad/teams-webhook.url +chmod 600 ~/.squad/teams-webhook.url # Protect webhook URL +``` + +### Notification Tiers + +Different urgency levels for different situations: + +| Tier | When | Examples | +|------|------|----------| +| ⚡ **Breaking** | Critical failures | CI broken, merge conflicts, blocked PRs | +| 📰 **Briefings** | Daily summaries | Progress reports, work completed | +| 📊 **Recaps** | Weekly highlights | Stats, trends, milestones | +| 🎯 **Flashes** | Quick snapshots | Board status, queue depth | + +**Key rule:** Only send when there's genuinely newsworthy activity. Don't spam yourself. + +### Sending a Notification + +```powershell +# Read webhook URL +$webhookUrl = Get-Content ~/.squad/teams-webhook.url -Raw + +# Build Adaptive Card +$card = @{ + type = "message" + attachments = @( + @{ + contentType = "application/vnd.microsoft.card.adaptive" + content = @{ + type = "AdaptiveCard" + version = "1.4" + body = @( + @{ + type = "TextBlock" + text = "⚡ CI Failure" + size = "Large" + weight = "Bolder" + } + @{ + type = "TextBlock" + text = "Build failed on main branch" + wrap = $true + } + ) + actions = @( + @{ + type = "Action.OpenUrl" + title = "View Logs" + url = "https://github.com/user/repo/actions/runs/123" + } + ) + } + } + ) +} | ConvertTo-Json -Depth 10 + +# Send notification +Invoke-RestMethod -Uri $webhookUrl -Method Post -Body $card -ContentType "application/json" +``` + +```bash +# Bash equivalent +WEBHOOK_URL=$(cat ~/.squad/teams-webhook.url) + +curl -X POST "$WEBHOOK_URL" \ + -H "Content-Type: application/json" \ + -d '{ + "type": "message", + "attachments": [{ + "contentType": "application/vnd.microsoft.card.adaptive", + "content": { + "type": "AdaptiveCard", + "version": "1.4", + "body": [{ + "type": "TextBlock", + "text": "⚡ CI Failure", + "size": "Large", + "weight": "Bolder" + }] + } + }] + }' +``` + +**Note:** This pattern works with any webhook-capable platform (Teams, Slack, Discord) — Teams is just the most common example. + +--- + +## Inbound: Scanning Emails and Teams for Work + +Use WorkIQ MCP server or Playwright to periodically read Teams channels and email, evaluating messages for actionability. When items need attention, create GitHub issues automatically. + +### Pattern Overview + +1. **Agent reads external sources** — Teams channels, email threads, shared documents +2. **Evaluates actionability** — Is this a bug report? Feature request? Question needing documentation? +3. **Creates GitHub issues** — Auto-create issues with `teams-bridge` label for items needing attention +4. **Anti-duplicate logic** — Check existing issues before creating new ones to avoid spam + +### Example Implementation + +```typescript +// Using WorkIQ MCP to scan Teams +const recentMessages = await workiq.getTeamsMessages({ + channel: "engineering", + since: "24h" +}); + +for (const message of recentMessages) { + // Evaluate: Does this need an issue? + const needsIssue = await evaluateActionability(message); + + if (!needsIssue) continue; + + // Check for duplicates + const existing = await github.searchIssues({ + q: `repo:owner/repo is:issue "${message.subject}"`, + label: "teams-bridge" + }); + + if (existing.length > 0) continue; + + // Create issue + await github.createIssue({ + title: message.subject, + body: `From Teams channel #${message.channel}:\n\n${message.content}`, + labels: ["teams-bridge"] + }); +} +``` + +**Key rule:** Do NOT spam. Only surface items that genuinely need attention. Use filters, keyword matching, and sentiment analysis to avoid creating issues for every casual message. + +### Label Convention + +Mark auto-created issues with `teams-bridge` (or similar) so the team knows the source. This also enables filtered views and routing rules. + +--- + +## Connecting the Loop + +These patterns work together to create a complete feedback cycle: + +1. **Inbound scanning creates issues** — External work enters GitHub +2. **Ralph picks them up** — Work monitor detects new issues, dispatches agents +3. **Agents do the work** — Code changes, PRs opened, reviews completed +4. **Outbound notifications report results** — You get notified on mobile + +The human stays informed via mobile (GitHub app + Teams notifications) without being at the terminal. You can review PRs from your phone, approve merges from anywhere, and jump in only when needed. + +### Example Flow + +``` +1. Customer posts bug report in Teams → WorkIQ creates issue #123 +2. Ralph triages issue #123 → Assigns to Backend agent +3. Backend creates PR #45 → Requests Lead review +4. Lead approves PR #45 → Merge successful +5. Ralph sends Teams notification → "🎯 Issue #123 closed via PR #45" +6. You see notification on phone → No action needed, just awareness +``` + +--- + +## Prerequisites + +These patterns enhance Squad but are not requirements. Squad works fine without them. + +### For Outbound Notifications + +- Teams webhook URL (or Slack/Discord equivalent) +- Stored at a known path (e.g., `~/.squad/teams-webhook.url`) +- Agents configured to send notifications for specific events + +### For Inbound Scanning + +- WorkIQ MCP server installed and authenticated +- Playwright or other automation tool for non-WorkIQ sources +- GitHub token with `repo` scope for creating issues +- Anti-spam filters and duplicate detection logic + +--- + +## See Also + +- [Ralph — Work Monitor](../features/ralph.md) — Ralph's work processing and monitoring +- [GitHub Issues Mode](../features/github-issues.md) — Issue-driven workflow basics +- [Ralph Operational Deployment Patterns](./ralph-operations.md) — 24/7 Ralph deployment diff --git a/docs/src/content/docs/scenarios/ralph-operations.md b/docs/src/content/docs/scenarios/ralph-operations.md new file mode 100644 index 000000000..ad31c8760 --- /dev/null +++ b/docs/src/content/docs/scenarios/ralph-operations.md @@ -0,0 +1,295 @@ +# Ralph Operational Deployment Patterns + +> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. + + +**Try this to understand Ralph's built-in behavior first:** +``` +Ralph, show me what's on the board +``` + +**Then explore this guide for 24/7 deployment patterns.** + +Ralph's built-in work monitoring runs during active Copilot sessions. When you need **persistent, unattended monitoring** — Ralph running 24/7 across server restarts, code updates, and agent configuration changes — you need an outer loop. This guide covers operational patterns for deploying Ralph in production environments. + +See [Ralph — Work Monitor](../features/ralph.md) for Ralph's in-session behavior, watch mode, and cloud heartbeat. This guide focuses on the **deployment layer** that wraps Squad. + +--- + +## The Outer Loop Pattern + +Ralph's in-session loop processes work until the board is clear, then idles. For continuous operation, wrap Squad in a persistent outer loop: + +```powershell +# Simplified pattern - see reference implementation for production version +while ($true) { + git pull origin main # Fresh code before each round + + # Spawn fresh Copilot process (picks up updated agent definitions) + gh copilot run "Ralph, process the work queue" + + # Log results, check exit code, update metrics + Write-Log "Round completed at $(Get-Date)" + + Start-Sleep -Seconds 600 # 10-minute pause between rounds +} +``` + +### Why the Outer Loop Matters + +- **Fresh context every round** — Agents may update charters, skills, or routing rules between sessions +- **Clean process state** — New Copilot process picks up MCP server changes, tool updates, configuration edits +- **Resilience** — Failed rounds don't crash the watchdog; the outer loop continues +- **Observability** — Each round produces discrete log entries with metrics + +**Reference implementation:** [ralph-watch.ps1](https://github.com/tamirdresher/squad-personal-demo/blob/main/ralph-watch.ps1) — A production-ready PowerShell outer loop with all patterns below integrated. + +--- + +## Single-Instance Guard + +Prevent multiple Ralph instances from running simultaneously using a system-wide named mutex or lockfile: + +```powershell +# Named mutex approach (Windows) +$mutex = [System.Threading.Mutex]::new($false, "Global\SquadRalphWatch") +if (-not $mutex.WaitOne(0)) { + Write-Error "Ralph is already running (PID found in heartbeat file)" + exit 1 +} + +try { + # Main watchdog loop runs here +} finally { + $mutex.ReleaseMutex() + $mutex.Dispose() +} +``` + +```bash +# Lockfile approach (Linux/macOS) +LOCKFILE="/var/run/squad-ralph.lock" + +if [ -f "$LOCKFILE" ]; then + PID=$(cat "$LOCKFILE") + if ps -p "$PID" > /dev/null 2>&1; then + echo "Ralph is already running (PID $PID)" + exit 1 + else + echo "Stale lockfile found, cleaning up" + rm -f "$LOCKFILE" + fi +fi + +echo $$ > "$LOCKFILE" +trap "rm -f $LOCKFILE" EXIT + +# Main watchdog loop runs here +``` + +### Stale Instance Detection + +If the watchdog crashes, the mutex releases automatically (Windows) or the lockfile remains but the process is gone (Unix). Before exiting with "already running", check if the PID in the lockfile/heartbeat is still alive. If not, clean up and proceed. + +--- + +## Structured Logging + +Each round produces a structured log entry with timestamp, round number, exit code, duration, and parsed metrics: + +```json +{ + "timestamp": "2025-03-15T14:32:00Z", + "round": 47, + "duration_seconds": 132, + "exit_code": 0, + "status": "success", + "metrics": { + "issues_closed": 2, + "prs_merged": 1, + "agent_actions": 5 + }, + "errors": [] +} +``` + +### Log Rotation + +Rotate logs when they reach 500 entries or 1 MB, whichever comes first. Keep the last 5 rotated files for historical analysis: + +- `ralph-watch.log` — current log +- `ralph-watch.log.1` — previous rotation +- `ralph-watch.log.2` through `ralph-watch.log.5` — older rotations + +--- + +## Failure Alerting + +Transient failures are normal in distributed systems. Alert only after **N consecutive failures** to avoid notification fatigue: + +```powershell +$consecutiveFailures = 0 +$FAILURE_THRESHOLD = 3 + +while ($true) { + $exitCode = Run-RalphRound + + if ($exitCode -ne 0) { + $consecutiveFailures++ + + if ($consecutiveFailures -ge $FAILURE_THRESHOLD) { + Send-Alert @{ + Title = "Ralph Watchdog: $consecutiveFailures consecutive failures" + ExitCode = $exitCode + Timestamp = Get-Date + LogTail = Get-Content ralph-watch.log -Tail 50 + } + } + } else { + $consecutiveFailures = 0 # Reset on success + } + + Start-Sleep -Seconds 600 +} +``` + +### Alert Channels + +- **Teams webhook** — Send structured cards with failure count, exit code, log excerpts +- **Email** — SMTP delivery for critical alerts +- **PagerDuty/Opsgenie** — Escalate to on-call if threshold exceeds N hours + +--- + +## Heartbeat File + +Write a JSON heartbeat file before and after every round with status, metrics, and process ID: + +```json +{ + "status": "running", + "pid": 12345, + "current_round": 47, + "last_success": "2025-03-15T14:32:00Z", + "metrics": { + "total_issues_closed": 142, + "total_prs_merged": 89, + "uptime_hours": 336 + }, + "health": "ok" +} +``` + +External monitoring tools (Prometheus, Datadog, Azure Monitor) can: + +- Poll the heartbeat file every minute +- Alert if `last_success` timestamp is older than expected round interval × 2 +- Track cumulative metrics over time +- Verify the PID is still running + +### Heartbeat Update Pattern + +```powershell +# Before round starts +$heartbeat = @{ + status = "running" + pid = $PID + current_round = $roundNumber + last_success = $lastSuccess +} +$heartbeat | ConvertTo-Json | Set-Content heartbeat.json + +# After round completes +$heartbeat.status = "idle" +$heartbeat.last_success = Get-Date -Format "o" +$heartbeat.metrics.total_issues_closed += $issuesClosed +$heartbeat | ConvertTo-Json | Set-Content heartbeat.json +``` + +--- + +## Background Activity Monitor + +While the Copilot session runs, tail logs every 30 seconds to show what's happening: + +```powershell +# Start Ralph round in background +$job = Start-Job -ScriptBlock { + gh copilot run "Ralph, process the work queue" +} + +# Monitor activity while it runs +while ($job.State -eq 'Running') { + # Show last 5 lines of Squad log + Get-Content .squad/log/session-*.log -Tail 5 | Write-Host -ForegroundColor Cyan + + Start-Sleep -Seconds 30 +} + +# Collect final results +$result = Receive-Job -Job $job -Wait +``` + +This prevents the terminal from sitting silently for 10+ minutes while Ralph works through a large backlog. The operator sees progress in real-time. + +--- + +## Deployment Checklist + +When deploying Ralph in production: + +- [ ] Outer loop pulls latest code before each round +- [ ] Single-instance guard prevents duplicate watchdogs +- [ ] Structured logging with rotation (500 entries or 1 MB) +- [ ] Failure alerting after N=3 consecutive failures +- [ ] Heartbeat file updated before and after rounds +- [ ] Background activity monitor shows progress +- [ ] Monitoring system polls heartbeat file +- [ ] Alert channels tested (Teams, email, PagerDuty) +- [ ] Log rotation tested (verify old logs are kept) +- [ ] Stale instance cleanup tested (kill PID, verify recovery) + +--- + +## Architecture Summary + +Ralph operates at three layers (from [Ralph — Work Monitor](../features/ralph.md)): + +| Layer | When | How | +|-------|------|-----| +| **In-session** | You're at the keyboard | "Ralph, go" — active loop while work exists | +| **Local watchdog** | You're away but machine is on | `squad watch --interval 10` | +| **Cloud heartbeat** | Fully unattended | `squad-heartbeat.yml` GitHub Actions cron | + +This guide adds a **fourth layer** — the operational wrapper that makes Ralph production-ready: + +| Layer | Purpose | +|-------|---------| +| **Outer loop** | Persistent deployment — fresh code, fresh process, structured logging, alerting | + +--- + +## Sample Prompts + +``` +Ralph, process the work queue +``` + +The outer loop invokes this command each round. Ralph triages issues, dispatches agents, monitors PRs, and reports results. + +--- + +## Notes + +- The outer loop is **not part of Squad** — it's infrastructure you build around Squad +- Ralph's built-in watch mode (`squad watch`) is suitable for local development; the outer loop is for production +- Test your outer loop with a small backlog first (1-2 issues) before deploying to a live board +- Monitor heartbeat file staleness — if no update in 2× the expected round interval, investigate + +--- + +## See Also + +- [Ralph — Work Monitor](../features/ralph.md) — Ralph's built-in behavior +- [GitHub Issues Mode](../features/github-issues.md) — Issue-driven workflow +- [CI/CD Integration](./ci-cd-integration.md) — Automated Squad workflows diff --git a/test/docs-build.test.ts b/test/docs-build.test.ts index a1982bdd8..17ef81046 100644 --- a/test/docs-build.test.ts +++ b/test/docs-build.test.ts @@ -22,7 +22,7 @@ const EXPECTED_GUIDES = ['tips-and-tricks', 'sample-prompts', 'personal-squad', const EXPECTED_REFERENCE = ['cli', 'sdk', 'config', 'api-reference', 'integration', 'tools-and-hooks', 'glossary']; const EXPECTED_SCENARIOS = [ - 'issue-driven-dev', 'existing-repo', 'ci-cd-integration', 'solo-dev', 'monorepo', 'team-of-humans', + 'issue-driven-dev', 'existing-repo', 'ci-cd-integration', 'solo-dev', 'monorepo', 'team-of-humans', 'ralph-operations', 'proactive-communication', ]; const EXPECTED_CONCEPTS = ['architecture', 'your-team', 'memory-and-knowledge', 'parallel-work', 'github-workflow', 'portability']; @@ -45,7 +45,7 @@ function getMarkdownFiles(section: string): string[] { } function getAllMarkdownFiles(): string[] { - const sections = ['get-started', 'guide', 'reference', 'scenarios', 'concepts']; + const sections = ['get-started', 'guide', 'reference', 'scenarios', 'features', 'concepts']; const allFiles: string[] = []; for (const section of sections) { allFiles.push(...getMarkdownFiles(section)); From acdd8a98f5f318837687550919eab114452db3ba Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:44:45 -0700 Subject: [PATCH 08/20] docs: scope Squad docs to framework-only content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove ralph-operations.md and proactive-communication.md — both document infrastructure around Squad (webhooks, WorkIQ, deployment patterns) rather than Squad itself. Content moves to Squad IRL repo. Reframe issue-templates.md to clarify GitHub Issue Templates are a platform feature configured for Squad routing, not a Squad feature. Litmus test applied: if Squad doesn't ship the code/config, it belongs in IRL. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/agents/pao/history.md | 6 + .../content/docs/features/issue-templates.md | 4 +- .../docs/scenarios/proactive-communication.md | 219 ------------- .../docs/scenarios/ralph-operations.md | 295 ------------------ test/docs-build.test.ts | 2 +- 5 files changed, 10 insertions(+), 516 deletions(-) delete mode 100644 docs/src/content/docs/scenarios/proactive-communication.md delete mode 100644 docs/src/content/docs/scenarios/ralph-operations.md diff --git a/.squad/agents/pao/history.md b/.squad/agents/pao/history.md index 455cef958..61157a7fd 100644 --- a/.squad/agents/pao/history.md +++ b/.squad/agents/pao/history.md @@ -34,3 +34,9 @@ Three trust levels for PR management: (1) Full review (default, team repos) — ### Final Docs Review Pattern (v0.8.26) Pre-PR quality reviews check: (1) Microsoft Style Guide compliance (sentence-case headings, active voice, no ampersands, present tense, second person); (2) Tone consistency (practical, developer-focused, no hype); (3) Technical accuracy (code examples, file paths, commands); (4) Cross-reference integrity (valid links between pages); (5) DOCS-TEST SYNC (test assertions match new pages); (6) Privacy directive compliance (no individual repos without consent). Fixed duplicate section heading in reviewer-protocol.md (merge artifact). All staged docs passed review and are ready to commit. +### Squad vs IRL Boundary Review (v0.8.26) +Evaluated four docs pages from PR #331 (Tamir's blog analysis) against Squad-specificity criterion: does content document Squad features/patterns (belongs in Squad docs) or community implementation examples (belongs in Squad IRL)? Key distinction: Squad docs = "how the feature works + universal best practices" vs IRL = "how one person built an amazing setup." Results: ralph-operations.md borderline (deployment wrappers are external infrastructure, not Squad features — trim "outer loop" framing), issue-templates.md borderline (GitHub feature documented for Squad context, not Squad code — clarify scope), proactive-communication.md does not belong (community extension pattern using WorkIQ/Playwright, not built into Squad), reviewer-protocol.md trust levels section belongs (documents user choice spectrum within Squad's existing review system). Pattern: if Squad doesn't ship the code, it's IRL content; if it's a GitHub platform feature used alongside Squad, clarify that distinction; if it documents actual Squad behavior/configuration, it belongs. + +### Boundary Review Execution (v0.8.26) +Executed boundary review findings from PR #331: (1) Deleted ralph-operations.md (infrastructure around Squad, not Squad itself — moved to IRL); (2) Deleted proactive-communication.md (external tools/webhooks — moved to IRL); (3) Reframed issue-templates.md intro to clarify "GitHub feature configured for Squad" not "Squad feature"; (4) Updated EXPECTED_SCENARIOS in docs-build.test.ts to match remaining files. Pattern reinforced: boundary review = remove external infrastructure docs, reframe platform integration docs to clarify whose feature it is, keep Squad behavior/config docs. Changes staged for commit. + diff --git a/docs/src/content/docs/features/issue-templates.md b/docs/src/content/docs/features/issue-templates.md index e31c403a1..adf5a04ad 100644 --- a/docs/src/content/docs/features/issue-templates.md +++ b/docs/src/content/docs/features/issue-templates.md @@ -16,6 +16,8 @@ When GitHub Issues are your work queue, creating tasks should be frictionless. I ## Why Issue Templates Matter for Squad +GitHub provides Issue Templates — a platform feature that pre-fills labels, fields, and structure when creating new issues. This guide shows how to configure templates that work smoothly with Squad's label-based routing. + Squad operates best when work is captured as GitHub Issues. But creating an issue from scratch takes time: you need to remember the right labels, format the description consistently, and ensure the structure matches what agents expect. Issue templates solve this: @@ -113,7 +115,7 @@ body: When Ralph scans the board, this issue is already labeled `squad:pao` — no triage needed, work goes straight to PAO. -### Setting Up Squad Member Labels +### Setting up labels for Squad routing Create labels in your repository for each squad member: diff --git a/docs/src/content/docs/scenarios/proactive-communication.md b/docs/src/content/docs/scenarios/proactive-communication.md deleted file mode 100644 index e10edda7c..000000000 --- a/docs/src/content/docs/scenarios/proactive-communication.md +++ /dev/null @@ -1,219 +0,0 @@ -# Proactive Communication Patterns - -> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. - - -**Try this to set up outbound notifications:** -```bash -# Store your Teams webhook URL -echo "https://outlook.office.com/webhook/..." > ~/.squad/teams-webhook.url -``` - -**Try this to scan for external work:** -``` -Ralph, check Teams and email for new work -``` - -Squad can both push notifications to you AND pull information from your environment. These patterns work independently but are most powerful together, creating a two-way communication layer between Squad and your work environment. - ---- - -## Outbound: Teams Webhook Notifications - -Any agent can send notifications to you via a Teams webhook by reading a stored URL and POSTing an Adaptive Card. This keeps you informed on mobile without being at the terminal. - -### Setup - -Store your webhook URL at a known path: - -```bash -# Create webhook URL file -mkdir -p ~/.squad -echo "https://outlook.office.com/webhook/..." > ~/.squad/teams-webhook.url -chmod 600 ~/.squad/teams-webhook.url # Protect webhook URL -``` - -### Notification Tiers - -Different urgency levels for different situations: - -| Tier | When | Examples | -|------|------|----------| -| ⚡ **Breaking** | Critical failures | CI broken, merge conflicts, blocked PRs | -| 📰 **Briefings** | Daily summaries | Progress reports, work completed | -| 📊 **Recaps** | Weekly highlights | Stats, trends, milestones | -| 🎯 **Flashes** | Quick snapshots | Board status, queue depth | - -**Key rule:** Only send when there's genuinely newsworthy activity. Don't spam yourself. - -### Sending a Notification - -```powershell -# Read webhook URL -$webhookUrl = Get-Content ~/.squad/teams-webhook.url -Raw - -# Build Adaptive Card -$card = @{ - type = "message" - attachments = @( - @{ - contentType = "application/vnd.microsoft.card.adaptive" - content = @{ - type = "AdaptiveCard" - version = "1.4" - body = @( - @{ - type = "TextBlock" - text = "⚡ CI Failure" - size = "Large" - weight = "Bolder" - } - @{ - type = "TextBlock" - text = "Build failed on main branch" - wrap = $true - } - ) - actions = @( - @{ - type = "Action.OpenUrl" - title = "View Logs" - url = "https://github.com/user/repo/actions/runs/123" - } - ) - } - } - ) -} | ConvertTo-Json -Depth 10 - -# Send notification -Invoke-RestMethod -Uri $webhookUrl -Method Post -Body $card -ContentType "application/json" -``` - -```bash -# Bash equivalent -WEBHOOK_URL=$(cat ~/.squad/teams-webhook.url) - -curl -X POST "$WEBHOOK_URL" \ - -H "Content-Type: application/json" \ - -d '{ - "type": "message", - "attachments": [{ - "contentType": "application/vnd.microsoft.card.adaptive", - "content": { - "type": "AdaptiveCard", - "version": "1.4", - "body": [{ - "type": "TextBlock", - "text": "⚡ CI Failure", - "size": "Large", - "weight": "Bolder" - }] - } - }] - }' -``` - -**Note:** This pattern works with any webhook-capable platform (Teams, Slack, Discord) — Teams is just the most common example. - ---- - -## Inbound: Scanning Emails and Teams for Work - -Use WorkIQ MCP server or Playwright to periodically read Teams channels and email, evaluating messages for actionability. When items need attention, create GitHub issues automatically. - -### Pattern Overview - -1. **Agent reads external sources** — Teams channels, email threads, shared documents -2. **Evaluates actionability** — Is this a bug report? Feature request? Question needing documentation? -3. **Creates GitHub issues** — Auto-create issues with `teams-bridge` label for items needing attention -4. **Anti-duplicate logic** — Check existing issues before creating new ones to avoid spam - -### Example Implementation - -```typescript -// Using WorkIQ MCP to scan Teams -const recentMessages = await workiq.getTeamsMessages({ - channel: "engineering", - since: "24h" -}); - -for (const message of recentMessages) { - // Evaluate: Does this need an issue? - const needsIssue = await evaluateActionability(message); - - if (!needsIssue) continue; - - // Check for duplicates - const existing = await github.searchIssues({ - q: `repo:owner/repo is:issue "${message.subject}"`, - label: "teams-bridge" - }); - - if (existing.length > 0) continue; - - // Create issue - await github.createIssue({ - title: message.subject, - body: `From Teams channel #${message.channel}:\n\n${message.content}`, - labels: ["teams-bridge"] - }); -} -``` - -**Key rule:** Do NOT spam. Only surface items that genuinely need attention. Use filters, keyword matching, and sentiment analysis to avoid creating issues for every casual message. - -### Label Convention - -Mark auto-created issues with `teams-bridge` (or similar) so the team knows the source. This also enables filtered views and routing rules. - ---- - -## Connecting the Loop - -These patterns work together to create a complete feedback cycle: - -1. **Inbound scanning creates issues** — External work enters GitHub -2. **Ralph picks them up** — Work monitor detects new issues, dispatches agents -3. **Agents do the work** — Code changes, PRs opened, reviews completed -4. **Outbound notifications report results** — You get notified on mobile - -The human stays informed via mobile (GitHub app + Teams notifications) without being at the terminal. You can review PRs from your phone, approve merges from anywhere, and jump in only when needed. - -### Example Flow - -``` -1. Customer posts bug report in Teams → WorkIQ creates issue #123 -2. Ralph triages issue #123 → Assigns to Backend agent -3. Backend creates PR #45 → Requests Lead review -4. Lead approves PR #45 → Merge successful -5. Ralph sends Teams notification → "🎯 Issue #123 closed via PR #45" -6. You see notification on phone → No action needed, just awareness -``` - ---- - -## Prerequisites - -These patterns enhance Squad but are not requirements. Squad works fine without them. - -### For Outbound Notifications - -- Teams webhook URL (or Slack/Discord equivalent) -- Stored at a known path (e.g., `~/.squad/teams-webhook.url`) -- Agents configured to send notifications for specific events - -### For Inbound Scanning - -- WorkIQ MCP server installed and authenticated -- Playwright or other automation tool for non-WorkIQ sources -- GitHub token with `repo` scope for creating issues -- Anti-spam filters and duplicate detection logic - ---- - -## See Also - -- [Ralph — Work Monitor](../features/ralph.md) — Ralph's work processing and monitoring -- [GitHub Issues Mode](../features/github-issues.md) — Issue-driven workflow basics -- [Ralph Operational Deployment Patterns](./ralph-operations.md) — 24/7 Ralph deployment diff --git a/docs/src/content/docs/scenarios/ralph-operations.md b/docs/src/content/docs/scenarios/ralph-operations.md deleted file mode 100644 index ad31c8760..000000000 --- a/docs/src/content/docs/scenarios/ralph-operations.md +++ /dev/null @@ -1,295 +0,0 @@ -# Ralph Operational Deployment Patterns - -> ⚠️ **Experimental** — Squad is alpha software. APIs, commands, and behavior may change between releases. - - -**Try this to understand Ralph's built-in behavior first:** -``` -Ralph, show me what's on the board -``` - -**Then explore this guide for 24/7 deployment patterns.** - -Ralph's built-in work monitoring runs during active Copilot sessions. When you need **persistent, unattended monitoring** — Ralph running 24/7 across server restarts, code updates, and agent configuration changes — you need an outer loop. This guide covers operational patterns for deploying Ralph in production environments. - -See [Ralph — Work Monitor](../features/ralph.md) for Ralph's in-session behavior, watch mode, and cloud heartbeat. This guide focuses on the **deployment layer** that wraps Squad. - ---- - -## The Outer Loop Pattern - -Ralph's in-session loop processes work until the board is clear, then idles. For continuous operation, wrap Squad in a persistent outer loop: - -```powershell -# Simplified pattern - see reference implementation for production version -while ($true) { - git pull origin main # Fresh code before each round - - # Spawn fresh Copilot process (picks up updated agent definitions) - gh copilot run "Ralph, process the work queue" - - # Log results, check exit code, update metrics - Write-Log "Round completed at $(Get-Date)" - - Start-Sleep -Seconds 600 # 10-minute pause between rounds -} -``` - -### Why the Outer Loop Matters - -- **Fresh context every round** — Agents may update charters, skills, or routing rules between sessions -- **Clean process state** — New Copilot process picks up MCP server changes, tool updates, configuration edits -- **Resilience** — Failed rounds don't crash the watchdog; the outer loop continues -- **Observability** — Each round produces discrete log entries with metrics - -**Reference implementation:** [ralph-watch.ps1](https://github.com/tamirdresher/squad-personal-demo/blob/main/ralph-watch.ps1) — A production-ready PowerShell outer loop with all patterns below integrated. - ---- - -## Single-Instance Guard - -Prevent multiple Ralph instances from running simultaneously using a system-wide named mutex or lockfile: - -```powershell -# Named mutex approach (Windows) -$mutex = [System.Threading.Mutex]::new($false, "Global\SquadRalphWatch") -if (-not $mutex.WaitOne(0)) { - Write-Error "Ralph is already running (PID found in heartbeat file)" - exit 1 -} - -try { - # Main watchdog loop runs here -} finally { - $mutex.ReleaseMutex() - $mutex.Dispose() -} -``` - -```bash -# Lockfile approach (Linux/macOS) -LOCKFILE="/var/run/squad-ralph.lock" - -if [ -f "$LOCKFILE" ]; then - PID=$(cat "$LOCKFILE") - if ps -p "$PID" > /dev/null 2>&1; then - echo "Ralph is already running (PID $PID)" - exit 1 - else - echo "Stale lockfile found, cleaning up" - rm -f "$LOCKFILE" - fi -fi - -echo $$ > "$LOCKFILE" -trap "rm -f $LOCKFILE" EXIT - -# Main watchdog loop runs here -``` - -### Stale Instance Detection - -If the watchdog crashes, the mutex releases automatically (Windows) or the lockfile remains but the process is gone (Unix). Before exiting with "already running", check if the PID in the lockfile/heartbeat is still alive. If not, clean up and proceed. - ---- - -## Structured Logging - -Each round produces a structured log entry with timestamp, round number, exit code, duration, and parsed metrics: - -```json -{ - "timestamp": "2025-03-15T14:32:00Z", - "round": 47, - "duration_seconds": 132, - "exit_code": 0, - "status": "success", - "metrics": { - "issues_closed": 2, - "prs_merged": 1, - "agent_actions": 5 - }, - "errors": [] -} -``` - -### Log Rotation - -Rotate logs when they reach 500 entries or 1 MB, whichever comes first. Keep the last 5 rotated files for historical analysis: - -- `ralph-watch.log` — current log -- `ralph-watch.log.1` — previous rotation -- `ralph-watch.log.2` through `ralph-watch.log.5` — older rotations - ---- - -## Failure Alerting - -Transient failures are normal in distributed systems. Alert only after **N consecutive failures** to avoid notification fatigue: - -```powershell -$consecutiveFailures = 0 -$FAILURE_THRESHOLD = 3 - -while ($true) { - $exitCode = Run-RalphRound - - if ($exitCode -ne 0) { - $consecutiveFailures++ - - if ($consecutiveFailures -ge $FAILURE_THRESHOLD) { - Send-Alert @{ - Title = "Ralph Watchdog: $consecutiveFailures consecutive failures" - ExitCode = $exitCode - Timestamp = Get-Date - LogTail = Get-Content ralph-watch.log -Tail 50 - } - } - } else { - $consecutiveFailures = 0 # Reset on success - } - - Start-Sleep -Seconds 600 -} -``` - -### Alert Channels - -- **Teams webhook** — Send structured cards with failure count, exit code, log excerpts -- **Email** — SMTP delivery for critical alerts -- **PagerDuty/Opsgenie** — Escalate to on-call if threshold exceeds N hours - ---- - -## Heartbeat File - -Write a JSON heartbeat file before and after every round with status, metrics, and process ID: - -```json -{ - "status": "running", - "pid": 12345, - "current_round": 47, - "last_success": "2025-03-15T14:32:00Z", - "metrics": { - "total_issues_closed": 142, - "total_prs_merged": 89, - "uptime_hours": 336 - }, - "health": "ok" -} -``` - -External monitoring tools (Prometheus, Datadog, Azure Monitor) can: - -- Poll the heartbeat file every minute -- Alert if `last_success` timestamp is older than expected round interval × 2 -- Track cumulative metrics over time -- Verify the PID is still running - -### Heartbeat Update Pattern - -```powershell -# Before round starts -$heartbeat = @{ - status = "running" - pid = $PID - current_round = $roundNumber - last_success = $lastSuccess -} -$heartbeat | ConvertTo-Json | Set-Content heartbeat.json - -# After round completes -$heartbeat.status = "idle" -$heartbeat.last_success = Get-Date -Format "o" -$heartbeat.metrics.total_issues_closed += $issuesClosed -$heartbeat | ConvertTo-Json | Set-Content heartbeat.json -``` - ---- - -## Background Activity Monitor - -While the Copilot session runs, tail logs every 30 seconds to show what's happening: - -```powershell -# Start Ralph round in background -$job = Start-Job -ScriptBlock { - gh copilot run "Ralph, process the work queue" -} - -# Monitor activity while it runs -while ($job.State -eq 'Running') { - # Show last 5 lines of Squad log - Get-Content .squad/log/session-*.log -Tail 5 | Write-Host -ForegroundColor Cyan - - Start-Sleep -Seconds 30 -} - -# Collect final results -$result = Receive-Job -Job $job -Wait -``` - -This prevents the terminal from sitting silently for 10+ minutes while Ralph works through a large backlog. The operator sees progress in real-time. - ---- - -## Deployment Checklist - -When deploying Ralph in production: - -- [ ] Outer loop pulls latest code before each round -- [ ] Single-instance guard prevents duplicate watchdogs -- [ ] Structured logging with rotation (500 entries or 1 MB) -- [ ] Failure alerting after N=3 consecutive failures -- [ ] Heartbeat file updated before and after rounds -- [ ] Background activity monitor shows progress -- [ ] Monitoring system polls heartbeat file -- [ ] Alert channels tested (Teams, email, PagerDuty) -- [ ] Log rotation tested (verify old logs are kept) -- [ ] Stale instance cleanup tested (kill PID, verify recovery) - ---- - -## Architecture Summary - -Ralph operates at three layers (from [Ralph — Work Monitor](../features/ralph.md)): - -| Layer | When | How | -|-------|------|-----| -| **In-session** | You're at the keyboard | "Ralph, go" — active loop while work exists | -| **Local watchdog** | You're away but machine is on | `squad watch --interval 10` | -| **Cloud heartbeat** | Fully unattended | `squad-heartbeat.yml` GitHub Actions cron | - -This guide adds a **fourth layer** — the operational wrapper that makes Ralph production-ready: - -| Layer | Purpose | -|-------|---------| -| **Outer loop** | Persistent deployment — fresh code, fresh process, structured logging, alerting | - ---- - -## Sample Prompts - -``` -Ralph, process the work queue -``` - -The outer loop invokes this command each round. Ralph triages issues, dispatches agents, monitors PRs, and reports results. - ---- - -## Notes - -- The outer loop is **not part of Squad** — it's infrastructure you build around Squad -- Ralph's built-in watch mode (`squad watch`) is suitable for local development; the outer loop is for production -- Test your outer loop with a small backlog first (1-2 issues) before deploying to a live board -- Monitor heartbeat file staleness — if no update in 2× the expected round interval, investigate - ---- - -## See Also - -- [Ralph — Work Monitor](../features/ralph.md) — Ralph's built-in behavior -- [GitHub Issues Mode](../features/github-issues.md) — Issue-driven workflow -- [CI/CD Integration](./ci-cd-integration.md) — Automated Squad workflows diff --git a/test/docs-build.test.ts b/test/docs-build.test.ts index 17ef81046..74d294df4 100644 --- a/test/docs-build.test.ts +++ b/test/docs-build.test.ts @@ -22,7 +22,7 @@ const EXPECTED_GUIDES = ['tips-and-tricks', 'sample-prompts', 'personal-squad', const EXPECTED_REFERENCE = ['cli', 'sdk', 'config', 'api-reference', 'integration', 'tools-and-hooks', 'glossary']; const EXPECTED_SCENARIOS = [ - 'issue-driven-dev', 'existing-repo', 'ci-cd-integration', 'solo-dev', 'monorepo', 'team-of-humans', 'ralph-operations', 'proactive-communication', + 'issue-driven-dev', 'existing-repo', 'ci-cd-integration', 'solo-dev', 'monorepo', 'team-of-humans', ]; const EXPECTED_CONCEPTS = ['architecture', 'your-team', 'memory-and-knowledge', 'parallel-work', 'github-workflow', 'portability']; From 804207ad0c9f4fa231bed920306127b41a24223e Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:13:35 -0700 Subject: [PATCH 09/20] docs: add cross-org authentication scenario Created comprehensive documentation for working across GitHub personal accounts and GitHub Enterprise Managed Users (EMU): - New scenario page: scenarios/cross-org-auth.md - Three solution approaches: gh auth switch, Copilot instructions, Squad skill pattern - Git credential helper configuration - Common error messages and verification steps - Updated troubleshooting.md with cross-org auth section - Updated enterprise-platforms.md authentication section with cross-ref - Added navigation entry for Cross-Org Auth scenario - Updated test assertions in docs-build.test.ts Follows Microsoft Style Guide (sentence-case headings, active voice). Includes practical examples and cross-references to related pages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/agents/flight/history.md | 4 + .squad/agents/pao/history.md | 3 + .../docs/features/enterprise-platforms.md | 2 + .../content/docs/scenarios/cross-org-auth.md | 257 ++++++++++++++++++ .../content/docs/scenarios/troubleshooting.md | 22 ++ docs/src/navigation.ts | 1 + test/docs-build.test.ts | 1 + 7 files changed, 290 insertions(+) create mode 100644 docs/src/content/docs/scenarios/cross-org-auth.md diff --git a/.squad/agents/flight/history.md b/.squad/agents/flight/history.md index 126f3acea..5319a6bd4 100644 --- a/.squad/agents/flight/history.md +++ b/.squad/agents/flight/history.md @@ -12,3 +12,7 @@ 📌 **Team update (2026-03-10T12-55-49Z):** Adoption tracking architecture finalized. Three-tier system approved: Tier 1 (aggregate-only, `.github/adoption/`) shipping with PR #326; Tier 2 (opt-in registry) designed for next PR; Tier 3 (public showcase) launches when ≥5 projects opt in. Append-only file governance rule enforced to prevent data loss. Microsoft ampersand style guide adopted for all user-facing documentation. +### PR #331 Review — Boundary Review Pattern Reinforced (2026-03-10) +Approved PR #331 ("docs: scenario and feature guides from blog analysis") for merge. PAO's boundary review (remove external infrastructure docs, reframe platform features to clarify scope, keep Squad behavior/config docs) was executed correctly. Key decisions: (1) ralph-operations.md and proactive-communication.md deleted — both document infrastructure around Squad, not Squad itself; (2) issue-templates.md reframed to clarify "GitHub feature configured for Squad" not "Squad feature"; (3) reviewer-protocol.md Trust Levels section kept — documents user choice spectrum within Squad's existing review system. Litmus test pattern: if Squad doesn't ship the code/config, it's IRL content. Docs-test sync maintained. Pattern reinforced as reusable boundary review heuristic for future doc PRs. + +**Adoption tracking architecture — three-tier opt-in system:** `.squad/` is for team state only, not adoption data (boundary pattern). Move tracking to `.github/adoption/`. Never list individual repos without owner consent — aggregate metrics only until opt-in exists. Tier 1 (ship now) = aggregate monitoring. Tier 2 (design next) = opt-in registry in `.github/adoption/registry.json`. Tier 3 (launch later) = public showcase once ≥5 projects opt in. Monitoring infra (GitHub Action + script) is solid — keep it. Privacy-first architecture: code search results are public data, but individual listings require consent. diff --git a/.squad/agents/pao/history.md b/.squad/agents/pao/history.md index 61157a7fd..abf213842 100644 --- a/.squad/agents/pao/history.md +++ b/.squad/agents/pao/history.md @@ -40,3 +40,6 @@ Evaluated four docs pages from PR #331 (Tamir's blog analysis) against Squad-spe ### Boundary Review Execution (v0.8.26) Executed boundary review findings from PR #331: (1) Deleted ralph-operations.md (infrastructure around Squad, not Squad itself — moved to IRL); (2) Deleted proactive-communication.md (external tools/webhooks — moved to IRL); (3) Reframed issue-templates.md intro to clarify "GitHub feature configured for Squad" not "Squad feature"; (4) Updated EXPECTED_SCENARIOS in docs-build.test.ts to match remaining files. Pattern reinforced: boundary review = remove external infrastructure docs, reframe platform integration docs to clarify whose feature it is, keep Squad behavior/config docs. Changes staged for commit. +### Cross-Org Authentication Docs (v0.8.26) +Created docs/src/content/docs/scenarios/cross-org-auth.md covering GitHub personal + Enterprise Managed Users (EMU) multi-account auth. Three solutions documented: (1) gh auth switch for manual account toggling; (2) Copilot instructions (.github/copilot-instructions.md) for account mapping documentation; (3) Squad skill pattern for auth error detection and recovery. Covered git credential helpers (per-host and per-org), EMU hostname variations (github.com vs dedicated instances), and common error messages (HTTP 401, authentication required). Added cross-references in troubleshooting.md (new section), enterprise-platforms.md (authentication section), and navigation.ts. Updated test/docs-build.test.ts with 'cross-org-auth' in EXPECTED_SCENARIOS. Pattern: Microsoft Style Guide (sentence-case), "Try this" prompts at top, problem/solution structure, practical examples over abstractions, links to related pages at bottom. + diff --git a/docs/src/content/docs/features/enterprise-platforms.md b/docs/src/content/docs/features/enterprise-platforms.md index ca51877ae..97e3598f7 100644 --- a/docs/src/content/docs/features/enterprise-platforms.md +++ b/docs/src/content/docs/features/enterprise-platforms.md @@ -102,6 +102,8 @@ All fields are optional. Omitted fields use the defaults shown above. Squad uses the Azure CLI for ADO authentication — **no Personal Access Tokens (PATs) needed.** Run `az login` once, and Squad agents use your authenticated session for all operations. +For GitHub repositories, Squad uses the `gh` CLI for authentication. When working across multiple GitHub accounts (e.g., personal GitHub and Enterprise Managed Users), use `gh auth switch` to toggle between accounts. See [Cross-organization authentication](../scenarios/cross-org-auth) for detailed multi-account setup. + Alternatively, if the Azure DevOps MCP server is configured in your environment, Squad will use it automatically for richer API access. Add it to `.copilot/mcp-config.json`: ```json diff --git a/docs/src/content/docs/scenarios/cross-org-auth.md b/docs/src/content/docs/scenarios/cross-org-auth.md new file mode 100644 index 000000000..2442d6be6 --- /dev/null +++ b/docs/src/content/docs/scenarios/cross-org-auth.md @@ -0,0 +1,257 @@ +# Cross-organization authentication + +**Try this when you have repos in multiple GitHub accounts:** +``` +I work across personal GitHub and Enterprise Managed Users +``` + +**Try this to set up multi-account auth:** +``` +Show me how to configure gh CLI for multiple GitHub accounts +``` + +You have repositories in both personal GitHub (github.com) and GitHub Enterprise Cloud with Enterprise Managed Users (EMU). The `gh` CLI and git credentials are tied to one account at a time. Squad agents hit authentication errors when working across these boundaries. + +--- + +## The problem + +GitHub Enterprise Managed Users (EMU) provisions user accounts managed by your enterprise. Usernames typically follow a pattern like `username_shortcode` (e.g., `alice_acme`). When you work across personal GitHub and EMU organizations: + +1. Your git credentials authenticate to one account at a time +2. The `gh` CLI authenticates to one account at a time +3. Squad agents inherit your authentication context +4. When an agent tries to access a repo tied to a different account, authentication fails + +**Common error messages:** + +``` +HTTP 401: Bad credentials (github.com/api/v3) +``` + +``` +gh: authentication required for https://github.com/ORGANIZATION/REPO +``` + +``` +fatal: could not read Username for 'https://github.com': terminal prompts disabled +``` + +--- + +## Solution 1: Use `gh auth switch` + +The `gh` CLI supports multiple authenticated accounts. Log in with both your personal and EMU accounts, then switch between them as needed. + +### Step 1: Log in with both accounts + +```bash +# Log in to personal GitHub +gh auth login + +# Log in to EMU account (use the EMU hostname if your org uses a separate instance) +gh auth login --hostname github.com +``` + +If your EMU organization uses a dedicated GitHub Enterprise Cloud hostname (e.g., `ghe.mycompany.com`), specify it: + +```bash +gh auth login --hostname ghe.mycompany.com +``` + +### Step 2: Check active account + +```bash +gh auth status +``` + +Output shows which account is currently active: + +``` +github.com + ✓ Logged in to github.com as alice (keyring) + ✓ Git operations for github.com configured to use https protocol. + ✓ Token: gho_**** + +ghe.mycompany.com + ✓ Logged in to ghe.mycompany.com as alice_acme (keyring) + ✓ Active account +``` + +### Step 3: Switch accounts + +```bash +gh auth switch +``` + +Select the account you need: + +``` +? What account do you want to switch to? + > alice (github.com) + alice_acme (ghe.mycompany.com) +``` + +Or switch directly: + +```bash +gh auth switch --user alice +gh auth switch --user alice_acme --hostname ghe.mycompany.com +``` + +--- + +## Solution 2: Copilot instructions + +Add account mapping to `.github/copilot-instructions.md` so Squad agents know which account to use for which repositories. + +Create or update `.github/copilot-instructions.md` in your repository: + +```markdown +# GitHub Account Context + +When working with repositories across multiple GitHub accounts, use the following mappings: + +## Personal GitHub (github.com) +- Authenticated as: alice +- Repositories: + - github.com/alice/portfolio + - github.com/alice/blog + - github.com/open-source-org/community-project + +## Enterprise Managed Users (EMU) +- Authenticated as: alice_acme (ghe.mycompany.com) +- Repositories: + - ghe.mycompany.com/engineering/api-gateway + - ghe.mycompany.com/engineering/frontend + +Before executing `gh` or `git` commands, check the repository URL and switch to the correct account with `gh auth switch --user ` if needed. +``` + +Squad agents will read this instruction and switch accounts when they detect a cross-account operation. + +**User-level instructions:** If you work across multiple repos, add the account mapping to your global Copilot instructions at `~/.github/copilot-instructions.md` (or `%USERPROFILE%\.github\copilot-instructions.md` on Windows). + +--- + +## Solution 3: Squad skill + +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`: + +```markdown +# Cross-Organization Authentication Recovery + +When `gh` or `git` operations fail with authentication errors (HTTP 401, "authentication required", "Bad credentials"), detect the failure and switch to the correct GitHub account. + +## Detection + +Look for these error patterns: +- `HTTP 401: Bad credentials` +- `gh: authentication required for https://github.com` +- `fatal: could not read Username for 'https://github.com'` + +## Recovery + +1. Run `gh auth status` to see which accounts are available +2. Extract the repository's organization or hostname from the error message +3. Match the repository to the correct account (use `.github/copilot-instructions.md` if available) +4. Run `gh auth switch --user ` to switch accounts +5. Retry the failed operation + +## Example + +```bash +# Operation fails +gh pr create --repo engineering/api-gateway +# Error: gh: authentication required for https://ghe.mycompany.com/engineering/api-gateway + +# Check accounts +gh auth status +# alice (github.com) — active +# alice_acme (ghe.mycompany.com) + +# Switch to EMU account +gh auth switch --user alice_acme --hostname ghe.mycompany.com + +# Retry operation +gh pr create --repo engineering/api-gateway +# Success +``` +``` + +The Scribe or another agent can apply this skill when auth errors occur. + +--- + +## Git credential helpers + +The `gh` CLI handles GitHub API authentication, but git clone/fetch/push operations use git's credential system. Configure git to use the correct credential helper per host or organization. + +### Per-host credentials + +If your EMU organization uses a separate hostname (e.g., `ghe.mycompany.com`): + +```bash +# Configure git to use gh CLI as credential helper for both hosts +git config --global credential.https://github.com.helper "!gh auth git-credential" +git config --global credential.https://ghe.mycompany.com.helper "!gh auth git-credential" +``` + +Git will now delegate authentication to the `gh` CLI, which uses the active account from `gh auth switch`. + +### Per-organization credentials (advanced) + +If both accounts share `github.com` but belong to different organizations: + +```bash +# Use gh CLI for personal repos +git config --global credential.https://github.com/alice.helper "!gh auth git-credential" + +# Use gh CLI for EMU org repos +git config --global credential.https://github.com/ORGANIZATION.helper "!gh auth git-credential" +``` + +**Note:** This requires git 2.36+ for per-URL credential helpers. + +--- + +## Verify active account + +Before running Squad agents, check which GitHub account is active: + +```bash +gh auth status +``` + +Look for the account marked as **Active**. + +To verify git operations use the correct account: + +```bash +# Test with a repository from each org +gh repo view alice/portfolio +gh repo view engineering/api-gateway +``` + +Both should succeed without authentication errors. + +--- + +## Tips + +- **Switch before starting Squad** — run `gh auth switch` before launching a Squad session if you know which repos you'll work on +- **Error detection works both ways** — if an agent hits an auth error, check `gh auth status` and switch manually before retrying +- **Use Copilot instructions for documentation** — document account mappings in `.github/copilot-instructions.md` so Squad agents (and human teammates) know which account to use +- **Test both accounts** — verify both `gh` and `git` operations work for each account before relying on multi-account workflows +- **EMU hostname varies** — some EMU orgs use `github.com` with organization-scoped access; others use dedicated hostnames like `ghe.mycompany.com`. Check with your GitHub admin. +- **Token permissions matter** — EMU accounts may have restricted permissions. Ensure your token has `repo`, `read:org`, and `workflow` scopes. + +--- + +## See also + +- [Private repos](./private-repos) — privacy and security for Squad on enterprise repos +- [Enterprise platforms](../features/enterprise-platforms) — Azure DevOps and Microsoft Planner support +- [Troubleshooting](./troubleshooting) — common Squad issues and fixes diff --git a/docs/src/content/docs/scenarios/troubleshooting.md b/docs/src/content/docs/scenarios/troubleshooting.md index 913e4c3c2..36fae207f 100644 --- a/docs/src/content/docs/scenarios/troubleshooting.md +++ b/docs/src/content/docs/scenarios/troubleshooting.md @@ -70,6 +70,28 @@ Common issues and fixes for Squad installation and usage. --- +## Authentication fails on cross-org repos + +**Problem:** Squad agents hit authentication errors when working with repositories across personal GitHub and GitHub Enterprise Managed Users (EMU) organizations. + +**Cause:** The `gh` CLI and git credentials are tied to one account at a time. When you switch contexts between personal and EMU repos, the active account may not have access to the target repository. + +**Fix:** + +1. Use `gh auth switch` to toggle between authenticated accounts: + ```bash + gh auth status + gh auth switch --user + ``` + +2. Add account mappings to `.github/copilot-instructions.md` so Squad agents know which account to use for which repos. + +3. Configure git credential helpers per host or organization. + +See [Cross-organization authentication](./cross-org-auth) for detailed setup instructions. + +--- + ## Node.js version too old **Problem:** `npx github:bradygaster/squad` fails with an engine compatibility error, or Squad behaves unexpectedly. diff --git a/docs/src/navigation.ts b/docs/src/navigation.ts index 5e975ec0b..0521230b7 100644 --- a/docs/src/navigation.ts +++ b/docs/src/navigation.ts @@ -102,6 +102,7 @@ export const NAV_SECTIONS: NavSection[] = [ { title: 'Upgrading', slug: 'scenarios/upgrading' }, { title: 'Multi-Codespace', slug: 'scenarios/multi-codespace' }, { title: 'Private Repos', slug: 'scenarios/private-repos' }, + { title: 'Cross-Org Auth', slug: 'scenarios/cross-org-auth' }, { title: 'Team Portability', slug: 'scenarios/team-portability' }, { title: 'Team State Storage', slug: 'scenarios/team-state-storage' }, { title: 'Switching Models', slug: 'scenarios/switching-models' }, diff --git a/test/docs-build.test.ts b/test/docs-build.test.ts index 74d294df4..3f2aacc14 100644 --- a/test/docs-build.test.ts +++ b/test/docs-build.test.ts @@ -23,6 +23,7 @@ const EXPECTED_REFERENCE = ['cli', 'sdk', 'config', 'api-reference', 'integratio const EXPECTED_SCENARIOS = [ 'issue-driven-dev', 'existing-repo', 'ci-cd-integration', 'solo-dev', 'monorepo', 'team-of-humans', + 'cross-org-auth', ]; const EXPECTED_CONCEPTS = ['architecture', 'your-team', 'memory-and-knowledge', 'parallel-work', 'github-workflow', 'portability']; From a180d8d61d0b7f0cd54b206c11466adf37b5da69 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:07:38 -0700 Subject: [PATCH 10/20] test: sync EXPECTED_SCENARIOS and EXPECTED_FEATURES with disk Fixed test assertion arrays to match actual files on disk: - EXPECTED_SCENARIOS: expanded from 7 to 25 entries - EXPECTED_FEATURES: added new array with 32 entries This resolves the sync issue identified in PR #331 review where test assertions were out of sync with docs/src/content/docs/ structure. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- test/docs-build.test.ts | 61 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/test/docs-build.test.ts b/test/docs-build.test.ts index 3f2aacc14..513a7d5ec 100644 --- a/test/docs-build.test.ts +++ b/test/docs-build.test.ts @@ -22,8 +22,66 @@ const EXPECTED_GUIDES = ['tips-and-tricks', 'sample-prompts', 'personal-squad', const EXPECTED_REFERENCE = ['cli', 'sdk', 'config', 'api-reference', 'integration', 'tools-and-hooks', 'glossary']; const EXPECTED_SCENARIOS = [ - 'issue-driven-dev', 'existing-repo', 'ci-cd-integration', 'solo-dev', 'monorepo', 'team-of-humans', + 'aspire-dashboard', + 'ci-cd-integration', + 'client-compatibility', 'cross-org-auth', + 'disaster-recovery', + 'existing-repo', + 'issue-driven-dev', + 'keep-my-squad', + 'large-codebase', + 'mid-project', + 'monorepo', + 'multi-codespace', + 'multiple-squads', + 'new-project', + 'open-source', + 'private-repos', + 'release-process', + 'scaling-workstreams', + 'solo-dev', + 'switching-models', + 'team-of-humans', + 'team-portability', + 'team-state-storage', + 'troubleshooting', + 'upgrading', +]; + +const EXPECTED_FEATURES = [ + 'ceremonies', + 'consult-mode', + 'copilot-coding-agent', + 'directives', + 'enterprise-platforms', + 'export-import', + 'github-issues', + 'gitlab-issues', + 'human-team-members', + 'issue-templates', + 'labels', + 'marketplace', + 'mcp', + 'memory', + 'model-selection', + 'notifications', + 'parallel-execution', + 'plugins', + 'prd-mode', + 'project-boards', + 'ralph', + 'remote-control', + 'response-modes', + 'reviewer-protocol', + 'routing', + 'skills', + 'squad-rc', + 'streams', + 'team-setup', + 'upstream-inheritance', + 'vscode', + 'worktrees', ]; const EXPECTED_CONCEPTS = ['architecture', 'your-team', 'memory-and-knowledge', 'parallel-work', 'github-workflow', 'portability']; @@ -172,6 +230,7 @@ describe('Docs Build Script (Astro)', () => { ...EXPECTED_GUIDES.map(n => ({ dir: 'guide', name: n })), ...EXPECTED_REFERENCE.map(n => ({ dir: 'reference', name: n })), ...EXPECTED_SCENARIOS.map(n => ({ dir: 'scenarios', name: n })), + ...EXPECTED_FEATURES.map(n => ({ dir: 'features', name: n })), ...EXPECTED_CONCEPTS.map(n => ({ dir: 'concepts', name: n })), ]; for (const { dir, name } of allExpected) { From 02fefa74c3d83a94237d328f26f3e1b5ea686664 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 18:30:18 -0700 Subject: [PATCH 11/20] =?UTF-8?q?docs(ai-team):=20Merge=20post-work=20orch?= =?UTF-8?q?estration=20=E2=80=94=20boundary=20heuristic,=20content=20triag?= =?UTF-8?q?e,=20remote=20access=20proposal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Session: 2026-03-11T01-27-57-post-work-orchestration Agents: FIDO (Quality Owner), Flight (Lead) Changes: - Orchestration logs for FIDO (test assertion sync fix, commit 6599db6) and Flight (content triage skill) - Merged 6 decisions from inbox to decisions.md - Consolidated boundary heuristic: "Squad Ships It" (docs vs IRL) across content-triage skill and PR #331 review - Added content-triage workflow for external content integration - Added phased rollout proposal for remote Squad access (Discussions → Copilot → Chat) - Added PR trust levels spectrum (full/selective/self-managing) - Cross-agent updates: FIDO, Flight, PAO history.md sync; boundary heuristic shared Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/agents/fido/history.md | 45 +++++++++ .squad/agents/flight/history.md | 7 ++ .squad/agents/pao/history.md | 2 + .squad/decisions.md | 162 +++++++++++++++++++++++++++++++- 4 files changed, 215 insertions(+), 1 deletion(-) diff --git a/.squad/agents/fido/history.md b/.squad/agents/fido/history.md index 774259727..0ed0f2f8c 100644 --- a/.squad/agents/fido/history.md +++ b/.squad/agents/fido/history.md @@ -21,3 +21,48 @@ cli-packaging-smoke.test.ts validates the PACKAGED CLI artifact (npm pack → in 📌 **Team update (2026-03-08T21:18:00Z):** FIDO + EECOM released unanimous GO verdict for v0.8.24. Smoke test approved as release gate. FIDO confirmed 32/32 pass + publish.yml wired correctly. EECOM confirmed 26/26 commands + packaging complete (minor gap: "streams" alias untested, non-blocking). +### PR #331 Quality Gate Review — NO-GO (Blocking Issues Found) (2026-03-10T14:13:00Z) + +**CRITICAL VIOLATIONS DETECTED:** + +1. **Stale Test Assertions (Hard Rule Violation)** — EXPECTED_SCENARIOS array in test/docs-build.test.ts contains only 7 values ['issue-driven-dev', 'existing-repo', 'ci-cd-integration', 'solo-dev', 'monorepo', 'team-of-humans', 'cross-org-auth'], but 25 scenario files exist on disk (aspire-dashboard, client-compatibility, disaster-recovery, keep-my-squad, large-codebase, mid-project, multi-codespace, multiple-squads, new-project, open-source, private-repos, release-process, scaling-workstreams, switching-models, team-portability, team-state-storage, troubleshooting, upgrading, + 7 in array). My charter: "When I add test count assertions, I MUST keep them in sync with the actual files on disk. Stale assertions that block CI are MY responsibility to prevent." This is MY responsibility to catch. + +2. **Missing EXPECTED_FEATURES Array** — PR adds 'features' to the sections list in test/docs-build.test.ts (line 46), but NO EXPECTED_FEATURES array exists. Test line 171 "all expected doc pages produce HTML in dist/" will skip features entirely. 32 feature files exist (.md files in docs/src/content/docs/features/). + +📌 **Team update (2026-03-11T01:27:57Z):** PR #331 quality gate resolved. FIDO fixed test assertion sync in docs-build.test.ts: EXPECTED_SCENARIOS updated to 25 entries, EXPECTED_FEATURES array created with 32 entries, test assertions updated for features validation. Tests: 6/6 passing. Commit: 6599db6. Blocking NO-GO converted to approval gate cleared. Lesson reinforced: test assertions must be synced to filesystem state; CI passing ≠ coverage. + +3. **Incomplete Test Coverage Sync** — PAO's history (line 41) states "Updated EXPECTED_SCENARIOS in docs-build.test.ts to match remaining files" after deleting ralph-operations.md and proactive-communication.md. But the diff shows ONLY a single-line change (adding 'features' to sections array). The full test update was not committed. + +**POSITIVE FINDINGS:** +- ✅ CI passed (test run completed successfully on GitHub) +- ✅ Markdown structure tests pass (6/6 syntax checks) +- ✅ Docs are well-written: sentence-case headings, active voice, present tense, second person +- ✅ Cross-references valid (labels.md link verified) +- ✅ No duplicate "How It Works" heading in reviewer-protocol.md +- ✅ Content intact (no accidental loss) +- ✅ Microsoft Style Guide compliance confirmed + +**ROOT CAUSE:** PAO staged the boundary review changes but the test update commit was incomplete. The assertion arrays must be synchronized before merge. + +**REQUIRED FIX:** Update test/docs-build.test.ts: +1. EXPECTED_SCENARIOS = [ all 25 actual scenario files, sorted ] +2. EXPECTED_FEATURES = [ all 32 actual feature files, sorted ] +3. Regenerate to match disk reality (use filesystem discovery if the project wants test-resilience) + +**VERDICT:** 🔴 **NO-GO** — Merge blocked until test assertions sync with disk state. This is a quality gate violation. + +### Test Assertion Sync Fix (2026-03-10T14:20:00Z) + +**Issue resolved:** Fixed stale test assertions in test/docs-build.test.ts identified during PR #331 review. + +**Changes made:** +1. Expanded EXPECTED_SCENARIOS from 7 to 25 entries (matched all .md files in docs/src/content/docs/scenarios/) +2. Added EXPECTED_FEATURES array with 32 entries (matched all .md files in docs/src/content/docs/features/) +3. Updated test logic to include features section in HTML build validation + +**Validation:** All structure validation tests passing (6/6). Build tests skipped as expected (Astro not installed). Arrays now accurately reflect disk state. + +**Commit:** 6599db6 on branch squad/289-squad-dir-explainer + +**Learning:** When test assertions reference file counts, they MUST be kept in sync with disk reality. The principle applies to ALL assertion arrays (EXPECTED_SCENARIOS, EXPECTED_FEATURES, EXPECTED_GUIDES, EXPECTED_REFERENCE, etc.). Consider dynamic discovery pattern (used in EXPECTED_BLOG) for resilience against content additions. + diff --git a/.squad/agents/flight/history.md b/.squad/agents/flight/history.md index 5319a6bd4..b0125983f 100644 --- a/.squad/agents/flight/history.md +++ b/.squad/agents/flight/history.md @@ -16,3 +16,10 @@ Approved PR #331 ("docs: scenario and feature guides from blog analysis") for merge. PAO's boundary review (remove external infrastructure docs, reframe platform features to clarify scope, keep Squad behavior/config docs) was executed correctly. Key decisions: (1) ralph-operations.md and proactive-communication.md deleted — both document infrastructure around Squad, not Squad itself; (2) issue-templates.md reframed to clarify "GitHub feature configured for Squad" not "Squad feature"; (3) reviewer-protocol.md Trust Levels section kept — documents user choice spectrum within Squad's existing review system. Litmus test pattern: if Squad doesn't ship the code/config, it's IRL content. Docs-test sync maintained. Pattern reinforced as reusable boundary review heuristic for future doc PRs. **Adoption tracking architecture — three-tier opt-in system:** `.squad/` is for team state only, not adoption data (boundary pattern). Move tracking to `.github/adoption/`. Never list individual repos without owner consent — aggregate metrics only until opt-in exists. Tier 1 (ship now) = aggregate monitoring. Tier 2 (design next) = opt-in registry in `.github/adoption/registry.json`. Tier 3 (launch later) = public showcase once ≥5 projects opt in. Monitoring infra (GitHub Action + script) is solid — keep it. Privacy-first architecture: code search results are public data, but individual listings require consent. + +**Remote Squad access — three-phase rollout:** Phase 1 (ship first): GitHub Discussions bot with `/squad` command. Workflow checks out repo → has full `.squad/` context → answers questions → posts reply. 1 day build, zero hosting, respects repo privacy automatically. Phase 2 (high value): GitHub Copilot Extension — fetches `.squad/` files via GitHub API, answers inline in any Copilot client (VS Code, CLI, mobile). Works truly remote, instant, no cold start. 1 week build. Phase 3 (enterprise): Slack/Teams bot for companies. Webhook + GitHub API fetch. 2 weeks build. Constraint: Squad needs `.squad/` state (team.md, decisions.md, histories, routing) to answer intelligently. Any remote solution must solve context access. GitHub Actions workflows solve this for free (checkout gives full state). Copilot Extension uses Contents API. Discussions wins for MVP because it's async (perfect for knowledge queries), persistent (answers are searchable), and zero infra. Proposal-first: write `docs/proposals/remote-squad-access.md` before building. + +### Content Triage Skill Codified (2026-03-10) +Created `.squad/skills/content-triage/SKILL.md` to codify the boundary heuristic from PR #331. Defines repeatable workflow for triaging external content (blog posts, sample repos, videos, talks) to determine what belongs in Squad's public docs vs IRL tracking. Key components: (1) "Squad Ships It" litmus test — if Squad doesn't ship the code/config, it's IRL content; (2) triage workflow triggered by `content-triage` label or external content reference in issue body; (3) output format with boundary analysis, sub-issues for PAO (doc extraction), and IRL reference entry for Scribe; (4) label convention (`content:blog`, `content:sample`, `content:video`, `content:talk`); (5) Ralph integration for routing to Flight, creating sub-issues, and notifying Scribe. Examples include Tamir blog analysis (PR #331), sample repo with ops patterns, and conference talk. Pattern prevents infrastructure docs from polluting Squad's public docs while ensuring community content accelerates adoption through proper extraction and referencing. + +📌 **Team update (2026-03-11T01:27:57Z):** Content triage skill finalized; "Squad Ships It" boundary heuristic codified into shared team decision (decisions.md). Remote Squad access phased rollout approved (Discussions bot → Copilot Extension → Chat bot). PR #331 boundary review pattern established as standard for all doc PRs. Triage workflow enables Flight to scale as community content accelerates. diff --git a/.squad/agents/pao/history.md b/.squad/agents/pao/history.md index abf213842..ae52afed4 100644 --- a/.squad/agents/pao/history.md +++ b/.squad/agents/pao/history.md @@ -28,6 +28,8 @@ Squad docs use plain markdown without Astro frontmatter. Structure: title (H1), ### Proactive Communication Patterns (v0.8.26) Two-way communication layer between Squad and work environment. Outbound: Teams webhook notifications (breaking, briefings, recaps, flashes) sent via Adaptive Cards — only when newsworthy. Inbound: WorkIQ/Playwright scanning of Teams channels and email → auto-create GitHub issues with teams-bridge label, anti-duplicate logic enforced. Loop: inbound creates issues → Ralph dispatches → agents work → outbound notifies results. Human stays informed on mobile. Prerequisites are enhancements, not requirements. +📌 **Team update (2026-03-11T01:27:57Z):** Proactive communication patterns and PR trust levels (full/selective/self-managing spectrum) documented in decisions.md. Pattern rationale reinforced: Ralph 24/7 autonomous deployment requires awareness loop (Teams webhooks for outbound) and external work integration (WorkIQ scanning for inbound). Trust levels enable context-appropriate oversight without bottlenecking teams. + ### PR Trust Model Documentation (v0.8.26) Three trust levels for PR management: (1) Full review (default, team repos) — human gate on every merge; (2) Selective review (personal projects with patterns) — human reviews only critical paths; (3) Self-managing (solo personal repos only) — Squad merges own PRs, human reviews retroactively. Added to reviewer-protocol.md as new section. Important: self-managing ≠ unmonitored; use Ralph work monitoring and Teams notifications for awareness. Decision matrix included for when to use each level. diff --git a/.squad/decisions.md b/.squad/decisions.md index cd515051f..3e2d3c74e 100644 --- a/.squad/decisions.md +++ b/.squad/decisions.md @@ -60,8 +60,28 @@ ## Adoption & Community +### `.squad/` Directory Scope — Owner Directive +**By:** Brady (project owner, PR #326 review) +**Date:** 2026-03-10 + +**Directive:** The `.squad/` directory is **reserved for team state only** — roster, routing, decisions, agent histories, casting, and orchestration logs. Non-team data (adoption tracking, community metrics, reports) must NOT live in `.squad/`. Use `.github/` for GitHub platform integration or `docs/` for documentation artifacts. + +**Source:** [PR #326 comment](https://github.com/bradygaster/squad/pull/326#issuecomment-4029193833) + +--- + +### No Individual Repo Listing Without Consent — Owner Directive +**By:** Brady (project owner, PR #326 review) +**Date:** 2026-03-10 + +**Directive:** Growth metrics must report **aggregate numbers only** (e.g., "78+ repositories found via GitHub code search") — never name or link to individual community repos without explicit opt-in consent. The monitoring script and GitHub Action concepts are approved, but any public showcase or tracking list that identifies specific repos is blocked until a community consent plan exists. + +**Source:** [PR #326 comment](https://github.com/bradygaster/squad/pull/326#issuecomment-4029222967) + +--- + ### Adoption Tracking — Opt-In Architecture -**By:** Flight +**By:** Flight (implementing Brady's directives above) **Date:** 2026-03-09 Privacy-first adoption monitoring using a three-tier system: @@ -163,6 +183,146 @@ Ampersands (&) are prohibited in user-facing documentation headings and body tex **What:** Agents must NEVER write secrets, API keys, tokens, or credentials into conversational history, commit messages, logs, or any persisted file. Acknowledge receipt without echoing values. **Why:** Secrets in logs or history are a security incident waiting to happen. +--- + +## Squad Ecosystem Boundaries & Content Governance + +### Squad Docs vs Squad IRL Boundary (consolidated) +**By:** PAO (via Copilot), Flight +**Date:** 2026-03-10 +**Status:** Active pattern for all documentation PRs + +**Litmus test:** If Squad doesn't ship the code or configuration, the documentation belongs in Squad IRL, not the Squad framework docs. + +**Categories:** + +1. **Squad docs** — Features Squad ships (routing, charters, reviewer protocol, config, behavior) +2. **Squad IRL** — Infrastructure around Squad (webhooks, deployment patterns, logging, external tools, operational patterns) +3. **Gray area:** Platform features (GitHub Issue Templates) → Squad docs if framed as "how to configure X for Squad" + +**Examples applied (PR #331):** + +| Document | Decision | Reason | +|----------|----------|--------| +| ralph-operations.md | DELETE → IRL | Infrastructure (deployment, logging) around Squad, not Squad itself | +| proactive-communication.md | DELETE → IRL | External tools (Teams, WorkIQ) configured by community, not built into Squad | +| issue-templates.md | KEEP, reframe | GitHub platform feature; clarify scope: "a GitHub feature configured for Squad" | +| reviewer-protocol.md (Trust Levels) | KEEP | Documents user choice spectrum within Squad's existing review system | + +**Enforcement:** Code review + reframe pattern ("GitHub provides X. Here's how to configure it for Squad's needs."). Mark suspicious deletions for restore (append-only governance). + +**Future use:** Apply this pattern to all documentation PRs to maintain clean boundaries. + +--- + +### Content Triage Skill — External Content Integration +**By:** Flight +**Date:** 2026-03-10 +**Status:** Skill created at `.squad/skills/content-triage/SKILL.md` + +**Pattern:** External content (blog posts, sample repos, videos, conference talks) that helps Squad adoption must be triaged using the "Squad Ships It" boundary heuristic before incorporation. + +**Workflow:** +1. Triggered by `content-triage` label or external content reference in issue +2. Flight performs boundary analysis +3. Sub-issues generated for Squad-ownable content extraction (PAO responsibility) +4. FIDO verifies docs-test sync on extracted content +5. Scribe manages IRL references in `.github/irl/references.yml` (YAML schema) + +**Label convention:** `content:blog`, `content:sample`, `content:video`, `content:talk` + +**Why:** Pattern from PR #331 (Tamir Dresher blog) shows parallel extraction of Squad-ownable patterns (scenario guides, reviewer protocol) and infrastructure patterns (Ralph ops, proactive comms). Without clear boundary, teams pollute Squad docs with operational content or miss valuable patterns that should be generalized. + +**Impact:** Enables community content to accelerate Squad adoption without polluting core docs. Flight's boundary analysis becomes reusable decision framework. Prevents scope creep as adoption grows. + +--- + +### PR #331 Quality Gate — Test Assertion Sync +**By:** FIDO (Quality Owner) +**Date:** 2026-03-10 +**Status:** 🟢 CLEARED (test fix applied, commit 6599db6) + +**What was blocked:** Merge blocked on stale test assertions in `test/docs-build.test.ts`. + +**Critical violations resolved:** +1. `EXPECTED_SCENARIOS` array stale (7 vs 25 disk files) — ✅ Updated to 25 entries +2. `EXPECTED_FEATURES` constant undefined (32 feature files) — ✅ Created array with 32 entries +3. Test assertion incomplete — ✅ Updated to validate features section + +**Why this matters:** Stale assertions that don't reflect filesystem state cause silent test skips. Regression: If someone deletes a scenario file, the test won't catch it. CI passing doesn't guarantee test coverage — only that the test didn't crash. + +**Lessons:** +- Test arrays must be refreshed when filesystem content changes +- Incomplete commits break the test-reality sync contract +- FIDO's charter: When adding test count assertions, must keep in sync with disk state + +**Outcome:** Test suite: 6/6 passing. Assertions synced to filesystem. No regression risk from stale assertions. + +--- + +### Communication Patterns and PR Trust Models +**By:** PAO +**Date:** 2026-03-10 +**Status:** Documented in features/reviewer-protocol.md (trust levels section) and scenarios/proactive-communication.md (infrastructure pattern) + +**Decision:** Document emerging patterns in real Squad usage: proactive communication loops and PR review trust spectrum. + +**Components:** + +1. **Proactive communication patterns** — Outbound notifications (Teams webhooks), inbound scanning (Teams/email for work items), two-way feedback loop connecting external sources to Squad workflow + +2. **PR trust levels spectrum:** + - **Full review** (default for team repos) — All PRs require human review + - **Selective review** (personal projects with patterns) — Domain-expert or routine PRs can auto-merge + - **Self-managing** (solo personal repos only) — PRs auto-merge; Ralph's work monitoring provides retroactive visibility + +**Why:** Ralph 24/7 autonomous deployment creates an awareness gap — how does the human stay informed? Outbound notifications solve visibility. Inbound scanning solves "work lives in multiple places." Trust levels let users tune oversight to their context (full review for team repos, selective for personal projects, self-managing for solo work only). + +**Important caveat:** Self-managing ≠ unmonitored; Ralph's work monitoring and notifications provide retroactive visibility. + +**Anti-spam expectations:** Don't spam yourself outbound (notification fatigue), don't spam GitHub inbound (volume controls). + +--- + +### Remote Squad Access — Phased Rollout (Proposed) +**By:** Flight +**Date:** 2026-03-10 +**Status:** Proposed — awaits proposal document in `docs/proposals/remote-squad-access.md` + +**Context:** Squad currently requires a local clone to answer questions. Users want remote access from mobile, browser, or different machine without checking out repo. + +**Phases:** + +**Phase 1: GitHub Discussions Bot (Ship First)** +- Surface: GitHub Discussions +- Trigger: `/squad` command or `@squad` mention +- Context: GitHub Actions workflow checks out repo → full `.squad/` state +- Response: Bot replies to thread +- Feasibility: 1 day +- Why first: Easy to build, zero hosting, respects repo privacy, async Q&A, immediately useful + +**Phase 2: GitHub Copilot Extension (High Value)** +- Surface: GitHub Copilot chat (VS Code, CLI, web, mobile) +- Trigger: `/squad ask {question}` in any Copilot client +- Context: Extension fetches `.squad/` files via GitHub API (no clone) +- Response: Answer inline in Copilot +- Feasibility: 1 week +- Why second: Works everywhere Copilot exists, instant response, natural UX + +**Phase 3: Slack/Teams Bot (Enterprise Value)** +- Surface: Slack or Teams channel +- Trigger: `@squad` mention in channel +- Context: Webhook fetches `.squad/` via GitHub API +- Response: Bot replies in thread +- Feasibility: 2 weeks +- Why third: Enterprise teams live in chat; high value for companies using Squad + +**Constraint:** Squad's intelligence lives in `.squad/` (roster, routing, decisions, histories). Any remote solution must solve context access. GitHub Actions workflows provide checkout for free. Copilot Extension and chat bots use GitHub API to fetch files. + +**Implementation:** Before Phase 1 execution, write proposal document. New CLI command: `squad answer --context discussions --question "..."`. New workflow: `.github/workflows/squad-answer.yml`. + +**Privacy:** All approaches respect repo visibility or require authentication. Most teams want private by default. + ### Test assertion discipline — mandatory **By:** FIDO (formerly Hockney), v0.8.24 **What:** All code agents must update tests when changing APIs. FIDO has PR blocking authority on quality grounds. From 204ad5b25a58f8f4052f5b0ac2213194bd6d2a94 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Thu, 12 Mar 2026 13:00:59 -0700 Subject: [PATCH 12/20] docs(pao): add SCANNABILITY REVIEW hard rule to PAO charter Adds a mandatory scannability framework for all content reviews: - Paragraphs: narrative flow, conceptual explanations (3-4 sentences max) - Bullet lists: features, options, scannable items (parallel structure) - Tables: comparisons, structured reference data, attribute grids - Quotes/indents: warnings, callouts, cited material - Decision test: hunt-for-one-item = convert to bullets/table PAO applies this on every PR with documentation impact. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/agents/pao/charter.md | 6 ++++++ .squad/agents/pao/history.md | 3 +++ 2 files changed, 9 insertions(+) diff --git a/.squad/agents/pao/charter.md b/.squad/agents/pao/charter.md index aa4d55841..1d149c9b9 100644 --- a/.squad/agents/pao/charter.md +++ b/.squad/agents/pao/charter.md @@ -26,6 +26,12 @@ - **DOCS-TEST SYNC (hard rule):** When adding new docs pages (guides, blog posts), update the corresponding test assertions in test/docs-build.test.ts in the SAME commit. Stale test assertions that block CI are a docs team failure. - **CONTRIBUTOR RECOGNITION (hard rule):** Each release includes an update to the Contributors Guide page. No contribution goes unappreciated. - **DOC-IMPACT REVIEW (hard rule):** Review every PR for documentation impact. If a change affects user-facing behavior, ensure corresponding docs are updated or flag the gap. +- **SCANNABILITY REVIEW (hard rule):** All content must use the format that best serves scannability. Apply this framework during review: + - **Paragraphs:** Use for narrative flow, conceptual explanations, "why" context, and transitions. Limit to 3-4 sentences. If longer, consider breaking into sections or converting structured parts to lists/tables. + - **Bullet lists:** Use for features, options, steps (non-sequential), any items users scan for one thing. Start with strong verbs or nouns. Keep items parallel in structure. + - **Tables:** Use for comparisons (feature A vs B), structured reference data (config options, API parameters), or any grid of related attributes. Include headers that describe the relationship. + - **Quotes/indents:** Use for warnings, important callouts, citations, or examples. Reserve for content that needs visual separation. + - **Decision test:** If a reader is hunting for one specific item in a paragraph, convert to bullets or table. If explaining relationships between concepts, keep paragraph. If comparing options, use table. ## Boundaries diff --git a/.squad/agents/pao/history.md b/.squad/agents/pao/history.md index 85b620eb5..2a6217981 100644 --- a/.squad/agents/pao/history.md +++ b/.squad/agents/pao/history.md @@ -18,3 +18,6 @@ Release blog posts use YAML frontmatter with: title, date, author, wave, tags, s ### Roster & Contributor Recognition (v0.8.25) Squad moved to Apollo 13/NASA Mission Control naming scheme (Flight, Procedures, EECOM, FIDO, PAO, CAPCOM, CONTROL, Surgeon, Booster, GNC, Network, RETRO, INCO, GUIDO, Telemetry, VOX, DSKY, Sims, Handbook). CONTRIBUTORS.md tracks both team roster and community contributors; contributor table entries grow with PRs (append PR counts rather than replace, maintaining attribution history). + +### Scannability Framework (v0.8.25) +Format selection is a scannability decision, not style preference. Paragraphs for narrative/concepts (3-4 sentences max). Bullets for scannable items (features, options, non-sequential steps). Tables for comparisons or structured reference data (config, API params). Quotes/indents for callouts/warnings. Decision test: if reader hunts for one item in a paragraph, convert to bullets/table. This framework is now a hard rule in charter under SCANNABILITY REVIEW. From a5fa0487547f6a4422507ba555fa31faf4796454 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 12 Mar 2026 13:21:49 -0700 Subject: [PATCH 13/20] feat: add task context classification to coordinator acknowledgment The coordinator's Acknowledge Immediately section now requires a task context signal (Continuing/New task/Related pivot) before naming agents. This helps users understand whether the system recognizes their message as part of ongoing work or a fresh request. Closes #359 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/agents/squad.agent.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/agents/squad.agent.md b/.github/agents/squad.agent.md index a42f2ec0a..6108beb62 100644 --- a/.github/agents/squad.agent.md +++ b/.github/agents/squad.agent.md @@ -102,9 +102,23 @@ For each squad member with assigned issues, note them in the session context. Wh **The user should never see a blank screen while agents work.** Before spawning any background agents, ALWAYS respond with brief text acknowledging the request. Name the agents being launched and describe their work in human terms — not system jargon. This acknowledgment is REQUIRED, not optional. -- **Single agent:** `"Fenster's on it — looking at the error handling now."` -- **Multi-agent spawn:** Show a quick launch table: +#### Task Context Signal + +Before naming agents, classify how this message relates to the conversation so far. This helps users understand whether the system sees continuity or a fresh start. Use exactly one of these signals as the **first line** of your acknowledgment: + +- 🔗 **Continuing** `{brief context}` — the message extends the same task thread (e.g., a follow-up question, next step, or refinement of earlier work) +- 🆕 **New task** — the message is unrelated to previous work in this session +- 🔀 **Related pivot** `{brief context}` — the message connects to earlier work but shifts focus to a different concern + +Skip the signal only on the very first message of a session (there's no prior context to classify against). + +#### Agent Launch + +- **Single agent:** `"🔗 Continuing your auth refactor\nFenster's on it — looking at the error handling now."` +- **Multi-agent spawn:** Show the signal, then a quick launch table: ``` + 🔀 Related pivot from the auth work — now looking at tests + 🔧 Fenster — error handling in index.js 🧪 Hockney — writing test cases 📋 Scribe — logging session From 4a6e7b168c1ed8ac5bc1ed9151e54122c6826937 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 12 Mar 2026 13:30:39 -0700 Subject: [PATCH 14/20] feat: conversation-aware thinking phrases in ThinkingIndicator When prior conversation history exists, the ThinkingIndicator cycles through context-aware phrases (e.g. 'Reviewing conversation context', 'Connecting to previous work') instead of generic ones. This gives users immediate feedback that the system recognizes ongoing conversation. - Add CONVERSATION_PHRASES array to ThinkingIndicator - Add hasConversation prop threaded through App -> MessageStream -> ThinkingIndicator - Derive hasConversation from messages array in App.tsx (true when agent responses exist) Closes #360 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/cli/shell/components/App.tsx | 8 ++++- .../cli/shell/components/MessageStream.tsx | 4 +++ .../shell/components/ThinkingIndicator.tsx | 30 +++++++++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/packages/squad-cli/src/cli/shell/components/App.tsx b/packages/squad-cli/src/cli/shell/components/App.tsx index ab252a507..56ad6a310 100644 --- a/packages/squad-cli/src/cli/shell/components/App.tsx +++ b/packages/squad-cli/src/cli/shell/components/App.tsx @@ -314,6 +314,12 @@ export const App: React.FC = ({ registry, renderer, teamRoot, version, return undefined; }, [messages, processing]); + // True when there is prior conversation history (at least one agent response). + const hasConversation = useMemo( + () => messages.some(m => m.role === 'agent'), + [messages], + ); + // Only archived (overflow) messages go to Static scrollback. // Current messages stay in the live region so the user can always see // the recent conversation without scrolling. This prevents the @@ -467,7 +473,7 @@ export const App: React.FC = ({ registry, renderer, teamRoot, version, count to prevent overflow into the InputPrompt area. */} - + {/* Fixed input box at bottom — Copilot/Claude style */} diff --git a/packages/squad-cli/src/cli/shell/components/MessageStream.tsx b/packages/squad-cli/src/cli/shell/components/MessageStream.tsx index 0ee0731f7..23f95df10 100644 --- a/packages/squad-cli/src/cli/shell/components/MessageStream.tsx +++ b/packages/squad-cli/src/cli/shell/components/MessageStream.tsx @@ -53,6 +53,8 @@ interface MessageStreamProps { agentActivities?: Map; thinkingPhase?: ThinkingPhase; maxVisible?: number; + /** When true, thinking indicator shows conversation-aware phrases. */ + hasConversation?: boolean; } /** Format elapsed seconds for response timestamps. */ @@ -199,6 +201,7 @@ export const MessageStream: React.FC = ({ agentActivities, thinkingPhase, maxVisible = 50, + hasConversation = false, }) => { const visible = messages.slice(-maxVisible); const visibleOffset = Math.max(0, messages.length - maxVisible); @@ -327,6 +330,7 @@ export const MessageStream: React.FC = ({ elapsedMs={elapsedMs} activityHint={activityHint} phase={thinkingPhase} + hasConversation={hasConversation} /> )} diff --git a/packages/squad-cli/src/cli/shell/components/ThinkingIndicator.tsx b/packages/squad-cli/src/cli/shell/components/ThinkingIndicator.tsx index ba98cdcdd..f1b44dbec 100644 --- a/packages/squad-cli/src/cli/shell/components/ThinkingIndicator.tsx +++ b/packages/squad-cli/src/cli/shell/components/ThinkingIndicator.tsx @@ -19,6 +19,8 @@ export interface ThinkingIndicatorProps { elapsedMs: number; activityHint?: string; phase?: ThinkingPhase; + /** When true, cycles conversation-aware phrases instead of generic ones. */ + hasConversation?: boolean; } /** Rotating thinking phrases — cycled every few seconds to keep the UI alive. */ @@ -40,6 +42,25 @@ export const THINKING_PHRASES = [ 'Exploring possibilities', ]; +/** Context-aware phrases shown when conversation history exists. */ +export const CONVERSATION_PHRASES = [ + 'Reviewing conversation context', + 'Connecting to previous work', + 'Analyzing how this relates', + 'Checking conversation thread', + 'Considering prior context', + 'Building on earlier discussion', + 'Mapping to your session', + 'Evaluating options', + 'Consulting the team', + 'Synthesizing a response', + 'Weighing trade-offs', + 'Gathering context', + 'Crafting a plan', + 'Connecting the dots', + 'Reading the codebase', +]; + /** Map phase to its default label. */ function phaseLabel(phase: ThinkingPhase): string { switch (phase) { @@ -72,6 +93,7 @@ export const ThinkingIndicator: React.FC = ({ elapsedMs, activityHint, phase = 'routing', + hasConversation = false, }) => { const noColor = isNoColor(); const [frame, setFrame] = useState(0); @@ -86,14 +108,16 @@ export const ThinkingIndicator: React.FC = ({ return () => clearInterval(timer); }, [isThinking, noColor]); + const phrases = hasConversation ? CONVERSATION_PHRASES : THINKING_PHRASES; + // Rotate thinking phrases every 3 seconds useEffect(() => { if (!isThinking) { setPhraseIndex(0); return; } const timer = setInterval(() => { - setPhraseIndex(i => (i + 1) % THINKING_PHRASES.length); + setPhraseIndex(i => (i + 1) % phrases.length); }, 3000); return () => clearInterval(timer); - }, [isThinking]); + }, [isThinking, phrases]); // Reset frame when thinking starts useEffect(() => { @@ -108,7 +132,7 @@ export const ThinkingIndicator: React.FC = ({ // Resolve the display label: activity hint > rotating phrase > phase label const displayLabel = activityHint ?? ( - phase === 'connecting' ? phaseLabel(phase) : `${THINKING_PHRASES[phraseIndex]}...` + phase === 'connecting' ? phaseLabel(phase) : `${phrases[phraseIndex]}...` ); // NO_COLOR: no color props, use text labels From 429a4156ca8b7092fdd9e340c12a3cfcb7811d6d Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Thu, 12 Mar 2026 17:04:37 -0700 Subject: [PATCH 15/20] feat: add non-blocking version check on CLI startup (Phase 1) Adds a background update check that runs when the interactive shell starts. On startup, Squad checks the npm registry for a newer version and displays a passive notification banner if one is available. Key design decisions: - Fire-and-forget: never blocks or delays shell startup - 24-hour cache: avoids repeated network calls - 3-second fetch timeout via AbortController - Opt-out via SQUAD_NO_UPDATE_CHECK=1 env var - Triple-wrapped error handling: silent on any failure New file: packages/squad-cli/src/cli/self-update.ts Modified: packages/squad-cli/src/cli-entry.ts (wired into no-args shell path) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/squad-cli/src/cli-entry.ts | 2 + packages/squad-cli/src/cli/self-update.ts | 122 ++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 packages/squad-cli/src/cli/self-update.ts diff --git a/packages/squad-cli/src/cli-entry.ts b/packages/squad-cli/src/cli-entry.ts index dba88bb0d..347cb8d11 100644 --- a/packages/squad-cli/src/cli-entry.ts +++ b/packages/squad-cli/src/cli-entry.ts @@ -211,6 +211,8 @@ async function main(): Promise { // No args → launch interactive shell; whitespace-only arg → show help if (rawCmd === undefined) { await checkNodeSqlite(); + // Fire-and-forget update check — non-blocking, never delays shell startup + import('./cli/self-update.js').then(m => m.notifyIfUpdateAvailable(VERSION)).catch(() => {}); const { runShell } = await lazyRunShell(); await runShell(); return; diff --git a/packages/squad-cli/src/cli/self-update.ts b/packages/squad-cli/src/cli/self-update.ts new file mode 100644 index 000000000..f99253067 --- /dev/null +++ b/packages/squad-cli/src/cli/self-update.ts @@ -0,0 +1,122 @@ +/** + * Self-update check — Phase 1: background version check with notification. + * + * Non-blocking startup check that queries the npm registry for the latest + * version of @bradygaster/squad-cli and displays a passive banner when + * an update is available. Results are cached for 24 hours. + * + * Disable with: SQUAD_NO_UPDATE_CHECK=1 + * + * @module cli/self-update + */ + +import fs from 'node:fs'; +import path from 'node:path'; +import os from 'node:os'; +import { compareVersions } from './upgrade.js'; +import { BOLD, RESET, DIM, YELLOW } from './core/output.js'; + +const PACKAGE_NAME = '@bradygaster/squad-cli'; +const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}`; +const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours +const FETCH_TIMEOUT_MS = 3000; // 3 seconds + +interface CacheData { + latestVersion: string; + checkedAt: number; +} + +/** Directory for squad CLI cache files. */ +function getCacheDir(): string { + const base = process.env.APPDATA + ?? (process.platform === 'darwin' + ? path.join(os.homedir(), 'Library', 'Application Support') + : path.join(os.homedir(), '.config')); + return path.join(base, 'squad-cli'); +} + +function getCachePath(): string { + return path.join(getCacheDir(), 'update-check.json'); +} + +/** Read cached version check result, if still valid. */ +function readCache(): CacheData | null { + try { + const raw = fs.readFileSync(getCachePath(), 'utf8'); + const data: CacheData = JSON.parse(raw); + if (Date.now() - data.checkedAt < CACHE_TTL_MS) { + return data; + } + } catch { + // Cache missing, corrupt, or expired — ignore + } + return null; +} + +/** Write version check result to cache. */ +function writeCache(data: CacheData): void { + try { + const dir = getCacheDir(); + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync(getCachePath(), JSON.stringify(data), 'utf8'); + } catch { + // Non-critical — silently ignore write failures + } +} + +/** Fetch latest version from npm registry with timeout. */ +async function fetchLatestVersion(): Promise { + try { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); + const res = await fetch(REGISTRY_URL, { + headers: { Accept: 'application/vnd.npm.install-v1+json' }, + signal: controller.signal, + }); + clearTimeout(timer); + if (!res.ok) return null; + const data = await res.json() as { 'dist-tags'?: { latest?: string } }; + return data['dist-tags']?.latest ?? null; + } catch { + // Network failure, timeout, or parse error — silently ignore + return null; + } +} + +/** + * Check for updates and print a banner if a newer version is available. + * + * This function is designed to be fire-and-forget: it never throws, + * never blocks the shell, and silently no-ops on any failure. + * + * @param currentVersion - The currently running CLI version + */ +export async function notifyIfUpdateAvailable(currentVersion: string): Promise { + try { + // Respect opt-out + if (process.env.SQUAD_NO_UPDATE_CHECK === '1') return; + + // Check cache first + const cached = readCache(); + let latest: string; + + if (cached) { + latest = cached.latestVersion; + } else { + const fetched = await fetchLatestVersion(); + if (!fetched) return; + latest = fetched; + writeCache({ latestVersion: latest, checkedAt: Date.now() }); + } + + // Only notify if strictly newer + if (compareVersions(latest, currentVersion) > 0) { + console.log( + `\n${YELLOW}⚡${RESET} ${BOLD}Squad v${latest}${RESET} available ${DIM}(you have v${currentVersion})${RESET}` + + `\n Run: ${BOLD}npm install -g @bradygaster/squad-cli@latest${RESET}\n`, + ); + } + } catch { + // Absolute safety net — never crash the CLI for an update check + } +} From 41efdb13bf0c782c503c0edff739e3e5e92fad16 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Fri, 13 Mar 2026 12:25:05 -0700 Subject: [PATCH 16/20] docs: recommend --yolo in README Quick Start Add prominent recommendation to use copilot --yolo in step 4 of the Quick Start guide. Squad sessions involve many tool calls, so --yolo provides a smoother experience by skipping individual approval prompts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fe8bb2fa3..3087bc790 100644 --- a/README.md +++ b/README.md @@ -49,10 +49,14 @@ gh auth login ### 4. Open Copilot and go +**Recommended:** start Copilot with `--yolo`. + ``` -copilot +copilot --yolo ``` +> **Why use `--yolo`?** Squad makes many tool calls in a typical session. Without it, Copilot will prompt you to approve each one. + **In the GitHub Copilot CLI**, type `/agent` and select **Squad**. **In VS Code**, type `/agents` and select **Squad**. From f287ed0e82447f7467ed034ebf7420a31ecde215 Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Fri, 13 Mar 2026 15:30:38 -0700 Subject: [PATCH 17/20] fix: resolve npm audit vulnerabilities and Aspire Docker test cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump minimatch 9.0.6→9.0.9 (ReDoS: GHSA-7r86-cg39-jmmj, GHSA-23c5-xmqv-rm74) - Bump rollup 4.58.0→4.59.0 (path traversal: GHSA-mw96-cpmx-2vgc) - Add --name to squad aspire Docker command to prevent unnamed orphan containers - Align test and CLI on shared container name (squad-aspire-dashboard) - Add SIGINT/SIGTERM/exit handlers to aspire integration test for cleanup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- package-lock.json | 306 +++++++++++------- packages/squad-cli/src/cli/commands/aspire.ts | 4 + test/aspire-integration.test.ts | 12 +- 3 files changed, 200 insertions(+), 122 deletions(-) diff --git a/package-lock.json b/package-lock.json index acb6242c5..ce8bcb274 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1884,9 +1884,9 @@ "optional": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.58.0.tgz", - "integrity": "sha512-mr0tmS/4FoVk1cnaeN244A/wjvGDNItZKR8hRhnmCzygyRXYtKF5jVDSIILR1U97CTzAYmbgIj/Dukg62ggG5w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", "cpu": [ "arm" ], @@ -1898,9 +1898,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.58.0.tgz", - "integrity": "sha512-+s++dbp+/RTte62mQD9wLSbiMTV+xr/PeRJEc/sFZFSBRlHPNPVaf5FXlzAL77Mr8FtSfQqCN+I598M8U41ccQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", "cpu": [ "arm64" ], @@ -1912,9 +1912,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.58.0.tgz", - "integrity": "sha512-MFWBwTcYs0jZbINQBXHfSrpSQJq3IUOakcKPzfeSznONop14Pxuqa0Kg19GD0rNBMPQI2tFtu3UzapZpH0Uc1Q==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", "cpu": [ "arm64" ], @@ -1926,9 +1926,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.58.0.tgz", - "integrity": "sha512-yiKJY7pj9c9JwzuKYLFaDZw5gma3fI9bkPEIyofvVfsPqjCWPglSHdpdwXpKGvDeYDms3Qal8qGMEHZ1M/4Udg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", "cpu": [ "x64" ], @@ -1940,9 +1940,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.58.0.tgz", - "integrity": "sha512-x97kCoBh5MOevpn/CNK9W1x8BEzO238541BGWBc315uOlN0AD/ifZ1msg+ZQB05Ux+VF6EcYqpiagfLJ8U3LvQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", "cpu": [ "arm64" ], @@ -1954,9 +1954,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.58.0.tgz", - "integrity": "sha512-Aa8jPoZ6IQAG2eIrcXPpjRcMjROMFxCt1UYPZZtCxRV68WkuSigYtQ/7Zwrcr2IvtNJo7T2JfDXyMLxq5L4Jlg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", "cpu": [ "x64" ], @@ -1968,9 +1968,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.58.0.tgz", - "integrity": "sha512-Ob8YgT5kD/lSIYW2Rcngs5kNB/44Q2RzBSPz9brf2WEtcGR7/f/E9HeHn1wYaAwKBni+bdXEwgHvUd0x12lQSA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", "cpu": [ "arm" ], @@ -1982,9 +1982,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.58.0.tgz", - "integrity": "sha512-K+RI5oP1ceqoadvNt1FecL17Qtw/n9BgRSzxif3rTL2QlIu88ccvY+Y9nnHe/cmT5zbH9+bpiJuG1mGHRVwF4Q==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", "cpu": [ "arm" ], @@ -1996,9 +1996,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.58.0.tgz", - "integrity": "sha512-T+17JAsCKUjmbopcKepJjHWHXSjeW7O5PL7lEFaeQmiVyw4kkc5/lyYKzrv6ElWRX/MrEWfPiJWqbTvfIvjM1Q==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", "cpu": [ "arm64" ], @@ -2010,9 +2010,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.58.0.tgz", - "integrity": "sha512-cCePktb9+6R9itIJdeCFF9txPU7pQeEHB5AbHu/MKsfH/k70ZtOeq1k4YAtBv9Z7mmKI5/wOLYjQ+B9QdxR6LA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", "cpu": [ "arm64" ], @@ -2024,9 +2024,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.58.0.tgz", - "integrity": "sha512-iekUaLkfliAsDl4/xSdoCJ1gnnIXvoNz85C8U8+ZxknM5pBStfZjeXgB8lXobDQvvPRCN8FPmmuTtH+z95HTmg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", "cpu": [ "loong64" ], @@ -2038,9 +2038,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.58.0.tgz", - "integrity": "sha512-68ofRgJNl/jYJbxFjCKE7IwhbfxOl1muPN4KbIqAIe32lm22KmU7E8OPvyy68HTNkI2iV/c8y2kSPSm2mW/Q9Q==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", "cpu": [ "loong64" ], @@ -2052,9 +2052,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.58.0.tgz", - "integrity": "sha512-dpz8vT0i+JqUKuSNPCP5SYyIV2Lh0sNL1+FhM7eLC457d5B9/BC3kDPp5BBftMmTNsBarcPcoz5UGSsnCiw4XQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", "cpu": [ "ppc64" ], @@ -2066,9 +2066,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.58.0.tgz", - "integrity": "sha512-4gdkkf9UJ7tafnweBCR/mk4jf3Jfl0cKX9Np80t5i78kjIH0ZdezUv/JDI2VtruE5lunfACqftJ8dIMGN4oHew==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", "cpu": [ "ppc64" ], @@ -2080,9 +2080,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.58.0.tgz", - "integrity": "sha512-YFS4vPnOkDTD/JriUeeZurFYoJhPf9GQQEF/v4lltp3mVcBmnsAdjEWhr2cjUCZzZNzxCG0HZOvJU44UGHSdzw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", "cpu": [ "riscv64" ], @@ -2094,9 +2094,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.58.0.tgz", - "integrity": "sha512-x2xgZlFne+QVNKV8b4wwaCS8pwq3y14zedZ5DqLzjdRITvreBk//4Knbcvm7+lWmms9V9qFp60MtUd0/t/PXPw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", "cpu": [ "riscv64" ], @@ -2108,9 +2108,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.58.0.tgz", - "integrity": "sha512-jIhrujyn4UnWF8S+DHSkAkDEO3hLX0cjzxJZPLF80xFyzyUIYgSMRcYQ3+uqEoyDD2beGq7Dj7edi8OnJcS/hg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", "cpu": [ "s390x" ], @@ -2122,9 +2122,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.58.0.tgz", - "integrity": "sha512-+410Srdoh78MKSJxTQ+hZ/Mx+ajd6RjjPwBPNd0R3J9FtL6ZA0GqiiyNjCO9In0IzZkCNrpGymSfn+kgyPQocg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", "cpu": [ "x64" ], @@ -2136,9 +2136,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.58.0.tgz", - "integrity": "sha512-ZjMyby5SICi227y1MTR3VYBpFTdZs823Rs/hpakufleBoufoOIB6jtm9FEoxn/cgO7l6PM2rCEl5Kre5vX0QrQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", "cpu": [ "x64" ], @@ -2150,9 +2150,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.58.0.tgz", - "integrity": "sha512-ds4iwfYkSQ0k1nb8LTcyXw//ToHOnNTJtceySpL3fa7tc/AsE+UpUFphW126A6fKBGJD5dhRvg8zw1rvoGFxmw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", "cpu": [ "x64" ], @@ -2164,9 +2164,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.58.0.tgz", - "integrity": "sha512-fd/zpJniln4ICdPkjWFhZYeY/bpnaN9pGa6ko+5WD38I0tTqk9lXMgXZg09MNdhpARngmxiCg0B0XUamNw/5BQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", "cpu": [ "arm64" ], @@ -2178,9 +2178,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.58.0.tgz", - "integrity": "sha512-YpG8dUOip7DCz3nr/JUfPbIUo+2d/dy++5bFzgi4ugOGBIox+qMbbqt/JoORwvI/C9Kn2tz6+Bieoqd5+B1CjA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", "cpu": [ "arm64" ], @@ -2192,9 +2192,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.58.0.tgz", - "integrity": "sha512-b9DI8jpFQVh4hIXFr0/+N/TzLdpBIoPzjt0Rt4xJbW3mzguV3mduR9cNgiuFcuL/TeORejJhCWiAXe3E/6PxWA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", "cpu": [ "ia32" ], @@ -2206,9 +2206,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.58.0.tgz", - "integrity": "sha512-CSrVpmoRJFN06LL9xhkitkwUcTZtIotYAF5p6XOR2zW0Zz5mzb3IPpcoPhB02frzMHFNo1reQ9xSF5fFm3hUsQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", "cpu": [ "x64" ], @@ -2220,9 +2220,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.58.0.tgz", - "integrity": "sha512-QFsBgQNTnh5K0t/sBsjJLq24YVqEIVkGpfN2VHsnN90soZyhaiA9UUHufcctVNL4ypJY0wrwad0wslx2KJQ1/w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", "cpu": [ "x64" ], @@ -2542,26 +2542,20 @@ } }, "node_modules/balanced-match": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", - "integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } + "license": "MIT" }, "node_modules/brace-expansion": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", - "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" + "balanced-match": "^1.0.0" } }, "node_modules/cac": { @@ -3500,13 +3494,13 @@ } }, "node_modules/minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -3858,9 +3852,9 @@ } }, "node_modules/rollup": { - "version": "4.58.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.58.0.tgz", - "integrity": "sha512-wbT0mBmWbIvvq8NeEYWWvevvxnOyhKChir47S66WCxw1SXqhw7ssIYejnQEVt7XYQpsj2y8F9PM+Cr3SNEa0gw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "dev": true, "license": "MIT", "dependencies": { @@ -3874,31 +3868,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.58.0", - "@rollup/rollup-android-arm64": "4.58.0", - "@rollup/rollup-darwin-arm64": "4.58.0", - "@rollup/rollup-darwin-x64": "4.58.0", - "@rollup/rollup-freebsd-arm64": "4.58.0", - "@rollup/rollup-freebsd-x64": "4.58.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.58.0", - "@rollup/rollup-linux-arm-musleabihf": "4.58.0", - "@rollup/rollup-linux-arm64-gnu": "4.58.0", - "@rollup/rollup-linux-arm64-musl": "4.58.0", - "@rollup/rollup-linux-loong64-gnu": "4.58.0", - "@rollup/rollup-linux-loong64-musl": "4.58.0", - "@rollup/rollup-linux-ppc64-gnu": "4.58.0", - "@rollup/rollup-linux-ppc64-musl": "4.58.0", - "@rollup/rollup-linux-riscv64-gnu": "4.58.0", - "@rollup/rollup-linux-riscv64-musl": "4.58.0", - "@rollup/rollup-linux-s390x-gnu": "4.58.0", - "@rollup/rollup-linux-x64-gnu": "4.58.0", - "@rollup/rollup-linux-x64-musl": "4.58.0", - "@rollup/rollup-openbsd-x64": "4.58.0", - "@rollup/rollup-openharmony-arm64": "4.58.0", - "@rollup/rollup-win32-arm64-msvc": "4.58.0", - "@rollup/rollup-win32-ia32-msvc": "4.58.0", - "@rollup/rollup-win32-x64-gnu": "4.58.0", - "@rollup/rollup-win32-x64-msvc": "4.58.0", + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" } }, @@ -5301,6 +5295,76 @@ "node": ">=20" } }, + "packages/squad-cli/node_modules/@bradygaster/squad-sdk": { + "version": "0.8.25", + "resolved": "https://registry.npmjs.org/@bradygaster/squad-sdk/-/squad-sdk-0.8.25.tgz", + "integrity": "sha512-9gQ1vatbG+uQCTxx3mVDPRNop6LWVRpE42i+nenzB+LhHOMaANYiANN/kDG8MmPFHIdXCsmnZKdeGblr6DPwvA==", + "license": "MIT", + "dependencies": { + "@github/copilot-sdk": "^0.1.32", + "vscode-jsonrpc": "^8.2.1" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "^0.57.2", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.57.2", + "@opentelemetry/resources": "^1.30.0", + "@opentelemetry/sdk-metrics": "^1.30.0", + "@opentelemetry/sdk-node": "^0.57.2", + "@opentelemetry/sdk-trace-base": "^1.30.0", + "@opentelemetry/sdk-trace-node": "^1.30.0", + "@opentelemetry/semantic-conventions": "^1.28.0", + "ws": "^8.18.0" + } + }, + "packages/squad-cli/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "packages/squad-cli/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "packages/squad-cli/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=14" + } + }, "packages/squad-sdk": { "name": "@bradygaster/squad-sdk", "version": "0.8.25", diff --git a/packages/squad-cli/src/cli/commands/aspire.ts b/packages/squad-cli/src/cli/commands/aspire.ts index 5042ac798..2b1293536 100644 --- a/packages/squad-cli/src/cli/commands/aspire.ts +++ b/packages/squad-cli/src/cli/commands/aspire.ts @@ -19,6 +19,9 @@ const ASPIRE_OTLP_ENDPOINT = 'http://localhost:4317'; /** Default Aspire dashboard UI port. */ const ASPIRE_DASHBOARD_PORT = 18888; +/** Docker container name for the Aspire dashboard. */ +const ASPIRE_CONTAINER_NAME = 'squad-aspire-dashboard'; + /** Docker image for the Aspire dashboard. */ const ASPIRE_DOCKER_IMAGE = 'mcr.microsoft.com/dotnet/aspire-dashboard:latest'; @@ -64,6 +67,7 @@ function launchWithDocker(): ChildProcess { console.log(`${DIM}Starting Aspire dashboard via Docker...${RESET}`); const child = spawn('docker', [ 'run', '--rm', + '--name', ASPIRE_CONTAINER_NAME, '-p', `${ASPIRE_DASHBOARD_PORT}:18888`, '-p', '4317:18889', '-e', 'DASHBOARD__FRONTEND__AUTHMODE=Unsecured', diff --git a/test/aspire-integration.test.ts b/test/aspire-integration.test.ts index 37ceb5ce4..d54d5a99e 100644 --- a/test/aspire-integration.test.ts +++ b/test/aspire-integration.test.ts @@ -38,7 +38,7 @@ const SKIP_REASON = process.env['SKIP_DOCKER_TESTS'] === '1' ? 'Docker not available' : null; -const CONTAINER_NAME = 'aspire-dashboard-test'; +const CONTAINER_NAME = 'squad-aspire-dashboard'; const DASHBOARD_URL = 'http://localhost:18888'; const OTLP_GRPC_TARGET = 'http://localhost:4317'; @@ -70,6 +70,16 @@ function removeContainer(): void { } } +// Best-effort cleanup on unexpected exit (Ctrl+C, uncaught exception, etc.) +// This prevents orphaned containers when the test runner is interrupted. +for (const signal of ['SIGINT', 'SIGTERM'] as const) { + process.once(signal, () => { + removeContainer(); + process.exit(128 + (signal === 'SIGINT' ? 2 : 15)); + }); +} +process.once('exit', () => removeContainer()); + // ============================================================================ // OTel setup — NodeSDK with gRPC exporters targeting the Aspire dashboard // ============================================================================ From 1da2dac676d7bed10118a77bc7a2254cfb4abebc Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Fri, 13 Mar 2026 15:46:44 -0700 Subject: [PATCH 18/20] fix: vitest resolve alias for workspace package mocking Resolve workspace packages to source in vitest so vi.mock intercepts correctly. Without this, npm ci installs a duplicate squad-sdk under squad-cli/node_modules which bypasses the mock. --- vitest.config.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/vitest.config.ts b/vitest.config.ts index 45635c592..0611b925e 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,6 +1,13 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ + resolve: { + // Force vitest to resolve @bradygaster/squad-sdk from the workspace root, + // not from a duplicate copy under packages/squad-cli/node_modules/. + // Without this, vi.mock('@bradygaster/squad-sdk') targets the root copy + // but the code under test imports from the duplicate — bypassing the mock. + dedupe: ['@bradygaster/squad-sdk'], + }, test: { include: ['test/**/*.test.ts'], coverage: { From e28ad4ac04210ea0c7908a41c57338735a5703a8 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 07:59:51 -0700 Subject: [PATCH 19/20] =?UTF-8?q?docs(ai-team):=20Scribe=20log=20=E2=80=94?= =?UTF-8?q?=20PAO=20orchestration,=20Copilot-only=20decision=20merged?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Session: 2026-03-14T14-40-47Z-copilot-requirement-docs Requested by: Copilot (Dina Berry) Changes: - Created orchestration log for PAO spawn (Issue #323 resolution) - Created session log documenting Copilot requirement clarification - Merged inbox decision (Copilot-only backend, BYOK not supported) - Deleted processed inbox file - Propagated team update to Flight history --- .squad/agents/flight/history.md | 2 ++ .squad/decisions.md | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/.squad/agents/flight/history.md b/.squad/agents/flight/history.md index b0125983f..87286fd4e 100644 --- a/.squad/agents/flight/history.md +++ b/.squad/agents/flight/history.md @@ -22,4 +22,6 @@ Approved PR #331 ("docs: scenario and feature guides from blog analysis") for me ### Content Triage Skill Codified (2026-03-10) Created `.squad/skills/content-triage/SKILL.md` to codify the boundary heuristic from PR #331. Defines repeatable workflow for triaging external content (blog posts, sample repos, videos, talks) to determine what belongs in Squad's public docs vs IRL tracking. Key components: (1) "Squad Ships It" litmus test — if Squad doesn't ship the code/config, it's IRL content; (2) triage workflow triggered by `content-triage` label or external content reference in issue body; (3) output format with boundary analysis, sub-issues for PAO (doc extraction), and IRL reference entry for Scribe; (4) label convention (`content:blog`, `content:sample`, `content:video`, `content:talk`); (5) Ralph integration for routing to Flight, creating sub-issues, and notifying Scribe. Examples include Tamir blog analysis (PR #331), sample repo with ops patterns, and conference talk. Pattern prevents infrastructure docs from polluting Squad's public docs while ensuring community content accelerates adoption through proper extraction and referencing. +📌 **Team update (2026-03-14T14:40:47Z):** LLM backend strategy clarified — GitHub Copilot is the only supported backend; BYOK not supported. Product decision embedded in team memory (decisions.md). Documentation updates shipped in PR #385 (README, installation, model selection, FAQ). Closes issue #323. Architecture simplified; no multi-backend abstraction layer planned. + 📌 **Team update (2026-03-11T01:27:57Z):** Content triage skill finalized; "Squad Ships It" boundary heuristic codified into shared team decision (decisions.md). Remote Squad access phased rollout approved (Discussions bot → Copilot Extension → Chat bot). PR #331 boundary review pattern established as standard for all doc PRs. Triage workflow enables Flight to scale as community content accelerates. diff --git a/.squad/decisions.md b/.squad/decisions.md index 3e2d3c74e..748527577 100644 --- a/.squad/decisions.md +++ b/.squad/decisions.md @@ -395,4 +395,17 @@ Definitive CLI completeness audit confirms all commands work post-publish. --- +### 2026-03-14T14:40:47Z: LLM Backend — Copilot-only, BYOK not supported +**By:** Copilot (via Copilot CLI) +**Date:** 2026-03-14 +**Status:** Decision — Product Constraint + +**What:** GitHub Copilot is the **only supported LLM backend**. BYOK (Bring Your Own Key) is **NOT a supported feature**. The `SquadProviderConfig` interface in `packages/squad-sdk/src/adapter/types.ts` should be marked as `@internal`. Docs must clearly state that Squad requires GitHub Copilot. + +**Why:** Product clarification on AI backend strategy. Squad's vision couples tightly with Copilot's capabilities. No multi-backend abstraction layer planned. Simplifies architecture, improves integration depth, focuses team effort. + +**Closure:** Issue #323 — answered by PR #385 with documentation clarification across README, installation guides, model selection docs, and FAQ. + +--- + *Fresh start — Mission Control rebirth, 2026-03-08. Previous decisions archived.* From c454c53bbb24c007852a032111fec8c1fbf0a925 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 07:57:50 -0700 Subject: [PATCH 20/20] docs: clarify GitHub Copilot requirement and BYOK status Closes #323 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/agents/pao/history.md | 7 +++++ .../decisions/inbox/pao-copilot-only-docs.md | 19 ++++++++++++++ README.md | 21 ++++++++++++++- .../content/docs/concepts/parallel-work.md | 4 +-- docs/src/content/docs/cookbook/recipes.md | 2 +- .../content/docs/features/model-selection.md | 26 +++++++++---------- .../content/docs/get-started/first-session.md | 4 ++- .../content/docs/get-started/installation.md | 13 ++++++++-- .../src/content/docs/get-started/migration.md | 1 + docs/src/content/docs/reference/config.md | 20 +++++++------- .../docs/scenarios/switching-models.md | 26 +++++++++---------- packages/squad-sdk/src/adapter/types.ts | 9 ++++--- 12 files changed, 106 insertions(+), 46 deletions(-) create mode 100644 .squad/decisions/inbox/pao-copilot-only-docs.md diff --git a/.squad/agents/pao/history.md b/.squad/agents/pao/history.md index 45a49e5a6..836e00771 100644 --- a/.squad/agents/pao/history.md +++ b/.squad/agents/pao/history.md @@ -19,6 +19,13 @@ Release blog posts use YAML frontmatter with: title, date, author, wave, tags, s ### Roster & Contributor Recognition (v0.8.25) Squad moved to Apollo 13/NASA Mission Control naming scheme (Flight, Procedures, EECOM, FIDO, PAO, CAPCOM, CONTROL, Surgeon, Booster, GNC, Network, RETRO, INCO, GUIDO, Telemetry, VOX, DSKY, Sims, Handbook). CONTRIBUTORS.md tracks both team roster and community contributors; contributor table entries grow with PRs (append PR counts rather than replace, maintaining attribution history). +### Scannability Framework (v0.8.25) +Format selection is a scannability decision, not style preference. Paragraphs for narrative/concepts (3-4 sentences max). Bullets for scannable items (features, options, non-sequential steps). Tables for comparisons or structured reference data (config, API params). Quotes/indents for callouts/warnings. Decision test: if reader hunts for one item in a paragraph, convert to bullets/table. This framework is now a hard rule in charter under SCANNABILITY REVIEW. + +### Copilot-only platform messaging (issue #323) +Clarified across README and docs that GitHub Copilot is Squad's only supported model access layer. Added a platform requirements section, an explicit FAQ answer, and getting-started guidance so users do not infer BYOK or direct provider access. + +Updated model selection docs to describe GitHub Copilot as the broker for available models, then marked `SquadProviderConfig` and the `provider` field as internal in SDK types so public docs do not present BYOK as a supported feature. Also refreshed related model-selection references in config, cookbook, and parallel-work docs for consistent messaging. ### Git Rebase for Doc Merges When rebasing doc PRs with conflicts from other merged doc PRs, the main branch version (already merged) should generally take priority. For Node.js version references, maintain LTS terminology when present (e.g., `nvm install --lts` over specific version numbers like `nvm install 20`). Conflict resolution pattern: preserve new content from PR branch only where it doesn't duplicate or contradict already-merged changes. Use `git -c core.editor=true rebase --continue` to bypass interactive editor issues on Windows. diff --git a/.squad/decisions/inbox/pao-copilot-only-docs.md b/.squad/decisions/inbox/pao-copilot-only-docs.md new file mode 100644 index 000000000..4a674dec3 --- /dev/null +++ b/.squad/decisions/inbox/pao-copilot-only-docs.md @@ -0,0 +1,19 @@ +# Copilot-only docs clarification + +**By:** PAO +**Date:** 2026-03-14 + +## What + +- Added a Platform requirements section and a GitHub Copilot FAQ answer in `README.md`. +- Updated installation, first-session, and migration docs to say Squad routes all LLM requests through GitHub Copilot. +- Updated model-selection-related docs to describe GitHub Copilot as the broker for model access and to remove BYOK-facing wording. +- Marked `SquadProviderConfig` and the `provider` field as internal in `packages/squad-sdk/src/adapter/types.ts`. + +## Why + +GitHub Copilot is the only supported LLM backend for Squad. User-facing docs must not imply direct provider API access or BYOK support, and internal SDK types should not look like supported public configuration. + +## Impact + +Users now see a consistent Copilot-only story across setup and model-selection docs. The SDK type surface is also less likely to leak experimental provider override details into generated documentation. diff --git a/README.md b/README.md index 3087bc790..bb195b2cc 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,17 @@ It's not a chatbot wearing hats. Each team member runs in its own context, reads --- +## Platform requirements + +Squad uses GitHub Copilot as its model access layer. Every LLM request goes through Copilot. + +- **GitHub Copilot is required.** Squad runs inside GitHub Copilot CLI and VS Code. +- **GitHub Copilot is the only supported backend.** Squad does not support bring your own key (BYOK) or direct provider API access. +- **Model availability comes from Copilot.** The models you can select depend on what GitHub Copilot exposes for your plan. +- **`gh` is still required for GitHub workflows.** Issues, pull requests, project boards, and Ralph still rely on GitHub CLI authentication. + +--- + ## Quick Start ### 1. Create your project @@ -73,6 +84,14 @@ Squad proposes a team — each member named from a persistent thematic cast. You --- +## Frequently asked questions + +### Can I use Squad without GitHub Copilot? + +No. GitHub Copilot is the only supported LLM backend for Squad. Squad routes every model request through Copilot, and bring your own key is not a supported feature. + +--- + ## All Commands (15 commands) | Command | What it does | @@ -687,7 +706,7 @@ npm run lint # Type check (tsc --noEmit) - **Alpha** — API and file formats may change between versions - **Node 20+** — requires Node.js 20.0.0 or later -- **GitHub Copilot CLI & VS Code** — Squad works on both CLI and VS Code +- **GitHub Copilot required** — Squad works through GitHub Copilot CLI and VS Code - **`gh` CLI required** — GitHub Issues, PRs, Ralph, and Project Boards all need `gh auth login` - **Knowledge grows with use** — the first session is the least capable; agents improve as they accumulate history - **npm distribution only** — Install via `npm install -g @bradygaster/squad-cli` or `npx @bradygaster/squad-cli`. GitHub-native distribution (`npx github:`) is no longer supported. diff --git a/docs/src/content/docs/concepts/parallel-work.md b/docs/src/content/docs/concepts/parallel-work.md index 6aa64103c..7ab4f7015 100644 --- a/docs/src/content/docs/concepts/parallel-work.md +++ b/docs/src/content/docs/concepts/parallel-work.md @@ -172,9 +172,9 @@ First match wins: | Git / Release | `claude-haiku-4.5` | Changelogs, tags, version bumps | | Designer / Visual | `claude-opus-4.5` | Vision capability required | -### Model Catalog (16 models) +### Model catalog -Squad supports models across three tiers: +GitHub Copilot exposes the model tiers that Squad can use: - **Premium:** claude-opus-4.6, claude-opus-4.6-fast, claude-opus-4.5 - **Standard:** claude-sonnet-4.5, gpt-5.2-codex, claude-sonnet-4, gpt-5.2, gpt-5.1-codex, gpt-5.1, gpt-5, gemini-3-pro-preview diff --git a/docs/src/content/docs/cookbook/recipes.md b/docs/src/content/docs/cookbook/recipes.md index d1f4f13ab..e166003ab 100644 --- a/docs/src/content/docs/cookbook/recipes.md +++ b/docs/src/content/docs/cookbook/recipes.md @@ -90,7 +90,7 @@ Squad runs entirely in your Copilot session. Nothing leaves your machine beyond > "Switch everyone to Haiku — I'm trying to save costs this sprint" -Squad supports 17 models across three tiers. Budget mode: `claude-haiku-4.5` for everything. Quality mode: `claude-opus-4.6` for the Lead, `claude-sonnet-4.5` for everyone else. Fallback chains handle unavailability automatically. +GitHub Copilot exposes the model tiers that Squad can use. Budget mode: `claude-haiku-4.5` for everything. Quality mode: `claude-opus-4.6` for the Lead, `claude-sonnet-4.5` for everyone else. Fallback chains handle unavailability automatically. ### Client Compatibility diff --git a/docs/src/content/docs/features/model-selection.md b/docs/src/content/docs/features/model-selection.md index 33bd81c70..a56c6b949 100644 --- a/docs/src/content/docs/features/model-selection.md +++ b/docs/src/content/docs/features/model-selection.md @@ -8,9 +8,9 @@ Have all agents use Opus for the rest of this session ``` -**Try this to optimize costs:** +**Try this to preserve premium requests:** ``` -Switch to Haiku — I'm trying to save costs +Switch to Haiku — I want to save premium requests ``` **Try this to balance quality and budget:** @@ -18,19 +18,17 @@ Switch to Haiku — I'm trying to save costs Use Sonnet for code, Haiku for everything else ``` -Squad adjusts model selection based on your directive. Agents writing code get quality models (Sonnet/Opus), agents doing docs/logs get cost-optimized models (Haiku). You can override anytime. +Squad adjusts model selection based on your directive. GitHub Copilot remains the broker: Squad asks Copilot for a model, and Copilot exposes whichever models your plan makes available. Squad does not connect directly to provider APIs, and bring your own key is not a supported feature. --- -## How It Works +## How it works -Squad routes each agent to the right model based on what they're doing — not a one-size-fits-all default. The governing principle: **cost first, unless code is being written.** - -## How It Works +Squad routes each agent to the right model based on what they're doing — not a one-size-fits-all default. The governing principle: **use the lightest GitHub Copilot-backed model that fits the job.** Model selection uses a layered system. First match wins: -1. **User Override** — You said "use opus" or "save costs"? Done. Session-wide directives persist until contradicted. +1. **User Override** — You said "use opus" or "save premium requests"? Done. Session-wide directives persist until contradicted. 2. **Charter Preference** — The agent's charter specifies a `## Model` section with a preferred model. 3. **Task-Aware Auto-Selection** — The coordinator checks what the agent is actually doing: @@ -56,13 +54,13 @@ Model selection uses a layered system. First match wins: | Git / Release | `claude-haiku-4.5` | Changelogs, tags, version bumps | | Designer / Visual | `claude-opus-4.5` | Vision capability required | -## 16-Model Catalog +## Model catalog -Squad supports 16 models across three tiers: +GitHub Copilot can expose these models to Squad, depending on your subscription tier and current rollout status: - **Premium:** claude-opus-4.6, claude-opus-4.6-fast, claude-opus-4.5 -- **Standard:** claude-sonnet-4.5, gpt-5.2-codex, claude-sonnet-4, gpt-5.2, gpt-5.1-codex, gpt-5.1, gpt-5, gemini-3-pro-preview -- **Fast/Cheap:** claude-haiku-4.5, gpt-5.1-codex-mini, gpt-4.1, gpt-5-mini, gpt-5.1-codex-mini +- **Standard:** claude-sonnet-4.5, claude-sonnet-4, gemini-3-pro-preview, gpt-5.3-codex, gpt-5.2-codex, gpt-5.2, gpt-5.1-codex-max, gpt-5.1-codex, gpt-5.1, gpt-5 +- **Fast/Cheap:** claude-haiku-4.5, gpt-5.1-codex-mini, gpt-5-mini, gpt-4.1 ## Fallback Chains @@ -93,10 +91,10 @@ use opus for this architecture work Override to premium model for a single high-stakes task. ``` -always use haiku to save costs +always use haiku to save premium requests ``` -Set session-wide preference for the cheapest model tier. +Set a session-wide preference for the lightest model tier. ``` what model did Kane use for that last task? diff --git a/docs/src/content/docs/get-started/first-session.md b/docs/src/content/docs/get-started/first-session.md index 6c62bbebc..47173cccf 100644 --- a/docs/src/content/docs/get-started/first-session.md +++ b/docs/src/content/docs/get-started/first-session.md @@ -48,7 +48,9 @@ Start the Copilot CLI: copilot ``` -Select **Squad** from the `/agent` list (CLI) or `/agents` (VS Code). Squad greets you by name: +Squad routes all model access through GitHub Copilot, so this step is required even if you installed the CLI or SDK separately. + +Select **Squad** from the `/agent` list (CLI) or `/agents` (VS Code). Squad greets you by name (pulled from `git config user.name`): ``` Hey Brady, what are you building? diff --git a/docs/src/content/docs/get-started/installation.md b/docs/src/content/docs/get-started/installation.md index 9b1e35e11..727f7f4a2 100644 --- a/docs/src/content/docs/get-started/installation.md +++ b/docs/src/content/docs/get-started/installation.md @@ -5,6 +5,15 @@ Three ways to get Squad running. Pick the one that fits. +## Platform requirements + +Squad routes all LLM requests through GitHub Copilot. The CLI, VS Code, and SDK all use Copilot as the model access layer. + +- GitHub Copilot is required. +- GitHub Copilot is the only supported backend. +- Bring your own key is not a supported feature. +- Model availability depends on what GitHub Copilot exposes for your plan. + --- ## Try this: @@ -68,7 +77,7 @@ Pick based on what you're doing: | Try Squad quickly | **CLI** with `npx` | No install needed. Run `npx @bradygaster/squad-cli init` and you're testing it. | | Use Squad across all projects | **CLI** with `--global` | One install. Works everywhere. Run `squad` from any terminal. | | Work inside VS Code | **VS Code** (just open your project) | Already using Copilot? Squad just works. Same `.squad/` directory as CLI. | -| Build tools on top of Squad | **SDK** | Typed APIs, routing config, agent lifecycle hooks. Programmatic access to everything. | +| Build tools on top of Squad | **SDK** | Typed APIs, routing config, agent lifecycle hooks. Runtime model access still goes through GitHub Copilot. | Can't decide? → Start with **CLI**. You can always add VS Code or the SDK later. Your `.squad/` directory works identically everywhere. @@ -96,7 +105,7 @@ Then import what you need: import { defineConfig, loadConfig, resolveSquad } from '@bradygaster/squad-sdk'; ``` -The SDK gives you typed configuration, routing, model selection, and the full agent lifecycle API. See the [SDK Reference](../reference/sdk.md) for details. +The SDK gives you typed configuration, routing, model selection, and the full agent lifecycle API. Runtime model access still goes through GitHub Copilot. See the [SDK Reference](../reference/sdk.md) for details. --- diff --git a/docs/src/content/docs/get-started/migration.md b/docs/src/content/docs/get-started/migration.md index d09459217..973e89b56 100644 --- a/docs/src/content/docs/get-started/migration.md +++ b/docs/src/content/docs/get-started/migration.md @@ -40,6 +40,7 @@ Never used Squad before? Start here. - Node.js 18 or later - npm 9 or later - A GitHub account with [GitHub Copilot](https://github.com/features/copilot) enabled +- GitHub Copilot is required because Squad routes all LLM requests through Copilot. Bring your own key is not supported. - `gh` CLI authenticated (`gh auth status` should show you logged in) ### Install diff --git a/docs/src/content/docs/reference/config.md b/docs/src/content/docs/reference/config.md index 0b6ffc1ea..7b4c22159 100644 --- a/docs/src/content/docs/reference/config.md +++ b/docs/src/content/docs/reference/config.md @@ -116,22 +116,24 @@ routing: { --- -## Model Configuration +## Model configuration -17 models across three tiers. Squad picks the right one, or you override: +GitHub Copilot exposes the models that Squad can request. Squad picks the right one, or you override it: -| Tier | Models | Use Case | -|------|--------|----------| -| **premium** | claude-opus-4, gpt-4.1 | Architecture, code review | -| **standard** | claude-sonnet-4, gpt-4.1 | Most work | -| **fast** | claude-haiku-3.5, gpt-4.1-mini | Triage, logging, quick tasks | +| Tier | Typical models | Use case | +|------|----------------|----------| +| **premium** | `claude-opus-*` | Architecture, code review | +| **standard** | `claude-sonnet-*`, `gpt-5.*`, `gemini-3-pro-preview` | Most work | +| **fast** | `claude-haiku-*`, `gpt-4.1`, `gpt-5-mini` | Triage, logging, quick tasks | + +Bring your own key is not a supported feature. Model availability comes from your GitHub Copilot plan. Per-agent overrides in `model-config.json`: ```json { - "neo": "claude-opus-4", - "tank": "claude-haiku-3.5" + "neo": "claude-opus-4.6", + "tank": "claude-haiku-4.5" } ``` diff --git a/docs/src/content/docs/scenarios/switching-models.md b/docs/src/content/docs/scenarios/switching-models.md index 9e6124b4d..7f1afabc5 100644 --- a/docs/src/content/docs/scenarios/switching-models.md +++ b/docs/src/content/docs/scenarios/switching-models.md @@ -1,8 +1,8 @@ # Switching AI Models — Budget vs Quality -**Try this to reduce costs temporarily:** +**Try this to reduce premium request usage temporarily:** ``` -Switch everyone to Haiku — I'm trying to save costs this sprint +Switch everyone to Haiku — I want to save premium requests this sprint ``` **Try this to maximize quality:** @@ -15,21 +15,21 @@ Use Opus for all agents — quality over cost for this session Reset to default model strategy ``` -Squad supports 16 models across three tiers. Switch the entire team, set per-agent preferences, or let Squad auto-route based on task type. +GitHub Copilot brokers the models that Squad can use. Switch the entire team, set per-agent preferences, or let Squad auto-route based on task type. --- -## 1. The Default: Cost-First Strategy +## 1. The default: cost-first strategy -Model selection strategies for different needs. Squad supports 16 models with fallback chains. +Model selection strategies for different needs. Squad uses the models that GitHub Copilot exposes to your plan, with fallback chains when a model is unavailable. -Squad's default model configuration prioritizes cost-effectiveness: +Squad's default model configuration tries to preserve premium requests while keeping code quality high: - **Non-code tasks** (planning, triage, decisions) → `claude-haiku-4.5` (fast, cheap) - **Code tasks** (implementation, refactoring) → `claude-sonnet-4.5` (balanced quality/cost) - **Code review** (critical analysis) → `claude-sonnet-4.5` -This is optimized for **everyday use** — good quality without burning your API budget. +This is optimized for **everyday use** — good quality without burning through premium requests. --- @@ -49,7 +49,7 @@ All agents now use claude-haiku-4.5 (fast/cheap tier) Agents will be faster but may need more guidance on complex tasks. ``` -This is written to `.ai-team/model-config.json`: +This is written to `.squad/model-config.json`: ```json { @@ -80,7 +80,7 @@ All other agents → claude-sonnet-4.5 (standard tier) Neo will give higher-quality code reviews and architectural guidance. ``` -This is written to `.ai-team/model-config.json`: +This is written to `.squad/model-config.json`: ```json { @@ -120,9 +120,9 @@ Tank doesn't need Opus to write tests. Neo does need it for code reviews. Balanc --- -## 5. The 16-Model Catalog +## 5. Model catalog -Squad supports these models (as of v0.2.0): +GitHub Copilot can expose these models to Squad, depending on your subscription tier and rollout status: **Premium tier** (highest quality, highest cost): - `claude-opus-4.6` — latest Opus, best reasoning @@ -147,7 +147,7 @@ Squad supports these models (as of v0.2.0): - `gpt-5-mini` - `gpt-4.1` -**Note:** Model availability depends on your GitHub Copilot subscription tier. +**Note:** GitHub Copilot remains the broker here. Squad does not connect directly to Anthropic, OpenAI, Azure OpenAI, or Gemini APIs, and bring your own key is not supported. --- @@ -230,4 +230,4 @@ You don't have to configure this — it's automatic. - **Use Opus for the Lead.** Code reviews benefit most from premium reasoning. Opus catches edge cases Sonnet misses. - **Haiku is underrated for tests.** Test writing doesn't require deep reasoning — Haiku is fast and accurate enough. - **Per-agent overrides are cheap.** Put Opus on the Lead, Haiku on the Tester, Sonnet on everyone else. Balanced budget. -- **Model config is in `.ai-team/model-config.json`.** Commit it so your team uses the same models. +- **Model config is in `.squad/model-config.json`.** Commit it so your team uses the same models. diff --git a/packages/squad-sdk/src/adapter/types.ts b/packages/squad-sdk/src/adapter/types.ts index 8dc73dade..e5458b61e 100644 --- a/packages/squad-sdk/src/adapter/types.ts +++ b/packages/squad-sdk/src/adapter/types.ts @@ -73,8 +73,9 @@ export interface SquadSessionConfig { excludedTools?: string[]; /** - * Custom provider configuration (BYOK - Bring Your Own Key). - * When specified, uses the provided API endpoint instead of Copilot API. + * Experimental internal provider override. + * GitHub Copilot remains the only supported public backend. + * @internal */ provider?: SquadProviderConfig; @@ -650,7 +651,9 @@ export type SquadUserInputHandler = ( // ============================================================================ /** - * Configuration for a custom API provider (BYOK - Bring Your Own Key). + * Internal configuration for an experimental provider override path. + * GitHub Copilot remains the only supported public backend. + * @internal */ export interface SquadProviderConfig { /**