squads-cli 0.4.11 → 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 +24 -4
- package/dist/chunk-3TSY2K7R.js +473 -0
- package/dist/chunk-3TSY2K7R.js.map +1 -0
- package/dist/cli.js +4292 -1091
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +54 -0
- package/dist/index.js +5 -1
- 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 +26 -0
- 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
|
|
|
@@ -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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/squad-parser.ts","../src/lib/mcp-config.ts"],"sourcesContent":["import { readFileSync, existsSync, readdirSync, writeFileSync } from 'fs';\nimport { join, basename } from 'path';\nimport matter from 'gray-matter';\nimport { resolveMcpConfig, type McpResolution } from './mcp-config.js';\n\nexport type EffortLevel = 'high' | 'medium' | 'low';\n\n// Context schema for frontmatter\nexport interface SquadContext {\n mcp?: string[];\n skills?: string[];\n memory?: {\n load?: string[];\n };\n model?: {\n default?: string;\n expensive?: string;\n cheap?: string;\n };\n budget?: {\n daily?: number;\n weekly?: number;\n perExecution?: number;\n };\n /** Cooldown between executions in seconds */\n cooldown?: number;\n}\n\n// Multi-LLM provider configuration\nexport interface SquadProviders {\n /** Default provider for all agents (default: anthropic) */\n default?: string;\n /** Provider for vision/image tasks */\n vision?: string;\n /** Provider for real-time data access */\n realtime?: string;\n /** Provider for high-volume/cheap operations */\n cheap?: string;\n /** Custom provider mappings by purpose */\n [key: string]: string | undefined;\n}\n\n// Frontmatter schema\nexport interface SquadFrontmatter {\n name?: string;\n mission?: string;\n repo?: string;\n stack?: string;\n context?: SquadContext;\n effort?: EffortLevel;\n /** Multi-LLM provider configuration */\n providers?: SquadProviders;\n}\n\nexport interface Agent {\n name: string;\n role: string;\n trigger: string;\n status?: string;\n filePath?: string;\n squad?: string;\n effort?: EffortLevel;\n /** LLM provider override (from agent file frontmatter) */\n provider?: string;\n /** Agent purpose (short description) */\n purpose?: string;\n /** Cron schedule for scheduled agents */\n schedule?: string;\n /** Output destinations */\n outputs?: string[];\n}\n\nexport interface Pipeline {\n name: string;\n agents: string[];\n}\n\nexport interface Goal {\n description: string;\n completed: boolean;\n progress?: string;\n metrics?: string[];\n}\n\n/**\n * Routine definition for autonomous scheduled execution.\n * Defined in SQUAD.md under ### Routines yaml block.\n */\nexport interface Routine {\n /** Unique name for the routine */\n name: string;\n /** Cron schedule (e.g., \"0 8 * * *\" for daily 8am) */\n schedule: string;\n /** Agents to run in this batch */\n agents: string[];\n /** Model to use (defaults to squad default or sonnet) */\n model?: 'opus' | 'sonnet' | 'haiku';\n /** Whether the routine is enabled */\n enabled?: boolean;\n /** Priority for execution ordering (lower = higher priority) */\n priority?: number;\n /** Minimum cooldown between runs (e.g., \"6 hours\") */\n cooldown?: string;\n}\n\nexport interface Squad {\n name: string;\n mission: string;\n agents: Agent[];\n pipelines: Pipeline[];\n triggers: {\n scheduled: string[];\n event: string[];\n manual: string[];\n };\n /** Autonomous routines for scheduled batch execution */\n routines: Routine[];\n dependencies: string[];\n outputPath: string;\n goals: Goal[];\n effort?: EffortLevel; // Squad-level default effort\n context?: SquadContext; // Frontmatter context block\n repo?: string;\n stack?: string;\n /** Multi-LLM provider configuration */\n providers?: SquadProviders;\n /** Domain this squad operates in */\n domain?: string;\n /** Permissions for this squad */\n permissions?: Record<string, boolean>;\n /** Raw frontmatter for accessing KPIs and other custom fields */\n frontmatter?: Record<string, unknown>;\n}\n\n/**\n * Resolved execution context with paths and metadata.\n * Extends SquadContext with resolved paths for MCP, skills, and memory.\n */\nexport interface ExecutionContext extends SquadContext {\n /** Squad name this context belongs to */\n squadName: string;\n /** Resolved paths and metadata */\n resolved: {\n /** Path to MCP config file to use */\n mcpConfigPath: string;\n /** Source of MCP config resolution */\n mcpSource: 'user-override' | 'generated' | 'fallback';\n /** List of MCP servers in the config */\n mcpServers: string[];\n /** Resolved skill directory paths */\n skillPaths: string[];\n /** Resolved memory file paths */\n memoryPaths: string[];\n };\n}\n\nexport function findSquadsDir(): string | null {\n // Look for .agents/squads in current directory or parent directories\n let dir = process.cwd();\n\n for (let i = 0; i < 5; i++) {\n const squadsPath = join(dir, '.agents', 'squads');\n if (existsSync(squadsPath)) {\n return squadsPath;\n }\n const parent = join(dir, '..');\n if (parent === dir) break;\n dir = parent;\n }\n\n return null;\n}\n\nexport function findProjectRoot(): string | null {\n // Find the root of the squads project (where .agents/ lives)\n const squadsDir = findSquadsDir();\n if (!squadsDir) return null;\n // squadsDir is /path/to/.agents/squads, so go up 2 levels\n return join(squadsDir, '..', '..');\n}\n\nexport function hasLocalInfraConfig(): boolean {\n // Check if the project has a local .env file with infra config\n const projectRoot = findProjectRoot();\n if (!projectRoot) return false;\n\n const envPath = join(projectRoot, '.env');\n if (!existsSync(envPath)) return false;\n\n // Check if .env has any infra-related keys\n const content = readFileSync(envPath, 'utf-8');\n const infraKeys = ['LANGFUSE_', 'SQUADS_BRIDGE', 'SQUADS_POSTGRES', 'SQUADS_REDIS'];\n return infraKeys.some(key => content.includes(key));\n}\n\nexport function listSquads(squadsDir: string): string[] {\n const squads: string[] = [];\n\n const entries = readdirSync(squadsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('_')) {\n const squadFile = join(squadsDir, entry.name, 'SQUAD.md');\n if (existsSync(squadFile)) {\n squads.push(entry.name);\n }\n }\n }\n\n return squads;\n}\n\nexport function listAgents(squadsDir: string, squadName?: string): Agent[] {\n const agents: Agent[] = [];\n\n const dirs = squadName\n ? [squadName]\n : readdirSync(squadsDir, { withFileTypes: true })\n .filter(e => e.isDirectory() && !e.name.startsWith('_'))\n .map(e => e.name);\n\n for (const dir of dirs) {\n const squadPath = join(squadsDir, dir);\n if (!existsSync(squadPath)) continue;\n\n const files = readdirSync(squadPath);\n for (const file of files) {\n if (file.endsWith('.md') && file !== 'SQUAD.md') {\n const agentName = file.replace('.md', '');\n agents.push({\n name: agentName,\n role: `Agent in ${dir}`,\n trigger: 'manual',\n filePath: join(squadPath, file)\n });\n }\n }\n }\n\n return agents;\n}\n\nexport function parseSquadFile(filePath: string): Squad {\n const rawContent = readFileSync(filePath, 'utf-8');\n\n // Parse frontmatter with gray-matter\n const { data: frontmatter, content: bodyContent } = matter(rawContent);\n const fm = frontmatter as SquadFrontmatter;\n\n const lines = bodyContent.split('\\n');\n\n const squad: Squad = {\n name: fm.name || basename(filePath).replace('.md', ''),\n mission: fm.mission || '',\n agents: [],\n pipelines: [],\n triggers: { scheduled: [], event: [], manual: [] },\n routines: [],\n dependencies: [],\n outputPath: '',\n goals: [],\n // Apply frontmatter fields\n effort: fm.effort,\n context: fm.context,\n repo: fm.repo,\n stack: fm.stack,\n providers: fm.providers,\n // Preserve raw frontmatter for KPIs and other custom fields\n frontmatter: frontmatter as Record<string, unknown>,\n };\n\n let currentSection = '';\n let inTable = false;\n let tableHeaders: string[] = [];\n\n for (const line of lines) {\n // Extract squad name from title\n if (line.startsWith('# Squad:')) {\n squad.name = line.replace('# Squad:', '').trim().toLowerCase();\n continue;\n }\n\n // Track sections\n if (line.startsWith('## ')) {\n currentSection = line.replace('## ', '').trim().toLowerCase();\n inTable = false;\n continue;\n }\n\n // Extract mission\n if (currentSection === 'mission' && line.trim() && !line.startsWith('#')) {\n if (!squad.mission) {\n squad.mission = line.trim();\n }\n }\n\n // Extract squad-level effort (e.g., \"effort: medium\" in Context section)\n const effortMatch = line.match(/^effort:\\s*(high|medium|low)/i);\n if (effortMatch && !squad.effort) {\n squad.effort = effortMatch[1].toLowerCase() as EffortLevel;\n }\n\n // Parse agent tables\n if (currentSection.includes('agent') || currentSection.includes('orchestrator') ||\n currentSection.includes('evaluator') || currentSection.includes('builder') ||\n currentSection.includes('priority')) {\n\n if (line.includes('|') && line.includes('Agent')) {\n inTable = true;\n tableHeaders = line.split('|').map(h => h.trim().toLowerCase());\n continue;\n }\n\n if (inTable && line.includes('|') && !line.includes('---')) {\n const cells = line.split('|').map(c => c.trim().replace(/`/g, ''));\n const agentIdx = tableHeaders.findIndex(h => h === 'agent');\n const roleIdx = tableHeaders.findIndex(h => h === 'role');\n const triggerIdx = tableHeaders.findIndex(h => h === 'trigger');\n const statusIdx = tableHeaders.findIndex(h => h === 'status');\n const effortIdx = tableHeaders.findIndex(h => h === 'effort');\n\n if (agentIdx >= 0 && cells[agentIdx]) {\n const effortValue = effortIdx >= 0 ? cells[effortIdx]?.toLowerCase() : undefined;\n const effort = ['high', 'medium', 'low'].includes(effortValue || '')\n ? effortValue as EffortLevel\n : undefined;\n\n squad.agents.push({\n name: cells[agentIdx],\n role: roleIdx >= 0 ? cells[roleIdx] : '',\n trigger: triggerIdx >= 0 ? cells[triggerIdx] : 'manual',\n status: statusIdx >= 0 ? cells[statusIdx] : 'active',\n effort\n });\n }\n }\n }\n\n // Parse pipelines (looking for patterns like: agent1 → agent2 → agent3)\n if (line.includes('→') && line.includes('`')) {\n const pipelineMatch = line.match(/`([^`]+)`\\s*→\\s*`([^`]+)`/g);\n if (pipelineMatch) {\n const agentNames = line.match(/`([^`]+)`/g)?.map(m => m.replace(/`/g, '')) || [];\n if (agentNames.length >= 2) {\n squad.pipelines.push({\n name: 'default',\n agents: agentNames\n });\n }\n }\n }\n\n // Also look for Pipeline: format\n if (line.toLowerCase().includes('pipeline:')) {\n const pipelineContent = line.split(':')[1];\n if (pipelineContent && pipelineContent.includes('→')) {\n const agentNames = pipelineContent.match(/`([^`]+)`/g)?.map(m => m.replace(/`/g, '')) || [];\n if (agentNames.length >= 2) {\n squad.pipelines.push({\n name: 'default',\n agents: agentNames\n });\n }\n }\n }\n\n // Extract output path\n if (line.toLowerCase().includes('primary') && line.includes('`')) {\n const match = line.match(/`([^`]+)`/);\n if (match) {\n squad.outputPath = match[1].replace(/\\/$/, '');\n }\n }\n\n // Parse goals (checkbox format: - [ ] or - [x])\n if (currentSection === 'goals') {\n const goalMatch = line.match(/^-\\s*\\[([ x])\\]\\s*(.+)$/);\n if (goalMatch) {\n const completed = goalMatch[1] === 'x';\n let description = goalMatch[2].trim();\n let progress: string | undefined;\n\n // Check for progress annotation\n const progressMatch = description.match(/\\(progress:\\s*([^)]+)\\)/i);\n if (progressMatch) {\n progress = progressMatch[1];\n description = description.replace(progressMatch[0], '').trim();\n }\n\n squad.goals.push({\n description,\n completed,\n progress\n });\n }\n }\n }\n\n return squad;\n}\n\nexport function loadSquad(squadName: string): Squad | null {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return null;\n\n const squadFile = join(squadsDir, squadName, 'SQUAD.md');\n if (!existsSync(squadFile)) return null;\n\n return parseSquadFile(squadFile);\n}\n\nexport function loadAgentDefinition(agentPath: string): string {\n if (!existsSync(agentPath)) return '';\n return readFileSync(agentPath, 'utf-8');\n}\n\n/**\n * Parse provider from an agent definition file.\n *\n * Looks for:\n * 1. Frontmatter: `provider: xai`\n * 2. Header syntax: `## Provider\\nxai`\n *\n * @returns Provider ID or undefined if not specified\n */\nexport function parseAgentProvider(agentPath: string): string | undefined {\n if (!existsSync(agentPath)) return undefined;\n\n const content = readFileSync(agentPath, 'utf-8');\n\n // Try parsing frontmatter\n try {\n const { data: frontmatter } = matter(content);\n if (frontmatter?.provider && typeof frontmatter.provider === 'string') {\n return frontmatter.provider.toLowerCase();\n }\n } catch {\n // Ignore frontmatter parsing errors\n }\n\n // Try header syntax: ## Provider\\n<provider>\n const providerHeaderMatch = content.match(/##\\s*Provider\\s*\\n+([a-zA-Z0-9_-]+)/i);\n if (providerHeaderMatch) {\n return providerHeaderMatch[1].toLowerCase();\n }\n\n return undefined;\n}\n\nexport function addGoalToSquad(squadName: string, goal: string): boolean {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return false;\n\n const squadFile = join(squadsDir, squadName, 'SQUAD.md');\n if (!existsSync(squadFile)) return false;\n\n let content = readFileSync(squadFile, 'utf-8');\n\n // Check if Goals section exists\n if (!content.includes('## Goals')) {\n // Add Goals section before Dependencies or at end\n const insertPoint = content.indexOf('## Dependencies');\n if (insertPoint > 0) {\n content = content.slice(0, insertPoint) + `## Goals\\n\\n- [ ] ${goal}\\n\\n` + content.slice(insertPoint);\n } else {\n content += `\\n## Goals\\n\\n- [ ] ${goal}\\n`;\n }\n } else {\n // Add to existing Goals section\n const goalsIdx = content.indexOf('## Goals');\n const nextSectionIdx = content.indexOf('\\n## ', goalsIdx + 1);\n const endIdx = nextSectionIdx > 0 ? nextSectionIdx : content.length;\n\n // Find last goal line or section header\n const goalsSection = content.slice(goalsIdx, endIdx);\n const lastGoalMatch = goalsSection.match(/^-\\s*\\[[ x]\\].+$/gm);\n\n if (lastGoalMatch) {\n // Add after last goal\n const lastGoal = lastGoalMatch[lastGoalMatch.length - 1];\n const lastGoalIdx = content.lastIndexOf(lastGoal, endIdx);\n const insertPos = lastGoalIdx + lastGoal.length;\n content = content.slice(0, insertPos) + `\\n- [ ] ${goal}` + content.slice(insertPos);\n } else {\n // No goals yet, add after section header\n const headerEnd = goalsIdx + '## Goals'.length;\n content = content.slice(0, headerEnd) + `\\n\\n- [ ] ${goal}` + content.slice(headerEnd);\n }\n }\n\n writeFileSync(squadFile, content);\n return true;\n}\n\nexport function updateGoalInSquad(\n squadName: string,\n goalIndex: number,\n updates: { completed?: boolean; progress?: string }\n): boolean {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return false;\n\n const squadFile = join(squadsDir, squadName, 'SQUAD.md');\n if (!existsSync(squadFile)) return false;\n\n const content = readFileSync(squadFile, 'utf-8');\n const lines = content.split('\\n');\n\n let currentSection = '';\n let goalCount = 0;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n if (line.startsWith('## ')) {\n currentSection = line.replace('## ', '').trim().toLowerCase();\n continue;\n }\n\n if (currentSection === 'goals') {\n const goalMatch = line.match(/^-\\s*\\[([ x])\\]\\s*(.+)$/);\n if (goalMatch) {\n if (goalCount === goalIndex) {\n let newLine = '- [' + (updates.completed ? 'x' : ' ') + '] ' + goalMatch[2];\n\n // Handle progress update\n if (updates.progress !== undefined) {\n // Remove existing progress annotation\n newLine = newLine.replace(/\\s*\\(progress:\\s*[^)]+\\)/i, '');\n if (updates.progress) {\n newLine += ` (progress: ${updates.progress})`;\n }\n }\n\n lines[i] = newLine;\n writeFileSync(squadFile, lines.join('\\n'));\n return true;\n }\n goalCount++;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Find the skills directory (.claude/skills)\n */\nfunction findSkillsDir(): string | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return null;\n\n const skillsDir = join(projectRoot, '.claude', 'skills');\n return existsSync(skillsDir) ? skillsDir : null;\n}\n\n/**\n * Find the memory directory (.agents/memory)\n */\nfunction findMemoryDir(): string | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return null;\n\n const memoryDir = join(projectRoot, '.agents', 'memory');\n return existsSync(memoryDir) ? memoryDir : null;\n}\n\n/**\n * Resolve a skill name to its directory path.\n */\nfunction resolveSkillPath(skillName: string): string | null {\n const skillsDir = findSkillsDir();\n if (!skillsDir) return null;\n\n const skillPath = join(skillsDir, skillName);\n return existsSync(skillPath) ? skillPath : null;\n}\n\n/**\n * Resolve memory glob patterns to actual file paths.\n */\nfunction resolveMemoryPaths(patterns: string[]): string[] {\n const memoryDir = findMemoryDir();\n if (!memoryDir) return [];\n\n const resolved: string[] = [];\n\n for (const pattern of patterns) {\n // Handle simple patterns like \"intelligence/*\" or \"research/*\"\n if (pattern.endsWith('/*')) {\n const subdir = pattern.slice(0, -2);\n const subdirPath = join(memoryDir, subdir);\n if (existsSync(subdirPath)) {\n // Add all .md files in the subdirectory\n try {\n const files = readdirSync(subdirPath);\n for (const file of files) {\n if (file.endsWith('.md')) {\n resolved.push(join(subdirPath, file));\n }\n }\n } catch {\n // Ignore read errors\n }\n }\n } else {\n // Direct path\n const fullPath = join(memoryDir, pattern);\n if (existsSync(fullPath)) {\n resolved.push(fullPath);\n }\n }\n }\n\n return resolved;\n}\n\n/**\n * Resolve execution context for a squad.\n *\n * Takes a Squad object and resolves all context references to actual paths:\n * - MCP config path (three-tier resolution)\n * - Skill directory paths\n * - Memory file paths\n *\n * @param squad - The squad to resolve context for\n * @param forceRegenerate - Force MCP config regeneration\n * @returns Resolved execution context with all paths\n */\nexport function resolveExecutionContext(\n squad: Squad,\n forceRegenerate = false\n): ExecutionContext {\n const ctx = squad.context || {};\n\n // Resolve MCP config\n const mcpResolution: McpResolution = resolveMcpConfig(\n squad.name,\n ctx.mcp,\n forceRegenerate\n );\n\n // Resolve skill paths\n const skillPaths: string[] = [];\n if (ctx.skills) {\n for (const skill of ctx.skills) {\n const path = resolveSkillPath(skill);\n if (path) {\n skillPaths.push(path);\n }\n }\n }\n\n // Resolve memory paths\n const memoryPaths = ctx.memory?.load\n ? resolveMemoryPaths(ctx.memory.load)\n : [];\n\n return {\n // Copy all SquadContext fields\n ...ctx,\n // Add squad name\n squadName: squad.name,\n // Add resolved paths\n resolved: {\n mcpConfigPath: mcpResolution.path,\n mcpSource: mcpResolution.source,\n mcpServers: mcpResolution.servers || [],\n skillPaths,\n memoryPaths,\n },\n };\n}\n","/**\n * MCP Config Generation and Resolution\n *\n * Provides dynamic MCP config generation based on squad context.\n * Three-tier resolution:\n * 1. User override: ~/.claude/mcp-configs/{squad}.json\n * 2. Generated from context.mcp: ~/.claude/contexts/{squad}.mcp.json\n * 3. Fallback: ~/.claude.json\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';\nimport { join, dirname } from 'path';\n\n/**\n * MCP server definition structure (matches Claude's .mcp.json format)\n */\nexport interface McpServerDef {\n type: 'stdio';\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\n/**\n * Full MCP config structure\n */\nexport interface McpConfig {\n mcpServers: Record<string, McpServerDef>;\n}\n\n/**\n * Registry of known MCP servers with their configurations.\n * These can be referenced by name in SQUAD.md context.mcp arrays.\n *\n * NOTE: We prefer CLI tools over MCP when possible (less complexity).\n * MCP is reserved for APIs that don't have good CLI alternatives.\n */\n/**\n * MCP Registry is now empty - we prefer CLI tools over MCP.\n *\n * Replacements:\n * - x-mcp → curl (see curl-master skill)\n * - nano-banana → scripts/img-gen CLI\n *\n * If you need to add MCP servers, add them here.\n */\nconst SERVER_REGISTRY: Record<string, McpServerDef> = {};\n\n/**\n * Get the home directory path.\n */\nfunction getHome(): string {\n return process.env.HOME || process.env.USERPROFILE || '';\n}\n\n/**\n * Get the path to generated contexts directory.\n */\nexport function getContextsDir(): string {\n return join(getHome(), '.claude', 'contexts');\n}\n\n/**\n * Get the path to user MCP configs directory.\n */\nexport function getMcpConfigsDir(): string {\n return join(getHome(), '.claude', 'mcp-configs');\n}\n\n/**\n * Check if a server is in the registry.\n */\nexport function isKnownServer(serverName: string): boolean {\n return serverName in SERVER_REGISTRY;\n}\n\n/**\n * Get server definition from registry.\n */\nexport function getServerDef(serverName: string): McpServerDef | undefined {\n return SERVER_REGISTRY[serverName];\n}\n\n/**\n * List all known servers in the registry.\n */\nexport function listKnownServers(): string[] {\n return Object.keys(SERVER_REGISTRY);\n}\n\n/**\n * Generate an MCP config from a list of server names.\n *\n * @param mcpServers - Array of server names to include\n * @returns Generated MCP config object\n */\nexport function generateMcpConfig(mcpServers: string[]): McpConfig {\n const config: McpConfig = { mcpServers: {} };\n\n for (const server of mcpServers) {\n const def = SERVER_REGISTRY[server];\n if (def) {\n config.mcpServers[server] = def;\n }\n // Unknown servers are silently skipped - they may be custom user servers\n }\n\n return config;\n}\n\n/**\n * Write an MCP config to a file.\n *\n * @param config - MCP config to write\n * @param path - Destination path\n */\nexport function writeMcpConfig(config: McpConfig, path: string): void {\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(path, JSON.stringify(config, null, 2));\n}\n\n/**\n * Read an existing MCP config file.\n *\n * @param path - Path to config file\n * @returns Parsed config or null if not found\n */\nexport function readMcpConfig(path: string): McpConfig | null {\n if (!existsSync(path)) return null;\n try {\n const content = readFileSync(path, 'utf-8');\n return JSON.parse(content) as McpConfig;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolution result with metadata.\n */\nexport interface McpResolution {\n /** Path to the MCP config to use */\n path: string;\n /** How the config was resolved */\n source: 'user-override' | 'generated' | 'fallback';\n /** Servers included (if generated or read) */\n servers?: string[];\n /** Whether config was freshly generated */\n generated?: boolean;\n}\n\n/**\n * Resolve the MCP config path for a squad using three-tier resolution:\n *\n * 1. User override: ~/.claude/mcp-configs/{squad}.json\n * 2. Generated from context.mcp: ~/.claude/contexts/{squad}.mcp.json\n * 3. Fallback: ~/.claude.json\n *\n * @param squadName - Name of the squad\n * @param mcpServers - Array of MCP server names from squad context (optional)\n * @param forceRegenerate - Force regeneration even if file exists\n * @returns Resolution result with path and metadata\n */\nexport function resolveMcpConfig(\n squadName: string,\n mcpServers?: string[],\n forceRegenerate = false\n): McpResolution {\n const home = getHome();\n\n // Tier 1: User override\n const userOverride = join(getMcpConfigsDir(), `${squadName}.json`);\n if (existsSync(userOverride)) {\n const config = readMcpConfig(userOverride);\n return {\n path: userOverride,\n source: 'user-override',\n servers: config ? Object.keys(config.mcpServers) : undefined,\n };\n }\n\n // Tier 2: Generate from context.mcp\n if (mcpServers && mcpServers.length > 0) {\n const generatedPath = join(getContextsDir(), `${squadName}.mcp.json`);\n\n // Check if we need to regenerate\n const shouldGenerate = forceRegenerate || !existsSync(generatedPath);\n\n if (shouldGenerate) {\n const config = generateMcpConfig(mcpServers);\n writeMcpConfig(config, generatedPath);\n\n return {\n path: generatedPath,\n source: 'generated',\n servers: Object.keys(config.mcpServers),\n generated: true,\n };\n }\n\n // Use existing generated config\n const config = readMcpConfig(generatedPath);\n return {\n path: generatedPath,\n source: 'generated',\n servers: config ? Object.keys(config.mcpServers) : mcpServers,\n generated: false,\n };\n }\n\n // Tier 3: Fallback to default\n return {\n path: join(home, '.claude.json'),\n source: 'fallback',\n };\n}\n\n/**\n * Convenience function to just get the path (for backward compatibility).\n */\nexport function resolveMcpConfigPath(\n squadName: string,\n mcpServers?: string[]\n): string {\n return resolveMcpConfig(squadName, mcpServers).path;\n}\n"],"mappings":"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","names":["readFileSync","existsSync","writeFileSync","join","config","join","existsSync","readFileSync","writeFileSync"]}
|