@bradygaster/squad-cli 0.8.5 → 0.8.17-preview

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.
Files changed (153) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/commands/copilot-bridge.d.ts +42 -0
  3. package/dist/cli/commands/copilot-bridge.d.ts.map +1 -0
  4. package/dist/cli/commands/copilot-bridge.js +191 -0
  5. package/dist/cli/commands/copilot-bridge.js.map +1 -0
  6. package/dist/cli/commands/import.js.map +1 -1
  7. package/dist/cli/commands/rc-tunnel.d.ts +30 -0
  8. package/dist/cli/commands/rc-tunnel.d.ts.map +1 -0
  9. package/dist/cli/commands/rc-tunnel.js +107 -0
  10. package/dist/cli/commands/rc-tunnel.js.map +1 -0
  11. package/dist/cli/commands/rc.d.ts +13 -0
  12. package/dist/cli/commands/rc.d.ts.map +1 -0
  13. package/dist/cli/commands/rc.js +270 -0
  14. package/dist/cli/commands/rc.js.map +1 -0
  15. package/dist/cli/commands/start.d.ts +18 -0
  16. package/dist/cli/commands/start.d.ts.map +1 -0
  17. package/dist/cli/commands/start.js +219 -0
  18. package/dist/cli/commands/start.js.map +1 -0
  19. package/dist/cli/commands/upstream.js.map +1 -1
  20. package/dist/cli/commands/watch.d.ts +10 -0
  21. package/dist/cli/commands/watch.d.ts.map +1 -1
  22. package/dist/cli/commands/watch.js +181 -65
  23. package/dist/cli/commands/watch.js.map +1 -1
  24. package/dist/cli/core/cast.d.ts +40 -0
  25. package/dist/cli/core/cast.d.ts.map +1 -0
  26. package/dist/cli/core/cast.js +442 -0
  27. package/dist/cli/core/cast.js.map +1 -0
  28. package/dist/cli/core/gh-cli.d.ts +25 -0
  29. package/dist/cli/core/gh-cli.d.ts.map +1 -1
  30. package/dist/cli/core/gh-cli.js +15 -1
  31. package/dist/cli/core/gh-cli.js.map +1 -1
  32. package/dist/cli/core/init.d.ts +9 -1
  33. package/dist/cli/core/init.d.ts.map +1 -1
  34. package/dist/cli/core/init.js +108 -13
  35. package/dist/cli/core/init.js.map +1 -1
  36. package/dist/cli/core/migrations.js.map +1 -1
  37. package/dist/cli/core/nap.d.ts +37 -0
  38. package/dist/cli/core/nap.d.ts.map +1 -0
  39. package/dist/cli/core/nap.js +528 -0
  40. package/dist/cli/core/nap.js.map +1 -0
  41. package/dist/cli/core/output.d.ts +5 -0
  42. package/dist/cli/core/output.d.ts.map +1 -1
  43. package/dist/cli/core/output.js +7 -0
  44. package/dist/cli/core/output.js.map +1 -1
  45. package/dist/cli/core/upgrade.d.ts +0 -1
  46. package/dist/cli/core/upgrade.d.ts.map +1 -1
  47. package/dist/cli/core/upgrade.js.map +1 -1
  48. package/dist/cli/core/version.js.map +1 -1
  49. package/dist/cli/shell/agent-status.d.ts +11 -0
  50. package/dist/cli/shell/agent-status.d.ts.map +1 -0
  51. package/dist/cli/shell/agent-status.js +26 -0
  52. package/dist/cli/shell/agent-status.js.map +1 -0
  53. package/dist/cli/shell/commands.d.ts +10 -0
  54. package/dist/cli/shell/commands.d.ts.map +1 -1
  55. package/dist/cli/shell/commands.js +143 -29
  56. package/dist/cli/shell/commands.js.map +1 -1
  57. package/dist/cli/shell/components/AgentPanel.d.ts +1 -4
  58. package/dist/cli/shell/components/AgentPanel.d.ts.map +1 -1
  59. package/dist/cli/shell/components/AgentPanel.js +88 -6
  60. package/dist/cli/shell/components/AgentPanel.js.map +1 -1
  61. package/dist/cli/shell/components/App.d.ts +11 -6
  62. package/dist/cli/shell/components/App.d.ts.map +1 -1
  63. package/dist/cli/shell/components/App.js +212 -35
  64. package/dist/cli/shell/components/App.js.map +1 -1
  65. package/dist/cli/shell/components/ErrorBoundary.d.ts +22 -0
  66. package/dist/cli/shell/components/ErrorBoundary.d.ts.map +1 -0
  67. package/dist/cli/shell/components/ErrorBoundary.js +31 -0
  68. package/dist/cli/shell/components/ErrorBoundary.js.map +1 -0
  69. package/dist/cli/shell/components/InputPrompt.d.ts +3 -0
  70. package/dist/cli/shell/components/InputPrompt.d.ts.map +1 -1
  71. package/dist/cli/shell/components/InputPrompt.js +155 -13
  72. package/dist/cli/shell/components/InputPrompt.js.map +1 -1
  73. package/dist/cli/shell/components/MessageStream.d.ts +17 -4
  74. package/dist/cli/shell/components/MessageStream.d.ts.map +1 -1
  75. package/dist/cli/shell/components/MessageStream.js +215 -23
  76. package/dist/cli/shell/components/MessageStream.js.map +1 -1
  77. package/dist/cli/shell/components/Separator.d.ts +17 -0
  78. package/dist/cli/shell/components/Separator.d.ts.map +1 -0
  79. package/dist/cli/shell/components/Separator.js +10 -0
  80. package/dist/cli/shell/components/Separator.js.map +1 -0
  81. package/dist/cli/shell/components/ThinkingIndicator.d.ts +21 -0
  82. package/dist/cli/shell/components/ThinkingIndicator.d.ts.map +1 -0
  83. package/dist/cli/shell/components/ThinkingIndicator.js +102 -0
  84. package/dist/cli/shell/components/ThinkingIndicator.js.map +1 -0
  85. package/dist/cli/shell/components/index.d.ts +3 -0
  86. package/dist/cli/shell/components/index.d.ts.map +1 -1
  87. package/dist/cli/shell/components/index.js +2 -0
  88. package/dist/cli/shell/components/index.js.map +1 -1
  89. package/dist/cli/shell/coordinator.d.ts +10 -0
  90. package/dist/cli/shell/coordinator.d.ts.map +1 -1
  91. package/dist/cli/shell/coordinator.js +99 -4
  92. package/dist/cli/shell/coordinator.js.map +1 -1
  93. package/dist/cli/shell/error-messages.d.ts +21 -0
  94. package/dist/cli/shell/error-messages.d.ts.map +1 -0
  95. package/dist/cli/shell/error-messages.js +61 -0
  96. package/dist/cli/shell/error-messages.js.map +1 -0
  97. package/dist/cli/shell/index.d.ts +24 -3
  98. package/dist/cli/shell/index.d.ts.map +1 -1
  99. package/dist/cli/shell/index.js +943 -34
  100. package/dist/cli/shell/index.js.map +1 -1
  101. package/dist/cli/shell/lifecycle.d.ts +2 -0
  102. package/dist/cli/shell/lifecycle.d.ts.map +1 -1
  103. package/dist/cli/shell/lifecycle.js +59 -6
  104. package/dist/cli/shell/lifecycle.js.map +1 -1
  105. package/dist/cli/shell/memory.d.ts +6 -1
  106. package/dist/cli/shell/memory.d.ts.map +1 -1
  107. package/dist/cli/shell/memory.js +12 -1
  108. package/dist/cli/shell/memory.js.map +1 -1
  109. package/dist/cli/shell/router.d.ts +16 -0
  110. package/dist/cli/shell/router.d.ts.map +1 -1
  111. package/dist/cli/shell/router.js +27 -0
  112. package/dist/cli/shell/router.js.map +1 -1
  113. package/dist/cli/shell/session-store.d.ts +47 -0
  114. package/dist/cli/shell/session-store.d.ts.map +1 -0
  115. package/dist/cli/shell/session-store.js +125 -0
  116. package/dist/cli/shell/session-store.js.map +1 -0
  117. package/dist/cli/shell/sessions.d.ts +2 -0
  118. package/dist/cli/shell/sessions.d.ts.map +1 -1
  119. package/dist/cli/shell/sessions.js +19 -5
  120. package/dist/cli/shell/sessions.js.map +1 -1
  121. package/dist/cli/shell/shell-metrics.d.ts +34 -0
  122. package/dist/cli/shell/shell-metrics.d.ts.map +1 -0
  123. package/dist/cli/shell/shell-metrics.js +98 -0
  124. package/dist/cli/shell/shell-metrics.js.map +1 -0
  125. package/dist/cli/shell/spawn.d.ts.map +1 -1
  126. package/dist/cli/shell/spawn.js +20 -6
  127. package/dist/cli/shell/spawn.js.map +1 -1
  128. package/dist/cli/shell/terminal.d.ts +26 -0
  129. package/dist/cli/shell/terminal.d.ts.map +1 -1
  130. package/dist/cli/shell/terminal.js +65 -2
  131. package/dist/cli/shell/terminal.js.map +1 -1
  132. package/dist/cli/shell/theme-colors.d.ts +39 -0
  133. package/dist/cli/shell/theme-colors.d.ts.map +1 -0
  134. package/dist/cli/shell/theme-colors.js +39 -0
  135. package/dist/cli/shell/theme-colors.js.map +1 -0
  136. package/dist/cli/shell/types.d.ts +2 -0
  137. package/dist/cli/shell/types.d.ts.map +1 -1
  138. package/dist/cli/shell/useAnimation.d.ts +42 -0
  139. package/dist/cli/shell/useAnimation.d.ts.map +1 -0
  140. package/dist/cli/shell/useAnimation.js +139 -0
  141. package/dist/cli/shell/useAnimation.js.map +1 -0
  142. package/dist/cli-entry.d.ts +0 -7
  143. package/dist/cli-entry.d.ts.map +1 -1
  144. package/dist/cli-entry.js +701 -96
  145. package/dist/cli-entry.js.map +1 -1
  146. package/package.json +18 -2
  147. package/templates/orchestration-log.md +1 -1
  148. package/templates/package.json +3 -0
  149. package/templates/ralph-triage.js +543 -0
  150. package/templates/scribe-charter.md +1 -1
  151. package/templates/squad.agent.md +10 -10
  152. package/templates/workflows/squad-heartbeat.yml +52 -196
  153. 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 cmd = args[0];
22
- // --version / -v
23
- if (cmd === '--version' || cmd === '-v') {
24
- console.log(`squad ${VERSION}`);
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} — Add an AI agent team to any project\n`);
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(`Commands:`);
32
- console.log(` ${BOLD}(default)${RESET} Launch interactive shell (no args)`);
33
- console.log(` Flags: --global (init in personal squad directory)`);
34
- console.log(` ${BOLD}init${RESET} Initialize Squad (skip files that already exist)`);
35
- console.log(` Flags: --global (init in personal squad directory)`);
36
- console.log(` --mode remote <path> (init linked to a remote team root)`);
37
- console.log(` ${BOLD}upgrade${RESET} Update Squad-owned files to latest version`);
38
- console.log(` Overwrites: squad.agent.md, templates dir (.squad-templates/ or .ai-team-templates/)`);
39
- console.log(` Never touches: .squad/ or .ai-team/ (your team state)`);
40
- console.log(` Flags: --global (upgrade personal squad), --migrate-directory (rename .ai-team/ → .squad/)`);
41
- console.log(` ${BOLD}status${RESET} Show which squad is active and why`);
42
- console.log(` ${BOLD}triage${RESET} Scan for work and categorize issues`);
43
- console.log(` Usage: triage [--interval <minutes>]`);
44
- console.log(` Default: checks every 10 minutes (Ctrl+C to stop)`);
45
- console.log(` ${BOLD}loop${RESET} Continuous work loop (Ralph mode)`);
46
- console.log(` Usage: loop [--filter <label>] [--interval <minutes>]`);
47
- console.log(` Default: checks every 10 minutes (Ctrl+C to stop)`);
48
- console.log(` ${BOLD}hire${RESET} Team creation wizard`);
49
- console.log(` Usage: hire [--name <name>] [--role <role>]`);
50
- console.log(` ${BOLD}copilot${RESET} Add/remove the Copilot coding agent (@copilot)`);
51
- console.log(` Usage: copilot [--off] [--auto-assign]`);
52
- console.log(` ${BOLD}plugin${RESET} Manage plugin marketplaces`);
53
- console.log(` Usage: plugin marketplace add|remove|list|browse`);
54
- console.log(` ${BOLD}export${RESET} Export squad to a portable JSON snapshot`);
55
- console.log(` Default: squad-export.json (use --out <path> to override)`);
56
- console.log(` ${BOLD}import${RESET} Import squad from an export file`);
57
- console.log(` Usage: import <file> [--force]`);
58
- console.log(` ${BOLD}scrub-emails${RESET} Remove email addresses from Squad state files`);
59
- console.log(` Usage: scrub-emails [directory] (default: .ai-team/)`);
60
- console.log(` ${BOLD}doctor${RESET} Validate squad setup integrity (diagnostic)`);
61
- console.log(` ${BOLD}link${RESET} Link project to a remote team root`);
62
- console.log(` Usage: link <team-repo-path>`);
63
- console.log(` ${BOLD}aspire${RESET} Launch Aspire dashboard for Squad observability`);
64
- console.log(` Flags: --docker (force Docker), --port <number> (OTLP port)`);
65
- console.log(` ${BOLD}help${RESET} Show this help message`);
66
- console.log(`\nFlags:`);
67
- console.log(` ${BOLD}--version, -v${RESET} Print version`);
68
- console.log(` ${BOLD}--help, -h${RESET} Show help`);
69
- console.log(` ${BOLD}--global${RESET} Use personal (global) squad path (for init, upgrade)`);
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 install --save-dev @bradygaster/squad-cli`);
590
+ console.log(` npm i --save-dev @bradygaster/squad-cli`);
72
591
  console.log(`\nInsider channel:`);
73
- console.log(` npm install --save-dev @bradygaster/squad-cli@insider\n`);
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
- // No args launch interactive shell
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
- await runShell();
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
- if (cmd === 'init') {
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('--mode remote requires a team root path. Usage: squad init --mode remote <team-root-path>');
678
+ fatal('squad init --mode remote <team-root-path>');
92
679
  }
93
680
  writeRemoteConfig(dest, teamRootArg);
94
681
  }
95
- await runInit(dest);
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('Usage: squad link <team-repo-path>');
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 === 'triage' || cmd === 'watch') {
126
- console.log('🕵️ Squad triage scanning for work... (full implementation pending)');
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
- if (cmd === 'loop') {
130
- const filterIdx = args.indexOf('--filter');
131
- const filter = (filterIdx !== -1 && args[filterIdx + 1]) ? args[filterIdx + 1] : undefined;
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
- console.log(`🔄 Squad loop starting... (full implementation pending)`);
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('Usage: squad import <file> [--force]');
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] || '.ai-team';
777
+ const targetDir = args[1] || '.squad';
189
778
  const count = await scrubEmails(targetDir);
190
779
  if (count > 0) {
191
- console.log(`Scrubbed ${count} email address(es).`);
780
+ console.log(`Scrubbed ${count} email${count !== 1 ? 's' : ''}.`);
192
781
  }
193
782
  else {
194
- console.log('No email addresses found.');
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 === 'doctor') {
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(` Active squad: ${BOLD}repo${RESET}`);
219
- console.log(` Path: ${repoSquad}`);
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(` Active squad: ${BOLD}personal (global)${RESET}`);
224
- console.log(` Path: ${globalSquadDir}`);
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(` Active squad: ${DIM}none${RESET}`);
229
- console.log(` Reason: No .squad/ found in repo tree or at global path`);
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 resolution: ${repoSquad ?? 'not found'}${RESET}`);
233
- console.log(` ${DIM}Global path: ${globalPath}${RESET}`);
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}\n Run 'squad help' for usage information.`);
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
- console.error(err);
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