@bradygaster/squad-cli 0.8.4 → 0.8.16
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/README.md +2 -2
- package/dist/cli/commands/aspire.d.ts.map +1 -1
- package/dist/cli/commands/aspire.js +7 -8
- package/dist/cli/commands/aspire.js.map +1 -1
- package/dist/cli/commands/copilot-bridge.d.ts +42 -0
- package/dist/cli/commands/copilot-bridge.d.ts.map +1 -0
- package/dist/cli/commands/copilot-bridge.js +191 -0
- package/dist/cli/commands/copilot-bridge.js.map +1 -0
- package/dist/cli/commands/import.js.map +1 -1
- package/dist/cli/commands/rc-tunnel.d.ts +30 -0
- package/dist/cli/commands/rc-tunnel.d.ts.map +1 -0
- package/dist/cli/commands/rc-tunnel.js +107 -0
- package/dist/cli/commands/rc-tunnel.js.map +1 -0
- package/dist/cli/commands/rc.d.ts +13 -0
- package/dist/cli/commands/rc.d.ts.map +1 -0
- package/dist/cli/commands/rc.js +270 -0
- package/dist/cli/commands/rc.js.map +1 -0
- package/dist/cli/commands/start.d.ts +18 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +219 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/upstream.js.map +1 -1
- package/dist/cli/commands/watch.d.ts +10 -0
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +181 -65
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/cli/core/cast.d.ts +40 -0
- package/dist/cli/core/cast.d.ts.map +1 -0
- package/dist/cli/core/cast.js +442 -0
- package/dist/cli/core/cast.js.map +1 -0
- package/dist/cli/core/gh-cli.d.ts +25 -0
- package/dist/cli/core/gh-cli.d.ts.map +1 -1
- package/dist/cli/core/gh-cli.js +15 -1
- package/dist/cli/core/gh-cli.js.map +1 -1
- package/dist/cli/core/init.d.ts +9 -1
- package/dist/cli/core/init.d.ts.map +1 -1
- package/dist/cli/core/init.js +108 -13
- package/dist/cli/core/init.js.map +1 -1
- package/dist/cli/core/migrations.js.map +1 -1
- package/dist/cli/core/nap.d.ts +37 -0
- package/dist/cli/core/nap.d.ts.map +1 -0
- package/dist/cli/core/nap.js +528 -0
- package/dist/cli/core/nap.js.map +1 -0
- package/dist/cli/core/output.d.ts +5 -0
- package/dist/cli/core/output.d.ts.map +1 -1
- package/dist/cli/core/output.js +7 -0
- package/dist/cli/core/output.js.map +1 -1
- package/dist/cli/core/upgrade.d.ts +0 -1
- package/dist/cli/core/upgrade.d.ts.map +1 -1
- package/dist/cli/core/upgrade.js.map +1 -1
- package/dist/cli/core/version.js.map +1 -1
- package/dist/cli/shell/agent-status.d.ts +11 -0
- package/dist/cli/shell/agent-status.d.ts.map +1 -0
- package/dist/cli/shell/agent-status.js +26 -0
- package/dist/cli/shell/agent-status.js.map +1 -0
- package/dist/cli/shell/commands.d.ts +10 -0
- package/dist/cli/shell/commands.d.ts.map +1 -1
- package/dist/cli/shell/commands.js +143 -29
- package/dist/cli/shell/commands.js.map +1 -1
- package/dist/cli/shell/components/AgentPanel.d.ts +1 -4
- package/dist/cli/shell/components/AgentPanel.d.ts.map +1 -1
- package/dist/cli/shell/components/AgentPanel.js +88 -6
- package/dist/cli/shell/components/AgentPanel.js.map +1 -1
- package/dist/cli/shell/components/App.d.ts +11 -6
- package/dist/cli/shell/components/App.d.ts.map +1 -1
- package/dist/cli/shell/components/App.js +212 -35
- package/dist/cli/shell/components/App.js.map +1 -1
- package/dist/cli/shell/components/ErrorBoundary.d.ts +22 -0
- package/dist/cli/shell/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/cli/shell/components/ErrorBoundary.js +31 -0
- package/dist/cli/shell/components/ErrorBoundary.js.map +1 -0
- package/dist/cli/shell/components/InputPrompt.d.ts +3 -0
- package/dist/cli/shell/components/InputPrompt.d.ts.map +1 -1
- package/dist/cli/shell/components/InputPrompt.js +155 -13
- package/dist/cli/shell/components/InputPrompt.js.map +1 -1
- package/dist/cli/shell/components/MessageStream.d.ts +17 -4
- package/dist/cli/shell/components/MessageStream.d.ts.map +1 -1
- package/dist/cli/shell/components/MessageStream.js +215 -23
- package/dist/cli/shell/components/MessageStream.js.map +1 -1
- package/dist/cli/shell/components/Separator.d.ts +17 -0
- package/dist/cli/shell/components/Separator.d.ts.map +1 -0
- package/dist/cli/shell/components/Separator.js +10 -0
- package/dist/cli/shell/components/Separator.js.map +1 -0
- package/dist/cli/shell/components/ThinkingIndicator.d.ts +21 -0
- package/dist/cli/shell/components/ThinkingIndicator.d.ts.map +1 -0
- package/dist/cli/shell/components/ThinkingIndicator.js +102 -0
- package/dist/cli/shell/components/ThinkingIndicator.js.map +1 -0
- package/dist/cli/shell/components/index.d.ts +3 -0
- package/dist/cli/shell/components/index.d.ts.map +1 -1
- package/dist/cli/shell/components/index.js +2 -0
- package/dist/cli/shell/components/index.js.map +1 -1
- package/dist/cli/shell/coordinator.d.ts +10 -0
- package/dist/cli/shell/coordinator.d.ts.map +1 -1
- package/dist/cli/shell/coordinator.js +99 -4
- package/dist/cli/shell/coordinator.js.map +1 -1
- package/dist/cli/shell/error-messages.d.ts +21 -0
- package/dist/cli/shell/error-messages.d.ts.map +1 -0
- package/dist/cli/shell/error-messages.js +61 -0
- package/dist/cli/shell/error-messages.js.map +1 -0
- package/dist/cli/shell/index.d.ts +24 -3
- package/dist/cli/shell/index.d.ts.map +1 -1
- package/dist/cli/shell/index.js +949 -29
- package/dist/cli/shell/index.js.map +1 -1
- package/dist/cli/shell/lifecycle.d.ts +2 -0
- package/dist/cli/shell/lifecycle.d.ts.map +1 -1
- package/dist/cli/shell/lifecycle.js +59 -6
- package/dist/cli/shell/lifecycle.js.map +1 -1
- package/dist/cli/shell/memory.d.ts +6 -1
- package/dist/cli/shell/memory.d.ts.map +1 -1
- package/dist/cli/shell/memory.js +12 -1
- package/dist/cli/shell/memory.js.map +1 -1
- package/dist/cli/shell/router.d.ts +16 -0
- package/dist/cli/shell/router.d.ts.map +1 -1
- package/dist/cli/shell/router.js +27 -0
- package/dist/cli/shell/router.js.map +1 -1
- package/dist/cli/shell/session-store.d.ts +47 -0
- package/dist/cli/shell/session-store.d.ts.map +1 -0
- package/dist/cli/shell/session-store.js +125 -0
- package/dist/cli/shell/session-store.js.map +1 -0
- package/dist/cli/shell/sessions.d.ts +2 -0
- package/dist/cli/shell/sessions.d.ts.map +1 -1
- package/dist/cli/shell/sessions.js +19 -5
- package/dist/cli/shell/sessions.js.map +1 -1
- package/dist/cli/shell/shell-metrics.d.ts +34 -0
- package/dist/cli/shell/shell-metrics.d.ts.map +1 -0
- package/dist/cli/shell/shell-metrics.js +98 -0
- package/dist/cli/shell/shell-metrics.js.map +1 -0
- package/dist/cli/shell/spawn.d.ts.map +1 -1
- package/dist/cli/shell/spawn.js +20 -6
- package/dist/cli/shell/spawn.js.map +1 -1
- package/dist/cli/shell/terminal.d.ts +26 -0
- package/dist/cli/shell/terminal.d.ts.map +1 -1
- package/dist/cli/shell/terminal.js +65 -2
- package/dist/cli/shell/terminal.js.map +1 -1
- package/dist/cli/shell/theme-colors.d.ts +39 -0
- package/dist/cli/shell/theme-colors.d.ts.map +1 -0
- package/dist/cli/shell/theme-colors.js +39 -0
- package/dist/cli/shell/theme-colors.js.map +1 -0
- package/dist/cli/shell/types.d.ts +2 -0
- package/dist/cli/shell/types.d.ts.map +1 -1
- package/dist/cli/shell/useAnimation.d.ts +42 -0
- package/dist/cli/shell/useAnimation.d.ts.map +1 -0
- package/dist/cli/shell/useAnimation.js +139 -0
- package/dist/cli/shell/useAnimation.js.map +1 -0
- package/dist/cli-entry.d.ts +0 -7
- package/dist/cli-entry.d.ts.map +1 -1
- package/dist/cli-entry.js +701 -96
- package/dist/cli-entry.js.map +1 -1
- package/package.json +156 -140
- package/templates/orchestration-log.md +1 -1
- package/templates/package.json +3 -0
- package/templates/ralph-triage.js +543 -0
- package/templates/scribe-charter.md +1 -1
- package/templates/squad.agent.md +10 -10
- package/templates/workflows/squad-heartbeat.yml +52 -196
- package/templates/workflows/squad-insider-release.yml +1 -1
package/dist/cli-entry.js
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
process.env.NODE_NO_WARNINGS = '1';
|
|
3
|
+
// Suppress Node.js experimental feature warnings (e.g., SQLite) from end users
|
|
4
|
+
const originalEmitWarning = process.emitWarning;
|
|
5
|
+
process.emitWarning = (warning, ...args) => {
|
|
6
|
+
if (typeof warning === 'string' && warning.includes('ExperimentalWarning'))
|
|
7
|
+
return;
|
|
8
|
+
if (warning?.name === 'ExperimentalWarning')
|
|
9
|
+
return;
|
|
10
|
+
return originalEmitWarning.call(process, warning, ...args);
|
|
11
|
+
};
|
|
2
12
|
/**
|
|
3
13
|
* Squad CLI — entry point for command-line invocation.
|
|
4
14
|
* Separated from src/index.ts so library consumers can import
|
|
@@ -6,6 +16,25 @@
|
|
|
6
16
|
*
|
|
7
17
|
* SDK library exports live in src/index.ts (dist/index.js).
|
|
8
18
|
*/
|
|
19
|
+
// Load .env file if present (dev mode)
|
|
20
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
21
|
+
import { resolve } from 'node:path';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
23
|
+
const envPath = resolve(process.cwd(), '.env');
|
|
24
|
+
if (existsSync(envPath)) {
|
|
25
|
+
for (const line of readFileSync(envPath, 'utf8').split('\n')) {
|
|
26
|
+
const trimmed = line.trim();
|
|
27
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
28
|
+
continue;
|
|
29
|
+
const eq = trimmed.indexOf('=');
|
|
30
|
+
if (eq > 0) {
|
|
31
|
+
const key = trimmed.slice(0, eq).trim();
|
|
32
|
+
const val = trimmed.slice(eq + 1).trim();
|
|
33
|
+
if (!process.env[key])
|
|
34
|
+
process.env[key] = val;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
9
38
|
import fs from 'node:fs';
|
|
10
39
|
import path from 'node:path';
|
|
11
40
|
import { fatal, SquadError } from './cli/core/errors.js';
|
|
@@ -13,73 +42,631 @@ import { BOLD, RESET, DIM, RED } from './cli/core/output.js';
|
|
|
13
42
|
import { runInit } from './cli/core/init.js';
|
|
14
43
|
import { resolveSquad, resolveGlobalSquadPath } from '@bradygaster/squad-sdk';
|
|
15
44
|
import { runShell } from './cli/shell/index.js';
|
|
45
|
+
import { loadWelcomeData } from './cli/shell/lifecycle.js';
|
|
16
46
|
// Keep VERSION in index.ts (public API); import it here via re-export
|
|
17
47
|
import { VERSION } from '@bradygaster/squad-sdk';
|
|
48
|
+
/** Debug logger — writes to stderr only when SQUAD_DEBUG=1. */
|
|
49
|
+
function debugLog(...args) {
|
|
50
|
+
if (process.env['SQUAD_DEBUG'] === '1') {
|
|
51
|
+
console.error('[SQUAD_DEBUG]', ...args);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Check if --help or -h appears in args after the subcommand. */
|
|
55
|
+
function hasHelpFlag(args) {
|
|
56
|
+
return args.slice(1).includes('--help') || args.slice(1).includes('-h');
|
|
57
|
+
}
|
|
58
|
+
/** Commands that skip the auto-link check (non-interactive or utility). */
|
|
59
|
+
const SKIP_AUTO_LINK_CMDS = new Set([
|
|
60
|
+
'--version', '-v', 'version',
|
|
61
|
+
'--help', '-h', 'help',
|
|
62
|
+
'export', 'import', 'doctor', 'heartbeat',
|
|
63
|
+
'scrub-emails', '--preview', '--dry-run',
|
|
64
|
+
]);
|
|
65
|
+
/**
|
|
66
|
+
* Dev convenience: when running from source (-preview), check if the CLI
|
|
67
|
+
* is globally linked and offer to link it if not.
|
|
68
|
+
*/
|
|
69
|
+
async function checkAutoLink(cmd, args) {
|
|
70
|
+
try {
|
|
71
|
+
if (!VERSION.includes('-preview'))
|
|
72
|
+
return;
|
|
73
|
+
if (cmd && SKIP_AUTO_LINK_CMDS.has(cmd))
|
|
74
|
+
return;
|
|
75
|
+
if (args.includes('--preview') || args.includes('--dry-run'))
|
|
76
|
+
return;
|
|
77
|
+
if (args.includes('--help') || args.includes('-h'))
|
|
78
|
+
return;
|
|
79
|
+
if (!process.stdin.isTTY)
|
|
80
|
+
return;
|
|
81
|
+
const { homedir } = await import('node:os');
|
|
82
|
+
const home = homedir();
|
|
83
|
+
const squadDir = path.join(home, '.squad');
|
|
84
|
+
const markerPath = path.join(squadDir, '.no-auto-link');
|
|
85
|
+
if (fs.existsSync(markerPath))
|
|
86
|
+
return;
|
|
87
|
+
// Locate this package and the monorepo root
|
|
88
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
89
|
+
const packageDir = path.resolve(path.dirname(thisFile), '..');
|
|
90
|
+
const repoRoot = path.resolve(packageDir, '..', '..');
|
|
91
|
+
// Check if already globally linked
|
|
92
|
+
const { execSync } = await import('node:child_process');
|
|
93
|
+
let alreadyLinked = false;
|
|
94
|
+
let output = '';
|
|
95
|
+
try {
|
|
96
|
+
output = execSync('npm ls -g @bradygaster/squad-cli --json', {
|
|
97
|
+
encoding: 'utf8',
|
|
98
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
const err = e;
|
|
103
|
+
output = err.stdout ?? '';
|
|
104
|
+
}
|
|
105
|
+
if (output) {
|
|
106
|
+
try {
|
|
107
|
+
const parsed = JSON.parse(output);
|
|
108
|
+
const dep = parsed?.dependencies?.['@bradygaster/squad-cli'];
|
|
109
|
+
if (dep?.link === true) {
|
|
110
|
+
alreadyLinked = true;
|
|
111
|
+
}
|
|
112
|
+
else if (typeof dep?.resolved === 'string' && dep.resolved.startsWith('file:')) {
|
|
113
|
+
try {
|
|
114
|
+
const resolvedPath = fileURLToPath(new URL(dep.resolved));
|
|
115
|
+
alreadyLinked = path.resolve(resolvedPath) === path.resolve(packageDir);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// URL parsing failed — skip
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// JSON parse failed
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (alreadyLinked)
|
|
127
|
+
return;
|
|
128
|
+
// Prompt the user
|
|
129
|
+
console.log(`\n📦 You're running Squad from source (${VERSION}).`);
|
|
130
|
+
console.log(` Run 'npm link -w packages/squad-cli' to make 'squad' use your local build?`);
|
|
131
|
+
const { createInterface } = await import('node:readline');
|
|
132
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
133
|
+
const answer = await new Promise((resolve) => {
|
|
134
|
+
rl.question('\n[Y/n] ', (ans) => {
|
|
135
|
+
rl.close();
|
|
136
|
+
resolve(ans.trim().toLowerCase());
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
if (answer === '' || answer === 'y' || answer === 'yes') {
|
|
140
|
+
console.log('Linking...');
|
|
141
|
+
try {
|
|
142
|
+
execSync('npm link -w packages/squad-cli', {
|
|
143
|
+
cwd: repoRoot,
|
|
144
|
+
encoding: 'utf8',
|
|
145
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
146
|
+
});
|
|
147
|
+
console.log('✓ Linked successfully. The "squad" command now uses your local build.\n');
|
|
148
|
+
}
|
|
149
|
+
catch (linkErr) {
|
|
150
|
+
debugLog('npm link failed:', linkErr);
|
|
151
|
+
console.log('Link failed — you can run it manually: npm link -w packages/squad-cli\n');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
if (!fs.existsSync(squadDir)) {
|
|
156
|
+
fs.mkdirSync(squadDir, { recursive: true });
|
|
157
|
+
}
|
|
158
|
+
fs.writeFileSync(markerPath, '# Created by squad CLI. Delete this file to be prompted again.\n');
|
|
159
|
+
console.log("Got it — won't ask again. Run 'npm link -w packages/squad-cli' manually anytime.\n");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
debugLog('Auto-link check failed, skipping:', err);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/** Per-command help text. Returns undefined for unknown commands. */
|
|
167
|
+
function getCommandHelp(cmd) {
|
|
168
|
+
const help = {
|
|
169
|
+
init: `
|
|
170
|
+
${BOLD}squad init${RESET} — Initialize Squad
|
|
171
|
+
|
|
172
|
+
${BOLD}USAGE${RESET}
|
|
173
|
+
squad init [prompt] [--file <path>] [--global] [--mode remote <path>]
|
|
174
|
+
|
|
175
|
+
${BOLD}DESCRIPTION${RESET}
|
|
176
|
+
Creates the .squad/ directory structure in your project or personal directory.
|
|
177
|
+
Detects your project type and scaffolds appropriate workflows and templates.
|
|
178
|
+
If a prompt is provided, stores it so the REPL can auto-cast your team.
|
|
179
|
+
|
|
180
|
+
${BOLD}OPTIONS${RESET}
|
|
181
|
+
prompt A project description (quoted string) for team casting
|
|
182
|
+
--file <path> Read the project description from a file
|
|
183
|
+
--global Create personal squad at ~/.squad/
|
|
184
|
+
--mode remote <path> Link to a remote team root (dual-root mode)
|
|
185
|
+
|
|
186
|
+
${BOLD}EXAMPLES${RESET}
|
|
187
|
+
${DIM}# Initialize and describe your project for auto-casting${RESET}
|
|
188
|
+
squad init "Build a snake game in HTML and JavaScript"
|
|
189
|
+
|
|
190
|
+
${DIM}# Initialize with a spec file${RESET}
|
|
191
|
+
squad init --file ./PROJECT.md
|
|
192
|
+
|
|
193
|
+
${DIM}# Initialize without a prompt (cast later interactively)${RESET}
|
|
194
|
+
squad init
|
|
195
|
+
|
|
196
|
+
${DIM}# Create personal squad${RESET}
|
|
197
|
+
squad init --global
|
|
198
|
+
|
|
199
|
+
${DIM}# Link to shared team in parent directory${RESET}
|
|
200
|
+
squad init --mode remote ../team-repo
|
|
201
|
+
`,
|
|
202
|
+
upgrade: `
|
|
203
|
+
${BOLD}squad upgrade${RESET} — Update Squad Files
|
|
204
|
+
|
|
205
|
+
${BOLD}USAGE${RESET}
|
|
206
|
+
squad upgrade [--global] [--migrate-directory]
|
|
207
|
+
|
|
208
|
+
${BOLD}DESCRIPTION${RESET}
|
|
209
|
+
Updates Squad-owned files to the latest version. Your team state
|
|
210
|
+
(team.md, decisions.md, agent history) is never modified.
|
|
211
|
+
|
|
212
|
+
${BOLD}OPTIONS${RESET}
|
|
213
|
+
--global Upgrade personal squad at ~/.squad/
|
|
214
|
+
--migrate-directory Rename .ai-team/ to .squad/ (legacy migration)
|
|
215
|
+
|
|
216
|
+
${BOLD}EXAMPLES${RESET}
|
|
217
|
+
${DIM}# Upgrade current repo's Squad files${RESET}
|
|
218
|
+
squad upgrade
|
|
219
|
+
|
|
220
|
+
${DIM}# Upgrade personal squad${RESET}
|
|
221
|
+
squad upgrade --global
|
|
222
|
+
|
|
223
|
+
${DIM}# Migrate from legacy .ai-team/ directory${RESET}
|
|
224
|
+
squad upgrade --migrate-directory
|
|
225
|
+
`,
|
|
226
|
+
status: `
|
|
227
|
+
${BOLD}squad status${RESET} — Show Active Squad
|
|
228
|
+
|
|
229
|
+
${BOLD}USAGE${RESET}
|
|
230
|
+
squad status
|
|
231
|
+
|
|
232
|
+
${BOLD}DESCRIPTION${RESET}
|
|
233
|
+
Displays which squad is currently active and where it's located.
|
|
234
|
+
Shows repo squad, personal squad path, and resolution order.
|
|
235
|
+
|
|
236
|
+
${BOLD}EXAMPLES${RESET}
|
|
237
|
+
${DIM}# Check active squad${RESET}
|
|
238
|
+
squad status
|
|
239
|
+
`,
|
|
240
|
+
triage: `
|
|
241
|
+
${BOLD}squad triage${RESET} — Auto-Triage Issues
|
|
242
|
+
|
|
243
|
+
${BOLD}USAGE${RESET}
|
|
244
|
+
squad triage [--interval <minutes>]
|
|
245
|
+
squad watch [--interval <minutes>] ${DIM}(alias)${RESET}
|
|
246
|
+
|
|
247
|
+
${BOLD}DESCRIPTION${RESET}
|
|
248
|
+
Ralph's work monitor. Continuously polls GitHub issues and automatically
|
|
249
|
+
assigns them to the right team member based on content and expertise.
|
|
250
|
+
|
|
251
|
+
${BOLD}OPTIONS${RESET}
|
|
252
|
+
--interval <minutes> Polling frequency (default: 10)
|
|
253
|
+
|
|
254
|
+
${BOLD}EXAMPLES${RESET}
|
|
255
|
+
${DIM}# Start triage with default 10-minute interval${RESET}
|
|
256
|
+
squad triage
|
|
257
|
+
|
|
258
|
+
${DIM}# Poll every 5 minutes${RESET}
|
|
259
|
+
squad triage --interval 5
|
|
260
|
+
`,
|
|
261
|
+
copilot: `
|
|
262
|
+
${BOLD}squad copilot${RESET} — Manage Copilot Coding Agent
|
|
263
|
+
|
|
264
|
+
${BOLD}USAGE${RESET}
|
|
265
|
+
squad copilot [--off] [--auto-assign]
|
|
266
|
+
|
|
267
|
+
${BOLD}DESCRIPTION${RESET}
|
|
268
|
+
Add or remove the GitHub Copilot coding agent (@copilot) from your team.
|
|
269
|
+
When enabled, @copilot can pick up issues labeled 'squad:copilot'.
|
|
270
|
+
|
|
271
|
+
${BOLD}OPTIONS${RESET}
|
|
272
|
+
--off Remove @copilot from the team
|
|
273
|
+
--auto-assign Enable automatic issue assignment for @copilot
|
|
274
|
+
|
|
275
|
+
${BOLD}EXAMPLES${RESET}
|
|
276
|
+
${DIM}# Add @copilot to the team${RESET}
|
|
277
|
+
squad copilot
|
|
278
|
+
|
|
279
|
+
${DIM}# Add @copilot with auto-assignment enabled${RESET}
|
|
280
|
+
squad copilot --auto-assign
|
|
281
|
+
|
|
282
|
+
${DIM}# Remove @copilot from the team${RESET}
|
|
283
|
+
squad copilot --off
|
|
284
|
+
`,
|
|
285
|
+
plugin: `
|
|
286
|
+
${BOLD}squad plugin${RESET} — Manage Plugin Marketplaces
|
|
287
|
+
|
|
288
|
+
${BOLD}USAGE${RESET}
|
|
289
|
+
squad plugin marketplace add <owner/repo>
|
|
290
|
+
squad plugin marketplace remove <name>
|
|
291
|
+
squad plugin marketplace list
|
|
292
|
+
squad plugin marketplace browse <name>
|
|
293
|
+
|
|
294
|
+
${BOLD}DESCRIPTION${RESET}
|
|
295
|
+
Manage plugin marketplaces — registries of Squad extensions, skills,
|
|
296
|
+
and agent templates.
|
|
297
|
+
|
|
298
|
+
${BOLD}EXAMPLES${RESET}
|
|
299
|
+
${DIM}# Add official Squad plugins marketplace${RESET}
|
|
300
|
+
squad plugin marketplace add bradygaster/squad-plugins
|
|
301
|
+
|
|
302
|
+
${DIM}# List all registered marketplaces${RESET}
|
|
303
|
+
squad plugin marketplace list
|
|
304
|
+
`,
|
|
305
|
+
export: `
|
|
306
|
+
${BOLD}squad export${RESET} — Export Squad to JSON
|
|
307
|
+
|
|
308
|
+
${BOLD}USAGE${RESET}
|
|
309
|
+
squad export [--out <path>]
|
|
310
|
+
|
|
311
|
+
${BOLD}DESCRIPTION${RESET}
|
|
312
|
+
Creates a portable JSON snapshot of your entire squad — casting state,
|
|
313
|
+
agent charters, accumulated history, and skills.
|
|
314
|
+
|
|
315
|
+
${BOLD}OPTIONS${RESET}
|
|
316
|
+
--out <path> Custom output path (default: squad-export.json)
|
|
317
|
+
|
|
318
|
+
${BOLD}EXAMPLES${RESET}
|
|
319
|
+
${DIM}# Export to default file${RESET}
|
|
320
|
+
squad export
|
|
321
|
+
|
|
322
|
+
${DIM}# Export to custom location${RESET}
|
|
323
|
+
squad export --out ./backups/team-backup.json
|
|
324
|
+
`,
|
|
325
|
+
import: `
|
|
326
|
+
${BOLD}squad import${RESET} — Import Squad from JSON
|
|
327
|
+
|
|
328
|
+
${BOLD}USAGE${RESET}
|
|
329
|
+
squad import <file> [--force]
|
|
330
|
+
|
|
331
|
+
${BOLD}DESCRIPTION${RESET}
|
|
332
|
+
Restores a squad from a JSON export file. Creates .squad/ directory
|
|
333
|
+
and populates it with casting state, agents, skills, and history.
|
|
334
|
+
|
|
335
|
+
${BOLD}OPTIONS${RESET}
|
|
336
|
+
--force Overwrite existing squad (archives old .squad/ first)
|
|
337
|
+
|
|
338
|
+
${BOLD}EXAMPLES${RESET}
|
|
339
|
+
${DIM}# Import into current directory${RESET}
|
|
340
|
+
squad import squad-export.json
|
|
341
|
+
|
|
342
|
+
${DIM}# Import and overwrite existing squad${RESET}
|
|
343
|
+
squad import squad-export.json --force
|
|
344
|
+
`,
|
|
345
|
+
'scrub-emails': `
|
|
346
|
+
${BOLD}squad scrub-emails${RESET} — Remove Email Addresses
|
|
347
|
+
|
|
348
|
+
${BOLD}USAGE${RESET}
|
|
349
|
+
squad scrub-emails [directory]
|
|
350
|
+
|
|
351
|
+
${BOLD}DESCRIPTION${RESET}
|
|
352
|
+
Removes email addresses (PII) from Squad state files. Useful before
|
|
353
|
+
committing to public repos or sharing exports.
|
|
354
|
+
|
|
355
|
+
${BOLD}OPTIONS${RESET}
|
|
356
|
+
[directory] Target directory (default: .squad)
|
|
357
|
+
|
|
358
|
+
${BOLD}EXAMPLES${RESET}
|
|
359
|
+
${DIM}# Scrub current squad${RESET}
|
|
360
|
+
squad scrub-emails .squad
|
|
361
|
+
|
|
362
|
+
${DIM}# Scrub legacy .ai-team directory${RESET}
|
|
363
|
+
squad scrub-emails
|
|
364
|
+
`,
|
|
365
|
+
doctor: `
|
|
366
|
+
${BOLD}squad doctor${RESET} — Validate Squad Setup
|
|
367
|
+
|
|
368
|
+
${BOLD}USAGE${RESET}
|
|
369
|
+
squad doctor
|
|
370
|
+
|
|
371
|
+
${BOLD}DESCRIPTION${RESET}
|
|
372
|
+
Runs a 9-check diagnostic on your squad setup. Reports health of
|
|
373
|
+
expected files, conventions, and directory structure.
|
|
374
|
+
|
|
375
|
+
${BOLD}EXAMPLES${RESET}
|
|
376
|
+
${DIM}# Check current squad setup${RESET}
|
|
377
|
+
squad doctor
|
|
378
|
+
`,
|
|
379
|
+
link: `
|
|
380
|
+
${BOLD}squad link${RESET} — Link to Remote Team
|
|
381
|
+
|
|
382
|
+
${BOLD}USAGE${RESET}
|
|
383
|
+
squad link <team-repo-path>
|
|
384
|
+
|
|
385
|
+
${BOLD}DESCRIPTION${RESET}
|
|
386
|
+
Links the current project to a remote team root. Enables dual-root mode
|
|
387
|
+
where project-specific state lives in .squad/ and team identity lives
|
|
388
|
+
in a shared location.
|
|
389
|
+
|
|
390
|
+
${BOLD}EXAMPLES${RESET}
|
|
391
|
+
${DIM}# Link to parent directory's team${RESET}
|
|
392
|
+
squad link ../team-repo
|
|
393
|
+
|
|
394
|
+
${DIM}# Link to absolute path${RESET}
|
|
395
|
+
squad link /Users/org/shared-squad
|
|
396
|
+
`,
|
|
397
|
+
aspire: `
|
|
398
|
+
${BOLD}squad aspire${RESET} — Launch Aspire Dashboard
|
|
399
|
+
|
|
400
|
+
${BOLD}USAGE${RESET}
|
|
401
|
+
squad aspire [--docker] [--port <number>]
|
|
402
|
+
|
|
403
|
+
${BOLD}DESCRIPTION${RESET}
|
|
404
|
+
Launches the .NET Aspire dashboard for observability. Squad exports
|
|
405
|
+
OpenTelemetry traces and metrics to the dashboard for real-time visibility.
|
|
406
|
+
|
|
407
|
+
${BOLD}OPTIONS${RESET}
|
|
408
|
+
--docker Force Docker mode
|
|
409
|
+
--port <number> OTLP port (default: 4317)
|
|
410
|
+
|
|
411
|
+
${BOLD}EXAMPLES${RESET}
|
|
412
|
+
${DIM}# Launch Aspire dashboard (auto-detect runtime)${RESET}
|
|
413
|
+
squad aspire
|
|
414
|
+
|
|
415
|
+
${DIM}# Force Docker mode${RESET}
|
|
416
|
+
squad aspire --docker
|
|
417
|
+
`,
|
|
418
|
+
upstream: `
|
|
419
|
+
${BOLD}squad upstream${RESET} — Manage Upstream Squad Sources
|
|
420
|
+
|
|
421
|
+
${BOLD}USAGE${RESET}
|
|
422
|
+
squad upstream add <source> [--name <name>] [--ref <branch>]
|
|
423
|
+
squad upstream remove <name>
|
|
424
|
+
squad upstream list
|
|
425
|
+
squad upstream sync [name]
|
|
426
|
+
|
|
427
|
+
${BOLD}DESCRIPTION${RESET}
|
|
428
|
+
Manage upstream Squad sources — external configurations from local
|
|
429
|
+
directories, git repositories, or export files that can be synced
|
|
430
|
+
into your squad.
|
|
431
|
+
|
|
432
|
+
${BOLD}OPTIONS${RESET}
|
|
433
|
+
add <source> Add an upstream (path, git URL, or .json export)
|
|
434
|
+
--name <name> Custom name (auto-derived if omitted)
|
|
435
|
+
--ref <branch> Git branch/tag (default: main)
|
|
436
|
+
remove <name> Remove an upstream by name
|
|
437
|
+
list Show all configured upstreams
|
|
438
|
+
sync [name] Sync all or a specific upstream
|
|
439
|
+
|
|
440
|
+
${BOLD}EXAMPLES${RESET}
|
|
441
|
+
${DIM}# Add a git upstream${RESET}
|
|
442
|
+
squad upstream add https://github.com/org/squad-config.git
|
|
443
|
+
|
|
444
|
+
${DIM}# Add a local upstream with custom name${RESET}
|
|
445
|
+
squad upstream add ../shared-squad --name shared
|
|
446
|
+
|
|
447
|
+
${DIM}# Sync all upstreams${RESET}
|
|
448
|
+
squad upstream sync
|
|
449
|
+
`,
|
|
450
|
+
nap: `
|
|
451
|
+
${BOLD}squad nap${RESET} — Context Hygiene
|
|
452
|
+
|
|
453
|
+
${BOLD}USAGE${RESET}
|
|
454
|
+
squad nap [--deep] [--dry-run]
|
|
455
|
+
|
|
456
|
+
${BOLD}DESCRIPTION${RESET}
|
|
457
|
+
Compresses agent histories, prunes old logs, archives stale decisions,
|
|
458
|
+
and cleans up orphaned inbox files. Shows before/after stats with
|
|
459
|
+
estimated token savings.
|
|
460
|
+
|
|
461
|
+
${BOLD}OPTIONS${RESET}
|
|
462
|
+
--deep Aggressive mode — tighter history compression
|
|
463
|
+
--dry-run Show what would change without modifying files
|
|
464
|
+
|
|
465
|
+
${BOLD}EXAMPLES${RESET}
|
|
466
|
+
${DIM}# Run a standard nap${RESET}
|
|
467
|
+
squad nap
|
|
468
|
+
|
|
469
|
+
${DIM}# Preview changes without modifying files${RESET}
|
|
470
|
+
squad nap --dry-run
|
|
471
|
+
|
|
472
|
+
${DIM}# Deep clean for maximum compression${RESET}
|
|
473
|
+
squad nap --deep
|
|
474
|
+
`,
|
|
475
|
+
shell: `
|
|
476
|
+
${BOLD}squad shell${RESET} — Launch Interactive Shell
|
|
477
|
+
|
|
478
|
+
${BOLD}USAGE${RESET}
|
|
479
|
+
squad shell [--preview] [--timeout <seconds>]
|
|
480
|
+
|
|
481
|
+
${BOLD}DESCRIPTION${RESET}
|
|
482
|
+
Starts the interactive Squad REPL. Messages are routed to the
|
|
483
|
+
right agent automatically based on content and team expertise.
|
|
484
|
+
|
|
485
|
+
${BOLD}OPTIONS${RESET}
|
|
486
|
+
--preview Show team summary without launching shell
|
|
487
|
+
--timeout <seconds> Set REPL inactivity timeout (default: 600)
|
|
488
|
+
|
|
489
|
+
${BOLD}EXAMPLES${RESET}
|
|
490
|
+
${DIM}# Start interactive shell${RESET}
|
|
491
|
+
squad shell
|
|
492
|
+
|
|
493
|
+
${DIM}# Preview team before launching${RESET}
|
|
494
|
+
squad shell --preview
|
|
495
|
+
`,
|
|
496
|
+
};
|
|
497
|
+
// Handle aliases
|
|
498
|
+
const aliases = {
|
|
499
|
+
watch: 'triage',
|
|
500
|
+
loop: 'triage',
|
|
501
|
+
hire: 'init',
|
|
502
|
+
heartbeat: 'doctor',
|
|
503
|
+
};
|
|
504
|
+
const key = aliases[cmd] ?? cmd;
|
|
505
|
+
return help[key];
|
|
506
|
+
}
|
|
18
507
|
async function main() {
|
|
19
508
|
const args = process.argv.slice(2);
|
|
20
509
|
const hasGlobal = args.includes('--global');
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
510
|
+
const rawCmd = args[0];
|
|
511
|
+
const cmd = rawCmd?.trim() || undefined;
|
|
512
|
+
// --timeout <seconds> → set SQUAD_REPL_TIMEOUT env var for shell
|
|
513
|
+
const timeoutIdx = args.indexOf('--timeout');
|
|
514
|
+
if (timeoutIdx >= 0 && args[timeoutIdx + 1]) {
|
|
515
|
+
process.env['SQUAD_REPL_TIMEOUT'] = args[timeoutIdx + 1];
|
|
516
|
+
}
|
|
517
|
+
// Dev convenience: offer to npm-link when running from source
|
|
518
|
+
await checkAutoLink(cmd, args);
|
|
519
|
+
// Empty or whitespace-only args should show help, not launch shell
|
|
520
|
+
if (rawCmd !== undefined && !cmd) {
|
|
521
|
+
console.log(`\n${BOLD}squad${RESET} v${VERSION}`);
|
|
522
|
+
console.log(`Your AI agent team\n`);
|
|
523
|
+
console.log(`Usage: squad [command] [options]`);
|
|
524
|
+
console.log(`\nRun 'squad help' for full command list.\n`);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
// --version / -v / version — canonical format: bare semver
|
|
528
|
+
if (cmd === '--version' || cmd === '-v' || cmd === 'version') {
|
|
529
|
+
console.log(VERSION);
|
|
25
530
|
return;
|
|
26
531
|
}
|
|
27
532
|
// --help / -h / help
|
|
28
533
|
if (cmd === '--help' || cmd === '-h' || cmd === 'help') {
|
|
29
|
-
console.log(`\n${BOLD}squad${RESET} v${VERSION}
|
|
534
|
+
console.log(`\n${BOLD}squad${RESET} v${VERSION}`);
|
|
535
|
+
console.log(`Your AI agent team\n`);
|
|
536
|
+
console.log(`${BOLD}Just type — squad routes your message to the right agent automatically${RESET}`);
|
|
537
|
+
console.log(` squad Start interactive shell`);
|
|
538
|
+
console.log(` squad --global Use your personal squad\n`);
|
|
30
539
|
console.log(`Usage: squad [command] [options]\n`);
|
|
31
|
-
console.log(`
|
|
32
|
-
console.log(` ${BOLD}
|
|
33
|
-
console.log(`
|
|
34
|
-
console.log(`
|
|
35
|
-
console.log(`
|
|
36
|
-
console.log(`
|
|
37
|
-
console.log(`
|
|
38
|
-
console.log(`
|
|
39
|
-
console.log(`
|
|
40
|
-
console.log(`
|
|
41
|
-
console.log(` ${BOLD}
|
|
42
|
-
console.log(
|
|
43
|
-
console.log(`
|
|
44
|
-
console.log(`
|
|
45
|
-
console.log(` ${BOLD}
|
|
46
|
-
console.log(`
|
|
47
|
-
console.log(`
|
|
48
|
-
console.log(` ${BOLD}
|
|
49
|
-
console.log(`
|
|
50
|
-
console.log(` ${BOLD}
|
|
51
|
-
console.log(`
|
|
52
|
-
console.log(
|
|
53
|
-
console.log(`
|
|
54
|
-
console.log(`
|
|
55
|
-
console.log(`
|
|
56
|
-
console.log(` ${BOLD}
|
|
57
|
-
console.log(`
|
|
58
|
-
console.log(` ${BOLD}
|
|
59
|
-
console.log(`
|
|
60
|
-
console.log(` ${BOLD}
|
|
61
|
-
console.log(`
|
|
62
|
-
console.log(`
|
|
63
|
-
console.log(`
|
|
64
|
-
console.log(`
|
|
65
|
-
console.log(` ${BOLD}
|
|
66
|
-
console.log(
|
|
67
|
-
console.log(
|
|
68
|
-
console.log(`
|
|
69
|
-
console.log(`
|
|
540
|
+
console.log(`${BOLD}Getting Started${RESET}`);
|
|
541
|
+
console.log(` ${BOLD}init${RESET} Create .squad/ in this repo ${DIM}(alias: hire)${RESET}`);
|
|
542
|
+
console.log(` --global Create personal squad directory`);
|
|
543
|
+
console.log(` --mode remote <path> Link to a remote team root`);
|
|
544
|
+
console.log(` ${BOLD}upgrade${RESET} Update Squad files to latest`);
|
|
545
|
+
console.log(` --global Upgrade personal squad`);
|
|
546
|
+
console.log(` --migrate-directory Rename .ai-team/ → .squad/`);
|
|
547
|
+
console.log(` ${BOLD}status${RESET} Show which squad is active`);
|
|
548
|
+
console.log(` ${BOLD}doctor${RESET} Check your setup ${DIM}(alias: heartbeat)${RESET}`);
|
|
549
|
+
console.log(`\n${BOLD}Development${RESET}`);
|
|
550
|
+
console.log(` ${BOLD}shell${RESET} Launch interactive shell`);
|
|
551
|
+
console.log(`\n${BOLD}Team Management${RESET}`);
|
|
552
|
+
console.log(` ${BOLD}triage${RESET} Watch issues and auto-triage ${DIM}(alias: watch, loop)${RESET}`);
|
|
553
|
+
console.log(` --interval <minutes> Polling frequency (default: 10)`);
|
|
554
|
+
console.log(` ${BOLD}copilot${RESET} Add/remove GitHub Copilot agent`);
|
|
555
|
+
console.log(` --off Remove @copilot`);
|
|
556
|
+
console.log(` --auto-assign Enable auto-assignment`);
|
|
557
|
+
console.log(` ${BOLD}link${RESET} Connect to a remote team`);
|
|
558
|
+
console.log(` <team-repo-path>`);
|
|
559
|
+
console.log(` ${BOLD}upstream${RESET} Manage upstream Squad sources`);
|
|
560
|
+
console.log(` add|remove|list|sync`);
|
|
561
|
+
console.log(`\n${BOLD}Utilities${RESET}`);
|
|
562
|
+
console.log(` ${BOLD}nap${RESET} Context hygiene — compress, prune, archive`);
|
|
563
|
+
console.log(` --deep Aggressive compression`);
|
|
564
|
+
console.log(` --dry-run Preview without changes`);
|
|
565
|
+
console.log(` ${BOLD}export${RESET} Save squad to JSON`);
|
|
566
|
+
console.log(` --out <path> Output path (default: squad-export.json)`);
|
|
567
|
+
console.log(` ${BOLD}import${RESET} Load squad from JSON`);
|
|
568
|
+
console.log(` <file> [--force]`);
|
|
569
|
+
console.log(` ${BOLD}plugin${RESET} Manage plugins`);
|
|
570
|
+
console.log(` marketplace add|remove|list|browse`);
|
|
571
|
+
console.log(` ${BOLD}aspire${RESET} Open Aspire dashboard`);
|
|
572
|
+
console.log(` --docker Force Docker mode`);
|
|
573
|
+
console.log(` --port <number> OTLP port (default: 4317)`);
|
|
574
|
+
console.log(` ${BOLD}scrub-emails${RESET} Remove email addresses from squad state`);
|
|
575
|
+
console.log(` [directory] Target directory (default: .squad/)`);
|
|
576
|
+
console.log(`\n${BOLD}Flags${RESET}`);
|
|
577
|
+
console.log(` --version, -v Print version`);
|
|
578
|
+
console.log(` --help, -h Show this help`);
|
|
579
|
+
console.log(` --preview Show team summary without launching shell`);
|
|
580
|
+
console.log(` --global Use personal squad path`);
|
|
581
|
+
console.log(` --timeout <seconds> Set REPL inactivity timeout (default: 600)`);
|
|
582
|
+
console.log(`\n${BOLD}Examples${RESET}`);
|
|
583
|
+
console.log(` ${DIM}$ squad init${RESET} Set up a squad in this repo`);
|
|
584
|
+
console.log(` ${DIM}$ squad triage --interval 5${RESET} Poll issues every 5 minutes`);
|
|
585
|
+
console.log(` ${DIM}$ squad export --out ./backups/team.json${RESET}`);
|
|
586
|
+
console.log(` Save squad snapshot`);
|
|
587
|
+
console.log(` ${DIM}$ squad copilot --auto-assign${RESET} Add @copilot with auto-assignment`);
|
|
588
|
+
console.log(` ${DIM}$ squad doctor${RESET} Diagnose setup issues`);
|
|
70
589
|
console.log(`\nInstallation:`);
|
|
71
|
-
console.log(` npm
|
|
590
|
+
console.log(` npm i --save-dev @bradygaster/squad-cli`);
|
|
72
591
|
console.log(`\nInsider channel:`);
|
|
73
|
-
console.log(` npm
|
|
592
|
+
console.log(` npm i --save-dev @bradygaster/squad-cli@insider`);
|
|
593
|
+
console.log(`\nRun 'squad <command> --help' for details.\n`);
|
|
74
594
|
return;
|
|
75
595
|
}
|
|
76
|
-
//
|
|
596
|
+
// --preview / --dry-run — show team summary without launching the interactive shell
|
|
597
|
+
if (cmd === '--preview' || cmd === '--dry-run' || (!cmd && args.includes('--preview')) || (cmd === 'shell' && args.includes('--preview'))) {
|
|
598
|
+
const squadDir = resolveSquad(process.cwd());
|
|
599
|
+
const globalPath = resolveGlobalSquadPath();
|
|
600
|
+
const globalSquadDir = path.join(globalPath, '.squad');
|
|
601
|
+
const teamRoot = squadDir ? path.dirname(squadDir) : fs.existsSync(globalSquadDir) ? globalPath : null;
|
|
602
|
+
if (!teamRoot) {
|
|
603
|
+
console.log(`${RED}✗${RESET} No squad found. Run 'squad init' first.`);
|
|
604
|
+
process.exit(1);
|
|
605
|
+
}
|
|
606
|
+
const data = loadWelcomeData(teamRoot);
|
|
607
|
+
if (!data) {
|
|
608
|
+
console.log(`${RED}✗${RESET} Could not read team configuration.`);
|
|
609
|
+
process.exit(1);
|
|
610
|
+
}
|
|
611
|
+
console.log(`\n${BOLD}Squad Preview${RESET}`);
|
|
612
|
+
console.log(`${'─'.repeat(40)}`);
|
|
613
|
+
console.log(` Team: ${data.projectName}`);
|
|
614
|
+
console.log(` Agents: ${data.agents.length}`);
|
|
615
|
+
if (data.description)
|
|
616
|
+
console.log(` About: ${data.description}`);
|
|
617
|
+
if (data.focus)
|
|
618
|
+
console.log(` Focus: ${data.focus}`);
|
|
619
|
+
console.log(`\n${BOLD}Agents${RESET}`);
|
|
620
|
+
for (const a of data.agents) {
|
|
621
|
+
console.log(` ${a.emoji} ${BOLD}${a.name}${RESET} — ${a.role}`);
|
|
622
|
+
}
|
|
623
|
+
console.log(`\n${BOLD}Shell Commands${RESET}`);
|
|
624
|
+
console.log(` /status — Check your team & what's happening`);
|
|
625
|
+
console.log(` /history — See recent messages`);
|
|
626
|
+
console.log(` /agents — List all team members`);
|
|
627
|
+
console.log(` /sessions — List saved sessions`);
|
|
628
|
+
console.log(` /resume — Restore a past session`);
|
|
629
|
+
console.log(` /clear — Clear the screen`);
|
|
630
|
+
console.log(` /quit — Exit`);
|
|
631
|
+
console.log(`\n${DIM}Run 'squad' to start the interactive shell.${RESET}\n`);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
// No args → check if .squad/ exists
|
|
77
635
|
if (!cmd) {
|
|
78
|
-
|
|
636
|
+
const squadPath = resolveSquad(process.cwd());
|
|
637
|
+
const globalPath = resolveGlobalSquadPath();
|
|
638
|
+
const globalSquadDir = path.join(globalPath, '.squad');
|
|
639
|
+
const hasSquad = squadPath || fs.existsSync(globalSquadDir);
|
|
640
|
+
if (hasSquad) {
|
|
641
|
+
// Squad exists, launch shell
|
|
642
|
+
await runShell();
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
// First run — no squad found. Welcome the user.
|
|
646
|
+
console.log(`\n${BOLD}Welcome to Squad${RESET} v${VERSION}`);
|
|
647
|
+
console.log(`Your AI agent team\n`);
|
|
648
|
+
console.log(`Squad adds a team of AI agents to your project. Each agent`);
|
|
649
|
+
console.log(`has a role — architect, tester, security reviewer — and they`);
|
|
650
|
+
console.log(`collaborate to help you build, review, and ship code.\n`);
|
|
651
|
+
console.log(`${BOLD}Get started:${RESET}`);
|
|
652
|
+
console.log(` ${BOLD}squad init${RESET} Set up a squad in this repo`);
|
|
653
|
+
console.log(` ${BOLD}squad init --global${RESET} Create a personal squad\n`);
|
|
654
|
+
console.log(`${DIM}After init, just run ${BOLD}squad${RESET}${DIM} to start talking to your team.${RESET}`);
|
|
655
|
+
console.log(`${DIM}Run ${BOLD}squad help${RESET}${DIM} for all commands.${RESET}\n`);
|
|
656
|
+
}
|
|
79
657
|
return;
|
|
80
658
|
}
|
|
659
|
+
// Per-command --help/-h: intercept before dispatching (fixes #511, #512)
|
|
660
|
+
if (hasHelpFlag(args)) {
|
|
661
|
+
const helpText = getCommandHelp(cmd);
|
|
662
|
+
if (helpText) {
|
|
663
|
+
console.log(helpText);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
81
667
|
// Route subcommands
|
|
82
|
-
|
|
668
|
+
// hire → alias for init (#501)
|
|
669
|
+
if (cmd === 'init' || cmd === 'hire') {
|
|
83
670
|
const modeIdx = args.indexOf('--mode');
|
|
84
671
|
const mode = (modeIdx !== -1 && args[modeIdx + 1]) ? args[modeIdx + 1] : 'local';
|
|
85
672
|
const dest = hasGlobal ? resolveGlobalSquadPath() : process.cwd();
|
|
@@ -88,18 +675,35 @@ async function main() {
|
|
|
88
675
|
// teamRoot can be provided as the next positional arg after --mode remote
|
|
89
676
|
const teamRootArg = args.find((a, i) => i > 0 && a !== '--mode' && a !== 'remote' && a !== '--global' && a !== 'init');
|
|
90
677
|
if (!teamRootArg) {
|
|
91
|
-
fatal('
|
|
678
|
+
fatal('squad init --mode remote <team-root-path>');
|
|
92
679
|
}
|
|
93
680
|
writeRemoteConfig(dest, teamRootArg);
|
|
94
681
|
}
|
|
95
|
-
|
|
682
|
+
// Extract project prompt: squad init "Build a snake game" or squad init --file ./spec.txt
|
|
683
|
+
let initPrompt;
|
|
684
|
+
const fileIdx = args.indexOf('--file');
|
|
685
|
+
if (fileIdx !== -1 && args[fileIdx + 1]) {
|
|
686
|
+
const filePath = resolve(args[fileIdx + 1]);
|
|
687
|
+
if (!existsSync(filePath)) {
|
|
688
|
+
fatal(`Prompt file not found: ${filePath}`);
|
|
689
|
+
}
|
|
690
|
+
initPrompt = readFileSync(filePath, 'utf-8').trim();
|
|
691
|
+
}
|
|
692
|
+
else {
|
|
693
|
+
// Look for a positional string arg (not a flag, not 'init'/'hire')
|
|
694
|
+
const skipSet = new Set(['init', 'hire', '--global', '--mode', mode]);
|
|
695
|
+
const positional = args.find((a, i) => i > 0 && !a.startsWith('--') && !skipSet.has(a));
|
|
696
|
+
if (positional)
|
|
697
|
+
initPrompt = positional;
|
|
698
|
+
}
|
|
699
|
+
await runInit(dest, { prompt: initPrompt });
|
|
96
700
|
return;
|
|
97
701
|
}
|
|
98
702
|
if (cmd === 'link') {
|
|
99
703
|
const { runLink } = await import('./cli/commands/link.js');
|
|
100
704
|
const linkTarget = args[1];
|
|
101
705
|
if (!linkTarget) {
|
|
102
|
-
fatal('
|
|
706
|
+
fatal('Run: squad link <team-repo-path>');
|
|
103
707
|
}
|
|
104
708
|
runLink(process.cwd(), linkTarget);
|
|
105
709
|
return;
|
|
@@ -108,7 +712,6 @@ async function main() {
|
|
|
108
712
|
const { runUpgrade } = await import('./cli/core/upgrade.js');
|
|
109
713
|
const { migrateDirectory } = await import('./cli/core/migrate-directory.js');
|
|
110
714
|
const migrateDir = args.includes('--migrate-directory');
|
|
111
|
-
const selfUpgrade = args.includes('--self');
|
|
112
715
|
const dest = hasGlobal ? resolveGlobalSquadPath() : process.cwd();
|
|
113
716
|
// Handle --migrate-directory flag
|
|
114
717
|
if (migrateDir) {
|
|
@@ -118,40 +721,26 @@ async function main() {
|
|
|
118
721
|
// Run upgrade
|
|
119
722
|
await runUpgrade(dest, {
|
|
120
723
|
migrateDirectory: migrateDir,
|
|
121
|
-
self: selfUpgrade
|
|
122
724
|
});
|
|
123
725
|
return;
|
|
124
726
|
}
|
|
125
|
-
if (cmd === '
|
|
126
|
-
|
|
727
|
+
if (cmd === 'nap') {
|
|
728
|
+
const { runNap, formatNapReport } = await import('./cli/core/nap.js');
|
|
729
|
+
const squadDir = path.join(hasGlobal ? resolveGlobalSquadPath() : process.cwd(), '.squad');
|
|
730
|
+
const deep = args.includes('--deep');
|
|
731
|
+
const dryRun = args.includes('--dry-run');
|
|
732
|
+
const result = await runNap({ squadDir, deep, dryRun });
|
|
733
|
+
console.log(formatNapReport(result, !!process.env['NO_COLOR']));
|
|
127
734
|
return;
|
|
128
735
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
736
|
+
// loop → alias for triage (#509)
|
|
737
|
+
if (cmd === 'triage' || cmd === 'watch' || cmd === 'loop') {
|
|
738
|
+
const { runWatch } = await import('./cli/commands/watch.js');
|
|
132
739
|
const intervalIdx = args.indexOf('--interval');
|
|
133
740
|
const intervalMinutes = (intervalIdx !== -1 && args[intervalIdx + 1])
|
|
134
741
|
? parseInt(args[intervalIdx + 1], 10)
|
|
135
742
|
: 10;
|
|
136
|
-
|
|
137
|
-
if (filter) {
|
|
138
|
-
console.log(` Filter: ${filter}`);
|
|
139
|
-
}
|
|
140
|
-
console.log(` Interval: ${intervalMinutes} minutes`);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (cmd === 'hire') {
|
|
144
|
-
const nameIdx = args.indexOf('--name');
|
|
145
|
-
const name = (nameIdx !== -1 && args[nameIdx + 1]) ? args[nameIdx + 1] : undefined;
|
|
146
|
-
const roleIdx = args.indexOf('--role');
|
|
147
|
-
const role = (roleIdx !== -1 && args[roleIdx + 1]) ? args[roleIdx + 1] : undefined;
|
|
148
|
-
console.log('👋 Squad hire — team creation wizard starting... (full implementation pending)');
|
|
149
|
-
if (name) {
|
|
150
|
-
console.log(` Name: ${name}`);
|
|
151
|
-
}
|
|
152
|
-
if (role) {
|
|
153
|
-
console.log(` Role: ${role}`);
|
|
154
|
-
}
|
|
743
|
+
await runWatch(process.cwd(), intervalMinutes);
|
|
155
744
|
return;
|
|
156
745
|
}
|
|
157
746
|
if (cmd === 'export') {
|
|
@@ -165,7 +754,7 @@ async function main() {
|
|
|
165
754
|
const { runImport } = await import('./cli/commands/import.js');
|
|
166
755
|
const importFile = args[1];
|
|
167
756
|
if (!importFile) {
|
|
168
|
-
fatal('
|
|
757
|
+
fatal('Run: squad import <file> [--force]');
|
|
169
758
|
}
|
|
170
759
|
const hasForce = args.includes('--force');
|
|
171
760
|
await runImport(process.cwd(), importFile, hasForce);
|
|
@@ -185,13 +774,13 @@ async function main() {
|
|
|
185
774
|
}
|
|
186
775
|
if (cmd === 'scrub-emails') {
|
|
187
776
|
const { scrubEmails } = await import('./cli/core/email-scrub.js');
|
|
188
|
-
const targetDir = args[1] || '.
|
|
777
|
+
const targetDir = args[1] || '.squad';
|
|
189
778
|
const count = await scrubEmails(targetDir);
|
|
190
779
|
if (count > 0) {
|
|
191
|
-
console.log(`Scrubbed ${count} email
|
|
780
|
+
console.log(`Scrubbed ${count} email${count !== 1 ? 's' : ''}.`);
|
|
192
781
|
}
|
|
193
782
|
else {
|
|
194
|
-
console.log('No
|
|
783
|
+
console.log('No emails found.');
|
|
195
784
|
}
|
|
196
785
|
return;
|
|
197
786
|
}
|
|
@@ -203,7 +792,13 @@ async function main() {
|
|
|
203
792
|
await runAspire({ docker: useDocker, port });
|
|
204
793
|
return;
|
|
205
794
|
}
|
|
206
|
-
if (cmd === '
|
|
795
|
+
if (cmd === 'upstream') {
|
|
796
|
+
const { upstreamCommand } = await import('./cli/commands/upstream.js');
|
|
797
|
+
await upstreamCommand(args.slice(1));
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
// heartbeat → alias for doctor (#503)
|
|
801
|
+
if (cmd === 'doctor' || cmd === 'heartbeat') {
|
|
207
802
|
const { doctorCommand } = await import('./cli/commands/doctor.js');
|
|
208
803
|
await doctorCommand(process.cwd());
|
|
209
804
|
return;
|
|
@@ -215,36 +810,46 @@ async function main() {
|
|
|
215
810
|
const globalExists = fs.existsSync(globalSquadDir);
|
|
216
811
|
console.log(`\n${BOLD}Squad Status${RESET}\n`);
|
|
217
812
|
if (repoSquad) {
|
|
218
|
-
console.log(`
|
|
219
|
-
console.log(` Path:
|
|
220
|
-
console.log(` Reason: Found .squad/ in repository tree`);
|
|
813
|
+
console.log(` Here: ${BOLD}repo${RESET} (in .squad/)`);
|
|
814
|
+
console.log(` Path: ${repoSquad}`);
|
|
221
815
|
}
|
|
222
816
|
else if (globalExists) {
|
|
223
|
-
console.log(`
|
|
224
|
-
console.log(` Path:
|
|
225
|
-
console.log(` Reason: No repo .squad/ found; personal squad exists at global path`);
|
|
817
|
+
console.log(` Here: ${BOLD}personal${RESET} (global)`);
|
|
818
|
+
console.log(` Path: ${globalSquadDir}`);
|
|
226
819
|
}
|
|
227
820
|
else {
|
|
228
|
-
console.log(`
|
|
229
|
-
console.log(`
|
|
821
|
+
console.log(` Here: ${DIM}none${RESET}`);
|
|
822
|
+
console.log(` Hint: Run 'squad init' to get started`);
|
|
230
823
|
}
|
|
231
824
|
console.log();
|
|
232
|
-
console.log(` ${DIM}Repo
|
|
233
|
-
console.log(` ${DIM}Global
|
|
234
|
-
console.log(` ${DIM}Global squad: ${globalExists ? globalSquadDir : 'not initialized'}${RESET}`);
|
|
825
|
+
console.log(` ${DIM}Repo squad: ${repoSquad ?? 'not found'}${RESET}`);
|
|
826
|
+
console.log(` ${DIM}Global: ${globalPath}${RESET}`);
|
|
235
827
|
console.log();
|
|
236
828
|
return;
|
|
237
829
|
}
|
|
830
|
+
// shell → explicit REPL launch (#507)
|
|
831
|
+
if (cmd === 'shell') {
|
|
832
|
+
await runShell();
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
238
835
|
// Unknown command
|
|
239
|
-
fatal(`Unknown command: ${cmd}
|
|
836
|
+
fatal(`Unknown command: ${cmd}. Run 'squad help' for commands.`);
|
|
240
837
|
}
|
|
241
838
|
main().catch(err => {
|
|
839
|
+
debugLog('Fatal CLI error:', err);
|
|
242
840
|
if (err instanceof SquadError) {
|
|
243
841
|
console.error(`${RED}✗${RESET} ${err.message}`);
|
|
244
842
|
}
|
|
245
843
|
else {
|
|
246
|
-
|
|
844
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
845
|
+
const friendly = msg.replace(/^Error:\s*/i, '');
|
|
846
|
+
console.error(`${RED}✗${RESET} ${friendly}`);
|
|
847
|
+
// Show stack trace only when SQUAD_DEBUG is enabled
|
|
848
|
+
if (process.env['SQUAD_DEBUG'] === '1' && err instanceof Error && err.stack) {
|
|
849
|
+
console.error(`\n${DIM}${err.stack}${RESET}`);
|
|
850
|
+
}
|
|
247
851
|
}
|
|
852
|
+
console.error(`\n${DIM}Tip: Run 'squad doctor' for help. Set SQUAD_DEBUG=1 for details.${RESET}`);
|
|
248
853
|
process.exit(1);
|
|
249
854
|
});
|
|
250
855
|
//# sourceMappingURL=cli-entry.js.map
|