squads-cli 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{autonomy-PSVZVX7A.js → autonomy-GARI6J2J.js} +4 -4
- package/dist/chunk-NP5BDPE6.js +240 -0
- package/dist/chunk-NP5BDPE6.js.map +1 -0
- package/dist/chunk-O632SBON.js +62 -0
- package/dist/chunk-O632SBON.js.map +1 -0
- package/dist/{chunk-QHNUMM4V.js → chunk-QRNR4GIT.js} +3 -2
- package/dist/chunk-QRNR4GIT.js.map +1 -0
- package/dist/chunk-XTHZT53Y.js +364 -0
- package/dist/chunk-XTHZT53Y.js.map +1 -0
- package/dist/cli.js +1026 -88
- package/dist/cli.js.map +1 -1
- package/dist/{context-GWPF4SEY.js → context-PYTO2UQG.js} +7 -7
- package/dist/{context-feed-AJGVAR6H.js → context-feed-TLVZZ24S.js} +15 -15
- package/dist/{cost-XBCDJ7XC.js → cost-OALPURUQ.js} +7 -7
- package/dist/{dashboard-LGT2B2BL.js → dashboard-HQIEHTZC.js} +14 -14
- package/dist/{doctor-XPUIIBHJ.js → doctor-TWHMR23W.js} +4 -4
- package/dist/{exec-OUXM7JBF.js → exec-DYLI4TXY.js} +2 -2
- package/dist/{feedback-KNAOG5QK.js → feedback-5AEACUX6.js} +8 -8
- package/dist/{goal-BVHV5573.js → goal-XUNV3CKV.js} +8 -8
- package/dist/{health-4UXN44PF.js → health-ZF3HSA4W.js} +4 -4
- package/dist/{history-ILH3SWHB.js → history-WP6R5BNG.js} +5 -5
- package/dist/history-WP6R5BNG.js.map +1 -0
- package/dist/{init-XQZ7BOGT.js → init-BQSCG57S.js} +115 -6
- package/dist/init-BQSCG57S.js.map +1 -0
- package/dist/{kpi-RQIU7WGK.js → kpi-VBGDO4GI.js} +6 -6
- package/dist/{learn-OIFUVZAS.js → learn-C4B2PQ5J.js} +8 -8
- package/dist/{login-DXZANWZY.js → login-F6ITE7PR.js} +7 -7
- package/dist/{memory-T3ACCS7E.js → memory-33HYD6AN.js} +11 -11
- package/dist/observability-CL23L7LD.js +20 -0
- package/dist/observability-CL23L7LD.js.map +1 -0
- package/dist/org-cycle-Q74OT4I4.js +130 -0
- package/dist/org-cycle-Q74OT4I4.js.map +1 -0
- package/dist/{progress-DAUZMT3N.js → progress-P2EIZBKP.js} +5 -5
- package/dist/{providers-3P5D2XL5.js → providers-LE744DM6.js} +2 -2
- package/dist/repo-enforcement-JJQMKDAU.js +75 -0
- package/dist/repo-enforcement-JJQMKDAU.js.map +1 -0
- package/dist/{results-UECWGLTB.js → results-6TH33HPN.js} +6 -6
- package/dist/{run-I6KAXU6U.js → run-DOY5SGF3.js} +3713 -3688
- package/dist/run-DOY5SGF3.js.map +1 -0
- package/dist/run-context-GB6GUCKZ.js +26 -0
- package/dist/run-context-GB6GUCKZ.js.map +1 -0
- package/dist/{status-AQNLDZVN.js → status-PFFB2NV6.js} +16 -16
- package/dist/{sync-ZI3MHA4G.js → sync-FR6LQJ4C.js} +12 -12
- package/dist/templates/seed/config/SYSTEM.md +6 -0
- package/dist/templates/seed/idp/catalog/service.yaml.template +25 -0
- package/dist/templates/seed/memory/_squad/goals.md +23 -0
- package/dist/templates/seed/memory/_squad/priorities.md +25 -0
- package/dist/templates/seed/memory/company/company.md +31 -0
- package/dist/templates/seed/skills/squads-cli/SKILL.md +302 -57
- package/dist/templates/seed/skills/squads-cli/references/commands.md +181 -0
- package/dist/templates/seed/squads/company/company-critic.md +12 -4
- package/dist/templates/seed/squads/company/company-eval.md +12 -4
- package/dist/templates/seed/squads/company/event-dispatcher.md +14 -4
- package/dist/templates/seed/squads/company/goal-tracker.md +12 -4
- package/dist/templates/seed/squads/company/manager.md +17 -11
- package/dist/templates/seed/squads/engineering/code-reviewer.md +14 -2
- package/dist/templates/seed/squads/engineering/issue-solver.md +10 -2
- package/dist/templates/seed/squads/engineering/test-writer.md +15 -5
- package/dist/templates/seed/squads/intelligence/intel-critic.md +19 -2
- package/dist/templates/seed/squads/intelligence/intel-eval.md +18 -1
- package/dist/templates/seed/squads/intelligence/intel-lead.md +12 -4
- package/dist/templates/seed/squads/marketing/content-drafter.md +14 -4
- package/dist/templates/seed/squads/marketing/growth-analyst.md +14 -2
- package/dist/templates/seed/squads/marketing/social-poster.md +15 -3
- package/dist/templates/seed/squads/operations/finance-tracker.md +11 -3
- package/dist/templates/seed/squads/operations/goal-tracker.md +14 -2
- package/dist/templates/seed/squads/operations/ops-lead.md +14 -4
- package/dist/templates/seed/squads/product/lead.md +11 -3
- package/dist/templates/seed/squads/product/scanner.md +12 -4
- package/dist/templates/seed/squads/product/worker.md +12 -4
- package/dist/templates/seed/squads/research/analyst.md +12 -4
- package/dist/templates/seed/squads/research/lead.md +11 -5
- package/dist/templates/seed/squads/research/synthesizer.md +12 -4
- package/dist/tier-detect-YX2HPNNR.js +15 -0
- package/dist/tier-detect-YX2HPNNR.js.map +1 -0
- package/package.json +1 -1
- package/templates/seed/config/SYSTEM.md +6 -0
- package/templates/seed/idp/catalog/service.yaml.template +25 -0
- package/templates/seed/memory/_squad/goals.md +23 -0
- package/templates/seed/memory/_squad/priorities.md +25 -0
- package/templates/seed/memory/company/company.md +31 -0
- package/templates/seed/skills/squads-cli/SKILL.md +302 -57
- package/templates/seed/skills/squads-cli/references/commands.md +181 -0
- package/templates/seed/squads/company/company-critic.md +12 -4
- package/templates/seed/squads/company/company-eval.md +12 -4
- package/templates/seed/squads/company/event-dispatcher.md +14 -4
- package/templates/seed/squads/company/goal-tracker.md +12 -4
- package/templates/seed/squads/company/manager.md +17 -11
- package/templates/seed/squads/engineering/code-reviewer.md +14 -2
- package/templates/seed/squads/engineering/issue-solver.md +10 -2
- package/templates/seed/squads/engineering/test-writer.md +15 -5
- package/templates/seed/squads/intelligence/intel-critic.md +19 -2
- package/templates/seed/squads/intelligence/intel-eval.md +18 -1
- package/templates/seed/squads/intelligence/intel-lead.md +12 -4
- package/templates/seed/squads/marketing/content-drafter.md +14 -4
- package/templates/seed/squads/marketing/growth-analyst.md +14 -2
- package/templates/seed/squads/marketing/social-poster.md +15 -3
- package/templates/seed/squads/operations/finance-tracker.md +11 -3
- package/templates/seed/squads/operations/goal-tracker.md +14 -2
- package/templates/seed/squads/operations/ops-lead.md +14 -4
- package/templates/seed/squads/product/lead.md +11 -3
- package/templates/seed/squads/product/scanner.md +12 -4
- package/templates/seed/squads/product/worker.md +12 -4
- package/templates/seed/squads/research/analyst.md +12 -4
- package/templates/seed/squads/research/lead.md +11 -5
- package/templates/seed/squads/research/synthesizer.md +12 -4
- package/dist/chunk-QHNUMM4V.js.map +0 -1
- package/dist/history-ILH3SWHB.js.map +0 -1
- package/dist/init-XQZ7BOGT.js.map +0 -1
- package/dist/run-I6KAXU6U.js.map +0 -1
- /package/dist/{autonomy-PSVZVX7A.js.map → autonomy-GARI6J2J.js.map} +0 -0
- /package/dist/{context-GWPF4SEY.js.map → context-PYTO2UQG.js.map} +0 -0
- /package/dist/{context-feed-AJGVAR6H.js.map → context-feed-TLVZZ24S.js.map} +0 -0
- /package/dist/{cost-XBCDJ7XC.js.map → cost-OALPURUQ.js.map} +0 -0
- /package/dist/{dashboard-LGT2B2BL.js.map → dashboard-HQIEHTZC.js.map} +0 -0
- /package/dist/{doctor-XPUIIBHJ.js.map → doctor-TWHMR23W.js.map} +0 -0
- /package/dist/{exec-OUXM7JBF.js.map → exec-DYLI4TXY.js.map} +0 -0
- /package/dist/{feedback-KNAOG5QK.js.map → feedback-5AEACUX6.js.map} +0 -0
- /package/dist/{goal-BVHV5573.js.map → goal-XUNV3CKV.js.map} +0 -0
- /package/dist/{health-4UXN44PF.js.map → health-ZF3HSA4W.js.map} +0 -0
- /package/dist/{kpi-RQIU7WGK.js.map → kpi-VBGDO4GI.js.map} +0 -0
- /package/dist/{learn-OIFUVZAS.js.map → learn-C4B2PQ5J.js.map} +0 -0
- /package/dist/{login-DXZANWZY.js.map → login-F6ITE7PR.js.map} +0 -0
- /package/dist/{memory-T3ACCS7E.js.map → memory-33HYD6AN.js.map} +0 -0
- /package/dist/{progress-DAUZMT3N.js.map → progress-P2EIZBKP.js.map} +0 -0
- /package/dist/{providers-3P5D2XL5.js.map → providers-LE744DM6.js.map} +0 -0
- /package/dist/{results-UECWGLTB.js.map → results-6TH33HPN.js.map} +0 -0
- /package/dist/{status-AQNLDZVN.js.map → status-PFFB2NV6.js.map} +0 -0
- /package/dist/{sync-ZI3MHA4G.js.map → sync-FR6LQJ4C.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,20 @@ import {
|
|
|
4
4
|
getNextCronRun,
|
|
5
5
|
parseCooldown
|
|
6
6
|
} from "./chunk-RM6BWILN.js";
|
|
7
|
+
import {
|
|
8
|
+
registerExitHandler,
|
|
9
|
+
track,
|
|
10
|
+
version
|
|
11
|
+
} from "./chunk-QJ7C7CMB.js";
|
|
12
|
+
import {
|
|
13
|
+
RESET,
|
|
14
|
+
bold,
|
|
15
|
+
colors,
|
|
16
|
+
writeLine
|
|
17
|
+
} from "./chunk-M5FXNY6Y.js";
|
|
18
|
+
import {
|
|
19
|
+
findMemoryDir
|
|
20
|
+
} from "./chunk-ZTQ7ISUR.js";
|
|
7
21
|
import {
|
|
8
22
|
getApiUrl
|
|
9
23
|
} from "./chunk-EHQJHRIW.js";
|
|
@@ -11,32 +25,26 @@ import {
|
|
|
11
25
|
loadSession
|
|
12
26
|
} from "./chunk-Z2UKDBNL.js";
|
|
13
27
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
28
|
+
detectTier
|
|
29
|
+
} from "./chunk-O632SBON.js";
|
|
30
|
+
import {
|
|
31
|
+
calculateCostSummary,
|
|
32
|
+
queryExecutions
|
|
33
|
+
} from "./chunk-NP5BDPE6.js";
|
|
18
34
|
import {
|
|
35
|
+
findProjectRoot,
|
|
19
36
|
findSquadsDir,
|
|
20
37
|
listAgents,
|
|
21
38
|
listSquads,
|
|
22
39
|
loadSquad,
|
|
23
40
|
resolveMcpConfigPath
|
|
24
41
|
} from "./chunk-TYFTF53O.js";
|
|
25
|
-
import {
|
|
26
|
-
findMemoryDir
|
|
27
|
-
} from "./chunk-ZTQ7ISUR.js";
|
|
28
|
-
import {
|
|
29
|
-
RESET,
|
|
30
|
-
bold,
|
|
31
|
-
colors,
|
|
32
|
-
writeLine
|
|
33
|
-
} from "./chunk-M5FXNY6Y.js";
|
|
34
42
|
import "./chunk-7OCVIDC7.js";
|
|
35
43
|
|
|
36
44
|
// src/cli.ts
|
|
37
45
|
import { config } from "dotenv";
|
|
38
|
-
import { existsSync as
|
|
39
|
-
import { join as
|
|
46
|
+
import { existsSync as existsSync12 } from "fs";
|
|
47
|
+
import { join as join10 } from "path";
|
|
40
48
|
import { homedir as homedir3 } from "os";
|
|
41
49
|
import { Command } from "commander";
|
|
42
50
|
import chalk6 from "chalk";
|
|
@@ -874,7 +882,7 @@ async function startScheduler() {
|
|
|
874
882
|
}
|
|
875
883
|
);
|
|
876
884
|
child.unref();
|
|
877
|
-
await new Promise((
|
|
885
|
+
await new Promise((resolve2) => setTimeout(resolve2, 2e3));
|
|
878
886
|
const check = isRunning();
|
|
879
887
|
if (check.running) {
|
|
880
888
|
writeLine(chalk2.green(`
|
|
@@ -1194,7 +1202,7 @@ Approval: ${approvalId}`);
|
|
|
1194
1202
|
}
|
|
1195
1203
|
writeLine(chalk3.dim(`Waiting for decision on ${approvalId}...`));
|
|
1196
1204
|
while (Date.now() - startTime < timeoutMs) {
|
|
1197
|
-
await new Promise((
|
|
1205
|
+
await new Promise((resolve2) => setTimeout(resolve2, 5e3));
|
|
1198
1206
|
const updated = await check();
|
|
1199
1207
|
if (!updated) {
|
|
1200
1208
|
console.error(chalk3.red("Approval disappeared"));
|
|
@@ -1226,7 +1234,7 @@ function registerApprovalCommand(program2) {
|
|
|
1226
1234
|
Types: issue, pr, content, run, brief
|
|
1227
1235
|
|
|
1228
1236
|
Examples:
|
|
1229
|
-
$ squads approval send pr --title "Merge feature X" --json '{"repo":"
|
|
1237
|
+
$ squads approval send pr --title "Merge feature X" --json '{"repo":"my-org/my-repo","number":123}'
|
|
1230
1238
|
$ squads approval send content -t "LinkedIn post" -d "New blog announcement"
|
|
1231
1239
|
$ echo '{"title":"Run overnight"}' | squads approval send run --json -
|
|
1232
1240
|
`
|
|
@@ -1446,13 +1454,13 @@ async function deployPullCommand(options) {
|
|
|
1446
1454
|
writeLine("");
|
|
1447
1455
|
writeLine(chalk4.bold("Recent Platform Executions"));
|
|
1448
1456
|
writeLine(chalk4.dim("\u2500".repeat(70)));
|
|
1449
|
-
for (const
|
|
1450
|
-
const statusColor =
|
|
1451
|
-
const cost =
|
|
1452
|
-
const time = new Date(
|
|
1453
|
-
writeLine(` ${statusColor(
|
|
1454
|
-
if (options.verbose &&
|
|
1455
|
-
const duration = (new Date(
|
|
1457
|
+
for (const exec4 of executions) {
|
|
1458
|
+
const statusColor = exec4.status === "completed" ? chalk4.green : exec4.status === "failed" ? chalk4.red : exec4.status === "running" ? chalk4.yellow : chalk4.dim;
|
|
1459
|
+
const cost = exec4.cost_usd !== null ? chalk4.dim(`$${exec4.cost_usd.toFixed(2)}`) : "";
|
|
1460
|
+
const time = new Date(exec4.started_at).toLocaleString();
|
|
1461
|
+
writeLine(` ${statusColor(exec4.status.padEnd(10))} ${chalk4.cyan(exec4.trigger_name)} ${chalk4.dim(time)} ${cost}`);
|
|
1462
|
+
if (options.verbose && exec4.completed_at) {
|
|
1463
|
+
const duration = (new Date(exec4.completed_at).getTime() - new Date(exec4.started_at).getTime()) / 1e3;
|
|
1456
1464
|
writeLine(` ${chalk4.dim(`duration: ${duration.toFixed(0)}s`)}`);
|
|
1457
1465
|
}
|
|
1458
1466
|
}
|
|
@@ -2170,6 +2178,928 @@ Examples:
|
|
|
2170
2178
|
cmd.command("reflect").description("Trigger meta-cognition reflection").option("-s, --scope <scope>", "Reflection scope (business, squad:X, agent:X)", "business").action(async (options) => reflectCommand(options));
|
|
2171
2179
|
}
|
|
2172
2180
|
|
|
2181
|
+
// src/lib/idp/catalog-loader.ts
|
|
2182
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
|
|
2183
|
+
import { join as join7 } from "path";
|
|
2184
|
+
import matter3 from "gray-matter";
|
|
2185
|
+
|
|
2186
|
+
// src/lib/idp/resolver.ts
|
|
2187
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2188
|
+
import { join as join6, resolve } from "path";
|
|
2189
|
+
function findIdpDir() {
|
|
2190
|
+
const envPath = process.env.SQUADS_IDP_PATH;
|
|
2191
|
+
if (envPath && existsSync7(envPath)) {
|
|
2192
|
+
return resolve(envPath);
|
|
2193
|
+
}
|
|
2194
|
+
const projectRoot = findProjectRoot();
|
|
2195
|
+
if (projectRoot) {
|
|
2196
|
+
const colocated = join6(projectRoot, ".agents", "idp");
|
|
2197
|
+
if (existsSync7(join6(colocated, "catalog"))) {
|
|
2198
|
+
return colocated;
|
|
2199
|
+
}
|
|
2200
|
+
const sibling = join6(projectRoot, "..", "idp");
|
|
2201
|
+
if (existsSync7(join6(sibling, "catalog"))) {
|
|
2202
|
+
return resolve(sibling);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
2206
|
+
const absolute = join6(home, "agents-squads", "idp");
|
|
2207
|
+
if (existsSync7(join6(absolute, "catalog"))) {
|
|
2208
|
+
return absolute;
|
|
2209
|
+
}
|
|
2210
|
+
return null;
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
// src/lib/idp/catalog-loader.ts
|
|
2214
|
+
function loadYaml(filePath) {
|
|
2215
|
+
if (!existsSync8(filePath)) return null;
|
|
2216
|
+
try {
|
|
2217
|
+
const raw = readFileSync7(filePath, "utf-8");
|
|
2218
|
+
const { data } = matter3(`---
|
|
2219
|
+
${raw}
|
|
2220
|
+
---`);
|
|
2221
|
+
return data;
|
|
2222
|
+
} catch {
|
|
2223
|
+
return null;
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
function loadCatalog() {
|
|
2227
|
+
const idpDir = findIdpDir();
|
|
2228
|
+
if (!idpDir) return [];
|
|
2229
|
+
const catalogDir = join7(idpDir, "catalog");
|
|
2230
|
+
if (!existsSync8(catalogDir)) return [];
|
|
2231
|
+
const entries = [];
|
|
2232
|
+
for (const file of readdirSync5(catalogDir).filter((f) => f.endsWith(".yaml")).sort()) {
|
|
2233
|
+
const entry = loadYaml(join7(catalogDir, file));
|
|
2234
|
+
if (entry?.metadata?.name) {
|
|
2235
|
+
entries.push(entry);
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
return entries;
|
|
2239
|
+
}
|
|
2240
|
+
function loadService(name) {
|
|
2241
|
+
const idpDir = findIdpDir();
|
|
2242
|
+
if (!idpDir) return null;
|
|
2243
|
+
const filePath = join7(idpDir, "catalog", `${name}.yaml`);
|
|
2244
|
+
return loadYaml(filePath);
|
|
2245
|
+
}
|
|
2246
|
+
function loadScorecard(name) {
|
|
2247
|
+
const idpDir = findIdpDir();
|
|
2248
|
+
if (!idpDir) return null;
|
|
2249
|
+
const filePath = join7(idpDir, "scorecards", `${name}.yaml`);
|
|
2250
|
+
return loadYaml(filePath);
|
|
2251
|
+
}
|
|
2252
|
+
function loadDependencyGraph() {
|
|
2253
|
+
const idpDir = findIdpDir();
|
|
2254
|
+
if (!idpDir) return null;
|
|
2255
|
+
return loadYaml(join7(idpDir, "dependencies", "graph.yaml"));
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
// src/lib/idp/scorecard-engine.ts
|
|
2259
|
+
import { existsSync as existsSync9, statSync as statSync2 } from "fs";
|
|
2260
|
+
import { join as join8 } from "path";
|
|
2261
|
+
import { execSync as execSync3 } from "child_process";
|
|
2262
|
+
function exec(cmd, cwd) {
|
|
2263
|
+
try {
|
|
2264
|
+
return execSync3(cmd, { encoding: "utf-8", timeout: 15e3, cwd, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
2265
|
+
} catch {
|
|
2266
|
+
return null;
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
function ghAvailable() {
|
|
2270
|
+
return exec("gh --version") !== null;
|
|
2271
|
+
}
|
|
2272
|
+
function runCheck(check, service, repoPath) {
|
|
2273
|
+
const result = { name: check.name, passed: false, weight: check.weight, detail: "unknown" };
|
|
2274
|
+
const repo = service.metadata.repo;
|
|
2275
|
+
switch (check.name) {
|
|
2276
|
+
case "ci-passing": {
|
|
2277
|
+
if (!ghAvailable()) {
|
|
2278
|
+
result.detail = "gh CLI not available";
|
|
2279
|
+
break;
|
|
2280
|
+
}
|
|
2281
|
+
const out = exec(`gh api repos/${repo}/actions/runs?per_page=1&status=completed --jq '.[0].conclusion // empty'`);
|
|
2282
|
+
const out2 = exec(`gh api repos/${repo}/actions/runs --jq '.workflow_runs[0].conclusion // empty'`);
|
|
2283
|
+
const conclusion = out || out2;
|
|
2284
|
+
if (conclusion === "success") {
|
|
2285
|
+
result.passed = true;
|
|
2286
|
+
result.detail = "latest run: success";
|
|
2287
|
+
} else if (conclusion) {
|
|
2288
|
+
result.detail = `latest run: ${conclusion}`;
|
|
2289
|
+
} else {
|
|
2290
|
+
result.detail = "no CI runs found";
|
|
2291
|
+
}
|
|
2292
|
+
break;
|
|
2293
|
+
}
|
|
2294
|
+
case "test-coverage": {
|
|
2295
|
+
if (service.spec.ci.test_command && service.spec.ci.test_command !== "null") {
|
|
2296
|
+
result.passed = true;
|
|
2297
|
+
result.detail = `test command defined: ${service.spec.ci.test_command}`;
|
|
2298
|
+
} else {
|
|
2299
|
+
result.detail = "no test command configured";
|
|
2300
|
+
}
|
|
2301
|
+
break;
|
|
2302
|
+
}
|
|
2303
|
+
case "build-succeeds": {
|
|
2304
|
+
if (repoPath && service.spec.ci.build_command) {
|
|
2305
|
+
const buildResult = exec(`cd "${repoPath}" && ${service.spec.ci.build_command} 2>&1`);
|
|
2306
|
+
if (buildResult !== null) {
|
|
2307
|
+
result.passed = true;
|
|
2308
|
+
result.detail = "build passed";
|
|
2309
|
+
} else {
|
|
2310
|
+
result.detail = "build failed";
|
|
2311
|
+
}
|
|
2312
|
+
} else {
|
|
2313
|
+
result.detail = repoPath ? "no build command" : "repo not found locally";
|
|
2314
|
+
}
|
|
2315
|
+
break;
|
|
2316
|
+
}
|
|
2317
|
+
case "no-security-alerts": {
|
|
2318
|
+
if (!ghAvailable()) {
|
|
2319
|
+
result.detail = "gh CLI not available";
|
|
2320
|
+
break;
|
|
2321
|
+
}
|
|
2322
|
+
const alerts = exec(`gh api repos/${repo}/dependabot/alerts --jq '[.[] | select(.state=="open" and (.security_advisory.severity=="high" or .security_advisory.severity=="critical"))] | length'`);
|
|
2323
|
+
if (alerts === "0") {
|
|
2324
|
+
result.passed = true;
|
|
2325
|
+
result.detail = "no high/critical alerts";
|
|
2326
|
+
} else if (alerts) {
|
|
2327
|
+
result.detail = `${alerts} high/critical alerts`;
|
|
2328
|
+
} else {
|
|
2329
|
+
result.detail = "could not check alerts";
|
|
2330
|
+
}
|
|
2331
|
+
break;
|
|
2332
|
+
}
|
|
2333
|
+
case "readme-exists": {
|
|
2334
|
+
if (repoPath) {
|
|
2335
|
+
const readmePath = join8(repoPath, "README.md");
|
|
2336
|
+
if (existsSync9(readmePath)) {
|
|
2337
|
+
const size = statSync2(readmePath).size;
|
|
2338
|
+
if (size > 100) {
|
|
2339
|
+
result.passed = true;
|
|
2340
|
+
result.detail = `README.md (${size} bytes)`;
|
|
2341
|
+
} else {
|
|
2342
|
+
result.detail = `README.md too short (${size} bytes)`;
|
|
2343
|
+
}
|
|
2344
|
+
} else {
|
|
2345
|
+
result.detail = "README.md not found";
|
|
2346
|
+
}
|
|
2347
|
+
} else {
|
|
2348
|
+
result.detail = "repo not found locally";
|
|
2349
|
+
}
|
|
2350
|
+
break;
|
|
2351
|
+
}
|
|
2352
|
+
case "branch-protection": {
|
|
2353
|
+
if (!ghAvailable()) {
|
|
2354
|
+
result.detail = "gh CLI not available";
|
|
2355
|
+
break;
|
|
2356
|
+
}
|
|
2357
|
+
const protection = exec(`gh api repos/${repo}/branches/${service.spec.branches.default}/protection --jq '.required_status_checks.strict // false' 2>/dev/null`);
|
|
2358
|
+
if (protection && protection !== "null") {
|
|
2359
|
+
result.passed = true;
|
|
2360
|
+
result.detail = "branch protection enabled";
|
|
2361
|
+
} else {
|
|
2362
|
+
result.detail = "no branch protection";
|
|
2363
|
+
}
|
|
2364
|
+
break;
|
|
2365
|
+
}
|
|
2366
|
+
case "deploy-frequency": {
|
|
2367
|
+
if (!ghAvailable()) {
|
|
2368
|
+
result.detail = "gh CLI not available";
|
|
2369
|
+
break;
|
|
2370
|
+
}
|
|
2371
|
+
const runs = exec(`gh api repos/${repo}/actions/runs --jq '[.workflow_runs[] | select(.event=="push" and .head_branch=="${service.spec.branches.default}")] | length'`);
|
|
2372
|
+
const count = parseInt(runs || "0", 10);
|
|
2373
|
+
if (count > 0) {
|
|
2374
|
+
result.passed = true;
|
|
2375
|
+
result.detail = `${count} deploys recently`;
|
|
2376
|
+
} else {
|
|
2377
|
+
result.detail = "no recent deploys";
|
|
2378
|
+
}
|
|
2379
|
+
break;
|
|
2380
|
+
}
|
|
2381
|
+
case "stale-prs": {
|
|
2382
|
+
if (!ghAvailable()) {
|
|
2383
|
+
result.detail = "gh CLI not available";
|
|
2384
|
+
break;
|
|
2385
|
+
}
|
|
2386
|
+
const stalePrs = exec(`gh pr list --repo ${repo} --state open --json updatedAt --jq '[.[] | select((now - (.updatedAt | fromdateiso8601)) > 1209600)] | length'`);
|
|
2387
|
+
const count = parseInt(stalePrs || "0", 10);
|
|
2388
|
+
if (count === 0) {
|
|
2389
|
+
result.passed = true;
|
|
2390
|
+
result.detail = "no stale PRs";
|
|
2391
|
+
} else {
|
|
2392
|
+
result.detail = `${count} PRs stale >14d`;
|
|
2393
|
+
}
|
|
2394
|
+
break;
|
|
2395
|
+
}
|
|
2396
|
+
case "recent-activity": {
|
|
2397
|
+
if (repoPath) {
|
|
2398
|
+
const commits = exec(`git -C "${repoPath}" log --since="30 days ago" --oneline 2>/dev/null | wc -l`);
|
|
2399
|
+
const count = parseInt(commits?.trim() || "0", 10);
|
|
2400
|
+
if (count > 0) {
|
|
2401
|
+
result.passed = true;
|
|
2402
|
+
result.detail = `${count} commits in last 30d`;
|
|
2403
|
+
} else {
|
|
2404
|
+
result.detail = "no commits in 30 days";
|
|
2405
|
+
}
|
|
2406
|
+
} else if (ghAvailable()) {
|
|
2407
|
+
const out = exec(`gh api repos/${repo}/commits?per_page=1 --jq '.[0].commit.committer.date // empty'`);
|
|
2408
|
+
if (out) {
|
|
2409
|
+
result.passed = true;
|
|
2410
|
+
result.detail = `last commit: ${out.slice(0, 10)}`;
|
|
2411
|
+
} else {
|
|
2412
|
+
result.detail = "no recent commits";
|
|
2413
|
+
}
|
|
2414
|
+
} else {
|
|
2415
|
+
result.detail = "repo not found locally";
|
|
2416
|
+
}
|
|
2417
|
+
break;
|
|
2418
|
+
}
|
|
2419
|
+
case "no-stale-prs": {
|
|
2420
|
+
if (!ghAvailable()) {
|
|
2421
|
+
result.detail = "gh CLI not available";
|
|
2422
|
+
break;
|
|
2423
|
+
}
|
|
2424
|
+
const stalePrs = exec(`gh pr list --repo ${repo} --state open --json updatedAt --jq '[.[] | select((now - (.updatedAt | fromdateiso8601)) > 604800)] | length'`);
|
|
2425
|
+
const count = parseInt(stalePrs || "0", 10);
|
|
2426
|
+
if (count === 0) {
|
|
2427
|
+
result.passed = true;
|
|
2428
|
+
result.detail = "no stale PRs";
|
|
2429
|
+
} else {
|
|
2430
|
+
result.detail = `${count} PRs stale >7d`;
|
|
2431
|
+
}
|
|
2432
|
+
break;
|
|
2433
|
+
}
|
|
2434
|
+
case "clean-structure": {
|
|
2435
|
+
result.passed = true;
|
|
2436
|
+
result.detail = "check not implemented (v0.2)";
|
|
2437
|
+
break;
|
|
2438
|
+
}
|
|
2439
|
+
default:
|
|
2440
|
+
result.detail = `unknown check: ${check.name}`;
|
|
2441
|
+
}
|
|
2442
|
+
return result;
|
|
2443
|
+
}
|
|
2444
|
+
function findRepoPath(repoFullName) {
|
|
2445
|
+
const repoName = repoFullName.split("/")[1];
|
|
2446
|
+
if (!repoName) return null;
|
|
2447
|
+
const home = process.env.HOME || "";
|
|
2448
|
+
const candidates = [
|
|
2449
|
+
join8(home, "agents-squads", repoName),
|
|
2450
|
+
join8(process.cwd(), "..", repoName)
|
|
2451
|
+
];
|
|
2452
|
+
for (const candidate of candidates) {
|
|
2453
|
+
if (existsSync9(candidate)) return candidate;
|
|
2454
|
+
}
|
|
2455
|
+
return null;
|
|
2456
|
+
}
|
|
2457
|
+
function evaluateService(service, scorecard) {
|
|
2458
|
+
const repoPath = findRepoPath(service.metadata.repo);
|
|
2459
|
+
const checks = [];
|
|
2460
|
+
for (const check of scorecard.checks) {
|
|
2461
|
+
checks.push(runCheck(check, service, repoPath));
|
|
2462
|
+
}
|
|
2463
|
+
const totalWeight = checks.reduce((sum, c) => sum + c.weight, 0);
|
|
2464
|
+
const earnedWeight = checks.filter((c) => c.passed).reduce((sum, c) => sum + c.weight, 0);
|
|
2465
|
+
const score = totalWeight > 0 ? Math.round(earnedWeight / totalWeight * 100) : 0;
|
|
2466
|
+
let grade = "F";
|
|
2467
|
+
const sortedGrades = Object.entries(scorecard.grades).sort((a, b) => b[1].min - a[1].min);
|
|
2468
|
+
for (const [g, { min }] of sortedGrades) {
|
|
2469
|
+
if (score >= min) {
|
|
2470
|
+
grade = g;
|
|
2471
|
+
break;
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
return {
|
|
2475
|
+
service: service.metadata.name,
|
|
2476
|
+
scorecard: scorecard.metadata.name,
|
|
2477
|
+
score,
|
|
2478
|
+
grade,
|
|
2479
|
+
checks,
|
|
2480
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2481
|
+
};
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
// src/commands/catalog.ts
|
|
2485
|
+
function noIdp() {
|
|
2486
|
+
if (!findIdpDir()) {
|
|
2487
|
+
writeLine(` ${colors.red}IDP not found${RESET}`);
|
|
2488
|
+
writeLine(` ${colors.dim}Set SQUADS_IDP_PATH or clone the idp repo as a sibling directory.${RESET}`);
|
|
2489
|
+
return true;
|
|
2490
|
+
}
|
|
2491
|
+
return false;
|
|
2492
|
+
}
|
|
2493
|
+
function registerCatalogCommands(program2) {
|
|
2494
|
+
const catalog = program2.command("catalog").description("Service catalog \u2014 browse, inspect, and validate services");
|
|
2495
|
+
catalog.command("list").description("List all services in the catalog").option("--type <type>", "Filter by type (product, domain)").option("--json", "Output as JSON").action((opts) => {
|
|
2496
|
+
if (noIdp()) return;
|
|
2497
|
+
const entries = loadCatalog();
|
|
2498
|
+
if (entries.length === 0) {
|
|
2499
|
+
writeLine(" No catalog entries found.");
|
|
2500
|
+
return;
|
|
2501
|
+
}
|
|
2502
|
+
const filtered = opts.type ? entries.filter((e) => e.spec.type === opts.type) : entries;
|
|
2503
|
+
if (opts.json) {
|
|
2504
|
+
console.log(JSON.stringify(filtered.map((e) => ({
|
|
2505
|
+
name: e.metadata.name,
|
|
2506
|
+
type: e.spec.type,
|
|
2507
|
+
stack: e.spec.stack,
|
|
2508
|
+
owner: e.metadata.owner,
|
|
2509
|
+
repo: e.metadata.repo
|
|
2510
|
+
})), null, 2));
|
|
2511
|
+
return;
|
|
2512
|
+
}
|
|
2513
|
+
writeLine();
|
|
2514
|
+
writeLine(` ${bold}Service Catalog${RESET} (${filtered.length} services)`);
|
|
2515
|
+
writeLine();
|
|
2516
|
+
const products = filtered.filter((e) => e.spec.type === "product");
|
|
2517
|
+
const domains = filtered.filter((e) => e.spec.type === "domain");
|
|
2518
|
+
if (products.length > 0) {
|
|
2519
|
+
writeLine(` ${colors.cyan}Product Services${RESET}`);
|
|
2520
|
+
writeLine();
|
|
2521
|
+
for (const e of products) {
|
|
2522
|
+
const ci = e.spec.ci.template ? `ci:${e.spec.ci.template}` : "no-ci";
|
|
2523
|
+
const deploy = e.spec.deploy?.target || "manual";
|
|
2524
|
+
writeLine(` ${bold}${e.metadata.name}${RESET} ${colors.dim}${e.spec.stack} | ${ci} | deploy:${deploy} | owner:${e.metadata.owner}${RESET}`);
|
|
2525
|
+
writeLine(` ${colors.dim}${e.metadata.description}${RESET}`);
|
|
2526
|
+
}
|
|
2527
|
+
writeLine();
|
|
2528
|
+
}
|
|
2529
|
+
if (domains.length > 0) {
|
|
2530
|
+
writeLine(` ${colors.cyan}Domain Repos${RESET}`);
|
|
2531
|
+
writeLine();
|
|
2532
|
+
for (const e of domains) {
|
|
2533
|
+
writeLine(` ${e.metadata.name} ${colors.dim}owner:${e.metadata.owner} | ${e.metadata.repo}${RESET}`);
|
|
2534
|
+
}
|
|
2535
|
+
writeLine();
|
|
2536
|
+
}
|
|
2537
|
+
});
|
|
2538
|
+
catalog.command("show <service>").description("Show detailed info for a service").option("--json", "Output as JSON").action((serviceName, opts) => {
|
|
2539
|
+
if (noIdp()) return;
|
|
2540
|
+
const entry = loadService(serviceName);
|
|
2541
|
+
if (!entry) {
|
|
2542
|
+
writeLine(` ${colors.red}Service not found: ${serviceName}${RESET}`);
|
|
2543
|
+
writeLine(` ${colors.dim}Run 'squads catalog list' to see available services.${RESET}`);
|
|
2544
|
+
return;
|
|
2545
|
+
}
|
|
2546
|
+
if (opts.json) {
|
|
2547
|
+
console.log(JSON.stringify(entry, null, 2));
|
|
2548
|
+
return;
|
|
2549
|
+
}
|
|
2550
|
+
writeLine();
|
|
2551
|
+
writeLine(` ${bold}${entry.metadata.name}${RESET} ${colors.dim}${entry.spec.type}${RESET}`);
|
|
2552
|
+
writeLine(` ${entry.metadata.description}`);
|
|
2553
|
+
writeLine();
|
|
2554
|
+
writeLine(` ${colors.cyan}General${RESET}`);
|
|
2555
|
+
writeLine(` Owner: ${entry.metadata.owner}`);
|
|
2556
|
+
writeLine(` Repo: ${entry.metadata.repo}`);
|
|
2557
|
+
writeLine(` Stack: ${entry.spec.stack}${entry.spec.framework ? ` (${entry.spec.framework})` : ""}`);
|
|
2558
|
+
writeLine(` Scorecard: ${entry.spec.scorecard}`);
|
|
2559
|
+
writeLine(` Tags: ${entry.metadata.tags?.join(", ") || "none"}`);
|
|
2560
|
+
writeLine();
|
|
2561
|
+
writeLine(` ${colors.cyan}Branches${RESET}`);
|
|
2562
|
+
writeLine(` Default: ${entry.spec.branches.default}`);
|
|
2563
|
+
writeLine(` Workflow: ${entry.spec.branches.workflow}`);
|
|
2564
|
+
if (entry.spec.branches.development) {
|
|
2565
|
+
writeLine(` Dev branch: ${entry.spec.branches.development}`);
|
|
2566
|
+
}
|
|
2567
|
+
writeLine();
|
|
2568
|
+
if (entry.spec.ci.template) {
|
|
2569
|
+
writeLine(` ${colors.cyan}CI/CD${RESET}`);
|
|
2570
|
+
writeLine(` Template: ${entry.spec.ci.template}`);
|
|
2571
|
+
writeLine(` Checks: ${entry.spec.ci.required_checks.join(", ") || "none"}`);
|
|
2572
|
+
if (entry.spec.ci.build_command) writeLine(` Build: ${entry.spec.ci.build_command}`);
|
|
2573
|
+
if (entry.spec.ci.test_command) writeLine(` Test: ${entry.spec.ci.test_command}`);
|
|
2574
|
+
writeLine();
|
|
2575
|
+
}
|
|
2576
|
+
if (entry.spec.deploy) {
|
|
2577
|
+
writeLine(` ${colors.cyan}Deploy${RESET}`);
|
|
2578
|
+
writeLine(` Target: ${entry.spec.deploy.target}`);
|
|
2579
|
+
writeLine(` Trigger: ${entry.spec.deploy.trigger}`);
|
|
2580
|
+
if (entry.spec.deploy.environments) {
|
|
2581
|
+
for (const env2 of entry.spec.deploy.environments) {
|
|
2582
|
+
writeLine(` ${env2.name}: ${env2.url}`);
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
writeLine();
|
|
2586
|
+
}
|
|
2587
|
+
if (entry.spec.dependencies.runtime.length > 0) {
|
|
2588
|
+
writeLine(` ${colors.cyan}Dependencies${RESET}`);
|
|
2589
|
+
for (const dep of entry.spec.dependencies.runtime) {
|
|
2590
|
+
const req = dep.required === false ? "(optional)" : "(required)";
|
|
2591
|
+
writeLine(` \u2192 ${dep.service} ${dep.version || ""} ${req}`);
|
|
2592
|
+
writeLine(` ${colors.dim}${dep.description}${RESET}`);
|
|
2593
|
+
}
|
|
2594
|
+
writeLine();
|
|
2595
|
+
}
|
|
2596
|
+
if (entry.spec.health.length > 0) {
|
|
2597
|
+
writeLine(` ${colors.cyan}Health Endpoints${RESET}`);
|
|
2598
|
+
for (const h of entry.spec.health) {
|
|
2599
|
+
writeLine(` ${h.name}: ${h.url}`);
|
|
2600
|
+
}
|
|
2601
|
+
writeLine();
|
|
2602
|
+
}
|
|
2603
|
+
});
|
|
2604
|
+
catalog.command("check [service]").description("Run scorecard checks for a service (or all)").option("--json", "Output as JSON").action((serviceName, opts) => {
|
|
2605
|
+
if (noIdp()) return;
|
|
2606
|
+
const entries = serviceName ? [loadService(serviceName)].filter(Boolean) : loadCatalog();
|
|
2607
|
+
if (entries.length === 0) {
|
|
2608
|
+
writeLine(` ${colors.red}No services found${RESET}`);
|
|
2609
|
+
return;
|
|
2610
|
+
}
|
|
2611
|
+
const results = [];
|
|
2612
|
+
for (const entry of entries) {
|
|
2613
|
+
const scorecard = loadScorecard(entry.spec.scorecard);
|
|
2614
|
+
if (!scorecard) {
|
|
2615
|
+
writeLine(` ${colors.dim}No scorecard '${entry.spec.scorecard}' for ${entry.metadata.name}${RESET}`);
|
|
2616
|
+
continue;
|
|
2617
|
+
}
|
|
2618
|
+
const result = evaluateService(entry, scorecard);
|
|
2619
|
+
results.push(result);
|
|
2620
|
+
if (!opts.json) {
|
|
2621
|
+
const gradeColor = result.grade === "A" ? colors.green : result.grade === "B" ? colors.cyan : result.grade === "C" ? colors.yellow : colors.red;
|
|
2622
|
+
writeLine();
|
|
2623
|
+
writeLine(` ${bold}${result.service}${RESET} ${gradeColor}${result.grade}${RESET} (${result.score}/100)`);
|
|
2624
|
+
for (const check of result.checks) {
|
|
2625
|
+
const icon = check.passed ? `${colors.green}pass${RESET}` : `${colors.red}fail${RESET}`;
|
|
2626
|
+
writeLine(` ${icon} ${check.name} ${colors.dim}(${check.detail})${RESET}`);
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
if (opts.json) {
|
|
2631
|
+
console.log(JSON.stringify(results, null, 2));
|
|
2632
|
+
} else {
|
|
2633
|
+
writeLine();
|
|
2634
|
+
}
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
// src/commands/release-check.ts
|
|
2639
|
+
async function checkHealth(url, expect) {
|
|
2640
|
+
try {
|
|
2641
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(1e4) });
|
|
2642
|
+
return { ok: response.status === expect, status: response.status };
|
|
2643
|
+
} catch (e) {
|
|
2644
|
+
return { ok: false, status: e instanceof Error ? e.message : "unreachable" };
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
function registerReleaseCommands(program2) {
|
|
2648
|
+
const release = program2.command("release").description("Release management \u2014 pre-deploy checks and status");
|
|
2649
|
+
release.command("pre-check <service>").description("Validate dependencies and health before deploying a service").option("--skip-health", "Skip health endpoint checks").action(async (serviceName, opts) => {
|
|
2650
|
+
const idpDir = findIdpDir();
|
|
2651
|
+
if (!idpDir) {
|
|
2652
|
+
writeLine(` ${colors.red}IDP not found${RESET}`);
|
|
2653
|
+
return;
|
|
2654
|
+
}
|
|
2655
|
+
const service = loadService(serviceName);
|
|
2656
|
+
if (!service) {
|
|
2657
|
+
writeLine(` ${colors.red}Service not found: ${serviceName}${RESET}`);
|
|
2658
|
+
return;
|
|
2659
|
+
}
|
|
2660
|
+
const graph = loadDependencyGraph();
|
|
2661
|
+
const deps = service.spec.dependencies.runtime;
|
|
2662
|
+
writeLine();
|
|
2663
|
+
writeLine(` ${bold}Release Pre-Check: ${serviceName}${RESET}`);
|
|
2664
|
+
writeLine();
|
|
2665
|
+
let allGreen = true;
|
|
2666
|
+
if (deps.length === 0) {
|
|
2667
|
+
writeLine(` ${colors.green}pass${RESET} No runtime dependencies`);
|
|
2668
|
+
} else {
|
|
2669
|
+
writeLine(` ${colors.cyan}Dependencies${RESET}`);
|
|
2670
|
+
for (const dep of deps) {
|
|
2671
|
+
const depService = loadService(dep.service);
|
|
2672
|
+
const req = dep.required !== false;
|
|
2673
|
+
if (!depService) {
|
|
2674
|
+
if (dep.type === "infrastructure") {
|
|
2675
|
+
writeLine(` ${colors.dim}skip${RESET} ${dep.service} (infrastructure \u2014 not in catalog)`);
|
|
2676
|
+
continue;
|
|
2677
|
+
}
|
|
2678
|
+
if (req) {
|
|
2679
|
+
writeLine(` ${colors.red}fail${RESET} ${dep.service} \u2014 not found in catalog`);
|
|
2680
|
+
allGreen = false;
|
|
2681
|
+
} else {
|
|
2682
|
+
writeLine(` ${colors.yellow}warn${RESET} ${dep.service} \u2014 not in catalog (optional)`);
|
|
2683
|
+
}
|
|
2684
|
+
continue;
|
|
2685
|
+
}
|
|
2686
|
+
if (!opts.skipHealth && depService.spec.health.length > 0) {
|
|
2687
|
+
for (const h of depService.spec.health) {
|
|
2688
|
+
const result = await checkHealth(h.url, h.expect);
|
|
2689
|
+
if (result.ok) {
|
|
2690
|
+
writeLine(` ${colors.green}pass${RESET} ${dep.service}/${h.name} \u2014 ${result.status}`);
|
|
2691
|
+
} else if (req) {
|
|
2692
|
+
writeLine(` ${colors.red}fail${RESET} ${dep.service}/${h.name} \u2014 ${result.status}`);
|
|
2693
|
+
allGreen = false;
|
|
2694
|
+
} else {
|
|
2695
|
+
writeLine(` ${colors.yellow}warn${RESET} ${dep.service}/${h.name} \u2014 ${result.status} (optional)`);
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
} else {
|
|
2699
|
+
writeLine(` ${colors.dim}skip${RESET} ${dep.service} health check (${opts.skipHealth ? "skipped" : "no endpoints"})`);
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
writeLine();
|
|
2704
|
+
if (graph) {
|
|
2705
|
+
const order = graph.deploy_order;
|
|
2706
|
+
let servicePhase = -1;
|
|
2707
|
+
for (let i = 0; i < order.length; i++) {
|
|
2708
|
+
if (order[i].includes(serviceName)) {
|
|
2709
|
+
servicePhase = i;
|
|
2710
|
+
break;
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
if (servicePhase >= 0) {
|
|
2714
|
+
writeLine(` ${colors.cyan}Deploy Order${RESET}`);
|
|
2715
|
+
for (let i = 0; i < order.length; i++) {
|
|
2716
|
+
const marker = i === servicePhase ? `${colors.green}\u2192${RESET}` : " ";
|
|
2717
|
+
const phase = order[i].join(", ");
|
|
2718
|
+
writeLine(` ${marker} Phase ${i + 1}: ${i === servicePhase ? bold : colors.dim}${phase}${RESET}`);
|
|
2719
|
+
}
|
|
2720
|
+
writeLine();
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
if (!opts.skipHealth && service.spec.health.length > 0) {
|
|
2724
|
+
writeLine(` ${colors.cyan}Self Health${RESET}`);
|
|
2725
|
+
for (const h of service.spec.health) {
|
|
2726
|
+
const result = await checkHealth(h.url, h.expect);
|
|
2727
|
+
if (result.ok) {
|
|
2728
|
+
writeLine(` ${colors.green}pass${RESET} ${h.name} \u2014 ${result.status}`);
|
|
2729
|
+
} else {
|
|
2730
|
+
writeLine(` ${colors.yellow}warn${RESET} ${h.name} \u2014 ${result.status}`);
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
writeLine();
|
|
2734
|
+
}
|
|
2735
|
+
if (allGreen) {
|
|
2736
|
+
writeLine(` ${colors.green}All checks passed \u2014 safe to deploy ${serviceName}${RESET}`);
|
|
2737
|
+
} else {
|
|
2738
|
+
writeLine(` ${colors.red}Pre-check failed \u2014 fix issues before deploying ${serviceName}${RESET}`);
|
|
2739
|
+
}
|
|
2740
|
+
writeLine();
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
// src/commands/observability.ts
|
|
2745
|
+
function registerObservabilityCommands(program2) {
|
|
2746
|
+
const obs = program2.command("obs").description("Observability \u2014 execution history, token costs, and trends");
|
|
2747
|
+
obs.command("history").description("Show execution history with tokens and cost").option("-s, --squad <squad>", "Filter by squad").option("-a, --agent <agent>", "Filter by agent").option("-n, --limit <n>", "Number of records", "20").option("--since <date>", "Since date (ISO or relative: 1d, 7d, 30d)").option("--json", "Output as JSON").action((opts) => {
|
|
2748
|
+
let since = opts.since;
|
|
2749
|
+
if (since && /^\d+d$/.test(since)) {
|
|
2750
|
+
const days = parseInt(since, 10);
|
|
2751
|
+
since = new Date(Date.now() - days * 24 * 60 * 60 * 1e3).toISOString();
|
|
2752
|
+
}
|
|
2753
|
+
const records = queryExecutions({
|
|
2754
|
+
squad: opts.squad,
|
|
2755
|
+
agent: opts.agent,
|
|
2756
|
+
since,
|
|
2757
|
+
limit: parseInt(opts.limit, 10)
|
|
2758
|
+
});
|
|
2759
|
+
if (records.length === 0) {
|
|
2760
|
+
writeLine(`
|
|
2761
|
+
${colors.dim}No executions found. Run \`squads run <squad>\` to generate data.${RESET}
|
|
2762
|
+
`);
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2765
|
+
if (opts.json) {
|
|
2766
|
+
console.log(JSON.stringify(records, null, 2));
|
|
2767
|
+
return;
|
|
2768
|
+
}
|
|
2769
|
+
writeLine(`
|
|
2770
|
+
${bold}Execution History${RESET} (${records.length} records)
|
|
2771
|
+
`);
|
|
2772
|
+
for (const r of records) {
|
|
2773
|
+
const icon = r.status === "completed" ? `${colors.green}pass${RESET}` : r.status === "failed" ? `${colors.red}fail${RESET}` : `${colors.yellow}timeout${RESET}`;
|
|
2774
|
+
const dur = r.duration_ms > 6e4 ? `${Math.round(r.duration_ms / 6e4)}m` : `${Math.round(r.duration_ms / 1e3)}s`;
|
|
2775
|
+
const cost = r.cost_usd > 0 ? `$${r.cost_usd.toFixed(3)}` : "$\u2014";
|
|
2776
|
+
const tok = r.input_tokens + r.output_tokens > 0 ? `${(r.input_tokens + r.output_tokens).toLocaleString()} tok` : "\u2014 tok";
|
|
2777
|
+
const date = r.ts.slice(0, 16).replace("T", " ");
|
|
2778
|
+
const grade = r.grade ? ` ${r.grade}` : "";
|
|
2779
|
+
writeLine(` ${icon} ${bold}${r.squad}/${r.agent}${RESET} ${colors.dim}${date} ${dur} ${tok} ${cost} ${r.model}${grade}${RESET}`);
|
|
2780
|
+
if (r.error) writeLine(` ${colors.red}${r.error.slice(0, 80)}${RESET}`);
|
|
2781
|
+
if (r.goals_changed && r.goals_changed.length > 0) {
|
|
2782
|
+
for (const g of r.goals_changed) {
|
|
2783
|
+
writeLine(` ${colors.green}goal: ${g.name} ${g.before} \u2192 ${g.after}${RESET}`);
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
writeLine();
|
|
2788
|
+
});
|
|
2789
|
+
obs.command("cost").description("Show token spend summary").option("-p, --period <period>", "Time period: today, 7d, 30d, all", "7d").option("--json", "Output as JSON").action((opts) => {
|
|
2790
|
+
const summary = calculateCostSummary(opts.period);
|
|
2791
|
+
if (summary.total_runs === 0) {
|
|
2792
|
+
writeLine(`
|
|
2793
|
+
${colors.dim}No executions in the last ${opts.period}.${RESET}
|
|
2794
|
+
`);
|
|
2795
|
+
return;
|
|
2796
|
+
}
|
|
2797
|
+
if (opts.json) {
|
|
2798
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2801
|
+
writeLine(`
|
|
2802
|
+
${bold}Cost Summary${RESET} (${summary.period})`);
|
|
2803
|
+
writeLine(`
|
|
2804
|
+
Total: ${bold}$${summary.total_cost.toFixed(2)}${RESET} across ${summary.total_runs} runs`);
|
|
2805
|
+
writeLine(` Tokens: ${summary.total_input_tokens.toLocaleString()} in / ${summary.total_output_tokens.toLocaleString()} out
|
|
2806
|
+
`);
|
|
2807
|
+
const squads = Object.entries(summary.by_squad).sort((a, b) => b[1].cost - a[1].cost);
|
|
2808
|
+
if (squads.length > 0) {
|
|
2809
|
+
writeLine(` ${colors.cyan}By Squad${RESET}`);
|
|
2810
|
+
for (const [name, data] of squads) {
|
|
2811
|
+
const bar = "\u2588".repeat(Math.max(1, Math.round(data.cost / (summary.total_cost || 1) * 20)));
|
|
2812
|
+
writeLine(` ${name.padEnd(20)} ${colors.dim}${bar}${RESET} $${data.cost.toFixed(2)} (${data.runs} runs, avg $${data.avg_cost.toFixed(3)})`);
|
|
2813
|
+
}
|
|
2814
|
+
writeLine();
|
|
2815
|
+
}
|
|
2816
|
+
const models = Object.entries(summary.by_model).sort((a, b) => b[1].cost - a[1].cost);
|
|
2817
|
+
if (models.length > 0) {
|
|
2818
|
+
writeLine(` ${colors.cyan}By Model${RESET}`);
|
|
2819
|
+
for (const [name, data] of models) {
|
|
2820
|
+
writeLine(` ${name.padEnd(30)} $${data.cost.toFixed(2)} (${data.runs} runs)`);
|
|
2821
|
+
}
|
|
2822
|
+
writeLine();
|
|
2823
|
+
}
|
|
2824
|
+
});
|
|
2825
|
+
obs.command("sync").description("Backfill JSONL execution data to Postgres (Tier 2)").option("--dry-run", "Show what would be synced without sending").action(async (opts) => {
|
|
2826
|
+
const { detectTier: detectTier2 } = await import("./tier-detect-YX2HPNNR.js");
|
|
2827
|
+
const { queryExecutions: queryExecutions2 } = await import("./observability-CL23L7LD.js");
|
|
2828
|
+
const info = await detectTier2();
|
|
2829
|
+
if (info.tier < 2 || !info.urls.api) {
|
|
2830
|
+
writeLine(`
|
|
2831
|
+
${colors.dim}Tier 2 not available. Run 'squads services up' first.${RESET}
|
|
2832
|
+
`);
|
|
2833
|
+
return;
|
|
2834
|
+
}
|
|
2835
|
+
const records = queryExecutions2({ limit: 1e4 });
|
|
2836
|
+
if (records.length === 0) {
|
|
2837
|
+
writeLine(`
|
|
2838
|
+
${colors.dim}No JSONL records to sync.${RESET}
|
|
2839
|
+
`);
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
writeLine(`
|
|
2843
|
+
${bold}Syncing ${records.length} records to Postgres...${RESET}
|
|
2844
|
+
`);
|
|
2845
|
+
let synced = 0;
|
|
2846
|
+
let skipped = 0;
|
|
2847
|
+
let errors = 0;
|
|
2848
|
+
for (const record of records) {
|
|
2849
|
+
if (opts.dryRun) {
|
|
2850
|
+
writeLine(` ${colors.dim}[dry-run] ${record.ts} ${record.squad}/${record.agent} $${record.cost_usd.toFixed(3)}${RESET}`);
|
|
2851
|
+
synced++;
|
|
2852
|
+
continue;
|
|
2853
|
+
}
|
|
2854
|
+
try {
|
|
2855
|
+
const res = await fetch(`${info.urls.api}/agent-executions`, {
|
|
2856
|
+
method: "POST",
|
|
2857
|
+
headers: { "Content-Type": "application/json" },
|
|
2858
|
+
body: JSON.stringify({
|
|
2859
|
+
execution_id: record.id,
|
|
2860
|
+
squad: record.squad,
|
|
2861
|
+
agent: record.agent,
|
|
2862
|
+
model: record.model,
|
|
2863
|
+
status: record.status,
|
|
2864
|
+
input_tokens: record.input_tokens,
|
|
2865
|
+
output_tokens: record.output_tokens,
|
|
2866
|
+
cache_read_tokens: record.cache_read_tokens,
|
|
2867
|
+
cache_write_tokens: record.cache_write_tokens,
|
|
2868
|
+
cost_usd: record.cost_usd,
|
|
2869
|
+
duration_seconds: Math.round(record.duration_ms / 1e3),
|
|
2870
|
+
error_message: record.error || null,
|
|
2871
|
+
metadata: { trigger: record.trigger, provider: record.provider }
|
|
2872
|
+
}),
|
|
2873
|
+
signal: AbortSignal.timeout(5e3)
|
|
2874
|
+
});
|
|
2875
|
+
if (res.ok) {
|
|
2876
|
+
synced++;
|
|
2877
|
+
} else if (res.status === 409) {
|
|
2878
|
+
skipped++;
|
|
2879
|
+
} else {
|
|
2880
|
+
errors++;
|
|
2881
|
+
}
|
|
2882
|
+
} catch {
|
|
2883
|
+
errors++;
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
writeLine(` ${colors.green}Synced: ${synced}${RESET} ${colors.dim}Skipped: ${skipped} Errors: ${errors}${RESET}
|
|
2887
|
+
`);
|
|
2888
|
+
});
|
|
2889
|
+
}
|
|
2890
|
+
|
|
2891
|
+
// src/commands/tier.ts
|
|
2892
|
+
import { existsSync as existsSync10, readdirSync as readdirSync6 } from "fs";
|
|
2893
|
+
function registerTierCommand(program2) {
|
|
2894
|
+
program2.command("tier").description("Show active infrastructure tier and available services").option("--json", "Output as JSON").action(async (opts) => {
|
|
2895
|
+
const info = await detectTier();
|
|
2896
|
+
if (opts.json) {
|
|
2897
|
+
console.log(JSON.stringify(info, null, 2));
|
|
2898
|
+
return;
|
|
2899
|
+
}
|
|
2900
|
+
writeLine();
|
|
2901
|
+
if (info.tier === 1) {
|
|
2902
|
+
writeLine(` ${bold}Tier 1${RESET} ${colors.dim}(file-based)${RESET}`);
|
|
2903
|
+
} else {
|
|
2904
|
+
writeLine(` ${bold}Tier 2${RESET} ${colors.green}(local services)${RESET}`);
|
|
2905
|
+
}
|
|
2906
|
+
writeLine();
|
|
2907
|
+
writeLine(` ${colors.cyan}Data${RESET}`);
|
|
2908
|
+
const executions = queryExecutions({ limit: 1e3 });
|
|
2909
|
+
writeLine(` Observability: ${executions.length} executions logged`);
|
|
2910
|
+
const squadsDir = findSquadsDir();
|
|
2911
|
+
if (squadsDir) {
|
|
2912
|
+
const squads = readdirSync6(squadsDir).filter((f) => {
|
|
2913
|
+
try {
|
|
2914
|
+
return existsSync10(`${squadsDir}/${f}/SQUAD.md`);
|
|
2915
|
+
} catch {
|
|
2916
|
+
return false;
|
|
2917
|
+
}
|
|
2918
|
+
});
|
|
2919
|
+
writeLine(` Squads: ${squads.length} defined`);
|
|
2920
|
+
}
|
|
2921
|
+
const memoryDir = findMemoryDir();
|
|
2922
|
+
if (memoryDir) {
|
|
2923
|
+
writeLine(` Memory: ${memoryDir}`);
|
|
2924
|
+
}
|
|
2925
|
+
const idpDir = findIdpDir();
|
|
2926
|
+
if (idpDir) {
|
|
2927
|
+
const catalog = loadCatalog();
|
|
2928
|
+
writeLine(` IDP: ${catalog.length} catalog entries`);
|
|
2929
|
+
} else {
|
|
2930
|
+
writeLine(` IDP: not configured`);
|
|
2931
|
+
}
|
|
2932
|
+
writeLine();
|
|
2933
|
+
writeLine(` ${colors.cyan}Services${RESET}`);
|
|
2934
|
+
const svc = info.services;
|
|
2935
|
+
const icon = (ok) => ok ? `${colors.green}up${RESET}` : `${colors.dim}\u2014${RESET}`;
|
|
2936
|
+
writeLine(` API: ${icon(svc.api)}${svc.api ? ` ${info.urls.api}` : ""}`);
|
|
2937
|
+
writeLine(` Bridge: ${icon(svc.bridge)}${svc.bridge ? ` ${info.urls.bridge}` : ""}`);
|
|
2938
|
+
writeLine(` Postgres: ${icon(svc.postgres)}`);
|
|
2939
|
+
writeLine(` Redis: ${icon(svc.redis)}`);
|
|
2940
|
+
writeLine();
|
|
2941
|
+
if (info.tier === 1) {
|
|
2942
|
+
writeLine(` ${colors.dim}Upgrade: run 'squads services up' for Tier 2${RESET}`);
|
|
2943
|
+
writeLine(` ${colors.dim}(smart triggers, Postgres, webhooks, budget enforcement)${RESET}`);
|
|
2944
|
+
} else {
|
|
2945
|
+
writeLine(` ${colors.dim}All local services healthy. Data syncs to Postgres.${RESET}`);
|
|
2946
|
+
}
|
|
2947
|
+
writeLine();
|
|
2948
|
+
});
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2951
|
+
// src/commands/services.ts
|
|
2952
|
+
import { execSync as execSync4 } from "child_process";
|
|
2953
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2954
|
+
import { join as join9 } from "path";
|
|
2955
|
+
function exec2(cmd, opts) {
|
|
2956
|
+
try {
|
|
2957
|
+
return execSync4(cmd, { encoding: "utf-8", timeout: 3e4, stdio: ["pipe", "pipe", "pipe"], ...opts }).trim();
|
|
2958
|
+
} catch {
|
|
2959
|
+
return null;
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
function findComposeFile() {
|
|
2963
|
+
const home = process.env.HOME || "";
|
|
2964
|
+
const candidates = [
|
|
2965
|
+
join9(home, "agents-squads", "engineering", "docker", "docker-compose.yml"),
|
|
2966
|
+
join9(home, "agents-squads", "engineering", "docker", "docker-compose.yaml"),
|
|
2967
|
+
join9(process.cwd(), "..", "engineering", "docker", "docker-compose.yml")
|
|
2968
|
+
];
|
|
2969
|
+
for (const candidate of candidates) {
|
|
2970
|
+
if (existsSync11(candidate)) return candidate;
|
|
2971
|
+
}
|
|
2972
|
+
return null;
|
|
2973
|
+
}
|
|
2974
|
+
function dockerAvailable() {
|
|
2975
|
+
return exec2("docker --version") !== null;
|
|
2976
|
+
}
|
|
2977
|
+
function dockerComposeAvailable() {
|
|
2978
|
+
return exec2("docker compose version") !== null;
|
|
2979
|
+
}
|
|
2980
|
+
function registerServicesCommands(program2) {
|
|
2981
|
+
const services = program2.command("services").description("Manage Tier 2 local services (Postgres, Redis, API, Bridge)");
|
|
2982
|
+
services.command("up").description("Start local services (Docker required)").option("--webhooks", "Also start ngrok tunnel for GitHub webhooks").option("--telemetry", "Also start OpenTelemetry collector").action(async (opts) => {
|
|
2983
|
+
if (!dockerAvailable()) {
|
|
2984
|
+
writeLine(`
|
|
2985
|
+
${colors.red}Docker not found.${RESET}`);
|
|
2986
|
+
writeLine(` ${colors.dim}Install Docker Desktop: https://www.docker.com/products/docker-desktop${RESET}
|
|
2987
|
+
`);
|
|
2988
|
+
return;
|
|
2989
|
+
}
|
|
2990
|
+
if (!dockerComposeAvailable()) {
|
|
2991
|
+
writeLine(`
|
|
2992
|
+
${colors.red}Docker Compose not found.${RESET}
|
|
2993
|
+
`);
|
|
2994
|
+
return;
|
|
2995
|
+
}
|
|
2996
|
+
const composeFile = findComposeFile();
|
|
2997
|
+
if (!composeFile) {
|
|
2998
|
+
writeLine(`
|
|
2999
|
+
${colors.red}docker-compose.yml not found.${RESET}`);
|
|
3000
|
+
writeLine(` ${colors.dim}Expected at: ../engineering/docker/docker-compose.yml (sibling repo)${RESET}
|
|
3001
|
+
`);
|
|
3002
|
+
return;
|
|
3003
|
+
}
|
|
3004
|
+
const composeDir = join9(composeFile, "..");
|
|
3005
|
+
writeLine(`
|
|
3006
|
+
${bold}Starting Tier 2 services...${RESET}
|
|
3007
|
+
`);
|
|
3008
|
+
let profileArgs = "";
|
|
3009
|
+
if (opts.webhooks) profileArgs += " --profile webhooks";
|
|
3010
|
+
if (opts.telemetry) profileArgs += " --profile telemetry";
|
|
3011
|
+
try {
|
|
3012
|
+
writeLine(` ${colors.dim}docker compose up -d${profileArgs}${RESET}`);
|
|
3013
|
+
execSync4(`docker compose${profileArgs} up -d`, {
|
|
3014
|
+
cwd: composeDir,
|
|
3015
|
+
stdio: "inherit",
|
|
3016
|
+
timeout: 12e4
|
|
3017
|
+
});
|
|
3018
|
+
writeLine();
|
|
3019
|
+
writeLine(` ${colors.green}Services started.${RESET} Waiting for health checks...`);
|
|
3020
|
+
let healthy = false;
|
|
3021
|
+
for (let i = 0; i < 15; i++) {
|
|
3022
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
3023
|
+
const info = await detectTier();
|
|
3024
|
+
if (info.services.api) {
|
|
3025
|
+
healthy = true;
|
|
3026
|
+
break;
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
if (healthy) {
|
|
3030
|
+
writeLine(` ${colors.green}Tier 2 active.${RESET} All services healthy.
|
|
3031
|
+
`);
|
|
3032
|
+
writeLine(` ${colors.dim}API: http://localhost:8090${RESET}`);
|
|
3033
|
+
writeLine(` ${colors.dim}Bridge: http://localhost:8088${RESET}`);
|
|
3034
|
+
writeLine(` ${colors.dim}Postgres: localhost:5432${RESET}`);
|
|
3035
|
+
writeLine(` ${colors.dim}Redis: localhost:6379${RESET}`);
|
|
3036
|
+
} else {
|
|
3037
|
+
writeLine(` ${colors.yellow}Services started but API not healthy yet. Run 'squads services status' to check.${RESET}`);
|
|
3038
|
+
}
|
|
3039
|
+
writeLine();
|
|
3040
|
+
} catch (e) {
|
|
3041
|
+
writeLine(`
|
|
3042
|
+
${colors.red}Failed to start services: ${e instanceof Error ? e.message : String(e)}${RESET}
|
|
3043
|
+
`);
|
|
3044
|
+
}
|
|
3045
|
+
});
|
|
3046
|
+
services.command("down").description("Stop local services").action(() => {
|
|
3047
|
+
const composeFile = findComposeFile();
|
|
3048
|
+
if (!composeFile) {
|
|
3049
|
+
writeLine(`
|
|
3050
|
+
${colors.dim}No docker-compose.yml found. Nothing to stop.${RESET}
|
|
3051
|
+
`);
|
|
3052
|
+
return;
|
|
3053
|
+
}
|
|
3054
|
+
const composeDir = join9(composeFile, "..");
|
|
3055
|
+
writeLine(`
|
|
3056
|
+
${bold}Stopping Tier 2 services...${RESET}
|
|
3057
|
+
`);
|
|
3058
|
+
try {
|
|
3059
|
+
execSync4("docker compose down", {
|
|
3060
|
+
cwd: composeDir,
|
|
3061
|
+
stdio: "inherit",
|
|
3062
|
+
timeout: 6e4
|
|
3063
|
+
});
|
|
3064
|
+
writeLine(`
|
|
3065
|
+
${colors.dim}Services stopped. Falling back to Tier 1 (file-based).${RESET}
|
|
3066
|
+
`);
|
|
3067
|
+
} catch (e) {
|
|
3068
|
+
writeLine(`
|
|
3069
|
+
${colors.red}Failed to stop services: ${e instanceof Error ? e.message : String(e)}${RESET}
|
|
3070
|
+
`);
|
|
3071
|
+
}
|
|
3072
|
+
});
|
|
3073
|
+
services.command("status").description("Show running services and health").action(async () => {
|
|
3074
|
+
const info = await detectTier();
|
|
3075
|
+
writeLine();
|
|
3076
|
+
writeLine(` ${bold}Services${RESET} (Tier ${info.tier})
|
|
3077
|
+
`);
|
|
3078
|
+
const containers = exec2('docker ps --filter name=squads --format "{{.Names}}\\t{{.Status}}\\t{{.Ports}}"');
|
|
3079
|
+
if (!containers) {
|
|
3080
|
+
writeLine(` ${colors.dim}No Docker containers running.${RESET}
|
|
3081
|
+
`);
|
|
3082
|
+
return;
|
|
3083
|
+
}
|
|
3084
|
+
for (const line of containers.split("\n").filter(Boolean)) {
|
|
3085
|
+
const [name, status, ports] = line.split(" ");
|
|
3086
|
+
const healthy = status?.includes("healthy") || status?.includes("Up");
|
|
3087
|
+
const icon = healthy ? `${colors.green}up${RESET}` : `${colors.red}down${RESET}`;
|
|
3088
|
+
const portStr = ports ? ` ${colors.dim}${ports.split(",")[0]}${RESET}` : "";
|
|
3089
|
+
writeLine(` ${icon} ${bold}${name}${RESET}${portStr}`);
|
|
3090
|
+
}
|
|
3091
|
+
writeLine();
|
|
3092
|
+
const jobCount = exec2("docker exec squads-postgres psql -U squads -d squads -t -c 'SELECT count(*) FROM procrastinate_jobs;'");
|
|
3093
|
+
const execCount = exec2("docker exec squads-postgres psql -U squads -d squads -t -c 'SELECT count(*) FROM agent_executions;'");
|
|
3094
|
+
if (jobCount || execCount) {
|
|
3095
|
+
writeLine(` ${colors.cyan}Database${RESET}`);
|
|
3096
|
+
if (jobCount) writeLine(` Procrastinate jobs: ${jobCount.trim()}`);
|
|
3097
|
+
if (execCount) writeLine(` Agent executions: ${execCount.trim()}`);
|
|
3098
|
+
writeLine();
|
|
3099
|
+
}
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
|
|
2173
3103
|
// src/cli.ts
|
|
2174
3104
|
if (!process.stdout.isTTY) {
|
|
2175
3105
|
chalk6.level = 0;
|
|
@@ -2187,12 +3117,12 @@ process.stderr.on("error", (err) => {
|
|
|
2187
3117
|
throw err;
|
|
2188
3118
|
});
|
|
2189
3119
|
var envPaths = [
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
3120
|
+
join10(process.cwd(), ".env"),
|
|
3121
|
+
join10(process.cwd(), "..", "hq", ".env"),
|
|
3122
|
+
join10(homedir3(), "agents-squads", "hq", ".env")
|
|
2193
3123
|
];
|
|
2194
3124
|
for (const envPath of envPaths) {
|
|
2195
|
-
if (
|
|
3125
|
+
if (existsSync12(envPath)) {
|
|
2196
3126
|
config({ path: envPath, quiet: true });
|
|
2197
3127
|
break;
|
|
2198
3128
|
}
|
|
@@ -2318,11 +3248,11 @@ program.name("squads").description("Your AI workforce \u2014 business operating
|
|
|
2318
3248
|
writeLine(` ${colors3.dim}Run \`squads update\` to install${RESET2}`);
|
|
2319
3249
|
writeLine();
|
|
2320
3250
|
}
|
|
2321
|
-
const { statusCommand } = await import("./status-
|
|
3251
|
+
const { statusCommand } = await import("./status-PFFB2NV6.js");
|
|
2322
3252
|
await statusCommand(void 0, {});
|
|
2323
3253
|
});
|
|
2324
3254
|
program.command("init").description("Plant the seed: create manager agent, CLI skill, and starter squads").option("-p, --provider <provider>", "LLM provider (claude, gemini, openai, ollama, none)").option("--pack <packs...>", "Additional squad packs to install (engineering, marketing, operations, all)").option("--skip-infra", "Skip infrastructure setup prompt").option("--force", "Skip requirement checks (for CI/testing)").option("-y, --yes", "Accept all defaults (non-interactive mode)").option("-q, --quick", "Quick init - create files only, skip interactive prompts").action(async (options) => {
|
|
2325
|
-
const { initCommand } = await import("./init-
|
|
3255
|
+
const { initCommand } = await import("./init-BQSCG57S.js");
|
|
2326
3256
|
return initCommand(options);
|
|
2327
3257
|
});
|
|
2328
3258
|
program.command("add <name>").description("Add a new squad with directory structure and starter files").option("-d, --description <text>", "Squad mission (one sentence)").option("-g, --goal <text>", "First goal for the squad").option("-m, --model <model>", "Default model (default: sonnet)").option("-f, --force", "Overwrite existing squad").option("-y, --yes", "Accept all defaults (non-interactive)").option("-r, --repo", "Create a GitHub repository for the squad").option("-o, --org <org>", "GitHub organization for --repo (default: detected from git remote)").addHelpText("after", `
|
|
@@ -2337,7 +3267,7 @@ Examples:
|
|
|
2337
3267
|
return createCommand(name, options);
|
|
2338
3268
|
});
|
|
2339
3269
|
program.command("create <name>", { hidden: true }).description("[renamed]").action(removedCommand("create", "Renamed to: squads add <name>"));
|
|
2340
|
-
program.command("run [target]").description("Run a squad, agent, or autopilot (no target = autopilot mode)").option("-v, --verbose", "Verbose output").option("-d, --dry-run", "Show what would be run without executing").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("-b, --background", "Run agent in background (detached process)").option("-w, --watch", "Run in background but tail the log for visibility").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)").option("--provider <provider>", "LLM provider: anthropic, google, openai, mistral, xai, aider, ollama").option("--model <model>", "Model to use (e.g., opus, sonnet, haiku, gemini-2.5-flash, gpt-4o)").option("--trigger <type>", "Trigger source: manual, scheduled, event, smart (default: manual)").option("--cloud", "Dispatch execution to cloud worker via API (requires squads login)").option("--task <directive>", "Founder directive for conversation mode (replaces lead briefing)").option("--max-turns <n>", "Max conversation turns (default: 20)", "20").option("--cost-ceiling <usd>", "Cost ceiling in USD (default: 25)", "25").option("--no-verify", "Skip post-execution verification (Ralph loop)").option("--execute", "Explicitly execute agents (default for run <target>)").option("-j, --json", "Output as JSON").option("-i, --interval <minutes>", "Autopilot: minutes between cycles", "30").option("--max-parallel <count>", "Autopilot: max parallel squad loops", "2").option("--budget <usd>", "Autopilot: daily budget cap ($)", "0").option("--once", "Autopilot: run one cycle then exit").option("--phased", "Autopilot: use dependency-based phase ordering (from SQUAD.md depends_on)").option("--no-eval", "Skip post-run COO evaluation").addHelpText("after", `
|
|
3270
|
+
program.command("run [target]").description("Run a squad, agent, or autopilot (no target = autopilot mode)").option("-v, --verbose", "Verbose output").option("-d, --dry-run", "Show what would be run without executing").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("-b, --background", "Run agent in background (detached process)").option("-w, --watch", "Run in background but tail the log for visibility").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)").option("--provider <provider>", "LLM provider: anthropic, google, openai, mistral, xai, aider, ollama").option("--model <model>", "Model to use (e.g., opus, sonnet, haiku, gemini-2.5-flash, gpt-4o)").option("--trigger <type>", "Trigger source: manual, scheduled, event, smart (default: manual)").option("--cloud", "Dispatch execution to cloud worker via API (requires squads login)").option("--task <directive>", "Founder directive for conversation mode (replaces lead briefing)").option("--max-turns <n>", "Max conversation turns (default: 20)", "20").option("--cost-ceiling <usd>", "Cost ceiling in USD (default: 25)", "25").option("--no-verify", "Skip post-execution verification (Ralph loop)").option("--execute", "Explicitly execute agents (default for run <target>)").option("-j, --json", "Output as JSON").option("-i, --interval <minutes>", "Autopilot: minutes between cycles", "30").option("--max-parallel <count>", "Autopilot: max parallel squad loops", "2").option("--budget <usd>", "Autopilot: daily budget cap ($)", "0").option("--once", "Autopilot: run one cycle then exit").option("--phased", "Autopilot: use dependency-based phase ordering (from SQUAD.md depends_on)").option("--no-eval", "Skip post-run COO evaluation").option("--org", "Run all squads as a coordinated org cycle (scan \u2192 plan \u2192 execute \u2192 report)").addHelpText("after", `
|
|
2341
3271
|
Examples:
|
|
2342
3272
|
$ squads run engineering Run squad conversation (lead \u2192 scan \u2192 work \u2192 review)
|
|
2343
3273
|
$ squads run engineering --task "fix CI" Conversation with founder directive
|
|
@@ -2354,37 +3284,40 @@ Examples:
|
|
|
2354
3284
|
$ squads run --once --dry-run Preview one autopilot cycle
|
|
2355
3285
|
$ squads run -i 15 --budget 50 Autopilot: 15min cycles, $50/day cap
|
|
2356
3286
|
`).action(async (target, options) => {
|
|
2357
|
-
const { runCommand } = await import("./run-
|
|
3287
|
+
const { runCommand } = await import("./run-DOY5SGF3.js");
|
|
2358
3288
|
return runCommand(target || null, { ...options, timeout: parseInt(options.timeout, 10) });
|
|
2359
3289
|
});
|
|
2360
|
-
program.command("list"
|
|
3290
|
+
program.command("list").description("List squads (alias for: squads status)").action(async () => {
|
|
3291
|
+
const { statusCommand } = await import("./status-PFFB2NV6.js");
|
|
3292
|
+
return statusCommand();
|
|
3293
|
+
});
|
|
2361
3294
|
registerOrchestrateCommand(program);
|
|
2362
3295
|
var env = program.command("env").description("View squad execution environment (MCP, skills, model, budget)").action(() => {
|
|
2363
3296
|
env.outputHelp();
|
|
2364
3297
|
});
|
|
2365
3298
|
env.command("show <squad>").description("Show execution environment for a squad").option("--json", "Output as JSON").action(async (squad, options) => {
|
|
2366
|
-
const { contextShowCommand } = await import("./context-
|
|
3299
|
+
const { contextShowCommand } = await import("./context-PYTO2UQG.js");
|
|
2367
3300
|
return contextShowCommand(squad, options);
|
|
2368
3301
|
});
|
|
2369
3302
|
env.command("prompt <squad>").description("Output ready-to-use prompt for Claude Code execution").option("-a, --agent <agent>", "Agent to execute (required)").option("--json", "Output as JSON").action(async (squad, options) => {
|
|
2370
|
-
const { contextPromptCommand } = await import("./context-
|
|
3303
|
+
const { contextPromptCommand } = await import("./context-PYTO2UQG.js");
|
|
2371
3304
|
return contextPromptCommand(squad, options);
|
|
2372
3305
|
});
|
|
2373
|
-
var
|
|
2374
|
-
|
|
2375
|
-
const { execListCommand } = await import("./exec-
|
|
3306
|
+
var exec3 = program.command("exec").description("View execution history and statistics");
|
|
3307
|
+
exec3.command("list").description("List recent executions").option("-s, --squad <squad>", "Filter by squad").option("-a, --agent <agent>", "Filter by agent").option("--status <status>", "Filter by status (running, completed, failed)").option("-n, --limit <n>", "Number of executions to show", "20").option("--json", "Output as JSON").action(async (options) => {
|
|
3308
|
+
const { execListCommand } = await import("./exec-DYLI4TXY.js");
|
|
2376
3309
|
return execListCommand({ ...options, limit: parseInt(options.limit, 10) });
|
|
2377
3310
|
});
|
|
2378
|
-
|
|
2379
|
-
const { execShowCommand } = await import("./exec-
|
|
3311
|
+
exec3.command("show <id>").description("Show execution details").option("--json", "Output as JSON").action(async (id, options) => {
|
|
3312
|
+
const { execShowCommand } = await import("./exec-DYLI4TXY.js");
|
|
2380
3313
|
return execShowCommand(id, options);
|
|
2381
3314
|
});
|
|
2382
|
-
|
|
2383
|
-
const { execStatsCommand } = await import("./exec-
|
|
3315
|
+
exec3.command("stats").description("Show execution statistics").option("-s, --squad <squad>", "Filter by squad").option("--json", "Output as JSON").action(async (options) => {
|
|
3316
|
+
const { execStatsCommand } = await import("./exec-DYLI4TXY.js");
|
|
2384
3317
|
return execStatsCommand(options);
|
|
2385
3318
|
});
|
|
2386
|
-
|
|
2387
|
-
const { execListCommand } = await import("./exec-
|
|
3319
|
+
exec3.action(async (options) => {
|
|
3320
|
+
const { execListCommand } = await import("./exec-DYLI4TXY.js");
|
|
2388
3321
|
return execListCommand(options);
|
|
2389
3322
|
});
|
|
2390
3323
|
program.command("dashboard [name]").alias("dash").description('Show dashboards. Use "squads dash" for overview, "squads dash <name>" for specific dashboard, "squads dash --list" to see all.').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)").option("-l, --list", "List available declarative dashboards").option("--view <view>", "Render specific view from dashboard").option("-j, --json", "Output as JSON").action(async (name, options) => {
|
|
@@ -2403,58 +3336,58 @@ program.command("dashboard [name]").alias("dash").description('Show dashboards.
|
|
|
2403
3336
|
writeLine(` Dashboard "${name}" not found. Showing default dashboard.
|
|
2404
3337
|
`);
|
|
2405
3338
|
}
|
|
2406
|
-
const { dashboardCommand } = await import("./dashboard-
|
|
3339
|
+
const { dashboardCommand } = await import("./dashboard-HQIEHTZC.js");
|
|
2407
3340
|
dashboardCommand({ ...options, fast: !options.full });
|
|
2408
3341
|
});
|
|
2409
3342
|
program.command("status [squad]").description("Show squad status and state").option("-v, --verbose", "Show detailed status").option("-j, --json", "Output as JSON").action(async (squad, options) => {
|
|
2410
|
-
const { statusCommand } = await import("./status-
|
|
3343
|
+
const { statusCommand } = await import("./status-PFFB2NV6.js");
|
|
2411
3344
|
return statusCommand(squad, options);
|
|
2412
3345
|
});
|
|
2413
3346
|
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(async (options) => {
|
|
2414
|
-
const { contextFeedCommand } = await import("./context-feed-
|
|
3347
|
+
const { contextFeedCommand } = await import("./context-feed-TLVZZ24S.js");
|
|
2415
3348
|
return contextFeedCommand(options);
|
|
2416
3349
|
});
|
|
2417
3350
|
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(async (options) => {
|
|
2418
|
-
const { costCommand } = await import("./cost-
|
|
3351
|
+
const { costCommand } = await import("./cost-OALPURUQ.js");
|
|
2419
3352
|
return costCommand(options);
|
|
2420
3353
|
});
|
|
2421
3354
|
program.command("budget").description("Check budget status for a squad").argument("<squad>", "Squad to check").option("--json", "Output as JSON").action(async (squad, options) => {
|
|
2422
|
-
const { budgetCheckCommand } = await import("./cost-
|
|
3355
|
+
const { budgetCheckCommand } = await import("./cost-OALPURUQ.js");
|
|
2423
3356
|
return budgetCheckCommand(squad, options);
|
|
2424
3357
|
});
|
|
2425
3358
|
program.command("health").description("Quick health check for all infrastructure services").option("-v, --verbose", "Show optional services").action(async (options) => {
|
|
2426
|
-
const { healthCommand } = await import("./health-
|
|
3359
|
+
const { healthCommand } = await import("./health-ZF3HSA4W.js");
|
|
2427
3360
|
return healthCommand(options);
|
|
2428
3361
|
});
|
|
2429
3362
|
program.command("doctor").description("Check local tools, auth, and project readiness").option("-v, --verbose", "Show install hints and optional tools").action(async (options) => {
|
|
2430
|
-
const { doctorCommand } = await import("./doctor-
|
|
3363
|
+
const { doctorCommand } = await import("./doctor-TWHMR23W.js");
|
|
2431
3364
|
return doctorCommand(options);
|
|
2432
3365
|
});
|
|
2433
3366
|
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(async (options) => {
|
|
2434
|
-
const { historyCommand } = await import("./history-
|
|
3367
|
+
const { historyCommand } = await import("./history-WP6R5BNG.js");
|
|
2435
3368
|
return historyCommand(options);
|
|
2436
3369
|
});
|
|
2437
3370
|
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(async (squad, options) => {
|
|
2438
|
-
const { resultsCommand } = await import("./results-
|
|
3371
|
+
const { resultsCommand } = await import("./results-6TH33HPN.js");
|
|
2439
3372
|
return resultsCommand({ ...options, squad });
|
|
2440
3373
|
});
|
|
2441
3374
|
var goal = program.command("goal").description("Manage squad goals").action(() => {
|
|
2442
3375
|
goal.outputHelp();
|
|
2443
3376
|
});
|
|
2444
3377
|
goal.command("set <squad> <description>").description("Set a goal for a squad").option("-m, --metric <metrics...>", "Metrics to track").action(async (squad, description, options) => {
|
|
2445
|
-
const { goalSetCommand } = await import("./goal-
|
|
3378
|
+
const { goalSetCommand } = await import("./goal-XUNV3CKV.js");
|
|
2446
3379
|
return goalSetCommand(squad, description, options);
|
|
2447
3380
|
});
|
|
2448
3381
|
goal.command("list [squad]").description("List goals for squad(s)").option("-a, --all", "Show completed goals too").option("-j, --json", "Output as JSON").action(async (squad, options) => {
|
|
2449
|
-
const { goalListCommand } = await import("./goal-
|
|
3382
|
+
const { goalListCommand } = await import("./goal-XUNV3CKV.js");
|
|
2450
3383
|
return goalListCommand(squad, options);
|
|
2451
3384
|
});
|
|
2452
3385
|
goal.command("complete <squad> <index>").description("Mark a goal as completed").action(async (squad, index) => {
|
|
2453
|
-
const { goalCompleteCommand } = await import("./goal-
|
|
3386
|
+
const { goalCompleteCommand } = await import("./goal-XUNV3CKV.js");
|
|
2454
3387
|
return goalCompleteCommand(squad, index);
|
|
2455
3388
|
});
|
|
2456
3389
|
goal.command("progress <squad> <index> <progress>").description("Update goal progress").action(async (squad, index, progress2) => {
|
|
2457
|
-
const { goalProgressCommand } = await import("./goal-
|
|
3390
|
+
const { goalProgressCommand } = await import("./goal-XUNV3CKV.js");
|
|
2458
3391
|
return goalProgressCommand(squad, index, progress2);
|
|
2459
3392
|
});
|
|
2460
3393
|
var kpi = program.command("kpi").description("Track and analyze squad KPIs (defined in SQUAD.md frontmatter)").addHelpText("after", `
|
|
@@ -2468,60 +3401,60 @@ Examples:
|
|
|
2468
3401
|
kpi.outputHelp();
|
|
2469
3402
|
});
|
|
2470
3403
|
kpi.command("list").description("List all KPIs across squads").option("-j, --json", "Output as JSON").action(async (options) => {
|
|
2471
|
-
const { kpiListCommand } = await import("./kpi-
|
|
3404
|
+
const { kpiListCommand } = await import("./kpi-VBGDO4GI.js");
|
|
2472
3405
|
return kpiListCommand(options);
|
|
2473
3406
|
});
|
|
2474
3407
|
kpi.command("show <squad>").description("Show KPI status for a squad").option("-j, --json", "Output as JSON").action(async (squad, options) => {
|
|
2475
|
-
const { kpiShowCommand } = await import("./kpi-
|
|
3408
|
+
const { kpiShowCommand } = await import("./kpi-VBGDO4GI.js");
|
|
2476
3409
|
return kpiShowCommand(squad, options);
|
|
2477
3410
|
});
|
|
2478
3411
|
kpi.command("record <squad> <kpi> <value>").description("Record a KPI value").option("-n, --note <note>", "Add a note to the record").option("-j, --json", "Output as JSON").action(async (squad, kpi2, value, options) => {
|
|
2479
|
-
const { kpiRecordCommand } = await import("./kpi-
|
|
3412
|
+
const { kpiRecordCommand } = await import("./kpi-VBGDO4GI.js");
|
|
2480
3413
|
return kpiRecordCommand(squad, kpi2, value, options);
|
|
2481
3414
|
});
|
|
2482
3415
|
kpi.command("trend <squad> <kpi>").description("Show KPI trend over time").option("-p, --periods <n>", "Number of periods to show", "7").option("-j, --json", "Output as JSON").action(async (squad, kpi2, options) => {
|
|
2483
|
-
const { kpiTrendCommand } = await import("./kpi-
|
|
3416
|
+
const { kpiTrendCommand } = await import("./kpi-VBGDO4GI.js");
|
|
2484
3417
|
return kpiTrendCommand(squad, kpi2, options);
|
|
2485
3418
|
});
|
|
2486
3419
|
kpi.command("insights [squad]").description("Generate insights from KPI data").option("-j, --json", "Output as JSON").action(async (squad, options) => {
|
|
2487
|
-
const { kpiInsightsCommand } = await import("./kpi-
|
|
3420
|
+
const { kpiInsightsCommand } = await import("./kpi-VBGDO4GI.js");
|
|
2488
3421
|
return kpiInsightsCommand(squad, options);
|
|
2489
3422
|
});
|
|
2490
3423
|
var progress = program.command("progress").description("Track active and completed agent tasks").option("-v, --verbose", "Show more activity").action(async (options) => {
|
|
2491
|
-
const { progressCommand } = await import("./progress-
|
|
3424
|
+
const { progressCommand } = await import("./progress-P2EIZBKP.js");
|
|
2492
3425
|
return progressCommand(options);
|
|
2493
3426
|
});
|
|
2494
3427
|
progress.command("start <squad> <description>").description("Register a new active task").action(async (squad, description) => {
|
|
2495
|
-
const { progressStartCommand } = await import("./progress-
|
|
3428
|
+
const { progressStartCommand } = await import("./progress-P2EIZBKP.js");
|
|
2496
3429
|
return progressStartCommand(squad, description);
|
|
2497
3430
|
});
|
|
2498
3431
|
progress.command("complete <taskId>").description("Mark a task as completed").option("-f, --failed", "Mark as failed instead").action(async (taskId, options) => {
|
|
2499
|
-
const { progressCompleteCommand } = await import("./progress-
|
|
3432
|
+
const { progressCompleteCommand } = await import("./progress-P2EIZBKP.js");
|
|
2500
3433
|
return progressCompleteCommand(taskId, options);
|
|
2501
3434
|
});
|
|
2502
3435
|
var feedback = program.command("feedback").description("Record and view execution feedback").action(() => {
|
|
2503
3436
|
feedback.outputHelp();
|
|
2504
3437
|
});
|
|
2505
3438
|
feedback.command("add <squad> <rating> <feedback>").description("Add feedback for last execution (rating 1-5)").option("-l, --learning <learnings...>", "Learnings to extract").action(async (squad, rating, feedbackText, options) => {
|
|
2506
|
-
const { feedbackAddCommand } = await import("./feedback-
|
|
3439
|
+
const { feedbackAddCommand } = await import("./feedback-5AEACUX6.js");
|
|
2507
3440
|
return feedbackAddCommand(squad, rating, feedbackText, options);
|
|
2508
3441
|
});
|
|
2509
3442
|
feedback.command("show <squad>").description("Show feedback history").option("-n, --limit <n>", "Number of entries to show", "5").action(async (squad, options) => {
|
|
2510
|
-
const { feedbackShowCommand } = await import("./feedback-
|
|
3443
|
+
const { feedbackShowCommand } = await import("./feedback-5AEACUX6.js");
|
|
2511
3444
|
return feedbackShowCommand(squad, options);
|
|
2512
3445
|
});
|
|
2513
3446
|
feedback.command("stats").description("Show feedback summary across all squads").action(async () => {
|
|
2514
|
-
const { feedbackStatsCommand } = await import("./feedback-
|
|
3447
|
+
const { feedbackStatsCommand } = await import("./feedback-5AEACUX6.js");
|
|
2515
3448
|
return feedbackStatsCommand();
|
|
2516
3449
|
});
|
|
2517
3450
|
program.command("autonomy").description("Show autonomy score and confidence metrics").option("-s, --squad <squad>", "Filter by squad").option("-p, --period <period>", "Time period: today, week, month", "today").option("-j, --json", "Output as JSON").action(async (options) => {
|
|
2518
|
-
const { autonomyCommand } = await import("./autonomy-
|
|
3451
|
+
const { autonomyCommand } = await import("./autonomy-GARI6J2J.js");
|
|
2519
3452
|
return autonomyCommand({ squad: options.squad, period: options.period, json: options.json });
|
|
2520
3453
|
});
|
|
2521
3454
|
program.command("autopilot").alias("daemon").description('[deprecated] Use "squads run" instead \u2014 autopilot mode when no target given').option("-i, --interval <minutes>", "Minutes between cycles", "30").option("-p, --parallel <count>", "Max parallel agent runs", "2").option("-b, --budget <dollars>", "Max daily spend in dollars (0 = unlimited/subscription)", "0").option("--once", "Run one cycle and exit").option("--dry-run", "Show what would run without dispatching").option("-v, --verbose", "Show detailed scoring").action(async (options) => {
|
|
2522
3455
|
const colors3 = colors;
|
|
2523
3456
|
writeLine(` ${colors3.yellow}Note: "squads autopilot" is now "squads run" (no arguments)${RESET}`);
|
|
2524
|
-
const { runCommand } = await import("./run-
|
|
3457
|
+
const { runCommand } = await import("./run-DOY5SGF3.js");
|
|
2525
3458
|
return runCommand(null, { interval: parseInt(options.interval || "30", 10), ...options });
|
|
2526
3459
|
});
|
|
2527
3460
|
program.command("stats [squad]").description("Show agent outcome scorecards: merge rate, waste, cost per outcome").option("-p, --period <period>", "Time period: 7d or 30d", "7d").option("-j, --json", "Output as JSON").action(async (squad, options) => {
|
|
@@ -2539,27 +3472,27 @@ Examples:
|
|
|
2539
3472
|
memory.outputHelp();
|
|
2540
3473
|
});
|
|
2541
3474
|
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(async (query, options) => {
|
|
2542
|
-
const { memoryQueryCommand } = await import("./memory-
|
|
3475
|
+
const { memoryQueryCommand } = await import("./memory-33HYD6AN.js");
|
|
2543
3476
|
return memoryQueryCommand(query, options);
|
|
2544
3477
|
});
|
|
2545
3478
|
memory.command("read <squad>").alias("show").description("Show memory for a squad").action(async (squad, options) => {
|
|
2546
|
-
const { memoryShowCommand } = await import("./memory-
|
|
3479
|
+
const { memoryShowCommand } = await import("./memory-33HYD6AN.js");
|
|
2547
3480
|
return memoryShowCommand(squad, options);
|
|
2548
3481
|
});
|
|
2549
3482
|
memory.command("write <squad> <content>").alias("update").description("Add to squad memory").option("-a, --agent <agent>", "Specific agent (default: squad-lead)").option("-t, --type <type>", "Memory type: state, learnings, feedback", "learnings").action(async (squad, content, options) => {
|
|
2550
|
-
const { memoryUpdateCommand } = await import("./memory-
|
|
3483
|
+
const { memoryUpdateCommand } = await import("./memory-33HYD6AN.js");
|
|
2551
3484
|
return memoryUpdateCommand(squad, content, options);
|
|
2552
3485
|
});
|
|
2553
3486
|
memory.command("list").description("List all memory entries").action(async () => {
|
|
2554
|
-
const { memoryListCommand } = await import("./memory-
|
|
3487
|
+
const { memoryListCommand } = await import("./memory-33HYD6AN.js");
|
|
2555
3488
|
return memoryListCommand();
|
|
2556
3489
|
});
|
|
2557
3490
|
memory.command("sync").description("Sync memory from git: pull remote changes, process commits, optionally push to Postgres").option("-v, --verbose", "Show detailed commit info").option("-p, --push", "Push local memory changes to remote after sync").option("--no-pull", "Skip pulling from remote").option("--postgres", "Sync cycle data (goals, feedback, KPIs, learnings) to Postgres").option("--dimensions", "Sync squad/agent definitions to Postgres dim tables").option("--learnings", "Sync learnings.md files to Postgres").option("--auto-learn", "Auto-generate learnings from session commits").action(async (options) => {
|
|
2558
|
-
const { syncCommand } = await import("./sync-
|
|
3491
|
+
const { syncCommand } = await import("./sync-FR6LQJ4C.js");
|
|
2559
3492
|
return syncCommand({ verbose: options.verbose, push: options.push, pull: options.pull, postgres: options.postgres, dimensions: options.dimensions, learnings: options.learnings, autoLearn: options.autoLearn });
|
|
2560
3493
|
});
|
|
2561
3494
|
memory.command("search <query>").description("Search stored conversations (requires authentication: squads login)").option("-l, --limit <limit>", "Number of results", "10").option("-r, --role <role>", "Filter by role: user, assistant, thinking").option("-i, --importance <importance>", "Filter by importance: low, normal, high").action(async (query, opts) => {
|
|
2562
|
-
const { memorySearchCommand } = await import("./memory-
|
|
3495
|
+
const { memorySearchCommand } = await import("./memory-33HYD6AN.js");
|
|
2563
3496
|
return memorySearchCommand(query, {
|
|
2564
3497
|
limit: parseInt(opts.limit, 10),
|
|
2565
3498
|
role: opts.role,
|
|
@@ -2567,7 +3500,7 @@ memory.command("search <query>").description("Search stored conversations (requi
|
|
|
2567
3500
|
});
|
|
2568
3501
|
});
|
|
2569
3502
|
memory.command("extract").description("Extract memories from recent conversations into Engram").option("-s, --session <session>", "Extract specific session only").option("-h, --hours <hours>", "Look back period in hours", "24").option("-d, --dry-run", "Preview without sending to Engram").action(async (opts) => {
|
|
2570
|
-
const { memoryExtractCommand } = await import("./memory-
|
|
3503
|
+
const { memoryExtractCommand } = await import("./memory-33HYD6AN.js");
|
|
2571
3504
|
return memoryExtractCommand({
|
|
2572
3505
|
session: opts.session,
|
|
2573
3506
|
hours: parseInt(opts.hours, 10),
|
|
@@ -2575,20 +3508,20 @@ memory.command("extract").description("Extract memories from recent conversation
|
|
|
2575
3508
|
});
|
|
2576
3509
|
});
|
|
2577
3510
|
program.command("learn <insight>").description("Capture a learning for future sessions").option("-s, --squad <squad>", "Squad to associate learning with").option("-c, --category <category>", "Category: success, failure, pattern, tip").option("-t, --tags <tags>", "Comma-separated tags").option("--context <context>", "Additional context").action(async (insight, options) => {
|
|
2578
|
-
const { learnCommand } = await import("./learn-
|
|
3511
|
+
const { learnCommand } = await import("./learn-C4B2PQ5J.js");
|
|
2579
3512
|
return learnCommand(insight, options);
|
|
2580
3513
|
});
|
|
2581
3514
|
var learn = program.command("learnings").description("View and search learnings");
|
|
2582
3515
|
learn.command("show <squad>").description("Show learnings for a squad").option("-n, --limit <n>", "Number to show", "10").option("-c, --category <category>", "Filter by category").option("--tag <tag>", "Filter by tag").action(async (squad, options) => {
|
|
2583
|
-
const { learnShowCommand } = await import("./learn-
|
|
3516
|
+
const { learnShowCommand } = await import("./learn-C4B2PQ5J.js");
|
|
2584
3517
|
return learnShowCommand(squad, options);
|
|
2585
3518
|
});
|
|
2586
3519
|
learn.command("search <query>").description("Search learnings across all squads").option("-n, --limit <n>", "Max results", "10").action(async (query, options) => {
|
|
2587
|
-
const { learnSearchCommand } = await import("./learn-
|
|
3520
|
+
const { learnSearchCommand } = await import("./learn-C4B2PQ5J.js");
|
|
2588
3521
|
return learnSearchCommand(query, options);
|
|
2589
3522
|
});
|
|
2590
3523
|
program.command("sync").description("Git memory synchronization (Postgres sync optional)").option("-v, --verbose", "Show detailed commit info").option("-p, --push", "Push local memory changes to remote after sync").option("--no-pull", "Skip pulling from remote").option("--postgres", "Sync cycle data to Postgres").action(async (options) => {
|
|
2591
|
-
const { syncCommand } = await import("./sync-
|
|
3524
|
+
const { syncCommand } = await import("./sync-FR6LQJ4C.js");
|
|
2592
3525
|
return syncCommand({ verbose: options.verbose, push: options.push, pull: options.pull, postgres: options.postgres });
|
|
2593
3526
|
});
|
|
2594
3527
|
registerTriggerCommand(program);
|
|
@@ -2610,8 +3543,8 @@ sessions.command("summary").description("Show pretty session summary (auto-detec
|
|
|
2610
3543
|
const { buildCurrentSessionSummary, sessionsSummaryCommand } = await import("./sessions-CK25VGPL.js");
|
|
2611
3544
|
let data;
|
|
2612
3545
|
if (options.file) {
|
|
2613
|
-
const { readFileSync:
|
|
2614
|
-
data = JSON.parse(
|
|
3546
|
+
const { readFileSync: readFileSync9 } = await import("fs");
|
|
3547
|
+
data = JSON.parse(readFileSync9(options.file, "utf-8"));
|
|
2615
3548
|
} else if (options.data) {
|
|
2616
3549
|
data = JSON.parse(options.data);
|
|
2617
3550
|
} else if (!process.stdin.isTTY) {
|
|
@@ -2650,22 +3583,27 @@ program.command("detect-squad").description("Detect current squad based on cwd (
|
|
|
2650
3583
|
return detectSquadCommand();
|
|
2651
3584
|
});
|
|
2652
3585
|
program.command("login").description("Log in to Squads (Pro & Enterprise)").action(async () => {
|
|
2653
|
-
const { loginCommand } = await import("./login-
|
|
3586
|
+
const { loginCommand } = await import("./login-F6ITE7PR.js");
|
|
2654
3587
|
return loginCommand();
|
|
2655
3588
|
});
|
|
2656
3589
|
program.command("logout").description("Log out from Squads").action(async () => {
|
|
2657
|
-
const { logoutCommand } = await import("./login-
|
|
3590
|
+
const { logoutCommand } = await import("./login-F6ITE7PR.js");
|
|
2658
3591
|
return logoutCommand();
|
|
2659
3592
|
});
|
|
2660
3593
|
program.command("whoami").description("Show current logged in user").action(async () => {
|
|
2661
|
-
const { whoamiCommand } = await import("./login-
|
|
3594
|
+
const { whoamiCommand } = await import("./login-F6ITE7PR.js");
|
|
2662
3595
|
return whoamiCommand();
|
|
2663
3596
|
});
|
|
2664
3597
|
registerEvalCommand(program);
|
|
2665
3598
|
registerDeployCommand(program);
|
|
2666
3599
|
registerCognitionCommand(program);
|
|
3600
|
+
registerCatalogCommands(program);
|
|
3601
|
+
registerReleaseCommands(program);
|
|
3602
|
+
registerObservabilityCommands(program);
|
|
3603
|
+
registerTierCommand(program);
|
|
3604
|
+
registerServicesCommands(program);
|
|
2667
3605
|
program.command("providers").description("Show available LLM CLI providers (claude, gemini, codex, etc.)").option("-j, --json", "Output as JSON").action(async (options) => {
|
|
2668
|
-
const { providersCommand } = await import("./providers-
|
|
3606
|
+
const { providersCommand } = await import("./providers-LE744DM6.js");
|
|
2669
3607
|
return providersCommand(options);
|
|
2670
3608
|
});
|
|
2671
3609
|
program.command("update").description("Check for and install updates").option("-y, --yes", "Auto-confirm update without prompting").option("-c, --check", "Check for updates without installing").action(async (options) => {
|