squads-cli 0.4.8 → 0.4.10
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 +38 -0
- package/dist/{chunk-IDZYXBZY.js → chunk-HIQ2APYR.js} +13 -269
- package/dist/chunk-HIQ2APYR.js.map +1 -0
- package/dist/chunk-HKWCBCEK.js +225 -0
- package/dist/chunk-HKWCBCEK.js.map +1 -0
- package/dist/chunk-NA3IECJA.js +288 -0
- package/dist/chunk-NA3IECJA.js.map +1 -0
- package/dist/cli.js +590 -620
- package/dist/cli.js.map +1 -1
- package/dist/{sessions-B6GVXJ2H.js → sessions-R4VWIGFR.js} +3 -2
- package/dist/terminal-JZSAQSN7.js +53 -0
- package/dist/terminal-JZSAQSN7.js.map +1 -0
- package/dist/update-MAY6EXFQ.js +17 -0
- package/dist/update-MAY6EXFQ.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-IDZYXBZY.js.map +0 -1
- /package/dist/{sessions-B6GVXJ2H.js.map → sessions-R4VWIGFR.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
autoUpdateOnStartup,
|
|
4
|
+
checkForUpdate,
|
|
5
|
+
performUpdate,
|
|
6
|
+
refreshVersionCache
|
|
7
|
+
} from "./chunk-HKWCBCEK.js";
|
|
2
8
|
import {
|
|
3
9
|
appendToMemory,
|
|
4
10
|
findMemoryDir,
|
|
@@ -6,38 +12,40 @@ import {
|
|
|
6
12
|
listMemoryEntries,
|
|
7
13
|
searchMemory
|
|
8
14
|
} from "./chunk-FUHBEL3L.js";
|
|
15
|
+
import {
|
|
16
|
+
cleanupStaleSessions,
|
|
17
|
+
detectSquad,
|
|
18
|
+
getLiveSessionSummaryAsync,
|
|
19
|
+
sessionsCommand,
|
|
20
|
+
sessionsHistoryCommand,
|
|
21
|
+
sessionsSummaryCommand,
|
|
22
|
+
startSession,
|
|
23
|
+
stopSession,
|
|
24
|
+
updateHeartbeat
|
|
25
|
+
} from "./chunk-HIQ2APYR.js";
|
|
9
26
|
import {
|
|
10
27
|
RESET,
|
|
11
28
|
barChart,
|
|
12
29
|
bold,
|
|
13
30
|
box,
|
|
14
|
-
cleanupStaleSessions,
|
|
15
31
|
colors,
|
|
16
|
-
detectSquad,
|
|
17
|
-
getLiveSessionSummaryAsync,
|
|
18
32
|
gradient,
|
|
19
33
|
icons,
|
|
20
34
|
padEnd,
|
|
21
35
|
progressBar,
|
|
22
|
-
sessionsCommand,
|
|
23
|
-
sessionsHistoryCommand,
|
|
24
|
-
sessionsSummaryCommand,
|
|
25
36
|
sparkline,
|
|
26
|
-
startSession,
|
|
27
|
-
stopSession,
|
|
28
37
|
truncate,
|
|
29
|
-
updateHeartbeat,
|
|
30
38
|
writeLine
|
|
31
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-NA3IECJA.js";
|
|
32
40
|
import {
|
|
33
41
|
__require
|
|
34
42
|
} from "./chunk-7OCVIDC7.js";
|
|
35
43
|
|
|
36
44
|
// src/cli.ts
|
|
37
45
|
import { config } from "dotenv";
|
|
38
|
-
import { existsSync as
|
|
39
|
-
import { join as
|
|
40
|
-
import { homedir as
|
|
46
|
+
import { existsSync as existsSync18 } from "fs";
|
|
47
|
+
import { join as join20 } from "path";
|
|
48
|
+
import { homedir as homedir4 } from "os";
|
|
41
49
|
import { Command } from "commander";
|
|
42
50
|
import chalk4 from "chalk";
|
|
43
51
|
|
|
@@ -47,227 +55,18 @@ var require2 = createRequire(import.meta.url);
|
|
|
47
55
|
var pkg = require2("../package.json");
|
|
48
56
|
var version = pkg.version;
|
|
49
57
|
|
|
50
|
-
// src/lib/update.ts
|
|
51
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs";
|
|
52
|
-
import { join, dirname } from "path";
|
|
53
|
-
import { homedir } from "os";
|
|
54
|
-
import { execSync } from "child_process";
|
|
55
|
-
import { fileURLToPath } from "url";
|
|
56
|
-
function getPackageVersion() {
|
|
57
|
-
try {
|
|
58
|
-
const __filename3 = fileURLToPath(import.meta.url);
|
|
59
|
-
const __dirname3 = dirname(__filename3);
|
|
60
|
-
const possiblePaths = [
|
|
61
|
-
join(__dirname3, "..", "..", "package.json"),
|
|
62
|
-
// From dist/lib/
|
|
63
|
-
join(__dirname3, "..", "package.json"),
|
|
64
|
-
// From dist/
|
|
65
|
-
join(__dirname3, "package.json")
|
|
66
|
-
// Same dir
|
|
67
|
-
];
|
|
68
|
-
for (const pkgPath of possiblePaths) {
|
|
69
|
-
if (existsSync(pkgPath)) {
|
|
70
|
-
const pkg2 = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
71
|
-
return pkg2.version || "0.0.0";
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
} catch {
|
|
75
|
-
}
|
|
76
|
-
return "0.0.0";
|
|
77
|
-
}
|
|
78
|
-
var CURRENT_VERSION = getPackageVersion();
|
|
79
|
-
var CACHE_DIR = join(homedir(), ".squads");
|
|
80
|
-
var CACHE_FILE = join(CACHE_DIR, "update-check.json");
|
|
81
|
-
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
82
|
-
function isNewerVersion(v1, v2) {
|
|
83
|
-
const parts1 = v1.replace(/^v/, "").split(".").map(Number);
|
|
84
|
-
const parts2 = v2.replace(/^v/, "").split(".").map(Number);
|
|
85
|
-
for (let i = 0; i < 3; i++) {
|
|
86
|
-
const p1 = parts1[i] || 0;
|
|
87
|
-
const p2 = parts2[i] || 0;
|
|
88
|
-
if (p2 > p1) return true;
|
|
89
|
-
if (p2 < p1) return false;
|
|
90
|
-
}
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
function readCache() {
|
|
94
|
-
try {
|
|
95
|
-
if (!existsSync(CACHE_FILE)) return null;
|
|
96
|
-
const data = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
|
|
97
|
-
return data;
|
|
98
|
-
} catch {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function writeCache(latestVersion) {
|
|
103
|
-
try {
|
|
104
|
-
if (!existsSync(CACHE_DIR)) {
|
|
105
|
-
mkdirSync(CACHE_DIR, { recursive: true });
|
|
106
|
-
}
|
|
107
|
-
const cache = {
|
|
108
|
-
latestVersion,
|
|
109
|
-
checkedAt: Date.now()
|
|
110
|
-
};
|
|
111
|
-
writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
112
|
-
} catch {
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function fetchLatestVersion() {
|
|
116
|
-
try {
|
|
117
|
-
const result = execSync("npm view squads-cli version 2>/dev/null", {
|
|
118
|
-
encoding: "utf-8",
|
|
119
|
-
timeout: 5e3
|
|
120
|
-
}).trim();
|
|
121
|
-
return result || null;
|
|
122
|
-
} catch {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
function checkForUpdate() {
|
|
127
|
-
const result = {
|
|
128
|
-
currentVersion: CURRENT_VERSION,
|
|
129
|
-
latestVersion: CURRENT_VERSION,
|
|
130
|
-
updateAvailable: false
|
|
131
|
-
};
|
|
132
|
-
const cache = readCache();
|
|
133
|
-
const now = Date.now();
|
|
134
|
-
if (cache) {
|
|
135
|
-
result.latestVersion = cache.latestVersion;
|
|
136
|
-
result.updateAvailable = isNewerVersion(CURRENT_VERSION, cache.latestVersion);
|
|
137
|
-
if (now - cache.checkedAt >= CACHE_TTL_MS) {
|
|
138
|
-
triggerBackgroundRefresh();
|
|
139
|
-
}
|
|
140
|
-
return result;
|
|
141
|
-
}
|
|
142
|
-
triggerBackgroundRefresh();
|
|
143
|
-
return result;
|
|
144
|
-
}
|
|
145
|
-
function triggerBackgroundRefresh() {
|
|
146
|
-
try {
|
|
147
|
-
const { spawn: spawn8 } = __require("child_process");
|
|
148
|
-
const child = spawn8("npm", ["view", "squads-cli", "version"], {
|
|
149
|
-
detached: true,
|
|
150
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
151
|
-
shell: true
|
|
152
|
-
});
|
|
153
|
-
let output = "";
|
|
154
|
-
child.stdout?.on("data", (data) => {
|
|
155
|
-
output += data.toString();
|
|
156
|
-
});
|
|
157
|
-
child.on("close", () => {
|
|
158
|
-
const version2 = output.trim();
|
|
159
|
-
if (version2 && /^\d+\.\d+\.\d+/.test(version2)) {
|
|
160
|
-
writeCache(version2);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
child.unref();
|
|
164
|
-
} catch {
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
function performUpdate() {
|
|
168
|
-
try {
|
|
169
|
-
execSync("npm update -g squads-cli", {
|
|
170
|
-
encoding: "utf-8",
|
|
171
|
-
stdio: "inherit",
|
|
172
|
-
timeout: 12e4
|
|
173
|
-
// 2 minutes
|
|
174
|
-
});
|
|
175
|
-
try {
|
|
176
|
-
unlinkSync(CACHE_FILE);
|
|
177
|
-
} catch {
|
|
178
|
-
}
|
|
179
|
-
return { success: true };
|
|
180
|
-
} catch (err) {
|
|
181
|
-
return {
|
|
182
|
-
success: false,
|
|
183
|
-
error: err instanceof Error ? err.message : "Unknown error"
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
function refreshVersionCache() {
|
|
188
|
-
const latestVersion = fetchLatestVersion();
|
|
189
|
-
if (latestVersion) {
|
|
190
|
-
writeCache(latestVersion);
|
|
191
|
-
return {
|
|
192
|
-
currentVersion: CURRENT_VERSION,
|
|
193
|
-
latestVersion,
|
|
194
|
-
updateAvailable: isNewerVersion(CURRENT_VERSION, latestVersion)
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
return checkForUpdate();
|
|
198
|
-
}
|
|
199
|
-
var AUTO_UPDATE_CACHE_FILE = join(CACHE_DIR, "auto-update.json");
|
|
200
|
-
var AUTO_UPDATE_COOLDOWN_MS = 60 * 60 * 1e3;
|
|
201
|
-
function readAutoUpdateCache() {
|
|
202
|
-
try {
|
|
203
|
-
if (!existsSync(AUTO_UPDATE_CACHE_FILE)) return null;
|
|
204
|
-
return JSON.parse(readFileSync(AUTO_UPDATE_CACHE_FILE, "utf-8"));
|
|
205
|
-
} catch {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
function writeAutoUpdateCache(cache) {
|
|
210
|
-
try {
|
|
211
|
-
if (!existsSync(CACHE_DIR)) {
|
|
212
|
-
mkdirSync(CACHE_DIR, { recursive: true });
|
|
213
|
-
}
|
|
214
|
-
writeFileSync(AUTO_UPDATE_CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
215
|
-
} catch {
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
async function autoUpdateOnStartup(silent = false) {
|
|
219
|
-
if (process.env.CI || process.env.SQUADS_NO_AUTO_UPDATE) return;
|
|
220
|
-
const autoCache = readAutoUpdateCache();
|
|
221
|
-
const now = Date.now();
|
|
222
|
-
if (autoCache && now - autoCache.lastAttempt < AUTO_UPDATE_COOLDOWN_MS) {
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
const info = checkForUpdate();
|
|
226
|
-
if (!info.updateAvailable) return;
|
|
227
|
-
writeAutoUpdateCache({ lastAttempt: now, lastSuccess: autoCache?.lastSuccess });
|
|
228
|
-
try {
|
|
229
|
-
const { spawn: spawn8 } = await import("child_process");
|
|
230
|
-
const child = spawn8("npm", ["update", "-g", "squads-cli"], {
|
|
231
|
-
detached: true,
|
|
232
|
-
stdio: silent ? "ignore" : ["ignore", "pipe", "pipe"],
|
|
233
|
-
shell: true
|
|
234
|
-
});
|
|
235
|
-
if (!silent && child.stdout) {
|
|
236
|
-
await new Promise((resolve) => {
|
|
237
|
-
child.on("close", (code) => {
|
|
238
|
-
if (code === 0) {
|
|
239
|
-
console.log(`
|
|
240
|
-
\x1B[32m\u2713\x1B[0m Update successful! v${info.latestVersion} will be used on your next run.
|
|
241
|
-
`);
|
|
242
|
-
writeAutoUpdateCache({ lastAttempt: now, lastSuccess: now });
|
|
243
|
-
try {
|
|
244
|
-
unlinkSync(CACHE_FILE);
|
|
245
|
-
} catch {
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
resolve();
|
|
249
|
-
});
|
|
250
|
-
setTimeout(() => resolve(), 3e4);
|
|
251
|
-
});
|
|
252
|
-
} else {
|
|
253
|
-
child.unref();
|
|
254
|
-
}
|
|
255
|
-
} catch {
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
58
|
// src/commands/init.ts
|
|
260
59
|
import chalk from "chalk";
|
|
261
60
|
import ora from "ora";
|
|
262
61
|
import fs from "fs/promises";
|
|
263
62
|
import path from "path";
|
|
264
|
-
import { execSync as
|
|
63
|
+
import { execSync as execSync2, spawn } from "child_process";
|
|
265
64
|
import { createInterface } from "readline";
|
|
266
65
|
|
|
267
66
|
// src/lib/git.ts
|
|
268
|
-
import { execSync
|
|
269
|
-
import { existsSync
|
|
270
|
-
import { join
|
|
67
|
+
import { execSync, exec } from "child_process";
|
|
68
|
+
import { existsSync } from "fs";
|
|
69
|
+
import { join } from "path";
|
|
271
70
|
import { promisify } from "util";
|
|
272
71
|
var execAsync = promisify(exec);
|
|
273
72
|
function checkGitStatus(cwd = process.cwd()) {
|
|
@@ -277,12 +76,12 @@ function checkGitStatus(cwd = process.cwd()) {
|
|
|
277
76
|
isDirty: false,
|
|
278
77
|
uncommittedCount: 0
|
|
279
78
|
};
|
|
280
|
-
if (!
|
|
79
|
+
if (!existsSync(join(cwd, ".git"))) {
|
|
281
80
|
return status;
|
|
282
81
|
}
|
|
283
82
|
status.isGitRepo = true;
|
|
284
83
|
try {
|
|
285
|
-
const combined =
|
|
84
|
+
const combined = execSync(
|
|
286
85
|
'echo "BRANCH:" && git rev-parse --abbrev-ref HEAD && echo "REMOTES:" && git remote -v && echo "STATUS:" && git status --porcelain',
|
|
287
86
|
{ cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 }
|
|
288
87
|
);
|
|
@@ -366,10 +165,10 @@ function getGitHubStatsOptimized(basePath, days = 30) {
|
|
|
366
165
|
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1e3).toISOString();
|
|
367
166
|
const results = [];
|
|
368
167
|
for (const repo of repos) {
|
|
369
|
-
const repoPath =
|
|
370
|
-
if (!
|
|
168
|
+
const repoPath = join(basePath, repo);
|
|
169
|
+
if (!existsSync(repoPath)) continue;
|
|
371
170
|
try {
|
|
372
|
-
const output =
|
|
171
|
+
const output = execSync(
|
|
373
172
|
`echo '{"prs":' && gh pr list --state all --json number,title,createdAt,mergedAt,labels --limit 50 2>/dev/null && echo ',"issues":' && gh issue list --state all --json number,title,state,closedAt,labels --limit 50 2>/dev/null && echo '}'`,
|
|
374
173
|
{ cwd: repoPath, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 1e4 }
|
|
375
174
|
);
|
|
@@ -380,11 +179,11 @@ function getGitHubStatsOptimized(basePath, days = 30) {
|
|
|
380
179
|
results.push({ repo, prs, issues });
|
|
381
180
|
} catch {
|
|
382
181
|
try {
|
|
383
|
-
const prsOutput =
|
|
182
|
+
const prsOutput = execSync(
|
|
384
183
|
`gh pr list --state all --json number,title,createdAt,mergedAt,labels --limit 50 2>/dev/null`,
|
|
385
184
|
{ cwd: repoPath, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 }
|
|
386
185
|
);
|
|
387
|
-
const issuesOutput =
|
|
186
|
+
const issuesOutput = execSync(
|
|
388
187
|
`gh issue list --state all --json number,title,state,closedAt,labels --limit 50 2>/dev/null`,
|
|
389
188
|
{ cwd: repoPath, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 }
|
|
390
189
|
);
|
|
@@ -500,12 +299,12 @@ function getMultiRepoGitStats(basePath, days = 30) {
|
|
|
500
299
|
};
|
|
501
300
|
const allCommits = [];
|
|
502
301
|
for (const repo of SQUAD_REPOS) {
|
|
503
|
-
const repoPath =
|
|
504
|
-
if (!
|
|
302
|
+
const repoPath = join(basePath, repo);
|
|
303
|
+
if (!existsSync(repoPath) || !existsSync(join(repoPath, ".git"))) {
|
|
505
304
|
continue;
|
|
506
305
|
}
|
|
507
306
|
try {
|
|
508
|
-
const logOutput =
|
|
307
|
+
const logOutput = execSync(
|
|
509
308
|
`git log --since="${days} days ago" --format="%H|%aN|%ad|%s" --date=short 2>/dev/null`,
|
|
510
309
|
{ cwd: repoPath, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
511
310
|
).trim();
|
|
@@ -562,12 +361,12 @@ function getActivitySparkline(basePath, days = 7) {
|
|
|
562
361
|
activity.push(0);
|
|
563
362
|
}
|
|
564
363
|
for (const repo of SQUAD_REPOS) {
|
|
565
|
-
const repoPath =
|
|
566
|
-
if (!
|
|
364
|
+
const repoPath = join(basePath, repo);
|
|
365
|
+
if (!existsSync(repoPath) || !existsSync(join(repoPath, ".git"))) {
|
|
567
366
|
continue;
|
|
568
367
|
}
|
|
569
368
|
try {
|
|
570
|
-
const logOutput =
|
|
369
|
+
const logOutput = execSync(
|
|
571
370
|
`git log --since="${days} days ago" --format="%ad" --date=short 2>/dev/null`,
|
|
572
371
|
{ cwd: repoPath, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
573
372
|
).trim();
|
|
@@ -587,13 +386,13 @@ function getActivitySparkline(basePath, days = 7) {
|
|
|
587
386
|
}
|
|
588
387
|
|
|
589
388
|
// src/lib/telemetry.ts
|
|
590
|
-
import { existsSync as
|
|
591
|
-
import { join as
|
|
592
|
-
import { homedir
|
|
389
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
390
|
+
import { join as join2 } from "path";
|
|
391
|
+
import { homedir, platform, release } from "os";
|
|
593
392
|
import { randomUUID } from "crypto";
|
|
594
|
-
var TELEMETRY_DIR =
|
|
595
|
-
var CONFIG_PATH =
|
|
596
|
-
var EVENTS_PATH =
|
|
393
|
+
var TELEMETRY_DIR = join2(homedir(), ".squads-cli");
|
|
394
|
+
var CONFIG_PATH = join2(TELEMETRY_DIR, "telemetry.json");
|
|
395
|
+
var EVENTS_PATH = join2(TELEMETRY_DIR, "events.json");
|
|
597
396
|
var TELEMETRY_ENDPOINT = Buffer.from(
|
|
598
397
|
"aHR0cHM6Ly9zcXVhZHMtdGVsZW1ldHJ5LTk3ODg3MTgxNzYxMC51cy1jZW50cmFsMS5ydW4uYXBwL3Bpbmc=",
|
|
599
398
|
"base64"
|
|
@@ -601,25 +400,39 @@ var TELEMETRY_ENDPOINT = Buffer.from(
|
|
|
601
400
|
var TELEMETRY_KEY = Buffer.from("c3FfdGVsX3YxXzdmOGE5YjJjM2Q0ZTVmNmE=", "base64").toString();
|
|
602
401
|
var eventQueue = [];
|
|
603
402
|
var flushScheduled = false;
|
|
403
|
+
var cachedSystemContext = null;
|
|
404
|
+
function getSystemContext() {
|
|
405
|
+
if (cachedSystemContext) return cachedSystemContext;
|
|
406
|
+
cachedSystemContext = {
|
|
407
|
+
os: platform(),
|
|
408
|
+
// darwin, linux, win32
|
|
409
|
+
osVersion: release(),
|
|
410
|
+
nodeVersion: process.version,
|
|
411
|
+
shell: process.env.SHELL?.split("/").pop() || process.env.ComSpec?.split("\\").pop(),
|
|
412
|
+
terminal: process.env.TERM_PROGRAM || void 0,
|
|
413
|
+
ci: process.env.CI === "true" ? "true" : void 0
|
|
414
|
+
};
|
|
415
|
+
return cachedSystemContext;
|
|
416
|
+
}
|
|
604
417
|
function ensureDir() {
|
|
605
|
-
if (!
|
|
606
|
-
|
|
418
|
+
if (!existsSync2(TELEMETRY_DIR)) {
|
|
419
|
+
mkdirSync(TELEMETRY_DIR, { recursive: true });
|
|
607
420
|
}
|
|
608
421
|
}
|
|
609
422
|
function getConfig() {
|
|
610
423
|
ensureDir();
|
|
611
|
-
if (!
|
|
424
|
+
if (!existsSync2(CONFIG_PATH)) {
|
|
612
425
|
const config2 = {
|
|
613
426
|
enabled: true,
|
|
614
427
|
// Opt-out by default (common for CLIs)
|
|
615
428
|
anonymousId: randomUUID(),
|
|
616
429
|
firstRun: (/* @__PURE__ */ new Date()).toISOString()
|
|
617
430
|
};
|
|
618
|
-
|
|
431
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config2, null, 2));
|
|
619
432
|
return config2;
|
|
620
433
|
}
|
|
621
434
|
try {
|
|
622
|
-
return JSON.parse(
|
|
435
|
+
return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
623
436
|
} catch {
|
|
624
437
|
return { enabled: false, anonymousId: "", firstRun: "" };
|
|
625
438
|
}
|
|
@@ -641,6 +454,7 @@ async function track(event, properties) {
|
|
|
641
454
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
642
455
|
properties: {
|
|
643
456
|
...properties,
|
|
457
|
+
...getSystemContext(),
|
|
644
458
|
anonymousId: config2.anonymousId,
|
|
645
459
|
cliVersion: process.env.npm_package_version || "unknown"
|
|
646
460
|
}
|
|
@@ -679,9 +493,9 @@ async function flushEvents() {
|
|
|
679
493
|
function storeEventLocally(event) {
|
|
680
494
|
ensureDir();
|
|
681
495
|
let events = [];
|
|
682
|
-
if (
|
|
496
|
+
if (existsSync2(EVENTS_PATH)) {
|
|
683
497
|
try {
|
|
684
|
-
events = JSON.parse(
|
|
498
|
+
events = JSON.parse(readFileSync(EVENTS_PATH, "utf-8"));
|
|
685
499
|
} catch {
|
|
686
500
|
events = [];
|
|
687
501
|
}
|
|
@@ -690,7 +504,7 @@ function storeEventLocally(event) {
|
|
|
690
504
|
if (events.length > 1e3) {
|
|
691
505
|
events = events.slice(-1e3);
|
|
692
506
|
}
|
|
693
|
-
|
|
507
|
+
writeFileSync(EVENTS_PATH, JSON.stringify(events, null, 2));
|
|
694
508
|
}
|
|
695
509
|
var Events = {
|
|
696
510
|
// Lifecycle
|
|
@@ -787,7 +601,7 @@ async function promptEmail() {
|
|
|
787
601
|
}
|
|
788
602
|
function commandExists(cmd) {
|
|
789
603
|
try {
|
|
790
|
-
|
|
604
|
+
execSync2(`which ${cmd}`, { stdio: "ignore" });
|
|
791
605
|
return true;
|
|
792
606
|
} catch {
|
|
793
607
|
return false;
|
|
@@ -795,7 +609,7 @@ function commandExists(cmd) {
|
|
|
795
609
|
}
|
|
796
610
|
function dockerRunning() {
|
|
797
611
|
try {
|
|
798
|
-
|
|
612
|
+
execSync2("docker info", { stdio: "ignore" });
|
|
799
613
|
return true;
|
|
800
614
|
} catch {
|
|
801
615
|
return false;
|
|
@@ -803,12 +617,12 @@ function dockerRunning() {
|
|
|
803
617
|
}
|
|
804
618
|
function checkClaudeAuth() {
|
|
805
619
|
try {
|
|
806
|
-
|
|
620
|
+
execSync2("which claude", { stdio: "ignore" });
|
|
807
621
|
} catch {
|
|
808
622
|
return { installed: false, loggedIn: false };
|
|
809
623
|
}
|
|
810
624
|
try {
|
|
811
|
-
const result =
|
|
625
|
+
const result = execSync2("claude --version", { stdio: "pipe" }).toString();
|
|
812
626
|
return { installed: true, loggedIn: result.includes("claude") };
|
|
813
627
|
} catch {
|
|
814
628
|
return { installed: true, loggedIn: false };
|
|
@@ -816,7 +630,7 @@ function checkClaudeAuth() {
|
|
|
816
630
|
}
|
|
817
631
|
function checkGhAuth() {
|
|
818
632
|
try {
|
|
819
|
-
|
|
633
|
+
execSync2("gh auth status", { stdio: "ignore" });
|
|
820
634
|
return true;
|
|
821
635
|
} catch {
|
|
822
636
|
return false;
|
|
@@ -953,7 +767,7 @@ async function setupInfrastructure(cwd) {
|
|
|
953
767
|
let allRunning = true;
|
|
954
768
|
for (const service of services) {
|
|
955
769
|
try {
|
|
956
|
-
|
|
770
|
+
execSync2(`docker ps --filter "name=${service}" --filter "status=running" -q`, { stdio: "pipe" });
|
|
957
771
|
} catch {
|
|
958
772
|
allRunning = false;
|
|
959
773
|
}
|
|
@@ -1038,8 +852,9 @@ async function initCommand(options) {
|
|
|
1038
852
|
}
|
|
1039
853
|
}
|
|
1040
854
|
console.log();
|
|
1041
|
-
if (hasMissingRequired) {
|
|
855
|
+
if (hasMissingRequired && !options.force) {
|
|
1042
856
|
console.log(chalk.yellow(" Install missing tools to continue, then run squads init again."));
|
|
857
|
+
console.log(chalk.dim(" Or use --force to skip requirement checks."));
|
|
1043
858
|
console.log();
|
|
1044
859
|
track(Events.CLI_INIT, {
|
|
1045
860
|
success: false,
|
|
@@ -1068,8 +883,11 @@ async function initCommand(options) {
|
|
|
1068
883
|
".agents/squads",
|
|
1069
884
|
".agents/squads/demo",
|
|
1070
885
|
".agents/memory",
|
|
886
|
+
".agents/memory/getting-started",
|
|
1071
887
|
".agents/outputs",
|
|
1072
|
-
".claude"
|
|
888
|
+
".claude",
|
|
889
|
+
".claude/skills",
|
|
890
|
+
".claude/skills/squads-workflow"
|
|
1073
891
|
];
|
|
1074
892
|
for (const dir of dirs) {
|
|
1075
893
|
await fs.mkdir(path.join(cwd, dir), { recursive: true });
|
|
@@ -1170,6 +988,139 @@ Markdown report saved to .agents/outputs/demo/project-analysis.md
|
|
|
1170
988
|
path.join(cwd, ".agents/squads/demo/analyzer.md"),
|
|
1171
989
|
analyzerAgent
|
|
1172
990
|
);
|
|
991
|
+
const squadsWorkflowSkill = `# Squads Workflow
|
|
992
|
+
|
|
993
|
+
Use this skill when working with squads-cli to maintain persistent memory, track goals, and coordinate work.
|
|
994
|
+
|
|
995
|
+
## Session Start
|
|
996
|
+
|
|
997
|
+
At session start, you'll see \`squads status\` output automatically. For complex tasks, run:
|
|
998
|
+
|
|
999
|
+
\`\`\`bash
|
|
1000
|
+
squads context # Get business context, goals, decisions
|
|
1001
|
+
squads memory query "<topic>" # Check what we already know
|
|
1002
|
+
\`\`\`
|
|
1003
|
+
|
|
1004
|
+
**Skip context loading for simple tasks** (typo fixes, quick questions).
|
|
1005
|
+
|
|
1006
|
+
## Core Commands
|
|
1007
|
+
|
|
1008
|
+
\`\`\`bash
|
|
1009
|
+
# Context & Status
|
|
1010
|
+
squads context # Business context for alignment
|
|
1011
|
+
squads status # Squad overview
|
|
1012
|
+
squads dash # Full dashboard
|
|
1013
|
+
|
|
1014
|
+
# Memory
|
|
1015
|
+
squads memory query "<topic>" # Search memory
|
|
1016
|
+
squads memory show <squad> # Squad's full memory
|
|
1017
|
+
|
|
1018
|
+
# Goals
|
|
1019
|
+
squads goal list # All active goals
|
|
1020
|
+
squads goal set <squad> "X" # Add a goal
|
|
1021
|
+
|
|
1022
|
+
# Running Agents
|
|
1023
|
+
squads run <squad> # Run all agents in squad
|
|
1024
|
+
squads run <squad>/<agent> # Run specific agent
|
|
1025
|
+
squads list # List all agents
|
|
1026
|
+
\`\`\`
|
|
1027
|
+
|
|
1028
|
+
## Workflow
|
|
1029
|
+
|
|
1030
|
+
### Before Research
|
|
1031
|
+
Always check memory first to avoid re-researching:
|
|
1032
|
+
\`\`\`bash
|
|
1033
|
+
squads memory query "topic"
|
|
1034
|
+
\`\`\`
|
|
1035
|
+
|
|
1036
|
+
### After Work
|
|
1037
|
+
Update memory with what you learned by editing:
|
|
1038
|
+
\`.agents/memory/<squad>/<agent>/state.md\`
|
|
1039
|
+
|
|
1040
|
+
### Commits
|
|
1041
|
+
Include goal attribution when relevant:
|
|
1042
|
+
\`\`\`
|
|
1043
|
+
feat: add user auth [goal:engineering/1]
|
|
1044
|
+
\`\`\`
|
|
1045
|
+
|
|
1046
|
+
## Agent Execution
|
|
1047
|
+
|
|
1048
|
+
When a task could be automated:
|
|
1049
|
+
1. Check if agent exists: \`squads list | grep <keyword>\`
|
|
1050
|
+
2. If yes: \`squads run <squad>/<agent>\`
|
|
1051
|
+
3. If no: Create agent in \`.agents/squads/<squad>/<name>.md\`
|
|
1052
|
+
|
|
1053
|
+
## Memory Locations
|
|
1054
|
+
|
|
1055
|
+
- \`.agents/memory/<squad>/<agent>/state.md\` - Current knowledge
|
|
1056
|
+
- \`.agents/memory/<squad>/<agent>/learnings.md\` - Insights over time
|
|
1057
|
+
|
|
1058
|
+
## Key Principle
|
|
1059
|
+
|
|
1060
|
+
**Memory is your cross-session brain.** Without it, every session starts fresh. With it, you build on previous work.
|
|
1061
|
+
`;
|
|
1062
|
+
await fs.writeFile(
|
|
1063
|
+
path.join(cwd, ".claude/skills/squads-workflow/instruction.md"),
|
|
1064
|
+
squadsWorkflowSkill
|
|
1065
|
+
);
|
|
1066
|
+
const seedMemory = `# Getting Started with Squads
|
|
1067
|
+
|
|
1068
|
+
## What You've Set Up
|
|
1069
|
+
|
|
1070
|
+
- **Demo squad** ready to run (\`squads run demo\`)
|
|
1071
|
+
- **Memory system** for persistent context
|
|
1072
|
+
- **Hooks** that sync status and memory automatically
|
|
1073
|
+
|
|
1074
|
+
## Next Steps
|
|
1075
|
+
|
|
1076
|
+
1. Run the demo: \`squads run demo\`
|
|
1077
|
+
2. Check the dashboard: \`squads dash\`
|
|
1078
|
+
3. Create your first real squad in \`.agents/squads/\`
|
|
1079
|
+
|
|
1080
|
+
## Tips
|
|
1081
|
+
|
|
1082
|
+
- Check memory before researching: \`squads memory query "<topic>"\`
|
|
1083
|
+
- Use \`squads context\` for business alignment on complex tasks
|
|
1084
|
+
- Agents are reusable - if you do something twice, make it an agent
|
|
1085
|
+
`;
|
|
1086
|
+
await fs.writeFile(
|
|
1087
|
+
path.join(cwd, ".agents/memory/getting-started/state.md"),
|
|
1088
|
+
seedMemory
|
|
1089
|
+
);
|
|
1090
|
+
const businessBrief = `# Business Brief
|
|
1091
|
+
|
|
1092
|
+
## #1 Priority
|
|
1093
|
+
|
|
1094
|
+
**[Define your top priority here]**
|
|
1095
|
+
|
|
1096
|
+
## Runway
|
|
1097
|
+
|
|
1098
|
+
**Pressure**: LOW | MEDIUM | HIGH
|
|
1099
|
+
|
|
1100
|
+
## Current Focus
|
|
1101
|
+
|
|
1102
|
+
1. **[Focus area 1]** - Description
|
|
1103
|
+
2. **[Focus area 2]** - Description
|
|
1104
|
+
3. **[Focus area 3]** - Description
|
|
1105
|
+
|
|
1106
|
+
## Blockers
|
|
1107
|
+
|
|
1108
|
+
- None currently (or list blockers)
|
|
1109
|
+
|
|
1110
|
+
## Decision Framework
|
|
1111
|
+
|
|
1112
|
+
1. Does this drive the #1 priority?
|
|
1113
|
+
2. Is there a simpler approach?
|
|
1114
|
+
3. What's the opportunity cost?
|
|
1115
|
+
|
|
1116
|
+
---
|
|
1117
|
+
|
|
1118
|
+
*This file is read by \`squads context\` to provide business alignment.*
|
|
1119
|
+
`;
|
|
1120
|
+
await fs.writeFile(
|
|
1121
|
+
path.join(cwd, ".agents/BUSINESS_BRIEF.md"),
|
|
1122
|
+
businessBrief
|
|
1123
|
+
);
|
|
1173
1124
|
const claudeSettings = {
|
|
1174
1125
|
hooks: {
|
|
1175
1126
|
SessionStart: [
|
|
@@ -1224,12 +1175,22 @@ Each **squad** is a team of **agents** (markdown prompts) that execute via Claud
|
|
|
1224
1175
|
|
|
1225
1176
|
## For Claude (READ THIS)
|
|
1226
1177
|
|
|
1227
|
-
|
|
1178
|
+
**Skill available**: Use \`/squads-workflow\` for detailed workflow guidance.
|
|
1179
|
+
|
|
1180
|
+
### Session Start
|
|
1228
1181
|
|
|
1229
|
-
|
|
1182
|
+
You'll see \`squads status\` automatically. For complex tasks, also run:
|
|
1230
1183
|
\`\`\`bash
|
|
1231
|
-
squads
|
|
1232
|
-
squads memory query "
|
|
1184
|
+
squads context # Business context, goals, decisions
|
|
1185
|
+
squads memory query "<topic>" # What we already know
|
|
1186
|
+
\`\`\`
|
|
1187
|
+
|
|
1188
|
+
**Skip for simple tasks** (typo fixes, quick questions).
|
|
1189
|
+
|
|
1190
|
+
### Before Research
|
|
1191
|
+
Always check memory first:
|
|
1192
|
+
\`\`\`bash
|
|
1193
|
+
squads memory query "<topic>"
|
|
1233
1194
|
\`\`\`
|
|
1234
1195
|
|
|
1235
1196
|
### Creating Agents
|
|
@@ -1272,10 +1233,12 @@ squads goal set <squad> "X" # Add a goal
|
|
|
1272
1233
|
| "What's the status?" | Run \`squads dash\` or \`squads status\` |
|
|
1273
1234
|
| "Run the X agent" | \`squads run <squad>/x\` |
|
|
1274
1235
|
| "Check memory" | \`squads memory query "<topic>"\` |
|
|
1236
|
+
| "Get context" | \`squads context\` |
|
|
1275
1237
|
|
|
1276
1238
|
## Quick Reference
|
|
1277
1239
|
|
|
1278
1240
|
\`\`\`bash
|
|
1241
|
+
squads context # Business context for alignment
|
|
1279
1242
|
squads status # Overview
|
|
1280
1243
|
squads dash # Full dashboard
|
|
1281
1244
|
squads run demo # Try demo squad
|
|
@@ -1338,9 +1301,12 @@ squads goal list # View goals
|
|
|
1338
1301
|
console.log(chalk.green.bold(" \u2713 Squads initialized!"));
|
|
1339
1302
|
console.log();
|
|
1340
1303
|
console.log(chalk.dim(" Created:"));
|
|
1341
|
-
console.log(chalk.dim(" \u2022 .agents/squads/demo/
|
|
1342
|
-
console.log(chalk.dim(" \u2022 .
|
|
1343
|
-
console.log(chalk.dim(" \u2022
|
|
1304
|
+
console.log(chalk.dim(" \u2022 .agents/squads/demo/ - Demo squad with 2 agents"));
|
|
1305
|
+
console.log(chalk.dim(" \u2022 .agents/BUSINESS_BRIEF - Business context template"));
|
|
1306
|
+
console.log(chalk.dim(" \u2022 .agents/memory/ - Seed memory"));
|
|
1307
|
+
console.log(chalk.dim(" \u2022 .claude/settings.json - Claude Code hooks"));
|
|
1308
|
+
console.log(chalk.dim(" \u2022 .claude/skills/ - Squads workflow skill"));
|
|
1309
|
+
console.log(chalk.dim(" \u2022 CLAUDE.md - Agent instructions"));
|
|
1344
1310
|
console.log();
|
|
1345
1311
|
const email = await promptEmail();
|
|
1346
1312
|
if (email) {
|
|
@@ -1365,21 +1331,21 @@ squads goal list # View goals
|
|
|
1365
1331
|
// src/commands/run.ts
|
|
1366
1332
|
import ora2 from "ora";
|
|
1367
1333
|
import { spawn as spawn2 } from "child_process";
|
|
1368
|
-
import { join as
|
|
1369
|
-
import { existsSync as
|
|
1334
|
+
import { join as join4, dirname } from "path";
|
|
1335
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
1370
1336
|
|
|
1371
1337
|
// src/lib/squad-parser.ts
|
|
1372
|
-
import { readFileSync as
|
|
1373
|
-
import { join as
|
|
1338
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3, readdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1339
|
+
import { join as join3, basename } from "path";
|
|
1374
1340
|
import matter from "gray-matter";
|
|
1375
1341
|
function findSquadsDir() {
|
|
1376
1342
|
let dir = process.cwd();
|
|
1377
1343
|
for (let i = 0; i < 5; i++) {
|
|
1378
|
-
const squadsPath =
|
|
1379
|
-
if (
|
|
1344
|
+
const squadsPath = join3(dir, ".agents", "squads");
|
|
1345
|
+
if (existsSync3(squadsPath)) {
|
|
1380
1346
|
return squadsPath;
|
|
1381
1347
|
}
|
|
1382
|
-
const parent =
|
|
1348
|
+
const parent = join3(dir, "..");
|
|
1383
1349
|
if (parent === dir) break;
|
|
1384
1350
|
dir = parent;
|
|
1385
1351
|
}
|
|
@@ -1388,14 +1354,14 @@ function findSquadsDir() {
|
|
|
1388
1354
|
function findProjectRoot() {
|
|
1389
1355
|
const squadsDir = findSquadsDir();
|
|
1390
1356
|
if (!squadsDir) return null;
|
|
1391
|
-
return
|
|
1357
|
+
return join3(squadsDir, "..", "..");
|
|
1392
1358
|
}
|
|
1393
1359
|
function hasLocalInfraConfig() {
|
|
1394
1360
|
const projectRoot = findProjectRoot();
|
|
1395
1361
|
if (!projectRoot) return false;
|
|
1396
|
-
const envPath =
|
|
1397
|
-
if (!
|
|
1398
|
-
const content =
|
|
1362
|
+
const envPath = join3(projectRoot, ".env");
|
|
1363
|
+
if (!existsSync3(envPath)) return false;
|
|
1364
|
+
const content = readFileSync2(envPath, "utf-8");
|
|
1399
1365
|
const infraKeys = ["LANGFUSE_", "SQUADS_BRIDGE", "SQUADS_POSTGRES", "SQUADS_REDIS"];
|
|
1400
1366
|
return infraKeys.some((key) => content.includes(key));
|
|
1401
1367
|
}
|
|
@@ -1404,8 +1370,8 @@ function listSquads(squadsDir) {
|
|
|
1404
1370
|
const entries = readdirSync(squadsDir, { withFileTypes: true });
|
|
1405
1371
|
for (const entry of entries) {
|
|
1406
1372
|
if (entry.isDirectory() && !entry.name.startsWith("_")) {
|
|
1407
|
-
const squadFile =
|
|
1408
|
-
if (
|
|
1373
|
+
const squadFile = join3(squadsDir, entry.name, "SQUAD.md");
|
|
1374
|
+
if (existsSync3(squadFile)) {
|
|
1409
1375
|
squads.push(entry.name);
|
|
1410
1376
|
}
|
|
1411
1377
|
}
|
|
@@ -1416,8 +1382,8 @@ function listAgents(squadsDir, squadName) {
|
|
|
1416
1382
|
const agents = [];
|
|
1417
1383
|
const dirs = squadName ? [squadName] : readdirSync(squadsDir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_")).map((e) => e.name);
|
|
1418
1384
|
for (const dir of dirs) {
|
|
1419
|
-
const squadPath =
|
|
1420
|
-
if (!
|
|
1385
|
+
const squadPath = join3(squadsDir, dir);
|
|
1386
|
+
if (!existsSync3(squadPath)) continue;
|
|
1421
1387
|
const files = readdirSync(squadPath);
|
|
1422
1388
|
for (const file of files) {
|
|
1423
1389
|
if (file.endsWith(".md") && file !== "SQUAD.md") {
|
|
@@ -1426,7 +1392,7 @@ function listAgents(squadsDir, squadName) {
|
|
|
1426
1392
|
name: agentName,
|
|
1427
1393
|
role: `Agent in ${dir}`,
|
|
1428
1394
|
trigger: "manual",
|
|
1429
|
-
filePath:
|
|
1395
|
+
filePath: join3(squadPath, file)
|
|
1430
1396
|
});
|
|
1431
1397
|
}
|
|
1432
1398
|
}
|
|
@@ -1434,7 +1400,7 @@ function listAgents(squadsDir, squadName) {
|
|
|
1434
1400
|
return agents;
|
|
1435
1401
|
}
|
|
1436
1402
|
function parseSquadFile(filePath) {
|
|
1437
|
-
const rawContent =
|
|
1403
|
+
const rawContent = readFileSync2(filePath, "utf-8");
|
|
1438
1404
|
const { data: frontmatter, content: bodyContent } = matter(rawContent);
|
|
1439
1405
|
const fm = frontmatter;
|
|
1440
1406
|
const lines = bodyContent.split("\n");
|
|
@@ -1555,20 +1521,20 @@ function parseSquadFile(filePath) {
|
|
|
1555
1521
|
function loadSquad(squadName) {
|
|
1556
1522
|
const squadsDir = findSquadsDir();
|
|
1557
1523
|
if (!squadsDir) return null;
|
|
1558
|
-
const squadFile =
|
|
1559
|
-
if (!
|
|
1524
|
+
const squadFile = join3(squadsDir, squadName, "SQUAD.md");
|
|
1525
|
+
if (!existsSync3(squadFile)) return null;
|
|
1560
1526
|
return parseSquadFile(squadFile);
|
|
1561
1527
|
}
|
|
1562
1528
|
function loadAgentDefinition(agentPath) {
|
|
1563
|
-
if (!
|
|
1564
|
-
return
|
|
1529
|
+
if (!existsSync3(agentPath)) return "";
|
|
1530
|
+
return readFileSync2(agentPath, "utf-8");
|
|
1565
1531
|
}
|
|
1566
1532
|
function addGoalToSquad(squadName, goal2) {
|
|
1567
1533
|
const squadsDir = findSquadsDir();
|
|
1568
1534
|
if (!squadsDir) return false;
|
|
1569
|
-
const squadFile =
|
|
1570
|
-
if (!
|
|
1571
|
-
let content =
|
|
1535
|
+
const squadFile = join3(squadsDir, squadName, "SQUAD.md");
|
|
1536
|
+
if (!existsSync3(squadFile)) return false;
|
|
1537
|
+
let content = readFileSync2(squadFile, "utf-8");
|
|
1572
1538
|
if (!content.includes("## Goals")) {
|
|
1573
1539
|
const insertPoint = content.indexOf("## Dependencies");
|
|
1574
1540
|
if (insertPoint > 0) {
|
|
@@ -1603,15 +1569,15 @@ function addGoalToSquad(squadName, goal2) {
|
|
|
1603
1569
|
- [ ] ${goal2}` + content.slice(headerEnd);
|
|
1604
1570
|
}
|
|
1605
1571
|
}
|
|
1606
|
-
|
|
1572
|
+
writeFileSync2(squadFile, content);
|
|
1607
1573
|
return true;
|
|
1608
1574
|
}
|
|
1609
1575
|
function updateGoalInSquad(squadName, goalIndex, updates) {
|
|
1610
1576
|
const squadsDir = findSquadsDir();
|
|
1611
1577
|
if (!squadsDir) return false;
|
|
1612
|
-
const squadFile =
|
|
1613
|
-
if (!
|
|
1614
|
-
const content =
|
|
1578
|
+
const squadFile = join3(squadsDir, squadName, "SQUAD.md");
|
|
1579
|
+
if (!existsSync3(squadFile)) return false;
|
|
1580
|
+
const content = readFileSync2(squadFile, "utf-8");
|
|
1615
1581
|
const lines = content.split("\n");
|
|
1616
1582
|
let currentSection = "";
|
|
1617
1583
|
let goalCount = 0;
|
|
@@ -1633,7 +1599,7 @@ function updateGoalInSquad(squadName, goalIndex, updates) {
|
|
|
1633
1599
|
}
|
|
1634
1600
|
}
|
|
1635
1601
|
lines[i] = newLine;
|
|
1636
|
-
|
|
1602
|
+
writeFileSync2(squadFile, lines.join("\n"));
|
|
1637
1603
|
return true;
|
|
1638
1604
|
}
|
|
1639
1605
|
goalCount++;
|
|
@@ -1734,11 +1700,11 @@ function validateMcpServer(server, allow, deny) {
|
|
|
1734
1700
|
}
|
|
1735
1701
|
return null;
|
|
1736
1702
|
}
|
|
1737
|
-
function validateExecution(
|
|
1703
|
+
function validateExecution(context, request) {
|
|
1738
1704
|
const violations = [];
|
|
1739
1705
|
if (request.bashCommands) {
|
|
1740
1706
|
for (const cmd of request.bashCommands) {
|
|
1741
|
-
const violation = validateBashCommand(cmd,
|
|
1707
|
+
const violation = validateBashCommand(cmd, context.permissions.bash);
|
|
1742
1708
|
if (violation) {
|
|
1743
1709
|
violations.push(violation);
|
|
1744
1710
|
}
|
|
@@ -1746,7 +1712,7 @@ function validateExecution(context2, request) {
|
|
|
1746
1712
|
}
|
|
1747
1713
|
if (request.writePaths) {
|
|
1748
1714
|
for (const path3 of request.writePaths) {
|
|
1749
|
-
const violation = validateFilePath(path3,
|
|
1715
|
+
const violation = validateFilePath(path3, context.permissions.write, "write");
|
|
1750
1716
|
if (violation) {
|
|
1751
1717
|
violations.push(violation);
|
|
1752
1718
|
}
|
|
@@ -1754,7 +1720,7 @@ function validateExecution(context2, request) {
|
|
|
1754
1720
|
}
|
|
1755
1721
|
if (request.readPaths) {
|
|
1756
1722
|
for (const path3 of request.readPaths) {
|
|
1757
|
-
const violation = validateFilePath(path3,
|
|
1723
|
+
const violation = validateFilePath(path3, context.permissions.read, "read");
|
|
1758
1724
|
if (violation) {
|
|
1759
1725
|
violations.push(violation);
|
|
1760
1726
|
}
|
|
@@ -1764,8 +1730,8 @@ function validateExecution(context2, request) {
|
|
|
1764
1730
|
for (const server of request.mcpServers) {
|
|
1765
1731
|
const violation = validateMcpServer(
|
|
1766
1732
|
server,
|
|
1767
|
-
|
|
1768
|
-
|
|
1733
|
+
context.permissions.mcp.allow,
|
|
1734
|
+
context.permissions.mcp.deny
|
|
1769
1735
|
);
|
|
1770
1736
|
if (violation) {
|
|
1771
1737
|
violations.push(violation);
|
|
@@ -1773,11 +1739,11 @@ function validateExecution(context2, request) {
|
|
|
1773
1739
|
}
|
|
1774
1740
|
}
|
|
1775
1741
|
const hasErrors = violations.some((v) => v.severity === "error");
|
|
1776
|
-
const allowed =
|
|
1742
|
+
const allowed = context.permissions.mode !== "strict" || !hasErrors;
|
|
1777
1743
|
return {
|
|
1778
1744
|
allowed,
|
|
1779
1745
|
violations,
|
|
1780
|
-
mode:
|
|
1746
|
+
mode: context.permissions.mode
|
|
1781
1747
|
};
|
|
1782
1748
|
}
|
|
1783
1749
|
function formatViolations(result) {
|
|
@@ -1840,16 +1806,16 @@ function parsePermissionsYaml(content) {
|
|
|
1840
1806
|
return Object.keys(permissions).length > 0 ? permissions : null;
|
|
1841
1807
|
}
|
|
1842
1808
|
function buildContextFromSquad(squadName, squadContent, agentName) {
|
|
1843
|
-
const
|
|
1809
|
+
const context = getDefaultContext(squadName, agentName);
|
|
1844
1810
|
const parsed = parsePermissionsYaml(squadContent);
|
|
1845
1811
|
if (parsed) {
|
|
1846
|
-
if (parsed.mode)
|
|
1847
|
-
if (parsed.bash)
|
|
1848
|
-
if (parsed.write)
|
|
1849
|
-
if (parsed.read)
|
|
1850
|
-
if (parsed.mcp)
|
|
1812
|
+
if (parsed.mode) context.permissions.mode = parsed.mode;
|
|
1813
|
+
if (parsed.bash) context.permissions.bash = parsed.bash;
|
|
1814
|
+
if (parsed.write) context.permissions.write = parsed.write;
|
|
1815
|
+
if (parsed.read) context.permissions.read = parsed.read;
|
|
1816
|
+
if (parsed.mcp) context.permissions.mcp = parsed.mcp;
|
|
1851
1817
|
}
|
|
1852
|
-
return
|
|
1818
|
+
return context;
|
|
1853
1819
|
}
|
|
1854
1820
|
|
|
1855
1821
|
// src/commands/run.ts
|
|
@@ -1882,7 +1848,7 @@ function generateExecutionId() {
|
|
|
1882
1848
|
}
|
|
1883
1849
|
function selectMcpConfig(squadName) {
|
|
1884
1850
|
const home = process.env.HOME || "";
|
|
1885
|
-
const configsDir =
|
|
1851
|
+
const configsDir = join4(home, ".claude", "mcp-configs");
|
|
1886
1852
|
const squadConfigs = {
|
|
1887
1853
|
website: "website.json",
|
|
1888
1854
|
// chrome-devtools, nano-banana
|
|
@@ -1897,12 +1863,12 @@ function selectMcpConfig(squadName) {
|
|
|
1897
1863
|
};
|
|
1898
1864
|
const configFile = squadConfigs[squadName.toLowerCase()];
|
|
1899
1865
|
if (configFile) {
|
|
1900
|
-
const configPath =
|
|
1901
|
-
if (
|
|
1866
|
+
const configPath = join4(configsDir, configFile);
|
|
1867
|
+
if (existsSync4(configPath)) {
|
|
1902
1868
|
return configPath;
|
|
1903
1869
|
}
|
|
1904
1870
|
}
|
|
1905
|
-
return
|
|
1871
|
+
return join4(home, ".claude.json");
|
|
1906
1872
|
}
|
|
1907
1873
|
function detectTaskType(agentName) {
|
|
1908
1874
|
const name = agentName.toLowerCase();
|
|
@@ -1918,12 +1884,12 @@ function detectTaskType(agentName) {
|
|
|
1918
1884
|
return "execution";
|
|
1919
1885
|
}
|
|
1920
1886
|
function ensureProjectTrusted(projectPath) {
|
|
1921
|
-
const configPath =
|
|
1922
|
-
if (!
|
|
1887
|
+
const configPath = join4(process.env.HOME || "", ".claude.json");
|
|
1888
|
+
if (!existsSync4(configPath)) {
|
|
1923
1889
|
return;
|
|
1924
1890
|
}
|
|
1925
1891
|
try {
|
|
1926
|
-
const config2 = JSON.parse(
|
|
1892
|
+
const config2 = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1927
1893
|
if (!config2.projects) {
|
|
1928
1894
|
config2.projects = {};
|
|
1929
1895
|
}
|
|
@@ -1932,7 +1898,7 @@ function ensureProjectTrusted(projectPath) {
|
|
|
1932
1898
|
}
|
|
1933
1899
|
if (!config2.projects[projectPath].hasTrustDialogAccepted) {
|
|
1934
1900
|
config2.projects[projectPath].hasTrustDialogAccepted = true;
|
|
1935
|
-
|
|
1901
|
+
writeFileSync3(configPath, JSON.stringify(config2, null, 2));
|
|
1936
1902
|
}
|
|
1937
1903
|
} catch {
|
|
1938
1904
|
}
|
|
@@ -1940,25 +1906,25 @@ function ensureProjectTrusted(projectPath) {
|
|
|
1940
1906
|
function getProjectRoot() {
|
|
1941
1907
|
const squadsDir = findSquadsDir();
|
|
1942
1908
|
if (squadsDir) {
|
|
1943
|
-
return
|
|
1909
|
+
return dirname(dirname(squadsDir));
|
|
1944
1910
|
}
|
|
1945
1911
|
return process.cwd();
|
|
1946
1912
|
}
|
|
1947
1913
|
function getExecutionLogPath(squadName, agentName) {
|
|
1948
1914
|
const memoryDir = findMemoryDir();
|
|
1949
1915
|
if (!memoryDir) return null;
|
|
1950
|
-
return
|
|
1916
|
+
return join4(memoryDir, squadName, agentName, "executions.md");
|
|
1951
1917
|
}
|
|
1952
1918
|
function logExecution(record) {
|
|
1953
1919
|
const logPath = getExecutionLogPath(record.squadName, record.agentName);
|
|
1954
1920
|
if (!logPath) return;
|
|
1955
|
-
const dir =
|
|
1956
|
-
if (!
|
|
1957
|
-
|
|
1921
|
+
const dir = dirname(logPath);
|
|
1922
|
+
if (!existsSync4(dir)) {
|
|
1923
|
+
mkdirSync2(dir, { recursive: true });
|
|
1958
1924
|
}
|
|
1959
1925
|
let content = "";
|
|
1960
|
-
if (
|
|
1961
|
-
content =
|
|
1926
|
+
if (existsSync4(logPath)) {
|
|
1927
|
+
content = readFileSync3(logPath, "utf-8").trimEnd();
|
|
1962
1928
|
} else {
|
|
1963
1929
|
content = `# ${record.squadName}/${record.agentName} - Execution Log`;
|
|
1964
1930
|
}
|
|
@@ -1971,12 +1937,12 @@ function logExecution(record) {
|
|
|
1971
1937
|
- Trigger: ${record.trigger || "manual"}
|
|
1972
1938
|
- Task Type: ${record.taskType || "execution"}
|
|
1973
1939
|
`;
|
|
1974
|
-
|
|
1940
|
+
writeFileSync3(logPath, content + entry);
|
|
1975
1941
|
}
|
|
1976
1942
|
function updateExecutionStatus(squadName, agentName, executionId, status, details) {
|
|
1977
1943
|
const logPath = getExecutionLogPath(squadName, agentName);
|
|
1978
|
-
if (!logPath || !
|
|
1979
|
-
let content =
|
|
1944
|
+
if (!logPath || !existsSync4(logPath)) return;
|
|
1945
|
+
let content = readFileSync3(logPath, "utf-8");
|
|
1980
1946
|
const endTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
1981
1947
|
const execMarker = `<!-- exec:${executionId} -->`;
|
|
1982
1948
|
const markerIndex = content.indexOf(execMarker);
|
|
@@ -1997,7 +1963,7 @@ function updateExecutionStatus(squadName, agentName, executionId, status, detail
|
|
|
1997
1963
|
- Error: ${details.error}`;
|
|
1998
1964
|
}
|
|
1999
1965
|
content = content.slice(0, entryStart) + updatedEntry + content.slice(entryEnd);
|
|
2000
|
-
|
|
1966
|
+
writeFileSync3(logPath, content);
|
|
2001
1967
|
}
|
|
2002
1968
|
function extractMcpServersFromDefinition(definition) {
|
|
2003
1969
|
const servers = /* @__PURE__ */ new Set();
|
|
@@ -2076,8 +2042,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
2076
2042
|
if (options.parallel) {
|
|
2077
2043
|
const agentFiles = squad.agents.map((a) => ({
|
|
2078
2044
|
name: a.name,
|
|
2079
|
-
path:
|
|
2080
|
-
})).filter((a) =>
|
|
2045
|
+
path: join4(squadsDir, squad.name, `${a.name}.md`)
|
|
2046
|
+
})).filter((a) => existsSync4(a.path));
|
|
2081
2047
|
if (agentFiles.length === 0) {
|
|
2082
2048
|
writeLine(` ${icons.error} ${colors.red}No agent files found${RESET}`);
|
|
2083
2049
|
return;
|
|
@@ -2113,8 +2079,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
2113
2079
|
writeLine();
|
|
2114
2080
|
for (let i = 0; i < pipeline.agents.length; i++) {
|
|
2115
2081
|
const agentName = pipeline.agents[i];
|
|
2116
|
-
const agentPath =
|
|
2117
|
-
if (
|
|
2082
|
+
const agentPath = join4(squadsDir, squad.name, `${agentName}.md`);
|
|
2083
|
+
if (existsSync4(agentPath)) {
|
|
2118
2084
|
writeLine(` ${colors.dim}[${i + 1}/${pipeline.agents.length}]${RESET}`);
|
|
2119
2085
|
await runAgent(agentName, agentPath, squad.name, options);
|
|
2120
2086
|
writeLine();
|
|
@@ -2124,8 +2090,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
2124
2090
|
}
|
|
2125
2091
|
} else {
|
|
2126
2092
|
if (options.agent) {
|
|
2127
|
-
const agentPath =
|
|
2128
|
-
if (
|
|
2093
|
+
const agentPath = join4(squadsDir, squad.name, `${options.agent}.md`);
|
|
2094
|
+
if (existsSync4(agentPath)) {
|
|
2129
2095
|
await runAgent(options.agent, agentPath, squad.name, options);
|
|
2130
2096
|
} else {
|
|
2131
2097
|
writeLine(` ${icons.error} ${colors.red}Agent ${options.agent} not found${RESET}`);
|
|
@@ -2136,8 +2102,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
2136
2102
|
(a) => a.name.includes("lead") || a.trigger === "Manual"
|
|
2137
2103
|
);
|
|
2138
2104
|
if (orchestrator) {
|
|
2139
|
-
const agentPath =
|
|
2140
|
-
if (
|
|
2105
|
+
const agentPath = join4(squadsDir, squad.name, `${orchestrator.name}.md`);
|
|
2106
|
+
if (existsSync4(agentPath)) {
|
|
2141
2107
|
await runAgent(orchestrator.name, agentPath, squad.name, options);
|
|
2142
2108
|
}
|
|
2143
2109
|
} else {
|
|
@@ -2163,9 +2129,9 @@ async function runLeadMode(squad, squadsDir, options) {
|
|
|
2163
2129
|
if (!squad) return;
|
|
2164
2130
|
const agentFiles = squad.agents.map((a) => ({
|
|
2165
2131
|
name: a.name,
|
|
2166
|
-
path:
|
|
2132
|
+
path: join4(squadsDir, squad.name, `${a.name}.md`),
|
|
2167
2133
|
role: a.role || ""
|
|
2168
|
-
})).filter((a) =>
|
|
2134
|
+
})).filter((a) => existsSync4(a.path));
|
|
2169
2135
|
if (agentFiles.length === 0) {
|
|
2170
2136
|
writeLine(` ${icons.error} ${colors.red}No agent files found${RESET}`);
|
|
2171
2137
|
return;
|
|
@@ -2284,9 +2250,9 @@ async function runAgent(agentName, agentPath, squadName, options) {
|
|
|
2284
2250
|
}
|
|
2285
2251
|
const squadsDir = findSquadsDir();
|
|
2286
2252
|
if (squadsDir) {
|
|
2287
|
-
const squadFilePath =
|
|
2288
|
-
if (
|
|
2289
|
-
const squadContent =
|
|
2253
|
+
const squadFilePath = join4(squadsDir, squadName, "SQUAD.md");
|
|
2254
|
+
if (existsSync4(squadFilePath)) {
|
|
2255
|
+
const squadContent = readFileSync3(squadFilePath, "utf-8");
|
|
2290
2256
|
const permContext = buildContextFromSquad(squadName, squadContent, agentName);
|
|
2291
2257
|
const mcpServers = extractMcpServersFromDefinition(definition);
|
|
2292
2258
|
const execRequest = {
|
|
@@ -2566,8 +2532,8 @@ async function listCommand(options) {
|
|
|
2566
2532
|
}
|
|
2567
2533
|
|
|
2568
2534
|
// src/commands/status.ts
|
|
2569
|
-
import { existsSync as
|
|
2570
|
-
import { join as
|
|
2535
|
+
import { existsSync as existsSync5, statSync } from "fs";
|
|
2536
|
+
import { join as join5 } from "path";
|
|
2571
2537
|
async function statusCommand(squadName, options = {}) {
|
|
2572
2538
|
await track(Events.CLI_STATUS, { squad: squadName || "all", verbose: options.verbose });
|
|
2573
2539
|
const squadsDir = findSquadsDir();
|
|
@@ -2620,8 +2586,8 @@ async function showOverallStatus(squadsDir, _options) {
|
|
|
2620
2586
|
let lastActivity = `${colors.dim}\u2014${RESET}`;
|
|
2621
2587
|
let activityColor = colors.dim;
|
|
2622
2588
|
if (memoryDir) {
|
|
2623
|
-
const squadMemoryPath =
|
|
2624
|
-
if (
|
|
2589
|
+
const squadMemoryPath = join5(memoryDir, squadName);
|
|
2590
|
+
if (existsSync5(squadMemoryPath)) {
|
|
2625
2591
|
const states = getSquadState(squadName);
|
|
2626
2592
|
memoryStatus = `${colors.green}${states.length} ${states.length === 1 ? "entry" : "entries"}${RESET}`;
|
|
2627
2593
|
let mostRecent = 0;
|
|
@@ -2723,14 +2689,14 @@ async function showSquadStatus(squadName, squadsDir, options) {
|
|
|
2723
2689
|
}
|
|
2724
2690
|
|
|
2725
2691
|
// src/commands/stack.ts
|
|
2726
|
-
import { existsSync as
|
|
2727
|
-
import { join as
|
|
2728
|
-
import { homedir as
|
|
2729
|
-
import { execSync as
|
|
2692
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, copyFileSync } from "fs";
|
|
2693
|
+
import { join as join6, dirname as dirname2 } from "path";
|
|
2694
|
+
import { homedir as homedir2 } from "os";
|
|
2695
|
+
import { execSync as execSync3, spawn as spawn3 } from "child_process";
|
|
2730
2696
|
import { createInterface as createInterface2 } from "readline";
|
|
2731
|
-
import { fileURLToPath
|
|
2732
|
-
var __filename2 =
|
|
2733
|
-
var __dirname2 =
|
|
2697
|
+
import { fileURLToPath } from "url";
|
|
2698
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
2699
|
+
var __dirname2 = dirname2(__filename2);
|
|
2734
2700
|
var DEFAULT_CONFIG = {
|
|
2735
2701
|
SQUADS_DATABASE_URL: "postgresql://squads:squads@localhost:5433/squads",
|
|
2736
2702
|
SQUADS_BRIDGE_URL: "http://localhost:8088",
|
|
@@ -2739,8 +2705,8 @@ var DEFAULT_CONFIG = {
|
|
|
2739
2705
|
LANGFUSE_SECRET_KEY: "",
|
|
2740
2706
|
REDIS_URL: "redis://localhost:6379"
|
|
2741
2707
|
};
|
|
2742
|
-
var CONFIG_PATH2 =
|
|
2743
|
-
var SQUADS_DATA_DIR =
|
|
2708
|
+
var CONFIG_PATH2 = join6(homedir2(), ".squadsrc");
|
|
2709
|
+
var SQUADS_DATA_DIR = join6(homedir2(), ".squads");
|
|
2744
2710
|
var SERVICES = {
|
|
2745
2711
|
bridge: {
|
|
2746
2712
|
name: "Bridge API",
|
|
@@ -2874,25 +2840,25 @@ async function confirm2(question, defaultYes = true) {
|
|
|
2874
2840
|
function findPackageDockerDir() {
|
|
2875
2841
|
const candidates = [
|
|
2876
2842
|
// From npm package (relative to dist/commands/stack.js)
|
|
2877
|
-
|
|
2843
|
+
join6(__dirname2, "..", "..", "docker"),
|
|
2878
2844
|
// Local development
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2845
|
+
join6(process.cwd(), "docker"),
|
|
2846
|
+
join6(process.cwd(), "..", "squads-cli", "docker"),
|
|
2847
|
+
join6(homedir2(), "agents-squads", "squads-cli", "docker")
|
|
2882
2848
|
];
|
|
2883
2849
|
for (const dir of candidates) {
|
|
2884
|
-
if (
|
|
2850
|
+
if (existsSync6(join6(dir, "docker-compose.yml"))) {
|
|
2885
2851
|
return dir;
|
|
2886
2852
|
}
|
|
2887
2853
|
}
|
|
2888
2854
|
return null;
|
|
2889
2855
|
}
|
|
2890
2856
|
function loadStackConfig() {
|
|
2891
|
-
if (!
|
|
2857
|
+
if (!existsSync6(CONFIG_PATH2)) {
|
|
2892
2858
|
return null;
|
|
2893
2859
|
}
|
|
2894
2860
|
try {
|
|
2895
|
-
const content =
|
|
2861
|
+
const content = readFileSync4(CONFIG_PATH2, "utf-8");
|
|
2896
2862
|
const config2 = {};
|
|
2897
2863
|
for (const line of content.split("\n")) {
|
|
2898
2864
|
const trimmed = line.trim();
|
|
@@ -2933,7 +2899,7 @@ function saveStackConfig(config2) {
|
|
|
2933
2899
|
"# To activate: source ~/.squadsrc",
|
|
2934
2900
|
""
|
|
2935
2901
|
];
|
|
2936
|
-
|
|
2902
|
+
writeFileSync4(CONFIG_PATH2, lines.join("\n"));
|
|
2937
2903
|
}
|
|
2938
2904
|
function applyStackConfig() {
|
|
2939
2905
|
const config2 = loadStackConfig();
|
|
@@ -2946,7 +2912,7 @@ function applyStackConfig() {
|
|
|
2946
2912
|
}
|
|
2947
2913
|
function isDockerRunning() {
|
|
2948
2914
|
try {
|
|
2949
|
-
|
|
2915
|
+
execSync3("docker info", { stdio: "ignore" });
|
|
2950
2916
|
return true;
|
|
2951
2917
|
} catch {
|
|
2952
2918
|
return false;
|
|
@@ -2954,7 +2920,7 @@ function isDockerRunning() {
|
|
|
2954
2920
|
}
|
|
2955
2921
|
function getContainerStatus(name) {
|
|
2956
2922
|
try {
|
|
2957
|
-
const runningOutput =
|
|
2923
|
+
const runningOutput = execSync3(
|
|
2958
2924
|
`docker inspect ${name} --format '{{.State.Running}}'`,
|
|
2959
2925
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] }
|
|
2960
2926
|
).trim();
|
|
@@ -2964,7 +2930,7 @@ function getContainerStatus(name) {
|
|
|
2964
2930
|
}
|
|
2965
2931
|
let port;
|
|
2966
2932
|
try {
|
|
2967
|
-
const portOutput =
|
|
2933
|
+
const portOutput = execSync3(
|
|
2968
2934
|
`docker inspect ${name} --format '{{range .NetworkSettings.Ports}}{{range .}}{{.HostPort}}{{end}}{{end}}'`,
|
|
2969
2935
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] }
|
|
2970
2936
|
).trim();
|
|
@@ -2973,7 +2939,7 @@ function getContainerStatus(name) {
|
|
|
2973
2939
|
}
|
|
2974
2940
|
let healthy = true;
|
|
2975
2941
|
try {
|
|
2976
|
-
const healthOutput =
|
|
2942
|
+
const healthOutput = execSync3(
|
|
2977
2943
|
`docker inspect ${name} --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}'`,
|
|
2978
2944
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] }
|
|
2979
2945
|
).trim();
|
|
@@ -3005,13 +2971,13 @@ async function checkService(url, timeout = 2e3) {
|
|
|
3005
2971
|
}
|
|
3006
2972
|
function getLangfuseKeysFromDockerEnv() {
|
|
3007
2973
|
const envPaths2 = [
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
2974
|
+
join6(process.cwd(), "docker", ".env"),
|
|
2975
|
+
join6(process.cwd(), "..", "squads-cli", "docker", ".env"),
|
|
2976
|
+
join6(homedir2(), "agents-squads", "squads-cli", "docker", ".env")
|
|
3011
2977
|
];
|
|
3012
2978
|
for (const envPath of envPaths2) {
|
|
3013
|
-
if (
|
|
3014
|
-
const content =
|
|
2979
|
+
if (existsSync6(envPath)) {
|
|
2980
|
+
const content = readFileSync4(envPath, "utf-8");
|
|
3015
2981
|
const publicMatch = content.match(/LANGFUSE_PUBLIC_KEY=(\S+)/);
|
|
3016
2982
|
const secretMatch = content.match(/LANGFUSE_SECRET_KEY=(\S+)/);
|
|
3017
2983
|
if (publicMatch && secretMatch) {
|
|
@@ -3026,12 +2992,12 @@ function getLangfuseKeysFromDockerEnv() {
|
|
|
3026
2992
|
}
|
|
3027
2993
|
function findDockerComposeDir() {
|
|
3028
2994
|
const candidates = [
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
2995
|
+
join6(process.cwd(), "docker"),
|
|
2996
|
+
join6(process.cwd(), "..", "squads-cli", "docker"),
|
|
2997
|
+
join6(homedir2(), "agents-squads", "squads-cli", "docker")
|
|
3032
2998
|
];
|
|
3033
2999
|
for (const dir of candidates) {
|
|
3034
|
-
if (
|
|
3000
|
+
if (existsSync6(join6(dir, "docker-compose.yml"))) {
|
|
3035
3001
|
return dir;
|
|
3036
3002
|
}
|
|
3037
3003
|
}
|
|
@@ -3057,7 +3023,7 @@ async function stackInitCommand() {
|
|
|
3057
3023
|
writeLine();
|
|
3058
3024
|
writeLine(` ${bold}Step 2: Docker Compose Files${RESET}`);
|
|
3059
3025
|
let composeDir = findPackageDockerDir();
|
|
3060
|
-
const targetDir =
|
|
3026
|
+
const targetDir = join6(SQUADS_DATA_DIR, "docker");
|
|
3061
3027
|
if (!composeDir) {
|
|
3062
3028
|
writeLine(` ${colors.red}${icons.error}${RESET} Docker compose files not found`);
|
|
3063
3029
|
writeLine(` ${colors.dim}This shouldn't happen if you installed via npm.${RESET}`);
|
|
@@ -3065,20 +3031,20 @@ async function stackInitCommand() {
|
|
|
3065
3031
|
writeLine();
|
|
3066
3032
|
return;
|
|
3067
3033
|
}
|
|
3068
|
-
if (composeDir !== targetDir && !
|
|
3034
|
+
if (composeDir !== targetDir && !existsSync6(targetDir)) {
|
|
3069
3035
|
writeLine(` ${colors.cyan}${icons.progress}${RESET} Copying docker files to ${colors.dim}~/.squads/docker${RESET}`);
|
|
3070
3036
|
try {
|
|
3071
|
-
|
|
3072
|
-
|
|
3037
|
+
mkdirSync3(SQUADS_DATA_DIR, { recursive: true });
|
|
3038
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
3073
3039
|
const filesToCopy = [
|
|
3074
3040
|
"docker-compose.yml",
|
|
3075
3041
|
"docker-compose.engram.yml",
|
|
3076
3042
|
".env.example"
|
|
3077
3043
|
];
|
|
3078
3044
|
for (const file of filesToCopy) {
|
|
3079
|
-
const src =
|
|
3080
|
-
const dst =
|
|
3081
|
-
if (
|
|
3045
|
+
const src = join6(composeDir, file);
|
|
3046
|
+
const dst = join6(targetDir, file);
|
|
3047
|
+
if (existsSync6(src)) {
|
|
3082
3048
|
copyFileSync(src, dst);
|
|
3083
3049
|
}
|
|
3084
3050
|
}
|
|
@@ -3087,7 +3053,7 @@ async function stackInitCommand() {
|
|
|
3087
3053
|
} catch {
|
|
3088
3054
|
writeLine(` ${colors.yellow}${icons.warning}${RESET} Could not copy files, using source location`);
|
|
3089
3055
|
}
|
|
3090
|
-
} else if (
|
|
3056
|
+
} else if (existsSync6(targetDir)) {
|
|
3091
3057
|
composeDir = targetDir;
|
|
3092
3058
|
writeLine(` ${colors.green}${icons.success}${RESET} Using ${colors.dim}~/.squads/docker${RESET}`);
|
|
3093
3059
|
} else {
|
|
@@ -3095,10 +3061,10 @@ async function stackInitCommand() {
|
|
|
3095
3061
|
}
|
|
3096
3062
|
writeLine();
|
|
3097
3063
|
writeLine(` ${bold}Step 3: Environment Configuration${RESET}`);
|
|
3098
|
-
const envPath =
|
|
3099
|
-
const envExamplePath =
|
|
3100
|
-
if (!
|
|
3101
|
-
if (
|
|
3064
|
+
const envPath = join6(composeDir, ".env");
|
|
3065
|
+
const envExamplePath = join6(composeDir, ".env.example");
|
|
3066
|
+
if (!existsSync6(envPath)) {
|
|
3067
|
+
if (existsSync6(envExamplePath)) {
|
|
3102
3068
|
copyFileSync(envExamplePath, envPath);
|
|
3103
3069
|
writeLine(` ${colors.cyan}${icons.progress}${RESET} Created .env from template`);
|
|
3104
3070
|
} else {
|
|
@@ -3127,13 +3093,13 @@ LANGFUSE_PORT=3100
|
|
|
3127
3093
|
OTEL_PORT=4318
|
|
3128
3094
|
BRIDGE_PORT=8088
|
|
3129
3095
|
`;
|
|
3130
|
-
|
|
3096
|
+
writeFileSync4(envPath, minimalEnv);
|
|
3131
3097
|
writeLine(` ${colors.cyan}${icons.progress}${RESET} Created default .env`);
|
|
3132
3098
|
}
|
|
3133
3099
|
} else {
|
|
3134
3100
|
writeLine(` ${colors.green}${icons.success}${RESET} .env exists`);
|
|
3135
3101
|
}
|
|
3136
|
-
const envContent =
|
|
3102
|
+
const envContent = readFileSync4(envPath, "utf-8");
|
|
3137
3103
|
const missingSecrets = [];
|
|
3138
3104
|
const llmProvider = envContent.match(/LLM_PROVIDER=(\w+)/)?.[1] || "ollama";
|
|
3139
3105
|
if (llmProvider === "openai") {
|
|
@@ -3174,7 +3140,7 @@ BRIDGE_PORT=8088
|
|
|
3174
3140
|
writeLine();
|
|
3175
3141
|
writeLine(` ${colors.cyan}${icons.progress}${RESET} Starting containers...`);
|
|
3176
3142
|
try {
|
|
3177
|
-
|
|
3143
|
+
execSync3("docker compose up -d", {
|
|
3178
3144
|
cwd: composeDir,
|
|
3179
3145
|
stdio: "inherit"
|
|
3180
3146
|
});
|
|
@@ -3374,7 +3340,7 @@ async function stackHealthCommand(verbose = false) {
|
|
|
3374
3340
|
let logs;
|
|
3375
3341
|
if (!ok && verbose) {
|
|
3376
3342
|
try {
|
|
3377
|
-
logs =
|
|
3343
|
+
logs = execSync3(`docker logs ${container.name} --tail 10 2>&1`, {
|
|
3378
3344
|
encoding: "utf-8",
|
|
3379
3345
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3380
3346
|
});
|
|
@@ -3425,7 +3391,7 @@ function stackLogsCommand(service, tail = 50) {
|
|
|
3425
3391
|
};
|
|
3426
3392
|
const container = containerMap[service] || `squads-${service}`;
|
|
3427
3393
|
try {
|
|
3428
|
-
|
|
3394
|
+
execSync3(`docker logs ${container} --tail ${tail}`, { stdio: "inherit" });
|
|
3429
3395
|
} catch {
|
|
3430
3396
|
writeLine(` ${colors.red}${icons.error}${RESET} Container ${container} not found`);
|
|
3431
3397
|
}
|
|
@@ -3812,9 +3778,9 @@ async function memoryExtractCommand(options = {}) {
|
|
|
3812
3778
|
}
|
|
3813
3779
|
|
|
3814
3780
|
// src/commands/sync.ts
|
|
3815
|
-
import { execSync as
|
|
3816
|
-
import { existsSync as
|
|
3817
|
-
import { join as
|
|
3781
|
+
import { execSync as execSync4 } from "child_process";
|
|
3782
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync2 } from "fs";
|
|
3783
|
+
import { join as join7 } from "path";
|
|
3818
3784
|
var PATH_TO_SQUAD = {
|
|
3819
3785
|
"squads-cli": "product",
|
|
3820
3786
|
"agents-squads-web": "website",
|
|
@@ -3842,21 +3808,21 @@ var MESSAGE_TO_SQUAD = {
|
|
|
3842
3808
|
"infra": "engineering"
|
|
3843
3809
|
};
|
|
3844
3810
|
function getLastSyncTime(memoryDir) {
|
|
3845
|
-
const syncFile =
|
|
3846
|
-
if (
|
|
3847
|
-
return
|
|
3811
|
+
const syncFile = join7(memoryDir, ".last-sync");
|
|
3812
|
+
if (existsSync7(syncFile)) {
|
|
3813
|
+
return readFileSync5(syncFile, "utf-8").trim();
|
|
3848
3814
|
}
|
|
3849
3815
|
return null;
|
|
3850
3816
|
}
|
|
3851
3817
|
function updateLastSyncTime(memoryDir) {
|
|
3852
|
-
const syncFile =
|
|
3853
|
-
|
|
3818
|
+
const syncFile = join7(memoryDir, ".last-sync");
|
|
3819
|
+
writeFileSync5(syncFile, (/* @__PURE__ */ new Date()).toISOString());
|
|
3854
3820
|
}
|
|
3855
3821
|
function getRecentCommits(since) {
|
|
3856
3822
|
const commits = [];
|
|
3857
3823
|
try {
|
|
3858
3824
|
const sinceArg = since ? `--since="${since}"` : "-n 20";
|
|
3859
|
-
const logOutput =
|
|
3825
|
+
const logOutput = execSync4(
|
|
3860
3826
|
`git log ${sinceArg} --format="%H|%aI|%s" --name-only`,
|
|
3861
3827
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
3862
3828
|
).trim();
|
|
@@ -3922,22 +3888,22 @@ ${messages}
|
|
|
3922
3888
|
`;
|
|
3923
3889
|
}
|
|
3924
3890
|
function appendToSquadMemory(memoryDir, squad, summary) {
|
|
3925
|
-
const squadMemoryDir =
|
|
3926
|
-
if (!
|
|
3927
|
-
|
|
3891
|
+
const squadMemoryDir = join7(memoryDir, squad);
|
|
3892
|
+
if (!existsSync7(squadMemoryDir)) {
|
|
3893
|
+
mkdirSync4(squadMemoryDir, { recursive: true });
|
|
3928
3894
|
}
|
|
3929
3895
|
let agentDir;
|
|
3930
|
-
const existingDirs =
|
|
3896
|
+
const existingDirs = existsSync7(squadMemoryDir) ? readdirSync2(squadMemoryDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name) : [];
|
|
3931
3897
|
if (existingDirs.length > 0) {
|
|
3932
|
-
agentDir =
|
|
3898
|
+
agentDir = join7(squadMemoryDir, existingDirs[0]);
|
|
3933
3899
|
} else {
|
|
3934
|
-
agentDir =
|
|
3935
|
-
|
|
3900
|
+
agentDir = join7(squadMemoryDir, `${squad}-lead`);
|
|
3901
|
+
mkdirSync4(agentDir, { recursive: true });
|
|
3936
3902
|
}
|
|
3937
|
-
const statePath =
|
|
3903
|
+
const statePath = join7(agentDir, "state.md");
|
|
3938
3904
|
let content = "";
|
|
3939
|
-
if (
|
|
3940
|
-
content =
|
|
3905
|
+
if (existsSync7(statePath)) {
|
|
3906
|
+
content = readFileSync5(statePath, "utf-8");
|
|
3941
3907
|
} else {
|
|
3942
3908
|
content = `# ${squad} Squad - State
|
|
3943
3909
|
|
|
@@ -3949,13 +3915,13 @@ Updated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
|
|
|
3949
3915
|
`Updated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`
|
|
3950
3916
|
);
|
|
3951
3917
|
content += summary;
|
|
3952
|
-
|
|
3918
|
+
writeFileSync5(statePath, content);
|
|
3953
3919
|
return true;
|
|
3954
3920
|
}
|
|
3955
3921
|
function gitPullMemory() {
|
|
3956
3922
|
try {
|
|
3957
|
-
|
|
3958
|
-
const status =
|
|
3923
|
+
execSync4("git fetch origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
3924
|
+
const status = execSync4("git status -sb", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
3959
3925
|
const behindMatch = status.match(/behind (\d+)/);
|
|
3960
3926
|
const aheadMatch = status.match(/ahead (\d+)/);
|
|
3961
3927
|
const behind = behindMatch ? parseInt(behindMatch[1]) : 0;
|
|
@@ -3963,7 +3929,7 @@ function gitPullMemory() {
|
|
|
3963
3929
|
if (behind === 0) {
|
|
3964
3930
|
return { success: true, output: "Already up to date", behind: 0, ahead };
|
|
3965
3931
|
}
|
|
3966
|
-
const output =
|
|
3932
|
+
const output = execSync4("git pull --rebase origin main", {
|
|
3967
3933
|
encoding: "utf-8",
|
|
3968
3934
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3969
3935
|
});
|
|
@@ -3975,18 +3941,18 @@ function gitPullMemory() {
|
|
|
3975
3941
|
}
|
|
3976
3942
|
function gitPushMemory() {
|
|
3977
3943
|
try {
|
|
3978
|
-
const status =
|
|
3944
|
+
const status = execSync4("git status --porcelain .agents/memory/", {
|
|
3979
3945
|
encoding: "utf-8",
|
|
3980
3946
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3981
3947
|
}).trim();
|
|
3982
3948
|
if (status) {
|
|
3983
|
-
|
|
3984
|
-
|
|
3949
|
+
execSync4("git add .agents/memory/", { stdio: ["pipe", "pipe", "pipe"] });
|
|
3950
|
+
execSync4('git commit -m "chore: sync squad memory"', {
|
|
3985
3951
|
encoding: "utf-8",
|
|
3986
3952
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3987
3953
|
});
|
|
3988
3954
|
}
|
|
3989
|
-
const output =
|
|
3955
|
+
const output = execSync4("git push origin main", {
|
|
3990
3956
|
encoding: "utf-8",
|
|
3991
3957
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3992
3958
|
});
|
|
@@ -4226,28 +4192,28 @@ async function goalProgressCommand(squadName, goalIndex, progress2) {
|
|
|
4226
4192
|
}
|
|
4227
4193
|
|
|
4228
4194
|
// src/commands/feedback.ts
|
|
4229
|
-
import { readFileSync as
|
|
4230
|
-
import { join as
|
|
4195
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync3 } from "fs";
|
|
4196
|
+
import { join as join8, dirname as dirname3 } from "path";
|
|
4231
4197
|
function getFeedbackPath(squadName) {
|
|
4232
4198
|
const memoryDir = findMemoryDir();
|
|
4233
4199
|
if (!memoryDir) return null;
|
|
4234
4200
|
const squad = loadSquad(squadName);
|
|
4235
4201
|
const agentName = squad?.agents[0]?.name || `${squadName}-lead`;
|
|
4236
|
-
return
|
|
4202
|
+
return join8(memoryDir, squadName, agentName, "feedback.md");
|
|
4237
4203
|
}
|
|
4238
4204
|
function getOutputPath(squadName) {
|
|
4239
4205
|
const memoryDir = findMemoryDir();
|
|
4240
4206
|
if (!memoryDir) return null;
|
|
4241
4207
|
const squad = loadSquad(squadName);
|
|
4242
4208
|
const agentName = squad?.agents[0]?.name || `${squadName}-lead`;
|
|
4243
|
-
return
|
|
4209
|
+
return join8(memoryDir, squadName, agentName, "output.md");
|
|
4244
4210
|
}
|
|
4245
4211
|
function getLastExecution(squadName) {
|
|
4246
4212
|
const outputPath = getOutputPath(squadName);
|
|
4247
|
-
if (!outputPath || !
|
|
4213
|
+
if (!outputPath || !existsSync8(outputPath)) {
|
|
4248
4214
|
return null;
|
|
4249
4215
|
}
|
|
4250
|
-
const content =
|
|
4216
|
+
const content = readFileSync6(outputPath, "utf-8");
|
|
4251
4217
|
const lines = content.split("\n");
|
|
4252
4218
|
let date = "unknown";
|
|
4253
4219
|
let summary = lines.slice(0, 5).join("\n");
|
|
@@ -4299,9 +4265,9 @@ async function feedbackAddCommand(squadName, rating, feedback2, options) {
|
|
|
4299
4265
|
return;
|
|
4300
4266
|
}
|
|
4301
4267
|
const lastExec = getLastExecution(squadName);
|
|
4302
|
-
const dir =
|
|
4303
|
-
if (!
|
|
4304
|
-
|
|
4268
|
+
const dir = dirname3(feedbackPath);
|
|
4269
|
+
if (!existsSync8(dir)) {
|
|
4270
|
+
mkdirSync5(dir, { recursive: true });
|
|
4305
4271
|
}
|
|
4306
4272
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4307
4273
|
let entry = `
|
|
@@ -4329,15 +4295,15 @@ _Date: ${date}_
|
|
|
4329
4295
|
}
|
|
4330
4296
|
}
|
|
4331
4297
|
let existing = "";
|
|
4332
|
-
if (
|
|
4333
|
-
existing =
|
|
4298
|
+
if (existsSync8(feedbackPath)) {
|
|
4299
|
+
existing = readFileSync6(feedbackPath, "utf-8");
|
|
4334
4300
|
} else {
|
|
4335
4301
|
existing = `# ${squadName} - Feedback Log
|
|
4336
4302
|
|
|
4337
4303
|
> Execution feedback and learnings
|
|
4338
4304
|
`;
|
|
4339
4305
|
}
|
|
4340
|
-
|
|
4306
|
+
writeFileSync6(feedbackPath, existing + entry);
|
|
4341
4307
|
const stars = `${colors.yellow}${"\u2605".repeat(ratingNum)}${"\u2606".repeat(5 - ratingNum)}${RESET}`;
|
|
4342
4308
|
writeLine();
|
|
4343
4309
|
writeLine(` ${icons.success} Feedback recorded for ${colors.cyan}${squadName}${RESET}`);
|
|
@@ -4351,11 +4317,11 @@ _Date: ${date}_
|
|
|
4351
4317
|
async function feedbackShowCommand(squadName, options) {
|
|
4352
4318
|
await track(Events.CLI_FEEDBACK_SHOW, { squad: squadName });
|
|
4353
4319
|
const feedbackPath = getFeedbackPath(squadName);
|
|
4354
|
-
if (!feedbackPath || !
|
|
4320
|
+
if (!feedbackPath || !existsSync8(feedbackPath)) {
|
|
4355
4321
|
writeLine(` ${colors.yellow}No feedback recorded for ${squadName}${RESET}`);
|
|
4356
4322
|
return;
|
|
4357
4323
|
}
|
|
4358
|
-
const content =
|
|
4324
|
+
const content = readFileSync6(feedbackPath, "utf-8");
|
|
4359
4325
|
const entries = parseFeedbackHistory(content);
|
|
4360
4326
|
const limit = options.limit ? parseInt(options.limit) : 5;
|
|
4361
4327
|
const recent = entries.slice(-limit).reverse();
|
|
@@ -4400,10 +4366,10 @@ async function feedbackStatsCommand() {
|
|
|
4400
4366
|
writeLine(` ${colors.purple}${box.teeRight}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.teeLeft}${RESET}`);
|
|
4401
4367
|
for (const squad of squads) {
|
|
4402
4368
|
const feedbackPath = getFeedbackPath(squad);
|
|
4403
|
-
if (!feedbackPath || !
|
|
4369
|
+
if (!feedbackPath || !existsSync8(feedbackPath)) {
|
|
4404
4370
|
continue;
|
|
4405
4371
|
}
|
|
4406
|
-
const content =
|
|
4372
|
+
const content = readFileSync6(feedbackPath, "utf-8");
|
|
4407
4373
|
const entries = parseFeedbackHistory(content);
|
|
4408
4374
|
if (entries.length === 0) continue;
|
|
4409
4375
|
const avgRating = entries.reduce((sum, e) => sum + e.rating, 0) / entries.length;
|
|
@@ -4426,8 +4392,8 @@ async function feedbackStatsCommand() {
|
|
|
4426
4392
|
}
|
|
4427
4393
|
|
|
4428
4394
|
// src/commands/dashboard.ts
|
|
4429
|
-
import { readdirSync as readdirSync4, existsSync as
|
|
4430
|
-
import { join as
|
|
4395
|
+
import { readdirSync as readdirSync4, existsSync as existsSync9, statSync as statSync2 } from "fs";
|
|
4396
|
+
import { join as join9 } from "path";
|
|
4431
4397
|
|
|
4432
4398
|
// src/lib/providers.ts
|
|
4433
4399
|
var PROVIDERS = {
|
|
@@ -5286,16 +5252,16 @@ async function closeDatabase() {
|
|
|
5286
5252
|
function getLastActivityDate(squadName) {
|
|
5287
5253
|
const memoryDir = findMemoryDir();
|
|
5288
5254
|
if (!memoryDir) return "unknown";
|
|
5289
|
-
const squadMemory =
|
|
5290
|
-
if (!
|
|
5255
|
+
const squadMemory = join9(memoryDir, squadName);
|
|
5256
|
+
if (!existsSync9(squadMemory)) return "\u2014";
|
|
5291
5257
|
let latestTime = 0;
|
|
5292
5258
|
try {
|
|
5293
5259
|
const agents = readdirSync4(squadMemory, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5294
5260
|
for (const agent of agents) {
|
|
5295
|
-
const agentPath =
|
|
5261
|
+
const agentPath = join9(squadMemory, agent.name);
|
|
5296
5262
|
const files = readdirSync4(agentPath).filter((f) => f.endsWith(".md"));
|
|
5297
5263
|
for (const file of files) {
|
|
5298
|
-
const filePath =
|
|
5264
|
+
const filePath = join9(agentPath, file);
|
|
5299
5265
|
const stats = statSync2(filePath);
|
|
5300
5266
|
if (stats.mtimeMs > latestTime) {
|
|
5301
5267
|
latestTime = stats.mtimeMs;
|
|
@@ -5542,11 +5508,11 @@ async function dashboardCommand(options = {}) {
|
|
|
5542
5508
|
await closeDatabase();
|
|
5543
5509
|
}
|
|
5544
5510
|
function findAgentsSquadsDir() {
|
|
5545
|
-
const parentDir =
|
|
5546
|
-
if (
|
|
5511
|
+
const parentDir = join9(process.cwd(), "..");
|
|
5512
|
+
if (existsSync9(join9(parentDir, "hq"))) {
|
|
5547
5513
|
return parentDir;
|
|
5548
5514
|
}
|
|
5549
|
-
if (
|
|
5515
|
+
if (existsSync9(join9(process.cwd(), ".git"))) {
|
|
5550
5516
|
return process.cwd();
|
|
5551
5517
|
}
|
|
5552
5518
|
return null;
|
|
@@ -5935,7 +5901,7 @@ function renderInsightsCached(cache) {
|
|
|
5935
5901
|
}
|
|
5936
5902
|
|
|
5937
5903
|
// src/commands/issues.ts
|
|
5938
|
-
import { execSync as
|
|
5904
|
+
import { execSync as execSync5 } from "child_process";
|
|
5939
5905
|
function getLabelName(label) {
|
|
5940
5906
|
return typeof label === "string" ? label : label.name;
|
|
5941
5907
|
}
|
|
@@ -5948,7 +5914,7 @@ async function issuesCommand(options = {}) {
|
|
|
5948
5914
|
writeLine(` ${gradient("squads")} ${colors.dim}issues${RESET}`);
|
|
5949
5915
|
writeLine();
|
|
5950
5916
|
try {
|
|
5951
|
-
|
|
5917
|
+
execSync5("gh --version", { stdio: "pipe" });
|
|
5952
5918
|
} catch {
|
|
5953
5919
|
writeLine(` ${colors.red}GitHub CLI (gh) not found${RESET}`);
|
|
5954
5920
|
writeLine(` ${colors.dim}Install: brew install gh${RESET}`);
|
|
@@ -5959,7 +5925,7 @@ async function issuesCommand(options = {}) {
|
|
|
5959
5925
|
let totalOpen = 0;
|
|
5960
5926
|
for (const repo of repos) {
|
|
5961
5927
|
try {
|
|
5962
|
-
const result =
|
|
5928
|
+
const result = execSync5(
|
|
5963
5929
|
`gh issue list -R ${org}/${repo} --state open --json number,title,state,labels,createdAt --limit 50`,
|
|
5964
5930
|
{ stdio: "pipe", encoding: "utf-8" }
|
|
5965
5931
|
);
|
|
@@ -6010,7 +5976,7 @@ async function issuesCommand(options = {}) {
|
|
|
6010
5976
|
}
|
|
6011
5977
|
|
|
6012
5978
|
// src/commands/solve-issues.ts
|
|
6013
|
-
import { execSync as
|
|
5979
|
+
import { execSync as execSync6, spawn as spawn4 } from "child_process";
|
|
6014
5980
|
import ora3 from "ora";
|
|
6015
5981
|
var DEFAULT_ORG2 = "agents-squads";
|
|
6016
5982
|
var DEFAULT_REPOS2 = ["hq", "agents-squads-web", "squads-cli", "agents-squads"];
|
|
@@ -6020,7 +5986,7 @@ async function solveIssuesCommand(options = {}) {
|
|
|
6020
5986
|
writeLine(` ${gradient("squads")} ${colors.dim}solve-issues${RESET}`);
|
|
6021
5987
|
writeLine();
|
|
6022
5988
|
try {
|
|
6023
|
-
|
|
5989
|
+
execSync6("gh --version", { stdio: "pipe" });
|
|
6024
5990
|
} catch {
|
|
6025
5991
|
writeLine(` ${colors.red}GitHub CLI (gh) not found${RESET}`);
|
|
6026
5992
|
writeLine(` ${colors.dim}Install: brew install gh${RESET}`);
|
|
@@ -6030,7 +5996,7 @@ async function solveIssuesCommand(options = {}) {
|
|
|
6030
5996
|
if (options.issue) {
|
|
6031
5997
|
const repo = options.repo || "hq";
|
|
6032
5998
|
try {
|
|
6033
|
-
const result =
|
|
5999
|
+
const result = execSync6(
|
|
6034
6000
|
`gh issue view ${options.issue} -R ${DEFAULT_ORG2}/${repo} --json number,title,labels,body`,
|
|
6035
6001
|
{ stdio: "pipe", encoding: "utf-8" }
|
|
6036
6002
|
);
|
|
@@ -6043,7 +6009,7 @@ async function solveIssuesCommand(options = {}) {
|
|
|
6043
6009
|
} else {
|
|
6044
6010
|
for (const repo of repos) {
|
|
6045
6011
|
try {
|
|
6046
|
-
const result =
|
|
6012
|
+
const result = execSync6(
|
|
6047
6013
|
`gh issue list -R ${DEFAULT_ORG2}/${repo} --label "ready-to-fix" --state open --json number,title,labels --limit 20`,
|
|
6048
6014
|
{ stdio: "pipe", encoding: "utf-8" }
|
|
6049
6015
|
);
|
|
@@ -6097,7 +6063,7 @@ function showSolveInstructions(issues) {
|
|
|
6097
6063
|
async function solveWithClaude(issues) {
|
|
6098
6064
|
const spinner = ora3("Starting issue solver...").start();
|
|
6099
6065
|
try {
|
|
6100
|
-
|
|
6066
|
+
execSync6("which claude", { stdio: "pipe" });
|
|
6101
6067
|
} catch {
|
|
6102
6068
|
spinner.fail("Claude CLI not found");
|
|
6103
6069
|
writeLine(` ${colors.dim}Install: npm install -g @anthropic-ai/claude-code${RESET}`);
|
|
@@ -6183,9 +6149,9 @@ function executeClaudePrompt(prompt2) {
|
|
|
6183
6149
|
}
|
|
6184
6150
|
|
|
6185
6151
|
// src/commands/open-issues.ts
|
|
6186
|
-
import { execSync as
|
|
6152
|
+
import { execSync as execSync7, spawn as spawn5 } from "child_process";
|
|
6187
6153
|
import { readdirSync as readdirSync5 } from "fs";
|
|
6188
|
-
import { join as
|
|
6154
|
+
import { join as join10 } from "path";
|
|
6189
6155
|
import ora4 from "ora";
|
|
6190
6156
|
var ISSUE_FINDER_PATTERNS = [
|
|
6191
6157
|
"*-eval.md",
|
|
@@ -6243,7 +6209,7 @@ function findEvalAgents(squadsDir, filterSquad) {
|
|
|
6243
6209
|
const agents = [];
|
|
6244
6210
|
const squads = readdirSync5(squadsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).filter((d) => !filterSquad || d.name === filterSquad).map((d) => d.name);
|
|
6245
6211
|
for (const squad of squads) {
|
|
6246
|
-
const squadPath =
|
|
6212
|
+
const squadPath = join10(squadsDir, squad);
|
|
6247
6213
|
const files = readdirSync5(squadPath).filter((f) => f.endsWith(".md"));
|
|
6248
6214
|
for (const file of files) {
|
|
6249
6215
|
const isEval = ISSUE_FINDER_PATTERNS.some((pattern) => {
|
|
@@ -6254,7 +6220,7 @@ function findEvalAgents(squadsDir, filterSquad) {
|
|
|
6254
6220
|
agents.push({
|
|
6255
6221
|
name: file,
|
|
6256
6222
|
squad,
|
|
6257
|
-
path:
|
|
6223
|
+
path: join10(squadPath, file)
|
|
6258
6224
|
});
|
|
6259
6225
|
}
|
|
6260
6226
|
}
|
|
@@ -6280,7 +6246,7 @@ function showRunInstructions(agents) {
|
|
|
6280
6246
|
async function runEvaluators(agents) {
|
|
6281
6247
|
const spinner = ora4("Starting evaluators...").start();
|
|
6282
6248
|
try {
|
|
6283
|
-
|
|
6249
|
+
execSync7("which claude", { stdio: "pipe" });
|
|
6284
6250
|
} catch {
|
|
6285
6251
|
spinner.fail("Claude CLI not found");
|
|
6286
6252
|
writeLine(` ${colors.dim}Install: npm install -g @anthropic-ai/claude-code${RESET}`);
|
|
@@ -6364,9 +6330,9 @@ import open from "open";
|
|
|
6364
6330
|
|
|
6365
6331
|
// src/lib/auth.ts
|
|
6366
6332
|
import { createClient } from "@supabase/supabase-js";
|
|
6367
|
-
import { existsSync as
|
|
6368
|
-
import { join as
|
|
6369
|
-
import { homedir as
|
|
6333
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6 } from "fs";
|
|
6334
|
+
import { join as join11 } from "path";
|
|
6335
|
+
import { homedir as homedir3 } from "os";
|
|
6370
6336
|
import "open";
|
|
6371
6337
|
import http from "http";
|
|
6372
6338
|
var PERSONAL_DOMAINS = [
|
|
@@ -6395,8 +6361,8 @@ var PERSONAL_DOMAINS = [
|
|
|
6395
6361
|
"tutanota.com",
|
|
6396
6362
|
"hey.com"
|
|
6397
6363
|
];
|
|
6398
|
-
var AUTH_DIR =
|
|
6399
|
-
var AUTH_PATH =
|
|
6364
|
+
var AUTH_DIR = join11(homedir3(), ".squads-cli");
|
|
6365
|
+
var AUTH_PATH = join11(AUTH_DIR, "auth.json");
|
|
6400
6366
|
function isPersonalEmail(email) {
|
|
6401
6367
|
const domain = email.split("@")[1]?.toLowerCase();
|
|
6402
6368
|
return PERSONAL_DOMAINS.includes(domain);
|
|
@@ -6405,22 +6371,22 @@ function getEmailDomain(email) {
|
|
|
6405
6371
|
return email.split("@")[1]?.toLowerCase() || "";
|
|
6406
6372
|
}
|
|
6407
6373
|
function saveSession(session2) {
|
|
6408
|
-
if (!
|
|
6409
|
-
|
|
6374
|
+
if (!existsSync10(AUTH_DIR)) {
|
|
6375
|
+
mkdirSync6(AUTH_DIR, { recursive: true });
|
|
6410
6376
|
}
|
|
6411
|
-
|
|
6377
|
+
writeFileSync7(AUTH_PATH, JSON.stringify(session2, null, 2));
|
|
6412
6378
|
}
|
|
6413
6379
|
function loadSession() {
|
|
6414
|
-
if (!
|
|
6380
|
+
if (!existsSync10(AUTH_PATH)) return null;
|
|
6415
6381
|
try {
|
|
6416
|
-
return JSON.parse(
|
|
6382
|
+
return JSON.parse(readFileSync7(AUTH_PATH, "utf-8"));
|
|
6417
6383
|
} catch {
|
|
6418
6384
|
return null;
|
|
6419
6385
|
}
|
|
6420
6386
|
}
|
|
6421
6387
|
function clearSession() {
|
|
6422
|
-
if (
|
|
6423
|
-
|
|
6388
|
+
if (existsSync10(AUTH_PATH)) {
|
|
6389
|
+
writeFileSync7(AUTH_PATH, "");
|
|
6424
6390
|
}
|
|
6425
6391
|
}
|
|
6426
6392
|
function startAuthCallbackServer(port = 54321) {
|
|
@@ -6642,26 +6608,26 @@ async function updateCommand(options = {}) {
|
|
|
6642
6608
|
}
|
|
6643
6609
|
|
|
6644
6610
|
// src/commands/progress.ts
|
|
6645
|
-
import { execSync as
|
|
6646
|
-
import { existsSync as
|
|
6647
|
-
import { join as
|
|
6611
|
+
import { execSync as execSync8 } from "child_process";
|
|
6612
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7 } from "fs";
|
|
6613
|
+
import { join as join12 } from "path";
|
|
6648
6614
|
function getTasksFilePath() {
|
|
6649
6615
|
const memoryDir = findMemoryDir();
|
|
6650
6616
|
if (!memoryDir) {
|
|
6651
6617
|
const cwd = process.cwd();
|
|
6652
|
-
const agentsDir =
|
|
6653
|
-
if (!
|
|
6654
|
-
|
|
6618
|
+
const agentsDir = join12(cwd, ".agents");
|
|
6619
|
+
if (!existsSync11(agentsDir)) {
|
|
6620
|
+
mkdirSync7(agentsDir, { recursive: true });
|
|
6655
6621
|
}
|
|
6656
|
-
return
|
|
6622
|
+
return join12(agentsDir, "tasks.json");
|
|
6657
6623
|
}
|
|
6658
|
-
return
|
|
6624
|
+
return join12(memoryDir, "..", "tasks.json");
|
|
6659
6625
|
}
|
|
6660
6626
|
function loadTasks() {
|
|
6661
6627
|
const tasksPath = getTasksFilePath();
|
|
6662
|
-
if (
|
|
6628
|
+
if (existsSync11(tasksPath)) {
|
|
6663
6629
|
try {
|
|
6664
|
-
return JSON.parse(
|
|
6630
|
+
return JSON.parse(readFileSync8(tasksPath, "utf-8"));
|
|
6665
6631
|
} catch {
|
|
6666
6632
|
return { tasks: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6667
6633
|
}
|
|
@@ -6671,7 +6637,7 @@ function loadTasks() {
|
|
|
6671
6637
|
function saveTasks(data) {
|
|
6672
6638
|
const tasksPath = getTasksFilePath();
|
|
6673
6639
|
data.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
6674
|
-
|
|
6640
|
+
writeFileSync8(tasksPath, JSON.stringify(data, null, 2));
|
|
6675
6641
|
}
|
|
6676
6642
|
function getRecentActivity() {
|
|
6677
6643
|
const activity = [];
|
|
@@ -6687,7 +6653,7 @@ function getRecentActivity() {
|
|
|
6687
6653
|
marketing: ["marketing", "content", "social"]
|
|
6688
6654
|
};
|
|
6689
6655
|
try {
|
|
6690
|
-
const logOutput =
|
|
6656
|
+
const logOutput = execSync8(
|
|
6691
6657
|
'git log --since="24 hours ago" --format="%h|%aI|%s" 2>/dev/null',
|
|
6692
6658
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6693
6659
|
).trim();
|
|
@@ -6821,7 +6787,7 @@ function getElapsedTime(startTime) {
|
|
|
6821
6787
|
}
|
|
6822
6788
|
|
|
6823
6789
|
// src/commands/results.ts
|
|
6824
|
-
import { execSync as
|
|
6790
|
+
import { execSync as execSync9 } from "child_process";
|
|
6825
6791
|
function getGitStats(days = 7) {
|
|
6826
6792
|
const stats = /* @__PURE__ */ new Map();
|
|
6827
6793
|
const squadKeywords = {
|
|
@@ -6836,7 +6802,7 @@ function getGitStats(days = 7) {
|
|
|
6836
6802
|
marketing: ["marketing"]
|
|
6837
6803
|
};
|
|
6838
6804
|
try {
|
|
6839
|
-
const logOutput =
|
|
6805
|
+
const logOutput = execSync9(
|
|
6840
6806
|
`git log --since="${days} days ago" --format="%s" --name-only 2>/dev/null`,
|
|
6841
6807
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6842
6808
|
).trim();
|
|
@@ -6875,7 +6841,7 @@ function getGitHubStats(days = 7) {
|
|
|
6875
6841
|
const prsMerged = /* @__PURE__ */ new Map();
|
|
6876
6842
|
const issuesClosed = /* @__PURE__ */ new Map();
|
|
6877
6843
|
try {
|
|
6878
|
-
const prsOutput =
|
|
6844
|
+
const prsOutput = execSync9(
|
|
6879
6845
|
`gh pr list --state all --json title,createdAt,mergedAt --limit 50 2>/dev/null`,
|
|
6880
6846
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6881
6847
|
);
|
|
@@ -6890,7 +6856,7 @@ function getGitHubStats(days = 7) {
|
|
|
6890
6856
|
prsMerged.set(squad, (prsMerged.get(squad) || 0) + 1);
|
|
6891
6857
|
}
|
|
6892
6858
|
}
|
|
6893
|
-
const issuesOutput =
|
|
6859
|
+
const issuesOutput = execSync9(
|
|
6894
6860
|
`gh issue list --state closed --json title,closedAt --limit 50 2>/dev/null`,
|
|
6895
6861
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
6896
6862
|
);
|
|
@@ -7023,8 +6989,8 @@ async function resultsCommand(options = {}) {
|
|
|
7023
6989
|
}
|
|
7024
6990
|
|
|
7025
6991
|
// src/commands/history.ts
|
|
7026
|
-
import { existsSync as
|
|
7027
|
-
import { join as
|
|
6992
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
6993
|
+
import { join as join13 } from "path";
|
|
7028
6994
|
var BRIDGE_URL2 = process.env.SQUADS_BRIDGE_URL || "http://localhost:8088";
|
|
7029
6995
|
var FETCH_TIMEOUT_MS2 = 3e3;
|
|
7030
6996
|
async function fetchWithTimeout2(url, timeoutMs = FETCH_TIMEOUT_MS2) {
|
|
@@ -7069,12 +7035,12 @@ async function fetchFromBridge2(days, squad) {
|
|
|
7069
7035
|
function fetchFromLocal(days, squad) {
|
|
7070
7036
|
const executions = [];
|
|
7071
7037
|
const historyPaths = [
|
|
7072
|
-
|
|
7073
|
-
|
|
7038
|
+
join13(process.cwd(), ".agents/sessions/history.jsonl"),
|
|
7039
|
+
join13(process.env.HOME || "", "agents-squads/hq/.agents/sessions/history.jsonl")
|
|
7074
7040
|
];
|
|
7075
7041
|
let historyPath;
|
|
7076
7042
|
for (const path3 of historyPaths) {
|
|
7077
|
-
if (
|
|
7043
|
+
if (existsSync12(path3)) {
|
|
7078
7044
|
historyPath = path3;
|
|
7079
7045
|
break;
|
|
7080
7046
|
}
|
|
@@ -7083,7 +7049,7 @@ function fetchFromLocal(days, squad) {
|
|
|
7083
7049
|
return [];
|
|
7084
7050
|
}
|
|
7085
7051
|
try {
|
|
7086
|
-
const content =
|
|
7052
|
+
const content = readFileSync9(historyPath, "utf-8");
|
|
7087
7053
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
7088
7054
|
const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
7089
7055
|
for (const line of lines) {
|
|
@@ -7419,19 +7385,19 @@ async function healthCommand(options = {}) {
|
|
|
7419
7385
|
}
|
|
7420
7386
|
|
|
7421
7387
|
// src/commands/workers.ts
|
|
7422
|
-
import { execSync as
|
|
7423
|
-
import { existsSync as
|
|
7424
|
-
import { join as
|
|
7388
|
+
import { execSync as execSync10 } from "child_process";
|
|
7389
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
|
|
7390
|
+
import { join as join14 } from "path";
|
|
7425
7391
|
function getTasksFilePath2() {
|
|
7426
7392
|
const memoryDir = findMemoryDir();
|
|
7427
7393
|
if (!memoryDir) return null;
|
|
7428
|
-
return
|
|
7394
|
+
return join14(memoryDir, "..", "tasks.json");
|
|
7429
7395
|
}
|
|
7430
7396
|
function loadActiveTasks() {
|
|
7431
7397
|
const tasksPath = getTasksFilePath2();
|
|
7432
|
-
if (!tasksPath || !
|
|
7398
|
+
if (!tasksPath || !existsSync13(tasksPath)) return [];
|
|
7433
7399
|
try {
|
|
7434
|
-
const data = JSON.parse(
|
|
7400
|
+
const data = JSON.parse(readFileSync10(tasksPath, "utf-8"));
|
|
7435
7401
|
return data.tasks?.filter((t) => t.status === "active") || [];
|
|
7436
7402
|
} catch {
|
|
7437
7403
|
return [];
|
|
@@ -7440,7 +7406,7 @@ function loadActiveTasks() {
|
|
|
7440
7406
|
function getRunningProcesses() {
|
|
7441
7407
|
const processes = [];
|
|
7442
7408
|
try {
|
|
7443
|
-
const psOutput =
|
|
7409
|
+
const psOutput = execSync10(
|
|
7444
7410
|
'ps aux | grep -E "claude|squads|astro|node.*agent" | grep -v grep',
|
|
7445
7411
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
7446
7412
|
).trim();
|
|
@@ -7491,7 +7457,7 @@ async function workersCommand(options = {}) {
|
|
|
7491
7457
|
writeLine();
|
|
7492
7458
|
if (options.kill) {
|
|
7493
7459
|
try {
|
|
7494
|
-
|
|
7460
|
+
execSync10(`kill ${options.kill}`, { stdio: "pipe" });
|
|
7495
7461
|
writeLine(` ${icons.success} Killed process ${colors.cyan}${options.kill}${RESET}`);
|
|
7496
7462
|
writeLine();
|
|
7497
7463
|
return;
|
|
@@ -7578,8 +7544,8 @@ function getElapsedTime2(startTime) {
|
|
|
7578
7544
|
}
|
|
7579
7545
|
|
|
7580
7546
|
// src/commands/context-feed.ts
|
|
7581
|
-
import { existsSync as
|
|
7582
|
-
import { join as
|
|
7547
|
+
import { existsSync as existsSync14, statSync as statSync3, readdirSync as readdirSync6, readFileSync as readFileSync11 } from "fs";
|
|
7548
|
+
import { join as join15 } from "path";
|
|
7583
7549
|
var BRIDGE_URL3 = process.env.SQUADS_BRIDGE_URL || "http://localhost:8088";
|
|
7584
7550
|
async function syncBriefToBridge(brief, sourcePath) {
|
|
7585
7551
|
try {
|
|
@@ -7608,10 +7574,10 @@ async function syncBriefToBridge(brief, sourcePath) {
|
|
|
7608
7574
|
}
|
|
7609
7575
|
function readBusinessBrief(squadsDir) {
|
|
7610
7576
|
if (!squadsDir) return void 0;
|
|
7611
|
-
const briefPath =
|
|
7612
|
-
if (!
|
|
7577
|
+
const briefPath = join15(squadsDir, "..", "BUSINESS_BRIEF.md");
|
|
7578
|
+
if (!existsSync14(briefPath)) return void 0;
|
|
7613
7579
|
try {
|
|
7614
|
-
const content =
|
|
7580
|
+
const content = readFileSync11(briefPath, "utf-8");
|
|
7615
7581
|
const brief = { raw: content };
|
|
7616
7582
|
const priorityMatch = content.match(/##\s*#1 Priority\s*\n+\*\*([^*]+)\*\*/);
|
|
7617
7583
|
if (priorityMatch) {
|
|
@@ -7661,7 +7627,7 @@ function readBusinessBrief(squadsDir) {
|
|
|
7661
7627
|
async function collectBriefingData(options) {
|
|
7662
7628
|
const squadsDir = findSquadsDir();
|
|
7663
7629
|
const memoryDir = findMemoryDir();
|
|
7664
|
-
const baseDir = squadsDir ?
|
|
7630
|
+
const baseDir = squadsDir ? join15(squadsDir, "..", "..", "..") : null;
|
|
7665
7631
|
const allSquads = squadsDir ? listSquads(squadsDir) : [];
|
|
7666
7632
|
if (options.squad && !allSquads.includes(options.squad)) {
|
|
7667
7633
|
return {
|
|
@@ -7701,14 +7667,14 @@ async function collectBriefingData(options) {
|
|
|
7701
7667
|
}
|
|
7702
7668
|
let lastActivity;
|
|
7703
7669
|
if (memoryDir) {
|
|
7704
|
-
const squadMemoryPath =
|
|
7705
|
-
if (
|
|
7670
|
+
const squadMemoryPath = join15(memoryDir, squadName);
|
|
7671
|
+
if (existsSync14(squadMemoryPath)) {
|
|
7706
7672
|
let mostRecent = 0;
|
|
7707
7673
|
try {
|
|
7708
7674
|
const walkDir = (dir) => {
|
|
7709
7675
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
7710
7676
|
for (const entry of entries) {
|
|
7711
|
-
const fullPath =
|
|
7677
|
+
const fullPath = join15(dir, entry.name);
|
|
7712
7678
|
if (entry.isDirectory()) {
|
|
7713
7679
|
walkDir(fullPath);
|
|
7714
7680
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -7788,7 +7754,7 @@ async function collectBriefingData(options) {
|
|
|
7788
7754
|
}
|
|
7789
7755
|
const brief = readBusinessBrief(squadsDir);
|
|
7790
7756
|
if (brief && squadsDir) {
|
|
7791
|
-
const briefPath =
|
|
7757
|
+
const briefPath = join15(squadsDir, "..", "BUSINESS_BRIEF.md");
|
|
7792
7758
|
syncBriefToBridge(brief, briefPath).catch(() => {
|
|
7793
7759
|
});
|
|
7794
7760
|
}
|
|
@@ -7996,7 +7962,7 @@ async function runCommand2(command, args, clear) {
|
|
|
7996
7962
|
// src/commands/live.ts
|
|
7997
7963
|
import blessed from "blessed";
|
|
7998
7964
|
import contrib from "blessed-contrib";
|
|
7999
|
-
import { execSync as
|
|
7965
|
+
import { execSync as execSync11 } from "child_process";
|
|
8000
7966
|
async function liveCommand(_options) {
|
|
8001
7967
|
const screen = blessed.screen({
|
|
8002
7968
|
smartCSR: true,
|
|
@@ -8039,7 +8005,7 @@ async function liveCommand(_options) {
|
|
|
8039
8005
|
});
|
|
8040
8006
|
function getAgents() {
|
|
8041
8007
|
try {
|
|
8042
|
-
const output =
|
|
8008
|
+
const output = execSync11('ps aux | grep -E "claude|node.*squads" | grep -v grep', {
|
|
8043
8009
|
encoding: "utf-8",
|
|
8044
8010
|
timeout: 5e3
|
|
8045
8011
|
});
|
|
@@ -8088,7 +8054,7 @@ async function liveCommand(_options) {
|
|
|
8088
8054
|
}
|
|
8089
8055
|
function getRecentActivity2() {
|
|
8090
8056
|
try {
|
|
8091
|
-
const output =
|
|
8057
|
+
const output = execSync11(
|
|
8092
8058
|
"gh issue list --repo agents-squads/squads-cli --state open --limit 5 --json number,title,createdAt 2>/dev/null",
|
|
8093
8059
|
{ encoding: "utf-8", timeout: 1e4 }
|
|
8094
8060
|
);
|
|
@@ -8102,7 +8068,7 @@ async function liveCommand(_options) {
|
|
|
8102
8068
|
}
|
|
8103
8069
|
function getMemoryUpdates() {
|
|
8104
8070
|
try {
|
|
8105
|
-
const output =
|
|
8071
|
+
const output = execSync11(
|
|
8106
8072
|
'find .agents/memory -name "state.md" -mmin -60 2>/dev/null | head -5',
|
|
8107
8073
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
8108
8074
|
);
|
|
@@ -8178,7 +8144,7 @@ async function liveCommand(_options) {
|
|
|
8178
8144
|
}
|
|
8179
8145
|
|
|
8180
8146
|
// src/commands/top.ts
|
|
8181
|
-
import { execSync as
|
|
8147
|
+
import { execSync as execSync12 } from "child_process";
|
|
8182
8148
|
var ESC = "\x1B";
|
|
8183
8149
|
var CLEAR_SCREEN = `${ESC}[2J`;
|
|
8184
8150
|
var CURSOR_HOME = `${ESC}[H`;
|
|
@@ -8212,7 +8178,7 @@ function getProcessData() {
|
|
|
8212
8178
|
let claudeCount = 0;
|
|
8213
8179
|
let agentCount = 0;
|
|
8214
8180
|
try {
|
|
8215
|
-
const psOutput =
|
|
8181
|
+
const psOutput = execSync12(
|
|
8216
8182
|
'ps aux | grep -E "[c]laude" | head -15',
|
|
8217
8183
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
8218
8184
|
);
|
|
@@ -8237,7 +8203,7 @@ function getProcessData() {
|
|
|
8237
8203
|
} catch {
|
|
8238
8204
|
}
|
|
8239
8205
|
try {
|
|
8240
|
-
const tmuxOutput =
|
|
8206
|
+
const tmuxOutput = execSync12("tmux ls 2>/dev/null | grep squads- | wc -l", { encoding: "utf-8" });
|
|
8241
8207
|
agentCount = parseInt(tmuxOutput.trim()) || 0;
|
|
8242
8208
|
} catch {
|
|
8243
8209
|
}
|
|
@@ -8340,7 +8306,7 @@ async function detectSquadCommand() {
|
|
|
8340
8306
|
|
|
8341
8307
|
// src/commands/trigger.ts
|
|
8342
8308
|
import chalk3 from "chalk";
|
|
8343
|
-
import { existsSync as
|
|
8309
|
+
import { existsSync as existsSync15 } from "fs";
|
|
8344
8310
|
var SCHEDULER_URL = process.env.SCHEDULER_URL || "http://localhost:8090";
|
|
8345
8311
|
async function fetchScheduler(path3, options) {
|
|
8346
8312
|
const res = await fetch(`${SCHEDULER_URL}${path3}`, {
|
|
@@ -8386,12 +8352,12 @@ async function listTriggers(squad) {
|
|
|
8386
8352
|
}
|
|
8387
8353
|
async function syncTriggers() {
|
|
8388
8354
|
console.log(chalk3.gray("Syncing triggers from SQUAD.md files...\n"));
|
|
8389
|
-
const { execSync:
|
|
8355
|
+
const { execSync: execSync14 } = await import("child_process");
|
|
8390
8356
|
const hqPath = process.env.HQ_PATH || `${process.env.HOME}/agents-squads/hq`;
|
|
8391
8357
|
try {
|
|
8392
8358
|
const venvPython = `${hqPath}/squads-scheduler/.venv/bin/python`;
|
|
8393
|
-
const pythonCmd =
|
|
8394
|
-
const output =
|
|
8359
|
+
const pythonCmd = existsSync15(venvPython) ? venvPython : "python3";
|
|
8360
|
+
const output = execSync14(
|
|
8395
8361
|
`${pythonCmd} ${hqPath}/squads-scheduler/sync_triggers.py`,
|
|
8396
8362
|
{ encoding: "utf-8", cwd: hqPath }
|
|
8397
8363
|
);
|
|
@@ -8474,13 +8440,13 @@ function registerTriggerCommand(program2) {
|
|
|
8474
8440
|
|
|
8475
8441
|
// src/commands/skill.ts
|
|
8476
8442
|
import ora6 from "ora";
|
|
8477
|
-
import { existsSync as
|
|
8478
|
-
import { join as
|
|
8443
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9, readFileSync as readFileSync13 } from "fs";
|
|
8444
|
+
import { join as join17, basename as basename2, dirname as dirname4 } from "path";
|
|
8479
8445
|
|
|
8480
8446
|
// src/lib/anthropic.ts
|
|
8481
8447
|
import Anthropic from "@anthropic-ai/sdk";
|
|
8482
|
-
import { readFileSync as
|
|
8483
|
-
import { join as
|
|
8448
|
+
import { readFileSync as readFileSync12, readdirSync as readdirSync7 } from "fs";
|
|
8449
|
+
import { join as join16 } from "path";
|
|
8484
8450
|
var client = null;
|
|
8485
8451
|
function getClient() {
|
|
8486
8452
|
if (!client) {
|
|
@@ -8509,12 +8475,12 @@ function loadSkillFiles(skillPath) {
|
|
|
8509
8475
|
function walkDir(dir, prefix = "") {
|
|
8510
8476
|
const entries = readdirSync7(dir, { withFileTypes: true });
|
|
8511
8477
|
for (const entry of entries) {
|
|
8512
|
-
const fullPath =
|
|
8478
|
+
const fullPath = join16(dir, entry.name);
|
|
8513
8479
|
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
8514
8480
|
if (entry.isDirectory()) {
|
|
8515
8481
|
walkDir(fullPath, relativePath);
|
|
8516
8482
|
} else if (entry.isFile()) {
|
|
8517
|
-
const content =
|
|
8483
|
+
const content = readFileSync12(fullPath, "utf-8");
|
|
8518
8484
|
files.push({
|
|
8519
8485
|
name: relativePath,
|
|
8520
8486
|
content
|
|
@@ -8678,14 +8644,14 @@ async function skillUploadCommand(skillPath) {
|
|
|
8678
8644
|
writeLine();
|
|
8679
8645
|
return;
|
|
8680
8646
|
}
|
|
8681
|
-
const fullPath = skillPath.startsWith("/") ? skillPath :
|
|
8682
|
-
if (!
|
|
8647
|
+
const fullPath = skillPath.startsWith("/") ? skillPath : join17(process.cwd(), skillPath);
|
|
8648
|
+
if (!existsSync16(fullPath)) {
|
|
8683
8649
|
writeLine(` ${icons.error} ${colors.red}Directory not found: ${skillPath}${RESET}`);
|
|
8684
8650
|
writeLine();
|
|
8685
8651
|
return;
|
|
8686
8652
|
}
|
|
8687
|
-
const skillMdPath =
|
|
8688
|
-
if (!
|
|
8653
|
+
const skillMdPath = join17(fullPath, "SKILL.md");
|
|
8654
|
+
if (!existsSync16(skillMdPath)) {
|
|
8689
8655
|
writeLine(` ${icons.error} ${colors.red}SKILL.md not found in ${skillPath}${RESET}`);
|
|
8690
8656
|
writeLine();
|
|
8691
8657
|
writeLine(` ${colors.dim}Create a SKILL.md file or use:${RESET}`);
|
|
@@ -8796,7 +8762,7 @@ async function skillConvertCommand(agentPath, options) {
|
|
|
8796
8762
|
const [squad, agent] = agentPath.split("/");
|
|
8797
8763
|
squadName = squad;
|
|
8798
8764
|
agentName = agent.replace(".md", "");
|
|
8799
|
-
agentFilePath =
|
|
8765
|
+
agentFilePath = join17(squadsDir, squad, `${agentName}.md`);
|
|
8800
8766
|
} else {
|
|
8801
8767
|
agentName = agentPath.replace(".md", "");
|
|
8802
8768
|
const foundPath = findAgentFile(squadsDir, agentName);
|
|
@@ -8807,22 +8773,22 @@ async function skillConvertCommand(agentPath, options) {
|
|
|
8807
8773
|
return;
|
|
8808
8774
|
}
|
|
8809
8775
|
agentFilePath = foundPath;
|
|
8810
|
-
squadName = basename2(
|
|
8776
|
+
squadName = basename2(dirname4(agentFilePath));
|
|
8811
8777
|
}
|
|
8812
|
-
if (!
|
|
8778
|
+
if (!existsSync16(agentFilePath)) {
|
|
8813
8779
|
writeLine(` ${icons.error} ${colors.red}Agent file not found: ${agentFilePath}${RESET}`);
|
|
8814
8780
|
writeLine();
|
|
8815
8781
|
return;
|
|
8816
8782
|
}
|
|
8817
|
-
const agentContent =
|
|
8783
|
+
const agentContent = readFileSync13(agentFilePath, "utf-8");
|
|
8818
8784
|
const skillName = `${squadName}-${agentName}`;
|
|
8819
|
-
const outputDir = options.output ||
|
|
8820
|
-
if (!
|
|
8821
|
-
|
|
8785
|
+
const outputDir = options.output || join17(dirname4(squadsDir), "skills", skillName);
|
|
8786
|
+
if (!existsSync16(outputDir)) {
|
|
8787
|
+
mkdirSync8(outputDir, { recursive: true });
|
|
8822
8788
|
}
|
|
8823
8789
|
const skillMd = convertAgentToSkill(agentContent, squadName, agentName);
|
|
8824
|
-
const skillMdPath =
|
|
8825
|
-
|
|
8790
|
+
const skillMdPath = join17(outputDir, "SKILL.md");
|
|
8791
|
+
writeFileSync9(skillMdPath, skillMd);
|
|
8826
8792
|
writeLine(` ${icons.success} ${colors.green}Converted:${RESET} ${agentPath}`);
|
|
8827
8793
|
writeLine();
|
|
8828
8794
|
writeLine(` ${colors.dim}Output:${RESET} ${outputDir}`);
|
|
@@ -8839,8 +8805,8 @@ function findAgentFile(squadsDir, agentName) {
|
|
|
8839
8805
|
const { readdirSync: readdirSync9 } = __require("fs");
|
|
8840
8806
|
const squads = readdirSync9(squadsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("_")).map((d) => d.name);
|
|
8841
8807
|
for (const squad of squads) {
|
|
8842
|
-
const agentPath =
|
|
8843
|
-
if (
|
|
8808
|
+
const agentPath = join17(squadsDir, squad, `${agentName}.md`);
|
|
8809
|
+
if (existsSync16(agentPath)) {
|
|
8844
8810
|
return agentPath;
|
|
8845
8811
|
}
|
|
8846
8812
|
}
|
|
@@ -8881,8 +8847,8 @@ function formatBytes(bytes) {
|
|
|
8881
8847
|
}
|
|
8882
8848
|
|
|
8883
8849
|
// src/commands/permissions.ts
|
|
8884
|
-
import { readFileSync as
|
|
8885
|
-
import { join as
|
|
8850
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
8851
|
+
import { join as join18 } from "path";
|
|
8886
8852
|
function registerPermissionsCommand(program2) {
|
|
8887
8853
|
const permissions = program2.command("permissions").alias("perms").description("Manage and validate squad permissions");
|
|
8888
8854
|
permissions.command("show <squad>").description("Show permission context for a squad").action(permissionsShowCommand);
|
|
@@ -8904,11 +8870,11 @@ async function permissionsShowCommand(squadName) {
|
|
|
8904
8870
|
writeLine();
|
|
8905
8871
|
return;
|
|
8906
8872
|
}
|
|
8907
|
-
const squadFilePath =
|
|
8908
|
-
const squadContent =
|
|
8909
|
-
const
|
|
8873
|
+
const squadFilePath = join18(squadsDir, squadName, "SQUAD.md");
|
|
8874
|
+
const squadContent = readFileSync14(squadFilePath, "utf-8");
|
|
8875
|
+
const context = buildContextFromSquad(squadName, squadContent);
|
|
8910
8876
|
const defaults = getDefaultContext(squadName);
|
|
8911
|
-
const isDefault = JSON.stringify(
|
|
8877
|
+
const isDefault = JSON.stringify(context.permissions) === JSON.stringify(defaults.permissions);
|
|
8912
8878
|
writeLine(` ${bold}Squad:${RESET} ${squadName}`);
|
|
8913
8879
|
if (squad.mission) {
|
|
8914
8880
|
writeLine(` ${colors.dim}${squad.mission}${RESET}`);
|
|
@@ -8919,47 +8885,47 @@ async function permissionsShowCommand(squadName) {
|
|
|
8919
8885
|
writeLine(` ${colors.dim}Add a permissions block to SQUAD.md to restrict access.${RESET}`);
|
|
8920
8886
|
writeLine();
|
|
8921
8887
|
}
|
|
8922
|
-
writeLine(` ${bold}Mode:${RESET} ${formatMode(
|
|
8888
|
+
writeLine(` ${bold}Mode:${RESET} ${formatMode(context.permissions.mode)}`);
|
|
8923
8889
|
writeLine();
|
|
8924
8890
|
writeLine(` ${bold}Bash Commands:${RESET}`);
|
|
8925
|
-
if (
|
|
8891
|
+
if (context.permissions.bash.includes("*")) {
|
|
8926
8892
|
writeLine(` ${colors.dim}All commands allowed (*)${RESET}`);
|
|
8927
8893
|
} else {
|
|
8928
|
-
for (const cmd of
|
|
8894
|
+
for (const cmd of context.permissions.bash) {
|
|
8929
8895
|
writeLine(` ${icons.active} ${cmd}`);
|
|
8930
8896
|
}
|
|
8931
8897
|
}
|
|
8932
8898
|
writeLine();
|
|
8933
8899
|
writeLine(` ${bold}Write Paths:${RESET}`);
|
|
8934
|
-
if (
|
|
8900
|
+
if (context.permissions.write.includes("**") || context.permissions.write.includes("*")) {
|
|
8935
8901
|
writeLine(` ${colors.dim}All paths writable (**)${RESET}`);
|
|
8936
8902
|
} else {
|
|
8937
|
-
for (const path3 of
|
|
8903
|
+
for (const path3 of context.permissions.write) {
|
|
8938
8904
|
writeLine(` ${icons.active} ${path3}`);
|
|
8939
8905
|
}
|
|
8940
8906
|
}
|
|
8941
8907
|
writeLine();
|
|
8942
8908
|
writeLine(` ${bold}Read Paths:${RESET}`);
|
|
8943
|
-
if (
|
|
8909
|
+
if (context.permissions.read.includes("**") || context.permissions.read.includes("*")) {
|
|
8944
8910
|
writeLine(` ${colors.dim}All paths readable (**)${RESET}`);
|
|
8945
8911
|
} else {
|
|
8946
|
-
for (const path3 of
|
|
8912
|
+
for (const path3 of context.permissions.read) {
|
|
8947
8913
|
writeLine(` ${icons.active} ${path3}`);
|
|
8948
8914
|
}
|
|
8949
8915
|
}
|
|
8950
8916
|
writeLine();
|
|
8951
8917
|
writeLine(` ${bold}MCP Servers:${RESET}`);
|
|
8952
|
-
if (
|
|
8918
|
+
if (context.permissions.mcp.allow.includes("*")) {
|
|
8953
8919
|
writeLine(` ${colors.dim}All servers allowed (*)${RESET}`);
|
|
8954
8920
|
} else {
|
|
8955
8921
|
writeLine(` ${colors.green}Allow:${RESET}`);
|
|
8956
|
-
for (const server of
|
|
8922
|
+
for (const server of context.permissions.mcp.allow) {
|
|
8957
8923
|
writeLine(` ${icons.success} ${server}`);
|
|
8958
8924
|
}
|
|
8959
8925
|
}
|
|
8960
|
-
if (
|
|
8926
|
+
if (context.permissions.mcp.deny.length > 0) {
|
|
8961
8927
|
writeLine(` ${colors.red}Deny:${RESET}`);
|
|
8962
|
-
for (const server of
|
|
8928
|
+
for (const server of context.permissions.mcp.deny) {
|
|
8963
8929
|
writeLine(` ${icons.error} ${server}`);
|
|
8964
8930
|
}
|
|
8965
8931
|
}
|
|
@@ -8999,9 +8965,9 @@ async function permissionsCheckCommand(squadName, options) {
|
|
|
8999
8965
|
writeLine();
|
|
9000
8966
|
return;
|
|
9001
8967
|
}
|
|
9002
|
-
const squadFilePath =
|
|
9003
|
-
const squadContent =
|
|
9004
|
-
const
|
|
8968
|
+
const squadFilePath = join18(squadsDir, squadName, "SQUAD.md");
|
|
8969
|
+
const squadContent = readFileSync14(squadFilePath, "utf-8");
|
|
8970
|
+
const context = buildContextFromSquad(squadName, squadContent, options.agent);
|
|
9005
8971
|
const request = {
|
|
9006
8972
|
mcpServers: options.mcp,
|
|
9007
8973
|
bashCommands: options.bash,
|
|
@@ -9018,7 +8984,7 @@ async function permissionsCheckCommand(squadName, options) {
|
|
|
9018
8984
|
writeLine();
|
|
9019
8985
|
return;
|
|
9020
8986
|
}
|
|
9021
|
-
const result = validateExecution(
|
|
8987
|
+
const result = validateExecution(context, request);
|
|
9022
8988
|
if (result.violations.length === 0) {
|
|
9023
8989
|
writeLine(` ${icons.success} ${colors.green}All permissions valid${RESET}`);
|
|
9024
8990
|
writeLine();
|
|
@@ -9433,8 +9399,8 @@ function createBudgetBar(percent, width = 10) {
|
|
|
9433
9399
|
}
|
|
9434
9400
|
|
|
9435
9401
|
// src/lib/executions.ts
|
|
9436
|
-
import { readFileSync as
|
|
9437
|
-
import { join as
|
|
9402
|
+
import { readFileSync as readFileSync15, existsSync as existsSync17, readdirSync as readdirSync8 } from "fs";
|
|
9403
|
+
import { join as join19 } from "path";
|
|
9438
9404
|
function parseExecutionEntry(content, squad, agent) {
|
|
9439
9405
|
const idMatch = content.match(/<!-- exec:(\S+) -->/);
|
|
9440
9406
|
if (!idMatch) return null;
|
|
@@ -9472,8 +9438,8 @@ function parseExecutionEntry(content, squad, agent) {
|
|
|
9472
9438
|
};
|
|
9473
9439
|
}
|
|
9474
9440
|
function parseExecutionLog(filePath, squad, agent) {
|
|
9475
|
-
if (!
|
|
9476
|
-
const content =
|
|
9441
|
+
if (!existsSync17(filePath)) return [];
|
|
9442
|
+
const content = readFileSync15(filePath, "utf-8");
|
|
9477
9443
|
const executions = [];
|
|
9478
9444
|
const entries = content.split(/\n---\n/);
|
|
9479
9445
|
for (const entry of entries) {
|
|
@@ -9511,11 +9477,11 @@ function listExecutions(options = {}) {
|
|
|
9511
9477
|
const squads = readdirSync8(memoryDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
9512
9478
|
for (const squad of squads) {
|
|
9513
9479
|
if (filterSquad && squad !== filterSquad) continue;
|
|
9514
|
-
const squadPath =
|
|
9480
|
+
const squadPath = join19(memoryDir, squad);
|
|
9515
9481
|
const agents = readdirSync8(squadPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
9516
9482
|
for (const agent of agents) {
|
|
9517
9483
|
if (filterAgent && agent !== filterAgent) continue;
|
|
9518
|
-
const logPath =
|
|
9484
|
+
const logPath = join19(squadPath, agent, "executions.md");
|
|
9519
9485
|
const agentExecutions = parseExecutionLog(logPath, squad, agent);
|
|
9520
9486
|
executions.push(...agentExecutions);
|
|
9521
9487
|
}
|
|
@@ -9765,12 +9731,12 @@ async function execStatsCommand(options = {}) {
|
|
|
9765
9731
|
// src/commands/tonight.ts
|
|
9766
9732
|
import ora7 from "ora";
|
|
9767
9733
|
import fs2 from "fs/promises";
|
|
9768
|
-
import path2, { dirname as
|
|
9769
|
-
import { execSync as
|
|
9734
|
+
import path2, { dirname as dirname5 } from "path";
|
|
9735
|
+
import { execSync as execSync13, spawn as spawn7 } from "child_process";
|
|
9770
9736
|
function getProjectRoot2() {
|
|
9771
9737
|
const squadsDir = findSquadsDir();
|
|
9772
9738
|
if (squadsDir) {
|
|
9773
|
-
return
|
|
9739
|
+
return dirname5(dirname5(squadsDir));
|
|
9774
9740
|
}
|
|
9775
9741
|
return process.cwd();
|
|
9776
9742
|
}
|
|
@@ -9789,10 +9755,10 @@ async function getCurrentCost() {
|
|
|
9789
9755
|
}
|
|
9790
9756
|
function killAllSessions() {
|
|
9791
9757
|
try {
|
|
9792
|
-
const sessions2 =
|
|
9758
|
+
const sessions2 = execSync13('tmux ls 2>/dev/null | grep "squads-tonight-" | cut -d: -f1', { encoding: "utf-8" }).trim().split("\n").filter(Boolean);
|
|
9793
9759
|
for (const session2 of sessions2) {
|
|
9794
9760
|
try {
|
|
9795
|
-
|
|
9761
|
+
execSync13(`tmux kill-session -t "${session2}"`, { stdio: "ignore" });
|
|
9796
9762
|
} catch {
|
|
9797
9763
|
}
|
|
9798
9764
|
}
|
|
@@ -9803,7 +9769,7 @@ function killAllSessions() {
|
|
|
9803
9769
|
}
|
|
9804
9770
|
function getRunningSessionCount() {
|
|
9805
9771
|
try {
|
|
9806
|
-
const output =
|
|
9772
|
+
const output = execSync13('tmux ls 2>/dev/null | grep "squads-tonight-" | wc -l', { encoding: "utf-8" });
|
|
9807
9773
|
return parseInt(output.trim()) || 0;
|
|
9808
9774
|
} catch {
|
|
9809
9775
|
return 0;
|
|
@@ -9813,6 +9779,7 @@ function launchAgent(target, projectRoot, sessionId, logFile) {
|
|
|
9813
9779
|
const sessionName = `squads-tonight-${sessionId}`;
|
|
9814
9780
|
const claudeCmd = [
|
|
9815
9781
|
`cd '${projectRoot}'`,
|
|
9782
|
+
`unset ANTHROPIC_API_KEY`,
|
|
9816
9783
|
`echo "=== Tonight Session: ${target} ===" >> '${logFile}'`,
|
|
9817
9784
|
`echo "Started: $(date)" >> '${logFile}'`,
|
|
9818
9785
|
`claude --dangerously-skip-permissions -p 'You are running as part of an overnight autonomous session. Execute your tasks efficiently. If you encounter errors, document them clearly and move on. Do not ask for user input.' -- "Run squad: ${target}" 2>&1 | tee -a '${logFile}'`,
|
|
@@ -10018,7 +9985,7 @@ async function tonightStatusCommand() {
|
|
|
10018
9985
|
}
|
|
10019
9986
|
writeLine();
|
|
10020
9987
|
try {
|
|
10021
|
-
const sessions2 =
|
|
9988
|
+
const sessions2 = execSync13("tmux ls 2>/dev/null | grep squads-tonight", { encoding: "utf-8" }).trim().split("\n").filter(Boolean);
|
|
10022
9989
|
if (sessions2.length > 0) {
|
|
10023
9990
|
writeLine(` ${colors.dim}Sessions:${RESET}`);
|
|
10024
9991
|
for (const session2 of sessions2) {
|
|
@@ -10098,12 +10065,12 @@ process.stderr.on("error", (err) => {
|
|
|
10098
10065
|
throw err;
|
|
10099
10066
|
});
|
|
10100
10067
|
var envPaths = [
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10068
|
+
join20(process.cwd(), ".env"),
|
|
10069
|
+
join20(process.cwd(), "..", "hq", ".env"),
|
|
10070
|
+
join20(homedir4(), "agents-squads", "hq", ".env")
|
|
10104
10071
|
];
|
|
10105
10072
|
for (const envPath of envPaths) {
|
|
10106
|
-
if (
|
|
10073
|
+
if (existsSync18(envPath)) {
|
|
10107
10074
|
config({ path: envPath, quiet: true });
|
|
10108
10075
|
break;
|
|
10109
10076
|
}
|
|
@@ -10122,15 +10089,38 @@ program.name("squads").description("A CLI for humans and agents").version(versio
|
|
|
10122
10089
|
process.exit(err.exitCode);
|
|
10123
10090
|
}
|
|
10124
10091
|
throw err;
|
|
10092
|
+
}).action(async () => {
|
|
10093
|
+
const { gradient: gradient2, colors: colors2, RESET: RESET3 } = await import("./terminal-JZSAQSN7.js");
|
|
10094
|
+
const { checkForUpdate: checkForUpdate2 } = await import("./update-MAY6EXFQ.js");
|
|
10095
|
+
console.log();
|
|
10096
|
+
console.log(` ${gradient2("squads")} ${colors2.dim}v${version}${RESET3}`);
|
|
10097
|
+
console.log();
|
|
10098
|
+
const updateInfo = checkForUpdate2();
|
|
10099
|
+
if (updateInfo.updateAvailable) {
|
|
10100
|
+
console.log(` ${colors2.cyan}\u2B06${RESET3} Update available: ${colors2.dim}${updateInfo.currentVersion}${RESET3} \u2192 ${colors2.green}${updateInfo.latestVersion}${RESET3}`);
|
|
10101
|
+
console.log(` ${colors2.dim}Run \`squads update\` to install${RESET3}`);
|
|
10102
|
+
console.log();
|
|
10103
|
+
}
|
|
10104
|
+
await statusCommand(void 0, {});
|
|
10125
10105
|
});
|
|
10126
|
-
program.command("init").description("Initialize a new squad project").option("-t, --template <template>", "Project template", "default").option("--skip-infra", "Skip infrastructure setup prompt").action(initCommand);
|
|
10127
|
-
program.command("run <target>").description("Run a squad or agent").option("-v, --verbose", "Verbose output").option("-d, --dry-run", "Show what would be run without executing").option("-e, --execute", "Execute agent via Claude CLI (requires claude installed)").option("-a, --agent <agent>", "Run specific agent within squad").option("-t, --timeout <minutes>", "Execution timeout in minutes (default: 30)", "30").option("-p, --parallel", "Run all agents in parallel (N tmux sessions)").option("-l, --lead", "Lead mode: single orchestrator using Task tool for parallelization").option("-f, --foreground", "Run in foreground (no tmux, blocks terminal)").option("--use-api", "Use API credits instead of subscription").option("--effort <level>", "Effort level: high, medium, low (default: from SQUAD.md or high)").option("--skills <skills...>", "Skills to load (skill IDs or local paths)").
|
|
10128
|
-
|
|
10106
|
+
program.command("init").description("Initialize a new squad project").option("-t, --template <template>", "Project template", "default").option("--skip-infra", "Skip infrastructure setup prompt").option("--force", "Skip requirement checks (for CI/testing)").action(initCommand);
|
|
10107
|
+
program.command("run <target>").description("Run a squad or agent").option("-v, --verbose", "Verbose output").option("-d, --dry-run", "Show what would be run without executing").option("-e, --execute", "Execute agent via Claude CLI (requires claude installed)").option("-a, --agent <agent>", "Run specific agent within squad").option("-t, --timeout <minutes>", "Execution timeout in minutes (default: 30)", "30").option("-p, --parallel", "Run all agents in parallel (N tmux sessions)").option("-l, --lead", "Lead mode: single orchestrator using Task tool for parallelization").option("-f, --foreground", "Run in foreground (no tmux, blocks terminal)").option("--use-api", "Use API credits instead of subscription").option("--effort <level>", "Effort level: high, medium, low (default: from SQUAD.md or high)").option("--skills <skills...>", "Skills to load (skill IDs or local paths)").addHelpText("after", `
|
|
10108
|
+
Examples:
|
|
10109
|
+
$ squads run engineering Run whole squad (shows agent list)
|
|
10110
|
+
$ squads run engineering/code-review Run specific agent (slash notation)
|
|
10111
|
+
$ squads run engineering -a code-review Same as above (flag notation)
|
|
10112
|
+
$ squads run engineering --dry-run Preview what would run
|
|
10113
|
+
$ squads run engineering --execute Execute via Claude CLI
|
|
10114
|
+
$ squads run engineering --parallel Run all agents in parallel (tmux)
|
|
10115
|
+
$ squads run engineering --lead Single orchestrator with Task tool
|
|
10116
|
+
$ squads run engineering -f Run in foreground (blocks terminal)
|
|
10117
|
+
`).action((target, options) => runCommand(target, { ...options, timeout: parseInt(options.timeout, 10) }));
|
|
10118
|
+
program.command("list").description("List agents and squads").option("-s, --squads", "List squads only").option("-a, --agents", "List agents only").option("-v, --verbose", "Show additional details").action(listCommand);
|
|
10129
10119
|
program.command("status [squad]").description("Show squad status and state").option("-v, --verbose", "Show detailed status").action(statusCommand);
|
|
10130
10120
|
program.command("dashboard").alias("dash").description("Show comprehensive goals and metrics dashboard").option("-v, --verbose", "Show additional details").option("-c, --ceo", "Executive summary with priorities and blockers").option("-f, --full", "Include GitHub PR/issue stats (slower, ~30s)").action((options) => dashboardCommand({ ...options, fast: !options.full }));
|
|
10131
|
-
var
|
|
10132
|
-
|
|
10133
|
-
|
|
10121
|
+
var env = program.command("env").description("View squad execution environment (MCP, skills, model, budget)");
|
|
10122
|
+
env.command("show <squad>").description("Show execution environment for a squad").option("--json", "Output as JSON").action(contextShowCommand);
|
|
10123
|
+
env.command("list").description("List execution environment for all squads").option("--json", "Output as JSON").action(contextListCommand);
|
|
10134
10124
|
program.command("cost").description("Show cost summary (today, week, by squad)").option("-s, --squad <squad>", "Filter to specific squad").option("--json", "Output as JSON").action(costCommand);
|
|
10135
10125
|
program.command("budget").description("Check budget status for a squad").argument("<squad>", "Squad to check").option("--json", "Output as JSON").action(budgetCheckCommand);
|
|
10136
10126
|
var exec2 = program.command("exec").description("View execution history and statistics");
|
|
@@ -10146,7 +10136,7 @@ progress.command("start <squad> <description>").description("Register a new acti
|
|
|
10146
10136
|
progress.command("complete <taskId>").description("Mark a task as completed").option("-f, --failed", "Mark as failed instead").action(progressCompleteCommand);
|
|
10147
10137
|
program.command("results [squad]").description("Show squad results: git activity + KPI goals vs actuals").option("-d, --days <days>", "Days to look back", "7").option("-v, --verbose", "Show detailed KPIs per goal").action((squad, options) => resultsCommand({ ...options, squad }));
|
|
10148
10138
|
program.command("history").description("Show recent agent execution history").option("-d, --days <days>", "Days to look back", "7").option("-s, --squad <squad>", "Filter by squad").option("-v, --verbose", "Show cost and token details").option("-j, --json", "Output as JSON").action((options) => historyCommand(options));
|
|
10149
|
-
program.command("context
|
|
10139
|
+
program.command("context").alias("feed").description("Get business context for alignment: goals, memory, costs, activity").option("-s, --squad <squad>", "Focus on specific squad").option("-t, --topic <topic>", "Search memory for relevant context").option("-a, --agent", "Output JSON for agent consumption").option("-j, --json", "Output as JSON (alias for --agent)").option("-v, --verbose", "Show additional details").action((options) => contextFeedCommand(options));
|
|
10150
10140
|
program.command("workers").description("Show active workers: Claude sessions, tasks, dev servers").option("-v, --verbose", "Show more details").option("-k, --kill <pid>", "Kill a process by PID").action(workersCommand);
|
|
10151
10141
|
program.command("health").description("Quick health check for all infrastructure services").option("-v, --verbose", "Show optional services").action((options) => healthCommand(options));
|
|
10152
10142
|
program.command("watch <command> [args...]").description("Live refresh any squads command (like Unix watch)").option("-n, --interval <seconds>", "Refresh interval in seconds", "2").option("--no-clear", "Don't clear screen between refreshes").action((command, args, options) => watchCommand(command, args, {
|
|
@@ -10155,7 +10145,14 @@ program.command("watch <command> [args...]").description("Live refresh any squad
|
|
|
10155
10145
|
}));
|
|
10156
10146
|
program.command("live").description("Live TUI dashboard with real-time metrics (like htop)").option("-m, --minimal", "Minimal view").option("-f, --focus <panel>", "Focus on specific panel (agents, cost, activity, memory)").action((options) => liveCommand(options));
|
|
10157
10147
|
program.command("top").description("Live process table (like Unix top) - numbers update in place").action(() => topCommand());
|
|
10158
|
-
var memory = program.command("memory").description("Query and manage squad memory").
|
|
10148
|
+
var memory = program.command("memory").description("Query and manage squad memory").addHelpText("after", `
|
|
10149
|
+
Examples:
|
|
10150
|
+
$ squads memory query "pricing" Search all memory for "pricing"
|
|
10151
|
+
$ squads memory show engineering View engineering squad's memory
|
|
10152
|
+
$ squads memory update research "Found: MCP adoption at 15%"
|
|
10153
|
+
$ squads memory list List all memory entries
|
|
10154
|
+
$ squads memory sync --push Sync and push to git
|
|
10155
|
+
`).action(() => {
|
|
10159
10156
|
memory.outputHelp();
|
|
10160
10157
|
});
|
|
10161
10158
|
memory.command("query <query>").description("Search across all squad memory").option("-s, --squad <squad>", "Limit search to specific squad").option("-a, --agent <agent>", "Limit search to specific agent").action(memoryQueryCommand);
|
|
@@ -10191,11 +10188,11 @@ sessions.command("history").description("Show session history and statistics").o
|
|
|
10191
10188
|
json: options.json
|
|
10192
10189
|
}));
|
|
10193
10190
|
sessions.command("summary").description("Show pretty session summary (auto-detects current session or pass JSON)").option("-d, --data <json>", "JSON data for summary (overrides auto-detection)").option("-f, --file <path>", "Path to JSON file with summary data").option("-j, --json", "Output as JSON instead of pretty format").action(async (options) => {
|
|
10194
|
-
const { buildCurrentSessionSummary } = await import("./sessions-
|
|
10191
|
+
const { buildCurrentSessionSummary } = await import("./sessions-R4VWIGFR.js");
|
|
10195
10192
|
let data;
|
|
10196
10193
|
if (options.file) {
|
|
10197
|
-
const { readFileSync:
|
|
10198
|
-
data = JSON.parse(
|
|
10194
|
+
const { readFileSync: readFileSync16 } = await import("fs");
|
|
10195
|
+
data = JSON.parse(readFileSync16(options.file, "utf-8"));
|
|
10199
10196
|
} else if (options.data) {
|
|
10200
10197
|
data = JSON.parse(options.data);
|
|
10201
10198
|
} else if (!process.stdin.isTTY) {
|
|
@@ -10291,31 +10288,4 @@ try {
|
|
|
10291
10288
|
} catch (error) {
|
|
10292
10289
|
handleError(error);
|
|
10293
10290
|
}
|
|
10294
|
-
if (!process.argv.slice(2).length) {
|
|
10295
|
-
console.log(`
|
|
10296
|
-
${chalk4.bold.magenta("squads")} - AI agent squad management
|
|
10297
|
-
|
|
10298
|
-
${chalk4.dim("Quick start:")}
|
|
10299
|
-
${chalk4.cyan("squads status")} View all squads status
|
|
10300
|
-
${chalk4.cyan("squads run <squad>")} Run a squad
|
|
10301
|
-
${chalk4.cyan('squads memory query "<term>"')} Search squad memory
|
|
10302
|
-
|
|
10303
|
-
${chalk4.dim("Goals & Feedback:")}
|
|
10304
|
-
${chalk4.cyan('squads goal set <squad> "<goal>"')} Set a goal
|
|
10305
|
-
${chalk4.cyan("squads goal list")} View active goals
|
|
10306
|
-
${chalk4.cyan('squads feedback add <squad> 4 "msg"')} Rate last execution
|
|
10307
|
-
|
|
10308
|
-
${chalk4.dim("Smart Triggers:")}
|
|
10309
|
-
${chalk4.cyan("squads trigger list")} View all triggers
|
|
10310
|
-
${chalk4.cyan("squads trigger sync")} Sync from SQUAD.md
|
|
10311
|
-
${chalk4.cyan("squads trigger fire <name>")} Manually fire trigger
|
|
10312
|
-
|
|
10313
|
-
${chalk4.dim("Examples:")}
|
|
10314
|
-
${chalk4.cyan("squads run website")} Run website squad
|
|
10315
|
-
${chalk4.cyan('squads goal set finance "Track costs"')} Set finance goal
|
|
10316
|
-
${chalk4.cyan("squads trigger status")} Scheduler health
|
|
10317
|
-
|
|
10318
|
-
${chalk4.dim("Run")} ${chalk4.cyan("squads --help")} ${chalk4.dim("for all commands.")}
|
|
10319
|
-
`);
|
|
10320
|
-
}
|
|
10321
10291
|
//# sourceMappingURL=cli.js.map
|