@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 +21 -21
- package/README.md +29 -24
- package/TODO.md +10 -10
- package/bin/squad.js +164 -164
- package/dashboard.js +901 -886
- package/docs/engine-restart.md +92 -0
- package/engine/ado-mcp-wrapper.js +49 -49
- package/engine.js +194 -14
- package/package.json +46 -46
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
|
|
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
|
|
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 `
|
|
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 `
|
|
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: `
|
|
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
|
|
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
|
-
```
|
|
412
|
-
|
|
413
|
-
|
|
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 `
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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
|
|
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
|
|
446
|
-
work-items.json <- Central work queue (
|
|
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
|
|
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
|
-
- [
|
|
10
|
-
- [
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
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
|
-
- [
|
|
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
|
+
}
|