squads-cli 0.4.9 → 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 +104 -2
- package/dist/cli.js +1098 -286
- 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";
|
|
@@ -388,7 +388,7 @@ function getActivitySparkline(basePath, days = 7) {
|
|
|
388
388
|
// src/lib/telemetry.ts
|
|
389
389
|
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
390
390
|
import { join as join2 } from "path";
|
|
391
|
-
import { homedir } from "os";
|
|
391
|
+
import { homedir, platform, release } from "os";
|
|
392
392
|
import { randomUUID } from "crypto";
|
|
393
393
|
var TELEMETRY_DIR = join2(homedir(), ".squads-cli");
|
|
394
394
|
var CONFIG_PATH = join2(TELEMETRY_DIR, "telemetry.json");
|
|
@@ -400,6 +400,20 @@ var TELEMETRY_ENDPOINT = Buffer.from(
|
|
|
400
400
|
var TELEMETRY_KEY = Buffer.from("c3FfdGVsX3YxXzdmOGE5YjJjM2Q0ZTVmNmE=", "base64").toString();
|
|
401
401
|
var eventQueue = [];
|
|
402
402
|
var flushScheduled = false;
|
|
403
|
+
var cachedSystemContext = null;
|
|
404
|
+
function getSystemContext() {
|
|
405
|
+
if (cachedSystemContext) return cachedSystemContext;
|
|
406
|
+
cachedSystemContext = {
|
|
407
|
+
os: platform(),
|
|
408
|
+
// darwin, linux, win32
|
|
409
|
+
osVersion: release(),
|
|
410
|
+
nodeVersion: process.version,
|
|
411
|
+
shell: process.env.SHELL?.split("/").pop() || process.env.ComSpec?.split("\\").pop(),
|
|
412
|
+
terminal: process.env.TERM_PROGRAM || void 0,
|
|
413
|
+
ci: process.env.CI === "true" ? "true" : void 0
|
|
414
|
+
};
|
|
415
|
+
return cachedSystemContext;
|
|
416
|
+
}
|
|
403
417
|
function ensureDir() {
|
|
404
418
|
if (!existsSync2(TELEMETRY_DIR)) {
|
|
405
419
|
mkdirSync(TELEMETRY_DIR, { recursive: true });
|
|
@@ -440,6 +454,7 @@ async function track(event, properties) {
|
|
|
440
454
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
441
455
|
properties: {
|
|
442
456
|
...properties,
|
|
457
|
+
...getSystemContext(),
|
|
443
458
|
anonymousId: config2.anonymousId,
|
|
444
459
|
cliVersion: process.env.npm_package_version || "unknown"
|
|
445
460
|
}
|
|
@@ -519,9 +534,18 @@ var Events = {
|
|
|
519
534
|
CLI_FEEDBACK_ADD: "cli.feedback.add",
|
|
520
535
|
CLI_FEEDBACK_SHOW: "cli.feedback.show",
|
|
521
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",
|
|
522
541
|
// Auth
|
|
523
542
|
CLI_LOGIN: "cli.login",
|
|
524
|
-
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"
|
|
525
549
|
};
|
|
526
550
|
var exitHandlerRegistered = false;
|
|
527
551
|
function registerExitHandler() {
|
|
@@ -837,8 +861,9 @@ async function initCommand(options) {
|
|
|
837
861
|
}
|
|
838
862
|
}
|
|
839
863
|
console.log();
|
|
840
|
-
if (hasMissingRequired) {
|
|
864
|
+
if (hasMissingRequired && !options.force) {
|
|
841
865
|
console.log(chalk.yellow(" Install missing tools to continue, then run squads init again."));
|
|
866
|
+
console.log(chalk.dim(" Or use --force to skip requirement checks."));
|
|
842
867
|
console.log();
|
|
843
868
|
track(Events.CLI_INIT, {
|
|
844
869
|
success: false,
|
|
@@ -867,8 +892,14 @@ async function initCommand(options) {
|
|
|
867
892
|
".agents/squads",
|
|
868
893
|
".agents/squads/demo",
|
|
869
894
|
".agents/memory",
|
|
895
|
+
".agents/memory/getting-started",
|
|
896
|
+
".agents/memory/general/shared",
|
|
897
|
+
// For cross-squad learnings
|
|
870
898
|
".agents/outputs",
|
|
871
|
-
".claude"
|
|
899
|
+
".claude",
|
|
900
|
+
".claude/skills",
|
|
901
|
+
".claude/skills/squads-workflow",
|
|
902
|
+
".claude/skills/squads-learn"
|
|
872
903
|
];
|
|
873
904
|
for (const dir of dirs) {
|
|
874
905
|
await fs.mkdir(path.join(cwd, dir), { recursive: true });
|
|
@@ -969,6 +1000,189 @@ Markdown report saved to .agents/outputs/demo/project-analysis.md
|
|
|
969
1000
|
path.join(cwd, ".agents/squads/demo/analyzer.md"),
|
|
970
1001
|
analyzerAgent
|
|
971
1002
|
);
|
|
1003
|
+
const squadsWorkflowSkill = `# Squads Workflow
|
|
1004
|
+
|
|
1005
|
+
Use this skill when working with squads-cli to maintain persistent memory, track goals, and coordinate work.
|
|
1006
|
+
|
|
1007
|
+
## Session Start
|
|
1008
|
+
|
|
1009
|
+
At session start, you'll see \`squads status\` output automatically. For complex tasks, run:
|
|
1010
|
+
|
|
1011
|
+
\`\`\`bash
|
|
1012
|
+
squads context # Get business context, goals, decisions
|
|
1013
|
+
squads memory query "<topic>" # Check what we already know
|
|
1014
|
+
\`\`\`
|
|
1015
|
+
|
|
1016
|
+
**Skip context loading for simple tasks** (typo fixes, quick questions).
|
|
1017
|
+
|
|
1018
|
+
## Core Commands
|
|
1019
|
+
|
|
1020
|
+
\`\`\`bash
|
|
1021
|
+
# Context & Status
|
|
1022
|
+
squads context # Business context for alignment
|
|
1023
|
+
squads status # Squad overview
|
|
1024
|
+
squads dash # Full dashboard
|
|
1025
|
+
|
|
1026
|
+
# Memory
|
|
1027
|
+
squads memory query "<topic>" # Search memory
|
|
1028
|
+
squads memory show <squad> # Squad's full memory
|
|
1029
|
+
|
|
1030
|
+
# Goals
|
|
1031
|
+
squads goal list # All active goals
|
|
1032
|
+
squads goal set <squad> "X" # Add a goal
|
|
1033
|
+
|
|
1034
|
+
# Running Agents
|
|
1035
|
+
squads run <squad> # Run all agents in squad
|
|
1036
|
+
squads run <squad>/<agent> # Run specific agent
|
|
1037
|
+
squads list # List all agents
|
|
1038
|
+
\`\`\`
|
|
1039
|
+
|
|
1040
|
+
## Workflow
|
|
1041
|
+
|
|
1042
|
+
### Before Research
|
|
1043
|
+
Always check memory first to avoid re-researching:
|
|
1044
|
+
\`\`\`bash
|
|
1045
|
+
squads memory query "topic"
|
|
1046
|
+
\`\`\`
|
|
1047
|
+
|
|
1048
|
+
### After Work
|
|
1049
|
+
Update memory with what you learned by editing:
|
|
1050
|
+
\`.agents/memory/<squad>/<agent>/state.md\`
|
|
1051
|
+
|
|
1052
|
+
### Commits
|
|
1053
|
+
Include goal attribution when relevant:
|
|
1054
|
+
\`\`\`
|
|
1055
|
+
feat: add user auth [goal:engineering/1]
|
|
1056
|
+
\`\`\`
|
|
1057
|
+
|
|
1058
|
+
## Agent Execution
|
|
1059
|
+
|
|
1060
|
+
When a task could be automated:
|
|
1061
|
+
1. Check if agent exists: \`squads list | grep <keyword>\`
|
|
1062
|
+
2. If yes: \`squads run <squad>/<agent>\`
|
|
1063
|
+
3. If no: Create agent in \`.agents/squads/<squad>/<name>.md\`
|
|
1064
|
+
|
|
1065
|
+
## Memory Locations
|
|
1066
|
+
|
|
1067
|
+
- \`.agents/memory/<squad>/<agent>/state.md\` - Current knowledge
|
|
1068
|
+
- \`.agents/memory/<squad>/<agent>/learnings.md\` - Insights over time
|
|
1069
|
+
|
|
1070
|
+
## Key Principle
|
|
1071
|
+
|
|
1072
|
+
**Memory is your cross-session brain.** Without it, every session starts fresh. With it, you build on previous work.
|
|
1073
|
+
`;
|
|
1074
|
+
await fs.writeFile(
|
|
1075
|
+
path.join(cwd, ".claude/skills/squads-workflow/instruction.md"),
|
|
1076
|
+
squadsWorkflowSkill
|
|
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
|
+
);
|
|
1128
|
+
const seedMemory = `# Getting Started with Squads
|
|
1129
|
+
|
|
1130
|
+
## What You've Set Up
|
|
1131
|
+
|
|
1132
|
+
- **Demo squad** ready to run (\`squads run demo\`)
|
|
1133
|
+
- **Memory system** for persistent context
|
|
1134
|
+
- **Hooks** that sync status and memory automatically
|
|
1135
|
+
|
|
1136
|
+
## Next Steps
|
|
1137
|
+
|
|
1138
|
+
1. Run the demo: \`squads run demo\`
|
|
1139
|
+
2. Check the dashboard: \`squads dash\`
|
|
1140
|
+
3. Create your first real squad in \`.agents/squads/\`
|
|
1141
|
+
|
|
1142
|
+
## Tips
|
|
1143
|
+
|
|
1144
|
+
- Check memory before researching: \`squads memory query "<topic>"\`
|
|
1145
|
+
- Use \`squads context\` for business alignment on complex tasks
|
|
1146
|
+
- Agents are reusable - if you do something twice, make it an agent
|
|
1147
|
+
`;
|
|
1148
|
+
await fs.writeFile(
|
|
1149
|
+
path.join(cwd, ".agents/memory/getting-started/state.md"),
|
|
1150
|
+
seedMemory
|
|
1151
|
+
);
|
|
1152
|
+
const businessBrief = `# Business Brief
|
|
1153
|
+
|
|
1154
|
+
## #1 Priority
|
|
1155
|
+
|
|
1156
|
+
**[Define your top priority here]**
|
|
1157
|
+
|
|
1158
|
+
## Runway
|
|
1159
|
+
|
|
1160
|
+
**Pressure**: LOW | MEDIUM | HIGH
|
|
1161
|
+
|
|
1162
|
+
## Current Focus
|
|
1163
|
+
|
|
1164
|
+
1. **[Focus area 1]** - Description
|
|
1165
|
+
2. **[Focus area 2]** - Description
|
|
1166
|
+
3. **[Focus area 3]** - Description
|
|
1167
|
+
|
|
1168
|
+
## Blockers
|
|
1169
|
+
|
|
1170
|
+
- None currently (or list blockers)
|
|
1171
|
+
|
|
1172
|
+
## Decision Framework
|
|
1173
|
+
|
|
1174
|
+
1. Does this drive the #1 priority?
|
|
1175
|
+
2. Is there a simpler approach?
|
|
1176
|
+
3. What's the opportunity cost?
|
|
1177
|
+
|
|
1178
|
+
---
|
|
1179
|
+
|
|
1180
|
+
*This file is read by \`squads context\` to provide business alignment.*
|
|
1181
|
+
`;
|
|
1182
|
+
await fs.writeFile(
|
|
1183
|
+
path.join(cwd, ".agents/BUSINESS_BRIEF.md"),
|
|
1184
|
+
businessBrief
|
|
1185
|
+
);
|
|
972
1186
|
const claudeSettings = {
|
|
973
1187
|
hooks: {
|
|
974
1188
|
SessionStart: [
|
|
@@ -987,7 +1201,7 @@ Markdown report saved to .agents/outputs/demo/project-analysis.md
|
|
|
987
1201
|
hooks: [
|
|
988
1202
|
{
|
|
989
1203
|
type: "command",
|
|
990
|
-
command:
|
|
1204
|
+
command: 'squads memory sync && echo "\\n\u{1F4A1} Capture learnings: squads learn \\"<what you learned>\\"\\n"',
|
|
991
1205
|
timeout: 15
|
|
992
1206
|
}
|
|
993
1207
|
]
|
|
@@ -1023,12 +1237,22 @@ Each **squad** is a team of **agents** (markdown prompts) that execute via Claud
|
|
|
1023
1237
|
|
|
1024
1238
|
## For Claude (READ THIS)
|
|
1025
1239
|
|
|
1026
|
-
|
|
1240
|
+
**Skill available**: Use \`/squads-workflow\` for detailed workflow guidance.
|
|
1241
|
+
|
|
1242
|
+
### Session Start
|
|
1027
1243
|
|
|
1028
|
-
|
|
1244
|
+
You'll see \`squads status\` automatically. For complex tasks, also run:
|
|
1029
1245
|
\`\`\`bash
|
|
1030
|
-
squads
|
|
1031
|
-
squads memory query "
|
|
1246
|
+
squads context # Business context, goals, decisions
|
|
1247
|
+
squads memory query "<topic>" # What we already know
|
|
1248
|
+
\`\`\`
|
|
1249
|
+
|
|
1250
|
+
**Skip for simple tasks** (typo fixes, quick questions).
|
|
1251
|
+
|
|
1252
|
+
### Before Research
|
|
1253
|
+
Always check memory first:
|
|
1254
|
+
\`\`\`bash
|
|
1255
|
+
squads memory query "<topic>"
|
|
1032
1256
|
\`\`\`
|
|
1033
1257
|
|
|
1034
1258
|
### Creating Agents
|
|
@@ -1071,10 +1295,12 @@ squads goal set <squad> "X" # Add a goal
|
|
|
1071
1295
|
| "What's the status?" | Run \`squads dash\` or \`squads status\` |
|
|
1072
1296
|
| "Run the X agent" | \`squads run <squad>/x\` |
|
|
1073
1297
|
| "Check memory" | \`squads memory query "<topic>"\` |
|
|
1298
|
+
| "Get context" | \`squads context\` |
|
|
1074
1299
|
|
|
1075
1300
|
## Quick Reference
|
|
1076
1301
|
|
|
1077
1302
|
\`\`\`bash
|
|
1303
|
+
squads context # Business context for alignment
|
|
1078
1304
|
squads status # Overview
|
|
1079
1305
|
squads dash # Full dashboard
|
|
1080
1306
|
squads run demo # Try demo squad
|
|
@@ -1137,9 +1363,12 @@ squads goal list # View goals
|
|
|
1137
1363
|
console.log(chalk.green.bold(" \u2713 Squads initialized!"));
|
|
1138
1364
|
console.log();
|
|
1139
1365
|
console.log(chalk.dim(" Created:"));
|
|
1140
|
-
console.log(chalk.dim(" \u2022 .agents/squads/demo/
|
|
1141
|
-
console.log(chalk.dim(" \u2022 .
|
|
1142
|
-
console.log(chalk.dim(" \u2022
|
|
1366
|
+
console.log(chalk.dim(" \u2022 .agents/squads/demo/ - Demo squad with 2 agents"));
|
|
1367
|
+
console.log(chalk.dim(" \u2022 .agents/BUSINESS_BRIEF - Business context template"));
|
|
1368
|
+
console.log(chalk.dim(" \u2022 .agents/memory/ - Seed memory + learnings"));
|
|
1369
|
+
console.log(chalk.dim(" \u2022 .claude/settings.json - Claude Code hooks"));
|
|
1370
|
+
console.log(chalk.dim(" \u2022 .claude/skills/ - Workflow + learn skills"));
|
|
1371
|
+
console.log(chalk.dim(" \u2022 CLAUDE.md - Agent instructions"));
|
|
1143
1372
|
console.log();
|
|
1144
1373
|
const email = await promptEmail();
|
|
1145
1374
|
if (email) {
|
|
@@ -1164,21 +1393,185 @@ squads goal list # View goals
|
|
|
1164
1393
|
// src/commands/run.ts
|
|
1165
1394
|
import ora2 from "ora";
|
|
1166
1395
|
import { spawn as spawn2 } from "child_process";
|
|
1167
|
-
import { join as
|
|
1168
|
-
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";
|
|
1169
1398
|
|
|
1170
1399
|
// src/lib/squad-parser.ts
|
|
1171
|
-
import { readFileSync as
|
|
1172
|
-
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";
|
|
1173
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
|
|
1174
1567
|
function findSquadsDir() {
|
|
1175
1568
|
let dir = process.cwd();
|
|
1176
1569
|
for (let i = 0; i < 5; i++) {
|
|
1177
|
-
const squadsPath =
|
|
1178
|
-
if (
|
|
1570
|
+
const squadsPath = join4(dir, ".agents", "squads");
|
|
1571
|
+
if (existsSync4(squadsPath)) {
|
|
1179
1572
|
return squadsPath;
|
|
1180
1573
|
}
|
|
1181
|
-
const parent =
|
|
1574
|
+
const parent = join4(dir, "..");
|
|
1182
1575
|
if (parent === dir) break;
|
|
1183
1576
|
dir = parent;
|
|
1184
1577
|
}
|
|
@@ -1187,14 +1580,14 @@ function findSquadsDir() {
|
|
|
1187
1580
|
function findProjectRoot() {
|
|
1188
1581
|
const squadsDir = findSquadsDir();
|
|
1189
1582
|
if (!squadsDir) return null;
|
|
1190
|
-
return
|
|
1583
|
+
return join4(squadsDir, "..", "..");
|
|
1191
1584
|
}
|
|
1192
1585
|
function hasLocalInfraConfig() {
|
|
1193
1586
|
const projectRoot = findProjectRoot();
|
|
1194
1587
|
if (!projectRoot) return false;
|
|
1195
|
-
const envPath =
|
|
1196
|
-
if (!
|
|
1197
|
-
const content =
|
|
1588
|
+
const envPath = join4(projectRoot, ".env");
|
|
1589
|
+
if (!existsSync4(envPath)) return false;
|
|
1590
|
+
const content = readFileSync3(envPath, "utf-8");
|
|
1198
1591
|
const infraKeys = ["LANGFUSE_", "SQUADS_BRIDGE", "SQUADS_POSTGRES", "SQUADS_REDIS"];
|
|
1199
1592
|
return infraKeys.some((key) => content.includes(key));
|
|
1200
1593
|
}
|
|
@@ -1203,8 +1596,8 @@ function listSquads(squadsDir) {
|
|
|
1203
1596
|
const entries = readdirSync(squadsDir, { withFileTypes: true });
|
|
1204
1597
|
for (const entry of entries) {
|
|
1205
1598
|
if (entry.isDirectory() && !entry.name.startsWith("_")) {
|
|
1206
|
-
const squadFile =
|
|
1207
|
-
if (
|
|
1599
|
+
const squadFile = join4(squadsDir, entry.name, "SQUAD.md");
|
|
1600
|
+
if (existsSync4(squadFile)) {
|
|
1208
1601
|
squads.push(entry.name);
|
|
1209
1602
|
}
|
|
1210
1603
|
}
|
|
@@ -1215,8 +1608,8 @@ function listAgents(squadsDir, squadName) {
|
|
|
1215
1608
|
const agents = [];
|
|
1216
1609
|
const dirs = squadName ? [squadName] : readdirSync(squadsDir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_")).map((e) => e.name);
|
|
1217
1610
|
for (const dir of dirs) {
|
|
1218
|
-
const squadPath =
|
|
1219
|
-
if (!
|
|
1611
|
+
const squadPath = join4(squadsDir, dir);
|
|
1612
|
+
if (!existsSync4(squadPath)) continue;
|
|
1220
1613
|
const files = readdirSync(squadPath);
|
|
1221
1614
|
for (const file of files) {
|
|
1222
1615
|
if (file.endsWith(".md") && file !== "SQUAD.md") {
|
|
@@ -1225,7 +1618,7 @@ function listAgents(squadsDir, squadName) {
|
|
|
1225
1618
|
name: agentName,
|
|
1226
1619
|
role: `Agent in ${dir}`,
|
|
1227
1620
|
trigger: "manual",
|
|
1228
|
-
filePath:
|
|
1621
|
+
filePath: join4(squadPath, file)
|
|
1229
1622
|
});
|
|
1230
1623
|
}
|
|
1231
1624
|
}
|
|
@@ -1233,7 +1626,7 @@ function listAgents(squadsDir, squadName) {
|
|
|
1233
1626
|
return agents;
|
|
1234
1627
|
}
|
|
1235
1628
|
function parseSquadFile(filePath) {
|
|
1236
|
-
const rawContent =
|
|
1629
|
+
const rawContent = readFileSync3(filePath, "utf-8");
|
|
1237
1630
|
const { data: frontmatter, content: bodyContent } = matter(rawContent);
|
|
1238
1631
|
const fm = frontmatter;
|
|
1239
1632
|
const lines = bodyContent.split("\n");
|
|
@@ -1354,20 +1747,20 @@ function parseSquadFile(filePath) {
|
|
|
1354
1747
|
function loadSquad(squadName) {
|
|
1355
1748
|
const squadsDir = findSquadsDir();
|
|
1356
1749
|
if (!squadsDir) return null;
|
|
1357
|
-
const squadFile =
|
|
1358
|
-
if (!
|
|
1750
|
+
const squadFile = join4(squadsDir, squadName, "SQUAD.md");
|
|
1751
|
+
if (!existsSync4(squadFile)) return null;
|
|
1359
1752
|
return parseSquadFile(squadFile);
|
|
1360
1753
|
}
|
|
1361
1754
|
function loadAgentDefinition(agentPath) {
|
|
1362
|
-
if (!
|
|
1363
|
-
return
|
|
1755
|
+
if (!existsSync4(agentPath)) return "";
|
|
1756
|
+
return readFileSync3(agentPath, "utf-8");
|
|
1364
1757
|
}
|
|
1365
1758
|
function addGoalToSquad(squadName, goal2) {
|
|
1366
1759
|
const squadsDir = findSquadsDir();
|
|
1367
1760
|
if (!squadsDir) return false;
|
|
1368
|
-
const squadFile =
|
|
1369
|
-
if (!
|
|
1370
|
-
let content =
|
|
1761
|
+
const squadFile = join4(squadsDir, squadName, "SQUAD.md");
|
|
1762
|
+
if (!existsSync4(squadFile)) return false;
|
|
1763
|
+
let content = readFileSync3(squadFile, "utf-8");
|
|
1371
1764
|
if (!content.includes("## Goals")) {
|
|
1372
1765
|
const insertPoint = content.indexOf("## Dependencies");
|
|
1373
1766
|
if (insertPoint > 0) {
|
|
@@ -1402,15 +1795,15 @@ function addGoalToSquad(squadName, goal2) {
|
|
|
1402
1795
|
- [ ] ${goal2}` + content.slice(headerEnd);
|
|
1403
1796
|
}
|
|
1404
1797
|
}
|
|
1405
|
-
|
|
1798
|
+
writeFileSync3(squadFile, content);
|
|
1406
1799
|
return true;
|
|
1407
1800
|
}
|
|
1408
1801
|
function updateGoalInSquad(squadName, goalIndex, updates) {
|
|
1409
1802
|
const squadsDir = findSquadsDir();
|
|
1410
1803
|
if (!squadsDir) return false;
|
|
1411
|
-
const squadFile =
|
|
1412
|
-
if (!
|
|
1413
|
-
const content =
|
|
1804
|
+
const squadFile = join4(squadsDir, squadName, "SQUAD.md");
|
|
1805
|
+
if (!existsSync4(squadFile)) return false;
|
|
1806
|
+
const content = readFileSync3(squadFile, "utf-8");
|
|
1414
1807
|
const lines = content.split("\n");
|
|
1415
1808
|
let currentSection = "";
|
|
1416
1809
|
let goalCount = 0;
|
|
@@ -1432,7 +1825,7 @@ function updateGoalInSquad(squadName, goalIndex, updates) {
|
|
|
1432
1825
|
}
|
|
1433
1826
|
}
|
|
1434
1827
|
lines[i] = newLine;
|
|
1435
|
-
|
|
1828
|
+
writeFileSync3(squadFile, lines.join("\n"));
|
|
1436
1829
|
return true;
|
|
1437
1830
|
}
|
|
1438
1831
|
goalCount++;
|
|
@@ -1441,6 +1834,84 @@ function updateGoalInSquad(squadName, goalIndex, updates) {
|
|
|
1441
1834
|
}
|
|
1442
1835
|
return false;
|
|
1443
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
|
+
}
|
|
1444
1915
|
|
|
1445
1916
|
// src/lib/permissions.ts
|
|
1446
1917
|
import { minimatch } from "minimatch";
|
|
@@ -1533,11 +2004,11 @@ function validateMcpServer(server, allow, deny) {
|
|
|
1533
2004
|
}
|
|
1534
2005
|
return null;
|
|
1535
2006
|
}
|
|
1536
|
-
function validateExecution(
|
|
2007
|
+
function validateExecution(context, request) {
|
|
1537
2008
|
const violations = [];
|
|
1538
2009
|
if (request.bashCommands) {
|
|
1539
2010
|
for (const cmd of request.bashCommands) {
|
|
1540
|
-
const violation = validateBashCommand(cmd,
|
|
2011
|
+
const violation = validateBashCommand(cmd, context.permissions.bash);
|
|
1541
2012
|
if (violation) {
|
|
1542
2013
|
violations.push(violation);
|
|
1543
2014
|
}
|
|
@@ -1545,7 +2016,7 @@ function validateExecution(context2, request) {
|
|
|
1545
2016
|
}
|
|
1546
2017
|
if (request.writePaths) {
|
|
1547
2018
|
for (const path3 of request.writePaths) {
|
|
1548
|
-
const violation = validateFilePath(path3,
|
|
2019
|
+
const violation = validateFilePath(path3, context.permissions.write, "write");
|
|
1549
2020
|
if (violation) {
|
|
1550
2021
|
violations.push(violation);
|
|
1551
2022
|
}
|
|
@@ -1553,7 +2024,7 @@ function validateExecution(context2, request) {
|
|
|
1553
2024
|
}
|
|
1554
2025
|
if (request.readPaths) {
|
|
1555
2026
|
for (const path3 of request.readPaths) {
|
|
1556
|
-
const violation = validateFilePath(path3,
|
|
2027
|
+
const violation = validateFilePath(path3, context.permissions.read, "read");
|
|
1557
2028
|
if (violation) {
|
|
1558
2029
|
violations.push(violation);
|
|
1559
2030
|
}
|
|
@@ -1563,8 +2034,8 @@ function validateExecution(context2, request) {
|
|
|
1563
2034
|
for (const server of request.mcpServers) {
|
|
1564
2035
|
const violation = validateMcpServer(
|
|
1565
2036
|
server,
|
|
1566
|
-
|
|
1567
|
-
|
|
2037
|
+
context.permissions.mcp.allow,
|
|
2038
|
+
context.permissions.mcp.deny
|
|
1568
2039
|
);
|
|
1569
2040
|
if (violation) {
|
|
1570
2041
|
violations.push(violation);
|
|
@@ -1572,11 +2043,11 @@ function validateExecution(context2, request) {
|
|
|
1572
2043
|
}
|
|
1573
2044
|
}
|
|
1574
2045
|
const hasErrors = violations.some((v) => v.severity === "error");
|
|
1575
|
-
const allowed =
|
|
2046
|
+
const allowed = context.permissions.mode !== "strict" || !hasErrors;
|
|
1576
2047
|
return {
|
|
1577
2048
|
allowed,
|
|
1578
2049
|
violations,
|
|
1579
|
-
mode:
|
|
2050
|
+
mode: context.permissions.mode
|
|
1580
2051
|
};
|
|
1581
2052
|
}
|
|
1582
2053
|
function formatViolations(result) {
|
|
@@ -1639,16 +2110,16 @@ function parsePermissionsYaml(content) {
|
|
|
1639
2110
|
return Object.keys(permissions).length > 0 ? permissions : null;
|
|
1640
2111
|
}
|
|
1641
2112
|
function buildContextFromSquad(squadName, squadContent, agentName) {
|
|
1642
|
-
const
|
|
2113
|
+
const context = getDefaultContext(squadName, agentName);
|
|
1643
2114
|
const parsed = parsePermissionsYaml(squadContent);
|
|
1644
2115
|
if (parsed) {
|
|
1645
|
-
if (parsed.mode)
|
|
1646
|
-
if (parsed.bash)
|
|
1647
|
-
if (parsed.write)
|
|
1648
|
-
if (parsed.read)
|
|
1649
|
-
if (parsed.mcp)
|
|
2116
|
+
if (parsed.mode) context.permissions.mode = parsed.mode;
|
|
2117
|
+
if (parsed.bash) context.permissions.bash = parsed.bash;
|
|
2118
|
+
if (parsed.write) context.permissions.write = parsed.write;
|
|
2119
|
+
if (parsed.read) context.permissions.read = parsed.read;
|
|
2120
|
+
if (parsed.mcp) context.permissions.mcp = parsed.mcp;
|
|
1650
2121
|
}
|
|
1651
|
-
return
|
|
2122
|
+
return context;
|
|
1652
2123
|
}
|
|
1653
2124
|
|
|
1654
2125
|
// src/commands/run.ts
|
|
@@ -1679,9 +2150,12 @@ function generateExecutionId() {
|
|
|
1679
2150
|
const random = Math.random().toString(36).substring(2, 8);
|
|
1680
2151
|
return `exec_${timestamp}_${random}`;
|
|
1681
2152
|
}
|
|
1682
|
-
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
|
+
}
|
|
1683
2157
|
const home = process.env.HOME || "";
|
|
1684
|
-
const configsDir =
|
|
2158
|
+
const configsDir = join5(home, ".claude", "mcp-configs");
|
|
1685
2159
|
const squadConfigs = {
|
|
1686
2160
|
website: "website.json",
|
|
1687
2161
|
// chrome-devtools, nano-banana
|
|
@@ -1696,12 +2170,12 @@ function selectMcpConfig(squadName) {
|
|
|
1696
2170
|
};
|
|
1697
2171
|
const configFile = squadConfigs[squadName.toLowerCase()];
|
|
1698
2172
|
if (configFile) {
|
|
1699
|
-
const configPath =
|
|
1700
|
-
if (
|
|
2173
|
+
const configPath = join5(configsDir, configFile);
|
|
2174
|
+
if (existsSync5(configPath)) {
|
|
1701
2175
|
return configPath;
|
|
1702
2176
|
}
|
|
1703
2177
|
}
|
|
1704
|
-
return
|
|
2178
|
+
return join5(home, ".claude.json");
|
|
1705
2179
|
}
|
|
1706
2180
|
function detectTaskType(agentName) {
|
|
1707
2181
|
const name = agentName.toLowerCase();
|
|
@@ -1717,12 +2191,12 @@ function detectTaskType(agentName) {
|
|
|
1717
2191
|
return "execution";
|
|
1718
2192
|
}
|
|
1719
2193
|
function ensureProjectTrusted(projectPath) {
|
|
1720
|
-
const configPath =
|
|
1721
|
-
if (!
|
|
2194
|
+
const configPath = join5(process.env.HOME || "", ".claude.json");
|
|
2195
|
+
if (!existsSync5(configPath)) {
|
|
1722
2196
|
return;
|
|
1723
2197
|
}
|
|
1724
2198
|
try {
|
|
1725
|
-
const config2 = JSON.parse(
|
|
2199
|
+
const config2 = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
1726
2200
|
if (!config2.projects) {
|
|
1727
2201
|
config2.projects = {};
|
|
1728
2202
|
}
|
|
@@ -1731,7 +2205,7 @@ function ensureProjectTrusted(projectPath) {
|
|
|
1731
2205
|
}
|
|
1732
2206
|
if (!config2.projects[projectPath].hasTrustDialogAccepted) {
|
|
1733
2207
|
config2.projects[projectPath].hasTrustDialogAccepted = true;
|
|
1734
|
-
|
|
2208
|
+
writeFileSync4(configPath, JSON.stringify(config2, null, 2));
|
|
1735
2209
|
}
|
|
1736
2210
|
} catch {
|
|
1737
2211
|
}
|
|
@@ -1739,25 +2213,25 @@ function ensureProjectTrusted(projectPath) {
|
|
|
1739
2213
|
function getProjectRoot() {
|
|
1740
2214
|
const squadsDir = findSquadsDir();
|
|
1741
2215
|
if (squadsDir) {
|
|
1742
|
-
return
|
|
2216
|
+
return dirname2(dirname2(squadsDir));
|
|
1743
2217
|
}
|
|
1744
2218
|
return process.cwd();
|
|
1745
2219
|
}
|
|
1746
2220
|
function getExecutionLogPath(squadName, agentName) {
|
|
1747
2221
|
const memoryDir = findMemoryDir();
|
|
1748
2222
|
if (!memoryDir) return null;
|
|
1749
|
-
return
|
|
2223
|
+
return join5(memoryDir, squadName, agentName, "executions.md");
|
|
1750
2224
|
}
|
|
1751
2225
|
function logExecution(record) {
|
|
1752
2226
|
const logPath = getExecutionLogPath(record.squadName, record.agentName);
|
|
1753
2227
|
if (!logPath) return;
|
|
1754
|
-
const dir =
|
|
1755
|
-
if (!
|
|
1756
|
-
|
|
2228
|
+
const dir = dirname2(logPath);
|
|
2229
|
+
if (!existsSync5(dir)) {
|
|
2230
|
+
mkdirSync3(dir, { recursive: true });
|
|
1757
2231
|
}
|
|
1758
2232
|
let content = "";
|
|
1759
|
-
if (
|
|
1760
|
-
content =
|
|
2233
|
+
if (existsSync5(logPath)) {
|
|
2234
|
+
content = readFileSync4(logPath, "utf-8").trimEnd();
|
|
1761
2235
|
} else {
|
|
1762
2236
|
content = `# ${record.squadName}/${record.agentName} - Execution Log`;
|
|
1763
2237
|
}
|
|
@@ -1770,12 +2244,12 @@ function logExecution(record) {
|
|
|
1770
2244
|
- Trigger: ${record.trigger || "manual"}
|
|
1771
2245
|
- Task Type: ${record.taskType || "execution"}
|
|
1772
2246
|
`;
|
|
1773
|
-
|
|
2247
|
+
writeFileSync4(logPath, content + entry);
|
|
1774
2248
|
}
|
|
1775
2249
|
function updateExecutionStatus(squadName, agentName, executionId, status, details) {
|
|
1776
2250
|
const logPath = getExecutionLogPath(squadName, agentName);
|
|
1777
|
-
if (!logPath || !
|
|
1778
|
-
let content =
|
|
2251
|
+
if (!logPath || !existsSync5(logPath)) return;
|
|
2252
|
+
let content = readFileSync4(logPath, "utf-8");
|
|
1779
2253
|
const endTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
1780
2254
|
const execMarker = `<!-- exec:${executionId} -->`;
|
|
1781
2255
|
const markerIndex = content.indexOf(execMarker);
|
|
@@ -1796,7 +2270,7 @@ function updateExecutionStatus(squadName, agentName, executionId, status, detail
|
|
|
1796
2270
|
- Error: ${details.error}`;
|
|
1797
2271
|
}
|
|
1798
2272
|
content = content.slice(0, entryStart) + updatedEntry + content.slice(entryEnd);
|
|
1799
|
-
|
|
2273
|
+
writeFileSync4(logPath, content);
|
|
1800
2274
|
}
|
|
1801
2275
|
function extractMcpServersFromDefinition(definition) {
|
|
1802
2276
|
const servers = /* @__PURE__ */ new Set();
|
|
@@ -1875,8 +2349,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
1875
2349
|
if (options.parallel) {
|
|
1876
2350
|
const agentFiles = squad.agents.map((a) => ({
|
|
1877
2351
|
name: a.name,
|
|
1878
|
-
path:
|
|
1879
|
-
})).filter((a) =>
|
|
2352
|
+
path: join5(squadsDir, squad.name, `${a.name}.md`)
|
|
2353
|
+
})).filter((a) => existsSync5(a.path));
|
|
1880
2354
|
if (agentFiles.length === 0) {
|
|
1881
2355
|
writeLine(` ${icons.error} ${colors.red}No agent files found${RESET}`);
|
|
1882
2356
|
return;
|
|
@@ -1912,8 +2386,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
1912
2386
|
writeLine();
|
|
1913
2387
|
for (let i = 0; i < pipeline.agents.length; i++) {
|
|
1914
2388
|
const agentName = pipeline.agents[i];
|
|
1915
|
-
const agentPath =
|
|
1916
|
-
if (
|
|
2389
|
+
const agentPath = join5(squadsDir, squad.name, `${agentName}.md`);
|
|
2390
|
+
if (existsSync5(agentPath)) {
|
|
1917
2391
|
writeLine(` ${colors.dim}[${i + 1}/${pipeline.agents.length}]${RESET}`);
|
|
1918
2392
|
await runAgent(agentName, agentPath, squad.name, options);
|
|
1919
2393
|
writeLine();
|
|
@@ -1923,8 +2397,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
1923
2397
|
}
|
|
1924
2398
|
} else {
|
|
1925
2399
|
if (options.agent) {
|
|
1926
|
-
const agentPath =
|
|
1927
|
-
if (
|
|
2400
|
+
const agentPath = join5(squadsDir, squad.name, `${options.agent}.md`);
|
|
2401
|
+
if (existsSync5(agentPath)) {
|
|
1928
2402
|
await runAgent(options.agent, agentPath, squad.name, options);
|
|
1929
2403
|
} else {
|
|
1930
2404
|
writeLine(` ${icons.error} ${colors.red}Agent ${options.agent} not found${RESET}`);
|
|
@@ -1935,8 +2409,8 @@ async function runSquad(squad, squadsDir, options) {
|
|
|
1935
2409
|
(a) => a.name.includes("lead") || a.trigger === "Manual"
|
|
1936
2410
|
);
|
|
1937
2411
|
if (orchestrator) {
|
|
1938
|
-
const agentPath =
|
|
1939
|
-
if (
|
|
2412
|
+
const agentPath = join5(squadsDir, squad.name, `${orchestrator.name}.md`);
|
|
2413
|
+
if (existsSync5(agentPath)) {
|
|
1940
2414
|
await runAgent(orchestrator.name, agentPath, squad.name, options);
|
|
1941
2415
|
}
|
|
1942
2416
|
} else {
|
|
@@ -1962,9 +2436,9 @@ async function runLeadMode(squad, squadsDir, options) {
|
|
|
1962
2436
|
if (!squad) return;
|
|
1963
2437
|
const agentFiles = squad.agents.map((a) => ({
|
|
1964
2438
|
name: a.name,
|
|
1965
|
-
path:
|
|
2439
|
+
path: join5(squadsDir, squad.name, `${a.name}.md`),
|
|
1966
2440
|
role: a.role || ""
|
|
1967
|
-
})).filter((a) =>
|
|
2441
|
+
})).filter((a) => existsSync5(a.path));
|
|
1968
2442
|
if (agentFiles.length === 0) {
|
|
1969
2443
|
writeLine(` ${icons.error} ${colors.red}No agent files found${RESET}`);
|
|
1970
2444
|
return;
|
|
@@ -2083,9 +2557,9 @@ async function runAgent(agentName, agentPath, squadName, options) {
|
|
|
2083
2557
|
}
|
|
2084
2558
|
const squadsDir = findSquadsDir();
|
|
2085
2559
|
if (squadsDir) {
|
|
2086
|
-
const squadFilePath =
|
|
2087
|
-
if (
|
|
2088
|
-
const squadContent =
|
|
2560
|
+
const squadFilePath = join5(squadsDir, squadName, "SQUAD.md");
|
|
2561
|
+
if (existsSync5(squadFilePath)) {
|
|
2562
|
+
const squadContent = readFileSync4(squadFilePath, "utf-8");
|
|
2089
2563
|
const permContext = buildContextFromSquad(squadName, squadContent, agentName);
|
|
2090
2564
|
const mcpServers = extractMcpServersFromDefinition(definition);
|
|
2091
2565
|
const execRequest = {
|
|
@@ -2194,7 +2668,8 @@ async function executeWithClaude(prompt2, verbose, _timeoutMinutes = 30, foregro
|
|
|
2194
2668
|
const agentMatch = prompt2.match(/(\w+) agent/);
|
|
2195
2669
|
const squadName = process.env.SQUADS_SQUAD || squadMatch?.[1] || "unknown";
|
|
2196
2670
|
const agentName = process.env.SQUADS_AGENT || agentMatch?.[1] || "unknown";
|
|
2197
|
-
const
|
|
2671
|
+
const squad = squadName !== "unknown" ? loadSquad(squadName) : null;
|
|
2672
|
+
const mcpConfigPath = selectMcpConfig(squadName, squad);
|
|
2198
2673
|
const execContext = {
|
|
2199
2674
|
squad: squadName,
|
|
2200
2675
|
agent: agentName,
|
|
@@ -2365,8 +2840,8 @@ async function listCommand(options) {
|
|
|
2365
2840
|
}
|
|
2366
2841
|
|
|
2367
2842
|
// src/commands/status.ts
|
|
2368
|
-
import { existsSync as
|
|
2369
|
-
import { join as
|
|
2843
|
+
import { existsSync as existsSync6, statSync } from "fs";
|
|
2844
|
+
import { join as join6 } from "path";
|
|
2370
2845
|
async function statusCommand(squadName, options = {}) {
|
|
2371
2846
|
await track(Events.CLI_STATUS, { squad: squadName || "all", verbose: options.verbose });
|
|
2372
2847
|
const squadsDir = findSquadsDir();
|
|
@@ -2419,8 +2894,8 @@ async function showOverallStatus(squadsDir, _options) {
|
|
|
2419
2894
|
let lastActivity = `${colors.dim}\u2014${RESET}`;
|
|
2420
2895
|
let activityColor = colors.dim;
|
|
2421
2896
|
if (memoryDir) {
|
|
2422
|
-
const squadMemoryPath =
|
|
2423
|
-
if (
|
|
2897
|
+
const squadMemoryPath = join6(memoryDir, squadName);
|
|
2898
|
+
if (existsSync6(squadMemoryPath)) {
|
|
2424
2899
|
const states = getSquadState(squadName);
|
|
2425
2900
|
memoryStatus = `${colors.green}${states.length} ${states.length === 1 ? "entry" : "entries"}${RESET}`;
|
|
2426
2901
|
let mostRecent = 0;
|
|
@@ -2522,14 +2997,14 @@ async function showSquadStatus(squadName, squadsDir, options) {
|
|
|
2522
2997
|
}
|
|
2523
2998
|
|
|
2524
2999
|
// src/commands/stack.ts
|
|
2525
|
-
import { existsSync as
|
|
2526
|
-
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";
|
|
2527
3002
|
import { homedir as homedir2 } from "os";
|
|
2528
3003
|
import { execSync as execSync3, spawn as spawn3 } from "child_process";
|
|
2529
3004
|
import { createInterface as createInterface2 } from "readline";
|
|
2530
3005
|
import { fileURLToPath } from "url";
|
|
2531
3006
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
2532
|
-
var __dirname2 =
|
|
3007
|
+
var __dirname2 = dirname3(__filename2);
|
|
2533
3008
|
var DEFAULT_CONFIG = {
|
|
2534
3009
|
SQUADS_DATABASE_URL: "postgresql://squads:squads@localhost:5433/squads",
|
|
2535
3010
|
SQUADS_BRIDGE_URL: "http://localhost:8088",
|
|
@@ -2538,8 +3013,8 @@ var DEFAULT_CONFIG = {
|
|
|
2538
3013
|
LANGFUSE_SECRET_KEY: "",
|
|
2539
3014
|
REDIS_URL: "redis://localhost:6379"
|
|
2540
3015
|
};
|
|
2541
|
-
var CONFIG_PATH2 =
|
|
2542
|
-
var SQUADS_DATA_DIR =
|
|
3016
|
+
var CONFIG_PATH2 = join7(homedir2(), ".squadsrc");
|
|
3017
|
+
var SQUADS_DATA_DIR = join7(homedir2(), ".squads");
|
|
2543
3018
|
var SERVICES = {
|
|
2544
3019
|
bridge: {
|
|
2545
3020
|
name: "Bridge API",
|
|
@@ -2673,25 +3148,25 @@ async function confirm2(question, defaultYes = true) {
|
|
|
2673
3148
|
function findPackageDockerDir() {
|
|
2674
3149
|
const candidates = [
|
|
2675
3150
|
// From npm package (relative to dist/commands/stack.js)
|
|
2676
|
-
|
|
3151
|
+
join7(__dirname2, "..", "..", "docker"),
|
|
2677
3152
|
// Local development
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
3153
|
+
join7(process.cwd(), "docker"),
|
|
3154
|
+
join7(process.cwd(), "..", "squads-cli", "docker"),
|
|
3155
|
+
join7(homedir2(), "agents-squads", "squads-cli", "docker")
|
|
2681
3156
|
];
|
|
2682
3157
|
for (const dir of candidates) {
|
|
2683
|
-
if (
|
|
3158
|
+
if (existsSync7(join7(dir, "docker-compose.yml"))) {
|
|
2684
3159
|
return dir;
|
|
2685
3160
|
}
|
|
2686
3161
|
}
|
|
2687
3162
|
return null;
|
|
2688
3163
|
}
|
|
2689
3164
|
function loadStackConfig() {
|
|
2690
|
-
if (!
|
|
3165
|
+
if (!existsSync7(CONFIG_PATH2)) {
|
|
2691
3166
|
return null;
|
|
2692
3167
|
}
|
|
2693
3168
|
try {
|
|
2694
|
-
const content =
|
|
3169
|
+
const content = readFileSync5(CONFIG_PATH2, "utf-8");
|
|
2695
3170
|
const config2 = {};
|
|
2696
3171
|
for (const line of content.split("\n")) {
|
|
2697
3172
|
const trimmed = line.trim();
|
|
@@ -2732,7 +3207,7 @@ function saveStackConfig(config2) {
|
|
|
2732
3207
|
"# To activate: source ~/.squadsrc",
|
|
2733
3208
|
""
|
|
2734
3209
|
];
|
|
2735
|
-
|
|
3210
|
+
writeFileSync5(CONFIG_PATH2, lines.join("\n"));
|
|
2736
3211
|
}
|
|
2737
3212
|
function applyStackConfig() {
|
|
2738
3213
|
const config2 = loadStackConfig();
|
|
@@ -2804,13 +3279,13 @@ async function checkService(url, timeout = 2e3) {
|
|
|
2804
3279
|
}
|
|
2805
3280
|
function getLangfuseKeysFromDockerEnv() {
|
|
2806
3281
|
const envPaths2 = [
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
3282
|
+
join7(process.cwd(), "docker", ".env"),
|
|
3283
|
+
join7(process.cwd(), "..", "squads-cli", "docker", ".env"),
|
|
3284
|
+
join7(homedir2(), "agents-squads", "squads-cli", "docker", ".env")
|
|
2810
3285
|
];
|
|
2811
3286
|
for (const envPath of envPaths2) {
|
|
2812
|
-
if (
|
|
2813
|
-
const content =
|
|
3287
|
+
if (existsSync7(envPath)) {
|
|
3288
|
+
const content = readFileSync5(envPath, "utf-8");
|
|
2814
3289
|
const publicMatch = content.match(/LANGFUSE_PUBLIC_KEY=(\S+)/);
|
|
2815
3290
|
const secretMatch = content.match(/LANGFUSE_SECRET_KEY=(\S+)/);
|
|
2816
3291
|
if (publicMatch && secretMatch) {
|
|
@@ -2825,12 +3300,12 @@ function getLangfuseKeysFromDockerEnv() {
|
|
|
2825
3300
|
}
|
|
2826
3301
|
function findDockerComposeDir() {
|
|
2827
3302
|
const candidates = [
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
3303
|
+
join7(process.cwd(), "docker"),
|
|
3304
|
+
join7(process.cwd(), "..", "squads-cli", "docker"),
|
|
3305
|
+
join7(homedir2(), "agents-squads", "squads-cli", "docker")
|
|
2831
3306
|
];
|
|
2832
3307
|
for (const dir of candidates) {
|
|
2833
|
-
if (
|
|
3308
|
+
if (existsSync7(join7(dir, "docker-compose.yml"))) {
|
|
2834
3309
|
return dir;
|
|
2835
3310
|
}
|
|
2836
3311
|
}
|
|
@@ -2856,7 +3331,7 @@ async function stackInitCommand() {
|
|
|
2856
3331
|
writeLine();
|
|
2857
3332
|
writeLine(` ${bold}Step 2: Docker Compose Files${RESET}`);
|
|
2858
3333
|
let composeDir = findPackageDockerDir();
|
|
2859
|
-
const targetDir =
|
|
3334
|
+
const targetDir = join7(SQUADS_DATA_DIR, "docker");
|
|
2860
3335
|
if (!composeDir) {
|
|
2861
3336
|
writeLine(` ${colors.red}${icons.error}${RESET} Docker compose files not found`);
|
|
2862
3337
|
writeLine(` ${colors.dim}This shouldn't happen if you installed via npm.${RESET}`);
|
|
@@ -2864,20 +3339,20 @@ async function stackInitCommand() {
|
|
|
2864
3339
|
writeLine();
|
|
2865
3340
|
return;
|
|
2866
3341
|
}
|
|
2867
|
-
if (composeDir !== targetDir && !
|
|
3342
|
+
if (composeDir !== targetDir && !existsSync7(targetDir)) {
|
|
2868
3343
|
writeLine(` ${colors.cyan}${icons.progress}${RESET} Copying docker files to ${colors.dim}~/.squads/docker${RESET}`);
|
|
2869
3344
|
try {
|
|
2870
|
-
|
|
2871
|
-
|
|
3345
|
+
mkdirSync4(SQUADS_DATA_DIR, { recursive: true });
|
|
3346
|
+
mkdirSync4(targetDir, { recursive: true });
|
|
2872
3347
|
const filesToCopy = [
|
|
2873
3348
|
"docker-compose.yml",
|
|
2874
3349
|
"docker-compose.engram.yml",
|
|
2875
3350
|
".env.example"
|
|
2876
3351
|
];
|
|
2877
3352
|
for (const file of filesToCopy) {
|
|
2878
|
-
const src =
|
|
2879
|
-
const dst =
|
|
2880
|
-
if (
|
|
3353
|
+
const src = join7(composeDir, file);
|
|
3354
|
+
const dst = join7(targetDir, file);
|
|
3355
|
+
if (existsSync7(src)) {
|
|
2881
3356
|
copyFileSync(src, dst);
|
|
2882
3357
|
}
|
|
2883
3358
|
}
|
|
@@ -2886,7 +3361,7 @@ async function stackInitCommand() {
|
|
|
2886
3361
|
} catch {
|
|
2887
3362
|
writeLine(` ${colors.yellow}${icons.warning}${RESET} Could not copy files, using source location`);
|
|
2888
3363
|
}
|
|
2889
|
-
} else if (
|
|
3364
|
+
} else if (existsSync7(targetDir)) {
|
|
2890
3365
|
composeDir = targetDir;
|
|
2891
3366
|
writeLine(` ${colors.green}${icons.success}${RESET} Using ${colors.dim}~/.squads/docker${RESET}`);
|
|
2892
3367
|
} else {
|
|
@@ -2894,10 +3369,10 @@ async function stackInitCommand() {
|
|
|
2894
3369
|
}
|
|
2895
3370
|
writeLine();
|
|
2896
3371
|
writeLine(` ${bold}Step 3: Environment Configuration${RESET}`);
|
|
2897
|
-
const envPath =
|
|
2898
|
-
const envExamplePath =
|
|
2899
|
-
if (!
|
|
2900
|
-
if (
|
|
3372
|
+
const envPath = join7(composeDir, ".env");
|
|
3373
|
+
const envExamplePath = join7(composeDir, ".env.example");
|
|
3374
|
+
if (!existsSync7(envPath)) {
|
|
3375
|
+
if (existsSync7(envExamplePath)) {
|
|
2901
3376
|
copyFileSync(envExamplePath, envPath);
|
|
2902
3377
|
writeLine(` ${colors.cyan}${icons.progress}${RESET} Created .env from template`);
|
|
2903
3378
|
} else {
|
|
@@ -2926,13 +3401,13 @@ LANGFUSE_PORT=3100
|
|
|
2926
3401
|
OTEL_PORT=4318
|
|
2927
3402
|
BRIDGE_PORT=8088
|
|
2928
3403
|
`;
|
|
2929
|
-
|
|
3404
|
+
writeFileSync5(envPath, minimalEnv);
|
|
2930
3405
|
writeLine(` ${colors.cyan}${icons.progress}${RESET} Created default .env`);
|
|
2931
3406
|
}
|
|
2932
3407
|
} else {
|
|
2933
3408
|
writeLine(` ${colors.green}${icons.success}${RESET} .env exists`);
|
|
2934
3409
|
}
|
|
2935
|
-
const envContent =
|
|
3410
|
+
const envContent = readFileSync5(envPath, "utf-8");
|
|
2936
3411
|
const missingSecrets = [];
|
|
2937
3412
|
const llmProvider = envContent.match(/LLM_PROVIDER=(\w+)/)?.[1] || "ollama";
|
|
2938
3413
|
if (llmProvider === "openai") {
|
|
@@ -3612,8 +4087,8 @@ async function memoryExtractCommand(options = {}) {
|
|
|
3612
4087
|
|
|
3613
4088
|
// src/commands/sync.ts
|
|
3614
4089
|
import { execSync as execSync4 } from "child_process";
|
|
3615
|
-
import { existsSync as
|
|
3616
|
-
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";
|
|
3617
4092
|
var PATH_TO_SQUAD = {
|
|
3618
4093
|
"squads-cli": "product",
|
|
3619
4094
|
"agents-squads-web": "website",
|
|
@@ -3641,15 +4116,15 @@ var MESSAGE_TO_SQUAD = {
|
|
|
3641
4116
|
"infra": "engineering"
|
|
3642
4117
|
};
|
|
3643
4118
|
function getLastSyncTime(memoryDir) {
|
|
3644
|
-
const syncFile =
|
|
3645
|
-
if (
|
|
3646
|
-
return
|
|
4119
|
+
const syncFile = join8(memoryDir, ".last-sync");
|
|
4120
|
+
if (existsSync8(syncFile)) {
|
|
4121
|
+
return readFileSync6(syncFile, "utf-8").trim();
|
|
3647
4122
|
}
|
|
3648
4123
|
return null;
|
|
3649
4124
|
}
|
|
3650
4125
|
function updateLastSyncTime(memoryDir) {
|
|
3651
|
-
const syncFile =
|
|
3652
|
-
|
|
4126
|
+
const syncFile = join8(memoryDir, ".last-sync");
|
|
4127
|
+
writeFileSync6(syncFile, (/* @__PURE__ */ new Date()).toISOString());
|
|
3653
4128
|
}
|
|
3654
4129
|
function getRecentCommits(since) {
|
|
3655
4130
|
const commits = [];
|
|
@@ -3721,22 +4196,22 @@ ${messages}
|
|
|
3721
4196
|
`;
|
|
3722
4197
|
}
|
|
3723
4198
|
function appendToSquadMemory(memoryDir, squad, summary) {
|
|
3724
|
-
const squadMemoryDir =
|
|
3725
|
-
if (!
|
|
3726
|
-
|
|
4199
|
+
const squadMemoryDir = join8(memoryDir, squad);
|
|
4200
|
+
if (!existsSync8(squadMemoryDir)) {
|
|
4201
|
+
mkdirSync5(squadMemoryDir, { recursive: true });
|
|
3727
4202
|
}
|
|
3728
4203
|
let agentDir;
|
|
3729
|
-
const existingDirs =
|
|
4204
|
+
const existingDirs = existsSync8(squadMemoryDir) ? readdirSync2(squadMemoryDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name) : [];
|
|
3730
4205
|
if (existingDirs.length > 0) {
|
|
3731
|
-
agentDir =
|
|
4206
|
+
agentDir = join8(squadMemoryDir, existingDirs[0]);
|
|
3732
4207
|
} else {
|
|
3733
|
-
agentDir =
|
|
3734
|
-
|
|
4208
|
+
agentDir = join8(squadMemoryDir, `${squad}-lead`);
|
|
4209
|
+
mkdirSync5(agentDir, { recursive: true });
|
|
3735
4210
|
}
|
|
3736
|
-
const statePath =
|
|
4211
|
+
const statePath = join8(agentDir, "state.md");
|
|
3737
4212
|
let content = "";
|
|
3738
|
-
if (
|
|
3739
|
-
content =
|
|
4213
|
+
if (existsSync8(statePath)) {
|
|
4214
|
+
content = readFileSync6(statePath, "utf-8");
|
|
3740
4215
|
} else {
|
|
3741
4216
|
content = `# ${squad} Squad - State
|
|
3742
4217
|
|
|
@@ -3748,7 +4223,7 @@ Updated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
|
|
|
3748
4223
|
`Updated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`
|
|
3749
4224
|
);
|
|
3750
4225
|
content += summary;
|
|
3751
|
-
|
|
4226
|
+
writeFileSync6(statePath, content);
|
|
3752
4227
|
return true;
|
|
3753
4228
|
}
|
|
3754
4229
|
function gitPullMemory() {
|
|
@@ -4025,28 +4500,28 @@ async function goalProgressCommand(squadName, goalIndex, progress2) {
|
|
|
4025
4500
|
}
|
|
4026
4501
|
|
|
4027
4502
|
// src/commands/feedback.ts
|
|
4028
|
-
import { readFileSync as
|
|
4029
|
-
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";
|
|
4030
4505
|
function getFeedbackPath(squadName) {
|
|
4031
4506
|
const memoryDir = findMemoryDir();
|
|
4032
4507
|
if (!memoryDir) return null;
|
|
4033
4508
|
const squad = loadSquad(squadName);
|
|
4034
4509
|
const agentName = squad?.agents[0]?.name || `${squadName}-lead`;
|
|
4035
|
-
return
|
|
4510
|
+
return join9(memoryDir, squadName, agentName, "feedback.md");
|
|
4036
4511
|
}
|
|
4037
4512
|
function getOutputPath(squadName) {
|
|
4038
4513
|
const memoryDir = findMemoryDir();
|
|
4039
4514
|
if (!memoryDir) return null;
|
|
4040
4515
|
const squad = loadSquad(squadName);
|
|
4041
4516
|
const agentName = squad?.agents[0]?.name || `${squadName}-lead`;
|
|
4042
|
-
return
|
|
4517
|
+
return join9(memoryDir, squadName, agentName, "output.md");
|
|
4043
4518
|
}
|
|
4044
4519
|
function getLastExecution(squadName) {
|
|
4045
4520
|
const outputPath = getOutputPath(squadName);
|
|
4046
|
-
if (!outputPath || !
|
|
4521
|
+
if (!outputPath || !existsSync9(outputPath)) {
|
|
4047
4522
|
return null;
|
|
4048
4523
|
}
|
|
4049
|
-
const content =
|
|
4524
|
+
const content = readFileSync7(outputPath, "utf-8");
|
|
4050
4525
|
const lines = content.split("\n");
|
|
4051
4526
|
let date = "unknown";
|
|
4052
4527
|
let summary = lines.slice(0, 5).join("\n");
|
|
@@ -4098,9 +4573,9 @@ async function feedbackAddCommand(squadName, rating, feedback2, options) {
|
|
|
4098
4573
|
return;
|
|
4099
4574
|
}
|
|
4100
4575
|
const lastExec = getLastExecution(squadName);
|
|
4101
|
-
const dir =
|
|
4102
|
-
if (!
|
|
4103
|
-
|
|
4576
|
+
const dir = dirname4(feedbackPath);
|
|
4577
|
+
if (!existsSync9(dir)) {
|
|
4578
|
+
mkdirSync6(dir, { recursive: true });
|
|
4104
4579
|
}
|
|
4105
4580
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4106
4581
|
let entry = `
|
|
@@ -4128,15 +4603,15 @@ _Date: ${date}_
|
|
|
4128
4603
|
}
|
|
4129
4604
|
}
|
|
4130
4605
|
let existing = "";
|
|
4131
|
-
if (
|
|
4132
|
-
existing =
|
|
4606
|
+
if (existsSync9(feedbackPath)) {
|
|
4607
|
+
existing = readFileSync7(feedbackPath, "utf-8");
|
|
4133
4608
|
} else {
|
|
4134
4609
|
existing = `# ${squadName} - Feedback Log
|
|
4135
4610
|
|
|
4136
4611
|
> Execution feedback and learnings
|
|
4137
4612
|
`;
|
|
4138
4613
|
}
|
|
4139
|
-
|
|
4614
|
+
writeFileSync7(feedbackPath, existing + entry);
|
|
4140
4615
|
const stars = `${colors.yellow}${"\u2605".repeat(ratingNum)}${"\u2606".repeat(5 - ratingNum)}${RESET}`;
|
|
4141
4616
|
writeLine();
|
|
4142
4617
|
writeLine(` ${icons.success} Feedback recorded for ${colors.cyan}${squadName}${RESET}`);
|
|
@@ -4150,11 +4625,11 @@ _Date: ${date}_
|
|
|
4150
4625
|
async function feedbackShowCommand(squadName, options) {
|
|
4151
4626
|
await track(Events.CLI_FEEDBACK_SHOW, { squad: squadName });
|
|
4152
4627
|
const feedbackPath = getFeedbackPath(squadName);
|
|
4153
|
-
if (!feedbackPath || !
|
|
4628
|
+
if (!feedbackPath || !existsSync9(feedbackPath)) {
|
|
4154
4629
|
writeLine(` ${colors.yellow}No feedback recorded for ${squadName}${RESET}`);
|
|
4155
4630
|
return;
|
|
4156
4631
|
}
|
|
4157
|
-
const content =
|
|
4632
|
+
const content = readFileSync7(feedbackPath, "utf-8");
|
|
4158
4633
|
const entries = parseFeedbackHistory(content);
|
|
4159
4634
|
const limit = options.limit ? parseInt(options.limit) : 5;
|
|
4160
4635
|
const recent = entries.slice(-limit).reverse();
|
|
@@ -4199,10 +4674,10 @@ async function feedbackStatsCommand() {
|
|
|
4199
4674
|
writeLine(` ${colors.purple}${box.teeRight}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.teeLeft}${RESET}`);
|
|
4200
4675
|
for (const squad of squads) {
|
|
4201
4676
|
const feedbackPath = getFeedbackPath(squad);
|
|
4202
|
-
if (!feedbackPath || !
|
|
4677
|
+
if (!feedbackPath || !existsSync9(feedbackPath)) {
|
|
4203
4678
|
continue;
|
|
4204
4679
|
}
|
|
4205
|
-
const content =
|
|
4680
|
+
const content = readFileSync7(feedbackPath, "utf-8");
|
|
4206
4681
|
const entries = parseFeedbackHistory(content);
|
|
4207
4682
|
if (entries.length === 0) continue;
|
|
4208
4683
|
const avgRating = entries.reduce((sum, e) => sum + e.rating, 0) / entries.length;
|
|
@@ -4224,9 +4699,252 @@ async function feedbackStatsCommand() {
|
|
|
4224
4699
|
writeLine();
|
|
4225
4700
|
}
|
|
4226
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
|
+
|
|
4227
4945
|
// src/commands/dashboard.ts
|
|
4228
|
-
import { readdirSync as readdirSync4, existsSync as
|
|
4229
|
-
import { join as
|
|
4946
|
+
import { readdirSync as readdirSync4, existsSync as existsSync11, statSync as statSync2 } from "fs";
|
|
4947
|
+
import { join as join11 } from "path";
|
|
4230
4948
|
|
|
4231
4949
|
// src/lib/providers.ts
|
|
4232
4950
|
var PROVIDERS = {
|
|
@@ -5085,16 +5803,16 @@ async function closeDatabase() {
|
|
|
5085
5803
|
function getLastActivityDate(squadName) {
|
|
5086
5804
|
const memoryDir = findMemoryDir();
|
|
5087
5805
|
if (!memoryDir) return "unknown";
|
|
5088
|
-
const squadMemory =
|
|
5089
|
-
if (!
|
|
5806
|
+
const squadMemory = join11(memoryDir, squadName);
|
|
5807
|
+
if (!existsSync11(squadMemory)) return "\u2014";
|
|
5090
5808
|
let latestTime = 0;
|
|
5091
5809
|
try {
|
|
5092
5810
|
const agents = readdirSync4(squadMemory, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
5093
5811
|
for (const agent of agents) {
|
|
5094
|
-
const agentPath =
|
|
5812
|
+
const agentPath = join11(squadMemory, agent.name);
|
|
5095
5813
|
const files = readdirSync4(agentPath).filter((f) => f.endsWith(".md"));
|
|
5096
5814
|
for (const file of files) {
|
|
5097
|
-
const filePath =
|
|
5815
|
+
const filePath = join11(agentPath, file);
|
|
5098
5816
|
const stats = statSync2(filePath);
|
|
5099
5817
|
if (stats.mtimeMs > latestTime) {
|
|
5100
5818
|
latestTime = stats.mtimeMs;
|
|
@@ -5341,11 +6059,11 @@ async function dashboardCommand(options = {}) {
|
|
|
5341
6059
|
await closeDatabase();
|
|
5342
6060
|
}
|
|
5343
6061
|
function findAgentsSquadsDir() {
|
|
5344
|
-
const parentDir =
|
|
5345
|
-
if (
|
|
6062
|
+
const parentDir = join11(process.cwd(), "..");
|
|
6063
|
+
if (existsSync11(join11(parentDir, "hq"))) {
|
|
5346
6064
|
return parentDir;
|
|
5347
6065
|
}
|
|
5348
|
-
if (
|
|
6066
|
+
if (existsSync11(join11(process.cwd(), ".git"))) {
|
|
5349
6067
|
return process.cwd();
|
|
5350
6068
|
}
|
|
5351
6069
|
return null;
|
|
@@ -5984,7 +6702,7 @@ function executeClaudePrompt(prompt2) {
|
|
|
5984
6702
|
// src/commands/open-issues.ts
|
|
5985
6703
|
import { execSync as execSync7, spawn as spawn5 } from "child_process";
|
|
5986
6704
|
import { readdirSync as readdirSync5 } from "fs";
|
|
5987
|
-
import { join as
|
|
6705
|
+
import { join as join12 } from "path";
|
|
5988
6706
|
import ora4 from "ora";
|
|
5989
6707
|
var ISSUE_FINDER_PATTERNS = [
|
|
5990
6708
|
"*-eval.md",
|
|
@@ -6042,7 +6760,7 @@ function findEvalAgents(squadsDir, filterSquad) {
|
|
|
6042
6760
|
const agents = [];
|
|
6043
6761
|
const squads = readdirSync5(squadsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).filter((d) => !filterSquad || d.name === filterSquad).map((d) => d.name);
|
|
6044
6762
|
for (const squad of squads) {
|
|
6045
|
-
const squadPath =
|
|
6763
|
+
const squadPath = join12(squadsDir, squad);
|
|
6046
6764
|
const files = readdirSync5(squadPath).filter((f) => f.endsWith(".md"));
|
|
6047
6765
|
for (const file of files) {
|
|
6048
6766
|
const isEval = ISSUE_FINDER_PATTERNS.some((pattern) => {
|
|
@@ -6053,7 +6771,7 @@ function findEvalAgents(squadsDir, filterSquad) {
|
|
|
6053
6771
|
agents.push({
|
|
6054
6772
|
name: file,
|
|
6055
6773
|
squad,
|
|
6056
|
-
path:
|
|
6774
|
+
path: join12(squadPath, file)
|
|
6057
6775
|
});
|
|
6058
6776
|
}
|
|
6059
6777
|
}
|
|
@@ -6163,8 +6881,8 @@ import open from "open";
|
|
|
6163
6881
|
|
|
6164
6882
|
// src/lib/auth.ts
|
|
6165
6883
|
import { createClient } from "@supabase/supabase-js";
|
|
6166
|
-
import { existsSync as
|
|
6167
|
-
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";
|
|
6168
6886
|
import { homedir as homedir3 } from "os";
|
|
6169
6887
|
import "open";
|
|
6170
6888
|
import http from "http";
|
|
@@ -6194,8 +6912,8 @@ var PERSONAL_DOMAINS = [
|
|
|
6194
6912
|
"tutanota.com",
|
|
6195
6913
|
"hey.com"
|
|
6196
6914
|
];
|
|
6197
|
-
var AUTH_DIR =
|
|
6198
|
-
var AUTH_PATH =
|
|
6915
|
+
var AUTH_DIR = join13(homedir3(), ".squads-cli");
|
|
6916
|
+
var AUTH_PATH = join13(AUTH_DIR, "auth.json");
|
|
6199
6917
|
function isPersonalEmail(email) {
|
|
6200
6918
|
const domain = email.split("@")[1]?.toLowerCase();
|
|
6201
6919
|
return PERSONAL_DOMAINS.includes(domain);
|
|
@@ -6204,22 +6922,22 @@ function getEmailDomain(email) {
|
|
|
6204
6922
|
return email.split("@")[1]?.toLowerCase() || "";
|
|
6205
6923
|
}
|
|
6206
6924
|
function saveSession(session2) {
|
|
6207
|
-
if (!
|
|
6208
|
-
|
|
6925
|
+
if (!existsSync12(AUTH_DIR)) {
|
|
6926
|
+
mkdirSync8(AUTH_DIR, { recursive: true });
|
|
6209
6927
|
}
|
|
6210
|
-
|
|
6928
|
+
writeFileSync9(AUTH_PATH, JSON.stringify(session2, null, 2));
|
|
6211
6929
|
}
|
|
6212
6930
|
function loadSession() {
|
|
6213
|
-
if (!
|
|
6931
|
+
if (!existsSync12(AUTH_PATH)) return null;
|
|
6214
6932
|
try {
|
|
6215
|
-
return JSON.parse(
|
|
6933
|
+
return JSON.parse(readFileSync9(AUTH_PATH, "utf-8"));
|
|
6216
6934
|
} catch {
|
|
6217
6935
|
return null;
|
|
6218
6936
|
}
|
|
6219
6937
|
}
|
|
6220
6938
|
function clearSession() {
|
|
6221
|
-
if (
|
|
6222
|
-
|
|
6939
|
+
if (existsSync12(AUTH_PATH)) {
|
|
6940
|
+
writeFileSync9(AUTH_PATH, "");
|
|
6223
6941
|
}
|
|
6224
6942
|
}
|
|
6225
6943
|
function startAuthCallbackServer(port = 54321) {
|
|
@@ -6442,25 +7160,25 @@ async function updateCommand(options = {}) {
|
|
|
6442
7160
|
|
|
6443
7161
|
// src/commands/progress.ts
|
|
6444
7162
|
import { execSync as execSync8 } from "child_process";
|
|
6445
|
-
import { existsSync as
|
|
6446
|
-
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";
|
|
6447
7165
|
function getTasksFilePath() {
|
|
6448
7166
|
const memoryDir = findMemoryDir();
|
|
6449
7167
|
if (!memoryDir) {
|
|
6450
7168
|
const cwd = process.cwd();
|
|
6451
|
-
const agentsDir =
|
|
6452
|
-
if (!
|
|
6453
|
-
|
|
7169
|
+
const agentsDir = join14(cwd, ".agents");
|
|
7170
|
+
if (!existsSync13(agentsDir)) {
|
|
7171
|
+
mkdirSync9(agentsDir, { recursive: true });
|
|
6454
7172
|
}
|
|
6455
|
-
return
|
|
7173
|
+
return join14(agentsDir, "tasks.json");
|
|
6456
7174
|
}
|
|
6457
|
-
return
|
|
7175
|
+
return join14(memoryDir, "..", "tasks.json");
|
|
6458
7176
|
}
|
|
6459
7177
|
function loadTasks() {
|
|
6460
7178
|
const tasksPath = getTasksFilePath();
|
|
6461
|
-
if (
|
|
7179
|
+
if (existsSync13(tasksPath)) {
|
|
6462
7180
|
try {
|
|
6463
|
-
return JSON.parse(
|
|
7181
|
+
return JSON.parse(readFileSync10(tasksPath, "utf-8"));
|
|
6464
7182
|
} catch {
|
|
6465
7183
|
return { tasks: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6466
7184
|
}
|
|
@@ -6470,7 +7188,7 @@ function loadTasks() {
|
|
|
6470
7188
|
function saveTasks(data) {
|
|
6471
7189
|
const tasksPath = getTasksFilePath();
|
|
6472
7190
|
data.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
6473
|
-
|
|
7191
|
+
writeFileSync10(tasksPath, JSON.stringify(data, null, 2));
|
|
6474
7192
|
}
|
|
6475
7193
|
function getRecentActivity() {
|
|
6476
7194
|
const activity = [];
|
|
@@ -6822,8 +7540,8 @@ async function resultsCommand(options = {}) {
|
|
|
6822
7540
|
}
|
|
6823
7541
|
|
|
6824
7542
|
// src/commands/history.ts
|
|
6825
|
-
import { existsSync as
|
|
6826
|
-
import { join as
|
|
7543
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
|
|
7544
|
+
import { join as join15 } from "path";
|
|
6827
7545
|
var BRIDGE_URL2 = process.env.SQUADS_BRIDGE_URL || "http://localhost:8088";
|
|
6828
7546
|
var FETCH_TIMEOUT_MS2 = 3e3;
|
|
6829
7547
|
async function fetchWithTimeout2(url, timeoutMs = FETCH_TIMEOUT_MS2) {
|
|
@@ -6868,12 +7586,12 @@ async function fetchFromBridge2(days, squad) {
|
|
|
6868
7586
|
function fetchFromLocal(days, squad) {
|
|
6869
7587
|
const executions = [];
|
|
6870
7588
|
const historyPaths = [
|
|
6871
|
-
|
|
6872
|
-
|
|
7589
|
+
join15(process.cwd(), ".agents/sessions/history.jsonl"),
|
|
7590
|
+
join15(process.env.HOME || "", "agents-squads/hq/.agents/sessions/history.jsonl")
|
|
6873
7591
|
];
|
|
6874
7592
|
let historyPath;
|
|
6875
7593
|
for (const path3 of historyPaths) {
|
|
6876
|
-
if (
|
|
7594
|
+
if (existsSync14(path3)) {
|
|
6877
7595
|
historyPath = path3;
|
|
6878
7596
|
break;
|
|
6879
7597
|
}
|
|
@@ -6882,7 +7600,7 @@ function fetchFromLocal(days, squad) {
|
|
|
6882
7600
|
return [];
|
|
6883
7601
|
}
|
|
6884
7602
|
try {
|
|
6885
|
-
const content =
|
|
7603
|
+
const content = readFileSync11(historyPath, "utf-8");
|
|
6886
7604
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
6887
7605
|
const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
6888
7606
|
for (const line of lines) {
|
|
@@ -7219,18 +7937,18 @@ async function healthCommand(options = {}) {
|
|
|
7219
7937
|
|
|
7220
7938
|
// src/commands/workers.ts
|
|
7221
7939
|
import { execSync as execSync10 } from "child_process";
|
|
7222
|
-
import { existsSync as
|
|
7223
|
-
import { join as
|
|
7940
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
7941
|
+
import { join as join16 } from "path";
|
|
7224
7942
|
function getTasksFilePath2() {
|
|
7225
7943
|
const memoryDir = findMemoryDir();
|
|
7226
7944
|
if (!memoryDir) return null;
|
|
7227
|
-
return
|
|
7945
|
+
return join16(memoryDir, "..", "tasks.json");
|
|
7228
7946
|
}
|
|
7229
7947
|
function loadActiveTasks() {
|
|
7230
7948
|
const tasksPath = getTasksFilePath2();
|
|
7231
|
-
if (!tasksPath || !
|
|
7949
|
+
if (!tasksPath || !existsSync15(tasksPath)) return [];
|
|
7232
7950
|
try {
|
|
7233
|
-
const data = JSON.parse(
|
|
7951
|
+
const data = JSON.parse(readFileSync12(tasksPath, "utf-8"));
|
|
7234
7952
|
return data.tasks?.filter((t) => t.status === "active") || [];
|
|
7235
7953
|
} catch {
|
|
7236
7954
|
return [];
|
|
@@ -7377,8 +8095,8 @@ function getElapsedTime2(startTime) {
|
|
|
7377
8095
|
}
|
|
7378
8096
|
|
|
7379
8097
|
// src/commands/context-feed.ts
|
|
7380
|
-
import { existsSync as
|
|
7381
|
-
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";
|
|
7382
8100
|
var BRIDGE_URL3 = process.env.SQUADS_BRIDGE_URL || "http://localhost:8088";
|
|
7383
8101
|
async function syncBriefToBridge(brief, sourcePath) {
|
|
7384
8102
|
try {
|
|
@@ -7407,10 +8125,10 @@ async function syncBriefToBridge(brief, sourcePath) {
|
|
|
7407
8125
|
}
|
|
7408
8126
|
function readBusinessBrief(squadsDir) {
|
|
7409
8127
|
if (!squadsDir) return void 0;
|
|
7410
|
-
const briefPath =
|
|
7411
|
-
if (!
|
|
8128
|
+
const briefPath = join17(squadsDir, "..", "BUSINESS_BRIEF.md");
|
|
8129
|
+
if (!existsSync16(briefPath)) return void 0;
|
|
7412
8130
|
try {
|
|
7413
|
-
const content =
|
|
8131
|
+
const content = readFileSync13(briefPath, "utf-8");
|
|
7414
8132
|
const brief = { raw: content };
|
|
7415
8133
|
const priorityMatch = content.match(/##\s*#1 Priority\s*\n+\*\*([^*]+)\*\*/);
|
|
7416
8134
|
if (priorityMatch) {
|
|
@@ -7460,7 +8178,7 @@ function readBusinessBrief(squadsDir) {
|
|
|
7460
8178
|
async function collectBriefingData(options) {
|
|
7461
8179
|
const squadsDir = findSquadsDir();
|
|
7462
8180
|
const memoryDir = findMemoryDir();
|
|
7463
|
-
const baseDir = squadsDir ?
|
|
8181
|
+
const baseDir = squadsDir ? join17(squadsDir, "..", "..", "..") : null;
|
|
7464
8182
|
const allSquads = squadsDir ? listSquads(squadsDir) : [];
|
|
7465
8183
|
if (options.squad && !allSquads.includes(options.squad)) {
|
|
7466
8184
|
return {
|
|
@@ -7500,14 +8218,14 @@ async function collectBriefingData(options) {
|
|
|
7500
8218
|
}
|
|
7501
8219
|
let lastActivity;
|
|
7502
8220
|
if (memoryDir) {
|
|
7503
|
-
const squadMemoryPath =
|
|
7504
|
-
if (
|
|
8221
|
+
const squadMemoryPath = join17(memoryDir, squadName);
|
|
8222
|
+
if (existsSync16(squadMemoryPath)) {
|
|
7505
8223
|
let mostRecent = 0;
|
|
7506
8224
|
try {
|
|
7507
8225
|
const walkDir = (dir) => {
|
|
7508
8226
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
7509
8227
|
for (const entry of entries) {
|
|
7510
|
-
const fullPath =
|
|
8228
|
+
const fullPath = join17(dir, entry.name);
|
|
7511
8229
|
if (entry.isDirectory()) {
|
|
7512
8230
|
walkDir(fullPath);
|
|
7513
8231
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -7587,7 +8305,7 @@ async function collectBriefingData(options) {
|
|
|
7587
8305
|
}
|
|
7588
8306
|
const brief = readBusinessBrief(squadsDir);
|
|
7589
8307
|
if (brief && squadsDir) {
|
|
7590
|
-
const briefPath =
|
|
8308
|
+
const briefPath = join17(squadsDir, "..", "BUSINESS_BRIEF.md");
|
|
7591
8309
|
syncBriefToBridge(brief, briefPath).catch(() => {
|
|
7592
8310
|
});
|
|
7593
8311
|
}
|
|
@@ -8139,7 +8857,7 @@ async function detectSquadCommand() {
|
|
|
8139
8857
|
|
|
8140
8858
|
// src/commands/trigger.ts
|
|
8141
8859
|
import chalk3 from "chalk";
|
|
8142
|
-
import { existsSync as
|
|
8860
|
+
import { existsSync as existsSync17 } from "fs";
|
|
8143
8861
|
var SCHEDULER_URL = process.env.SCHEDULER_URL || "http://localhost:8090";
|
|
8144
8862
|
async function fetchScheduler(path3, options) {
|
|
8145
8863
|
const res = await fetch(`${SCHEDULER_URL}${path3}`, {
|
|
@@ -8189,7 +8907,7 @@ async function syncTriggers() {
|
|
|
8189
8907
|
const hqPath = process.env.HQ_PATH || `${process.env.HOME}/agents-squads/hq`;
|
|
8190
8908
|
try {
|
|
8191
8909
|
const venvPython = `${hqPath}/squads-scheduler/.venv/bin/python`;
|
|
8192
|
-
const pythonCmd =
|
|
8910
|
+
const pythonCmd = existsSync17(venvPython) ? venvPython : "python3";
|
|
8193
8911
|
const output = execSync14(
|
|
8194
8912
|
`${pythonCmd} ${hqPath}/squads-scheduler/sync_triggers.py`,
|
|
8195
8913
|
{ encoding: "utf-8", cwd: hqPath }
|
|
@@ -8273,13 +8991,13 @@ function registerTriggerCommand(program2) {
|
|
|
8273
8991
|
|
|
8274
8992
|
// src/commands/skill.ts
|
|
8275
8993
|
import ora6 from "ora";
|
|
8276
|
-
import { existsSync as
|
|
8277
|
-
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";
|
|
8278
8996
|
|
|
8279
8997
|
// src/lib/anthropic.ts
|
|
8280
8998
|
import Anthropic from "@anthropic-ai/sdk";
|
|
8281
|
-
import { readFileSync as
|
|
8282
|
-
import { join as
|
|
8999
|
+
import { readFileSync as readFileSync14, readdirSync as readdirSync7 } from "fs";
|
|
9000
|
+
import { join as join18 } from "path";
|
|
8283
9001
|
var client = null;
|
|
8284
9002
|
function getClient() {
|
|
8285
9003
|
if (!client) {
|
|
@@ -8308,12 +9026,12 @@ function loadSkillFiles(skillPath) {
|
|
|
8308
9026
|
function walkDir(dir, prefix = "") {
|
|
8309
9027
|
const entries = readdirSync7(dir, { withFileTypes: true });
|
|
8310
9028
|
for (const entry of entries) {
|
|
8311
|
-
const fullPath =
|
|
9029
|
+
const fullPath = join18(dir, entry.name);
|
|
8312
9030
|
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
8313
9031
|
if (entry.isDirectory()) {
|
|
8314
9032
|
walkDir(fullPath, relativePath);
|
|
8315
9033
|
} else if (entry.isFile()) {
|
|
8316
|
-
const content =
|
|
9034
|
+
const content = readFileSync14(fullPath, "utf-8");
|
|
8317
9035
|
files.push({
|
|
8318
9036
|
name: relativePath,
|
|
8319
9037
|
content
|
|
@@ -8477,14 +9195,14 @@ async function skillUploadCommand(skillPath) {
|
|
|
8477
9195
|
writeLine();
|
|
8478
9196
|
return;
|
|
8479
9197
|
}
|
|
8480
|
-
const fullPath = skillPath.startsWith("/") ? skillPath :
|
|
8481
|
-
if (!
|
|
9198
|
+
const fullPath = skillPath.startsWith("/") ? skillPath : join19(process.cwd(), skillPath);
|
|
9199
|
+
if (!existsSync18(fullPath)) {
|
|
8482
9200
|
writeLine(` ${icons.error} ${colors.red}Directory not found: ${skillPath}${RESET}`);
|
|
8483
9201
|
writeLine();
|
|
8484
9202
|
return;
|
|
8485
9203
|
}
|
|
8486
|
-
const skillMdPath =
|
|
8487
|
-
if (!
|
|
9204
|
+
const skillMdPath = join19(fullPath, "SKILL.md");
|
|
9205
|
+
if (!existsSync18(skillMdPath)) {
|
|
8488
9206
|
writeLine(` ${icons.error} ${colors.red}SKILL.md not found in ${skillPath}${RESET}`);
|
|
8489
9207
|
writeLine();
|
|
8490
9208
|
writeLine(` ${colors.dim}Create a SKILL.md file or use:${RESET}`);
|
|
@@ -8595,7 +9313,7 @@ async function skillConvertCommand(agentPath, options) {
|
|
|
8595
9313
|
const [squad, agent] = agentPath.split("/");
|
|
8596
9314
|
squadName = squad;
|
|
8597
9315
|
agentName = agent.replace(".md", "");
|
|
8598
|
-
agentFilePath =
|
|
9316
|
+
agentFilePath = join19(squadsDir, squad, `${agentName}.md`);
|
|
8599
9317
|
} else {
|
|
8600
9318
|
agentName = agentPath.replace(".md", "");
|
|
8601
9319
|
const foundPath = findAgentFile(squadsDir, agentName);
|
|
@@ -8606,22 +9324,22 @@ async function skillConvertCommand(agentPath, options) {
|
|
|
8606
9324
|
return;
|
|
8607
9325
|
}
|
|
8608
9326
|
agentFilePath = foundPath;
|
|
8609
|
-
squadName = basename2(
|
|
9327
|
+
squadName = basename2(dirname6(agentFilePath));
|
|
8610
9328
|
}
|
|
8611
|
-
if (!
|
|
9329
|
+
if (!existsSync18(agentFilePath)) {
|
|
8612
9330
|
writeLine(` ${icons.error} ${colors.red}Agent file not found: ${agentFilePath}${RESET}`);
|
|
8613
9331
|
writeLine();
|
|
8614
9332
|
return;
|
|
8615
9333
|
}
|
|
8616
|
-
const agentContent =
|
|
9334
|
+
const agentContent = readFileSync15(agentFilePath, "utf-8");
|
|
8617
9335
|
const skillName = `${squadName}-${agentName}`;
|
|
8618
|
-
const outputDir = options.output ||
|
|
8619
|
-
if (!
|
|
8620
|
-
|
|
9336
|
+
const outputDir = options.output || join19(dirname6(squadsDir), "skills", skillName);
|
|
9337
|
+
if (!existsSync18(outputDir)) {
|
|
9338
|
+
mkdirSync10(outputDir, { recursive: true });
|
|
8621
9339
|
}
|
|
8622
9340
|
const skillMd = convertAgentToSkill(agentContent, squadName, agentName);
|
|
8623
|
-
const skillMdPath =
|
|
8624
|
-
|
|
9341
|
+
const skillMdPath = join19(outputDir, "SKILL.md");
|
|
9342
|
+
writeFileSync11(skillMdPath, skillMd);
|
|
8625
9343
|
writeLine(` ${icons.success} ${colors.green}Converted:${RESET} ${agentPath}`);
|
|
8626
9344
|
writeLine();
|
|
8627
9345
|
writeLine(` ${colors.dim}Output:${RESET} ${outputDir}`);
|
|
@@ -8638,8 +9356,8 @@ function findAgentFile(squadsDir, agentName) {
|
|
|
8638
9356
|
const { readdirSync: readdirSync9 } = __require("fs");
|
|
8639
9357
|
const squads = readdirSync9(squadsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("_")).map((d) => d.name);
|
|
8640
9358
|
for (const squad of squads) {
|
|
8641
|
-
const agentPath =
|
|
8642
|
-
if (
|
|
9359
|
+
const agentPath = join19(squadsDir, squad, `${agentName}.md`);
|
|
9360
|
+
if (existsSync18(agentPath)) {
|
|
8643
9361
|
return agentPath;
|
|
8644
9362
|
}
|
|
8645
9363
|
}
|
|
@@ -8680,8 +9398,8 @@ function formatBytes(bytes) {
|
|
|
8680
9398
|
}
|
|
8681
9399
|
|
|
8682
9400
|
// src/commands/permissions.ts
|
|
8683
|
-
import { readFileSync as
|
|
8684
|
-
import { join as
|
|
9401
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
9402
|
+
import { join as join20 } from "path";
|
|
8685
9403
|
function registerPermissionsCommand(program2) {
|
|
8686
9404
|
const permissions = program2.command("permissions").alias("perms").description("Manage and validate squad permissions");
|
|
8687
9405
|
permissions.command("show <squad>").description("Show permission context for a squad").action(permissionsShowCommand);
|
|
@@ -8703,11 +9421,11 @@ async function permissionsShowCommand(squadName) {
|
|
|
8703
9421
|
writeLine();
|
|
8704
9422
|
return;
|
|
8705
9423
|
}
|
|
8706
|
-
const squadFilePath =
|
|
8707
|
-
const squadContent =
|
|
8708
|
-
const
|
|
9424
|
+
const squadFilePath = join20(squadsDir, squadName, "SQUAD.md");
|
|
9425
|
+
const squadContent = readFileSync16(squadFilePath, "utf-8");
|
|
9426
|
+
const context = buildContextFromSquad(squadName, squadContent);
|
|
8709
9427
|
const defaults = getDefaultContext(squadName);
|
|
8710
|
-
const isDefault = JSON.stringify(
|
|
9428
|
+
const isDefault = JSON.stringify(context.permissions) === JSON.stringify(defaults.permissions);
|
|
8711
9429
|
writeLine(` ${bold}Squad:${RESET} ${squadName}`);
|
|
8712
9430
|
if (squad.mission) {
|
|
8713
9431
|
writeLine(` ${colors.dim}${squad.mission}${RESET}`);
|
|
@@ -8718,47 +9436,47 @@ async function permissionsShowCommand(squadName) {
|
|
|
8718
9436
|
writeLine(` ${colors.dim}Add a permissions block to SQUAD.md to restrict access.${RESET}`);
|
|
8719
9437
|
writeLine();
|
|
8720
9438
|
}
|
|
8721
|
-
writeLine(` ${bold}Mode:${RESET} ${formatMode(
|
|
9439
|
+
writeLine(` ${bold}Mode:${RESET} ${formatMode(context.permissions.mode)}`);
|
|
8722
9440
|
writeLine();
|
|
8723
9441
|
writeLine(` ${bold}Bash Commands:${RESET}`);
|
|
8724
|
-
if (
|
|
9442
|
+
if (context.permissions.bash.includes("*")) {
|
|
8725
9443
|
writeLine(` ${colors.dim}All commands allowed (*)${RESET}`);
|
|
8726
9444
|
} else {
|
|
8727
|
-
for (const cmd of
|
|
9445
|
+
for (const cmd of context.permissions.bash) {
|
|
8728
9446
|
writeLine(` ${icons.active} ${cmd}`);
|
|
8729
9447
|
}
|
|
8730
9448
|
}
|
|
8731
9449
|
writeLine();
|
|
8732
9450
|
writeLine(` ${bold}Write Paths:${RESET}`);
|
|
8733
|
-
if (
|
|
9451
|
+
if (context.permissions.write.includes("**") || context.permissions.write.includes("*")) {
|
|
8734
9452
|
writeLine(` ${colors.dim}All paths writable (**)${RESET}`);
|
|
8735
9453
|
} else {
|
|
8736
|
-
for (const path3 of
|
|
9454
|
+
for (const path3 of context.permissions.write) {
|
|
8737
9455
|
writeLine(` ${icons.active} ${path3}`);
|
|
8738
9456
|
}
|
|
8739
9457
|
}
|
|
8740
9458
|
writeLine();
|
|
8741
9459
|
writeLine(` ${bold}Read Paths:${RESET}`);
|
|
8742
|
-
if (
|
|
9460
|
+
if (context.permissions.read.includes("**") || context.permissions.read.includes("*")) {
|
|
8743
9461
|
writeLine(` ${colors.dim}All paths readable (**)${RESET}`);
|
|
8744
9462
|
} else {
|
|
8745
|
-
for (const path3 of
|
|
9463
|
+
for (const path3 of context.permissions.read) {
|
|
8746
9464
|
writeLine(` ${icons.active} ${path3}`);
|
|
8747
9465
|
}
|
|
8748
9466
|
}
|
|
8749
9467
|
writeLine();
|
|
8750
9468
|
writeLine(` ${bold}MCP Servers:${RESET}`);
|
|
8751
|
-
if (
|
|
9469
|
+
if (context.permissions.mcp.allow.includes("*")) {
|
|
8752
9470
|
writeLine(` ${colors.dim}All servers allowed (*)${RESET}`);
|
|
8753
9471
|
} else {
|
|
8754
9472
|
writeLine(` ${colors.green}Allow:${RESET}`);
|
|
8755
|
-
for (const server of
|
|
9473
|
+
for (const server of context.permissions.mcp.allow) {
|
|
8756
9474
|
writeLine(` ${icons.success} ${server}`);
|
|
8757
9475
|
}
|
|
8758
9476
|
}
|
|
8759
|
-
if (
|
|
9477
|
+
if (context.permissions.mcp.deny.length > 0) {
|
|
8760
9478
|
writeLine(` ${colors.red}Deny:${RESET}`);
|
|
8761
|
-
for (const server of
|
|
9479
|
+
for (const server of context.permissions.mcp.deny) {
|
|
8762
9480
|
writeLine(` ${icons.error} ${server}`);
|
|
8763
9481
|
}
|
|
8764
9482
|
}
|
|
@@ -8798,9 +9516,9 @@ async function permissionsCheckCommand(squadName, options) {
|
|
|
8798
9516
|
writeLine();
|
|
8799
9517
|
return;
|
|
8800
9518
|
}
|
|
8801
|
-
const squadFilePath =
|
|
8802
|
-
const squadContent =
|
|
8803
|
-
const
|
|
9519
|
+
const squadFilePath = join20(squadsDir, squadName, "SQUAD.md");
|
|
9520
|
+
const squadContent = readFileSync16(squadFilePath, "utf-8");
|
|
9521
|
+
const context = buildContextFromSquad(squadName, squadContent, options.agent);
|
|
8804
9522
|
const request = {
|
|
8805
9523
|
mcpServers: options.mcp,
|
|
8806
9524
|
bashCommands: options.bash,
|
|
@@ -8817,7 +9535,7 @@ async function permissionsCheckCommand(squadName, options) {
|
|
|
8817
9535
|
writeLine();
|
|
8818
9536
|
return;
|
|
8819
9537
|
}
|
|
8820
|
-
const result = validateExecution(
|
|
9538
|
+
const result = validateExecution(context, request);
|
|
8821
9539
|
if (result.violations.length === 0) {
|
|
8822
9540
|
writeLine(` ${icons.success} ${colors.green}All permissions valid${RESET}`);
|
|
8823
9541
|
writeLine();
|
|
@@ -8986,6 +9704,77 @@ async function contextListCommand(options = {}) {
|
|
|
8986
9704
|
writeLine(` ${colors.purple}${box.bottomLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.bottomRight}${RESET}`);
|
|
8987
9705
|
writeLine();
|
|
8988
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
|
+
}
|
|
8989
9778
|
|
|
8990
9779
|
// src/commands/cost.ts
|
|
8991
9780
|
function getBudgetStatus(squadName, spent, dailyBudget, weeklyBudget) {
|
|
@@ -9232,8 +10021,8 @@ function createBudgetBar(percent, width = 10) {
|
|
|
9232
10021
|
}
|
|
9233
10022
|
|
|
9234
10023
|
// src/lib/executions.ts
|
|
9235
|
-
import { readFileSync as
|
|
9236
|
-
import { join as
|
|
10024
|
+
import { readFileSync as readFileSync17, existsSync as existsSync19, readdirSync as readdirSync8 } from "fs";
|
|
10025
|
+
import { join as join21 } from "path";
|
|
9237
10026
|
function parseExecutionEntry(content, squad, agent) {
|
|
9238
10027
|
const idMatch = content.match(/<!-- exec:(\S+) -->/);
|
|
9239
10028
|
if (!idMatch) return null;
|
|
@@ -9271,8 +10060,8 @@ function parseExecutionEntry(content, squad, agent) {
|
|
|
9271
10060
|
};
|
|
9272
10061
|
}
|
|
9273
10062
|
function parseExecutionLog(filePath, squad, agent) {
|
|
9274
|
-
if (!
|
|
9275
|
-
const content =
|
|
10063
|
+
if (!existsSync19(filePath)) return [];
|
|
10064
|
+
const content = readFileSync17(filePath, "utf-8");
|
|
9276
10065
|
const executions = [];
|
|
9277
10066
|
const entries = content.split(/\n---\n/);
|
|
9278
10067
|
for (const entry of entries) {
|
|
@@ -9310,11 +10099,11 @@ function listExecutions(options = {}) {
|
|
|
9310
10099
|
const squads = readdirSync8(memoryDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
9311
10100
|
for (const squad of squads) {
|
|
9312
10101
|
if (filterSquad && squad !== filterSquad) continue;
|
|
9313
|
-
const squadPath =
|
|
10102
|
+
const squadPath = join21(memoryDir, squad);
|
|
9314
10103
|
const agents = readdirSync8(squadPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
9315
10104
|
for (const agent of agents) {
|
|
9316
10105
|
if (filterAgent && agent !== filterAgent) continue;
|
|
9317
|
-
const logPath =
|
|
10106
|
+
const logPath = join21(squadPath, agent, "executions.md");
|
|
9318
10107
|
const agentExecutions = parseExecutionLog(logPath, squad, agent);
|
|
9319
10108
|
executions.push(...agentExecutions);
|
|
9320
10109
|
}
|
|
@@ -9564,12 +10353,12 @@ async function execStatsCommand(options = {}) {
|
|
|
9564
10353
|
// src/commands/tonight.ts
|
|
9565
10354
|
import ora7 from "ora";
|
|
9566
10355
|
import fs2 from "fs/promises";
|
|
9567
|
-
import path2, { dirname as
|
|
10356
|
+
import path2, { dirname as dirname7 } from "path";
|
|
9568
10357
|
import { execSync as execSync13, spawn as spawn7 } from "child_process";
|
|
9569
10358
|
function getProjectRoot2() {
|
|
9570
10359
|
const squadsDir = findSquadsDir();
|
|
9571
10360
|
if (squadsDir) {
|
|
9572
|
-
return
|
|
10361
|
+
return dirname7(dirname7(squadsDir));
|
|
9573
10362
|
}
|
|
9574
10363
|
return process.cwd();
|
|
9575
10364
|
}
|
|
@@ -9612,6 +10401,7 @@ function launchAgent(target, projectRoot, sessionId, logFile) {
|
|
|
9612
10401
|
const sessionName = `squads-tonight-${sessionId}`;
|
|
9613
10402
|
const claudeCmd = [
|
|
9614
10403
|
`cd '${projectRoot}'`,
|
|
10404
|
+
`unset ANTHROPIC_API_KEY`,
|
|
9615
10405
|
`echo "=== Tonight Session: ${target} ===" >> '${logFile}'`,
|
|
9616
10406
|
`echo "Started: $(date)" >> '${logFile}'`,
|
|
9617
10407
|
`claude --dangerously-skip-permissions -p 'You are running as part of an overnight autonomous session. Execute your tasks efficiently. If you encounter errors, document them clearly and move on. Do not ask for user input.' -- "Run squad: ${target}" 2>&1 | tee -a '${logFile}'`,
|
|
@@ -9897,12 +10687,12 @@ process.stderr.on("error", (err) => {
|
|
|
9897
10687
|
throw err;
|
|
9898
10688
|
});
|
|
9899
10689
|
var envPaths = [
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
10690
|
+
join22(process.cwd(), ".env"),
|
|
10691
|
+
join22(process.cwd(), "..", "hq", ".env"),
|
|
10692
|
+
join22(homedir4(), "agents-squads", "hq", ".env")
|
|
9903
10693
|
];
|
|
9904
10694
|
for (const envPath of envPaths) {
|
|
9905
|
-
if (
|
|
10695
|
+
if (existsSync20(envPath)) {
|
|
9906
10696
|
config({ path: envPath, quiet: true });
|
|
9907
10697
|
break;
|
|
9908
10698
|
}
|
|
@@ -9935,14 +10725,25 @@ program.name("squads").description("A CLI for humans and agents").version(versio
|
|
|
9935
10725
|
}
|
|
9936
10726
|
await statusCommand(void 0, {});
|
|
9937
10727
|
});
|
|
9938
|
-
program.command("init").description("Initialize a new squad project").option("-t, --template <template>", "Project template", "default").option("--skip-infra", "Skip infrastructure setup prompt").action(initCommand);
|
|
9939
|
-
program.command("run <target>").description("Run a squad or agent").option("-v, --verbose", "Verbose output").option("-d, --dry-run", "Show what would be run without executing").option("-e, --execute", "Execute agent via Claude CLI (requires claude installed)").option("-a, --agent <agent>", "Run specific agent within squad").option("-t, --timeout <minutes>", "Execution timeout in minutes (default: 30)", "30").option("-p, --parallel", "Run all agents in parallel (N tmux sessions)").option("-l, --lead", "Lead mode: single orchestrator using Task tool for parallelization").option("-f, --foreground", "Run in foreground (no tmux, blocks terminal)").option("--use-api", "Use API credits instead of subscription").option("--effort <level>", "Effort level: high, medium, low (default: from SQUAD.md or high)").option("--skills <skills...>", "Skills to load (skill IDs or local paths)").
|
|
9940
|
-
|
|
10728
|
+
program.command("init").description("Initialize a new squad project").option("-t, --template <template>", "Project template", "default").option("--skip-infra", "Skip infrastructure setup prompt").option("--force", "Skip requirement checks (for CI/testing)").action(initCommand);
|
|
10729
|
+
program.command("run <target>").description("Run a squad or agent").option("-v, --verbose", "Verbose output").option("-d, --dry-run", "Show what would be run without executing").option("-e, --execute", "Execute agent via Claude CLI (requires claude installed)").option("-a, --agent <agent>", "Run specific agent within squad").option("-t, --timeout <minutes>", "Execution timeout in minutes (default: 30)", "30").option("-p, --parallel", "Run all agents in parallel (N tmux sessions)").option("-l, --lead", "Lead mode: single orchestrator using Task tool for parallelization").option("-f, --foreground", "Run in foreground (no tmux, blocks terminal)").option("--use-api", "Use API credits instead of subscription").option("--effort <level>", "Effort level: high, medium, low (default: from SQUAD.md or high)").option("--skills <skills...>", "Skills to load (skill IDs or local paths)").addHelpText("after", `
|
|
10730
|
+
Examples:
|
|
10731
|
+
$ squads run engineering Run whole squad (shows agent list)
|
|
10732
|
+
$ squads run engineering/code-review Run specific agent (slash notation)
|
|
10733
|
+
$ squads run engineering -a code-review Same as above (flag notation)
|
|
10734
|
+
$ squads run engineering --dry-run Preview what would run
|
|
10735
|
+
$ squads run engineering --execute Execute via Claude CLI
|
|
10736
|
+
$ squads run engineering --parallel Run all agents in parallel (tmux)
|
|
10737
|
+
$ squads run engineering --lead Single orchestrator with Task tool
|
|
10738
|
+
$ squads run engineering -f Run in foreground (blocks terminal)
|
|
10739
|
+
`).action((target, options) => runCommand(target, { ...options, timeout: parseInt(options.timeout, 10) }));
|
|
10740
|
+
program.command("list").description("List agents and squads").option("-s, --squads", "List squads only").option("-a, --agents", "List agents only").option("-v, --verbose", "Show additional details").action(listCommand);
|
|
9941
10741
|
program.command("status [squad]").description("Show squad status and state").option("-v, --verbose", "Show detailed status").action(statusCommand);
|
|
9942
10742
|
program.command("dashboard").alias("dash").description("Show comprehensive goals and metrics dashboard").option("-v, --verbose", "Show additional details").option("-c, --ceo", "Executive summary with priorities and blockers").option("-f, --full", "Include GitHub PR/issue stats (slower, ~30s)").action((options) => dashboardCommand({ ...options, fast: !options.full }));
|
|
9943
|
-
var
|
|
9944
|
-
|
|
9945
|
-
|
|
10743
|
+
var env = program.command("env").description("View squad execution environment (MCP, skills, model, budget)");
|
|
10744
|
+
env.command("show <squad>").description("Show execution environment for a squad").option("--json", "Output as JSON").action(contextShowCommand);
|
|
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);
|
|
9946
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);
|
|
9947
10748
|
program.command("budget").description("Check budget status for a squad").argument("<squad>", "Squad to check").option("--json", "Output as JSON").action(budgetCheckCommand);
|
|
9948
10749
|
var exec2 = program.command("exec").description("View execution history and statistics");
|
|
@@ -9958,7 +10759,7 @@ progress.command("start <squad> <description>").description("Register a new acti
|
|
|
9958
10759
|
progress.command("complete <taskId>").description("Mark a task as completed").option("-f, --failed", "Mark as failed instead").action(progressCompleteCommand);
|
|
9959
10760
|
program.command("results [squad]").description("Show squad results: git activity + KPI goals vs actuals").option("-d, --days <days>", "Days to look back", "7").option("-v, --verbose", "Show detailed KPIs per goal").action((squad, options) => resultsCommand({ ...options, squad }));
|
|
9960
10761
|
program.command("history").description("Show recent agent execution history").option("-d, --days <days>", "Days to look back", "7").option("-s, --squad <squad>", "Filter by squad").option("-v, --verbose", "Show cost and token details").option("-j, --json", "Output as JSON").action((options) => historyCommand(options));
|
|
9961
|
-
program.command("context
|
|
10762
|
+
program.command("context").alias("feed").description("Get business context for alignment: goals, memory, costs, activity").option("-s, --squad <squad>", "Focus on specific squad").option("-t, --topic <topic>", "Search memory for relevant context").option("-a, --agent", "Output JSON for agent consumption").option("-j, --json", "Output as JSON (alias for --agent)").option("-v, --verbose", "Show additional details").action((options) => contextFeedCommand(options));
|
|
9962
10763
|
program.command("workers").description("Show active workers: Claude sessions, tasks, dev servers").option("-v, --verbose", "Show more details").option("-k, --kill <pid>", "Kill a process by PID").action(workersCommand);
|
|
9963
10764
|
program.command("health").description("Quick health check for all infrastructure services").option("-v, --verbose", "Show optional services").action((options) => healthCommand(options));
|
|
9964
10765
|
program.command("watch <command> [args...]").description("Live refresh any squads command (like Unix watch)").option("-n, --interval <seconds>", "Refresh interval in seconds", "2").option("--no-clear", "Don't clear screen between refreshes").action((command, args, options) => watchCommand(command, args, {
|
|
@@ -9967,7 +10768,14 @@ program.command("watch <command> [args...]").description("Live refresh any squad
|
|
|
9967
10768
|
}));
|
|
9968
10769
|
program.command("live").description("Live TUI dashboard with real-time metrics (like htop)").option("-m, --minimal", "Minimal view").option("-f, --focus <panel>", "Focus on specific panel (agents, cost, activity, memory)").action((options) => liveCommand(options));
|
|
9969
10770
|
program.command("top").description("Live process table (like Unix top) - numbers update in place").action(() => topCommand());
|
|
9970
|
-
var memory = program.command("memory").description("Query and manage squad memory").
|
|
10771
|
+
var memory = program.command("memory").description("Query and manage squad memory").addHelpText("after", `
|
|
10772
|
+
Examples:
|
|
10773
|
+
$ squads memory query "pricing" Search all memory for "pricing"
|
|
10774
|
+
$ squads memory show engineering View engineering squad's memory
|
|
10775
|
+
$ squads memory update research "Found: MCP adoption at 15%"
|
|
10776
|
+
$ squads memory list List all memory entries
|
|
10777
|
+
$ squads memory sync --push Sync and push to git
|
|
10778
|
+
`).action(() => {
|
|
9971
10779
|
memory.outputHelp();
|
|
9972
10780
|
});
|
|
9973
10781
|
memory.command("query <query>").description("Search across all squad memory").option("-s, --squad <squad>", "Limit search to specific squad").option("-a, --agent <agent>", "Limit search to specific agent").action(memoryQueryCommand);
|
|
@@ -9996,6 +10804,10 @@ var feedback = program.command("feedback").description("Record and view executio
|
|
|
9996
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);
|
|
9997
10805
|
feedback.command("show <squad>").description("Show feedback history").option("-n, --limit <n>", "Number of entries to show", "5").action(feedbackShowCommand);
|
|
9998
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);
|
|
9999
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);
|
|
10000
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({
|
|
10001
10813
|
days: parseInt(options.days, 10),
|
|
@@ -10006,8 +10818,8 @@ sessions.command("summary").description("Show pretty session summary (auto-detec
|
|
|
10006
10818
|
const { buildCurrentSessionSummary } = await import("./sessions-R4VWIGFR.js");
|
|
10007
10819
|
let data;
|
|
10008
10820
|
if (options.file) {
|
|
10009
|
-
const { readFileSync:
|
|
10010
|
-
data = JSON.parse(
|
|
10821
|
+
const { readFileSync: readFileSync18 } = await import("fs");
|
|
10822
|
+
data = JSON.parse(readFileSync18(options.file, "utf-8"));
|
|
10011
10823
|
} else if (options.data) {
|
|
10012
10824
|
data = JSON.parse(options.data);
|
|
10013
10825
|
} else if (!process.stdin.isTTY) {
|