squads-cli 0.4.11 → 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 (42) hide show
  1. package/README.md +24 -4
  2. package/dist/{chunk-HIQ2APYR.js → chunk-7PRYDHZW.js} +2 -2
  3. package/dist/chunk-BV6S5AWZ.js +419 -0
  4. package/dist/chunk-BV6S5AWZ.js.map +1 -0
  5. package/dist/chunk-QPH5OR7J.js +474 -0
  6. package/dist/chunk-QPH5OR7J.js.map +1 -0
  7. package/dist/cli.js +8031 -3620
  8. package/dist/cli.js.map +1 -1
  9. package/dist/index.d.ts +54 -0
  10. package/dist/index.js +8 -3
  11. package/dist/index.js.map +1 -1
  12. package/dist/{memory-4PVUKIDK.js → memory-ZXDXF6KF.js} +8 -4
  13. package/dist/{sessions-R4VWIGFR.js → sessions-F6LRY7EN.js} +2 -2
  14. package/dist/squad-parser-MSYE4PXL.js +31 -0
  15. package/dist/squad-parser-MSYE4PXL.js.map +1 -0
  16. package/dist/templates/core/AGENTS.md.template +64 -0
  17. package/dist/templates/core/BUSINESS_BRIEF.md.template +29 -0
  18. package/dist/templates/core/CLAUDE.md.template +50 -0
  19. package/dist/templates/core/provider.yaml.template +5 -0
  20. package/dist/templates/first-squad/SQUAD.md.template +23 -0
  21. package/dist/templates/first-squad/lead.md.template +44 -0
  22. package/dist/templates/memory/getting-started/state.md.template +19 -0
  23. package/dist/templates/skills/squads-learn/SKILL.md +86 -0
  24. package/dist/templates/skills/squads-workflow/instruction.md +70 -0
  25. package/docker/docker-compose.engram.yml +26 -0
  26. package/docker/docker-compose.yml +132 -76
  27. package/docker/squads-bridge/squads_bridge.py +534 -8
  28. package/package.json +15 -9
  29. package/templates/core/AGENTS.md.template +64 -0
  30. package/templates/core/BUSINESS_BRIEF.md.template +29 -0
  31. package/templates/core/CLAUDE.md.template +50 -0
  32. package/templates/core/provider.yaml.template +5 -0
  33. package/templates/first-squad/SQUAD.md.template +23 -0
  34. package/templates/first-squad/lead.md.template +44 -0
  35. package/templates/memory/getting-started/state.md.template +19 -0
  36. package/templates/skills/squads-learn/SKILL.md +86 -0
  37. package/templates/skills/squads-workflow/instruction.md +70 -0
  38. package/dist/chunk-FUHBEL3L.js +0 -203
  39. package/dist/chunk-FUHBEL3L.js.map +0 -1
  40. /package/dist/{chunk-HIQ2APYR.js.map → chunk-7PRYDHZW.js.map} +0 -0
  41. /package/dist/{memory-4PVUKIDK.js.map → memory-ZXDXF6KF.js.map} +0 -0
  42. /package/dist/{sessions-R4VWIGFR.js.map → sessions-F6LRY7EN.js.map} +0 -0
package/README.md CHANGED
@@ -1,13 +1,22 @@
1
1
  # squads-cli
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/squads-cli)](https://www.npmjs.com/package/squads-cli)
4
+ [![npm downloads](https://img.shields.io/npm/dw/squads-cli)](https://www.npmjs.com/package/squads-cli)
5
+ [![GitHub stars](https://img.shields.io/github/stars/agents-squads/squads-cli?style=social)](https://github.com/agents-squads/squads-cli)
4
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
- [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
6
7
 
7
- **Organize, run, and track autonomous AI agents.** Built for Claude Code.
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
  ![squads dashboard](./assets/dashboard.png)
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
- AI agents are powerful individually. But real work requires coordination.
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
- - **Stack** — Local infrastructure for telemetry and memory
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
 
@@ -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"]}