squads-cli 0.2.0 → 0.2.1

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 (223) hide show
  1. package/README.md +521 -288
  2. package/dist/auth-YW3UPFSB.js +23 -0
  3. package/dist/auth-YW3UPFSB.js.map +1 -0
  4. package/dist/autonomy-PSVZVX7A.js +105 -0
  5. package/dist/autonomy-PSVZVX7A.js.map +1 -0
  6. package/dist/chunk-67RO2HKR.js +174 -0
  7. package/dist/chunk-67RO2HKR.js.map +1 -0
  8. package/dist/chunk-7OCVIDC7.js +12 -0
  9. package/dist/chunk-7OCVIDC7.js.map +1 -0
  10. package/dist/chunk-BODLDQY7.js +452 -0
  11. package/dist/chunk-BODLDQY7.js.map +1 -0
  12. package/dist/chunk-EHQJHRIW.js +103 -0
  13. package/dist/chunk-EHQJHRIW.js.map +1 -0
  14. package/dist/chunk-FFFCFZ6A.js +121 -0
  15. package/dist/chunk-FFFCFZ6A.js.map +1 -0
  16. package/dist/chunk-FIWT2NMM.js +165 -0
  17. package/dist/chunk-FIWT2NMM.js.map +1 -0
  18. package/dist/chunk-HF4WR7RA.js +154 -0
  19. package/dist/chunk-HF4WR7RA.js.map +1 -0
  20. package/dist/chunk-J6QF4ZQX.js +230 -0
  21. package/dist/chunk-J6QF4ZQX.js.map +1 -0
  22. package/dist/chunk-LOA3KWYJ.js +294 -0
  23. package/dist/chunk-LOA3KWYJ.js.map +1 -0
  24. package/dist/chunk-M5FXNY6Y.js +384 -0
  25. package/dist/chunk-M5FXNY6Y.js.map +1 -0
  26. package/dist/chunk-QHNUMM4V.js +87 -0
  27. package/dist/chunk-QHNUMM4V.js.map +1 -0
  28. package/dist/chunk-QJ7C7CMB.js +223 -0
  29. package/dist/chunk-QJ7C7CMB.js.map +1 -0
  30. package/dist/chunk-RM6BWILN.js +74 -0
  31. package/dist/chunk-RM6BWILN.js.map +1 -0
  32. package/dist/chunk-TYFTF53O.js +613 -0
  33. package/dist/chunk-TYFTF53O.js.map +1 -0
  34. package/dist/chunk-TZXD6WFN.js +420 -0
  35. package/dist/chunk-TZXD6WFN.js.map +1 -0
  36. package/dist/chunk-WVOIY5GW.js +621 -0
  37. package/dist/chunk-WVOIY5GW.js.map +1 -0
  38. package/dist/chunk-Z2UKDBNL.js +162 -0
  39. package/dist/chunk-Z2UKDBNL.js.map +1 -0
  40. package/dist/chunk-ZTQ7ISUR.js +338 -0
  41. package/dist/chunk-ZTQ7ISUR.js.map +1 -0
  42. package/dist/cli.js +2483 -5902
  43. package/dist/cli.js.map +1 -1
  44. package/dist/context-GWPF4SEY.js +291 -0
  45. package/dist/context-GWPF4SEY.js.map +1 -0
  46. package/dist/context-feed-AJGVAR6H.js +394 -0
  47. package/dist/context-feed-AJGVAR6H.js.map +1 -0
  48. package/dist/cost-XBCDJ7XC.js +275 -0
  49. package/dist/cost-XBCDJ7XC.js.map +1 -0
  50. package/dist/create-BLFGG6PF.js +286 -0
  51. package/dist/create-BLFGG6PF.js.map +1 -0
  52. package/dist/dashboard-LGT2B2BL.js +951 -0
  53. package/dist/dashboard-LGT2B2BL.js.map +1 -0
  54. package/dist/dashboard-RMK2BOD2.js +794 -0
  55. package/dist/dashboard-RMK2BOD2.js.map +1 -0
  56. package/dist/doctor-XPUIIBHJ.js +374 -0
  57. package/dist/doctor-XPUIIBHJ.js.map +1 -0
  58. package/dist/env-config-SQEI3Y7Y.js +21 -0
  59. package/dist/env-config-SQEI3Y7Y.js.map +1 -0
  60. package/dist/exec-OUXM7JBF.js +223 -0
  61. package/dist/exec-OUXM7JBF.js.map +1 -0
  62. package/dist/feedback-KNAOG5QK.js +229 -0
  63. package/dist/feedback-KNAOG5QK.js.map +1 -0
  64. package/dist/github-UQTM5KMS.js +23 -0
  65. package/dist/github-UQTM5KMS.js.map +1 -0
  66. package/dist/goal-BVHV5573.js +168 -0
  67. package/dist/goal-BVHV5573.js.map +1 -0
  68. package/dist/health-4UXN44PF.js +218 -0
  69. package/dist/health-4UXN44PF.js.map +1 -0
  70. package/dist/history-ILH3SWHB.js +232 -0
  71. package/dist/history-ILH3SWHB.js.map +1 -0
  72. package/dist/index.d.ts +736 -8
  73. package/dist/index.js +1312 -6
  74. package/dist/index.js.map +1 -1
  75. package/dist/init-XQZ7BOGT.js +812 -0
  76. package/dist/init-XQZ7BOGT.js.map +1 -0
  77. package/dist/kpi-RQIU7WGK.js +413 -0
  78. package/dist/kpi-RQIU7WGK.js.map +1 -0
  79. package/dist/learn-OIFUVZAS.js +269 -0
  80. package/dist/learn-OIFUVZAS.js.map +1 -0
  81. package/dist/login-DXZANWZY.js +155 -0
  82. package/dist/login-DXZANWZY.js.map +1 -0
  83. package/dist/memory-T3ACCS7E.js +560 -0
  84. package/dist/memory-T3ACCS7E.js.map +1 -0
  85. package/dist/memory-VNF2VFRB.js +23 -0
  86. package/dist/memory-VNF2VFRB.js.map +1 -0
  87. package/dist/progress-DAUZMT3N.js +202 -0
  88. package/dist/progress-DAUZMT3N.js.map +1 -0
  89. package/dist/providers-3P5D2XL5.js +65 -0
  90. package/dist/providers-3P5D2XL5.js.map +1 -0
  91. package/dist/results-UECWGLTB.js +224 -0
  92. package/dist/results-UECWGLTB.js.map +1 -0
  93. package/dist/run-I6KAXU6U.js +4049 -0
  94. package/dist/run-I6KAXU6U.js.map +1 -0
  95. package/dist/session-HBU6KZOD.js +64 -0
  96. package/dist/session-HBU6KZOD.js.map +1 -0
  97. package/dist/sessions-CK25VGPL.js +333 -0
  98. package/dist/sessions-CK25VGPL.js.map +1 -0
  99. package/dist/squad-parser-DCG65BJS.js +35 -0
  100. package/dist/squad-parser-DCG65BJS.js.map +1 -0
  101. package/dist/stats-G6NAU5BD.js +334 -0
  102. package/dist/stats-G6NAU5BD.js.map +1 -0
  103. package/dist/status-AQNLDZVN.js +352 -0
  104. package/dist/status-AQNLDZVN.js.map +1 -0
  105. package/dist/sync-ZI3MHA4G.js +836 -0
  106. package/dist/sync-ZI3MHA4G.js.map +1 -0
  107. package/dist/templates/core/AGENTS.md.template +51 -0
  108. package/dist/templates/core/BUSINESS_BRIEF.md.template +29 -0
  109. package/dist/templates/core/CLAUDE.md.template +48 -0
  110. package/dist/templates/core/provider.yaml.template +5 -0
  111. package/dist/templates/first-squad/SQUAD.md.template +23 -0
  112. package/dist/templates/first-squad/lead.md.template +44 -0
  113. package/dist/templates/memory/getting-started/state.md.template +19 -0
  114. package/dist/templates/seed/BUSINESS_BRIEF.md.template +27 -0
  115. package/dist/templates/seed/CLAUDE.md.template +119 -0
  116. package/dist/templates/seed/README.md.template +42 -0
  117. package/dist/templates/seed/config/SYSTEM.md +52 -0
  118. package/dist/templates/seed/config/provider.yaml +4 -0
  119. package/dist/templates/seed/hooks/settings.json.template +31 -0
  120. package/dist/templates/seed/memory/company/directives.md +37 -0
  121. package/dist/templates/seed/memory/company/manager/state.md +16 -0
  122. package/dist/templates/seed/memory/engineering/issue-solver/state.md +12 -0
  123. package/dist/templates/seed/memory/intelligence/intel-lead/state.md +9 -0
  124. package/dist/templates/seed/memory/marketing/content-drafter/state.md +12 -0
  125. package/dist/templates/seed/memory/operations/ops-lead/state.md +12 -0
  126. package/dist/templates/seed/memory/product/lead/state.md +14 -0
  127. package/dist/templates/seed/memory/research/lead/state.md +14 -0
  128. package/dist/templates/seed/skills/gh/SKILL.md +57 -0
  129. package/dist/templates/seed/skills/squads-cli/SKILL.md +84 -0
  130. package/dist/templates/seed/squads/company/SQUAD.md +51 -0
  131. package/dist/templates/seed/squads/company/company-critic.md +49 -0
  132. package/dist/templates/seed/squads/company/company-eval.md +49 -0
  133. package/dist/templates/seed/squads/company/event-dispatcher.md +43 -0
  134. package/dist/templates/seed/squads/company/goal-tracker.md +43 -0
  135. package/dist/templates/seed/squads/company/manager.md +54 -0
  136. package/dist/templates/seed/squads/engineering/SQUAD.md +48 -0
  137. package/dist/templates/seed/squads/engineering/code-reviewer.md +57 -0
  138. package/dist/templates/seed/squads/engineering/issue-solver.md +58 -0
  139. package/dist/templates/seed/squads/engineering/test-writer.md +50 -0
  140. package/dist/templates/seed/squads/intelligence/SQUAD.md +38 -0
  141. package/dist/templates/seed/squads/intelligence/intel-critic.md +36 -0
  142. package/dist/templates/seed/squads/intelligence/intel-eval.md +31 -0
  143. package/dist/templates/seed/squads/intelligence/intel-lead.md +71 -0
  144. package/dist/templates/seed/squads/marketing/SQUAD.md +47 -0
  145. package/dist/templates/seed/squads/marketing/content-drafter.md +71 -0
  146. package/dist/templates/seed/squads/marketing/growth-analyst.md +49 -0
  147. package/dist/templates/seed/squads/marketing/social-poster.md +44 -0
  148. package/dist/templates/seed/squads/operations/SQUAD.md +45 -0
  149. package/dist/templates/seed/squads/operations/finance-tracker.md +47 -0
  150. package/dist/templates/seed/squads/operations/goal-tracker.md +48 -0
  151. package/dist/templates/seed/squads/operations/ops-lead.md +58 -0
  152. package/dist/templates/seed/squads/product/SQUAD.md +41 -0
  153. package/dist/templates/seed/squads/product/lead.md +56 -0
  154. package/dist/templates/seed/squads/product/scanner.md +50 -0
  155. package/dist/templates/seed/squads/product/worker.md +55 -0
  156. package/dist/templates/seed/squads/research/SQUAD.md +38 -0
  157. package/dist/templates/seed/squads/research/analyst.md +50 -0
  158. package/dist/templates/seed/squads/research/lead.md +52 -0
  159. package/dist/templates/seed/squads/research/synthesizer.md +59 -0
  160. package/dist/templates/skills/squads-learn/SKILL.md +86 -0
  161. package/dist/templates/skills/squads-workflow/instruction.md +70 -0
  162. package/dist/terminal-FBQFQTKZ.js +55 -0
  163. package/dist/terminal-FBQFQTKZ.js.map +1 -0
  164. package/dist/update-D7CGIZ3M.js +18 -0
  165. package/dist/update-D7CGIZ3M.js.map +1 -0
  166. package/dist/update-STU276HR.js +83 -0
  167. package/dist/update-STU276HR.js.map +1 -0
  168. package/package.json +31 -13
  169. package/templates/core/AGENTS.md.template +51 -0
  170. package/templates/core/BUSINESS_BRIEF.md.template +29 -0
  171. package/templates/core/CLAUDE.md.template +48 -0
  172. package/templates/core/provider.yaml.template +5 -0
  173. package/templates/first-squad/SQUAD.md.template +23 -0
  174. package/templates/first-squad/lead.md.template +44 -0
  175. package/templates/memory/getting-started/state.md.template +19 -0
  176. package/templates/seed/BUSINESS_BRIEF.md.template +27 -0
  177. package/templates/seed/CLAUDE.md.template +119 -0
  178. package/templates/seed/README.md.template +42 -0
  179. package/templates/seed/config/SYSTEM.md +52 -0
  180. package/templates/seed/config/provider.yaml +4 -0
  181. package/templates/seed/hooks/settings.json.template +31 -0
  182. package/templates/seed/memory/company/directives.md +37 -0
  183. package/templates/seed/memory/company/manager/state.md +16 -0
  184. package/templates/seed/memory/engineering/issue-solver/state.md +12 -0
  185. package/templates/seed/memory/intelligence/intel-lead/state.md +9 -0
  186. package/templates/seed/memory/marketing/content-drafter/state.md +12 -0
  187. package/templates/seed/memory/operations/ops-lead/state.md +12 -0
  188. package/templates/seed/memory/product/lead/state.md +14 -0
  189. package/templates/seed/memory/research/lead/state.md +14 -0
  190. package/templates/seed/skills/gh/SKILL.md +57 -0
  191. package/templates/seed/skills/squads-cli/SKILL.md +84 -0
  192. package/templates/seed/squads/company/SQUAD.md +51 -0
  193. package/templates/seed/squads/company/company-critic.md +49 -0
  194. package/templates/seed/squads/company/company-eval.md +49 -0
  195. package/templates/seed/squads/company/event-dispatcher.md +43 -0
  196. package/templates/seed/squads/company/goal-tracker.md +43 -0
  197. package/templates/seed/squads/company/manager.md +54 -0
  198. package/templates/seed/squads/engineering/SQUAD.md +48 -0
  199. package/templates/seed/squads/engineering/code-reviewer.md +57 -0
  200. package/templates/seed/squads/engineering/issue-solver.md +58 -0
  201. package/templates/seed/squads/engineering/test-writer.md +50 -0
  202. package/templates/seed/squads/intelligence/SQUAD.md +38 -0
  203. package/templates/seed/squads/intelligence/intel-critic.md +36 -0
  204. package/templates/seed/squads/intelligence/intel-eval.md +31 -0
  205. package/templates/seed/squads/intelligence/intel-lead.md +71 -0
  206. package/templates/seed/squads/marketing/SQUAD.md +47 -0
  207. package/templates/seed/squads/marketing/content-drafter.md +71 -0
  208. package/templates/seed/squads/marketing/growth-analyst.md +49 -0
  209. package/templates/seed/squads/marketing/social-poster.md +44 -0
  210. package/templates/seed/squads/operations/SQUAD.md +45 -0
  211. package/templates/seed/squads/operations/finance-tracker.md +47 -0
  212. package/templates/seed/squads/operations/goal-tracker.md +48 -0
  213. package/templates/seed/squads/operations/ops-lead.md +58 -0
  214. package/templates/seed/squads/product/SQUAD.md +41 -0
  215. package/templates/seed/squads/product/lead.md +56 -0
  216. package/templates/seed/squads/product/scanner.md +50 -0
  217. package/templates/seed/squads/product/worker.md +55 -0
  218. package/templates/seed/squads/research/SQUAD.md +38 -0
  219. package/templates/seed/squads/research/analyst.md +50 -0
  220. package/templates/seed/squads/research/lead.md +52 -0
  221. package/templates/seed/squads/research/synthesizer.md +59 -0
  222. package/templates/skills/squads-learn/SKILL.md +86 -0
  223. package/templates/skills/squads-workflow/instruction.md +70 -0
package/dist/index.js CHANGED
@@ -1,16 +1,1322 @@
1
1
  // src/version.ts
2
- var version = "0.1.0";
2
+ import { createRequire } from "module";
3
+ var require2 = createRequire(import.meta.url);
4
+ var pkg = require2("../package.json");
5
+ var version = pkg.version;
3
6
 
4
- // src/index.ts
5
- async function loadSquad(path) {
7
+ // src/lib/squad-parser.ts
8
+ import { readFileSync as readFileSync2, existsSync as existsSync2, readdirSync, writeFileSync as writeFileSync2 } from "fs";
9
+ import { join as join2, basename, dirname as dirname2 } from "path";
10
+ import matter from "gray-matter";
11
+
12
+ // src/lib/mcp-config.ts
13
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
14
+ import { join, dirname } from "path";
15
+ var SERVER_REGISTRY = {};
16
+ function getHome() {
17
+ return process.env.HOME || process.env.USERPROFILE || "";
18
+ }
19
+ function getContextsDir() {
20
+ return join(getHome(), ".claude", "contexts");
21
+ }
22
+ function getMcpConfigsDir() {
23
+ return join(getHome(), ".claude", "mcp-configs");
24
+ }
25
+ function generateMcpConfig(mcpServers) {
26
+ const config = { mcpServers: {} };
27
+ for (const server of mcpServers) {
28
+ const def = SERVER_REGISTRY[server];
29
+ if (def) {
30
+ config.mcpServers[server] = def;
31
+ }
32
+ }
33
+ return config;
34
+ }
35
+ function writeMcpConfig(config, path) {
36
+ const dir = dirname(path);
37
+ if (!existsSync(dir)) {
38
+ mkdirSync(dir, { recursive: true });
39
+ }
40
+ writeFileSync(path, JSON.stringify(config, null, 2));
41
+ }
42
+ function readMcpConfig(path) {
43
+ if (!existsSync(path)) return null;
44
+ try {
45
+ const content = readFileSync(path, "utf-8");
46
+ return JSON.parse(content);
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+ function resolveMcpConfig(squadName, mcpServers, forceRegenerate = false) {
52
+ const home = getHome();
53
+ const userOverride = join(getMcpConfigsDir(), `${squadName}.json`);
54
+ if (existsSync(userOverride)) {
55
+ const config = readMcpConfig(userOverride);
56
+ return {
57
+ path: userOverride,
58
+ source: "user-override",
59
+ servers: config ? Object.keys(config.mcpServers) : void 0
60
+ };
61
+ }
62
+ if (mcpServers && mcpServers.length > 0) {
63
+ const generatedPath = join(getContextsDir(), `${squadName}.mcp.json`);
64
+ const shouldGenerate = forceRegenerate || !existsSync(generatedPath);
65
+ if (shouldGenerate) {
66
+ const config2 = generateMcpConfig(mcpServers);
67
+ writeMcpConfig(config2, generatedPath);
68
+ return {
69
+ path: generatedPath,
70
+ source: "generated",
71
+ servers: Object.keys(config2.mcpServers),
72
+ generated: true
73
+ };
74
+ }
75
+ const config = readMcpConfig(generatedPath);
76
+ return {
77
+ path: generatedPath,
78
+ source: "generated",
79
+ servers: config ? Object.keys(config.mcpServers) : mcpServers,
80
+ generated: false
81
+ };
82
+ }
83
+ return {
84
+ path: join(home, ".claude.json"),
85
+ source: "fallback"
86
+ };
87
+ }
88
+
89
+ // src/lib/squad-parser.ts
90
+ function findSquadsDir() {
91
+ let dir = process.cwd();
92
+ for (let i = 0; i < 5; i++) {
93
+ const squadsPath = join2(dir, ".agents", "squads");
94
+ if (existsSync2(squadsPath)) {
95
+ return squadsPath;
96
+ }
97
+ const parent = join2(dir, "..");
98
+ if (parent === dir) break;
99
+ dir = parent;
100
+ }
101
+ return null;
102
+ }
103
+ function findProjectRoot() {
104
+ const squadsDir = findSquadsDir();
105
+ if (!squadsDir) return null;
106
+ return join2(squadsDir, "..", "..");
107
+ }
108
+ function listSquads(squadsDir) {
109
+ const squads = [];
110
+ const entries = readdirSync(squadsDir, { withFileTypes: true });
111
+ for (const entry of entries) {
112
+ if (entry.isDirectory() && !entry.name.startsWith("_")) {
113
+ const squadFile = join2(squadsDir, entry.name, "SQUAD.md");
114
+ if (existsSync2(squadFile)) {
115
+ squads.push(entry.name);
116
+ }
117
+ }
118
+ }
119
+ return squads;
120
+ }
121
+ function listAgents(squadsDir, squadName) {
122
+ const agents = [];
123
+ const dirs = squadName ? [squadName] : readdirSync(squadsDir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_")).map((e) => e.name);
124
+ for (const dir of dirs) {
125
+ const squadPath = join2(squadsDir, dir);
126
+ if (!existsSync2(squadPath)) continue;
127
+ const files = readdirSync(squadPath);
128
+ for (const file of files) {
129
+ if (file.endsWith(".md") && file !== "SQUAD.md") {
130
+ const agentName = file.replace(".md", "");
131
+ agents.push({
132
+ name: agentName,
133
+ role: `Agent in ${dir}`,
134
+ trigger: "manual",
135
+ filePath: join2(squadPath, file)
136
+ });
137
+ }
138
+ }
139
+ }
140
+ return agents;
141
+ }
142
+ function parseSquadFile(filePath) {
143
+ const rawContent = readFileSync2(filePath, "utf-8");
144
+ const { data: frontmatter, content: bodyContent } = matter(rawContent);
145
+ const fm = frontmatter;
146
+ const lines = bodyContent.split("\n");
147
+ const dirName = basename(dirname2(filePath));
148
+ const squad = {
149
+ // Display name can be different from dir (e.g., "Engineering Squad")
150
+ name: fm.name || dirName,
151
+ // Directory name for file path resolution
152
+ dir: dirName,
153
+ mission: fm.mission || "",
154
+ agents: [],
155
+ pipelines: [],
156
+ triggers: { scheduled: [], event: [], manual: [] },
157
+ routines: [],
158
+ dependencies: [],
159
+ outputPath: "",
160
+ goals: [],
161
+ // Apply frontmatter fields
162
+ effort: fm.effort,
163
+ context: fm.context,
164
+ repo: fm.repo,
165
+ stack: fm.stack,
166
+ providers: fm.providers,
167
+ // Preserve raw frontmatter for KPIs and other custom fields
168
+ frontmatter,
169
+ // Phase ordering: which squads must complete before this one
170
+ depends_on: Array.isArray(fm.depends_on) ? fm.depends_on : void 0
171
+ };
172
+ let currentSection = "";
173
+ let inTable = false;
174
+ let tableHeaders = [];
175
+ for (const line of lines) {
176
+ if (line.startsWith("# Squad:")) {
177
+ squad.name = line.replace("# Squad:", "").trim().toLowerCase();
178
+ continue;
179
+ }
180
+ if (line.startsWith("## ")) {
181
+ currentSection = line.replace("## ", "").trim().toLowerCase();
182
+ inTable = false;
183
+ continue;
184
+ }
185
+ if (currentSection === "mission" && line.trim() && !line.startsWith("#")) {
186
+ if (!squad.mission) {
187
+ squad.mission = line.trim();
188
+ }
189
+ }
190
+ const effortMatch = line.match(/^effort:\s*(high|medium|low)/i);
191
+ if (effortMatch && !squad.effort) {
192
+ squad.effort = effortMatch[1].toLowerCase();
193
+ }
194
+ if (currentSection.includes("agent") || currentSection.includes("orchestrator") || currentSection.includes("evaluator") || currentSection.includes("builder") || currentSection.includes("priority")) {
195
+ if (line.includes("|") && line.includes("Agent")) {
196
+ inTable = true;
197
+ tableHeaders = line.split("|").map((h) => h.trim().toLowerCase());
198
+ continue;
199
+ }
200
+ if (inTable && line.includes("|") && !line.includes("---")) {
201
+ const cells = line.split("|").map((c) => c.trim().replace(/`/g, "").replace(/\*\*/g, ""));
202
+ const agentIdx = tableHeaders.findIndex((h) => h === "agent");
203
+ const roleIdx = tableHeaders.findIndex((h) => h === "role");
204
+ const triggerIdx = tableHeaders.findIndex((h) => h === "trigger");
205
+ const statusIdx = tableHeaders.findIndex((h) => h === "status");
206
+ const effortIdx = tableHeaders.findIndex((h) => h === "effort");
207
+ if (agentIdx >= 0 && cells[agentIdx]) {
208
+ const effortValue = effortIdx >= 0 ? cells[effortIdx]?.toLowerCase() : void 0;
209
+ const effort = ["high", "medium", "low"].includes(effortValue || "") ? effortValue : void 0;
210
+ squad.agents.push({
211
+ name: cells[agentIdx],
212
+ role: roleIdx >= 0 ? cells[roleIdx] : "",
213
+ trigger: triggerIdx >= 0 ? cells[triggerIdx] : "manual",
214
+ status: statusIdx >= 0 ? cells[statusIdx] : "active",
215
+ effort
216
+ });
217
+ }
218
+ }
219
+ }
220
+ if (line.includes("\u2192") && line.includes("`")) {
221
+ const pipelineMatch = line.match(/`([^`]+)`\s*→\s*`([^`]+)`/g);
222
+ if (pipelineMatch) {
223
+ const agentNames = line.match(/`([^`]+)`/g)?.map((m) => m.replace(/`/g, "")) || [];
224
+ if (agentNames.length >= 2) {
225
+ squad.pipelines.push({
226
+ name: "default",
227
+ agents: agentNames
228
+ });
229
+ }
230
+ }
231
+ }
232
+ if (line.toLowerCase().includes("pipeline:")) {
233
+ const pipelineContent = line.split(":")[1];
234
+ if (pipelineContent && pipelineContent.includes("\u2192")) {
235
+ const agentNames = pipelineContent.match(/`([^`]+)`/g)?.map((m) => m.replace(/`/g, "")) || [];
236
+ if (agentNames.length >= 2) {
237
+ squad.pipelines.push({
238
+ name: "default",
239
+ agents: agentNames
240
+ });
241
+ }
242
+ }
243
+ }
244
+ if (line.toLowerCase().includes("primary") && line.includes("`")) {
245
+ const match = line.match(/`([^`]+)`/);
246
+ if (match) {
247
+ squad.outputPath = match[1].replace(/\/$/, "");
248
+ }
249
+ }
250
+ if (currentSection === "goals") {
251
+ const goalMatch = line.match(/^-\s*\[([ x])\]\s*(.+)$/);
252
+ if (goalMatch) {
253
+ const completed = goalMatch[1] === "x";
254
+ let description = goalMatch[2].trim();
255
+ let progress;
256
+ const progressMatch = description.match(/\(progress:\s*([^)]+)\)/i);
257
+ if (progressMatch) {
258
+ progress = progressMatch[1];
259
+ description = description.replace(progressMatch[0], "").trim();
260
+ }
261
+ squad.goals.push({
262
+ description,
263
+ completed,
264
+ progress
265
+ });
266
+ }
267
+ }
268
+ }
269
+ return squad;
270
+ }
271
+ function loadSquad(squadName) {
272
+ const squadsDir = findSquadsDir();
273
+ if (!squadsDir) return null;
274
+ const squadFile = join2(squadsDir, squadName, "SQUAD.md");
275
+ if (!existsSync2(squadFile)) return null;
276
+ return parseSquadFile(squadFile);
277
+ }
278
+ function loadAgentDefinition(agentPath) {
279
+ if (!existsSync2(agentPath)) return "";
280
+ return readFileSync2(agentPath, "utf-8");
281
+ }
282
+ function addGoalToSquad(squadName, goal) {
283
+ const squadsDir = findSquadsDir();
284
+ if (!squadsDir) return false;
285
+ const squadFile = join2(squadsDir, squadName, "SQUAD.md");
286
+ if (!existsSync2(squadFile)) return false;
287
+ let content = readFileSync2(squadFile, "utf-8");
288
+ if (!content.includes("## Goals")) {
289
+ const insertPoint = content.indexOf("## Dependencies");
290
+ if (insertPoint > 0) {
291
+ content = content.slice(0, insertPoint) + `## Goals
292
+
293
+ - [ ] ${goal}
294
+
295
+ ` + content.slice(insertPoint);
296
+ } else {
297
+ content += `
298
+ ## Goals
299
+
300
+ - [ ] ${goal}
301
+ `;
302
+ }
303
+ } else {
304
+ const goalsIdx = content.indexOf("## Goals");
305
+ const nextSectionIdx = content.indexOf("\n## ", goalsIdx + 1);
306
+ const endIdx = nextSectionIdx > 0 ? nextSectionIdx : content.length;
307
+ const goalsSection = content.slice(goalsIdx, endIdx);
308
+ const lastGoalMatch = goalsSection.match(/^-\s*\[[ x]\].+$/gm);
309
+ if (lastGoalMatch) {
310
+ const lastGoal = lastGoalMatch[lastGoalMatch.length - 1];
311
+ const lastGoalIdx = content.lastIndexOf(lastGoal, endIdx);
312
+ const insertPos = lastGoalIdx + lastGoal.length;
313
+ content = content.slice(0, insertPos) + `
314
+ - [ ] ${goal}` + content.slice(insertPos);
315
+ } else {
316
+ const headerEnd = goalsIdx + "## Goals".length;
317
+ content = content.slice(0, headerEnd) + `
318
+
319
+ - [ ] ${goal}` + content.slice(headerEnd);
320
+ }
321
+ }
322
+ writeFileSync2(squadFile, content);
323
+ return true;
324
+ }
325
+ function updateGoalInSquad(squadName, goalIndex, updates) {
326
+ const squadsDir = findSquadsDir();
327
+ if (!squadsDir) return false;
328
+ const squadFile = join2(squadsDir, squadName, "SQUAD.md");
329
+ if (!existsSync2(squadFile)) return false;
330
+ const content = readFileSync2(squadFile, "utf-8");
331
+ const lines = content.split("\n");
332
+ let currentSection = "";
333
+ let goalCount = 0;
334
+ for (let i = 0; i < lines.length; i++) {
335
+ const line = lines[i];
336
+ if (line.startsWith("## ")) {
337
+ currentSection = line.replace("## ", "").trim().toLowerCase();
338
+ continue;
339
+ }
340
+ if (currentSection === "goals") {
341
+ const goalMatch = line.match(/^-\s*\[([ x])\]\s*(.+)$/);
342
+ if (goalMatch) {
343
+ if (goalCount === goalIndex) {
344
+ let newLine = "- [" + (updates.completed ? "x" : " ") + "] " + goalMatch[2];
345
+ if (updates.progress !== void 0) {
346
+ newLine = newLine.replace(/\s*\(progress:\s*[^)]+\)/i, "");
347
+ if (updates.progress) {
348
+ newLine += ` (progress: ${updates.progress})`;
349
+ }
350
+ }
351
+ lines[i] = newLine;
352
+ writeFileSync2(squadFile, lines.join("\n"));
353
+ return true;
354
+ }
355
+ goalCount++;
356
+ }
357
+ }
358
+ }
359
+ return false;
360
+ }
361
+ function findProjectSkillsDir() {
362
+ const projectRoot = findProjectRoot();
363
+ if (!projectRoot) return null;
364
+ const skillsDir = join2(projectRoot, ".claude", "skills");
365
+ return existsSync2(skillsDir) ? skillsDir : null;
366
+ }
367
+ function findGlobalSkillsDir() {
368
+ const home = process.env.HOME || process.env.USERPROFILE || "";
369
+ if (!home) return null;
370
+ const skillsDir = join2(home, ".claude", "skills");
371
+ return existsSync2(skillsDir) ? skillsDir : null;
372
+ }
373
+ function findSquadLocalSkillsDir(squadDir) {
374
+ const squadsDir = findSquadsDir();
375
+ if (!squadsDir) return null;
376
+ const skillsDir = join2(squadsDir, squadDir, "skills");
377
+ return existsSync2(skillsDir) ? skillsDir : null;
378
+ }
379
+ function listSkillsInDir(skillsDir) {
380
+ if (!existsSync2(skillsDir)) return [];
381
+ try {
382
+ const entries = readdirSync(skillsDir, { withFileTypes: true });
383
+ const skills = [];
384
+ for (const entry of entries) {
385
+ if (entry.isDirectory()) {
386
+ const skillMdPath = join2(skillsDir, entry.name, "SKILL.md");
387
+ if (existsSync2(skillMdPath)) {
388
+ skills.push(entry.name);
389
+ }
390
+ } else if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "README.md") {
391
+ skills.push(entry.name.replace(".md", ""));
392
+ }
393
+ }
394
+ return skills;
395
+ } catch {
396
+ return [];
397
+ }
398
+ }
399
+ function findMemoryDir() {
400
+ const projectRoot = findProjectRoot();
401
+ if (!projectRoot) return null;
402
+ const memoryDir = join2(projectRoot, ".agents", "memory");
403
+ return existsSync2(memoryDir) ? memoryDir : null;
404
+ }
405
+ function resolveSkill(skillName, squadDir) {
406
+ if (squadDir) {
407
+ const squadSkillsDir = findSquadLocalSkillsDir(squadDir);
408
+ if (squadSkillsDir) {
409
+ const dirPath = join2(squadSkillsDir, skillName);
410
+ if (existsSync2(dirPath) && existsSync2(join2(dirPath, "SKILL.md"))) {
411
+ return { name: skillName, path: dirPath, source: "squad-local" };
412
+ }
413
+ const filePath = join2(squadSkillsDir, `${skillName}.md`);
414
+ if (existsSync2(filePath)) {
415
+ return { name: skillName, path: filePath, source: "squad-local" };
416
+ }
417
+ }
418
+ }
419
+ const projectSkillsDir = findProjectSkillsDir();
420
+ if (projectSkillsDir) {
421
+ const dirPath = join2(projectSkillsDir, skillName);
422
+ if (existsSync2(dirPath) && existsSync2(join2(dirPath, "SKILL.md"))) {
423
+ return { name: skillName, path: dirPath, source: "project" };
424
+ }
425
+ const filePath = join2(projectSkillsDir, `${skillName}.md`);
426
+ if (existsSync2(filePath)) {
427
+ return { name: skillName, path: filePath, source: "project" };
428
+ }
429
+ }
430
+ const globalSkillsDir = findGlobalSkillsDir();
431
+ if (globalSkillsDir) {
432
+ const dirPath = join2(globalSkillsDir, skillName);
433
+ if (existsSync2(dirPath) && existsSync2(join2(dirPath, "SKILL.md"))) {
434
+ return { name: skillName, path: dirPath, source: "global" };
435
+ }
436
+ const filePath = join2(globalSkillsDir, `${skillName}.md`);
437
+ if (existsSync2(filePath)) {
438
+ return { name: skillName, path: filePath, source: "global" };
439
+ }
440
+ }
6
441
  return null;
7
442
  }
8
- async function runAgent(agent, prompt) {
9
- return "";
443
+ function getSquadLocalSkills(squadDir) {
444
+ const squadSkillsDir = findSquadLocalSkillsDir(squadDir);
445
+ if (!squadSkillsDir) return [];
446
+ const skillNames = listSkillsInDir(squadSkillsDir);
447
+ const skills = [];
448
+ for (const name of skillNames) {
449
+ const resolved = resolveSkill(name, squadDir);
450
+ if (resolved && resolved.source === "squad-local") {
451
+ skills.push(resolved);
452
+ }
453
+ }
454
+ return skills;
455
+ }
456
+ function resolveMemoryPaths(patterns) {
457
+ const memoryDir = findMemoryDir();
458
+ if (!memoryDir) return [];
459
+ const resolved = [];
460
+ for (const pattern of patterns) {
461
+ if (pattern.endsWith("/*")) {
462
+ const subdir = pattern.slice(0, -2);
463
+ const subdirPath = join2(memoryDir, subdir);
464
+ if (existsSync2(subdirPath)) {
465
+ try {
466
+ const files = readdirSync(subdirPath);
467
+ for (const file of files) {
468
+ if (file.endsWith(".md")) {
469
+ resolved.push(join2(subdirPath, file));
470
+ }
471
+ }
472
+ } catch {
473
+ }
474
+ }
475
+ } else {
476
+ const fullPath = join2(memoryDir, pattern);
477
+ if (existsSync2(fullPath)) {
478
+ resolved.push(fullPath);
479
+ }
480
+ }
481
+ }
482
+ return resolved;
483
+ }
484
+ function findSquadLocalMcpConfig(squadDir) {
485
+ const squadsDir = findSquadsDir();
486
+ if (!squadsDir) return null;
487
+ const mcpConfigPath = join2(squadsDir, squadDir, "mcp.json");
488
+ return existsSync2(mcpConfigPath) ? mcpConfigPath : null;
489
+ }
490
+ function resolveExecutionContext(squad, forceRegenerate = false) {
491
+ const ctx = squad.context || {};
492
+ const squadLocalMcpConfig = findSquadLocalMcpConfig(squad.dir);
493
+ let mcpConfigPath;
494
+ let mcpSource;
495
+ let mcpServers = [];
496
+ if (squadLocalMcpConfig) {
497
+ mcpConfigPath = squadLocalMcpConfig;
498
+ mcpSource = "squad-local";
499
+ try {
500
+ const content = readFileSync2(squadLocalMcpConfig, "utf-8");
501
+ const config = JSON.parse(content);
502
+ mcpServers = Object.keys(config.mcpServers || {});
503
+ } catch {
504
+ }
505
+ } else {
506
+ const mcpResolution = resolveMcpConfig(
507
+ squad.name,
508
+ ctx.mcp,
509
+ forceRegenerate
510
+ );
511
+ mcpConfigPath = mcpResolution.path;
512
+ mcpSource = mcpResolution.source;
513
+ mcpServers = mcpResolution.servers || [];
514
+ }
515
+ const resolvedSkills = [];
516
+ const skillPaths = [];
517
+ const squadLocalSkills = getSquadLocalSkills(squad.dir);
518
+ for (const skill of squadLocalSkills) {
519
+ resolvedSkills.push(skill);
520
+ skillPaths.push(skill.path);
521
+ }
522
+ if (ctx.skills) {
523
+ for (const skillName of ctx.skills) {
524
+ if (resolvedSkills.some((s) => s.name === skillName)) {
525
+ continue;
526
+ }
527
+ const resolved = resolveSkill(skillName, squad.dir);
528
+ if (resolved) {
529
+ resolvedSkills.push(resolved);
530
+ skillPaths.push(resolved.path);
531
+ }
532
+ }
533
+ }
534
+ const memoryPaths = ctx.memory?.load ? resolveMemoryPaths(ctx.memory.load) : [];
535
+ return {
536
+ // Copy all SquadContext fields
537
+ ...ctx,
538
+ // Add squad name
539
+ squadName: squad.name,
540
+ // Add resolved paths
541
+ resolved: {
542
+ mcpConfigPath,
543
+ mcpSource,
544
+ mcpServers,
545
+ skillPaths,
546
+ // Backward compatible
547
+ skills: resolvedSkills,
548
+ // New detailed skill info
549
+ memoryPaths
550
+ }
551
+ };
552
+ }
553
+
554
+ // src/lib/condenser/tokens.ts
555
+ var RATIOS = {
556
+ english: 4,
557
+ // Standard English text
558
+ code: 3.5,
559
+ // Code tends to have more tokens per char
560
+ json: 3,
561
+ // JSON has many punctuation tokens
562
+ mixed: 3.75
563
+ // Default for mixed content
564
+ };
565
+ function estimateTokens(text, type = "mixed") {
566
+ if (!text) return 0;
567
+ const ratio = RATIOS[type];
568
+ return Math.ceil(text.length / ratio);
569
+ }
570
+ function estimateMessageTokens(message) {
571
+ let tokens = 4;
572
+ if (typeof message.content === "string") {
573
+ tokens += estimateTokens(message.content);
574
+ } else if (Array.isArray(message.content)) {
575
+ for (const part of message.content) {
576
+ if (part.text) {
577
+ tokens += estimateTokens(part.text);
578
+ }
579
+ tokens += 10;
580
+ }
581
+ }
582
+ return tokens;
583
+ }
584
+ var MODEL_LIMITS = {
585
+ // Anthropic models
586
+ "claude-opus-4-5-20251101": 2e5,
587
+ "claude-sonnet-4-20250514": 2e5,
588
+ "claude-3-5-haiku-20241022": 2e5,
589
+ // Aliases
590
+ opus: 2e5,
591
+ sonnet: 2e5,
592
+ haiku: 2e5,
593
+ // Default fallback
594
+ default: 2e5
595
+ };
596
+ function getModelLimit(model) {
597
+ return MODEL_LIMITS[model] ?? MODEL_LIMITS.default;
598
+ }
599
+ function createTracker(model = "default") {
600
+ return {
601
+ used: 0,
602
+ limit: getModelLimit(model),
603
+ percentage: 0,
604
+ breakdown: {
605
+ system: 0,
606
+ user: 0,
607
+ assistant: 0,
608
+ tools: 0
609
+ }
610
+ };
611
+ }
612
+ function updateTracker(tracker, content, category = "assistant") {
613
+ const tokens = estimateTokens(content);
614
+ tracker.used += tokens;
615
+ tracker.breakdown[category] += tokens;
616
+ tracker.percentage = tracker.used / tracker.limit;
617
+ }
618
+ function updateTrackerFromMessage(tracker, message) {
619
+ const tokens = estimateMessageTokens(message);
620
+ const category = mapRoleToCategory(message.role);
621
+ tracker.used += tokens;
622
+ tracker.breakdown[category] += tokens;
623
+ tracker.percentage = tracker.used / tracker.limit;
624
+ }
625
+ function mapRoleToCategory(role) {
626
+ switch (role) {
627
+ case "system":
628
+ return "system";
629
+ case "user":
630
+ return "user";
631
+ case "assistant":
632
+ return "assistant";
633
+ case "tool":
634
+ case "tool_result":
635
+ return "tools";
636
+ default:
637
+ return "assistant";
638
+ }
639
+ }
640
+ var DEFAULT_THRESHOLDS = {
641
+ light: 0.7,
642
+ medium: 0.85,
643
+ heavy: 0.95
644
+ };
645
+ function getCompressionLevel(tracker, thresholds = DEFAULT_THRESHOLDS) {
646
+ if (tracker.percentage >= thresholds.heavy) return "heavy";
647
+ if (tracker.percentage >= thresholds.medium) return "medium";
648
+ if (tracker.percentage >= thresholds.light) return "light";
649
+ return "none";
650
+ }
651
+ function formatTrackerStatus(tracker) {
652
+ const pct = (tracker.percentage * 100).toFixed(1);
653
+ const used = (tracker.used / 1e3).toFixed(1);
654
+ const limit = (tracker.limit / 1e3).toFixed(0);
655
+ const level = getCompressionLevel(tracker);
656
+ const levelIndicator = level === "none" ? "" : level === "light" ? " [!]" : level === "medium" ? " [!!]" : " [!!!]";
657
+ return `${used}K / ${limit}K tokens (${pct}%)${levelIndicator}`;
658
+ }
659
+
660
+ // src/lib/condenser/deduplication.ts
661
+ function hashContent(content) {
662
+ const prefix = content.slice(0, 100);
663
+ return `${content.length}:${prefix}`;
664
+ }
665
+ var FileDeduplicator = class {
666
+ /** Map of file path to all reads of that file */
667
+ reads = /* @__PURE__ */ new Map();
668
+ /** Current turn index */
669
+ currentTurn = 0;
670
+ /**
671
+ * Record a file read.
672
+ *
673
+ * @param path - File path that was read
674
+ * @param content - File content that was read
675
+ */
676
+ trackRead(path, content) {
677
+ const record = {
678
+ path,
679
+ turnIndex: this.currentTurn,
680
+ tokenCount: estimateTokens(content, "code"),
681
+ contentHash: hashContent(content)
682
+ };
683
+ const existing = this.reads.get(path) || [];
684
+ existing.push(record);
685
+ this.reads.set(path, existing);
686
+ }
687
+ /**
688
+ * Advance to next turn.
689
+ */
690
+ nextTurn() {
691
+ this.currentTurn++;
692
+ }
693
+ /**
694
+ * Get current turn index.
695
+ */
696
+ getTurn() {
697
+ return this.currentTurn;
698
+ }
699
+ /**
700
+ * Check if a file has been read before.
701
+ *
702
+ * @param path - File path to check
703
+ * @returns Previous read record if exists
704
+ */
705
+ getPreviousRead(path) {
706
+ const reads = this.reads.get(path);
707
+ if (!reads || reads.length === 0) return void 0;
708
+ return reads[reads.length - 1];
709
+ }
710
+ /**
711
+ * Get all files that have been read multiple times.
712
+ *
713
+ * @returns Map of path to read count
714
+ */
715
+ getDuplicateReads() {
716
+ const duplicates = /* @__PURE__ */ new Map();
717
+ for (const [path, reads] of this.reads) {
718
+ if (reads.length > 1) {
719
+ duplicates.set(path, reads.length);
720
+ }
721
+ }
722
+ return duplicates;
723
+ }
724
+ /**
725
+ * Calculate potential token savings from deduplication.
726
+ */
727
+ getPotentialSavings() {
728
+ let savings = 0;
729
+ for (const reads of this.reads.values()) {
730
+ if (reads.length > 1) {
731
+ for (let i = 0; i < reads.length - 1; i++) {
732
+ savings += reads[i].tokenCount;
733
+ }
734
+ }
735
+ }
736
+ return savings;
737
+ }
738
+ /**
739
+ * Generate a deduplication reference message.
740
+ *
741
+ * @param path - File path
742
+ * @param previousTurn - Turn where file was previously read
743
+ */
744
+ static createReference(path, previousTurn) {
745
+ return `[File "${path}" was read at turn ${previousTurn}. Content unchanged.]`;
746
+ }
747
+ /**
748
+ * Reset tracker state.
749
+ */
750
+ reset() {
751
+ this.reads.clear();
752
+ this.currentTurn = 0;
753
+ }
754
+ /**
755
+ * Get statistics for debugging.
756
+ */
757
+ getStats() {
758
+ let totalReads = 0;
759
+ let duplicateReads = 0;
760
+ for (const reads of this.reads.values()) {
761
+ totalReads += reads.length;
762
+ if (reads.length > 1) {
763
+ duplicateReads += reads.length - 1;
764
+ }
765
+ }
766
+ return {
767
+ filesTracked: this.reads.size,
768
+ totalReads,
769
+ duplicateReads,
770
+ potentialSavings: this.getPotentialSavings()
771
+ };
772
+ }
773
+ };
774
+
775
+ // src/lib/condenser/pruning.ts
776
+ var DEFAULT_CONFIG = {
777
+ protectRecent: 4e4,
778
+ minimumPrunable: 2e4,
779
+ protectedTools: ["skill", "memory", "goal"]
780
+ };
781
+ function createPrunedPlaceholder(toolName, tokensSaved) {
782
+ return `[Tool output pruned: ${toolName} (~${Math.round(tokensSaved / 1e3)}K tokens)]`;
783
+ }
784
+ var TokenPruner = class {
785
+ config;
786
+ constructor(config = {}) {
787
+ this.config = { ...DEFAULT_CONFIG, ...config };
788
+ }
789
+ /**
790
+ * Prune messages to reduce token count.
791
+ *
792
+ * Strategy:
793
+ * 1. Scan messages backward from newest to oldest
794
+ * 2. Accumulate tokens for tool outputs
795
+ * 3. Mark outputs beyond protection window for pruning
796
+ * 4. Replace pruned outputs with placeholders
797
+ *
798
+ * @param messages - Messages to prune
799
+ * @returns Pruned messages (new array, originals not mutated)
800
+ */
801
+ pruneMessages(messages) {
802
+ const annotated = messages.map((msg) => ({
803
+ ...msg,
804
+ _tokens: estimateMessageTokens(msg)
805
+ }));
806
+ const analysis = this.analyzePrunability(annotated);
807
+ if (analysis.prunableTokens < this.config.minimumPrunable) {
808
+ return messages;
809
+ }
810
+ return this.applyPruning(annotated, analysis.protectionIndex);
811
+ }
812
+ /**
813
+ * Analyze which messages can be pruned.
814
+ */
815
+ analyzePrunability(messages) {
816
+ let protectedTokens = 0;
817
+ let protectionIndex = messages.length;
818
+ for (let i = messages.length - 1; i >= 0; i--) {
819
+ const msg = messages[i];
820
+ if (!this.isToolResult(msg)) {
821
+ protectedTokens += msg._tokens;
822
+ continue;
823
+ }
824
+ if (this.isProtectedTool(msg)) {
825
+ protectedTokens += msg._tokens;
826
+ continue;
827
+ }
828
+ if (protectedTokens + msg._tokens <= this.config.protectRecent) {
829
+ protectedTokens += msg._tokens;
830
+ } else {
831
+ protectionIndex = i + 1;
832
+ break;
833
+ }
834
+ }
835
+ let prunableTokens = 0;
836
+ for (let i = 0; i < protectionIndex; i++) {
837
+ const msg = messages[i];
838
+ if (this.isToolResult(msg) && !this.isProtectedTool(msg)) {
839
+ prunableTokens += msg._tokens;
840
+ }
841
+ }
842
+ return { prunableTokens, protectedTokens, protectionIndex };
843
+ }
844
+ /**
845
+ * Apply pruning to messages before the protection index.
846
+ */
847
+ applyPruning(messages, protectionIndex) {
848
+ return messages.map((msg, i) => {
849
+ if (i >= protectionIndex) {
850
+ const { _tokens, _prunable, ...clean } = msg;
851
+ return clean;
852
+ }
853
+ if (!this.isToolResult(msg)) {
854
+ const { _tokens, _prunable, ...clean } = msg;
855
+ return clean;
856
+ }
857
+ if (this.isProtectedTool(msg)) {
858
+ const { _tokens, _prunable, ...clean } = msg;
859
+ return clean;
860
+ }
861
+ return this.createPrunedMessage(msg);
862
+ });
863
+ }
864
+ /**
865
+ * Create a pruned version of a message.
866
+ */
867
+ createPrunedMessage(msg) {
868
+ const toolName = this.getToolName(msg);
869
+ const placeholder = createPrunedPlaceholder(toolName, msg._tokens);
870
+ if (typeof msg.content === "string") {
871
+ return {
872
+ role: msg.role,
873
+ content: placeholder
874
+ };
875
+ }
876
+ const content = msg.content.map((part) => {
877
+ if (part.type === "tool_result" && part.text) {
878
+ return {
879
+ ...part,
880
+ text: placeholder,
881
+ _pruned: true,
882
+ _prunedAt: Date.now()
883
+ };
884
+ }
885
+ return part;
886
+ });
887
+ return {
888
+ role: msg.role,
889
+ content
890
+ };
891
+ }
892
+ /**
893
+ * Check if a message is a tool result.
894
+ */
895
+ isToolResult(msg) {
896
+ if (msg.role === "tool") return true;
897
+ if (Array.isArray(msg.content)) {
898
+ return msg.content.some((part) => part.type === "tool_result");
899
+ }
900
+ return false;
901
+ }
902
+ /**
903
+ * Check if a tool is in the protected list.
904
+ */
905
+ isProtectedTool(msg) {
906
+ const toolName = this.getToolName(msg);
907
+ return this.config.protectedTools.includes(toolName.toLowerCase());
908
+ }
909
+ /**
910
+ * Extract tool name from a message.
911
+ */
912
+ getToolName(msg) {
913
+ if (Array.isArray(msg.content)) {
914
+ for (const part of msg.content) {
915
+ if (part.name) return part.name;
916
+ }
917
+ }
918
+ if (typeof msg.content === "string") {
919
+ const match = msg.content.match(/Tool (?:output|result).*?:\s*(\w+)/i);
920
+ if (match) return match[1];
921
+ }
922
+ return "unknown";
923
+ }
924
+ /**
925
+ * Get statistics about potential pruning.
926
+ */
927
+ getStats(messages) {
928
+ const annotated = messages.map((msg) => ({
929
+ ...msg,
930
+ _tokens: estimateMessageTokens(msg)
931
+ }));
932
+ const totalTokens = annotated.reduce((sum, msg) => sum + msg._tokens, 0);
933
+ const analysis = this.analyzePrunability(annotated);
934
+ return {
935
+ totalTokens,
936
+ prunableTokens: analysis.prunableTokens,
937
+ protectedTokens: analysis.protectedTokens,
938
+ savingsPercentage: totalTokens > 0 ? analysis.prunableTokens / totalTokens * 100 : 0
939
+ };
940
+ }
941
+ };
942
+
943
+ // src/lib/condenser/summarizer.ts
944
+ import Anthropic from "@anthropic-ai/sdk";
945
+ var DEFAULT_CONFIG2 = {
946
+ keepFirst: 4,
947
+ keepLast: 20,
948
+ model: "claude-3-5-haiku-20241022",
949
+ // Cheap and fast
950
+ maxSummaryTokens: 2e3
951
+ };
952
+ var SUMMARIZATION_PROMPT = `You are summarizing a conversation between a user and an AI assistant to reduce context length while preserving essential information.
953
+
954
+ <conversation_to_summarize>
955
+ {{MIDDLE_CONTENT}}
956
+ </conversation_to_summarize>
957
+
958
+ Create a concise summary that preserves:
959
+ 1. **User's goals** - What the user is trying to accomplish
960
+ 2. **Progress made** - Key actions taken and their outcomes
961
+ 3. **Current state** - What's done vs. what still needs to be done
962
+ 4. **Critical context** - File paths, error messages, decisions made
963
+ 5. **Blockers** - Any issues that need to be resolved
964
+
965
+ Format your summary as a structured note that the assistant can reference to continue the work.
966
+
967
+ Keep the summary under {{MAX_TOKENS}} tokens. Focus on actionable information.`;
968
+ var ConversationSummarizer = class {
969
+ config;
970
+ client = null;
971
+ constructor(config = {}) {
972
+ this.config = { ...DEFAULT_CONFIG2, ...config };
973
+ }
974
+ /**
975
+ * Get or create Anthropic client.
976
+ */
977
+ getClient() {
978
+ if (!this.client) {
979
+ this.client = new Anthropic();
980
+ }
981
+ return this.client;
982
+ }
983
+ /**
984
+ * Summarize messages to reduce token count.
985
+ *
986
+ * Strategy:
987
+ * 1. Keep first N messages (system prompt, initial context)
988
+ * 2. Keep last M messages (recent context, current task)
989
+ * 3. Summarize everything in between
990
+ *
991
+ * @param messages - Messages to summarize
992
+ * @returns Summarized messages
993
+ */
994
+ async summarize(messages) {
995
+ if (messages.length <= this.config.keepFirst + this.config.keepLast) {
996
+ return messages;
997
+ }
998
+ const firstMessages = messages.slice(0, this.config.keepFirst);
999
+ const middleMessages = messages.slice(this.config.keepFirst, -this.config.keepLast);
1000
+ const lastMessages = messages.slice(-this.config.keepLast);
1001
+ const summary = await this.generateSummary(middleMessages);
1002
+ const summaryMessage = {
1003
+ role: "user",
1004
+ content: `[Context Summary - ${middleMessages.length} messages condensed]
1005
+
1006
+ ${summary}`
1007
+ };
1008
+ return [...firstMessages, summaryMessage, ...lastMessages];
1009
+ }
1010
+ /**
1011
+ * Generate a summary of the middle messages.
1012
+ */
1013
+ async generateSummary(messages) {
1014
+ const middleContent = this.formatMessagesForSummary(messages);
1015
+ const prompt = SUMMARIZATION_PROMPT.replace("{{MIDDLE_CONTENT}}", middleContent).replace(
1016
+ "{{MAX_TOKENS}}",
1017
+ String(this.config.maxSummaryTokens)
1018
+ );
1019
+ try {
1020
+ const client = this.getClient();
1021
+ const response = await client.messages.create({
1022
+ model: this.config.model,
1023
+ max_tokens: this.config.maxSummaryTokens,
1024
+ messages: [
1025
+ {
1026
+ role: "user",
1027
+ content: prompt
1028
+ }
1029
+ ]
1030
+ });
1031
+ const textBlock = response.content.find((block) => block.type === "text");
1032
+ if (textBlock && "text" in textBlock) {
1033
+ return textBlock.text;
1034
+ }
1035
+ return "[Summary generation failed - no text in response]";
1036
+ } catch (error) {
1037
+ const message = error instanceof Error ? error.message : String(error);
1038
+ return `[Summary generation failed: ${message}]`;
1039
+ }
1040
+ }
1041
+ /**
1042
+ * Format messages for the summarization prompt.
1043
+ */
1044
+ formatMessagesForSummary(messages) {
1045
+ return messages.map((msg, i) => {
1046
+ const role = msg.role.toUpperCase();
1047
+ const content = this.extractContent(msg);
1048
+ return `[${i + 1}] ${role}:
1049
+ ${content}`;
1050
+ }).join("\n\n---\n\n");
1051
+ }
1052
+ /**
1053
+ * Extract text content from a message.
1054
+ */
1055
+ extractContent(msg) {
1056
+ if (typeof msg.content === "string") {
1057
+ return this.truncateContent(msg.content);
1058
+ }
1059
+ const parts = [];
1060
+ for (const part of msg.content) {
1061
+ if (part.text) {
1062
+ parts.push(this.truncateContent(part.text));
1063
+ }
1064
+ }
1065
+ return parts.join("\n");
1066
+ }
1067
+ /**
1068
+ * Truncate very long content for summary input.
1069
+ */
1070
+ truncateContent(content, maxChars = 2e3) {
1071
+ if (content.length <= maxChars) return content;
1072
+ return content.slice(0, maxChars) + `
1073
+ [...truncated ${content.length - maxChars} chars]`;
1074
+ }
1075
+ /**
1076
+ * Estimate the cost of summarization.
1077
+ *
1078
+ * @param messages - Messages that would be summarized
1079
+ * @returns Estimated cost in USD
1080
+ */
1081
+ estimateCost(messages) {
1082
+ if (messages.length <= this.config.keepFirst + this.config.keepLast) {
1083
+ return 0;
1084
+ }
1085
+ const middleMessages = messages.slice(this.config.keepFirst, -this.config.keepLast);
1086
+ const inputTokens = middleMessages.reduce((sum, msg) => {
1087
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
1088
+ return sum + estimateTokens(content);
1089
+ }, 0);
1090
+ const inputCost = inputTokens / 1e6 * 0.25;
1091
+ const outputCost = this.config.maxSummaryTokens / 1e6 * 1.25;
1092
+ return inputCost + outputCost;
1093
+ }
1094
+ /**
1095
+ * Get statistics about potential summarization.
1096
+ */
1097
+ getStats(messages) {
1098
+ const wouldKeep = Math.min(messages.length, this.config.keepFirst + this.config.keepLast);
1099
+ const wouldSummarize = Math.max(0, messages.length - wouldKeep);
1100
+ return {
1101
+ totalMessages: messages.length,
1102
+ wouldKeep,
1103
+ wouldSummarize,
1104
+ estimatedCost: this.estimateCost(messages)
1105
+ };
1106
+ }
1107
+ };
1108
+
1109
+ // src/lib/condenser/index.ts
1110
+ var DEFAULT_CONFIG3 = {
1111
+ enabled: true,
1112
+ lightThreshold: 0.7,
1113
+ mediumThreshold: 0.85,
1114
+ heavyThreshold: 0.95,
1115
+ modelLimit: 2e5,
1116
+ model: "claude-sonnet-4-20250514",
1117
+ pruning: {},
1118
+ summarization: {}
1119
+ };
1120
+ var ContextCondenser = class {
1121
+ config;
1122
+ tracker;
1123
+ deduplicator;
1124
+ pruner;
1125
+ summarizer;
1126
+ /** Metrics for tracking */
1127
+ metrics = {
1128
+ condensationCount: 0,
1129
+ tokensRecovered: 0,
1130
+ lastLevel: "none"
1131
+ };
1132
+ constructor(config = {}) {
1133
+ this.config = { ...DEFAULT_CONFIG3, ...config };
1134
+ this.tracker = createTracker(this.config.model);
1135
+ this.tracker.limit = this.config.modelLimit;
1136
+ this.deduplicator = new FileDeduplicator();
1137
+ this.pruner = new TokenPruner(this.config.pruning);
1138
+ this.summarizer = new ConversationSummarizer(this.config.summarization);
1139
+ }
1140
+ /**
1141
+ * Main entry point - condense messages if needed.
1142
+ *
1143
+ * @param messages - Current conversation messages
1144
+ * @returns Condensed messages and metadata
1145
+ */
1146
+ async condense(messages) {
1147
+ const startTime = Date.now();
1148
+ if (!this.config.enabled) {
1149
+ return this.createResult(messages, messages, "none", startTime);
1150
+ }
1151
+ this.updateTrackerFromMessages(messages);
1152
+ const level = getCompressionLevel(this.tracker, {
1153
+ light: this.config.lightThreshold,
1154
+ medium: this.config.mediumThreshold,
1155
+ heavy: this.config.heavyThreshold
1156
+ });
1157
+ if (level === "none") {
1158
+ return this.createResult(messages, messages, level, startTime);
1159
+ }
1160
+ let condensed;
1161
+ switch (level) {
1162
+ case "light":
1163
+ condensed = this.applyDeduplication(messages);
1164
+ break;
1165
+ case "medium":
1166
+ condensed = this.applyPruning(messages);
1167
+ break;
1168
+ case "heavy":
1169
+ condensed = await this.applySummarization(messages);
1170
+ break;
1171
+ }
1172
+ this.metrics.condensationCount++;
1173
+ this.metrics.lastLevel = level;
1174
+ return this.createResult(messages, condensed, level, startTime);
1175
+ }
1176
+ /**
1177
+ * Apply light compression (deduplication).
1178
+ */
1179
+ applyDeduplication(messages) {
1180
+ return messages;
1181
+ }
1182
+ /**
1183
+ * Apply medium compression (pruning).
1184
+ */
1185
+ applyPruning(messages) {
1186
+ return this.pruner.pruneMessages(messages);
1187
+ }
1188
+ /**
1189
+ * Apply heavy compression (summarization).
1190
+ */
1191
+ async applySummarization(messages) {
1192
+ const pruned = this.applyPruning(messages);
1193
+ const summarized = await this.summarizer.summarize(pruned);
1194
+ return summarized;
1195
+ }
1196
+ /**
1197
+ * Update tracker from messages.
1198
+ */
1199
+ updateTrackerFromMessages(messages) {
1200
+ this.tracker = createTracker(this.config.model);
1201
+ this.tracker.limit = this.config.modelLimit;
1202
+ for (const msg of messages) {
1203
+ updateTrackerFromMessage(this.tracker, msg);
1204
+ }
1205
+ }
1206
+ /**
1207
+ * Create result object.
1208
+ */
1209
+ createResult(before, after, level, startTime) {
1210
+ const tokensBefore = this.estimateTokens(before);
1211
+ const tokensAfter = this.estimateTokens(after);
1212
+ const savings = tokensBefore > 0 ? (tokensBefore - tokensAfter) / tokensBefore * 100 : 0;
1213
+ if (level !== "none") {
1214
+ this.metrics.tokensRecovered += tokensBefore - tokensAfter;
1215
+ }
1216
+ return {
1217
+ messages: after,
1218
+ level,
1219
+ tokensBefore,
1220
+ tokensAfter,
1221
+ savingsPercentage: savings,
1222
+ durationMs: Date.now() - startTime
1223
+ };
1224
+ }
1225
+ /**
1226
+ * Estimate tokens for messages.
1227
+ */
1228
+ estimateTokens(messages) {
1229
+ const tempTracker = createTracker(this.config.model);
1230
+ for (const msg of messages) {
1231
+ updateTrackerFromMessage(tempTracker, msg);
1232
+ }
1233
+ return tempTracker.used;
1234
+ }
1235
+ /**
1236
+ * Get current tracker status.
1237
+ */
1238
+ getStatus() {
1239
+ return formatTrackerStatus(this.tracker);
1240
+ }
1241
+ /**
1242
+ * Get tracker for external monitoring.
1243
+ */
1244
+ getTracker() {
1245
+ return { ...this.tracker };
1246
+ }
1247
+ /**
1248
+ * Get metrics.
1249
+ */
1250
+ getMetrics() {
1251
+ return { ...this.metrics };
1252
+ }
1253
+ /**
1254
+ * Check if compression is needed.
1255
+ */
1256
+ needsCompression() {
1257
+ return getCompressionLevel(this.tracker, {
1258
+ light: this.config.lightThreshold,
1259
+ medium: this.config.mediumThreshold,
1260
+ heavy: this.config.heavyThreshold
1261
+ });
1262
+ }
1263
+ /**
1264
+ * Reset condenser state.
1265
+ */
1266
+ reset() {
1267
+ this.tracker = createTracker(this.config.model);
1268
+ this.tracker.limit = this.config.modelLimit;
1269
+ this.deduplicator.reset();
1270
+ this.metrics = {
1271
+ condensationCount: 0,
1272
+ tokensRecovered: 0,
1273
+ lastLevel: "none"
1274
+ };
1275
+ }
1276
+ /**
1277
+ * Get file deduplicator for integration with tool layer.
1278
+ */
1279
+ getDeduplicator() {
1280
+ return this.deduplicator;
1281
+ }
1282
+ };
1283
+ function createCondenser(squadConfig) {
1284
+ const condenserConfig = squadConfig?.condenser || {};
1285
+ const modelConfig = squadConfig?.model || {};
1286
+ return new ContextCondenser({
1287
+ enabled: condenserConfig.enabled ?? true,
1288
+ lightThreshold: condenserConfig.light_threshold ?? 0.7,
1289
+ mediumThreshold: condenserConfig.medium_threshold ?? 0.85,
1290
+ heavyThreshold: condenserConfig.heavy_threshold ?? 0.95,
1291
+ model: modelConfig.default ?? "claude-sonnet-4-20250514",
1292
+ pruning: {
1293
+ protectRecent: condenserConfig.protect_recent ?? 4e4
1294
+ }
1295
+ });
10
1296
  }
11
1297
  export {
1298
+ ContextCondenser,
1299
+ ConversationSummarizer,
1300
+ FileDeduplicator,
1301
+ TokenPruner,
1302
+ addGoalToSquad,
1303
+ createCondenser,
1304
+ createTracker,
1305
+ estimateMessageTokens,
1306
+ estimateTokens,
1307
+ findProjectRoot,
1308
+ findSquadsDir,
1309
+ formatTrackerStatus,
1310
+ getCompressionLevel,
1311
+ getSquadLocalSkills,
1312
+ listAgents,
1313
+ listSquads,
1314
+ loadAgentDefinition,
12
1315
  loadSquad,
13
- runAgent,
1316
+ parseSquadFile,
1317
+ resolveExecutionContext,
1318
+ updateGoalInSquad,
1319
+ updateTracker,
14
1320
  version
15
1321
  };
16
1322
  //# sourceMappingURL=index.js.map