squads-cli 0.4.10 → 0.4.13
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 +90 -6
- package/dist/chunk-3TSY2K7R.js +473 -0
- package/dist/chunk-3TSY2K7R.js.map +1 -0
- package/dist/cli.js +4692 -864
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +623 -5
- package/dist/index.js +1034 -0
- package/dist/index.js.map +1 -1
- package/dist/squad-parser-YRE2FEAA.js +31 -0
- package/dist/squad-parser-YRE2FEAA.js.map +1 -0
- package/docker/docker-compose.engram.yml +73 -58
- package/docker/docker-compose.yml +118 -78
- package/docker/squads-bridge/squads_bridge.py +534 -8
- package/package.json +12 -8
package/README.md
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
# squads-cli
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/squads-cli)
|
|
4
|
+
[](https://www.npmjs.com/package/squads-cli)
|
|
5
|
+
[](https://github.com/agents-squads/squads-cli)
|
|
4
6
|
[](LICENSE)
|
|
5
|
-
[](https://nodejs.org)
|
|
6
7
|
|
|
7
|
-
**
|
|
8
|
+
**Open source CLI for AI agent coordination.** Organize agents into domain-aligned squads with persistent memory, goal tracking, and coordinated execution.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g squads-cli && squads init
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
> **Why squads?** AI agents are powerful alone, but real work needs coordination. Squads organizes agents by business domain, gives them persistent memory, tracks goals, and delivers outcomes—not just answers. Works with any AI coding assistant.
|
|
8
15
|
|
|
9
16
|

|
|
10
17
|
|
|
18
|
+
⭐ **If you find this useful, [star the repo](https://github.com/agents-squads/squads-cli)** — it helps others discover it!
|
|
19
|
+
|
|
11
20
|
```
|
|
12
21
|
$ squads status
|
|
13
22
|
|
|
@@ -29,13 +38,24 @@ $ squads status
|
|
|
29
38
|
|
|
30
39
|
## Why squads-cli?
|
|
31
40
|
|
|
32
|
-
|
|
41
|
+
| Other Frameworks | squads-cli |
|
|
42
|
+
|------------------|------------|
|
|
43
|
+
| Framework lock-in | Markdown files you own |
|
|
44
|
+
| Complex setup | `npm install -g` and go |
|
|
45
|
+
| No memory | Persistent state across sessions |
|
|
46
|
+
| Single agent focus | Domain-aligned teams |
|
|
47
|
+
| Code-heavy | CLI-first, zero code to start |
|
|
48
|
+
|
|
49
|
+
**Works with:** Claude Code, Cursor, Aider, Gemini, GitHub Copilot, and more.
|
|
50
|
+
|
|
51
|
+
## Key Features
|
|
33
52
|
|
|
34
53
|
- **Squads** — Group agents by domain (engineering, research, marketing)
|
|
35
54
|
- **Memory** — Persistent state that survives across sessions
|
|
36
55
|
- **Goals** — Track objectives and measure progress
|
|
37
56
|
- **Sessions** — Real-time detection of running AI assistants
|
|
38
|
-
- **
|
|
57
|
+
- **Hooks** — Inject context at session start, sync memory at session end
|
|
58
|
+
- **Stack** — Optional local infrastructure for telemetry and cost tracking
|
|
39
59
|
|
|
40
60
|
No complex infrastructure. Just markdown files and a CLI.
|
|
41
61
|
|
|
@@ -285,6 +305,36 @@ squads memory show engineering
|
|
|
285
305
|
squads memory query "performance"
|
|
286
306
|
```
|
|
287
307
|
|
|
308
|
+
### Learning Loop
|
|
309
|
+
|
|
310
|
+
Capture insights that persist across sessions:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
# After fixing a bug
|
|
314
|
+
squads learn "PostgreSQL connection pool exhaustion was caused by unclosed transactions"
|
|
315
|
+
|
|
316
|
+
# With metadata
|
|
317
|
+
squads learn "Always check memory before researching" --squad engineering --category pattern
|
|
318
|
+
|
|
319
|
+
# View learnings
|
|
320
|
+
squads learnings show engineering
|
|
321
|
+
squads learnings search "postgres"
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Learnings are stored in `.agents/memory/<squad>/shared/learnings.md` and sync with `squads memory sync`.
|
|
325
|
+
|
|
326
|
+
**Categories:**
|
|
327
|
+
- `success` — What worked well
|
|
328
|
+
- `failure` — What didn't work (learn from mistakes)
|
|
329
|
+
- `pattern` — Reusable approach
|
|
330
|
+
- `tip` — General advice
|
|
331
|
+
|
|
332
|
+
**The learning loop:**
|
|
333
|
+
1. Session starts → hooks inject squad status + memory
|
|
334
|
+
2. Work happens → you solve problems, discover things
|
|
335
|
+
3. Session ends → Stop hook prompts "Capture learnings: squads learn..."
|
|
336
|
+
4. Next session → Previous learnings compound via memory queries
|
|
337
|
+
|
|
288
338
|
### Goals with Metrics
|
|
289
339
|
|
|
290
340
|
Goals can include optional metric annotations for tracking KPIs:
|
|
@@ -436,6 +486,19 @@ squads feedback show research -n 10 # Show more entries
|
|
|
436
486
|
squads feedback stats # Summary across all squads
|
|
437
487
|
```
|
|
438
488
|
|
|
489
|
+
### Learnings
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
squads learn "Insight here" # Capture a learning
|
|
493
|
+
squads learn "Pattern" -s engineering -c pattern # With squad and category
|
|
494
|
+
squads learn "Tip" -t "cli,perf" # With tags
|
|
495
|
+
squads learnings show engineering # View squad learnings
|
|
496
|
+
squads learnings show engineering -n 5 # Limit results
|
|
497
|
+
squads learnings show engineering --category pattern # Filter by category
|
|
498
|
+
squads learnings show engineering --tag perf # Filter by tag
|
|
499
|
+
squads learnings search "postgres" # Search all learnings
|
|
500
|
+
```
|
|
501
|
+
|
|
439
502
|
### Session Management
|
|
440
503
|
|
|
441
504
|
```bash
|
|
@@ -729,6 +792,18 @@ squads feedback show <squad> View history
|
|
|
729
792
|
-n, --limit <n> Entries to show (default: 5)
|
|
730
793
|
squads feedback stats Summary across squads
|
|
731
794
|
|
|
795
|
+
squads learn <insight> Capture a learning
|
|
796
|
+
-s, --squad <squad> Associate with squad (default: general)
|
|
797
|
+
-c, --category <category> Category: success, failure, pattern, tip
|
|
798
|
+
-t, --tags <tags> Comma-separated tags
|
|
799
|
+
--context <context> Additional context
|
|
800
|
+
squads learnings show <squad> View squad learnings
|
|
801
|
+
-n, --limit <n> Entries to show (default: 10)
|
|
802
|
+
--category <category> Filter by category
|
|
803
|
+
--tag <tag> Filter by tag
|
|
804
|
+
squads learnings search <query> Search all learnings
|
|
805
|
+
-n, --limit <n> Results to show (default: 10)
|
|
806
|
+
|
|
732
807
|
squads sessions List active sessions
|
|
733
808
|
-v, --verbose Session details
|
|
734
809
|
-j, --json JSON output
|
|
@@ -869,7 +944,7 @@ squads whoami Show user
|
|
|
869
944
|
|
|
870
945
|
## Claude Code Integration
|
|
871
946
|
|
|
872
|
-
### Option 1: Session
|
|
947
|
+
### Option 1: Session Hooks (Recommended)
|
|
873
948
|
|
|
874
949
|
Add to `.claude/settings.json`:
|
|
875
950
|
|
|
@@ -882,12 +957,21 @@ Add to `.claude/settings.json`:
|
|
|
882
957
|
"command": "squads status",
|
|
883
958
|
"timeout": 10
|
|
884
959
|
}]
|
|
960
|
+
}],
|
|
961
|
+
"Stop": [{
|
|
962
|
+
"hooks": [{
|
|
963
|
+
"type": "command",
|
|
964
|
+
"command": "squads memory sync && echo \"\\n💡 Capture learnings: squads learn \\\"<insight>\\\"\\n\"",
|
|
965
|
+
"timeout": 15
|
|
966
|
+
}]
|
|
885
967
|
}]
|
|
886
968
|
}
|
|
887
969
|
}
|
|
888
970
|
```
|
|
889
971
|
|
|
890
|
-
Now every Claude Code session
|
|
972
|
+
Now every Claude Code session:
|
|
973
|
+
- **Starts** with squad status injected
|
|
974
|
+
- **Ends** with memory synced and a prompt to capture learnings
|
|
891
975
|
|
|
892
976
|
### Option 2: CLAUDE.md Instructions
|
|
893
977
|
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/squad-parser.ts
|
|
4
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, readdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
5
|
+
import { join as join2, basename } from "path";
|
|
6
|
+
import matter from "gray-matter";
|
|
7
|
+
|
|
8
|
+
// src/lib/mcp-config.ts
|
|
9
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
10
|
+
import { join, dirname } from "path";
|
|
11
|
+
var SERVER_REGISTRY = {};
|
|
12
|
+
function getHome() {
|
|
13
|
+
return process.env.HOME || process.env.USERPROFILE || "";
|
|
14
|
+
}
|
|
15
|
+
function getContextsDir() {
|
|
16
|
+
return join(getHome(), ".claude", "contexts");
|
|
17
|
+
}
|
|
18
|
+
function getMcpConfigsDir() {
|
|
19
|
+
return join(getHome(), ".claude", "mcp-configs");
|
|
20
|
+
}
|
|
21
|
+
function generateMcpConfig(mcpServers) {
|
|
22
|
+
const config = { mcpServers: {} };
|
|
23
|
+
for (const server of mcpServers) {
|
|
24
|
+
const def = SERVER_REGISTRY[server];
|
|
25
|
+
if (def) {
|
|
26
|
+
config.mcpServers[server] = def;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return config;
|
|
30
|
+
}
|
|
31
|
+
function writeMcpConfig(config, path) {
|
|
32
|
+
const dir = dirname(path);
|
|
33
|
+
if (!existsSync(dir)) {
|
|
34
|
+
mkdirSync(dir, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
writeFileSync(path, JSON.stringify(config, null, 2));
|
|
37
|
+
}
|
|
38
|
+
function readMcpConfig(path) {
|
|
39
|
+
if (!existsSync(path)) return null;
|
|
40
|
+
try {
|
|
41
|
+
const content = readFileSync(path, "utf-8");
|
|
42
|
+
return JSON.parse(content);
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function resolveMcpConfig(squadName, mcpServers, forceRegenerate = false) {
|
|
48
|
+
const home = getHome();
|
|
49
|
+
const userOverride = join(getMcpConfigsDir(), `${squadName}.json`);
|
|
50
|
+
if (existsSync(userOverride)) {
|
|
51
|
+
const config = readMcpConfig(userOverride);
|
|
52
|
+
return {
|
|
53
|
+
path: userOverride,
|
|
54
|
+
source: "user-override",
|
|
55
|
+
servers: config ? Object.keys(config.mcpServers) : void 0
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (mcpServers && mcpServers.length > 0) {
|
|
59
|
+
const generatedPath = join(getContextsDir(), `${squadName}.mcp.json`);
|
|
60
|
+
const shouldGenerate = forceRegenerate || !existsSync(generatedPath);
|
|
61
|
+
if (shouldGenerate) {
|
|
62
|
+
const config2 = generateMcpConfig(mcpServers);
|
|
63
|
+
writeMcpConfig(config2, generatedPath);
|
|
64
|
+
return {
|
|
65
|
+
path: generatedPath,
|
|
66
|
+
source: "generated",
|
|
67
|
+
servers: Object.keys(config2.mcpServers),
|
|
68
|
+
generated: true
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const config = readMcpConfig(generatedPath);
|
|
72
|
+
return {
|
|
73
|
+
path: generatedPath,
|
|
74
|
+
source: "generated",
|
|
75
|
+
servers: config ? Object.keys(config.mcpServers) : mcpServers,
|
|
76
|
+
generated: false
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
path: join(home, ".claude.json"),
|
|
81
|
+
source: "fallback"
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function resolveMcpConfigPath(squadName, mcpServers) {
|
|
85
|
+
return resolveMcpConfig(squadName, mcpServers).path;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/lib/squad-parser.ts
|
|
89
|
+
function findSquadsDir() {
|
|
90
|
+
let dir = process.cwd();
|
|
91
|
+
for (let i = 0; i < 5; i++) {
|
|
92
|
+
const squadsPath = join2(dir, ".agents", "squads");
|
|
93
|
+
if (existsSync2(squadsPath)) {
|
|
94
|
+
return squadsPath;
|
|
95
|
+
}
|
|
96
|
+
const parent = join2(dir, "..");
|
|
97
|
+
if (parent === dir) break;
|
|
98
|
+
dir = parent;
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
function findProjectRoot() {
|
|
103
|
+
const squadsDir = findSquadsDir();
|
|
104
|
+
if (!squadsDir) return null;
|
|
105
|
+
return join2(squadsDir, "..", "..");
|
|
106
|
+
}
|
|
107
|
+
function hasLocalInfraConfig() {
|
|
108
|
+
const projectRoot = findProjectRoot();
|
|
109
|
+
if (!projectRoot) return false;
|
|
110
|
+
const envPath = join2(projectRoot, ".env");
|
|
111
|
+
if (!existsSync2(envPath)) return false;
|
|
112
|
+
const content = readFileSync2(envPath, "utf-8");
|
|
113
|
+
const infraKeys = ["LANGFUSE_", "SQUADS_BRIDGE", "SQUADS_POSTGRES", "SQUADS_REDIS"];
|
|
114
|
+
return infraKeys.some((key) => content.includes(key));
|
|
115
|
+
}
|
|
116
|
+
function listSquads(squadsDir) {
|
|
117
|
+
const squads = [];
|
|
118
|
+
const entries = readdirSync(squadsDir, { withFileTypes: true });
|
|
119
|
+
for (const entry of entries) {
|
|
120
|
+
if (entry.isDirectory() && !entry.name.startsWith("_")) {
|
|
121
|
+
const squadFile = join2(squadsDir, entry.name, "SQUAD.md");
|
|
122
|
+
if (existsSync2(squadFile)) {
|
|
123
|
+
squads.push(entry.name);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return squads;
|
|
128
|
+
}
|
|
129
|
+
function listAgents(squadsDir, squadName) {
|
|
130
|
+
const agents = [];
|
|
131
|
+
const dirs = squadName ? [squadName] : readdirSync(squadsDir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_")).map((e) => e.name);
|
|
132
|
+
for (const dir of dirs) {
|
|
133
|
+
const squadPath = join2(squadsDir, dir);
|
|
134
|
+
if (!existsSync2(squadPath)) continue;
|
|
135
|
+
const files = readdirSync(squadPath);
|
|
136
|
+
for (const file of files) {
|
|
137
|
+
if (file.endsWith(".md") && file !== "SQUAD.md") {
|
|
138
|
+
const agentName = file.replace(".md", "");
|
|
139
|
+
agents.push({
|
|
140
|
+
name: agentName,
|
|
141
|
+
role: `Agent in ${dir}`,
|
|
142
|
+
trigger: "manual",
|
|
143
|
+
filePath: join2(squadPath, file)
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return agents;
|
|
149
|
+
}
|
|
150
|
+
function parseSquadFile(filePath) {
|
|
151
|
+
const rawContent = readFileSync2(filePath, "utf-8");
|
|
152
|
+
const { data: frontmatter, content: bodyContent } = matter(rawContent);
|
|
153
|
+
const fm = frontmatter;
|
|
154
|
+
const lines = bodyContent.split("\n");
|
|
155
|
+
const squad = {
|
|
156
|
+
name: fm.name || basename(filePath).replace(".md", ""),
|
|
157
|
+
mission: fm.mission || "",
|
|
158
|
+
agents: [],
|
|
159
|
+
pipelines: [],
|
|
160
|
+
triggers: { scheduled: [], event: [], manual: [] },
|
|
161
|
+
routines: [],
|
|
162
|
+
dependencies: [],
|
|
163
|
+
outputPath: "",
|
|
164
|
+
goals: [],
|
|
165
|
+
// Apply frontmatter fields
|
|
166
|
+
effort: fm.effort,
|
|
167
|
+
context: fm.context,
|
|
168
|
+
repo: fm.repo,
|
|
169
|
+
stack: fm.stack,
|
|
170
|
+
providers: fm.providers,
|
|
171
|
+
// Preserve raw frontmatter for KPIs and other custom fields
|
|
172
|
+
frontmatter
|
|
173
|
+
};
|
|
174
|
+
let currentSection = "";
|
|
175
|
+
let inTable = false;
|
|
176
|
+
let tableHeaders = [];
|
|
177
|
+
for (const line of lines) {
|
|
178
|
+
if (line.startsWith("# Squad:")) {
|
|
179
|
+
squad.name = line.replace("# Squad:", "").trim().toLowerCase();
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (line.startsWith("## ")) {
|
|
183
|
+
currentSection = line.replace("## ", "").trim().toLowerCase();
|
|
184
|
+
inTable = false;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (currentSection === "mission" && line.trim() && !line.startsWith("#")) {
|
|
188
|
+
if (!squad.mission) {
|
|
189
|
+
squad.mission = line.trim();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const effortMatch = line.match(/^effort:\s*(high|medium|low)/i);
|
|
193
|
+
if (effortMatch && !squad.effort) {
|
|
194
|
+
squad.effort = effortMatch[1].toLowerCase();
|
|
195
|
+
}
|
|
196
|
+
if (currentSection.includes("agent") || currentSection.includes("orchestrator") || currentSection.includes("evaluator") || currentSection.includes("builder") || currentSection.includes("priority")) {
|
|
197
|
+
if (line.includes("|") && line.includes("Agent")) {
|
|
198
|
+
inTable = true;
|
|
199
|
+
tableHeaders = line.split("|").map((h) => h.trim().toLowerCase());
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (inTable && line.includes("|") && !line.includes("---")) {
|
|
203
|
+
const cells = line.split("|").map((c) => c.trim().replace(/`/g, ""));
|
|
204
|
+
const agentIdx = tableHeaders.findIndex((h) => h === "agent");
|
|
205
|
+
const roleIdx = tableHeaders.findIndex((h) => h === "role");
|
|
206
|
+
const triggerIdx = tableHeaders.findIndex((h) => h === "trigger");
|
|
207
|
+
const statusIdx = tableHeaders.findIndex((h) => h === "status");
|
|
208
|
+
const effortIdx = tableHeaders.findIndex((h) => h === "effort");
|
|
209
|
+
if (agentIdx >= 0 && cells[agentIdx]) {
|
|
210
|
+
const effortValue = effortIdx >= 0 ? cells[effortIdx]?.toLowerCase() : void 0;
|
|
211
|
+
const effort = ["high", "medium", "low"].includes(effortValue || "") ? effortValue : void 0;
|
|
212
|
+
squad.agents.push({
|
|
213
|
+
name: cells[agentIdx],
|
|
214
|
+
role: roleIdx >= 0 ? cells[roleIdx] : "",
|
|
215
|
+
trigger: triggerIdx >= 0 ? cells[triggerIdx] : "manual",
|
|
216
|
+
status: statusIdx >= 0 ? cells[statusIdx] : "active",
|
|
217
|
+
effort
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (line.includes("\u2192") && line.includes("`")) {
|
|
223
|
+
const pipelineMatch = line.match(/`([^`]+)`\s*→\s*`([^`]+)`/g);
|
|
224
|
+
if (pipelineMatch) {
|
|
225
|
+
const agentNames = line.match(/`([^`]+)`/g)?.map((m) => m.replace(/`/g, "")) || [];
|
|
226
|
+
if (agentNames.length >= 2) {
|
|
227
|
+
squad.pipelines.push({
|
|
228
|
+
name: "default",
|
|
229
|
+
agents: agentNames
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (line.toLowerCase().includes("pipeline:")) {
|
|
235
|
+
const pipelineContent = line.split(":")[1];
|
|
236
|
+
if (pipelineContent && pipelineContent.includes("\u2192")) {
|
|
237
|
+
const agentNames = pipelineContent.match(/`([^`]+)`/g)?.map((m) => m.replace(/`/g, "")) || [];
|
|
238
|
+
if (agentNames.length >= 2) {
|
|
239
|
+
squad.pipelines.push({
|
|
240
|
+
name: "default",
|
|
241
|
+
agents: agentNames
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (line.toLowerCase().includes("primary") && line.includes("`")) {
|
|
247
|
+
const match = line.match(/`([^`]+)`/);
|
|
248
|
+
if (match) {
|
|
249
|
+
squad.outputPath = match[1].replace(/\/$/, "");
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (currentSection === "goals") {
|
|
253
|
+
const goalMatch = line.match(/^-\s*\[([ x])\]\s*(.+)$/);
|
|
254
|
+
if (goalMatch) {
|
|
255
|
+
const completed = goalMatch[1] === "x";
|
|
256
|
+
let description = goalMatch[2].trim();
|
|
257
|
+
let progress;
|
|
258
|
+
const progressMatch = description.match(/\(progress:\s*([^)]+)\)/i);
|
|
259
|
+
if (progressMatch) {
|
|
260
|
+
progress = progressMatch[1];
|
|
261
|
+
description = description.replace(progressMatch[0], "").trim();
|
|
262
|
+
}
|
|
263
|
+
squad.goals.push({
|
|
264
|
+
description,
|
|
265
|
+
completed,
|
|
266
|
+
progress
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return squad;
|
|
272
|
+
}
|
|
273
|
+
function loadSquad(squadName) {
|
|
274
|
+
const squadsDir = findSquadsDir();
|
|
275
|
+
if (!squadsDir) return null;
|
|
276
|
+
const squadFile = join2(squadsDir, squadName, "SQUAD.md");
|
|
277
|
+
if (!existsSync2(squadFile)) return null;
|
|
278
|
+
return parseSquadFile(squadFile);
|
|
279
|
+
}
|
|
280
|
+
function loadAgentDefinition(agentPath) {
|
|
281
|
+
if (!existsSync2(agentPath)) return "";
|
|
282
|
+
return readFileSync2(agentPath, "utf-8");
|
|
283
|
+
}
|
|
284
|
+
function parseAgentProvider(agentPath) {
|
|
285
|
+
if (!existsSync2(agentPath)) return void 0;
|
|
286
|
+
const content = readFileSync2(agentPath, "utf-8");
|
|
287
|
+
try {
|
|
288
|
+
const { data: frontmatter } = matter(content);
|
|
289
|
+
if (frontmatter?.provider && typeof frontmatter.provider === "string") {
|
|
290
|
+
return frontmatter.provider.toLowerCase();
|
|
291
|
+
}
|
|
292
|
+
} catch {
|
|
293
|
+
}
|
|
294
|
+
const providerHeaderMatch = content.match(/##\s*Provider\s*\n+([a-zA-Z0-9_-]+)/i);
|
|
295
|
+
if (providerHeaderMatch) {
|
|
296
|
+
return providerHeaderMatch[1].toLowerCase();
|
|
297
|
+
}
|
|
298
|
+
return void 0;
|
|
299
|
+
}
|
|
300
|
+
function addGoalToSquad(squadName, goal) {
|
|
301
|
+
const squadsDir = findSquadsDir();
|
|
302
|
+
if (!squadsDir) return false;
|
|
303
|
+
const squadFile = join2(squadsDir, squadName, "SQUAD.md");
|
|
304
|
+
if (!existsSync2(squadFile)) return false;
|
|
305
|
+
let content = readFileSync2(squadFile, "utf-8");
|
|
306
|
+
if (!content.includes("## Goals")) {
|
|
307
|
+
const insertPoint = content.indexOf("## Dependencies");
|
|
308
|
+
if (insertPoint > 0) {
|
|
309
|
+
content = content.slice(0, insertPoint) + `## Goals
|
|
310
|
+
|
|
311
|
+
- [ ] ${goal}
|
|
312
|
+
|
|
313
|
+
` + content.slice(insertPoint);
|
|
314
|
+
} else {
|
|
315
|
+
content += `
|
|
316
|
+
## Goals
|
|
317
|
+
|
|
318
|
+
- [ ] ${goal}
|
|
319
|
+
`;
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
const goalsIdx = content.indexOf("## Goals");
|
|
323
|
+
const nextSectionIdx = content.indexOf("\n## ", goalsIdx + 1);
|
|
324
|
+
const endIdx = nextSectionIdx > 0 ? nextSectionIdx : content.length;
|
|
325
|
+
const goalsSection = content.slice(goalsIdx, endIdx);
|
|
326
|
+
const lastGoalMatch = goalsSection.match(/^-\s*\[[ x]\].+$/gm);
|
|
327
|
+
if (lastGoalMatch) {
|
|
328
|
+
const lastGoal = lastGoalMatch[lastGoalMatch.length - 1];
|
|
329
|
+
const lastGoalIdx = content.lastIndexOf(lastGoal, endIdx);
|
|
330
|
+
const insertPos = lastGoalIdx + lastGoal.length;
|
|
331
|
+
content = content.slice(0, insertPos) + `
|
|
332
|
+
- [ ] ${goal}` + content.slice(insertPos);
|
|
333
|
+
} else {
|
|
334
|
+
const headerEnd = goalsIdx + "## Goals".length;
|
|
335
|
+
content = content.slice(0, headerEnd) + `
|
|
336
|
+
|
|
337
|
+
- [ ] ${goal}` + content.slice(headerEnd);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
writeFileSync2(squadFile, content);
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
function updateGoalInSquad(squadName, goalIndex, updates) {
|
|
344
|
+
const squadsDir = findSquadsDir();
|
|
345
|
+
if (!squadsDir) return false;
|
|
346
|
+
const squadFile = join2(squadsDir, squadName, "SQUAD.md");
|
|
347
|
+
if (!existsSync2(squadFile)) return false;
|
|
348
|
+
const content = readFileSync2(squadFile, "utf-8");
|
|
349
|
+
const lines = content.split("\n");
|
|
350
|
+
let currentSection = "";
|
|
351
|
+
let goalCount = 0;
|
|
352
|
+
for (let i = 0; i < lines.length; i++) {
|
|
353
|
+
const line = lines[i];
|
|
354
|
+
if (line.startsWith("## ")) {
|
|
355
|
+
currentSection = line.replace("## ", "").trim().toLowerCase();
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (currentSection === "goals") {
|
|
359
|
+
const goalMatch = line.match(/^-\s*\[([ x])\]\s*(.+)$/);
|
|
360
|
+
if (goalMatch) {
|
|
361
|
+
if (goalCount === goalIndex) {
|
|
362
|
+
let newLine = "- [" + (updates.completed ? "x" : " ") + "] " + goalMatch[2];
|
|
363
|
+
if (updates.progress !== void 0) {
|
|
364
|
+
newLine = newLine.replace(/\s*\(progress:\s*[^)]+\)/i, "");
|
|
365
|
+
if (updates.progress) {
|
|
366
|
+
newLine += ` (progress: ${updates.progress})`;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
lines[i] = newLine;
|
|
370
|
+
writeFileSync2(squadFile, lines.join("\n"));
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
goalCount++;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
function findSkillsDir() {
|
|
380
|
+
const projectRoot = findProjectRoot();
|
|
381
|
+
if (!projectRoot) return null;
|
|
382
|
+
const skillsDir = join2(projectRoot, ".claude", "skills");
|
|
383
|
+
return existsSync2(skillsDir) ? skillsDir : null;
|
|
384
|
+
}
|
|
385
|
+
function findMemoryDir() {
|
|
386
|
+
const projectRoot = findProjectRoot();
|
|
387
|
+
if (!projectRoot) return null;
|
|
388
|
+
const memoryDir = join2(projectRoot, ".agents", "memory");
|
|
389
|
+
return existsSync2(memoryDir) ? memoryDir : null;
|
|
390
|
+
}
|
|
391
|
+
function resolveSkillPath(skillName) {
|
|
392
|
+
const skillsDir = findSkillsDir();
|
|
393
|
+
if (!skillsDir) return null;
|
|
394
|
+
const skillPath = join2(skillsDir, skillName);
|
|
395
|
+
return existsSync2(skillPath) ? skillPath : null;
|
|
396
|
+
}
|
|
397
|
+
function resolveMemoryPaths(patterns) {
|
|
398
|
+
const memoryDir = findMemoryDir();
|
|
399
|
+
if (!memoryDir) return [];
|
|
400
|
+
const resolved = [];
|
|
401
|
+
for (const pattern of patterns) {
|
|
402
|
+
if (pattern.endsWith("/*")) {
|
|
403
|
+
const subdir = pattern.slice(0, -2);
|
|
404
|
+
const subdirPath = join2(memoryDir, subdir);
|
|
405
|
+
if (existsSync2(subdirPath)) {
|
|
406
|
+
try {
|
|
407
|
+
const files = readdirSync(subdirPath);
|
|
408
|
+
for (const file of files) {
|
|
409
|
+
if (file.endsWith(".md")) {
|
|
410
|
+
resolved.push(join2(subdirPath, file));
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
} catch {
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
} else {
|
|
417
|
+
const fullPath = join2(memoryDir, pattern);
|
|
418
|
+
if (existsSync2(fullPath)) {
|
|
419
|
+
resolved.push(fullPath);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return resolved;
|
|
424
|
+
}
|
|
425
|
+
function resolveExecutionContext(squad, forceRegenerate = false) {
|
|
426
|
+
const ctx = squad.context || {};
|
|
427
|
+
const mcpResolution = resolveMcpConfig(
|
|
428
|
+
squad.name,
|
|
429
|
+
ctx.mcp,
|
|
430
|
+
forceRegenerate
|
|
431
|
+
);
|
|
432
|
+
const skillPaths = [];
|
|
433
|
+
if (ctx.skills) {
|
|
434
|
+
for (const skill of ctx.skills) {
|
|
435
|
+
const path = resolveSkillPath(skill);
|
|
436
|
+
if (path) {
|
|
437
|
+
skillPaths.push(path);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const memoryPaths = ctx.memory?.load ? resolveMemoryPaths(ctx.memory.load) : [];
|
|
442
|
+
return {
|
|
443
|
+
// Copy all SquadContext fields
|
|
444
|
+
...ctx,
|
|
445
|
+
// Add squad name
|
|
446
|
+
squadName: squad.name,
|
|
447
|
+
// Add resolved paths
|
|
448
|
+
resolved: {
|
|
449
|
+
mcpConfigPath: mcpResolution.path,
|
|
450
|
+
mcpSource: mcpResolution.source,
|
|
451
|
+
mcpServers: mcpResolution.servers || [],
|
|
452
|
+
skillPaths,
|
|
453
|
+
memoryPaths
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export {
|
|
459
|
+
resolveMcpConfigPath,
|
|
460
|
+
findSquadsDir,
|
|
461
|
+
findProjectRoot,
|
|
462
|
+
hasLocalInfraConfig,
|
|
463
|
+
listSquads,
|
|
464
|
+
listAgents,
|
|
465
|
+
parseSquadFile,
|
|
466
|
+
loadSquad,
|
|
467
|
+
loadAgentDefinition,
|
|
468
|
+
parseAgentProvider,
|
|
469
|
+
addGoalToSquad,
|
|
470
|
+
updateGoalInSquad,
|
|
471
|
+
resolveExecutionContext
|
|
472
|
+
};
|
|
473
|
+
//# sourceMappingURL=chunk-3TSY2K7R.js.map
|