@bradygaster/squad-cli 0.8.1 → 0.8.3

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 (61) hide show
  1. package/dist/cli/commands/aspire.d.ts +19 -0
  2. package/dist/cli/commands/aspire.d.ts.map +1 -0
  3. package/dist/cli/commands/aspire.js +151 -0
  4. package/dist/cli/commands/aspire.js.map +1 -0
  5. package/dist/cli/commands/doctor.d.ts +38 -0
  6. package/dist/cli/commands/doctor.d.ts.map +1 -0
  7. package/dist/cli/commands/doctor.js +247 -0
  8. package/dist/cli/commands/doctor.js.map +1 -0
  9. package/dist/cli/commands/init-remote.d.ts +18 -0
  10. package/dist/cli/commands/init-remote.d.ts.map +1 -0
  11. package/dist/cli/commands/init-remote.js +32 -0
  12. package/dist/cli/commands/init-remote.js.map +1 -0
  13. package/dist/cli/commands/link.d.ts +18 -0
  14. package/dist/cli/commands/link.d.ts.map +1 -0
  15. package/dist/cli/commands/link.js +49 -0
  16. package/dist/cli/commands/link.js.map +1 -0
  17. package/dist/cli/commands/plugin.d.ts.map +1 -1
  18. package/dist/cli/commands/plugin.js +2 -1
  19. package/dist/cli/commands/plugin.js.map +1 -1
  20. package/dist/cli/commands/upstream.d.ts +13 -0
  21. package/dist/cli/commands/upstream.d.ts.map +1 -0
  22. package/dist/cli/commands/upstream.js +232 -0
  23. package/dist/cli/commands/upstream.js.map +1 -0
  24. package/dist/cli/core/upgrade.js.map +1 -1
  25. package/dist/cli/shell/components/AgentPanel.d.ts +4 -0
  26. package/dist/cli/shell/components/AgentPanel.d.ts.map +1 -1
  27. package/dist/cli/shell/components/AgentPanel.js +9 -12
  28. package/dist/cli/shell/components/AgentPanel.js.map +1 -1
  29. package/dist/cli/shell/components/App.d.ts +30 -0
  30. package/dist/cli/shell/components/App.d.ts.map +1 -0
  31. package/dist/cli/shell/components/App.js +103 -0
  32. package/dist/cli/shell/components/App.js.map +1 -0
  33. package/dist/cli/shell/components/InputPrompt.js +1 -1
  34. package/dist/cli/shell/components/InputPrompt.js.map +1 -1
  35. package/dist/cli/shell/components/MessageStream.d.ts +3 -1
  36. package/dist/cli/shell/components/MessageStream.d.ts.map +1 -1
  37. package/dist/cli/shell/components/MessageStream.js +35 -3
  38. package/dist/cli/shell/components/MessageStream.js.map +1 -1
  39. package/dist/cli/shell/components/index.d.ts +2 -0
  40. package/dist/cli/shell/components/index.d.ts.map +1 -1
  41. package/dist/cli/shell/components/index.js +1 -0
  42. package/dist/cli/shell/components/index.js.map +1 -1
  43. package/dist/cli/shell/index.d.ts +4 -2
  44. package/dist/cli/shell/index.d.ts.map +1 -1
  45. package/dist/cli/shell/index.js +218 -39
  46. package/dist/cli/shell/index.js.map +1 -1
  47. package/dist/cli/shell/lifecycle.d.ts +14 -0
  48. package/dist/cli/shell/lifecycle.d.ts.map +1 -1
  49. package/dist/cli/shell/lifecycle.js +51 -0
  50. package/dist/cli/shell/lifecycle.js.map +1 -1
  51. package/dist/cli/shell/sdk-bridge.d.ts +50 -0
  52. package/dist/cli/shell/sdk-bridge.d.ts.map +1 -0
  53. package/dist/cli/shell/sdk-bridge.js +235 -0
  54. package/dist/cli/shell/sdk-bridge.js.map +1 -0
  55. package/dist/cli/shell/spawn.d.ts +9 -7
  56. package/dist/cli/shell/spawn.d.ts.map +1 -1
  57. package/dist/cli/shell/spawn.js +45 -20
  58. package/dist/cli/shell/spawn.js.map +1 -1
  59. package/dist/cli-entry.js +40 -3
  60. package/dist/cli-entry.js.map +1 -1
  61. package/package.json +97 -2
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Aspire command — Launch .NET Aspire dashboard for Squad observability
3
+ * (Issue #265)
4
+ *
5
+ * Starts the Aspire dashboard and configures OTLP export so Squad
6
+ * traces and metrics flow into the dashboard automatically.
7
+ */
8
+ export interface AspireOptions {
9
+ /** Use Docker even if dotnet is available */
10
+ docker?: boolean;
11
+ /** Custom OTLP endpoint port */
12
+ port?: number;
13
+ }
14
+ /**
15
+ * Run the `squad aspire` command.
16
+ * Launches the Aspire dashboard and configures OTLP export.
17
+ */
18
+ export declare function runAspire(options?: AspireOptions): Promise<void>;
19
+ //# sourceMappingURL=aspire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspire.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/aspire.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAyFH,MAAM,WAAW,aAAa;IAC5B,6CAA6C;IAC7C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gCAAgC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoE1E"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Aspire command — Launch .NET Aspire dashboard for Squad observability
3
+ * (Issue #265)
4
+ *
5
+ * Starts the Aspire dashboard and configures OTLP export so Squad
6
+ * traces and metrics flow into the dashboard automatically.
7
+ */
8
+ import { execSync, spawn } from 'node:child_process';
9
+ import { BOLD, RESET, DIM, GREEN, RED, YELLOW } from '../core/output.js';
10
+ // ============================================================================
11
+ // Constants
12
+ // ============================================================================
13
+ /** Default OTLP endpoint the Aspire dashboard listens on. */
14
+ const ASPIRE_OTLP_ENDPOINT = 'http://localhost:18888';
15
+ /** Default Aspire dashboard UI port. */
16
+ const ASPIRE_DASHBOARD_PORT = 18888;
17
+ /** Docker image for the Aspire dashboard. */
18
+ const ASPIRE_DOCKER_IMAGE = 'mcr.microsoft.com/dotnet/aspire-dashboard:latest';
19
+ // ============================================================================
20
+ // Detection helpers
21
+ // ============================================================================
22
+ function commandExists(cmd) {
23
+ try {
24
+ execSync(`${cmd} --version`, { stdio: 'ignore' });
25
+ return true;
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ function isDotnetAspireAvailable() {
32
+ if (!commandExists('dotnet'))
33
+ return false;
34
+ try {
35
+ const output = execSync('dotnet tool list -g', { encoding: 'utf8' });
36
+ if (output.toLowerCase().includes('aspire'))
37
+ return true;
38
+ }
39
+ catch {
40
+ // Ignore
41
+ }
42
+ try {
43
+ const workloads = execSync('dotnet workload list', { encoding: 'utf8' });
44
+ if (workloads.toLowerCase().includes('aspire'))
45
+ return true;
46
+ }
47
+ catch {
48
+ // Ignore
49
+ }
50
+ return false;
51
+ }
52
+ function isDockerAvailable() {
53
+ return commandExists('docker');
54
+ }
55
+ // ============================================================================
56
+ // Launch strategies
57
+ // ============================================================================
58
+ function launchWithDocker() {
59
+ console.log(`${DIM}Starting Aspire dashboard via Docker...${RESET}`);
60
+ const child = spawn('docker', [
61
+ 'run', '--rm',
62
+ '-p', `${ASPIRE_DASHBOARD_PORT}:18888`,
63
+ '-p', '4317:18889',
64
+ '-e', 'DASHBOARD__FRONTEND__AUTHMODE=Unsecured',
65
+ '-e', 'DASHBOARD__OTLP__AUTHMODE=ApiKey',
66
+ '-e', 'DASHBOARD__OTLP__PRIMARYAPIKEY=squad-dev-key',
67
+ ASPIRE_DOCKER_IMAGE,
68
+ ], {
69
+ stdio: ['ignore', 'pipe', 'pipe'],
70
+ detached: false,
71
+ });
72
+ return child;
73
+ }
74
+ function launchWithDotnet() {
75
+ console.log(`${DIM}Starting Aspire dashboard via dotnet...${RESET}`);
76
+ const child = spawn('dotnet', [
77
+ 'run', '--project', 'aspire',
78
+ '--', '--dashboard-port', String(ASPIRE_DASHBOARD_PORT),
79
+ ], {
80
+ stdio: ['ignore', 'pipe', 'pipe'],
81
+ detached: false,
82
+ });
83
+ return child;
84
+ }
85
+ /**
86
+ * Run the `squad aspire` command.
87
+ * Launches the Aspire dashboard and configures OTLP export.
88
+ */
89
+ export async function runAspire(options = {}) {
90
+ const port = options.port ?? ASPIRE_DASHBOARD_PORT;
91
+ const endpoint = `http://localhost:${port}`;
92
+ console.log(`\n${BOLD}🔭 Squad Aspire — OpenTelemetry Dashboard${RESET}\n`);
93
+ // Set OTLP environment so OTel providers pick it up
94
+ process.env['OTEL_EXPORTER_OTLP_ENDPOINT'] = endpoint;
95
+ console.log(`${DIM}OTLP endpoint: ${endpoint}${RESET}`);
96
+ // Determine launch strategy
97
+ const useDocker = options.docker || !isDotnetAspireAvailable();
98
+ let child;
99
+ if (useDocker && isDockerAvailable()) {
100
+ child = launchWithDocker();
101
+ }
102
+ else if (!useDocker && isDotnetAspireAvailable()) {
103
+ child = launchWithDotnet();
104
+ }
105
+ else if (isDockerAvailable()) {
106
+ child = launchWithDocker();
107
+ }
108
+ else {
109
+ console.log(`${RED}✗${RESET} Neither Docker nor .NET Aspire workload found.`);
110
+ console.log(`\n Install options:`);
111
+ console.log(` ${BOLD}Docker:${RESET} https://docker.com/get-started`);
112
+ console.log(` ${BOLD}Aspire:${RESET} dotnet workload install aspire\n`);
113
+ return;
114
+ }
115
+ // Wire up output
116
+ child.stdout?.on('data', (data) => {
117
+ const text = data.toString().trim();
118
+ if (text)
119
+ console.log(`${DIM}[aspire]${RESET} ${text}`);
120
+ });
121
+ child.stderr?.on('data', (data) => {
122
+ const text = data.toString().trim();
123
+ if (text)
124
+ console.log(`${YELLOW}[aspire]${RESET} ${text}`);
125
+ });
126
+ child.on('error', (err) => {
127
+ console.error(`${RED}✗${RESET} Failed to start Aspire: ${err.message}`);
128
+ });
129
+ // Give the dashboard a moment to start
130
+ await new Promise((resolve) => setTimeout(resolve, 2000));
131
+ console.log(`\n${GREEN}✓${RESET} Aspire dashboard launching`);
132
+ console.log(` ${BOLD}Dashboard:${RESET} http://localhost:${port}`);
133
+ console.log(` ${BOLD}OTLP gRPC:${RESET} localhost:4317`);
134
+ console.log(`\n${DIM}Squad OTel will automatically export to this endpoint.${RESET}`);
135
+ console.log(`${DIM}Press Ctrl+C to stop.${RESET}\n`);
136
+ // Keep alive until Ctrl+C
137
+ return new Promise((resolve) => {
138
+ const shutdown = () => {
139
+ console.log(`\n${DIM}🔭 Aspire dashboard stopping...${RESET}`);
140
+ child?.kill();
141
+ resolve();
142
+ };
143
+ process.on('SIGINT', shutdown);
144
+ process.on('SIGTERM', shutdown);
145
+ child?.on('close', () => {
146
+ console.log(`${DIM}Aspire process exited.${RESET}`);
147
+ resolve();
148
+ });
149
+ });
150
+ }
151
+ //# sourceMappingURL=aspire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspire.js","sourceRoot":"","sources":["../../../src/cli/commands/aspire.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * squad doctor — setup validation diagnostic command.
3
+ *
4
+ * Inspects the .squad/ directory (or hub layout) and reports
5
+ * the health of every expected file / convention. Always exits 0
6
+ * because this is a diagnostic tool, not a gate.
7
+ *
8
+ * Inspired by @spboyer (Shayne Boyer)'s doctor command in PR bradygaster/squad#131.
9
+ *
10
+ * @module cli/commands/doctor
11
+ */
12
+ /** Result of a single diagnostic check. */
13
+ export interface DoctorCheck {
14
+ name: string;
15
+ status: 'pass' | 'fail' | 'warn';
16
+ message: string;
17
+ }
18
+ /** Detected squad layout mode. */
19
+ export type DoctorMode = 'local' | 'remote' | 'hub';
20
+ /**
21
+ * Run all doctor checks for the given working directory.
22
+ * Returns an array of check results — never throws for check failures.
23
+ */
24
+ export declare function runDoctor(cwd?: string): Promise<DoctorCheck[]>;
25
+ /**
26
+ * Detect the squad mode for the given working directory.
27
+ * Exported for tests and display.
28
+ */
29
+ export declare function getDoctorMode(cwd?: string): DoctorMode;
30
+ /**
31
+ * Print doctor results to stdout. Intended for CLI use.
32
+ */
33
+ export declare function printDoctorReport(checks: DoctorCheck[], mode: DoctorMode): void;
34
+ /**
35
+ * CLI entry point — run doctor and print results.
36
+ */
37
+ export declare function doctorCommand(cwd?: string): Promise<void>;
38
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,kCAAkC;AAClC,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAkMpD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CA+BpE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CAEtD;AAUD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAc/E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK/D"}
@@ -0,0 +1,247 @@
1
+ /**
2
+ * squad doctor — setup validation diagnostic command.
3
+ *
4
+ * Inspects the .squad/ directory (or hub layout) and reports
5
+ * the health of every expected file / convention. Always exits 0
6
+ * because this is a diagnostic tool, not a gate.
7
+ *
8
+ * Inspired by @spboyer (Shayne Boyer)'s doctor command in PR bradygaster/squad#131.
9
+ *
10
+ * @module cli/commands/doctor
11
+ */
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+ // ── helpers ─────────────────────────────────────────────────────────
15
+ function fileExists(p) {
16
+ return fs.existsSync(p);
17
+ }
18
+ function isDirectory(p) {
19
+ try {
20
+ return fs.statSync(p).isDirectory();
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ function tryReadJson(p) {
27
+ try {
28
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ // ── mode detection ──────────────────────────────────────────────────
35
+ function detectMode(cwd) {
36
+ const squadDir = path.join(cwd, '.squad');
37
+ const configPath = path.join(squadDir, 'config.json');
38
+ // Remote mode: config.json exists with teamRoot
39
+ if (fileExists(configPath)) {
40
+ const cfg = tryReadJson(configPath);
41
+ if (cfg && typeof cfg === 'object' && 'teamRoot' in cfg) {
42
+ const raw = cfg['teamRoot'];
43
+ if (typeof raw === 'string' && raw.length > 0) {
44
+ return { mode: 'remote', squadDir, teamRoot: raw };
45
+ }
46
+ }
47
+ }
48
+ // Hub mode: squad-hub.json in cwd
49
+ if (fileExists(path.join(cwd, 'squad-hub.json'))) {
50
+ return { mode: 'hub', squadDir };
51
+ }
52
+ // Default: local
53
+ return { mode: 'local', squadDir };
54
+ }
55
+ // ── individual checks ───────────────────────────────────────────────
56
+ function checkSquadDir(squadDir) {
57
+ const exists = isDirectory(squadDir);
58
+ return {
59
+ name: '.squad/ directory exists',
60
+ status: exists ? 'pass' : 'fail',
61
+ message: exists ? 'directory present' : 'directory not found',
62
+ };
63
+ }
64
+ function checkConfigJson(squadDir) {
65
+ const configPath = path.join(squadDir, 'config.json');
66
+ if (!fileExists(configPath))
67
+ return undefined; // optional file — skip
68
+ const data = tryReadJson(configPath);
69
+ if (data === undefined) {
70
+ return {
71
+ name: 'config.json valid',
72
+ status: 'fail',
73
+ message: 'file exists but is not valid JSON',
74
+ };
75
+ }
76
+ if (typeof data === 'object' &&
77
+ data !== null &&
78
+ 'teamRoot' in data &&
79
+ typeof data['teamRoot'] !== 'string') {
80
+ return {
81
+ name: 'config.json valid',
82
+ status: 'fail',
83
+ message: 'teamRoot must be a string',
84
+ };
85
+ }
86
+ return {
87
+ name: 'config.json valid',
88
+ status: 'pass',
89
+ message: 'parses as JSON, schema OK',
90
+ };
91
+ }
92
+ function checkAbsoluteTeamRoot(squadDir) {
93
+ const configPath = path.join(squadDir, 'config.json');
94
+ if (!fileExists(configPath))
95
+ return undefined;
96
+ const data = tryReadJson(configPath);
97
+ if (!data || typeof data['teamRoot'] !== 'string')
98
+ return undefined;
99
+ const teamRoot = data['teamRoot'];
100
+ if (path.isAbsolute(teamRoot)) {
101
+ return {
102
+ name: 'absolute path warning',
103
+ status: 'warn',
104
+ message: `teamRoot is absolute (${teamRoot}) — prefer relative paths for portability`,
105
+ };
106
+ }
107
+ return undefined;
108
+ }
109
+ function checkTeamRootResolves(squadDir, teamRoot) {
110
+ const resolved = path.isAbsolute(teamRoot)
111
+ ? teamRoot
112
+ : path.resolve(path.dirname(squadDir), teamRoot);
113
+ const exists = isDirectory(resolved);
114
+ return {
115
+ name: 'team root resolves',
116
+ status: exists ? 'pass' : 'fail',
117
+ message: exists ? `resolved to ${resolved}` : `directory not found: ${resolved}`,
118
+ };
119
+ }
120
+ function checkTeamMd(squadDir) {
121
+ const teamPath = path.join(squadDir, 'team.md');
122
+ if (!fileExists(teamPath)) {
123
+ return { name: 'team.md found with ## Members header', status: 'fail', message: 'file not found' };
124
+ }
125
+ const content = fs.readFileSync(teamPath, 'utf8');
126
+ if (!content.includes('## Members')) {
127
+ return { name: 'team.md found with ## Members header', status: 'warn', message: 'file exists but missing ## Members header' };
128
+ }
129
+ return { name: 'team.md found with ## Members header', status: 'pass', message: 'file present, header found' };
130
+ }
131
+ function checkRoutingMd(squadDir) {
132
+ const exists = fileExists(path.join(squadDir, 'routing.md'));
133
+ return {
134
+ name: 'routing.md found',
135
+ status: exists ? 'pass' : 'fail',
136
+ message: exists ? 'file present' : 'file not found',
137
+ };
138
+ }
139
+ function checkAgentsDir(squadDir) {
140
+ const agentsDir = path.join(squadDir, 'agents');
141
+ if (!isDirectory(agentsDir)) {
142
+ return { name: 'agents/ directory exists', status: 'fail', message: 'directory not found' };
143
+ }
144
+ let count = 0;
145
+ try {
146
+ for (const entry of fs.readdirSync(agentsDir, { withFileTypes: true })) {
147
+ if (entry.isDirectory())
148
+ count++;
149
+ }
150
+ }
151
+ catch { /* empty */ }
152
+ return {
153
+ name: 'agents/ directory exists',
154
+ status: 'pass',
155
+ message: `directory present (${count} agent${count === 1 ? '' : 's'})`,
156
+ };
157
+ }
158
+ function checkCastingRegistry(squadDir) {
159
+ const registryPath = path.join(squadDir, 'casting', 'registry.json');
160
+ if (!fileExists(registryPath)) {
161
+ return { name: 'casting/registry.json exists', status: 'fail', message: 'file not found' };
162
+ }
163
+ const data = tryReadJson(registryPath);
164
+ if (data === undefined) {
165
+ return { name: 'casting/registry.json exists', status: 'fail', message: 'file exists but is not valid JSON' };
166
+ }
167
+ return { name: 'casting/registry.json exists', status: 'pass', message: 'file present, valid JSON' };
168
+ }
169
+ function checkDecisionsMd(squadDir) {
170
+ const exists = fileExists(path.join(squadDir, 'decisions.md'));
171
+ return {
172
+ name: 'decisions.md exists',
173
+ status: exists ? 'pass' : 'fail',
174
+ message: exists ? 'file present' : 'file not found',
175
+ };
176
+ }
177
+ // ── public API ──────────────────────────────────────────────────────
178
+ /**
179
+ * Run all doctor checks for the given working directory.
180
+ * Returns an array of check results — never throws for check failures.
181
+ */
182
+ export async function runDoctor(cwd) {
183
+ const resolvedCwd = cwd ?? process.cwd();
184
+ const { mode, squadDir, teamRoot } = detectMode(resolvedCwd);
185
+ const checks = [];
186
+ // 1. .squad/ directory
187
+ checks.push(checkSquadDir(squadDir));
188
+ // 2. config.json (if present)
189
+ const configCheck = checkConfigJson(squadDir);
190
+ if (configCheck)
191
+ checks.push(configCheck);
192
+ // 3. Absolute path warning
193
+ const absWarn = checkAbsoluteTeamRoot(squadDir);
194
+ if (absWarn)
195
+ checks.push(absWarn);
196
+ // 4. Remote team root resolution
197
+ if (mode === 'remote' && teamRoot) {
198
+ checks.push(checkTeamRootResolves(squadDir, teamRoot));
199
+ }
200
+ // 5–9 standard files (only if .squad/ exists)
201
+ if (isDirectory(squadDir)) {
202
+ checks.push(checkTeamMd(squadDir));
203
+ checks.push(checkRoutingMd(squadDir));
204
+ checks.push(checkAgentsDir(squadDir));
205
+ checks.push(checkCastingRegistry(squadDir));
206
+ checks.push(checkDecisionsMd(squadDir));
207
+ }
208
+ return checks;
209
+ }
210
+ /**
211
+ * Detect the squad mode for the given working directory.
212
+ * Exported for tests and display.
213
+ */
214
+ export function getDoctorMode(cwd) {
215
+ return detectMode(cwd ?? process.cwd()).mode;
216
+ }
217
+ // ── CLI output ──────────────────────────────────────────────────────
218
+ const STATUS_ICON = {
219
+ pass: '✅',
220
+ fail: '❌',
221
+ warn: '⚠️',
222
+ };
223
+ /**
224
+ * Print doctor results to stdout. Intended for CLI use.
225
+ */
226
+ export function printDoctorReport(checks, mode) {
227
+ console.log('\n🩺 Squad Doctor');
228
+ console.log('═══════════════\n');
229
+ console.log(`Mode: ${mode}\n`);
230
+ for (const c of checks) {
231
+ console.log(`${STATUS_ICON[c.status]} ${c.name} — ${c.message}`);
232
+ }
233
+ const passed = checks.filter(c => c.status === 'pass').length;
234
+ const failed = checks.filter(c => c.status === 'fail').length;
235
+ const warned = checks.filter(c => c.status === 'warn').length;
236
+ console.log(`\nSummary: ${passed} passed, ${failed} failed, ${warned} warnings\n`);
237
+ }
238
+ /**
239
+ * CLI entry point — run doctor and print results.
240
+ */
241
+ export async function doctorCommand(cwd) {
242
+ const resolvedCwd = cwd ?? process.cwd();
243
+ const mode = getDoctorMode(resolvedCwd);
244
+ const checks = await runDoctor(resolvedCwd);
245
+ printDoctorReport(checks, mode);
246
+ }
247
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * squad init --mode remote — remote mode variant of the init command.
3
+ *
4
+ * Creates .squad/ with config.json pointing to an external team root.
5
+ * The standard init scaffolding still runs; this just adds the config link.
6
+ *
7
+ * Remote squad mode concept by @spboyer (Shayne Boyer), PR bradygaster/squad#131.
8
+ *
9
+ * @module cli/commands/init-remote
10
+ */
11
+ /**
12
+ * Write `.squad/config.json` for remote mode.
13
+ *
14
+ * @param projectDir - Project root (where .squad/ lives or will be created).
15
+ * @param teamRepoPath - Absolute or relative path to the team repo.
16
+ */
17
+ export declare function writeRemoteConfig(projectDir: string, teamRepoPath: string): void;
18
+ //# sourceMappingURL=init-remote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-remote.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init-remote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAmBhF"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * squad init --mode remote — remote mode variant of the init command.
3
+ *
4
+ * Creates .squad/ with config.json pointing to an external team root.
5
+ * The standard init scaffolding still runs; this just adds the config link.
6
+ *
7
+ * Remote squad mode concept by @spboyer (Shayne Boyer), PR bradygaster/squad#131.
8
+ *
9
+ * @module cli/commands/init-remote
10
+ */
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ /**
14
+ * Write `.squad/config.json` for remote mode.
15
+ *
16
+ * @param projectDir - Project root (where .squad/ lives or will be created).
17
+ * @param teamRepoPath - Absolute or relative path to the team repo.
18
+ */
19
+ export function writeRemoteConfig(projectDir, teamRepoPath) {
20
+ const squadDir = path.join(projectDir, '.squad');
21
+ fs.mkdirSync(squadDir, { recursive: true });
22
+ // Always store a relative path from the project root to the team repo
23
+ const absoluteTeam = path.resolve(projectDir, teamRepoPath);
24
+ const relativePath = path.relative(projectDir, absoluteTeam);
25
+ const config = {
26
+ version: 1,
27
+ teamRoot: relativePath,
28
+ projectKey: null,
29
+ };
30
+ fs.writeFileSync(path.join(squadDir, 'config.json'), JSON.stringify(config, null, 2) + '\n', 'utf-8');
31
+ }
32
+ //# sourceMappingURL=init-remote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-remote.js","sourceRoot":"","sources":["../../../src/cli/commands/init-remote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,YAAoB;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACjD,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,sEAAsE;IACtE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAE7D,MAAM,MAAM,GAAG;QACb,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,YAAY;QACtB,UAAU,EAAE,IAAI;KACjB,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,EAClC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EACtC,OAAO,CACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * squad link <team-repo-path> — link a project to a remote team root.
3
+ *
4
+ * Writes `.squad/config.json` with a relative `teamRoot` path so the
5
+ * dual-root resolver (resolveSquadPaths) can find the team identity dir.
6
+ *
7
+ * Remote squad mode concept by @spboyer (Shayne Boyer), PR bradygaster/squad#131.
8
+ *
9
+ * @module cli/commands/link
10
+ */
11
+ /**
12
+ * Link the current project to a remote team root.
13
+ *
14
+ * @param projectDir - Project root (cwd or explicit).
15
+ * @param teamRepoPath - Path (relative or absolute) to the team repo.
16
+ */
17
+ export declare function runLink(projectDir: string, teamRepoPath: string): void;
18
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAwCtE"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * squad link <team-repo-path> — link a project to a remote team root.
3
+ *
4
+ * Writes `.squad/config.json` with a relative `teamRoot` path so the
5
+ * dual-root resolver (resolveSquadPaths) can find the team identity dir.
6
+ *
7
+ * Remote squad mode concept by @spboyer (Shayne Boyer), PR bradygaster/squad#131.
8
+ *
9
+ * @module cli/commands/link
10
+ */
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ import { fatal } from '../core/errors.js';
14
+ /**
15
+ * Link the current project to a remote team root.
16
+ *
17
+ * @param projectDir - Project root (cwd or explicit).
18
+ * @param teamRepoPath - Path (relative or absolute) to the team repo.
19
+ */
20
+ export function runLink(projectDir, teamRepoPath) {
21
+ // Resolve the team repo path to an absolute path
22
+ const absoluteTeam = path.resolve(projectDir, teamRepoPath);
23
+ // Validate the target exists
24
+ if (!fs.existsSync(absoluteTeam)) {
25
+ fatal(`Target path does not exist: ${absoluteTeam}`);
26
+ }
27
+ if (!fs.statSync(absoluteTeam).isDirectory()) {
28
+ fatal(`Target path is not a directory: ${absoluteTeam}`);
29
+ }
30
+ // Validate the target contains a .squad/ or .ai-team/ directory
31
+ const hasSquad = fs.existsSync(path.join(absoluteTeam, '.squad'));
32
+ const hasAiTeam = fs.existsSync(path.join(absoluteTeam, '.ai-team'));
33
+ if (!hasSquad && !hasAiTeam) {
34
+ fatal(`Target does not contain a .squad/ directory: ${absoluteTeam}`);
35
+ }
36
+ // Ensure .squad/ exists locally
37
+ const squadDir = path.join(projectDir, '.squad');
38
+ fs.mkdirSync(squadDir, { recursive: true });
39
+ // Compute relative path from project root to team repo
40
+ const relativePath = path.relative(projectDir, absoluteTeam);
41
+ const config = {
42
+ version: 1,
43
+ teamRoot: relativePath,
44
+ projectKey: null,
45
+ };
46
+ fs.writeFileSync(path.join(squadDir, 'config.json'), JSON.stringify(config, null, 2) + '\n', 'utf-8');
47
+ console.log(`✅ Linked to team root: ${relativePath}`);
48
+ }
49
+ //# sourceMappingURL=link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.js","sourceRoot":"","sources":["../../../src/cli/commands/link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,UAAkB,EAAE,YAAoB;IAC9D,iDAAiD;IACjD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAE5D,6BAA6B;IAC7B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,+BAA+B,YAAY,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QAC7C,KAAK,CAAC,mCAAmC,YAAY,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,gEAAgE;IAChE,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC;IAClE,MAAM,SAAS,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;IACrE,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,KAAK,CAAC,gDAAgD,YAAY,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,gCAAgC;IAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACjD,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,uDAAuD;IACvD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAE7D,MAAM,MAAM,GAAG;QACb,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,YAAY;QACtB,UAAU,EAAE,IAAI;KACjB,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,EAClC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EACtC,OAAO,CACR,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,0BAA0B,YAAY,EAAE,CAAC,CAAC;AACxD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/plugin.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AAID,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgJ3E"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/plugin.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAiBH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AAID,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgJ3E"}
@@ -7,6 +7,7 @@ import { existsSync } from 'node:fs';
7
7
  import { join } from 'node:path';
8
8
  import { execFile } from 'node:child_process';
9
9
  import { promisify } from 'node:util';
10
+ import { TIMEOUTS } from '@bradygaster/squad-sdk';
10
11
  import { success, info, DIM, BOLD, RESET } from '../core/output.js';
11
12
  import { fatal } from '../core/errors.js';
12
13
  import { detectSquadDir } from '../core/detect-squad-dir.js';
@@ -112,7 +113,7 @@ export async function runPlugin(dest, args) {
112
113
  // Browse the marketplace repo for plugins using gh CLI
113
114
  let entries;
114
115
  try {
115
- const { stdout } = await execFileAsync('gh', ['api', `repos/${marketplace.source}/contents`, '--jq', '[.[] | select(.type == "dir") | .name]'], { timeout: 15000 });
116
+ const { stdout } = await execFileAsync('gh', ['api', `repos/${marketplace.source}/contents`, '--jq', '[.[] | select(.type == "dir") | .name]'], { timeout: TIMEOUTS.PLUGIN_FETCH_MS });
116
117
  entries = JSON.parse(stdout.trim());
117
118
  }
118
119
  catch (err) {