@yemi33/squad 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 yemi33
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 yemi33
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -172,7 +172,7 @@ The web dashboard at `http://localhost:7331` provides:
172
172
 
173
173
  ## Project Config
174
174
 
175
- When you run `squad.js add <dir>`, it prompts for project details and saves them to `config.json`. Each project entry looks like:
175
+ When you run `squad add <dir>`, it prompts for project details and saves them to `config.json`. Each project entry looks like:
176
176
 
177
177
  ```json
178
178
  {
@@ -205,7 +205,7 @@ The init script also creates `<project>/.squad/` with empty `work-items.json` an
205
205
 
206
206
  ### Auto-Discovery
207
207
 
208
- When you run `squad.js add`, the tool automatically detects what it can from the repo:
208
+ When you run `squad add`, the tool automatically detects what it can from the repo:
209
209
 
210
210
  | What | How |
211
211
  |------|-----|
@@ -227,7 +227,7 @@ Agents need MCP tools to interact with your repo host (create PRs, post review c
227
227
 
228
228
  **Example:** If you use Azure DevOps, configure the `azure-ado` MCP server in your Claude Code settings. If you use GitHub, configure the `github` MCP server. Agents will discover and use whichever tools are available.
229
229
 
230
- Manually refresh with `node engine.js mcp-sync`.
230
+ Manually refresh with `squad mcp-sync`.
231
231
 
232
232
  ## Work Items
233
233
 
@@ -235,7 +235,7 @@ All work items use the shared `playbooks/work-item.md` template, which provides
235
235
 
236
236
  **Per-project** — scoped to one repo. Select a project in the Command Center dropdown.
237
237
 
238
- **Central (auto-route)** — agent gets all project descriptions and decides where to work. Use "Auto (agent decides)" in the dropdown, or `node engine.js work "title"`. Can span multiple repos.
238
+ **Central (auto-route)** — agent gets all project descriptions and decides where to work. Use "Auto (agent decides)" in the dropdown, or `squad work "title"`. Can span multiple repos.
239
239
 
240
240
  ### Fan-Out (Parallel Multi-Agent)
241
241
 
@@ -328,9 +328,10 @@ Routing rules in `routing.md`. Charters in `agents/{name}/charter.md`. Both are
328
328
  | `implement.md` | Build a PRD item in a git worktree, create PR |
329
329
  | `review.md` | Review a PR, post findings to repo host |
330
330
  | `fix.md` | Fix review feedback on existing PR branch |
331
- | `analyze.md` | Generate PRD gap analysis in a worktree |
332
331
  | `explore.md` | Read-only codebase exploration |
333
332
  | `test.md` | Run tests and report results |
333
+ | `build-and-test.md` | Build project and run test suite |
334
+ | `plan-to-prd.md` | Convert a plan into PRD gap items |
334
335
 
335
336
  All playbooks use `{{template_variables}}` filled from project config. The `work-item.md` playbook uses `{{scope_section}}` to inject project-specific or multi-project context. Playbooks are fully customizable — edit them to match your workflow.
336
337
 
@@ -355,7 +356,7 @@ Agents can run for hours as long as they're producing output. The `heartbeatTime
355
356
  | Orphaned worktrees | >24 hours old, no active dispatch references them |
356
357
  | Zombie processes | In memory but no matching dispatch |
357
358
 
358
- Manual cleanup: `node engine.js cleanup`
359
+ Manual cleanup: `squad cleanup`
359
360
 
360
361
  ## Self-Improvement Loop
361
362
 
@@ -374,7 +375,7 @@ When a reviewer flags issues, the engine creates `feedback-<author>-from-<review
374
375
  `engine/metrics.json` tracks per agent: tasks completed, errors, PRs created/approved/rejected, reviews done. Visible in CLI (`status`) and dashboard with color-coded approval rates.
375
376
 
376
377
  ### 5. Skills
377
- Agents save repeatable workflows to `skills/<name>.md` with Claude Code-compatible frontmatter. Engine builds an index injected into all prompts. Skills can also be stored per-project at `<project>/.claude/skills/<name>/SKILL.md` (requires a PR). Visible in dashboard alongside decisions.
378
+ Agents save repeatable workflows to `skills/<name>.md` with Claude Code-compatible frontmatter. Engine builds an index injected into all prompts. Skills can also be stored per-project at `<project>/.claude/skills/<name>/SKILL.md` (requires a PR). Visible in the dashboard Skills section.
378
379
 
379
380
  See `docs/self-improvement.md` for the full breakdown.
380
381
 
@@ -408,9 +409,9 @@ Engine behavior is controlled via `config.json`. Key settings:
408
409
 
409
410
  The engine and all spawned agents use the Node binary that started the engine (`process.execPath`). After upgrading Node, restart the engine:
410
411
 
411
- ```powershell
412
- node ~/.squad/engine.js stop
413
- node ~/.squad/engine.js
412
+ ```bash
413
+ squad stop
414
+ squad start
414
415
  ```
415
416
 
416
417
  ## Portability
@@ -418,41 +419,45 @@ node ~/.squad/engine.js
418
419
  **Portable (works on any machine):** Engine, dashboard, playbooks, charters, routing, notes, skills, docs, work items.
419
420
 
420
421
  **Machine-specific (reconfigure per machine):**
421
- - `config.json` — contains absolute paths to project directories. Re-link via `node squad.js add <dir>`.
422
+ - `config.json` — contains absolute paths to project directories. Re-link via `squad add <dir>`.
422
423
  - `mcp-servers.json` — auto-synced from `~/.claude.json` on engine start.
423
424
 
424
- To move to a new machine: clone `~/.squad/`, delete `engine/control.json`, re-run `node squad.js add` for each project.
425
+ To move to a new machine: `npm install -g @yemi33/squad && squad init --force`, then re-run `squad add` for each project.
425
426
 
426
427
  ## File Layout
427
428
 
428
429
  ```
429
430
  ~/.squad/
430
- squad.js <- CLI: init, add, remove, list projects
431
+ bin/
432
+ squad.js <- Unified CLI entry point (npm package)
433
+ squad.js <- Project management: init, add, remove, list
431
434
  engine.js <- Engine daemon
432
435
  engine/
433
436
  spawn-agent.js <- Agent spawn wrapper (resolves claude cli.js)
434
- control.json <- running/paused/stopped
435
- dispatch.json <- pending/active/completed queue
436
- log.json <- Audit trail (capped at 500)
437
- metrics.json <- Per-agent quality metrics
437
+ ado-mcp-wrapper.js <- ADO MCP authentication wrapper
438
+ control.json <- running/paused/stopped (runtime)
439
+ dispatch.json <- pending/active/completed queue (runtime)
440
+ log.json <- Audit trail, capped at 500 (runtime)
441
+ metrics.json <- Per-agent quality metrics (runtime)
438
442
  dashboard.js <- Web dashboard server
439
- dashboard.html <- Dashboard UI
443
+ dashboard.html <- Dashboard UI (single-file)
440
444
  config.json <- projects[], agents, engine, claude settings
441
- config.template.json <- Template for reference
445
+ config.template.json <- Template for new installs
446
+ package.json <- npm package definition
442
447
  mcp-servers.json <- MCP servers (auto-synced, gitignored)
443
448
  routing.md <- Dispatch rules table (editable)
444
449
  team.md <- Team roster
445
- notes.md <- Team rules + consolidated learnings
446
- work-items.json <- Central work queue (agent decides which project)
447
- TODO.md <- Future improvements roadmap
450
+ notes.md <- Team rules + consolidated learnings (runtime)
451
+ work-items.json <- Central work queue (runtime)
448
452
  playbooks/
449
453
  work-item.md <- Shared work item template
450
454
  implement.md <- Build a PRD item
451
455
  review.md <- Review a PR
452
456
  fix.md <- Fix review feedback
453
- analyze.md <- Generate new PRD
454
457
  explore.md <- Codebase exploration
455
458
  test.md <- Run tests
459
+ build-and-test.md <- Build project and run test suite
460
+ plan-to-prd.md <- Convert plan to PRD gap items
456
461
  skills/
457
462
  README.md <- Skill format guide
458
463
  <name>.md <- Agent-created reusable workflows
@@ -460,7 +465,7 @@ To move to a new machine: clone `~/.squad/`, delete `engine/control.json`, re-ru
460
465
  {name}/
461
466
  charter.md <- Agent identity and boundaries (editable)
462
467
  status.json <- Current state (runtime)
463
- history.md <- Task history (last 20, runtime)
468
+ history.md <- Task history, last 20 (runtime)
464
469
  live-output.log <- Streaming output while working (runtime)
465
470
  output.log <- Final output after completion (runtime)
466
471
  identity/
package/TODO.md CHANGED
@@ -6,21 +6,21 @@ Ordered by difficulty: quick wins first, larger efforts later.
6
6
 
7
7
  ## Quick Wins (< 1 hour each)
8
8
 
9
- - [ ] **Output.log append, not overwrite** — keep all dispatch outputs, not just the last one. Rotate by dispatch ID.
10
- - [ ] **Persistent cooldowns** — save cooldown state to disk so engine restarts don't re-dispatch everything
11
- - [ ] **Worktree cleanup on merge/close** — when `pollPrStatus` detects a PR merged or abandoned, auto-remove its worktree. Currently only `runCleanup` catches old worktrees on a timer.
12
- - [ ] **Discovery skip logging** — log why items were skipped during discovery (cooldown, already dispatched, no idle agent) so users can diagnose "why isn't my work item being picked up?"
13
- - [ ] **Idle threshold alert** — if all agents are idle for >N minutes, notify via Teams/dashboard
14
- - [ ] **Config validation at startup** — verify all project paths exist, all agents defined, all playbooks exist. Fail fast with clear errors instead of silently skipping.
15
- - [ ] **macOS/Linux browser launch** — replace Windows `start` command with `open` (macOS) / `xdg-open` (Linux)
16
- - [ ] **Health check endpoint** — `/api/health` returning engine state, project reachability, agent statuses for monitoring
17
- - [ ] **Fan-out per-agent timeout** — when `@everyone` dispatches, set individual deadlines per agent instead of relying only on global `agentTimeout`
9
+ - [x] **Output.log append, not overwrite** — per-dispatch archive at `output-{id}.log` + latest copy at `output.log`
10
+ - [x] **Persistent cooldowns** — saved to `engine/cooldowns.json`, loaded at startup, 24hr auto-prune
11
+ - [x] **Worktree cleanup on merge/close** — `handlePostMerge` removes worktrees when PR merges or is abandoned
12
+ - [x] **Discovery skip logging** — PRD and work item discovery log skip counts by reason at debug level
13
+ - [x] **Idle threshold alert** — warns when all agents idle >15min (configurable via `engine.idleAlertMinutes`)
14
+ - [x] **Config validation at startup** — checks agents, project paths, playbooks, routing.md. Fatal errors exit(1).
15
+ - [x] **macOS/Linux browser launch** — already platform-aware (was done previously)
16
+ - [x] **Health check endpoint** — `GET /api/health` returning engine state, agents, project reachability, uptime
17
+ - [x] **Fan-out per-agent timeout** — fan-out items carry `meta.deadline`, configurable via `engine.fanOutTimeout`
18
18
 
19
19
  ## Small Effort (1–3 hours each)
20
20
 
21
21
  - [ ] **Auto-retry with backoff** — when an agent errors, auto-retry with exponential backoff (5min, 15min, cap at 3 attempts) instead of requiring manual dashboard retry.
22
22
  - [ ] **Auto-escalation** — if an agent errors 3 times in a row, pause their dispatch and alert via dashboard/Teams
23
- - [ ] **Post-merge hooks** — when a PR merges, trigger configurable actions: clean up worktree, update PRD item status to `done`, notify Teams, update metrics
23
+ - [x] **Post-merge hooks** — `handlePostMerge`: worktree cleanup, PRD status implemented, prsMerged metric, Teams notification
24
24
  - [ ] **Pending dispatch explanation** — show in dashboard why each pending item hasn't been dispatched (no idle agent? cooldown? max concurrency?)
25
25
  - [ ] **Work item editing** — edit title, description, type, priority, agent assignment from the dashboard UI (currently requires editing JSON)
26
26
  - [ ] **Bulk operations** — retry/delete/reassign multiple work items at once
package/bin/squad.js CHANGED
@@ -1,164 +1,164 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Squad CLI — Central AI dev team manager
4
- *
5
- * Usage:
6
- * squad init Bootstrap ~/.squad/ with default config and agents
7
- * squad add <project-dir> Link a project (interactive)
8
- * squad remove <project-dir> Unlink a project
9
- * squad list List linked projects
10
- * squad start Start the engine
11
- * squad stop Stop the engine
12
- * squad status Show engine status
13
- * squad pause / resume Pause/resume dispatching
14
- * squad dash Start the dashboard
15
- * squad work <title> [opts-json] Add a work item
16
- * squad spawn <agent> <prompt> Manually spawn an agent
17
- * squad dispatch Force a dispatch cycle
18
- * squad discover Dry-run work discovery
19
- * squad cleanup Run cleanup manually
20
- * squad plan <file|text> [proj] Run a plan
21
- */
22
-
23
- const fs = require('fs');
24
- const path = require('path');
25
- const { spawn, execSync } = require('child_process');
26
-
27
- const SQUAD_HOME = path.join(require('os').homedir(), '.squad');
28
- const PKG_ROOT = path.resolve(__dirname, '..');
29
-
30
- const [cmd, ...rest] = process.argv.slice(2);
31
-
32
- // ─── Init: bootstrap ~/.squad/ from package templates ───────────────────────
33
-
34
- function init() {
35
- if (fs.existsSync(path.join(SQUAD_HOME, 'engine.js'))) {
36
- console.log(`\n Squad already installed at ${SQUAD_HOME}`);
37
- console.log(' To reinitialize config: squad init --force\n');
38
- if (!rest.includes('--force')) return;
39
- }
40
-
41
- console.log(`\n Bootstrapping Squad to ${SQUAD_HOME}...\n`);
42
- fs.mkdirSync(SQUAD_HOME, { recursive: true });
43
-
44
- // Copy all package files to ~/.squad/
45
- const exclude = new Set([
46
- 'bin', 'node_modules', '.git', '.claude', 'package.json',
47
- 'package-lock.json', 'LICENSE', '.npmignore', '.gitignore',
48
- ]);
49
-
50
- copyDir(PKG_ROOT, SQUAD_HOME, exclude);
51
-
52
- // Create config from template if it doesn't exist
53
- const configPath = path.join(SQUAD_HOME, 'config.json');
54
- if (!fs.existsSync(configPath)) {
55
- const tmpl = path.join(SQUAD_HOME, 'config.template.json');
56
- if (fs.existsSync(tmpl)) {
57
- fs.copyFileSync(tmpl, configPath);
58
- }
59
- }
60
-
61
- // Ensure runtime directories exist
62
- const dirs = [
63
- 'engine', 'notes/inbox', 'notes/archive',
64
- 'identity', 'plans',
65
- ];
66
- for (const d of dirs) {
67
- fs.mkdirSync(path.join(SQUAD_HOME, d), { recursive: true });
68
- }
69
-
70
- // Run squad.js init to populate config with defaults
71
- execSync(`node "${path.join(SQUAD_HOME, 'squad.js')}" init`, { stdio: 'inherit' });
72
-
73
- console.log('\n Next steps:');
74
- console.log(' squad add ~/my-project Link your first project');
75
- console.log(' squad start Start the engine');
76
- console.log(' squad dash Open the dashboard\n');
77
- }
78
-
79
- function copyDir(src, dest, exclude) {
80
- const entries = fs.readdirSync(src, { withFileTypes: true });
81
- for (const entry of entries) {
82
- if (exclude.has(entry.name)) continue;
83
- const srcPath = path.join(src, entry.name);
84
- const destPath = path.join(dest, entry.name);
85
- if (entry.isDirectory()) {
86
- fs.mkdirSync(destPath, { recursive: true });
87
- copyDir(srcPath, destPath, new Set()); // only exclude at top level
88
- } else {
89
- // Don't overwrite user-modified files (except on --force)
90
- if (fs.existsSync(destPath) && !rest.includes('--force')) {
91
- // Always update engine code files
92
- if (!entry.name.endsWith('.js') && !entry.name.endsWith('.html')) continue;
93
- }
94
- fs.copyFileSync(srcPath, destPath);
95
- }
96
- }
97
- }
98
-
99
- // ─── Delegate: run commands against installed ~/.squad/ ─────────────────────
100
-
101
- function ensureInstalled() {
102
- if (!fs.existsSync(path.join(SQUAD_HOME, 'engine.js'))) {
103
- console.log('\n Squad is not installed. Run: squad init\n');
104
- process.exit(1);
105
- }
106
- }
107
-
108
- function delegate(script, args) {
109
- ensureInstalled();
110
- const child = spawn(process.execPath, [path.join(SQUAD_HOME, script), ...args], {
111
- stdio: 'inherit',
112
- cwd: SQUAD_HOME,
113
- });
114
- child.on('exit', code => process.exit(code || 0));
115
- }
116
-
117
- // ─── Command routing ────────────────────────────────────────────────────────
118
-
119
- const engineCmds = new Set([
120
- 'start', 'stop', 'status', 'pause', 'resume',
121
- 'queue', 'sources', 'discover', 'dispatch',
122
- 'spawn', 'work', 'cleanup', 'mcp-sync', 'plan',
123
- ]);
124
-
125
- if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
126
- console.log(`
127
- Squad — Central AI dev team manager
128
-
129
- Setup:
130
- squad init Bootstrap ~/.squad/ (first time)
131
- squad add <project-dir> Link a project (interactive)
132
- squad remove <project-dir> Unlink a project
133
- squad list List linked projects
134
-
135
- Engine:
136
- squad start Start engine daemon
137
- squad stop Stop the engine
138
- squad status Show agents, projects, queue
139
- squad pause / resume Pause/resume dispatching
140
- squad dispatch Force a dispatch cycle
141
- squad discover Dry-run work discovery
142
- squad work <title> [opts] Add a work item
143
- squad spawn <agent> <prompt> Manually spawn an agent
144
- squad plan <file|text> [proj] Run a plan
145
- squad cleanup Clean temp files, worktrees, zombies
146
-
147
- Dashboard:
148
- squad dash Start web dashboard (default :7331)
149
-
150
- Home: ${SQUAD_HOME}
151
- `);
152
- } else if (cmd === 'init') {
153
- init();
154
- } else if (cmd === 'add' || cmd === 'remove' || cmd === 'list') {
155
- delegate('squad.js', [cmd, ...rest]);
156
- } else if (cmd === 'dash' || cmd === 'dashboard') {
157
- delegate('dashboard.js', rest);
158
- } else if (engineCmds.has(cmd)) {
159
- delegate('engine.js', [cmd, ...rest]);
160
- } else {
161
- console.log(` Unknown command: ${cmd}`);
162
- console.log(' Run "squad help" for usage.\n');
163
- process.exit(1);
164
- }
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Squad CLI — Central AI dev team manager
4
+ *
5
+ * Usage:
6
+ * squad init Bootstrap ~/.squad/ with default config and agents
7
+ * squad add <project-dir> Link a project (interactive)
8
+ * squad remove <project-dir> Unlink a project
9
+ * squad list List linked projects
10
+ * squad start Start the engine
11
+ * squad stop Stop the engine
12
+ * squad status Show engine status
13
+ * squad pause / resume Pause/resume dispatching
14
+ * squad dash Start the dashboard
15
+ * squad work <title> [opts-json] Add a work item
16
+ * squad spawn <agent> <prompt> Manually spawn an agent
17
+ * squad dispatch Force a dispatch cycle
18
+ * squad discover Dry-run work discovery
19
+ * squad cleanup Run cleanup manually
20
+ * squad plan <file|text> [proj] Run a plan
21
+ */
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+ const { spawn, execSync } = require('child_process');
26
+
27
+ const SQUAD_HOME = path.join(require('os').homedir(), '.squad');
28
+ const PKG_ROOT = path.resolve(__dirname, '..');
29
+
30
+ const [cmd, ...rest] = process.argv.slice(2);
31
+
32
+ // ─── Init: bootstrap ~/.squad/ from package templates ───────────────────────
33
+
34
+ function init() {
35
+ if (fs.existsSync(path.join(SQUAD_HOME, 'engine.js'))) {
36
+ console.log(`\n Squad already installed at ${SQUAD_HOME}`);
37
+ console.log(' To reinitialize config: squad init --force\n');
38
+ if (!rest.includes('--force')) return;
39
+ }
40
+
41
+ console.log(`\n Bootstrapping Squad to ${SQUAD_HOME}...\n`);
42
+ fs.mkdirSync(SQUAD_HOME, { recursive: true });
43
+
44
+ // Copy all package files to ~/.squad/
45
+ const exclude = new Set([
46
+ 'bin', 'node_modules', '.git', '.claude', 'package.json',
47
+ 'package-lock.json', 'LICENSE', '.npmignore', '.gitignore',
48
+ ]);
49
+
50
+ copyDir(PKG_ROOT, SQUAD_HOME, exclude);
51
+
52
+ // Create config from template if it doesn't exist
53
+ const configPath = path.join(SQUAD_HOME, 'config.json');
54
+ if (!fs.existsSync(configPath)) {
55
+ const tmpl = path.join(SQUAD_HOME, 'config.template.json');
56
+ if (fs.existsSync(tmpl)) {
57
+ fs.copyFileSync(tmpl, configPath);
58
+ }
59
+ }
60
+
61
+ // Ensure runtime directories exist
62
+ const dirs = [
63
+ 'engine', 'notes/inbox', 'notes/archive',
64
+ 'identity', 'plans',
65
+ ];
66
+ for (const d of dirs) {
67
+ fs.mkdirSync(path.join(SQUAD_HOME, d), { recursive: true });
68
+ }
69
+
70
+ // Run squad.js init to populate config with defaults
71
+ execSync(`node "${path.join(SQUAD_HOME, 'squad.js')}" init`, { stdio: 'inherit' });
72
+
73
+ console.log('\n Next steps:');
74
+ console.log(' squad add ~/my-project Link your first project');
75
+ console.log(' squad start Start the engine');
76
+ console.log(' squad dash Open the dashboard\n');
77
+ }
78
+
79
+ function copyDir(src, dest, exclude) {
80
+ const entries = fs.readdirSync(src, { withFileTypes: true });
81
+ for (const entry of entries) {
82
+ if (exclude.has(entry.name)) continue;
83
+ const srcPath = path.join(src, entry.name);
84
+ const destPath = path.join(dest, entry.name);
85
+ if (entry.isDirectory()) {
86
+ fs.mkdirSync(destPath, { recursive: true });
87
+ copyDir(srcPath, destPath, new Set()); // only exclude at top level
88
+ } else {
89
+ // Don't overwrite user-modified files (except on --force)
90
+ if (fs.existsSync(destPath) && !rest.includes('--force')) {
91
+ // Always update engine code files
92
+ if (!entry.name.endsWith('.js') && !entry.name.endsWith('.html')) continue;
93
+ }
94
+ fs.copyFileSync(srcPath, destPath);
95
+ }
96
+ }
97
+ }
98
+
99
+ // ─── Delegate: run commands against installed ~/.squad/ ─────────────────────
100
+
101
+ function ensureInstalled() {
102
+ if (!fs.existsSync(path.join(SQUAD_HOME, 'engine.js'))) {
103
+ console.log('\n Squad is not installed. Run: squad init\n');
104
+ process.exit(1);
105
+ }
106
+ }
107
+
108
+ function delegate(script, args) {
109
+ ensureInstalled();
110
+ const child = spawn(process.execPath, [path.join(SQUAD_HOME, script), ...args], {
111
+ stdio: 'inherit',
112
+ cwd: SQUAD_HOME,
113
+ });
114
+ child.on('exit', code => process.exit(code || 0));
115
+ }
116
+
117
+ // ─── Command routing ────────────────────────────────────────────────────────
118
+
119
+ const engineCmds = new Set([
120
+ 'start', 'stop', 'status', 'pause', 'resume',
121
+ 'queue', 'sources', 'discover', 'dispatch',
122
+ 'spawn', 'work', 'cleanup', 'mcp-sync', 'plan',
123
+ ]);
124
+
125
+ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
126
+ console.log(`
127
+ Squad — Central AI dev team manager
128
+
129
+ Setup:
130
+ squad init Bootstrap ~/.squad/ (first time)
131
+ squad add <project-dir> Link a project (interactive)
132
+ squad remove <project-dir> Unlink a project
133
+ squad list List linked projects
134
+
135
+ Engine:
136
+ squad start Start engine daemon
137
+ squad stop Stop the engine
138
+ squad status Show agents, projects, queue
139
+ squad pause / resume Pause/resume dispatching
140
+ squad dispatch Force a dispatch cycle
141
+ squad discover Dry-run work discovery
142
+ squad work <title> [opts] Add a work item
143
+ squad spawn <agent> <prompt> Manually spawn an agent
144
+ squad plan <file|text> [proj] Run a plan
145
+ squad cleanup Clean temp files, worktrees, zombies
146
+
147
+ Dashboard:
148
+ squad dash Start web dashboard (default :7331)
149
+
150
+ Home: ${SQUAD_HOME}
151
+ `);
152
+ } else if (cmd === 'init') {
153
+ init();
154
+ } else if (cmd === 'add' || cmd === 'remove' || cmd === 'list') {
155
+ delegate('squad.js', [cmd, ...rest]);
156
+ } else if (cmd === 'dash' || cmd === 'dashboard') {
157
+ delegate('dashboard.js', rest);
158
+ } else if (engineCmds.has(cmd)) {
159
+ delegate('engine.js', [cmd, ...rest]);
160
+ } else {
161
+ console.log(` Unknown command: ${cmd}`);
162
+ console.log(' Run "squad help" for usage.\n');
163
+ process.exit(1);
164
+ }