squads-cli 0.4.13 → 0.5.0

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.
Files changed (39) hide show
  1. package/dist/{chunk-HIQ2APYR.js → chunk-7PRYDHZW.js} +2 -2
  2. package/dist/chunk-BV6S5AWZ.js +419 -0
  3. package/dist/chunk-BV6S5AWZ.js.map +1 -0
  4. package/dist/{chunk-3TSY2K7R.js → chunk-QPH5OR7J.js} +4 -3
  5. package/dist/chunk-QPH5OR7J.js.map +1 -0
  6. package/dist/cli.js +4580 -3370
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.js +3 -2
  9. package/dist/index.js.map +1 -1
  10. package/dist/{memory-4PVUKIDK.js → memory-ZXDXF6KF.js} +8 -4
  11. package/dist/{sessions-R4VWIGFR.js → sessions-F6LRY7EN.js} +2 -2
  12. package/dist/{squad-parser-YRE2FEAA.js → squad-parser-MSYE4PXL.js} +2 -2
  13. package/dist/templates/core/AGENTS.md.template +64 -0
  14. package/dist/templates/core/BUSINESS_BRIEF.md.template +29 -0
  15. package/dist/templates/core/CLAUDE.md.template +50 -0
  16. package/dist/templates/core/provider.yaml.template +5 -0
  17. package/dist/templates/first-squad/SQUAD.md.template +23 -0
  18. package/dist/templates/first-squad/lead.md.template +44 -0
  19. package/dist/templates/memory/getting-started/state.md.template +19 -0
  20. package/dist/templates/skills/squads-learn/SKILL.md +86 -0
  21. package/dist/templates/skills/squads-workflow/instruction.md +70 -0
  22. package/docker/docker-compose.yml +16 -0
  23. package/package.json +4 -2
  24. package/templates/core/AGENTS.md.template +64 -0
  25. package/templates/core/BUSINESS_BRIEF.md.template +29 -0
  26. package/templates/core/CLAUDE.md.template +50 -0
  27. package/templates/core/provider.yaml.template +5 -0
  28. package/templates/first-squad/SQUAD.md.template +23 -0
  29. package/templates/first-squad/lead.md.template +44 -0
  30. package/templates/memory/getting-started/state.md.template +19 -0
  31. package/templates/skills/squads-learn/SKILL.md +86 -0
  32. package/templates/skills/squads-workflow/instruction.md +70 -0
  33. package/dist/chunk-3TSY2K7R.js.map +0 -1
  34. package/dist/chunk-FUHBEL3L.js +0 -203
  35. package/dist/chunk-FUHBEL3L.js.map +0 -1
  36. /package/dist/{chunk-HIQ2APYR.js.map → chunk-7PRYDHZW.js.map} +0 -0
  37. /package/dist/{memory-4PVUKIDK.js.map → memory-ZXDXF6KF.js.map} +0 -0
  38. /package/dist/{sessions-R4VWIGFR.js.map → sessions-F6LRY7EN.js.map} +0 -0
  39. /package/dist/{squad-parser-YRE2FEAA.js.map → squad-parser-MSYE4PXL.js.map} +0 -0
@@ -604,7 +604,7 @@ async function sessionsSummaryCommand(data, options = {}) {
604
604
  async function buildCurrentSessionSummary() {
605
605
  const { existsSync: existsSync2, readdirSync: readdirSync2, statSync, readFileSync: readFileSync2 } = await import("fs");
606
606
  const { join: join2 } = await import("path");
607
- const { findMemoryDir } = await import("./memory-4PVUKIDK.js");
607
+ const { findMemoryDir } = await import("./memory-ZXDXF6KF.js");
608
608
  const memoryDir = findMemoryDir();
609
609
  const squads = [];
610
610
  const filesUpdated = [];
@@ -764,4 +764,4 @@ export {
764
764
  buildCurrentSessionSummary,
765
765
  sessionsHistoryCommand
766
766
  };
767
- //# sourceMappingURL=chunk-HIQ2APYR.js.map
767
+ //# sourceMappingURL=chunk-7PRYDHZW.js.map
@@ -0,0 +1,419 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ __require
4
+ } from "./chunk-7OCVIDC7.js";
5
+
6
+ // src/lib/memory.ts
7
+ import { readFileSync, writeFileSync as writeFileSync2, existsSync as existsSync2, readdirSync, mkdirSync as mkdirSync2 } from "fs";
8
+ import { join as join2, dirname as dirname2 } from "path";
9
+
10
+ // src/lib/lock.ts
11
+ import Redis from "ioredis";
12
+ import { existsSync, writeFileSync, unlinkSync, mkdirSync } from "fs";
13
+ import { join, dirname } from "path";
14
+ import { createHash } from "crypto";
15
+ var LOCK_TTL_MS = 3e4;
16
+ var LOCK_RETRY_DELAY_MS = 100;
17
+ var LOCK_MAX_RETRIES = 50;
18
+ var redisClient = null;
19
+ var redisAvailable = null;
20
+ async function getRedis() {
21
+ if (redisAvailable === false) return null;
22
+ if (!redisClient) {
23
+ try {
24
+ redisClient = new Redis({
25
+ host: process.env.REDIS_HOST || "localhost",
26
+ port: parseInt(process.env.REDIS_PORT || "6379"),
27
+ connectTimeout: 1e3,
28
+ maxRetriesPerRequest: 1,
29
+ lazyConnect: true
30
+ });
31
+ await redisClient.connect();
32
+ redisAvailable = true;
33
+ } catch {
34
+ redisAvailable = false;
35
+ redisClient = null;
36
+ return null;
37
+ }
38
+ }
39
+ return redisClient;
40
+ }
41
+ function generateLockId() {
42
+ return `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
43
+ }
44
+ function getLockKey(filePath) {
45
+ const hash = createHash("md5").update(filePath).digest("hex").slice(0, 12);
46
+ return `squads:lock:${hash}`;
47
+ }
48
+ function getFileLockPath(filePath) {
49
+ const lockDir = join(dirname(filePath), ".locks");
50
+ const hash = createHash("md5").update(filePath).digest("hex").slice(0, 12);
51
+ return join(lockDir, `${hash}.lock`);
52
+ }
53
+ function sleep(ms) {
54
+ return new Promise((resolve) => setTimeout(resolve, ms));
55
+ }
56
+ async function acquireRedisLock(key, lockId) {
57
+ const redis = await getRedis();
58
+ if (!redis) return false;
59
+ try {
60
+ const result = await redis.set(key, lockId, "PX", LOCK_TTL_MS, "NX");
61
+ return result === "OK";
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
66
+ async function releaseRedisLock(key, lockId) {
67
+ const redis = await getRedis();
68
+ if (!redis) return false;
69
+ try {
70
+ const script = `
71
+ if redis.call("get", KEYS[1]) == ARGV[1] then
72
+ return redis.call("del", KEYS[1])
73
+ else
74
+ return 0
75
+ end
76
+ `;
77
+ const result = await redis.eval(script, 1, key, lockId);
78
+ return result === 1;
79
+ } catch {
80
+ return false;
81
+ }
82
+ }
83
+ function acquireFileLock(lockPath, lockId) {
84
+ try {
85
+ const lockDir = dirname(lockPath);
86
+ if (!existsSync(lockDir)) {
87
+ mkdirSync(lockDir, { recursive: true });
88
+ }
89
+ if (existsSync(lockPath)) {
90
+ const { statSync, readFileSync: readFileSync2 } = __require("fs");
91
+ const stats = statSync(lockPath);
92
+ const ageMs = Date.now() - stats.mtimeMs;
93
+ if (ageMs > LOCK_TTL_MS) {
94
+ unlinkSync(lockPath);
95
+ } else {
96
+ return false;
97
+ }
98
+ }
99
+ const { openSync, closeSync } = __require("fs");
100
+ const fd = openSync(lockPath, "wx");
101
+ writeFileSync(fd, lockId);
102
+ closeSync(fd);
103
+ return true;
104
+ } catch {
105
+ return false;
106
+ }
107
+ }
108
+ function releaseFileLock(lockPath, lockId) {
109
+ try {
110
+ if (!existsSync(lockPath)) return true;
111
+ const { readFileSync: readFileSync2 } = __require("fs");
112
+ const currentId = readFileSync2(lockPath, "utf-8").trim();
113
+ if (currentId === lockId) {
114
+ unlinkSync(lockPath);
115
+ return true;
116
+ }
117
+ return false;
118
+ } catch {
119
+ return false;
120
+ }
121
+ }
122
+ async function acquireLock(filePath) {
123
+ const lockId = generateLockId();
124
+ const redisKey = getLockKey(filePath);
125
+ const fileLockPath = getFileLockPath(filePath);
126
+ let useRedis = false;
127
+ for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt++) {
128
+ if (await acquireRedisLock(redisKey, lockId)) {
129
+ useRedis = true;
130
+ break;
131
+ }
132
+ if (redisAvailable === false && acquireFileLock(fileLockPath, lockId)) {
133
+ useRedis = false;
134
+ break;
135
+ }
136
+ if (attempt < LOCK_MAX_RETRIES - 1) {
137
+ await sleep(LOCK_RETRY_DELAY_MS);
138
+ }
139
+ }
140
+ const redis = await getRedis();
141
+ let gotLock = false;
142
+ if (redis) {
143
+ try {
144
+ const currentOwner = await redis.get(redisKey);
145
+ gotLock = currentOwner === lockId;
146
+ } catch {
147
+ gotLock = false;
148
+ }
149
+ } else {
150
+ try {
151
+ if (existsSync(fileLockPath)) {
152
+ const { readFileSync: readFileSync2 } = __require("fs");
153
+ const currentId = readFileSync2(fileLockPath, "utf-8").trim();
154
+ gotLock = currentId === lockId;
155
+ }
156
+ } catch {
157
+ gotLock = false;
158
+ }
159
+ }
160
+ if (!gotLock) {
161
+ return null;
162
+ }
163
+ return async () => {
164
+ if (useRedis) {
165
+ await releaseRedisLock(redisKey, lockId);
166
+ } else {
167
+ releaseFileLock(fileLockPath, lockId);
168
+ }
169
+ };
170
+ }
171
+ async function withLock(filePath, fn) {
172
+ const release = await acquireLock(filePath);
173
+ if (!release) {
174
+ throw new Error(`Failed to acquire lock for: ${filePath}`);
175
+ }
176
+ try {
177
+ return await fn();
178
+ } finally {
179
+ await release();
180
+ }
181
+ }
182
+
183
+ // src/lib/memory.ts
184
+ function findMemoryDir() {
185
+ let dir = process.cwd();
186
+ for (let i = 0; i < 5; i++) {
187
+ const memoryPath = join2(dir, ".agents", "memory");
188
+ if (existsSync2(memoryPath)) {
189
+ return memoryPath;
190
+ }
191
+ const parent = join2(dir, "..");
192
+ if (parent === dir) break;
193
+ dir = parent;
194
+ }
195
+ return null;
196
+ }
197
+ function listMemoryEntries(memoryDir) {
198
+ const entries = [];
199
+ const squads = readdirSync(memoryDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
200
+ for (const squad of squads) {
201
+ const squadPath = join2(memoryDir, squad);
202
+ const agents = readdirSync(squadPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
203
+ for (const agent of agents) {
204
+ const agentPath = join2(squadPath, agent);
205
+ const files = readdirSync(agentPath).filter((f) => f.endsWith(".md"));
206
+ for (const file of files) {
207
+ const filePath = join2(agentPath, file);
208
+ const type = file.replace(".md", "");
209
+ entries.push({
210
+ squad,
211
+ agent,
212
+ type,
213
+ content: readFileSync(filePath, "utf-8"),
214
+ path: filePath
215
+ });
216
+ }
217
+ }
218
+ }
219
+ return entries;
220
+ }
221
+ var SEMANTIC_EXPANSIONS = {
222
+ "pricing": ["price", "cost", "$", "revenue", "fee", "rate"],
223
+ "price": ["pricing", "cost", "$", "fee"],
224
+ "revenue": ["income", "sales", "mrr", "arr", "$"],
225
+ "cost": ["expense", "spend", "budget", "$", "price"],
226
+ "customer": ["client", "lead", "prospect", "user"],
227
+ "client": ["customer", "lead", "prospect"],
228
+ "lead": ["prospect", "customer", "client", "pipeline"],
229
+ "agent": ["squad", "bot", "ai"],
230
+ "squad": ["team", "agent", "group"],
231
+ "status": ["state", "progress", "health"],
232
+ "bug": ["issue", "error", "problem", "fix"],
233
+ "feature": ["capability", "function", "ability"]
234
+ };
235
+ function expandQuery(query) {
236
+ const words = query.toLowerCase().split(/\s+/);
237
+ const expanded = new Set(words);
238
+ for (const word of words) {
239
+ if (SEMANTIC_EXPANSIONS[word]) {
240
+ SEMANTIC_EXPANSIONS[word].forEach((syn) => expanded.add(syn));
241
+ }
242
+ }
243
+ return Array.from(expanded);
244
+ }
245
+ function getFileAge(filePath) {
246
+ try {
247
+ const { statSync } = __require("fs");
248
+ const stats = statSync(filePath);
249
+ const ageMs = Date.now() - stats.mtimeMs;
250
+ const ageDays = ageMs / (1e3 * 60 * 60 * 24);
251
+ return ageDays;
252
+ } catch {
253
+ return 999;
254
+ }
255
+ }
256
+ function searchMemory(query, memoryDir) {
257
+ const dir = memoryDir || findMemoryDir();
258
+ if (!dir) return [];
259
+ const entries = listMemoryEntries(dir);
260
+ const results = [];
261
+ const queryLower = query.toLowerCase();
262
+ const expandedTerms = expandQuery(queryLower);
263
+ for (const entry of entries) {
264
+ const contentLower = entry.content.toLowerCase();
265
+ const lines = entry.content.split("\n");
266
+ const matches = [];
267
+ let score = 0;
268
+ let directHits = 0;
269
+ let expandedHits = 0;
270
+ const directWords = queryLower.split(/\s+/);
271
+ for (const word of directWords) {
272
+ if (contentLower.includes(word)) {
273
+ directHits += 1;
274
+ score += 2;
275
+ }
276
+ }
277
+ for (const term of expandedTerms) {
278
+ if (contentLower.includes(term)) {
279
+ expandedHits += 1;
280
+ score += 0.5;
281
+ for (let i = 0; i < lines.length; i++) {
282
+ const line = lines[i];
283
+ if (line.toLowerCase().includes(term) && line.trim() && !matches.includes(line.trim())) {
284
+ matches.push(line.trim());
285
+ }
286
+ }
287
+ }
288
+ }
289
+ if (contentLower.includes(queryLower)) {
290
+ score += 5;
291
+ }
292
+ const ageDays = getFileAge(entry.path);
293
+ if (ageDays < 1) {
294
+ score *= 1.5;
295
+ } else if (ageDays < 7) {
296
+ score *= 1.2;
297
+ } else if (ageDays > 30) {
298
+ score *= 0.8;
299
+ }
300
+ const typeWeights = {
301
+ "state": 1.2,
302
+ // Current state slightly preferred
303
+ "learnings": 1.1,
304
+ // Learnings are valuable
305
+ "output": 1,
306
+ // Recent outputs
307
+ "feedback": 0.9
308
+ // Feedback less commonly needed
309
+ };
310
+ score *= typeWeights[entry.type] || 1;
311
+ if (score > 0 && (directHits > 0 || expandedHits > 1)) {
312
+ results.push({ entry, matches: matches.slice(0, 7), score });
313
+ }
314
+ }
315
+ return results.sort((a, b) => b.score - a.score);
316
+ }
317
+ function getSquadState(squadName) {
318
+ const memoryDir = findMemoryDir();
319
+ if (!memoryDir) return [];
320
+ const squadPath = join2(memoryDir, squadName);
321
+ if (!existsSync2(squadPath)) return [];
322
+ const entries = [];
323
+ const agents = readdirSync(squadPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
324
+ for (const agent of agents) {
325
+ const statePath = join2(squadPath, agent, "state.md");
326
+ if (existsSync2(statePath)) {
327
+ entries.push({
328
+ squad: squadName,
329
+ agent,
330
+ type: "state",
331
+ content: readFileSync(statePath, "utf-8"),
332
+ path: statePath
333
+ });
334
+ }
335
+ }
336
+ return entries;
337
+ }
338
+ async function updateMemory(squadName, agentName, type, content) {
339
+ const memoryDir = findMemoryDir();
340
+ if (!memoryDir) {
341
+ throw new Error("No .agents/memory directory found");
342
+ }
343
+ const filePath = join2(memoryDir, squadName, agentName, `${type}.md`);
344
+ const dir = dirname2(filePath);
345
+ await withLock(filePath, () => {
346
+ if (!existsSync2(dir)) {
347
+ mkdirSync2(dir, { recursive: true });
348
+ }
349
+ writeFileSync2(filePath, content);
350
+ });
351
+ }
352
+ function updateMemorySync(squadName, agentName, type, content) {
353
+ const memoryDir = findMemoryDir();
354
+ if (!memoryDir) {
355
+ throw new Error("No .agents/memory directory found");
356
+ }
357
+ const filePath = join2(memoryDir, squadName, agentName, `${type}.md`);
358
+ const dir = dirname2(filePath);
359
+ if (!existsSync2(dir)) {
360
+ mkdirSync2(dir, { recursive: true });
361
+ }
362
+ writeFileSync2(filePath, content);
363
+ }
364
+ async function appendToMemory(squadName, agentName, type, addition) {
365
+ const memoryDir = findMemoryDir();
366
+ if (!memoryDir) {
367
+ throw new Error("No .agents/memory directory found");
368
+ }
369
+ const filePath = join2(memoryDir, squadName, agentName, `${type}.md`);
370
+ await withLock(filePath, () => {
371
+ let existing = "";
372
+ if (existsSync2(filePath)) {
373
+ existing = readFileSync(filePath, "utf-8");
374
+ }
375
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
376
+ const newContent = existing + `
377
+
378
+ ---
379
+ _Added: ${timestamp}_
380
+
381
+ ${addition}`;
382
+ const dir = dirname2(filePath);
383
+ if (!existsSync2(dir)) {
384
+ mkdirSync2(dir, { recursive: true });
385
+ }
386
+ writeFileSync2(filePath, newContent.trim());
387
+ });
388
+ }
389
+ function appendToMemorySync(squadName, agentName, type, addition) {
390
+ const memoryDir = findMemoryDir();
391
+ if (!memoryDir) {
392
+ throw new Error("No .agents/memory directory found");
393
+ }
394
+ const filePath = join2(memoryDir, squadName, agentName, `${type}.md`);
395
+ let existing = "";
396
+ if (existsSync2(filePath)) {
397
+ existing = readFileSync(filePath, "utf-8");
398
+ }
399
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
400
+ const newContent = existing + `
401
+
402
+ ---
403
+ _Added: ${timestamp}_
404
+
405
+ ${addition}`;
406
+ updateMemorySync(squadName, agentName, type, newContent.trim());
407
+ }
408
+
409
+ export {
410
+ findMemoryDir,
411
+ listMemoryEntries,
412
+ searchMemory,
413
+ getSquadState,
414
+ updateMemory,
415
+ updateMemorySync,
416
+ appendToMemory,
417
+ appendToMemorySync
418
+ };
419
+ //# sourceMappingURL=chunk-BV6S5AWZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/memory.ts","../src/lib/lock.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { withLock } from './lock.js';\n\nexport interface MemoryEntry {\n squad: string;\n agent: string;\n type: 'state' | 'output' | 'learnings' | 'feedback';\n content: string;\n path: string;\n lastUpdated?: string;\n}\n\nexport interface SearchResult {\n entry: MemoryEntry;\n matches: string[];\n score: number;\n}\n\nexport function findMemoryDir(): string | null {\n let dir = process.cwd();\n\n for (let i = 0; i < 5; i++) {\n const memoryPath = join(dir, '.agents', 'memory');\n if (existsSync(memoryPath)) {\n return memoryPath;\n }\n const parent = join(dir, '..');\n if (parent === dir) break;\n dir = parent;\n }\n\n return null;\n}\n\nexport function listMemoryEntries(memoryDir: string): MemoryEntry[] {\n const entries: MemoryEntry[] = [];\n\n const squads = readdirSync(memoryDir, { withFileTypes: true })\n .filter(e => e.isDirectory())\n .map(e => e.name);\n\n for (const squad of squads) {\n const squadPath = join(memoryDir, squad);\n const agents = readdirSync(squadPath, { withFileTypes: true })\n .filter(e => e.isDirectory())\n .map(e => e.name);\n\n for (const agent of agents) {\n const agentPath = join(squadPath, agent);\n const files = readdirSync(agentPath).filter(f => f.endsWith('.md'));\n\n for (const file of files) {\n const filePath = join(agentPath, file);\n const type = file.replace('.md', '') as MemoryEntry['type'];\n\n entries.push({\n squad,\n agent,\n type,\n content: readFileSync(filePath, 'utf-8'),\n path: filePath\n });\n }\n }\n }\n\n return entries;\n}\n\n// Semantic expansions for common business terms\nconst SEMANTIC_EXPANSIONS: Record<string, string[]> = {\n 'pricing': ['price', 'cost', '$', 'revenue', 'fee', 'rate'],\n 'price': ['pricing', 'cost', '$', 'fee'],\n 'revenue': ['income', 'sales', 'mrr', 'arr', '$'],\n 'cost': ['expense', 'spend', 'budget', '$', 'price'],\n 'customer': ['client', 'lead', 'prospect', 'user'],\n 'client': ['customer', 'lead', 'prospect'],\n 'lead': ['prospect', 'customer', 'client', 'pipeline'],\n 'agent': ['squad', 'bot', 'ai'],\n 'squad': ['team', 'agent', 'group'],\n 'status': ['state', 'progress', 'health'],\n 'bug': ['issue', 'error', 'problem', 'fix'],\n 'feature': ['capability', 'function', 'ability'],\n};\n\nfunction expandQuery(query: string): string[] {\n const words = query.toLowerCase().split(/\\s+/);\n const expanded = new Set(words);\n\n for (const word of words) {\n if (SEMANTIC_EXPANSIONS[word]) {\n SEMANTIC_EXPANSIONS[word].forEach(syn => expanded.add(syn));\n }\n }\n\n return Array.from(expanded);\n}\n\nfunction getFileAge(filePath: string): number {\n try {\n const { statSync } = require('fs');\n const stats = statSync(filePath);\n const ageMs = Date.now() - stats.mtimeMs;\n const ageDays = ageMs / (1000 * 60 * 60 * 24);\n return ageDays;\n } catch {\n return 999;\n }\n}\n\nexport function searchMemory(query: string, memoryDir?: string): SearchResult[] {\n const dir = memoryDir || findMemoryDir();\n if (!dir) return [];\n\n const entries = listMemoryEntries(dir);\n const results: SearchResult[] = [];\n const queryLower = query.toLowerCase();\n const expandedTerms = expandQuery(queryLower);\n\n for (const entry of entries) {\n const contentLower = entry.content.toLowerCase();\n const lines = entry.content.split('\\n');\n const matches: string[] = [];\n let score = 0;\n let directHits = 0;\n let expandedHits = 0;\n\n // Check direct query words first\n const directWords = queryLower.split(/\\s+/);\n for (const word of directWords) {\n if (contentLower.includes(word)) {\n directHits += 1;\n score += 2; // Direct matches worth more\n }\n }\n\n // Check expanded terms\n for (const term of expandedTerms) {\n if (contentLower.includes(term)) {\n expandedHits += 1;\n score += 0.5; // Expanded matches worth less but still count\n\n // Find matching lines with context\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.toLowerCase().includes(term) && line.trim() && !matches.includes(line.trim())) {\n matches.push(line.trim());\n }\n }\n }\n }\n\n // Boost score for exact phrase match\n if (contentLower.includes(queryLower)) {\n score += 5;\n }\n\n // Recency boost - files updated recently are more relevant\n const ageDays = getFileAge(entry.path);\n if (ageDays < 1) {\n score *= 1.5; // Updated today\n } else if (ageDays < 7) {\n score *= 1.2; // Updated this week\n } else if (ageDays > 30) {\n score *= 0.8; // Stale data penalty\n }\n\n // Type weighting - balanced across types\n const typeWeights: Record<string, number> = {\n 'state': 1.2, // Current state slightly preferred\n 'learnings': 1.1, // Learnings are valuable\n 'output': 1.0, // Recent outputs\n 'feedback': 0.9, // Feedback less commonly needed\n };\n score *= typeWeights[entry.type] || 1.0;\n\n if (score > 0 && (directHits > 0 || expandedHits > 1)) {\n results.push({ entry, matches: matches.slice(0, 7), score });\n }\n }\n\n // Sort by score descending\n return results.sort((a, b) => b.score - a.score);\n}\n\nexport function getSquadState(squadName: string): MemoryEntry[] {\n const memoryDir = findMemoryDir();\n if (!memoryDir) return [];\n\n const squadPath = join(memoryDir, squadName);\n if (!existsSync(squadPath)) return [];\n\n const entries: MemoryEntry[] = [];\n const agents = readdirSync(squadPath, { withFileTypes: true })\n .filter(e => e.isDirectory())\n .map(e => e.name);\n\n for (const agent of agents) {\n const statePath = join(squadPath, agent, 'state.md');\n if (existsSync(statePath)) {\n entries.push({\n squad: squadName,\n agent,\n type: 'state',\n content: readFileSync(statePath, 'utf-8'),\n path: statePath\n });\n }\n }\n\n return entries;\n}\n\n/**\n * Update memory file with distributed locking\n * Safe for concurrent access from multiple agents\n */\nexport async function updateMemory(\n squadName: string,\n agentName: string,\n type: MemoryEntry['type'],\n content: string\n): Promise<void> {\n const memoryDir = findMemoryDir();\n if (!memoryDir) {\n throw new Error('No .agents/memory directory found');\n }\n\n const filePath = join(memoryDir, squadName, agentName, `${type}.md`);\n const dir = dirname(filePath);\n\n await withLock(filePath, () => {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(filePath, content);\n });\n}\n\n/**\n * Sync version for backward compatibility (no locking - use with caution)\n * @deprecated Use async updateMemory() for safe concurrent access\n */\nexport function updateMemorySync(\n squadName: string,\n agentName: string,\n type: MemoryEntry['type'],\n content: string\n): void {\n const memoryDir = findMemoryDir();\n if (!memoryDir) {\n throw new Error('No .agents/memory directory found');\n }\n\n const filePath = join(memoryDir, squadName, agentName, `${type}.md`);\n const dir = dirname(filePath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n writeFileSync(filePath, content);\n}\n\n/**\n * Append to memory file with distributed locking\n * Safe for concurrent access from multiple agents\n */\nexport async function appendToMemory(\n squadName: string,\n agentName: string,\n type: MemoryEntry['type'],\n addition: string\n): Promise<void> {\n const memoryDir = findMemoryDir();\n if (!memoryDir) {\n throw new Error('No .agents/memory directory found');\n }\n\n const filePath = join(memoryDir, squadName, agentName, `${type}.md`);\n\n await withLock(filePath, () => {\n let existing = '';\n if (existsSync(filePath)) {\n existing = readFileSync(filePath, 'utf-8');\n }\n\n const timestamp = new Date().toISOString().split('T')[0];\n const newContent = existing + `\\n\\n---\\n_Added: ${timestamp}_\\n\\n${addition}`;\n\n const dir = dirname(filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(filePath, newContent.trim());\n });\n}\n\n/**\n * Sync version for backward compatibility (no locking - use with caution)\n * @deprecated Use async appendToMemory() for safe concurrent access\n */\nexport function appendToMemorySync(\n squadName: string,\n agentName: string,\n type: MemoryEntry['type'],\n addition: string\n): void {\n const memoryDir = findMemoryDir();\n if (!memoryDir) {\n throw new Error('No .agents/memory directory found');\n }\n\n const filePath = join(memoryDir, squadName, agentName, `${type}.md`);\n\n let existing = '';\n if (existsSync(filePath)) {\n existing = readFileSync(filePath, 'utf-8');\n }\n\n const timestamp = new Date().toISOString().split('T')[0];\n const newContent = existing + `\\n\\n---\\n_Added: ${timestamp}_\\n\\n${addition}`;\n\n updateMemorySync(squadName, agentName, type, newContent.trim());\n}\n","/**\n * Distributed locking for safe concurrent file access\n * Uses Redis when available, falls back to file-based locks\n */\n\nimport Redis from 'ioredis';\nimport { existsSync, writeFileSync, unlinkSync, mkdirSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { createHash } from 'crypto';\n\n// Lock configuration\nconst LOCK_TTL_MS = 30000; // 30 seconds max lock hold time\nconst LOCK_RETRY_DELAY_MS = 100;\nconst LOCK_MAX_RETRIES = 50; // 5 seconds max wait\n\n// Singleton Redis client\nlet redisClient: Redis | null = null;\nlet redisAvailable: boolean | null = null;\n\n/**\n * Get or create Redis client\n */\nasync function getRedis(): Promise<Redis | null> {\n if (redisAvailable === false) return null;\n\n if (!redisClient) {\n try {\n redisClient = new Redis({\n host: process.env.REDIS_HOST || 'localhost',\n port: parseInt(process.env.REDIS_PORT || '6379'),\n connectTimeout: 1000,\n maxRetriesPerRequest: 1,\n lazyConnect: true,\n });\n\n await redisClient.connect();\n redisAvailable = true;\n } catch {\n redisAvailable = false;\n redisClient = null;\n return null;\n }\n }\n\n return redisClient;\n}\n\n/**\n * Generate a unique lock ID for this process\n */\nfunction generateLockId(): string {\n return `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Hash a file path to create a lock key\n */\nfunction getLockKey(filePath: string): string {\n const hash = createHash('md5').update(filePath).digest('hex').slice(0, 12);\n return `squads:lock:${hash}`;\n}\n\n/**\n * Get file lock path for fallback locking\n */\nfunction getFileLockPath(filePath: string): string {\n const lockDir = join(dirname(filePath), '.locks');\n const hash = createHash('md5').update(filePath).digest('hex').slice(0, 12);\n return join(lockDir, `${hash}.lock`);\n}\n\n/**\n * Sleep helper\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Acquire a distributed lock using Redis\n */\nasync function acquireRedisLock(key: string, lockId: string): Promise<boolean> {\n const redis = await getRedis();\n if (!redis) return false;\n\n try {\n // SET key lockId NX PX ttl - atomic set-if-not-exists with expiry\n const result = await redis.set(key, lockId, 'PX', LOCK_TTL_MS, 'NX');\n return result === 'OK';\n } catch {\n return false;\n }\n}\n\n/**\n * Release a Redis lock (only if we own it)\n */\nasync function releaseRedisLock(key: string, lockId: string): Promise<boolean> {\n const redis = await getRedis();\n if (!redis) return false;\n\n try {\n // Lua script to atomically check and delete\n const script = `\n if redis.call(\"get\", KEYS[1]) == ARGV[1] then\n return redis.call(\"del\", KEYS[1])\n else\n return 0\n end\n `;\n const result = await redis.eval(script, 1, key, lockId);\n return result === 1;\n } catch {\n return false;\n }\n}\n\n/**\n * Acquire a file-based lock (fallback when Redis unavailable)\n */\nfunction acquireFileLock(lockPath: string, lockId: string): boolean {\n try {\n const lockDir = dirname(lockPath);\n if (!existsSync(lockDir)) {\n mkdirSync(lockDir, { recursive: true });\n }\n\n // Check if lock exists and is still valid\n if (existsSync(lockPath)) {\n const { statSync, readFileSync } = require('fs');\n const stats = statSync(lockPath);\n const ageMs = Date.now() - stats.mtimeMs;\n\n // Lock expired - clean it up\n if (ageMs > LOCK_TTL_MS) {\n unlinkSync(lockPath);\n } else {\n return false; // Lock held by another process\n }\n }\n\n // Create lock file atomically using O_EXCL flag\n const { openSync, closeSync } = require('fs');\n const fd = openSync(lockPath, 'wx');\n writeFileSync(fd, lockId);\n closeSync(fd);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Release a file-based lock\n */\nfunction releaseFileLock(lockPath: string, lockId: string): boolean {\n try {\n if (!existsSync(lockPath)) return true;\n\n const { readFileSync } = require('fs');\n const currentId = readFileSync(lockPath, 'utf-8').trim();\n\n if (currentId === lockId) {\n unlinkSync(lockPath);\n return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\n/**\n * Acquire a lock for a file path\n * Returns a release function, or null if lock couldn't be acquired\n */\nexport async function acquireLock(filePath: string): Promise<(() => Promise<void>) | null> {\n const lockId = generateLockId();\n const redisKey = getLockKey(filePath);\n const fileLockPath = getFileLockPath(filePath);\n\n let useRedis = false;\n\n for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt++) {\n // Try Redis first\n if (await acquireRedisLock(redisKey, lockId)) {\n useRedis = true;\n break;\n }\n\n // Fall back to file lock if Redis unavailable\n if (redisAvailable === false && acquireFileLock(fileLockPath, lockId)) {\n useRedis = false;\n break;\n }\n\n // Wait and retry\n if (attempt < LOCK_MAX_RETRIES - 1) {\n await sleep(LOCK_RETRY_DELAY_MS);\n }\n }\n\n // Check if we got the lock\n const redis = await getRedis();\n let gotLock = false;\n\n if (redis) {\n try {\n const currentOwner = await redis.get(redisKey);\n gotLock = currentOwner === lockId;\n } catch {\n gotLock = false;\n }\n } else {\n // Check file lock\n try {\n if (existsSync(fileLockPath)) {\n const { readFileSync } = require('fs');\n const currentId = readFileSync(fileLockPath, 'utf-8').trim();\n gotLock = currentId === lockId;\n }\n } catch {\n gotLock = false;\n }\n }\n\n if (!gotLock) {\n return null;\n }\n\n // Return release function\n return async () => {\n if (useRedis) {\n await releaseRedisLock(redisKey, lockId);\n } else {\n releaseFileLock(fileLockPath, lockId);\n }\n };\n}\n\n/**\n * Execute a function while holding a lock\n * Automatically acquires and releases the lock\n */\nexport async function withLock<T>(\n filePath: string,\n fn: () => T | Promise<T>\n): Promise<T> {\n const release = await acquireLock(filePath);\n\n if (!release) {\n throw new Error(`Failed to acquire lock for: ${filePath}`);\n }\n\n try {\n return await fn();\n } finally {\n await release();\n }\n}\n\n/**\n * Graceful shutdown - close Redis connection\n */\nexport async function closeLockClient(): Promise<void> {\n if (redisClient) {\n await redisClient.quit();\n redisClient = null;\n redisAvailable = null;\n }\n}\n"],"mappings":"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","names":["writeFileSync","existsSync","mkdirSync","join","dirname","readFileSync","join","existsSync","dirname","mkdirSync","writeFileSync"]}
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/lib/squad-parser.ts
4
4
  import { readFileSync as readFileSync2, existsSync as existsSync2, readdirSync, writeFileSync as writeFileSync2 } from "fs";
5
- import { join as join2, basename } from "path";
5
+ import { join as join2, basename, dirname as dirname2 } from "path";
6
6
  import matter from "gray-matter";
7
7
 
8
8
  // src/lib/mcp-config.ts
@@ -153,7 +153,8 @@ function parseSquadFile(filePath) {
153
153
  const fm = frontmatter;
154
154
  const lines = bodyContent.split("\n");
155
155
  const squad = {
156
- name: fm.name || basename(filePath).replace(".md", ""),
156
+ // Use directory name (e.g., "demo" from ".agents/squads/demo/SQUAD.md")
157
+ name: fm.name || basename(dirname2(filePath)),
157
158
  mission: fm.mission || "",
158
159
  agents: [],
159
160
  pipelines: [],
@@ -470,4 +471,4 @@ export {
470
471
  updateGoalInSquad,
471
472
  resolveExecutionContext
472
473
  };
473
- //# sourceMappingURL=chunk-3TSY2K7R.js.map
474
+ //# sourceMappingURL=chunk-QPH5OR7J.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, dirname } 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 // Use directory name (e.g., \"demo\" from \".agents/squads/demo/SQUAD.md\")\n name: fm.name || basename(dirname(filePath)),\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","dirname","config","join","existsSync","readFileSync","dirname","writeFileSync"]}