squads-cli 0.4.10 → 0.4.11
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 +66 -2
- package/dist/cli.js +868 -241
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +569 -5
- package/dist/index.js +1030 -0
- package/dist/index.js.map +1 -1
- package/docker/docker-compose.engram.yml +55 -66
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -43,8 +43,8 @@ import {
|
|
|
43
43
|
|
|
44
44
|
// src/cli.ts
|
|
45
45
|
import { config } from "dotenv";
|
|
46
|
-
import { existsSync as
|
|
47
|
-
import { join as
|
|
46
|
+
import { existsSync as existsSync20 } from "fs";
|
|
47
|
+
import { join as join22 } from "path";
|
|
48
48
|
import { homedir as homedir4 } from "os";
|
|
49
49
|
import { Command } from "commander";
|
|
50
50
|
import chalk4 from "chalk";
|
|
@@ -534,9 +534,18 @@ var Events = {
|
|
|
534
534
|
CLI_FEEDBACK_ADD: "cli.feedback.add",
|
|
535
535
|
CLI_FEEDBACK_SHOW: "cli.feedback.show",
|
|
536
536
|
CLI_FEEDBACK_STATS: "cli.feedback.stats",
|
|
537
|
+
// Learnings
|
|
538
|
+
CLI_LEARN: "cli.learn",
|
|
539
|
+
CLI_LEARN_SHOW: "cli.learn.show",
|
|
540
|
+
CLI_LEARN_SEARCH: "cli.learn.search",
|
|
537
541
|
// Auth
|
|
538
542
|
CLI_LOGIN: "cli.login",
|
|
539
|
-
CLI_LOGOUT: "cli.logout"
|
|
543
|
+
CLI_LOGOUT: "cli.logout",
|
|
544
|
+
// Context Condenser
|
|
545
|
+
CONDENSER_COMPRESS: "condenser.compress",
|
|
546
|
+
CONDENSER_DEDUPE: "condenser.dedupe",
|
|
547
|
+
CONDENSER_PRUNE: "condenser.prune",
|
|
548
|
+
CONDENSER_SUMMARIZE: "condenser.summarize"
|
|
540
549
|
};
|
|
541
550
|
var exitHandlerRegistered = false;
|
|
542
551
|
function registerExitHandler() {
|
|
@@ -884,10 +893,13 @@ async function initCommand(options) {
|
|
|
884
893
|
".agents/squads/demo",
|
|
885
894
|
".agents/memory",
|
|
886
895
|
".agents/memory/getting-started",
|
|
896
|
+
".agents/memory/general/shared",
|
|
897
|
+
// For cross-squad learnings
|
|
887
898
|
".agents/outputs",
|
|
888
899
|
".claude",
|
|
889
900
|
".claude/skills",
|
|
890
|
-
".claude/skills/squads-workflow"
|
|
901
|
+
".claude/skills/squads-workflow",
|
|
902
|
+
".claude/skills/squads-learn"
|
|
891
903
|
];
|
|
892
904
|
for (const dir of dirs) {
|
|
893
905
|
await fs.mkdir(path.join(cwd, dir), { recursive: true });
|
|
@@ -1063,6 +1075,56 @@ When a task could be automated:
|
|
|
1063
1075
|
path.join(cwd, ".claude/skills/squads-workflow/instruction.md"),
|
|
1064
1076
|
squadsWorkflowSkill
|
|
1065
1077
|
);
|
|
1078
|
+
const squadsLearnSkill = `---
|
|
1079
|
+
name: squads-learn
|
|
1080
|
+
description: Capture learnings after completing work. Use when finishing a task, fixing a bug, discovering a pattern, or learning something worth remembering for future sessions.
|
|
1081
|
+
---
|
|
1082
|
+
|
|
1083
|
+
# Capture Learnings
|
|
1084
|
+
|
|
1085
|
+
After completing work, capture what you learned so future sessions can benefit.
|
|
1086
|
+
|
|
1087
|
+
## When to Use
|
|
1088
|
+
|
|
1089
|
+
- After fixing a bug - What was the root cause?
|
|
1090
|
+
- After completing a feature - What approach worked?
|
|
1091
|
+
- After research - What's the key insight?
|
|
1092
|
+
- When you notice a pattern - Something that works consistently
|
|
1093
|
+
|
|
1094
|
+
## Quick Commands
|
|
1095
|
+
|
|
1096
|
+
\`\`\`bash
|
|
1097
|
+
# Capture a learning
|
|
1098
|
+
squads learn "The auth token needs refresh after 1 hour, not on 401"
|
|
1099
|
+
|
|
1100
|
+
# With squad and category
|
|
1101
|
+
squads learn "Always check memory first" --squad engineering --category pattern
|
|
1102
|
+
|
|
1103
|
+
# View learnings
|
|
1104
|
+
squads learnings show engineering
|
|
1105
|
+
squads learnings search "auth"
|
|
1106
|
+
\`\`\`
|
|
1107
|
+
|
|
1108
|
+
## Categories
|
|
1109
|
+
|
|
1110
|
+
- \`success\` - Something that worked well
|
|
1111
|
+
- \`failure\` - Something that didn't work (learn from mistakes)
|
|
1112
|
+
- \`pattern\` - A reusable approach
|
|
1113
|
+
- \`tip\` - General advice
|
|
1114
|
+
|
|
1115
|
+
## End of Task Checklist
|
|
1116
|
+
|
|
1117
|
+
Before marking done, ask:
|
|
1118
|
+
1. What worked that I should remember?
|
|
1119
|
+
2. What didn't work that I should avoid?
|
|
1120
|
+
3. Is there a pattern worth capturing?
|
|
1121
|
+
|
|
1122
|
+
If yes \u2192 \`squads learn "<insight>"\`
|
|
1123
|
+
`;
|
|
1124
|
+
await fs.writeFile(
|
|
1125
|
+
path.join(cwd, ".claude/skills/squads-learn/SKILL.md"),
|
|
1126
|
+
squadsLearnSkill
|
|
1127
|
+
);
|
|
1066
1128
|
const seedMemory = `# Getting Started with Squads
|
|
1067
1129
|
|
|
1068
1130
|
## What You've Set Up
|
|
@@ -1139,7 +1201,7 @@ When a task could be automated:
|
|
|
1139
1201
|
hooks: [
|
|
1140
1202
|
{
|
|
1141
1203
|
type: "command",
|
|
1142
|
-
command:
|
|
1204
|
+
command: 'squads memory sync && echo "\\n\u{1F4A1} Capture learnings: squads learn \\"<what you learned>\\"\\n"',
|
|
1143
1205
|
timeout: 15
|
|
1144
1206
|
}
|
|
1145
1207
|
]
|
|
@@ -1303,9 +1365,9 @@ squads goal list # View goals
|
|
|
1303
1365
|
console.log(chalk.dim(" Created:"));
|
|
1304
1366
|
console.log(chalk.dim(" \u2022 .agents/squads/demo/ - Demo squad with 2 agents"));
|
|
1305
1367
|
console.log(chalk.dim(" \u2022 .agents/BUSINESS_BRIEF - Business context template"));
|
|
1306
|
-
console.log(chalk.dim(" \u2022 .agents/memory/ - Seed memory"));
|
|
1368
|
+
console.log(chalk.dim(" \u2022 .agents/memory/ - Seed memory + learnings"));
|
|
1307
1369
|
console.log(chalk.dim(" \u2022 .claude/settings.json - Claude Code hooks"));
|
|
1308
|
-
console.log(chalk.dim(" \u2022 .claude/skills/ -
|
|
1370
|
+
console.log(chalk.dim(" \u2022 .claude/skills/ - Workflow + learn skills"));
|
|
1309
1371
|
console.log(chalk.dim(" \u2022 CLAUDE.md - Agent instructions"));
|
|
1310
1372
|
console.log();
|
|
1311
1373
|
const email = await promptEmail();
|
|
@@ -1331,21 +1393,185 @@ squads goal list # View goals
|
|
|
1331
1393
|
// src/commands/run.ts
|
|
1332
1394
|
import ora2 from "ora";
|
|
1333
1395
|
import { spawn as spawn2 } from "child_process";
|
|
1334
|
-
import { join as
|
|
1335
|
-
import { existsSync as
|
|
1396
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
1397
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
1336
1398
|
|
|
1337
1399
|
// src/lib/squad-parser.ts
|
|
1338
|
-
import { readFileSync as
|
|
1339
|
-
import { join as
|
|
1400
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4, readdirSync, writeFileSync as writeFileSync3 } from "fs";
|
|
1401
|
+
import { join as join4, basename } from "path";
|
|
1340
1402
|
import matter from "gray-matter";
|
|
1403
|
+
|
|
1404
|
+
// src/lib/mcp-config.ts
|
|
1405
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync2 } from "fs";
|
|
1406
|
+
import { join as join3, dirname } from "path";
|
|
1407
|
+
var SERVER_REGISTRY = {
|
|
1408
|
+
// Browser/Web tools
|
|
1409
|
+
"chrome-devtools": {
|
|
1410
|
+
type: "stdio",
|
|
1411
|
+
command: "npx",
|
|
1412
|
+
args: ["chrome-devtools-mcp", "--isolated", "--headless"],
|
|
1413
|
+
env: {}
|
|
1414
|
+
},
|
|
1415
|
+
"nano-banana": {
|
|
1416
|
+
type: "stdio",
|
|
1417
|
+
command: "npx",
|
|
1418
|
+
args: ["nano-banana-mcp"],
|
|
1419
|
+
env: {}
|
|
1420
|
+
},
|
|
1421
|
+
"firecrawl": {
|
|
1422
|
+
type: "stdio",
|
|
1423
|
+
command: "npx",
|
|
1424
|
+
args: ["firecrawl-mcp"],
|
|
1425
|
+
env: {
|
|
1426
|
+
FIRECRAWL_API_KEY: "${FIRECRAWL_API_KEY}"
|
|
1427
|
+
}
|
|
1428
|
+
},
|
|
1429
|
+
// Data tools
|
|
1430
|
+
"supabase": {
|
|
1431
|
+
type: "stdio",
|
|
1432
|
+
command: "npx",
|
|
1433
|
+
args: ["-y", "@supabase/mcp-server-supabase@latest", "--read-only"],
|
|
1434
|
+
env: {
|
|
1435
|
+
SUPABASE_ACCESS_TOKEN: "${SUPABASE_ACCESS_TOKEN}"
|
|
1436
|
+
}
|
|
1437
|
+
},
|
|
1438
|
+
"grafana": {
|
|
1439
|
+
type: "stdio",
|
|
1440
|
+
command: "npx",
|
|
1441
|
+
args: ["-y", "@leval/mcp-grafana"],
|
|
1442
|
+
env: {
|
|
1443
|
+
GRAFANA_URL: "${GRAFANA_URL}",
|
|
1444
|
+
GRAFANA_SERVICE_ACCOUNT_TOKEN: "${GRAFANA_SERVICE_ACCOUNT_TOKEN}"
|
|
1445
|
+
}
|
|
1446
|
+
},
|
|
1447
|
+
// AI/ML tools
|
|
1448
|
+
"huggingface": {
|
|
1449
|
+
type: "stdio",
|
|
1450
|
+
command: "npx",
|
|
1451
|
+
args: ["huggingface-mcp"],
|
|
1452
|
+
env: {
|
|
1453
|
+
HF_TOKEN: "${HF_TOKEN}"
|
|
1454
|
+
}
|
|
1455
|
+
},
|
|
1456
|
+
"context7": {
|
|
1457
|
+
type: "stdio",
|
|
1458
|
+
command: "npx",
|
|
1459
|
+
args: ["context7-mcp"],
|
|
1460
|
+
env: {}
|
|
1461
|
+
},
|
|
1462
|
+
// Social/Research tools
|
|
1463
|
+
"x-mcp": {
|
|
1464
|
+
type: "stdio",
|
|
1465
|
+
command: "python3",
|
|
1466
|
+
args: ["~/.claude/mcps/x-mcp/server.py"],
|
|
1467
|
+
env: {
|
|
1468
|
+
X_BEARER_TOKEN: "${X_BEARER_TOKEN}"
|
|
1469
|
+
}
|
|
1470
|
+
},
|
|
1471
|
+
// Telemetry
|
|
1472
|
+
"langfuse-telemetry": {
|
|
1473
|
+
type: "stdio",
|
|
1474
|
+
command: "python3",
|
|
1475
|
+
args: ["~/.claude/mcps/langfuse-telemetry/server.py"],
|
|
1476
|
+
env: {
|
|
1477
|
+
LANGFUSE_HOST: "${LANGFUSE_HOST}",
|
|
1478
|
+
LANGFUSE_PUBLIC_KEY: "${LANGFUSE_PUBLIC_KEY}",
|
|
1479
|
+
LANGFUSE_SECRET_KEY: "${LANGFUSE_SECRET_KEY}"
|
|
1480
|
+
}
|
|
1481
|
+
},
|
|
1482
|
+
// Analytics
|
|
1483
|
+
"ga4-admin": {
|
|
1484
|
+
type: "stdio",
|
|
1485
|
+
command: "python3",
|
|
1486
|
+
args: ["-m", "mcp_ga4.server"],
|
|
1487
|
+
env: {}
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
function getHome() {
|
|
1491
|
+
return process.env.HOME || process.env.USERPROFILE || "";
|
|
1492
|
+
}
|
|
1493
|
+
function getContextsDir() {
|
|
1494
|
+
return join3(getHome(), ".claude", "contexts");
|
|
1495
|
+
}
|
|
1496
|
+
function getMcpConfigsDir() {
|
|
1497
|
+
return join3(getHome(), ".claude", "mcp-configs");
|
|
1498
|
+
}
|
|
1499
|
+
function generateMcpConfig(mcpServers) {
|
|
1500
|
+
const config2 = { mcpServers: {} };
|
|
1501
|
+
for (const server of mcpServers) {
|
|
1502
|
+
const def = SERVER_REGISTRY[server];
|
|
1503
|
+
if (def) {
|
|
1504
|
+
config2.mcpServers[server] = def;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
return config2;
|
|
1508
|
+
}
|
|
1509
|
+
function writeMcpConfig(config2, path3) {
|
|
1510
|
+
const dir = dirname(path3);
|
|
1511
|
+
if (!existsSync3(dir)) {
|
|
1512
|
+
mkdirSync2(dir, { recursive: true });
|
|
1513
|
+
}
|
|
1514
|
+
writeFileSync2(path3, JSON.stringify(config2, null, 2));
|
|
1515
|
+
}
|
|
1516
|
+
function readMcpConfig(path3) {
|
|
1517
|
+
if (!existsSync3(path3)) return null;
|
|
1518
|
+
try {
|
|
1519
|
+
const content = readFileSync2(path3, "utf-8");
|
|
1520
|
+
return JSON.parse(content);
|
|
1521
|
+
} catch {
|
|
1522
|
+
return null;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
function resolveMcpConfig(squadName, mcpServers, forceRegenerate = false) {
|
|
1526
|
+
const home = getHome();
|
|
1527
|
+
const userOverride = join3(getMcpConfigsDir(), `${squadName}.json`);
|
|
1528
|
+
if (existsSync3(userOverride)) {
|
|
1529
|
+
const config2 = readMcpConfig(userOverride);
|
|
1530
|
+
return {
|
|
1531
|
+
path: userOverride,
|
|
1532
|
+
source: "user-override",
|
|
1533
|
+
servers: config2 ? Object.keys(config2.mcpServers) : void 0
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
if (mcpServers && mcpServers.length > 0) {
|
|
1537
|
+
const generatedPath = join3(getContextsDir(), `${squadName}.mcp.json`);
|
|
1538
|
+
const shouldGenerate = forceRegenerate || !existsSync3(generatedPath);
|
|
1539
|
+
if (shouldGenerate) {
|
|
1540
|
+
const config3 = generateMcpConfig(mcpServers);
|
|
1541
|
+
writeMcpConfig(config3, generatedPath);
|
|
1542
|
+
return {
|
|
1543
|
+
path: generatedPath,
|
|
1544
|
+
source: "generated",
|
|
1545
|
+
servers: Object.keys(config3.mcpServers),
|
|
1546
|
+
generated: true
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
const config2 = readMcpConfig(generatedPath);
|
|
1550
|
+
return {
|
|
1551
|
+
path: generatedPath,
|
|
1552
|
+
source: "generated",
|
|
1553
|
+
servers: config2 ? Object.keys(config2.mcpServers) : mcpServers,
|
|
1554
|
+
generated: false
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
return {
|
|
1558
|
+
path: join3(home, ".claude.json"),
|
|
1559
|
+
source: "fallback"
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
function resolveMcpConfigPath(squadName, mcpServers) {
|
|
1563
|
+
return resolveMcpConfig(squadName, mcpServers).path;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
// src/lib/squad-parser.ts
|
|
1341
1567
|
function findSquadsDir() {
|
|
1342
1568
|
let dir = process.cwd();
|
|
1343
1569
|
for (let i = 0; i < 5; i++) {
|
|
1344
|
-
const squadsPath =
|
|
1345
|
-
if (
|
|
1570
|
+
const squadsPath = join4(dir, ".agents", "squads");
|
|
1571
|
+
if (existsSync4(squadsPath)) {
|
|
1346
1572
|
return squadsPath;
|
|
1347
1573
|
}
|
|
1348
|
-
const parent =
|
|
1574
|
+
const parent = join4(dir, "..");
|
|
1349
1575
|
if (parent === dir) break;
|
|
1350
1576
|
dir = parent;
|
|
1351
1577
|
}
|
|
@@ -1354,14 +1580,14 @@ function findSquadsDir() {
|
|
|
1354
1580
|
function findProjectRoot() {
|
|
1355
1581
|
const squadsDir = findSquadsDir();
|
|
1356
1582
|
if (!squadsDir) return null;
|
|
1357
|
-
return
|
|
1583
|
+
return join4(squadsDir, "..", "..");
|
|
1358
1584
|
}
|
|
1359
1585
|
function hasLocalInfraConfig() {
|
|
1360
1586
|
const projectRoot = findProjectRoot();
|
|
1361
1587
|
if (!projectRoot) return false;
|
|
1362
|
-
const envPath =
|
|
1363
|
-
if (!
|
|
1364
|
-
const content =
|
|
1588
|
+
const envPath = join4(projectRoot, ".env");
|
|
1589
|
+
if (!existsSync4(envPath)) return false;
|
|
1590
|
+
const content = readFileSync3(envPath, "utf-8");
|
|
1365
1591
|
const infraKeys = ["LANGFUSE_", "SQUADS_BRIDGE", "SQUADS_POSTGRES", "SQUADS_REDIS"];
|
|
1366
1592
|
return infraKeys.some((key) => content.includes(key));
|
|
1367
1593
|
}
|
|
@@ -1370,8 +1596,8 @@ function listSquads(squadsDir) {
|
|
|
1370
1596
|
const entries = readdirSync(squadsDir, { withFileTypes: true });
|
|
1371
1597
|
for (const entry of entries) {
|
|
1372
1598
|
if (entry.isDirectory() && !entry.name.startsWith("_")) {
|
|
1373
|
-
const squadFile =
|
|
1374
|
-
if (
|
|
1599
|
+
const squadFile = join4(squadsDir, entry.name, "SQUAD.md");
|
|
1600
|
+
if (existsSync4(squadFile)) {
|
|
1375
1601
|
squads.push(entry.name);
|
|
1376
1602
|
}
|
|
1377
1603
|
}
|
|
@@ -1382,8 +1608,8 @@ function listAgents(squadsDir, squadName) {
|
|
|
1382
1608
|
const agents = [];
|
|
1383
1609
|
const dirs = squadName ? [squadName] : readdirSync(squadsDir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_")).map((e) => e.name);
|
|
1384
1610
|
for (const dir of dirs) {
|
|
1385
|
-
const squadPath =
|
|
1386
|
-
if (!
|
|
1611
|
+
const squadPath = join4(squadsDir, dir);
|
|
1612
|
+
if (!existsSync4(squadPath)) continue;
|
|
1387
1613
|
const files = readdirSync(squadPath);
|
|
1388
1614
|
for (const file of files) {
|
|
1389
1615
|
if (file.endsWith(".md") && file !== "SQUAD.md") {
|
|
@@ -1392,7 +1618,7 @@ function listAgents(squadsDir, squadName) {
|
|
|
1392
1618
|
name: agentName,
|
|
1393
1619
|
role: `Agent in ${dir}`,
|
|
1394
1620
|
trigger: "manual",
|
|
1395
|
-
filePath:
|
|
1621
|
+
filePath: join4(squadPath, file)
|
|
1396
1622
|
});
|
|
1397
1623
|
}
|
|
1398
1624
|
}
|
|
@@ -1400,7 +1626,7 @@ function listAgents(squadsDir, squadName) {
|
|
|
1400
1626
|
return agents;
|
|
1401
1627
|
}
|
|
1402
1628
|
function parseSquadFile(filePath) {
|
|
1403
|
-
const rawContent =
|
|
1629
|
+
const rawContent = readFileSync3(filePath, "utf-8");
|
|
1404
1630
|
const { data: frontmatter, content: bodyContent } = matter(rawContent);
|
|
1405
1631
|
const fm = frontmatter;
|
|
1406
1632
|
const lines = bodyContent.split("\n");
|
|
@@ -1521,20 +1747,20 @@ function parseSquadFile(filePath) {
|
|
|
1521
1747
|
function loadSquad(squadName) {
|
|
1522
1748
|
const squadsDir = findSquadsDir();
|
|
1523
1749
|
if (!squadsDir) return null;
|
|
1524
|
-
const squadFile =
|
|
1525
|
-
if (!
|
|
1750
|
+
const squadFile = join4(squadsDir, squadName, "SQUAD.md");
|
|
1751
|
+
if (!existsSync4(squadFile)) return null;
|
|
1526
1752
|
return parseSquadFile(squadFile);
|
|
1527
1753
|
}
|
|
1528
1754
|
function loadAgentDefinition(agentPath) {
|
|
1529
|
-
if (!
|
|
1530
|
-
return
|
|
1755
|
+
if (!existsSync4(agentPath)) return "";
|
|
1756
|
+
return readFileSync3(agentPath, "utf-8");
|
|
1531
1757
|
}
|
|
1532
1758
|
function addGoalToSquad(squadName, goal2) {
|
|
1533
1759
|
const squadsDir = findSquadsDir();
|
|
1534
1760
|
if (!squadsDir) return false;
|
|
1535
|
-
const squadFile =
|
|
1536
|
-
if (!
|
|
1537
|
-
let content =
|
|
1761
|
+
const squadFile = join4(squadsDir, squadName, "SQUAD.md");
|
|
1762
|
+
if (!existsSync4(squadFile)) return false;
|
|
1763
|
+
let content = readFileSync3(squadFile, "utf-8");
|
|
1538
1764
|
if (!content.includes("## Goals")) {
|
|
1539
1765
|
const insertPoint = content.indexOf("## Dependencies");
|
|
1540
1766
|
if (insertPoint > 0) {
|
|
@@ -1569,15 +1795,15 @@ function addGoalToSquad(squadName, goal2) {
|
|
|
1569
1795
|
- [ ] ${goal2}` + content.slice(headerEnd);
|
|
1570
1796
|
}
|
|
1571
1797
|
}
|
|
1572
|
-
|
|
1798
|
+
writeFileSync3(squadFile, content);
|
|
1573
1799
|
return true;
|
|
1574
1800
|
}
|
|
1575
1801
|
function updateGoalInSquad(squadName, goalIndex, updates) {
|
|
1576
1802
|
const squadsDir = findSquadsDir();
|
|
1577
1803
|
if (!squadsDir) return false;
|
|
1578
|
-
const squadFile =
|
|
1579
|
-
if (!
|
|
1580
|
-
const content =
|
|
1804
|
+
const squadFile = join4(squadsDir, squadName, "SQUAD.md");
|
|
1805
|
+
if (!existsSync4(squadFile)) return false;
|
|
1806
|
+
const content = readFileSync3(squadFile, "utf-8");
|
|
1581
1807
|
const lines = content.split("\n");
|
|
1582
1808
|
let currentSection = "";
|
|
1583
1809
|
let goalCount = 0;
|
|
@@ -1599,7 +1825,7 @@ function updateGoalInSquad(squadName, goalIndex, updates) {
|
|
|
1599
1825
|
}
|
|
1600
1826
|
}
|
|
1601
1827
|
lines[i] = newLine;
|
|
1602
|
-
|
|
1828
|
+
writeFileSync3(squadFile, lines.join("\n"));
|
|
1603
1829
|
return true;
|
|
1604
1830
|
}
|
|
1605
1831
|
goalCount++;
|
|
@@ -1608,6 +1834,84 @@ function updateGoalInSquad(squadName, goalIndex, updates) {
|
|
|
1608
1834
|
}
|
|
1609
1835
|
return false;
|
|
1610
1836
|
}
|
|
1837
|
+
function findSkillsDir() {
|
|
1838
|
+
const projectRoot = findProjectRoot();
|
|
1839
|
+
if (!projectRoot) return null;
|
|
1840
|
+
const skillsDir = join4(projectRoot, ".claude", "skills");
|
|
1841
|
+
return existsSync4(skillsDir) ? skillsDir : null;
|
|
1842
|
+
}
|
|
1843
|
+
function findMemoryDir2() {
|
|
1844
|
+
const projectRoot = findProjectRoot();
|
|
1845
|
+
if (!projectRoot) return null;
|
|
1846
|
+
const memoryDir = join4(projectRoot, ".agents", "memory");
|
|
1847
|
+
return existsSync4(memoryDir) ? memoryDir : null;
|
|
1848
|
+
}
|
|
1849
|
+
function resolveSkillPath(skillName) {
|
|
1850
|
+
const skillsDir = findSkillsDir();
|
|
1851
|
+
if (!skillsDir) return null;
|
|
1852
|
+
const skillPath = join4(skillsDir, skillName);
|
|
1853
|
+
return existsSync4(skillPath) ? skillPath : null;
|
|
1854
|
+
}
|
|
1855
|
+
function resolveMemoryPaths(patterns) {
|
|
1856
|
+
const memoryDir = findMemoryDir2();
|
|
1857
|
+
if (!memoryDir) return [];
|
|
1858
|
+
const resolved = [];
|
|
1859
|
+
for (const pattern of patterns) {
|
|
1860
|
+
if (pattern.endsWith("/*")) {
|
|
1861
|
+
const subdir = pattern.slice(0, -2);
|
|
1862
|
+
const subdirPath = join4(memoryDir, subdir);
|
|
1863
|
+
if (existsSync4(subdirPath)) {
|
|
1864
|
+
try {
|
|
1865
|
+
const files = readdirSync(subdirPath);
|
|
1866
|
+
for (const file of files) {
|
|
1867
|
+
if (file.endsWith(".md")) {
|
|
1868
|
+
resolved.push(join4(subdirPath, file));
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
} catch {
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
} else {
|
|
1875
|
+
const fullPath = join4(memoryDir, pattern);
|
|
1876
|
+
if (existsSync4(fullPath)) {
|
|
1877
|
+
resolved.push(fullPath);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
return resolved;
|
|
1882
|
+
}
|
|
1883
|
+
function resolveExecutionContext(squad, forceRegenerate = false) {
|
|
1884
|
+
const ctx = squad.context || {};
|
|
1885
|
+
const mcpResolution = resolveMcpConfig(
|
|
1886
|
+
squad.name,
|
|
1887
|
+
ctx.mcp,
|
|
1888
|
+
forceRegenerate
|
|
1889
|
+
);
|
|
1890
|
+
const skillPaths = [];
|
|
1891
|
+
if (ctx.skills) {
|
|
1892
|
+
for (const skill of ctx.skills) {
|
|
1893
|
+
const path3 = resolveSkillPath(skill);
|
|
1894
|
+
if (path3) {
|
|
1895
|
+
skillPaths.push(path3);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
const memoryPaths = ctx.memory?.load ? resolveMemoryPaths(ctx.memory.load) : [];
|
|
1900
|
+
return {
|
|
1901
|
+
// Copy all SquadContext fields
|
|
1902
|
+
...ctx,
|
|
1903
|
+
// Add squad name
|
|
1904
|
+
squadName: squad.name,
|
|
1905
|
+
// Add resolved paths
|
|
1906
|
+
resolved: {
|
|
1907
|
+
mcpConfigPath: mcpResolution.path,
|
|
1908
|
+
mcpSource: mcpResolution.source,
|
|
1909
|
+
mcpServers: mcpResolution.servers || [],
|
|
1910
|
+
skillPaths,
|
|
1911
|
+
memoryPaths
|
|
1912
|
+
}
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1611
1915
|
|
|
1612
1916
|
// src/lib/permissions.ts
|
|
1613
1917
|
import { minimatch } from "minimatch";
|
|
@@ -1846,9 +2150,12 @@ function generateExecutionId() {
|
|
|
1846
2150
|
const random = Math.random().toString(36).substring(2, 8);
|
|
1847
2151
|
return `exec_${timestamp}_${random}`;
|
|
1848
2152
|
}
|
|
1849
|
-
function selectMcpConfig(squadName) {
|
|
2153
|
+
function selectMcpConfig(squadName, squad) {
|
|
2154
|
+
if (squad?.context?.mcp && squad.context.mcp.length > 0) {
|
|
2155
|
+
return resolveMcpConfigPath(squadName, squad.context.mcp);
|
|
2156
|
+
}
|
|
1850
2157
|
const home = process.env.HOME || "";
|
|
1851
|
-
const configsDir =
|
|
2158
|
+
const configsDir = join5(home, ".claude", "mcp-configs");
|
|
1852
2159
|
const squadConfigs = {
|
|
1853
2160
|
website: "website.json",
|
|
1854
2161
|
// chrome-devtools, nano-banana
|
|
@@ -1863,12 +2170,12 @@ function selectMcpConfig(squadName) {
|
|
|
1863
2170
|
};
|
|
1864
2171
|
const configFile = squadConfigs[squadName.toLowerCase()];
|
|
1865
2172
|
if (configFile) {
|
|
1866
|
-
const configPath =
|
|
1867
|
-
if (
|
|
2173
|
+
const configPath = join5(configsDir, configFile);
|
|
2174
|
+
if (existsSync5(configPath)) {
|
|
1868
2175
|
return configPath;
|
|
1869
2176
|
}
|
|
1870
2177
|
}
|
|
1871
|
-
return
|
|
2178
|
+
return join5(home, ".claude.json");
|
|
1872
2179
|
}
|
|
1873
2180
|
function detectTaskType(agentName) {
|
|
1874
2181
|
const name = agentName.toLowerCase();
|
|
@@ -1884,12 +2191,12 @@ function detectTaskType(agentName) {
|
|
|
1884
2191
|
return "execution";
|
|
1885
2192
|
}
|
|
1886
2193
|
function ensureProjectTrusted(projectPath) {
|
|
1887
|
-
const configPath =
|
|
1888
|
-
if (!
|
|
2194
|
+
const configPath = join5(process.env.HOME || "", ".claude.json");
|
|
2195
|
+
if (!existsSync5(configPath)) {
|
|
1889
2196
|
return;
|
|
1890
2197
|
}
|
|
1891
2198
|
try {
|
|
1892
|
-
const config2 = JSON.parse(
|
|
2199
|
+
const config2 = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
1893
2200
|
if (!config2.projects) {
|
|
1894
2201
|
config2.projects = {};
|
|
1895
2202
|
}
|
|
@@ -1898,7 +2205,7 @@ function ensureProjectTrusted(projectPath) {
|
|
|
1898
2205
|
}
|
|
1899
2206
|
if (!config2.projects[projectPath].hasTrustDialogAccepted) {
|
|
1900
2207
|
config2.projects[projectPath].hasTrustDialogAccepted = true;
|
|
1901
|
-
|
|
2208
|
+
writeFileSync4(configPath, JSON.stringify(config2, null, 2));
|
|
1902
2209
|
}
|
|
1903
2210
|
} catch {
|
|
1904
2211
|
}
|
|
@@ -1906,25 +2213,25 @@ function ensureProjectTrusted(projectPath) {
|
|
|
1906
2213
|
function getProjectRoot() {
|
|
1907
2214
|
const squadsDir = findSquadsDir();
|
|
1908
2215
|
if (squadsDir) {
|
|
1909
|
-
return
|
|
2216
|
+
return dirname2(dirname2(squadsDir));
|
|
1910
2217
|
}
|
|
1911
2218
|
return process.cwd();
|
|
1912
2219
|
}
|
|
1913
2220
|
function getExecutionLogPath(squadName, agentName) {
|
|
1914
2221
|
const memoryDir = findMemoryDir();
|
|
1915
2222
|
if (!memoryDir) return null;
|
|
1916
|
-
return
|
|
2223
|
+
return join5(memoryDir, squadName, agentName, "executions.md");
|
|
1917
2224
|
}
|
|
1918
2225
|
function logExecution(record) {
|
|
1919
2226
|
const logPath = getExecutionLogPath(record.squadName, record.agentName);
|
|
1920
2227
|
if (!logPath) return;
|
|
1921
|
-
const dir =
|
|
1922
|
-
if (!
|
|
1923
|
-
|
|
2228
|
+
const dir = dirname2(logPath);
|
|
2229
|
+
if (!existsSync5(dir)) {
|
|
2230
|
+
mkdirSync3(dir, { recursive: true });
|
|
1924
2231
|
}
|
|
1925
2232
|
let content = "";
|
|
1926
|
-
if (
|
|
1927
|
-
content =
|
|
2233
|
+
if (existsSync5(logPath)) {
|
|
2234
|
+
content = readFileSync4(logPath, "utf-8").trimEnd();
|
|
1928
2235
|
} else {
|
|
1929
2236
|
content = `# ${record.squadName}/${record.agentName} - Execution Log`;
|
|
1930
2237
|
}
|
|
@@ -1937,12 +2244,12 @@ function logExecution(record) {
|
|
|
1937
2244
|
- Trigger: ${record.trigger || "manual"}
|
|
1938
2245
|
- Task Type: ${record.taskType || "execution"}
|
|
1939
2246
|
`;
|
|
1940
|
-
|
|
2247
|
+
writeFileSync4(logPath, content + entry);
|
|
1941
2248
|
}
|
|
1942
2249
|
function updateExecutionStatus(squadName, agentName, executionId, status, details) {
|
|
1943
2250
|
const logPath = getExecutionLogPath(squadName, agentName);
|
|
1944
|
-
if (!logPath || !
|
|
1945
|
-
let content =
|
|
2251
|
+
if (!logPath || !existsSync5(logPath)) return;
|
|
2252
|
+
let content = readFileSync4(logPath, "utf-8");
|
|
1946
2253
|
const endTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
1947
2254
|
const execMarker = `<!-- exec:${executionId} -->`;
|
|
1948
2255
|
const markerIndex = content.indexOf(execMarker);
|
|
@@ -1963,7 +2270,7 @@ function updateExecutionStatus(squadName, agentName, executionId, status, detail
|
|
|
1963
2270
|
- Error: ${details.error}`;
|
|
1964
2271
|
}
|
|
1965
2272
|
content = content.slice(0, entryStart) + updatedEntry + content.slice(entryEnd);
|
|
1966
|
-
|
|
2273
|
+
writeFileSync4(logPath, content);
|
|
1967
2274
|
}
|
|
1968
2275
|
function extractMcpServersFromDefinition(definition) {
|
|
1969
2276
|
const servers = /* @__PURE__ */ new Set();
|
|
@@ -2042,8 +2349,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
2042
2349
|
if (options.parallel) {
|
|
2043
2350
|
const agentFiles = squad.agents.map((a) => ({
|
|
2044
2351
|
name: a.name,
|
|
2045
|
-
path:
|
|
2046
|
-
})).filter((a) =>
|
|
2352
|
+
path: join5(squadsDir, squad.name, `${a.name}.md`)
|
|
2353
|
+
})).filter((a) => existsSync5(a.path));
|
|
2047
2354
|
if (agentFiles.length === 0) {
|
|
2048
2355
|
writeLine(` ${icons.error} ${colors.red}No agent files found${RESET}`);
|
|
2049
2356
|
return;
|
|
@@ -2079,8 +2386,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
2079
2386
|
writeLine();
|
|
2080
2387
|
for (let i = 0; i < pipeline.agents.length; i++) {
|
|
2081
2388
|
const agentName = pipeline.agents[i];
|
|
2082
|
-
const agentPath =
|
|
2083
|
-
if (
|
|
2389
|
+
const agentPath = join5(squadsDir, squad.name, `${agentName}.md`);
|
|
2390
|
+
if (existsSync5(agentPath)) {
|
|
2084
2391
|
writeLine(` ${colors.dim}[${i + 1}/${pipeline.agents.length}]${RESET}`);
|
|
2085
2392
|
await runAgent(agentName, agentPath, squad.name, options);
|
|
2086
2393
|
writeLine();
|
|
@@ -2090,8 +2397,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
2090
2397
|
}
|
|
2091
2398
|
} else {
|
|
2092
2399
|
if (options.agent) {
|
|
2093
|
-
const agentPath =
|
|
2094
|
-
if (
|
|
2400
|
+
const agentPath = join5(squadsDir, squad.name, `${options.agent}.md`);
|
|
2401
|
+
if (existsSync5(agentPath)) {
|
|
2095
2402
|
await runAgent(options.agent, agentPath, squad.name, options);
|
|
2096
2403
|
} else {
|
|
2097
2404
|
writeLine(` ${icons.error} ${colors.red}Agent ${options.agent} not found${RESET}`);
|
|
@@ -2102,8 +2409,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
2102
2409
|
(a) => a.name.includes("lead") || a.trigger === "Manual"
|
|
2103
2410
|
);
|
|
2104
2411
|
if (orchestrator) {
|
|
2105
|
-
const agentPath =
|
|
2106
|
-
if (
|
|
2412
|
+
const agentPath = join5(squadsDir, squad.name, `${orchestrator.name}.md`);
|
|
2413
|
+
if (existsSync5(agentPath)) {
|
|
2107
2414
|
await runAgent(orchestrator.name, agentPath, squad.name, options);
|
|
2108
2415
|
}
|
|
2109
2416
|
} else {
|
|
@@ -2129,9 +2436,9 @@ async function runLeadMode(squad, squadsDir, options) {
|
|
|
2129
2436
|
if (!squad) return;
|
|
2130
2437
|
const agentFiles = squad.agents.map((a) => ({
|
|
2131
2438
|
name: a.name,
|
|
2132
|
-
path:
|
|
2439
|
+
path: join5(squadsDir, squad.name, `${a.name}.md`),
|
|
2133
2440
|
role: a.role || ""
|
|
2134
|
-
})).filter((a) =>
|
|
2441
|
+
})).filter((a) => existsSync5(a.path));
|
|
2135
2442
|
if (agentFiles.length === 0) {
|
|
2136
2443
|
writeLine(` ${icons.error} ${colors.red}No agent files found${RESET}`);
|
|
2137
2444
|
return;
|
|
@@ -2250,9 +2557,9 @@ async function runAgent(agentName, agentPath, squadName, options) {
|
|
|
2250
2557
|
}
|
|
2251
2558
|
const squadsDir = findSquadsDir();
|
|
2252
2559
|
if (squadsDir) {
|
|
2253
|
-
const squadFilePath =
|
|
2254
|
-
if (
|
|
2255
|
-
const squadContent =
|
|
2560
|
+
const squadFilePath = join5(squadsDir, squadName, "SQUAD.md");
|
|
2561
|
+
if (existsSync5(squadFilePath)) {
|
|
2562
|
+
const squadContent = readFileSync4(squadFilePath, "utf-8");
|
|
2256
2563
|
const permContext = buildContextFromSquad(squadName, squadContent, agentName);
|
|
2257
2564
|
const mcpServers = extractMcpServersFromDefinition(definition);
|
|
2258
2565
|
const execRequest = {
|
|
@@ -2361,7 +2668,8 @@ async function executeWithClaude(prompt2, verbose, _timeoutMinutes = 30, foregro
|
|
|
2361
2668
|
const agentMatch = prompt2.match(/(\w+) agent/);
|
|
2362
2669
|
const squadName = process.env.SQUADS_SQUAD || squadMatch?.[1] || "unknown";
|
|
2363
2670
|
const agentName = process.env.SQUADS_AGENT || agentMatch?.[1] || "unknown";
|
|
2364
|
-
const
|
|
2671
|
+
const squad = squadName !== "unknown" ? loadSquad(squadName) : null;
|
|
2672
|
+
const mcpConfigPath = selectMcpConfig(squadName, squad);
|
|
2365
2673
|
const execContext = {
|
|
2366
2674
|
squad: squadName,
|
|
2367
2675
|
agent: agentName,
|
|
@@ -2532,8 +2840,8 @@ async function listCommand(options) {
|
|
|
2532
2840
|
}
|
|
2533
2841
|
|
|
2534
2842
|
// src/commands/status.ts
|
|
2535
|
-
import { existsSync as
|
|
2536
|
-
import { join as
|
|
2843
|
+
import { existsSync as existsSync6, statSync } from "fs";
|
|
2844
|
+
import { join as join6 } from "path";
|
|
2537
2845
|
async function statusCommand(squadName, options = {}) {
|
|
2538
2846
|
await track(Events.CLI_STATUS, { squad: squadName || "all", verbose: options.verbose });
|
|
2539
2847
|
const squadsDir = findSquadsDir();
|
|
@@ -2586,8 +2894,8 @@ async function showOverallStatus(squadsDir, _options) {
|
|
|
2586
2894
|
let lastActivity = `${colors.dim}\u2014${RESET}`;
|
|
2587
2895
|
let activityColor = colors.dim;
|
|
2588
2896
|
if (memoryDir) {
|
|
2589
|
-
const squadMemoryPath =
|
|
2590
|
-
if (
|
|
2897
|
+
const squadMemoryPath = join6(memoryDir, squadName);
|
|
2898
|
+
if (existsSync6(squadMemoryPath)) {
|
|
2591
2899
|
const states = getSquadState(squadName);
|
|
2592
2900
|
memoryStatus = `${colors.green}${states.length} ${states.length === 1 ? "entry" : "entries"}${RESET}`;
|
|
2593
2901
|
let mostRecent = 0;
|
|
@@ -2689,14 +2997,14 @@ async function showSquadStatus(squadName, squadsDir, options) {
|
|
|
2689
2997
|
}
|
|
2690
2998
|
|
|
2691
2999
|
// src/commands/stack.ts
|
|
2692
|
-
import { existsSync as
|
|
2693
|
-
import { join as
|
|
3000
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, copyFileSync } from "fs";
|
|
3001
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
2694
3002
|
import { homedir as homedir2 } from "os";
|
|
2695
3003
|
import { execSync as execSync3, spawn as spawn3 } from "child_process";
|
|
2696
3004
|
import { createInterface as createInterface2 } from "readline";
|
|
2697
3005
|
import { fileURLToPath } from "url";
|
|
2698
3006
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
2699
|
-
var __dirname2 =
|
|
3007
|
+
var __dirname2 = dirname3(__filename2);
|
|
2700
3008
|
var DEFAULT_CONFIG = {
|
|
2701
3009
|
SQUADS_DATABASE_URL: "postgresql://squads:squads@localhost:5433/squads",
|
|
2702
3010
|
SQUADS_BRIDGE_URL: "http://localhost:8088",
|
|
@@ -2705,8 +3013,8 @@ var DEFAULT_CONFIG = {
|
|
|
2705
3013
|
LANGFUSE_SECRET_KEY: "",
|
|
2706
3014
|
REDIS_URL: "redis://localhost:6379"
|
|
2707
3015
|
};
|
|
2708
|
-
var CONFIG_PATH2 =
|
|
2709
|
-
var SQUADS_DATA_DIR =
|
|
3016
|
+
var CONFIG_PATH2 = join7(homedir2(), ".squadsrc");
|
|
3017
|
+
var SQUADS_DATA_DIR = join7(homedir2(), ".squads");
|
|
2710
3018
|
var SERVICES = {
|
|
2711
3019
|
bridge: {
|
|
2712
3020
|
name: "Bridge API",
|
|
@@ -2840,25 +3148,25 @@ async function confirm2(question, defaultYes = true) {
|
|
|
2840
3148
|
function findPackageDockerDir() {
|
|
2841
3149
|
const candidates = [
|
|
2842
3150
|
// From npm package (relative to dist/commands/stack.js)
|
|
2843
|
-
|
|
3151
|
+
join7(__dirname2, "..", "..", "docker"),
|
|
2844
3152
|
// Local development
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
3153
|
+
join7(process.cwd(), "docker"),
|
|
3154
|
+
join7(process.cwd(), "..", "squads-cli", "docker"),
|
|
3155
|
+
join7(homedir2(), "agents-squads", "squads-cli", "docker")
|
|
2848
3156
|
];
|
|
2849
3157
|
for (const dir of candidates) {
|
|
2850
|
-
if (
|
|
3158
|
+
if (existsSync7(join7(dir, "docker-compose.yml"))) {
|
|
2851
3159
|
return dir;
|
|
2852
3160
|
}
|
|
2853
3161
|
}
|
|
2854
3162
|
return null;
|
|
2855
3163
|
}
|
|
2856
3164
|
function loadStackConfig() {
|
|
2857
|
-
if (!
|
|
3165
|
+
if (!existsSync7(CONFIG_PATH2)) {
|
|
2858
3166
|
return null;
|
|
2859
3167
|
}
|
|
2860
3168
|
try {
|
|
2861
|
-
const content =
|
|
3169
|
+
const content = readFileSync5(CONFIG_PATH2, "utf-8");
|
|
2862
3170
|
const config2 = {};
|
|
2863
3171
|
for (const line of content.split("\n")) {
|
|
2864
3172
|
const trimmed = line.trim();
|
|
@@ -2899,7 +3207,7 @@ function saveStackConfig(config2) {
|
|
|
2899
3207
|
"# To activate: source ~/.squadsrc",
|
|
2900
3208
|
""
|
|
2901
3209
|
];
|
|
2902
|
-
|
|
3210
|
+
writeFileSync5(CONFIG_PATH2, lines.join("\n"));
|
|
2903
3211
|
}
|
|
2904
3212
|
function applyStackConfig() {
|
|
2905
3213
|
const config2 = loadStackConfig();
|
|
@@ -2971,13 +3279,13 @@ async function checkService(url, timeout = 2e3) {
|
|
|
2971
3279
|
}
|
|
2972
3280
|
function getLangfuseKeysFromDockerEnv() {
|
|
2973
3281
|
const envPaths2 = [
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
3282
|
+
join7(process.cwd(), "docker", ".env"),
|
|
3283
|
+
join7(process.cwd(), "..", "squads-cli", "docker", ".env"),
|
|
3284
|
+
join7(homedir2(), "agents-squads", "squads-cli", "docker", ".env")
|
|
2977
3285
|
];
|
|
2978
3286
|
for (const envPath of envPaths2) {
|
|
2979
|
-
if (
|
|
2980
|
-
const content =
|
|
3287
|
+
if (existsSync7(envPath)) {
|
|
3288
|
+
const content = readFileSync5(envPath, "utf-8");
|
|
2981
3289
|
const publicMatch = content.match(/LANGFUSE_PUBLIC_KEY=(\S+)/);
|
|
2982
3290
|
const secretMatch = content.match(/LANGFUSE_SECRET_KEY=(\S+)/);
|
|
2983
3291
|
if (publicMatch && secretMatch) {
|
|
@@ -2992,12 +3300,12 @@ function getLangfuseKeysFromDockerEnv() {
|
|
|
2992
3300
|
}
|
|
2993
3301
|
function findDockerComposeDir() {
|
|
2994
3302
|
const candidates = [
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
3303
|
+
join7(process.cwd(), "docker"),
|
|
3304
|
+
join7(process.cwd(), "..", "squads-cli", "docker"),
|
|
3305
|
+
join7(homedir2(), "agents-squads", "squads-cli", "docker")
|
|
2998
3306
|
];
|
|
2999
3307
|
for (const dir of candidates) {
|
|
3000
|
-
if (
|
|
3308
|
+
if (existsSync7(join7(dir, "docker-compose.yml"))) {
|
|
3001
3309
|
return dir;
|
|
3002
3310
|
}
|
|
3003
3311
|
}
|
|
@@ -3023,7 +3331,7 @@ async function stackInitCommand() {
|
|
|
3023
3331
|
writeLine();
|
|
3024
3332
|
writeLine(` ${bold}Step 2: Docker Compose Files${RESET}`);
|
|
3025
3333
|
let composeDir = findPackageDockerDir();
|
|
3026
|
-
const targetDir =
|
|
3334
|
+
const targetDir = join7(SQUADS_DATA_DIR, "docker");
|
|
3027
3335
|
if (!composeDir) {
|
|
3028
3336
|
writeLine(` ${colors.red}${icons.error}${RESET} Docker compose files not found`);
|
|
3029
3337
|
writeLine(` ${colors.dim}This shouldn't happen if you installed via npm.${RESET}`);
|
|
@@ -3031,20 +3339,20 @@ async function stackInitCommand() {
|
|
|
3031
3339
|
writeLine();
|
|
3032
3340
|
return;
|
|
3033
3341
|
}
|
|
3034
|
-
if (composeDir !== targetDir && !
|
|
3342
|
+
if (composeDir !== targetDir && !existsSync7(targetDir)) {
|
|
3035
3343
|
writeLine(` ${colors.cyan}${icons.progress}${RESET} Copying docker files to ${colors.dim}~/.squads/docker${RESET}`);
|
|
3036
3344
|
try {
|
|
3037
|
-
|
|
3038
|
-
|
|
3345
|
+
mkdirSync4(SQUADS_DATA_DIR, { recursive: true });
|
|
3346
|
+
mkdirSync4(targetDir, { recursive: true });
|
|
3039
3347
|
const filesToCopy = [
|
|
3040
3348
|
"docker-compose.yml",
|
|
3041
3349
|
"docker-compose.engram.yml",
|
|
3042
3350
|
".env.example"
|
|
3043
3351
|
];
|
|
3044
3352
|
for (const file of filesToCopy) {
|
|
3045
|
-
const src =
|
|
3046
|
-
const dst =
|
|
3047
|
-
if (
|
|
3353
|
+
const src = join7(composeDir, file);
|
|
3354
|
+
const dst = join7(targetDir, file);
|
|
3355
|
+
if (existsSync7(src)) {
|
|
3048
3356
|
copyFileSync(src, dst);
|
|
3049
3357
|
}
|
|
3050
3358
|
}
|
|
@@ -3053,7 +3361,7 @@ async function stackInitCommand() {
|
|
|
3053
3361
|
} catch {
|
|
3054
3362
|
writeLine(` ${colors.yellow}${icons.warning}${RESET} Could not copy files, using source location`);
|
|
3055
3363
|
}
|
|
3056
|
-
} else if (
|
|
3364
|
+
} else if (existsSync7(targetDir)) {
|
|
3057
3365
|
composeDir = targetDir;
|
|
3058
3366
|
writeLine(` ${colors.green}${icons.success}${RESET} Using ${colors.dim}~/.squads/docker${RESET}`);
|
|
3059
3367
|
} else {
|
|
@@ -3061,10 +3369,10 @@ async function stackInitCommand() {
|
|
|
3061
3369
|
}
|
|
3062
3370
|
writeLine();
|
|
3063
3371
|
writeLine(` ${bold}Step 3: Environment Configuration${RESET}`);
|
|
3064
|
-
const envPath =
|
|
3065
|
-
const envExamplePath =
|
|
3066
|
-
if (!
|
|
3067
|
-
if (
|
|
3372
|
+
const envPath = join7(composeDir, ".env");
|
|
3373
|
+
const envExamplePath = join7(composeDir, ".env.example");
|
|
3374
|
+
if (!existsSync7(envPath)) {
|
|
3375
|
+
if (existsSync7(envExamplePath)) {
|
|
3068
3376
|
copyFileSync(envExamplePath, envPath);
|
|
3069
3377
|
writeLine(` ${colors.cyan}${icons.progress}${RESET} Created .env from template`);
|
|
3070
3378
|
} else {
|
|
@@ -3093,13 +3401,13 @@ LANGFUSE_PORT=3100
|
|
|
3093
3401
|
OTEL_PORT=4318
|
|
3094
3402
|
BRIDGE_PORT=8088
|
|
3095
3403
|
`;
|
|
3096
|
-
|
|
3404
|
+
writeFileSync5(envPath, minimalEnv);
|
|
3097
3405
|
writeLine(` ${colors.cyan}${icons.progress}${RESET} Created default .env`);
|
|
3098
3406
|
}
|
|
3099
3407
|
} else {
|
|
3100
3408
|
writeLine(` ${colors.green}${icons.success}${RESET} .env exists`);
|
|
3101
3409
|
}
|
|
3102
|
-
const envContent =
|
|
3410
|
+
const envContent = readFileSync5(envPath, "utf-8");
|
|
3103
3411
|
const missingSecrets = [];
|
|
3104
3412
|
const llmProvider = envContent.match(/LLM_PROVIDER=(\w+)/)?.[1] || "ollama";
|
|
3105
3413
|
if (llmProvider === "openai") {
|
|
@@ -3779,8 +4087,8 @@ async function memoryExtractCommand(options = {}) {
|
|
|
3779
4087
|
|
|
3780
4088
|
// src/commands/sync.ts
|
|
3781
4089
|
import { execSync as execSync4 } from "child_process";
|
|
3782
|
-
import { existsSync as
|
|
3783
|
-
import { join as
|
|
4090
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, readdirSync as readdirSync2 } from "fs";
|
|
4091
|
+
import { join as join8 } from "path";
|
|
3784
4092
|
var PATH_TO_SQUAD = {
|
|
3785
4093
|
"squads-cli": "product",
|
|
3786
4094
|
"agents-squads-web": "website",
|
|
@@ -3808,15 +4116,15 @@ var MESSAGE_TO_SQUAD = {
|
|
|
3808
4116
|
"infra": "engineering"
|
|
3809
4117
|
};
|
|
3810
4118
|
function getLastSyncTime(memoryDir) {
|
|
3811
|
-
const syncFile =
|
|
3812
|
-
if (
|
|
3813
|
-
return
|
|
4119
|
+
const syncFile = join8(memoryDir, ".last-sync");
|
|
4120
|
+
if (existsSync8(syncFile)) {
|
|
4121
|
+
return readFileSync6(syncFile, "utf-8").trim();
|
|
3814
4122
|
}
|
|
3815
4123
|
return null;
|
|
3816
4124
|
}
|
|
3817
4125
|
function updateLastSyncTime(memoryDir) {
|
|
3818
|
-
const syncFile =
|
|
3819
|
-
|
|
4126
|
+
const syncFile = join8(memoryDir, ".last-sync");
|
|
4127
|
+
writeFileSync6(syncFile, (/* @__PURE__ */ new Date()).toISOString());
|
|
3820
4128
|
}
|
|
3821
4129
|
function getRecentCommits(since) {
|
|
3822
4130
|
const commits = [];
|
|
@@ -3888,22 +4196,22 @@ ${messages}
|
|
|
3888
4196
|
`;
|
|
3889
4197
|
}
|
|
3890
4198
|
function appendToSquadMemory(memoryDir, squad, summary) {
|
|
3891
|
-
const squadMemoryDir =
|
|
3892
|
-
if (!
|
|
3893
|
-
|
|
4199
|
+
const squadMemoryDir = join8(memoryDir, squad);
|
|
4200
|
+
if (!existsSync8(squadMemoryDir)) {
|
|
4201
|
+
mkdirSync5(squadMemoryDir, { recursive: true });
|
|
3894
4202
|
}
|
|
3895
4203
|
let agentDir;
|
|
3896
|
-
const existingDirs =
|
|
4204
|
+
const existingDirs = existsSync8(squadMemoryDir) ? readdirSync2(squadMemoryDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name) : [];
|
|
3897
4205
|
if (existingDirs.length > 0) {
|
|
3898
|
-
agentDir =
|
|
4206
|
+
agentDir = join8(squadMemoryDir, existingDirs[0]);
|
|
3899
4207
|
} else {
|
|
3900
|
-
agentDir =
|
|
3901
|
-
|
|
4208
|
+
agentDir = join8(squadMemoryDir, `${squad}-lead`);
|
|
4209
|
+
mkdirSync5(agentDir, { recursive: true });
|
|
3902
4210
|
}
|
|
3903
|
-
const statePath =
|
|
4211
|
+
const statePath = join8(agentDir, "state.md");
|
|
3904
4212
|
let content = "";
|
|
3905
|
-
if (
|
|
3906
|
-
content =
|
|
4213
|
+
if (existsSync8(statePath)) {
|
|
4214
|
+
content = readFileSync6(statePath, "utf-8");
|
|
3907
4215
|
} else {
|
|
3908
4216
|
content = `# ${squad} Squad - State
|
|
3909
4217
|
|
|
@@ -3915,7 +4223,7 @@ Updated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
|
|
|
3915
4223
|
`Updated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`
|
|
3916
4224
|
);
|
|
3917
4225
|
content += summary;
|
|
3918
|
-
|
|
4226
|
+
writeFileSync6(statePath, content);
|
|
3919
4227
|
return true;
|
|
3920
4228
|
}
|
|
3921
4229
|
function gitPullMemory() {
|
|
@@ -4192,28 +4500,28 @@ async function goalProgressCommand(squadName, goalIndex, progress2) {
|
|
|
4192
4500
|
}
|
|
4193
4501
|
|
|
4194
4502
|
// src/commands/feedback.ts
|
|
4195
|
-
import { readFileSync as
|
|
4196
|
-
import { join as
|
|
4503
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, existsSync as existsSync9, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
|
|
4504
|
+
import { join as join9, dirname as dirname4 } from "path";
|
|
4197
4505
|
function getFeedbackPath(squadName) {
|
|
4198
4506
|
const memoryDir = findMemoryDir();
|
|
4199
4507
|
if (!memoryDir) return null;
|
|
4200
4508
|
const squad = loadSquad(squadName);
|
|
4201
4509
|
const agentName = squad?.agents[0]?.name || `${squadName}-lead`;
|
|
4202
|
-
return
|
|
4510
|
+
return join9(memoryDir, squadName, agentName, "feedback.md");
|
|
4203
4511
|
}
|
|
4204
4512
|
function getOutputPath(squadName) {
|
|
4205
4513
|
const memoryDir = findMemoryDir();
|
|
4206
4514
|
if (!memoryDir) return null;
|
|
4207
4515
|
const squad = loadSquad(squadName);
|
|
4208
4516
|
const agentName = squad?.agents[0]?.name || `${squadName}-lead`;
|
|
4209
|
-
return
|
|
4517
|
+
return join9(memoryDir, squadName, agentName, "output.md");
|
|
4210
4518
|
}
|
|
4211
4519
|
function getLastExecution(squadName) {
|
|
4212
4520
|
const outputPath = getOutputPath(squadName);
|
|
4213
|
-
if (!outputPath || !
|
|
4521
|
+
if (!outputPath || !existsSync9(outputPath)) {
|
|
4214
4522
|
return null;
|
|
4215
4523
|
}
|
|
4216
|
-
const content =
|
|
4524
|
+
const content = readFileSync7(outputPath, "utf-8");
|
|
4217
4525
|
const lines = content.split("\n");
|
|
4218
4526
|
let date = "unknown";
|
|
4219
4527
|
let summary = lines.slice(0, 5).join("\n");
|
|
@@ -4265,9 +4573,9 @@ async function feedbackAddCommand(squadName, rating, feedback2, options) {
|
|
|
4265
4573
|
return;
|
|
4266
4574
|
}
|
|
4267
4575
|
const lastExec = getLastExecution(squadName);
|
|
4268
|
-
const dir =
|
|
4269
|
-
if (!
|
|
4270
|
-
|
|
4576
|
+
const dir = dirname4(feedbackPath);
|
|
4577
|
+
if (!existsSync9(dir)) {
|
|
4578
|
+
mkdirSync6(dir, { recursive: true });
|
|
4271
4579
|
}
|
|
4272
4580
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4273
4581
|
let entry = `
|
|
@@ -4295,15 +4603,15 @@ _Date: ${date}_
|
|
|
4295
4603
|
}
|
|
4296
4604
|
}
|
|
4297
4605
|
let existing = "";
|
|
4298
|
-
if (
|
|
4299
|
-
existing =
|
|
4606
|
+
if (existsSync9(feedbackPath)) {
|
|
4607
|
+
existing = readFileSync7(feedbackPath, "utf-8");
|
|
4300
4608
|
} else {
|
|
4301
4609
|
existing = `# ${squadName} - Feedback Log
|
|
4302
4610
|
|
|
4303
4611
|
> Execution feedback and learnings
|
|
4304
4612
|
`;
|
|
4305
4613
|
}
|
|
4306
|
-
|
|
4614
|
+
writeFileSync7(feedbackPath, existing + entry);
|
|
4307
4615
|
const stars = `${colors.yellow}${"\u2605".repeat(ratingNum)}${"\u2606".repeat(5 - ratingNum)}${RESET}`;
|
|
4308
4616
|
writeLine();
|
|
4309
4617
|
writeLine(` ${icons.success} Feedback recorded for ${colors.cyan}${squadName}${RESET}`);
|
|
@@ -4317,11 +4625,11 @@ _Date: ${date}_
|
|
|
4317
4625
|
async function feedbackShowCommand(squadName, options) {
|
|
4318
4626
|
await track(Events.CLI_FEEDBACK_SHOW, { squad: squadName });
|
|
4319
4627
|
const feedbackPath = getFeedbackPath(squadName);
|
|
4320
|
-
if (!feedbackPath || !
|
|
4628
|
+
if (!feedbackPath || !existsSync9(feedbackPath)) {
|
|
4321
4629
|
writeLine(` ${colors.yellow}No feedback recorded for ${squadName}${RESET}`);
|
|
4322
4630
|
return;
|
|
4323
4631
|
}
|
|
4324
|
-
const content =
|
|
4632
|
+
const content = readFileSync7(feedbackPath, "utf-8");
|
|
4325
4633
|
const entries = parseFeedbackHistory(content);
|
|
4326
4634
|
const limit = options.limit ? parseInt(options.limit) : 5;
|
|
4327
4635
|
const recent = entries.slice(-limit).reverse();
|
|
@@ -4366,10 +4674,10 @@ async function feedbackStatsCommand() {
|
|
|
4366
4674
|
writeLine(` ${colors.purple}${box.teeRight}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.teeLeft}${RESET}`);
|
|
4367
4675
|
for (const squad of squads) {
|
|
4368
4676
|
const feedbackPath = getFeedbackPath(squad);
|
|
4369
|
-
if (!feedbackPath || !
|
|
4677
|
+
if (!feedbackPath || !existsSync9(feedbackPath)) {
|
|
4370
4678
|
continue;
|
|
4371
4679
|
}
|
|
4372
|
-
const content =
|
|
4680
|
+
const content = readFileSync7(feedbackPath, "utf-8");
|
|
4373
4681
|
const entries = parseFeedbackHistory(content);
|
|
4374
4682
|
if (entries.length === 0) continue;
|
|
4375
4683
|
const avgRating = entries.reduce((sum, e) => sum + e.rating, 0) / entries.length;
|
|
@@ -4391,9 +4699,252 @@ async function feedbackStatsCommand() {
|
|
|
4391
4699
|
writeLine();
|
|
4392
4700
|
}
|
|
4393
4701
|
|
|
4702
|
+
// src/commands/learn.ts
|
|
4703
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
4704
|
+
import { join as join10, dirname as dirname5 } from "path";
|
|
4705
|
+
function parseLearnings(content) {
|
|
4706
|
+
const learnings = [];
|
|
4707
|
+
const sections = content.split(/\n---\n/).filter((s) => s.trim());
|
|
4708
|
+
for (const section of sections) {
|
|
4709
|
+
if (section.startsWith("#")) continue;
|
|
4710
|
+
const dateMatch = section.match(/_(\d{4}-\d{2}-\d{2})_/);
|
|
4711
|
+
const categoryMatch = section.match(/\*\*(\w+)\*\*:/);
|
|
4712
|
+
const tagsMatch = section.match(/Tags: `([^`]+)`/);
|
|
4713
|
+
const lines = section.split("\n").filter((l) => l.trim() && !l.startsWith("_") && !l.includes("Tags:"));
|
|
4714
|
+
const insight = lines.map((l) => l.replace(/^\*\*\w+\*\*:\s*/, "")).join(" ").trim();
|
|
4715
|
+
if (dateMatch && insight) {
|
|
4716
|
+
learnings.push({
|
|
4717
|
+
date: dateMatch[1],
|
|
4718
|
+
insight,
|
|
4719
|
+
category: categoryMatch?.[1]?.toLowerCase() || "tip",
|
|
4720
|
+
tags: tagsMatch?.[1]?.split(",").map((t) => t.trim()) || []
|
|
4721
|
+
});
|
|
4722
|
+
}
|
|
4723
|
+
}
|
|
4724
|
+
return learnings;
|
|
4725
|
+
}
|
|
4726
|
+
function formatLearning(learning) {
|
|
4727
|
+
const categoryEmoji = {
|
|
4728
|
+
success: "\u25CF",
|
|
4729
|
+
failure: "\u2717",
|
|
4730
|
+
pattern: "\u25C6",
|
|
4731
|
+
tip: "\u2192"
|
|
4732
|
+
};
|
|
4733
|
+
let entry = `
|
|
4734
|
+
---
|
|
4735
|
+
_${learning.date}_
|
|
4736
|
+
|
|
4737
|
+
`;
|
|
4738
|
+
entry += `${categoryEmoji[learning.category]} **${learning.category.charAt(0).toUpperCase() + learning.category.slice(1)}**: ${learning.insight}
|
|
4739
|
+
`;
|
|
4740
|
+
if (learning.tags.length > 0) {
|
|
4741
|
+
entry += `Tags: \`${learning.tags.join(", ")}\`
|
|
4742
|
+
`;
|
|
4743
|
+
}
|
|
4744
|
+
if (learning.context) {
|
|
4745
|
+
entry += `
|
|
4746
|
+
_Context: ${learning.context}_
|
|
4747
|
+
`;
|
|
4748
|
+
}
|
|
4749
|
+
return entry;
|
|
4750
|
+
}
|
|
4751
|
+
async function learnCommand(insight, options) {
|
|
4752
|
+
let squadName = options.squad || "general";
|
|
4753
|
+
if (options.squad) {
|
|
4754
|
+
const squadsDir = findSquadsDir();
|
|
4755
|
+
if (squadsDir) {
|
|
4756
|
+
const squads = listSquads(squadsDir);
|
|
4757
|
+
if (!squads.includes(options.squad)) {
|
|
4758
|
+
writeLine();
|
|
4759
|
+
writeLine(` ${colors.yellow}${icons.warning} Squad '${options.squad}' not found${RESET}`);
|
|
4760
|
+
writeLine(` ${colors.dim}Available: ${squads.join(", ")}${RESET}`);
|
|
4761
|
+
writeLine(` ${colors.dim}Using 'general' instead${RESET}`);
|
|
4762
|
+
squadName = "general";
|
|
4763
|
+
}
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
const tags = options.tags ? options.tags.split(",").map((t) => t.trim().toLowerCase()) : [];
|
|
4767
|
+
if (tags.length === 0) {
|
|
4768
|
+
const autoTags = extractTags(insight);
|
|
4769
|
+
tags.push(...autoTags);
|
|
4770
|
+
}
|
|
4771
|
+
const category = options.category || inferCategory(insight);
|
|
4772
|
+
const learning = {
|
|
4773
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
4774
|
+
insight,
|
|
4775
|
+
category,
|
|
4776
|
+
tags,
|
|
4777
|
+
squad: squadName,
|
|
4778
|
+
context: options.context
|
|
4779
|
+
};
|
|
4780
|
+
const memoryDir = findMemoryDir();
|
|
4781
|
+
if (!memoryDir) {
|
|
4782
|
+
writeLine(` ${colors.red}No .agents/memory directory found${RESET}`);
|
|
4783
|
+
writeLine(` ${colors.dim}Run 'squads init' first${RESET}`);
|
|
4784
|
+
return;
|
|
4785
|
+
}
|
|
4786
|
+
const learningsPath = join10(memoryDir, squadName, "shared", "learnings.md");
|
|
4787
|
+
const dir = dirname5(learningsPath);
|
|
4788
|
+
if (!existsSync10(dir)) {
|
|
4789
|
+
mkdirSync7(dir, { recursive: true });
|
|
4790
|
+
}
|
|
4791
|
+
let content = "";
|
|
4792
|
+
if (existsSync10(learningsPath)) {
|
|
4793
|
+
content = readFileSync8(learningsPath, "utf-8");
|
|
4794
|
+
} else {
|
|
4795
|
+
content = `# ${squadName} - Learnings
|
|
4796
|
+
|
|
4797
|
+
> Accumulated knowledge from sessions
|
|
4798
|
+
`;
|
|
4799
|
+
}
|
|
4800
|
+
content += formatLearning(learning);
|
|
4801
|
+
writeFileSync8(learningsPath, content);
|
|
4802
|
+
await track(Events.CLI_LEARN, {
|
|
4803
|
+
squad: squadName,
|
|
4804
|
+
category,
|
|
4805
|
+
tagCount: tags.length
|
|
4806
|
+
});
|
|
4807
|
+
writeLine();
|
|
4808
|
+
writeLine(` ${icons.success} Learning captured for ${colors.cyan}${squadName}${RESET}`);
|
|
4809
|
+
writeLine();
|
|
4810
|
+
writeLine(` ${colors.dim}Category:${RESET} ${category}`);
|
|
4811
|
+
if (tags.length > 0) {
|
|
4812
|
+
writeLine(` ${colors.dim}Tags:${RESET} ${tags.map((t) => `#${t}`).join(" ")}`);
|
|
4813
|
+
}
|
|
4814
|
+
writeLine();
|
|
4815
|
+
writeLine(` ${colors.green}${insight}${RESET}`);
|
|
4816
|
+
writeLine();
|
|
4817
|
+
}
|
|
4818
|
+
async function learnShowCommand(squadName, options) {
|
|
4819
|
+
const memoryDir = findMemoryDir();
|
|
4820
|
+
if (!memoryDir) {
|
|
4821
|
+
writeLine(` ${colors.red}No memory directory found${RESET}`);
|
|
4822
|
+
return;
|
|
4823
|
+
}
|
|
4824
|
+
const learningsPath = join10(memoryDir, squadName, "shared", "learnings.md");
|
|
4825
|
+
if (!existsSync10(learningsPath)) {
|
|
4826
|
+
writeLine(` ${colors.yellow}No learnings recorded for ${squadName}${RESET}`);
|
|
4827
|
+
writeLine(` ${colors.dim}Add one: squads learn "insight" --squad ${squadName}${RESET}`);
|
|
4828
|
+
return;
|
|
4829
|
+
}
|
|
4830
|
+
const content = readFileSync8(learningsPath, "utf-8");
|
|
4831
|
+
const learnings = parseLearnings(content);
|
|
4832
|
+
let filtered = learnings;
|
|
4833
|
+
if (options.category) {
|
|
4834
|
+
filtered = filtered.filter((l) => l.category === options.category);
|
|
4835
|
+
}
|
|
4836
|
+
if (options.tag) {
|
|
4837
|
+
const tag = options.tag.toLowerCase();
|
|
4838
|
+
filtered = filtered.filter((l) => l.tags.includes(tag));
|
|
4839
|
+
}
|
|
4840
|
+
const limit = options.limit ? parseInt(options.limit) : 10;
|
|
4841
|
+
const recent = filtered.slice(-limit).reverse();
|
|
4842
|
+
await track(Events.CLI_LEARN_SHOW, { squad: squadName });
|
|
4843
|
+
writeLine();
|
|
4844
|
+
writeLine(` ${gradient("squads")} ${colors.dim}learnings${RESET} ${colors.cyan}${squadName}${RESET}`);
|
|
4845
|
+
writeLine();
|
|
4846
|
+
if (recent.length === 0) {
|
|
4847
|
+
writeLine(` ${colors.dim}No learnings found${RESET}`);
|
|
4848
|
+
return;
|
|
4849
|
+
}
|
|
4850
|
+
writeLine(` ${colors.dim}Showing ${recent.length} of ${filtered.length} learnings${RESET}`);
|
|
4851
|
+
writeLine();
|
|
4852
|
+
const categoryIcon = {
|
|
4853
|
+
success: `${colors.green}${icons.success}${RESET}`,
|
|
4854
|
+
failure: `${colors.red}\u2717${RESET}`,
|
|
4855
|
+
pattern: `${colors.purple}\u25C6${RESET}`,
|
|
4856
|
+
tip: `${colors.cyan}\u2192${RESET}`
|
|
4857
|
+
};
|
|
4858
|
+
for (const learning of recent) {
|
|
4859
|
+
writeLine(` ${colors.dim}${learning.date}${RESET} ${categoryIcon[learning.category]} ${learning.insight}`);
|
|
4860
|
+
if (learning.tags.length > 0) {
|
|
4861
|
+
writeLine(` ${colors.dim}${learning.tags.map((t) => `#${t}`).join(" ")}${RESET}`);
|
|
4862
|
+
}
|
|
4863
|
+
}
|
|
4864
|
+
writeLine();
|
|
4865
|
+
}
|
|
4866
|
+
async function learnSearchCommand(query, options) {
|
|
4867
|
+
const memoryDir = findMemoryDir();
|
|
4868
|
+
if (!memoryDir) {
|
|
4869
|
+
writeLine(` ${colors.red}No memory directory found${RESET}`);
|
|
4870
|
+
return;
|
|
4871
|
+
}
|
|
4872
|
+
const squadsDir = findSquadsDir();
|
|
4873
|
+
const squads = squadsDir ? listSquads(squadsDir) : [];
|
|
4874
|
+
squads.push("general");
|
|
4875
|
+
const allLearnings = [];
|
|
4876
|
+
for (const squad of squads) {
|
|
4877
|
+
const learningsPath = join10(memoryDir, squad, "shared", "learnings.md");
|
|
4878
|
+
if (existsSync10(learningsPath)) {
|
|
4879
|
+
const content = readFileSync8(learningsPath, "utf-8");
|
|
4880
|
+
const learnings = parseLearnings(content);
|
|
4881
|
+
allLearnings.push(...learnings.map((l) => ({ ...l, squad })));
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
const queryLower = query.toLowerCase();
|
|
4885
|
+
const matches = allLearnings.filter(
|
|
4886
|
+
(l) => l.insight.toLowerCase().includes(queryLower) || l.tags.some((t) => t.includes(queryLower))
|
|
4887
|
+
);
|
|
4888
|
+
await track(Events.CLI_LEARN_SEARCH, { query, matches: matches.length });
|
|
4889
|
+
writeLine();
|
|
4890
|
+
writeLine(` ${gradient("squads")} ${colors.dim}search learnings${RESET} "${query}"`);
|
|
4891
|
+
writeLine();
|
|
4892
|
+
const limit = options.limit ? parseInt(options.limit) : 10;
|
|
4893
|
+
const limited = matches.slice(0, limit);
|
|
4894
|
+
if (limited.length === 0) {
|
|
4895
|
+
writeLine(` ${colors.dim}No learnings found matching "${query}"${RESET}`);
|
|
4896
|
+
return;
|
|
4897
|
+
}
|
|
4898
|
+
writeLine(` ${colors.dim}Found ${matches.length} matches${RESET}`);
|
|
4899
|
+
writeLine();
|
|
4900
|
+
const categoryIcon = {
|
|
4901
|
+
success: `${colors.green}${icons.success}${RESET}`,
|
|
4902
|
+
failure: `${colors.red}\u2717${RESET}`,
|
|
4903
|
+
pattern: `${colors.purple}\u25C6${RESET}`,
|
|
4904
|
+
tip: `${colors.cyan}\u2192${RESET}`
|
|
4905
|
+
};
|
|
4906
|
+
for (const learning of limited) {
|
|
4907
|
+
writeLine(` ${colors.cyan}${learning.squad}${RESET} ${categoryIcon[learning.category]} ${learning.insight}`);
|
|
4908
|
+
}
|
|
4909
|
+
writeLine();
|
|
4910
|
+
}
|
|
4911
|
+
function extractTags(insight) {
|
|
4912
|
+
const tags = [];
|
|
4913
|
+
const lowerInsight = insight.toLowerCase();
|
|
4914
|
+
const patterns = {
|
|
4915
|
+
"auth": ["auth", "login", "token", "session", "password"],
|
|
4916
|
+
"api": ["api", "endpoint", "request", "response", "rest"],
|
|
4917
|
+
"bug": ["bug", "fix", "error", "issue", "crash"],
|
|
4918
|
+
"perf": ["performance", "slow", "fast", "optimize", "cache"],
|
|
4919
|
+
"ux": ["user", "ui", "interface", "design", "experience"],
|
|
4920
|
+
"test": ["test", "spec", "coverage", "assert"],
|
|
4921
|
+
"db": ["database", "query", "sql", "postgres", "redis"],
|
|
4922
|
+
"deploy": ["deploy", "release", "production", "staging"]
|
|
4923
|
+
};
|
|
4924
|
+
for (const [tag, keywords] of Object.entries(patterns)) {
|
|
4925
|
+
if (keywords.some((kw) => lowerInsight.includes(kw))) {
|
|
4926
|
+
tags.push(tag);
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
return tags.slice(0, 3);
|
|
4930
|
+
}
|
|
4931
|
+
function inferCategory(insight) {
|
|
4932
|
+
const lower = insight.toLowerCase();
|
|
4933
|
+
if (lower.includes("worked") || lower.includes("success") || lower.includes("fixed")) {
|
|
4934
|
+
return "success";
|
|
4935
|
+
}
|
|
4936
|
+
if (lower.includes("failed") || lower.includes("error") || lower.includes("didn't work")) {
|
|
4937
|
+
return "failure";
|
|
4938
|
+
}
|
|
4939
|
+
if (lower.includes("pattern") || lower.includes("always") || lower.includes("whenever")) {
|
|
4940
|
+
return "pattern";
|
|
4941
|
+
}
|
|
4942
|
+
return "tip";
|
|
4943
|
+
}
|
|
4944
|
+
|
|
4394
4945
|
// src/commands/dashboard.ts
|
|
4395
|
-
import { readdirSync as readdirSync4, existsSync as
|
|
4396
|
-
import { join as
|
|
4946
|
+
import { readdirSync as readdirSync4, existsSync as existsSync11, statSync as statSync2 } from "fs";
|
|
4947
|
+
import { join as join11 } from "path";
|
|
4397
4948
|
|
|
4398
4949
|
// src/lib/providers.ts
|
|
4399
4950
|
var PROVIDERS = {
|
|
@@ -5252,16 +5803,16 @@ async function closeDatabase() {
|
|
|
5252
5803
|
function getLastActivityDate(squadName) {
|
|
5253
5804
|
const memoryDir = findMemoryDir();
|
|
5254
5805
|
if (!memoryDir) return "unknown";
|
|
5255
|
-
const squadMemory =
|
|
5256
|
-
if (!
|
|
5806
|
+
const squadMemory = join11(memoryDir, squadName);
|
|
5807
|
+
if (!existsSync11(squadMemory)) return "\u2014";
|
|
5257
5808
|
let latestTime = 0;
|
|
5258
5809
|
try {
|
|
5259
5810
|
const agents = readdirSync4(squadMemory, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5260
5811
|
for (const agent of agents) {
|
|
5261
|
-
const agentPath =
|
|
5812
|
+
const agentPath = join11(squadMemory, agent.name);
|
|
5262
5813
|
const files = readdirSync4(agentPath).filter((f) => f.endsWith(".md"));
|
|
5263
5814
|
for (const file of files) {
|
|
5264
|
-
const filePath =
|
|
5815
|
+
const filePath = join11(agentPath, file);
|
|
5265
5816
|
const stats = statSync2(filePath);
|
|
5266
5817
|
if (stats.mtimeMs > latestTime) {
|
|
5267
5818
|
latestTime = stats.mtimeMs;
|
|
@@ -5508,11 +6059,11 @@ async function dashboardCommand(options = {}) {
|
|
|
5508
6059
|
await closeDatabase();
|
|
5509
6060
|
}
|
|
5510
6061
|
function findAgentsSquadsDir() {
|
|
5511
|
-
const parentDir =
|
|
5512
|
-
if (
|
|
6062
|
+
const parentDir = join11(process.cwd(), "..");
|
|
6063
|
+
if (existsSync11(join11(parentDir, "hq"))) {
|
|
5513
6064
|
return parentDir;
|
|
5514
6065
|
}
|
|
5515
|
-
if (
|
|
6066
|
+
if (existsSync11(join11(process.cwd(), ".git"))) {
|
|
5516
6067
|
return process.cwd();
|
|
5517
6068
|
}
|
|
5518
6069
|
return null;
|
|
@@ -6151,7 +6702,7 @@ function executeClaudePrompt(prompt2) {
|
|
|
6151
6702
|
// src/commands/open-issues.ts
|
|
6152
6703
|
import { execSync as execSync7, spawn as spawn5 } from "child_process";
|
|
6153
6704
|
import { readdirSync as readdirSync5 } from "fs";
|
|
6154
|
-
import { join as
|
|
6705
|
+
import { join as join12 } from "path";
|
|
6155
6706
|
import ora4 from "ora";
|
|
6156
6707
|
var ISSUE_FINDER_PATTERNS = [
|
|
6157
6708
|
"*-eval.md",
|
|
@@ -6209,7 +6760,7 @@ function findEvalAgents(squadsDir, filterSquad) {
|
|
|
6209
6760
|
const agents = [];
|
|
6210
6761
|
const squads = readdirSync5(squadsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).filter((d) => !filterSquad || d.name === filterSquad).map((d) => d.name);
|
|
6211
6762
|
for (const squad of squads) {
|
|
6212
|
-
const squadPath =
|
|
6763
|
+
const squadPath = join12(squadsDir, squad);
|
|
6213
6764
|
const files = readdirSync5(squadPath).filter((f) => f.endsWith(".md"));
|
|
6214
6765
|
for (const file of files) {
|
|
6215
6766
|
const isEval = ISSUE_FINDER_PATTERNS.some((pattern) => {
|
|
@@ -6220,7 +6771,7 @@ function findEvalAgents(squadsDir, filterSquad) {
|
|
|
6220
6771
|
agents.push({
|
|
6221
6772
|
name: file,
|
|
6222
6773
|
squad,
|
|
6223
|
-
path:
|
|
6774
|
+
path: join12(squadPath, file)
|
|
6224
6775
|
});
|
|
6225
6776
|
}
|
|
6226
6777
|
}
|
|
@@ -6330,8 +6881,8 @@ import open from "open";
|
|
|
6330
6881
|
|
|
6331
6882
|
// src/lib/auth.ts
|
|
6332
6883
|
import { createClient } from "@supabase/supabase-js";
|
|
6333
|
-
import { existsSync as
|
|
6334
|
-
import { join as
|
|
6884
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8 } from "fs";
|
|
6885
|
+
import { join as join13 } from "path";
|
|
6335
6886
|
import { homedir as homedir3 } from "os";
|
|
6336
6887
|
import "open";
|
|
6337
6888
|
import http from "http";
|
|
@@ -6361,8 +6912,8 @@ var PERSONAL_DOMAINS = [
|
|
|
6361
6912
|
"tutanota.com",
|
|
6362
6913
|
"hey.com"
|
|
6363
6914
|
];
|
|
6364
|
-
var AUTH_DIR =
|
|
6365
|
-
var AUTH_PATH =
|
|
6915
|
+
var AUTH_DIR = join13(homedir3(), ".squads-cli");
|
|
6916
|
+
var AUTH_PATH = join13(AUTH_DIR, "auth.json");
|
|
6366
6917
|
function isPersonalEmail(email) {
|
|
6367
6918
|
const domain = email.split("@")[1]?.toLowerCase();
|
|
6368
6919
|
return PERSONAL_DOMAINS.includes(domain);
|
|
@@ -6371,22 +6922,22 @@ function getEmailDomain(email) {
|
|
|
6371
6922
|
return email.split("@")[1]?.toLowerCase() || "";
|
|
6372
6923
|
}
|
|
6373
6924
|
function saveSession(session2) {
|
|
6374
|
-
if (!
|
|
6375
|
-
|
|
6925
|
+
if (!existsSync12(AUTH_DIR)) {
|
|
6926
|
+
mkdirSync8(AUTH_DIR, { recursive: true });
|
|
6376
6927
|
}
|
|
6377
|
-
|
|
6928
|
+
writeFileSync9(AUTH_PATH, JSON.stringify(session2, null, 2));
|
|
6378
6929
|
}
|
|
6379
6930
|
function loadSession() {
|
|
6380
|
-
if (!
|
|
6931
|
+
if (!existsSync12(AUTH_PATH)) return null;
|
|
6381
6932
|
try {
|
|
6382
|
-
return JSON.parse(
|
|
6933
|
+
return JSON.parse(readFileSync9(AUTH_PATH, "utf-8"));
|
|
6383
6934
|
} catch {
|
|
6384
6935
|
return null;
|
|
6385
6936
|
}
|
|
6386
6937
|
}
|
|
6387
6938
|
function clearSession() {
|
|
6388
|
-
if (
|
|
6389
|
-
|
|
6939
|
+
if (existsSync12(AUTH_PATH)) {
|
|
6940
|
+
writeFileSync9(AUTH_PATH, "");
|
|
6390
6941
|
}
|
|
6391
6942
|
}
|
|
6392
6943
|
function startAuthCallbackServer(port = 54321) {
|
|
@@ -6609,25 +7160,25 @@ async function updateCommand(options = {}) {
|
|
|
6609
7160
|
|
|
6610
7161
|
// src/commands/progress.ts
|
|
6611
7162
|
import { execSync as execSync8 } from "child_process";
|
|
6612
|
-
import { existsSync as
|
|
6613
|
-
import { join as
|
|
7163
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9 } from "fs";
|
|
7164
|
+
import { join as join14 } from "path";
|
|
6614
7165
|
function getTasksFilePath() {
|
|
6615
7166
|
const memoryDir = findMemoryDir();
|
|
6616
7167
|
if (!memoryDir) {
|
|
6617
7168
|
const cwd = process.cwd();
|
|
6618
|
-
const agentsDir =
|
|
6619
|
-
if (!
|
|
6620
|
-
|
|
7169
|
+
const agentsDir = join14(cwd, ".agents");
|
|
7170
|
+
if (!existsSync13(agentsDir)) {
|
|
7171
|
+
mkdirSync9(agentsDir, { recursive: true });
|
|
6621
7172
|
}
|
|
6622
|
-
return
|
|
7173
|
+
return join14(agentsDir, "tasks.json");
|
|
6623
7174
|
}
|
|
6624
|
-
return
|
|
7175
|
+
return join14(memoryDir, "..", "tasks.json");
|
|
6625
7176
|
}
|
|
6626
7177
|
function loadTasks() {
|
|
6627
7178
|
const tasksPath = getTasksFilePath();
|
|
6628
|
-
if (
|
|
7179
|
+
if (existsSync13(tasksPath)) {
|
|
6629
7180
|
try {
|
|
6630
|
-
return JSON.parse(
|
|
7181
|
+
return JSON.parse(readFileSync10(tasksPath, "utf-8"));
|
|
6631
7182
|
} catch {
|
|
6632
7183
|
return { tasks: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6633
7184
|
}
|
|
@@ -6637,7 +7188,7 @@ function loadTasks() {
|
|
|
6637
7188
|
function saveTasks(data) {
|
|
6638
7189
|
const tasksPath = getTasksFilePath();
|
|
6639
7190
|
data.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
6640
|
-
|
|
7191
|
+
writeFileSync10(tasksPath, JSON.stringify(data, null, 2));
|
|
6641
7192
|
}
|
|
6642
7193
|
function getRecentActivity() {
|
|
6643
7194
|
const activity = [];
|
|
@@ -6989,8 +7540,8 @@ async function resultsCommand(options = {}) {
|
|
|
6989
7540
|
}
|
|
6990
7541
|
|
|
6991
7542
|
// src/commands/history.ts
|
|
6992
|
-
import { existsSync as
|
|
6993
|
-
import { join as
|
|
7543
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
|
|
7544
|
+
import { join as join15 } from "path";
|
|
6994
7545
|
var BRIDGE_URL2 = process.env.SQUADS_BRIDGE_URL || "http://localhost:8088";
|
|
6995
7546
|
var FETCH_TIMEOUT_MS2 = 3e3;
|
|
6996
7547
|
async function fetchWithTimeout2(url, timeoutMs = FETCH_TIMEOUT_MS2) {
|
|
@@ -7035,12 +7586,12 @@ async function fetchFromBridge2(days, squad) {
|
|
|
7035
7586
|
function fetchFromLocal(days, squad) {
|
|
7036
7587
|
const executions = [];
|
|
7037
7588
|
const historyPaths = [
|
|
7038
|
-
|
|
7039
|
-
|
|
7589
|
+
join15(process.cwd(), ".agents/sessions/history.jsonl"),
|
|
7590
|
+
join15(process.env.HOME || "", "agents-squads/hq/.agents/sessions/history.jsonl")
|
|
7040
7591
|
];
|
|
7041
7592
|
let historyPath;
|
|
7042
7593
|
for (const path3 of historyPaths) {
|
|
7043
|
-
if (
|
|
7594
|
+
if (existsSync14(path3)) {
|
|
7044
7595
|
historyPath = path3;
|
|
7045
7596
|
break;
|
|
7046
7597
|
}
|
|
@@ -7049,7 +7600,7 @@ function fetchFromLocal(days, squad) {
|
|
|
7049
7600
|
return [];
|
|
7050
7601
|
}
|
|
7051
7602
|
try {
|
|
7052
|
-
const content =
|
|
7603
|
+
const content = readFileSync11(historyPath, "utf-8");
|
|
7053
7604
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
7054
7605
|
const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
7055
7606
|
for (const line of lines) {
|
|
@@ -7386,18 +7937,18 @@ async function healthCommand(options = {}) {
|
|
|
7386
7937
|
|
|
7387
7938
|
// src/commands/workers.ts
|
|
7388
7939
|
import { execSync as execSync10 } from "child_process";
|
|
7389
|
-
import { existsSync as
|
|
7390
|
-
import { join as
|
|
7940
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
7941
|
+
import { join as join16 } from "path";
|
|
7391
7942
|
function getTasksFilePath2() {
|
|
7392
7943
|
const memoryDir = findMemoryDir();
|
|
7393
7944
|
if (!memoryDir) return null;
|
|
7394
|
-
return
|
|
7945
|
+
return join16(memoryDir, "..", "tasks.json");
|
|
7395
7946
|
}
|
|
7396
7947
|
function loadActiveTasks() {
|
|
7397
7948
|
const tasksPath = getTasksFilePath2();
|
|
7398
|
-
if (!tasksPath || !
|
|
7949
|
+
if (!tasksPath || !existsSync15(tasksPath)) return [];
|
|
7399
7950
|
try {
|
|
7400
|
-
const data = JSON.parse(
|
|
7951
|
+
const data = JSON.parse(readFileSync12(tasksPath, "utf-8"));
|
|
7401
7952
|
return data.tasks?.filter((t) => t.status === "active") || [];
|
|
7402
7953
|
} catch {
|
|
7403
7954
|
return [];
|
|
@@ -7544,8 +8095,8 @@ function getElapsedTime2(startTime) {
|
|
|
7544
8095
|
}
|
|
7545
8096
|
|
|
7546
8097
|
// src/commands/context-feed.ts
|
|
7547
|
-
import { existsSync as
|
|
7548
|
-
import { join as
|
|
8098
|
+
import { existsSync as existsSync16, statSync as statSync3, readdirSync as readdirSync6, readFileSync as readFileSync13 } from "fs";
|
|
8099
|
+
import { join as join17 } from "path";
|
|
7549
8100
|
var BRIDGE_URL3 = process.env.SQUADS_BRIDGE_URL || "http://localhost:8088";
|
|
7550
8101
|
async function syncBriefToBridge(brief, sourcePath) {
|
|
7551
8102
|
try {
|
|
@@ -7574,10 +8125,10 @@ async function syncBriefToBridge(brief, sourcePath) {
|
|
|
7574
8125
|
}
|
|
7575
8126
|
function readBusinessBrief(squadsDir) {
|
|
7576
8127
|
if (!squadsDir) return void 0;
|
|
7577
|
-
const briefPath =
|
|
7578
|
-
if (!
|
|
8128
|
+
const briefPath = join17(squadsDir, "..", "BUSINESS_BRIEF.md");
|
|
8129
|
+
if (!existsSync16(briefPath)) return void 0;
|
|
7579
8130
|
try {
|
|
7580
|
-
const content =
|
|
8131
|
+
const content = readFileSync13(briefPath, "utf-8");
|
|
7581
8132
|
const brief = { raw: content };
|
|
7582
8133
|
const priorityMatch = content.match(/##\s*#1 Priority\s*\n+\*\*([^*]+)\*\*/);
|
|
7583
8134
|
if (priorityMatch) {
|
|
@@ -7627,7 +8178,7 @@ function readBusinessBrief(squadsDir) {
|
|
|
7627
8178
|
async function collectBriefingData(options) {
|
|
7628
8179
|
const squadsDir = findSquadsDir();
|
|
7629
8180
|
const memoryDir = findMemoryDir();
|
|
7630
|
-
const baseDir = squadsDir ?
|
|
8181
|
+
const baseDir = squadsDir ? join17(squadsDir, "..", "..", "..") : null;
|
|
7631
8182
|
const allSquads = squadsDir ? listSquads(squadsDir) : [];
|
|
7632
8183
|
if (options.squad && !allSquads.includes(options.squad)) {
|
|
7633
8184
|
return {
|
|
@@ -7667,14 +8218,14 @@ async function collectBriefingData(options) {
|
|
|
7667
8218
|
}
|
|
7668
8219
|
let lastActivity;
|
|
7669
8220
|
if (memoryDir) {
|
|
7670
|
-
const squadMemoryPath =
|
|
7671
|
-
if (
|
|
8221
|
+
const squadMemoryPath = join17(memoryDir, squadName);
|
|
8222
|
+
if (existsSync16(squadMemoryPath)) {
|
|
7672
8223
|
let mostRecent = 0;
|
|
7673
8224
|
try {
|
|
7674
8225
|
const walkDir = (dir) => {
|
|
7675
8226
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
7676
8227
|
for (const entry of entries) {
|
|
7677
|
-
const fullPath =
|
|
8228
|
+
const fullPath = join17(dir, entry.name);
|
|
7678
8229
|
if (entry.isDirectory()) {
|
|
7679
8230
|
walkDir(fullPath);
|
|
7680
8231
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -7754,7 +8305,7 @@ async function collectBriefingData(options) {
|
|
|
7754
8305
|
}
|
|
7755
8306
|
const brief = readBusinessBrief(squadsDir);
|
|
7756
8307
|
if (brief && squadsDir) {
|
|
7757
|
-
const briefPath =
|
|
8308
|
+
const briefPath = join17(squadsDir, "..", "BUSINESS_BRIEF.md");
|
|
7758
8309
|
syncBriefToBridge(brief, briefPath).catch(() => {
|
|
7759
8310
|
});
|
|
7760
8311
|
}
|
|
@@ -8306,7 +8857,7 @@ async function detectSquadCommand() {
|
|
|
8306
8857
|
|
|
8307
8858
|
// src/commands/trigger.ts
|
|
8308
8859
|
import chalk3 from "chalk";
|
|
8309
|
-
import { existsSync as
|
|
8860
|
+
import { existsSync as existsSync17 } from "fs";
|
|
8310
8861
|
var SCHEDULER_URL = process.env.SCHEDULER_URL || "http://localhost:8090";
|
|
8311
8862
|
async function fetchScheduler(path3, options) {
|
|
8312
8863
|
const res = await fetch(`${SCHEDULER_URL}${path3}`, {
|
|
@@ -8356,7 +8907,7 @@ async function syncTriggers() {
|
|
|
8356
8907
|
const hqPath = process.env.HQ_PATH || `${process.env.HOME}/agents-squads/hq`;
|
|
8357
8908
|
try {
|
|
8358
8909
|
const venvPython = `${hqPath}/squads-scheduler/.venv/bin/python`;
|
|
8359
|
-
const pythonCmd =
|
|
8910
|
+
const pythonCmd = existsSync17(venvPython) ? venvPython : "python3";
|
|
8360
8911
|
const output = execSync14(
|
|
8361
8912
|
`${pythonCmd} ${hqPath}/squads-scheduler/sync_triggers.py`,
|
|
8362
8913
|
{ encoding: "utf-8", cwd: hqPath }
|
|
@@ -8440,13 +8991,13 @@ function registerTriggerCommand(program2) {
|
|
|
8440
8991
|
|
|
8441
8992
|
// src/commands/skill.ts
|
|
8442
8993
|
import ora6 from "ora";
|
|
8443
|
-
import { existsSync as
|
|
8444
|
-
import { join as
|
|
8994
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync10, writeFileSync as writeFileSync11, readFileSync as readFileSync15 } from "fs";
|
|
8995
|
+
import { join as join19, basename as basename2, dirname as dirname6 } from "path";
|
|
8445
8996
|
|
|
8446
8997
|
// src/lib/anthropic.ts
|
|
8447
8998
|
import Anthropic from "@anthropic-ai/sdk";
|
|
8448
|
-
import { readFileSync as
|
|
8449
|
-
import { join as
|
|
8999
|
+
import { readFileSync as readFileSync14, readdirSync as readdirSync7 } from "fs";
|
|
9000
|
+
import { join as join18 } from "path";
|
|
8450
9001
|
var client = null;
|
|
8451
9002
|
function getClient() {
|
|
8452
9003
|
if (!client) {
|
|
@@ -8475,12 +9026,12 @@ function loadSkillFiles(skillPath) {
|
|
|
8475
9026
|
function walkDir(dir, prefix = "") {
|
|
8476
9027
|
const entries = readdirSync7(dir, { withFileTypes: true });
|
|
8477
9028
|
for (const entry of entries) {
|
|
8478
|
-
const fullPath =
|
|
9029
|
+
const fullPath = join18(dir, entry.name);
|
|
8479
9030
|
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
8480
9031
|
if (entry.isDirectory()) {
|
|
8481
9032
|
walkDir(fullPath, relativePath);
|
|
8482
9033
|
} else if (entry.isFile()) {
|
|
8483
|
-
const content =
|
|
9034
|
+
const content = readFileSync14(fullPath, "utf-8");
|
|
8484
9035
|
files.push({
|
|
8485
9036
|
name: relativePath,
|
|
8486
9037
|
content
|
|
@@ -8644,14 +9195,14 @@ async function skillUploadCommand(skillPath) {
|
|
|
8644
9195
|
writeLine();
|
|
8645
9196
|
return;
|
|
8646
9197
|
}
|
|
8647
|
-
const fullPath = skillPath.startsWith("/") ? skillPath :
|
|
8648
|
-
if (!
|
|
9198
|
+
const fullPath = skillPath.startsWith("/") ? skillPath : join19(process.cwd(), skillPath);
|
|
9199
|
+
if (!existsSync18(fullPath)) {
|
|
8649
9200
|
writeLine(` ${icons.error} ${colors.red}Directory not found: ${skillPath}${RESET}`);
|
|
8650
9201
|
writeLine();
|
|
8651
9202
|
return;
|
|
8652
9203
|
}
|
|
8653
|
-
const skillMdPath =
|
|
8654
|
-
if (!
|
|
9204
|
+
const skillMdPath = join19(fullPath, "SKILL.md");
|
|
9205
|
+
if (!existsSync18(skillMdPath)) {
|
|
8655
9206
|
writeLine(` ${icons.error} ${colors.red}SKILL.md not found in ${skillPath}${RESET}`);
|
|
8656
9207
|
writeLine();
|
|
8657
9208
|
writeLine(` ${colors.dim}Create a SKILL.md file or use:${RESET}`);
|
|
@@ -8762,7 +9313,7 @@ async function skillConvertCommand(agentPath, options) {
|
|
|
8762
9313
|
const [squad, agent] = agentPath.split("/");
|
|
8763
9314
|
squadName = squad;
|
|
8764
9315
|
agentName = agent.replace(".md", "");
|
|
8765
|
-
agentFilePath =
|
|
9316
|
+
agentFilePath = join19(squadsDir, squad, `${agentName}.md`);
|
|
8766
9317
|
} else {
|
|
8767
9318
|
agentName = agentPath.replace(".md", "");
|
|
8768
9319
|
const foundPath = findAgentFile(squadsDir, agentName);
|
|
@@ -8773,22 +9324,22 @@ async function skillConvertCommand(agentPath, options) {
|
|
|
8773
9324
|
return;
|
|
8774
9325
|
}
|
|
8775
9326
|
agentFilePath = foundPath;
|
|
8776
|
-
squadName = basename2(
|
|
9327
|
+
squadName = basename2(dirname6(agentFilePath));
|
|
8777
9328
|
}
|
|
8778
|
-
if (!
|
|
9329
|
+
if (!existsSync18(agentFilePath)) {
|
|
8779
9330
|
writeLine(` ${icons.error} ${colors.red}Agent file not found: ${agentFilePath}${RESET}`);
|
|
8780
9331
|
writeLine();
|
|
8781
9332
|
return;
|
|
8782
9333
|
}
|
|
8783
|
-
const agentContent =
|
|
9334
|
+
const agentContent = readFileSync15(agentFilePath, "utf-8");
|
|
8784
9335
|
const skillName = `${squadName}-${agentName}`;
|
|
8785
|
-
const outputDir = options.output ||
|
|
8786
|
-
if (!
|
|
8787
|
-
|
|
9336
|
+
const outputDir = options.output || join19(dirname6(squadsDir), "skills", skillName);
|
|
9337
|
+
if (!existsSync18(outputDir)) {
|
|
9338
|
+
mkdirSync10(outputDir, { recursive: true });
|
|
8788
9339
|
}
|
|
8789
9340
|
const skillMd = convertAgentToSkill(agentContent, squadName, agentName);
|
|
8790
|
-
const skillMdPath =
|
|
8791
|
-
|
|
9341
|
+
const skillMdPath = join19(outputDir, "SKILL.md");
|
|
9342
|
+
writeFileSync11(skillMdPath, skillMd);
|
|
8792
9343
|
writeLine(` ${icons.success} ${colors.green}Converted:${RESET} ${agentPath}`);
|
|
8793
9344
|
writeLine();
|
|
8794
9345
|
writeLine(` ${colors.dim}Output:${RESET} ${outputDir}`);
|
|
@@ -8805,8 +9356,8 @@ function findAgentFile(squadsDir, agentName) {
|
|
|
8805
9356
|
const { readdirSync: readdirSync9 } = __require("fs");
|
|
8806
9357
|
const squads = readdirSync9(squadsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("_")).map((d) => d.name);
|
|
8807
9358
|
for (const squad of squads) {
|
|
8808
|
-
const agentPath =
|
|
8809
|
-
if (
|
|
9359
|
+
const agentPath = join19(squadsDir, squad, `${agentName}.md`);
|
|
9360
|
+
if (existsSync18(agentPath)) {
|
|
8810
9361
|
return agentPath;
|
|
8811
9362
|
}
|
|
8812
9363
|
}
|
|
@@ -8847,8 +9398,8 @@ function formatBytes(bytes) {
|
|
|
8847
9398
|
}
|
|
8848
9399
|
|
|
8849
9400
|
// src/commands/permissions.ts
|
|
8850
|
-
import { readFileSync as
|
|
8851
|
-
import { join as
|
|
9401
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
9402
|
+
import { join as join20 } from "path";
|
|
8852
9403
|
function registerPermissionsCommand(program2) {
|
|
8853
9404
|
const permissions = program2.command("permissions").alias("perms").description("Manage and validate squad permissions");
|
|
8854
9405
|
permissions.command("show <squad>").description("Show permission context for a squad").action(permissionsShowCommand);
|
|
@@ -8870,8 +9421,8 @@ async function permissionsShowCommand(squadName) {
|
|
|
8870
9421
|
writeLine();
|
|
8871
9422
|
return;
|
|
8872
9423
|
}
|
|
8873
|
-
const squadFilePath =
|
|
8874
|
-
const squadContent =
|
|
9424
|
+
const squadFilePath = join20(squadsDir, squadName, "SQUAD.md");
|
|
9425
|
+
const squadContent = readFileSync16(squadFilePath, "utf-8");
|
|
8875
9426
|
const context = buildContextFromSquad(squadName, squadContent);
|
|
8876
9427
|
const defaults = getDefaultContext(squadName);
|
|
8877
9428
|
const isDefault = JSON.stringify(context.permissions) === JSON.stringify(defaults.permissions);
|
|
@@ -8965,8 +9516,8 @@ async function permissionsCheckCommand(squadName, options) {
|
|
|
8965
9516
|
writeLine();
|
|
8966
9517
|
return;
|
|
8967
9518
|
}
|
|
8968
|
-
const squadFilePath =
|
|
8969
|
-
const squadContent =
|
|
9519
|
+
const squadFilePath = join20(squadsDir, squadName, "SQUAD.md");
|
|
9520
|
+
const squadContent = readFileSync16(squadFilePath, "utf-8");
|
|
8970
9521
|
const context = buildContextFromSquad(squadName, squadContent, options.agent);
|
|
8971
9522
|
const request = {
|
|
8972
9523
|
mcpServers: options.mcp,
|
|
@@ -9153,6 +9704,77 @@ async function contextListCommand(options = {}) {
|
|
|
9153
9704
|
writeLine(` ${colors.purple}${box.bottomLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.bottomRight}${RESET}`);
|
|
9154
9705
|
writeLine();
|
|
9155
9706
|
}
|
|
9707
|
+
async function contextActivateCommand(squadName, options = {}) {
|
|
9708
|
+
await track(Events.CLI_CONTEXT, { squad: squadName, action: "activate" });
|
|
9709
|
+
const squadsDir = findSquadsDir();
|
|
9710
|
+
if (!squadsDir) {
|
|
9711
|
+
writeLine(`${colors.red}No .agents/squads directory found${RESET}`);
|
|
9712
|
+
process.exit(1);
|
|
9713
|
+
}
|
|
9714
|
+
const squad = loadSquad(squadName);
|
|
9715
|
+
if (!squad) {
|
|
9716
|
+
writeLine(`${colors.red}Squad "${squadName}" not found.${RESET}`);
|
|
9717
|
+
process.exit(1);
|
|
9718
|
+
}
|
|
9719
|
+
const execContext = resolveExecutionContext(squad, options.force);
|
|
9720
|
+
if (options.json) {
|
|
9721
|
+
console.log(JSON.stringify(execContext, null, 2));
|
|
9722
|
+
return;
|
|
9723
|
+
}
|
|
9724
|
+
if (options.dryRun) {
|
|
9725
|
+
writeLine();
|
|
9726
|
+
writeLine(` ${gradient("squads")} ${colors.dim}context activate${RESET} ${colors.cyan}${squadName}${RESET} ${colors.yellow}(dry run)${RESET}`);
|
|
9727
|
+
writeLine();
|
|
9728
|
+
writeLine(` ${colors.dim}Would resolve:${RESET}`);
|
|
9729
|
+
writeLine();
|
|
9730
|
+
writeLine(` ${bold}MCP Config${RESET}`);
|
|
9731
|
+
writeLine(` Path: ${execContext.resolved.mcpConfigPath}`);
|
|
9732
|
+
writeLine(` Source: ${execContext.resolved.mcpSource}`);
|
|
9733
|
+
if (execContext.resolved.mcpServers.length > 0) {
|
|
9734
|
+
writeLine(` Servers: ${execContext.resolved.mcpServers.join(", ")}`);
|
|
9735
|
+
}
|
|
9736
|
+
if (execContext.resolved.skillPaths.length > 0) {
|
|
9737
|
+
writeLine();
|
|
9738
|
+
writeLine(` ${bold}Skills${RESET}`);
|
|
9739
|
+
for (const path3 of execContext.resolved.skillPaths) {
|
|
9740
|
+
writeLine(` ${colors.dim}${path3}${RESET}`);
|
|
9741
|
+
}
|
|
9742
|
+
}
|
|
9743
|
+
if (execContext.resolved.memoryPaths.length > 0) {
|
|
9744
|
+
writeLine();
|
|
9745
|
+
writeLine(` ${bold}Memory${RESET}`);
|
|
9746
|
+
for (const path3 of execContext.resolved.memoryPaths) {
|
|
9747
|
+
writeLine(` ${colors.dim}${path3}${RESET}`);
|
|
9748
|
+
}
|
|
9749
|
+
}
|
|
9750
|
+
writeLine();
|
|
9751
|
+
writeLine(` ${colors.dim}Run without --dry-run to generate config${RESET}`);
|
|
9752
|
+
writeLine();
|
|
9753
|
+
return;
|
|
9754
|
+
}
|
|
9755
|
+
writeLine();
|
|
9756
|
+
writeLine(` ${gradient("squads")} ${colors.dim}context activate${RESET} ${colors.cyan}${squadName}${RESET}`);
|
|
9757
|
+
writeLine();
|
|
9758
|
+
const sourceLabel = execContext.resolved.mcpSource === "generated" ? `${colors.green}generated${RESET}` : execContext.resolved.mcpSource === "user-override" ? `${colors.cyan}user override${RESET}` : `${colors.dim}fallback${RESET}`;
|
|
9759
|
+
writeLine(` ${icons.success} MCP config: ${sourceLabel}`);
|
|
9760
|
+
writeLine(` ${colors.dim}${execContext.resolved.mcpConfigPath}${RESET}`);
|
|
9761
|
+
if (execContext.resolved.mcpServers.length > 0) {
|
|
9762
|
+
writeLine(` ${colors.dim}Servers: ${execContext.resolved.mcpServers.join(", ")}${RESET}`);
|
|
9763
|
+
}
|
|
9764
|
+
if (execContext.resolved.skillPaths.length > 0) {
|
|
9765
|
+
writeLine(` ${icons.success} Skills: ${execContext.resolved.skillPaths.length} resolved`);
|
|
9766
|
+
}
|
|
9767
|
+
if (execContext.resolved.memoryPaths.length > 0) {
|
|
9768
|
+
writeLine(` ${icons.success} Memory: ${execContext.resolved.memoryPaths.length} files`);
|
|
9769
|
+
}
|
|
9770
|
+
writeLine();
|
|
9771
|
+
writeLine(` ${colors.dim}To use this context manually:${RESET}`);
|
|
9772
|
+
writeLine(` ${colors.dim}$${RESET} claude --mcp-config '${execContext.resolved.mcpConfigPath}'`);
|
|
9773
|
+
writeLine();
|
|
9774
|
+
writeLine(` ${colors.dim}Or run with squads:${RESET}`);
|
|
9775
|
+
writeLine(` ${colors.dim}$${RESET} squads run ${colors.cyan}${squadName}${RESET}`);
|
|
9776
|
+
writeLine();
|
|
9777
|
+
}
|
|
9156
9778
|
|
|
9157
9779
|
// src/commands/cost.ts
|
|
9158
9780
|
function getBudgetStatus(squadName, spent, dailyBudget, weeklyBudget) {
|
|
@@ -9399,8 +10021,8 @@ function createBudgetBar(percent, width = 10) {
|
|
|
9399
10021
|
}
|
|
9400
10022
|
|
|
9401
10023
|
// src/lib/executions.ts
|
|
9402
|
-
import { readFileSync as
|
|
9403
|
-
import { join as
|
|
10024
|
+
import { readFileSync as readFileSync17, existsSync as existsSync19, readdirSync as readdirSync8 } from "fs";
|
|
10025
|
+
import { join as join21 } from "path";
|
|
9404
10026
|
function parseExecutionEntry(content, squad, agent) {
|
|
9405
10027
|
const idMatch = content.match(/<!-- exec:(\S+) -->/);
|
|
9406
10028
|
if (!idMatch) return null;
|
|
@@ -9438,8 +10060,8 @@ function parseExecutionEntry(content, squad, agent) {
|
|
|
9438
10060
|
};
|
|
9439
10061
|
}
|
|
9440
10062
|
function parseExecutionLog(filePath, squad, agent) {
|
|
9441
|
-
if (!
|
|
9442
|
-
const content =
|
|
10063
|
+
if (!existsSync19(filePath)) return [];
|
|
10064
|
+
const content = readFileSync17(filePath, "utf-8");
|
|
9443
10065
|
const executions = [];
|
|
9444
10066
|
const entries = content.split(/\n---\n/);
|
|
9445
10067
|
for (const entry of entries) {
|
|
@@ -9477,11 +10099,11 @@ function listExecutions(options = {}) {
|
|
|
9477
10099
|
const squads = readdirSync8(memoryDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
9478
10100
|
for (const squad of squads) {
|
|
9479
10101
|
if (filterSquad && squad !== filterSquad) continue;
|
|
9480
|
-
const squadPath =
|
|
10102
|
+
const squadPath = join21(memoryDir, squad);
|
|
9481
10103
|
const agents = readdirSync8(squadPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
9482
10104
|
for (const agent of agents) {
|
|
9483
10105
|
if (filterAgent && agent !== filterAgent) continue;
|
|
9484
|
-
const logPath =
|
|
10106
|
+
const logPath = join21(squadPath, agent, "executions.md");
|
|
9485
10107
|
const agentExecutions = parseExecutionLog(logPath, squad, agent);
|
|
9486
10108
|
executions.push(...agentExecutions);
|
|
9487
10109
|
}
|
|
@@ -9731,12 +10353,12 @@ async function execStatsCommand(options = {}) {
|
|
|
9731
10353
|
// src/commands/tonight.ts
|
|
9732
10354
|
import ora7 from "ora";
|
|
9733
10355
|
import fs2 from "fs/promises";
|
|
9734
|
-
import path2, { dirname as
|
|
10356
|
+
import path2, { dirname as dirname7 } from "path";
|
|
9735
10357
|
import { execSync as execSync13, spawn as spawn7 } from "child_process";
|
|
9736
10358
|
function getProjectRoot2() {
|
|
9737
10359
|
const squadsDir = findSquadsDir();
|
|
9738
10360
|
if (squadsDir) {
|
|
9739
|
-
return
|
|
10361
|
+
return dirname7(dirname7(squadsDir));
|
|
9740
10362
|
}
|
|
9741
10363
|
return process.cwd();
|
|
9742
10364
|
}
|
|
@@ -10065,12 +10687,12 @@ process.stderr.on("error", (err) => {
|
|
|
10065
10687
|
throw err;
|
|
10066
10688
|
});
|
|
10067
10689
|
var envPaths = [
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10690
|
+
join22(process.cwd(), ".env"),
|
|
10691
|
+
join22(process.cwd(), "..", "hq", ".env"),
|
|
10692
|
+
join22(homedir4(), "agents-squads", "hq", ".env")
|
|
10071
10693
|
];
|
|
10072
10694
|
for (const envPath of envPaths) {
|
|
10073
|
-
if (
|
|
10695
|
+
if (existsSync20(envPath)) {
|
|
10074
10696
|
config({ path: envPath, quiet: true });
|
|
10075
10697
|
break;
|
|
10076
10698
|
}
|
|
@@ -10121,6 +10743,7 @@ program.command("dashboard").alias("dash").description("Show comprehensive goals
|
|
|
10121
10743
|
var env = program.command("env").description("View squad execution environment (MCP, skills, model, budget)");
|
|
10122
10744
|
env.command("show <squad>").description("Show execution environment for a squad").option("--json", "Output as JSON").action(contextShowCommand);
|
|
10123
10745
|
env.command("list").description("List execution environment for all squads").option("--json", "Output as JSON").action(contextListCommand);
|
|
10746
|
+
env.command("activate <squad>").description("Activate execution context for a squad (generates scoped MCP config)").option("-d, --dry-run", "Show what would be generated without writing files").option("-f, --force", "Force regeneration even if config exists").option("--json", "Output as JSON").action(contextActivateCommand);
|
|
10124
10747
|
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);
|
|
10125
10748
|
program.command("budget").description("Check budget status for a squad").argument("<squad>", "Squad to check").option("--json", "Output as JSON").action(budgetCheckCommand);
|
|
10126
10749
|
var exec2 = program.command("exec").description("View execution history and statistics");
|
|
@@ -10181,6 +10804,10 @@ var feedback = program.command("feedback").description("Record and view executio
|
|
|
10181
10804
|
feedback.command("add <squad> <rating> <feedback>").description("Add feedback for last execution (rating 1-5)").option("-l, --learning <learnings...>", "Learnings to extract").action(feedbackAddCommand);
|
|
10182
10805
|
feedback.command("show <squad>").description("Show feedback history").option("-n, --limit <n>", "Number of entries to show", "5").action(feedbackShowCommand);
|
|
10183
10806
|
feedback.command("stats").description("Show feedback summary across all squads").action(feedbackStatsCommand);
|
|
10807
|
+
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(learnCommand);
|
|
10808
|
+
var learn = program.command("learnings").description("View and search learnings");
|
|
10809
|
+
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(learnShowCommand);
|
|
10810
|
+
learn.command("search <query>").description("Search learnings across all squads").option("-n, --limit <n>", "Max results", "10").action(learnSearchCommand);
|
|
10184
10811
|
var sessions = program.command("sessions").description("Show active Claude Code sessions across squads").option("-v, --verbose", "Show session details").option("-j, --json", "Output as JSON").action(sessionsCommand);
|
|
10185
10812
|
sessions.command("history").description("Show session history and statistics").option("-d, --days <days>", "Days of history to show", "7").option("-s, --squad <squad>", "Filter by squad").option("-j, --json", "Output as JSON").action((options) => sessionsHistoryCommand({
|
|
10186
10813
|
days: parseInt(options.days, 10),
|
|
@@ -10191,8 +10818,8 @@ sessions.command("summary").description("Show pretty session summary (auto-detec
|
|
|
10191
10818
|
const { buildCurrentSessionSummary } = await import("./sessions-R4VWIGFR.js");
|
|
10192
10819
|
let data;
|
|
10193
10820
|
if (options.file) {
|
|
10194
|
-
const { readFileSync:
|
|
10195
|
-
data = JSON.parse(
|
|
10821
|
+
const { readFileSync: readFileSync18 } = await import("fs");
|
|
10822
|
+
data = JSON.parse(readFileSync18(options.file, "utf-8"));
|
|
10196
10823
|
} else if (options.data) {
|
|
10197
10824
|
data = JSON.parse(options.data);
|
|
10198
10825
|
} else if (!process.stdin.isTTY) {
|