@jaimevalasek/aioson 1.6.0 → 1.7.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 (252) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +729 -232
  3. package/docs/design-previews/pt.squarespace.com-homepage.html +889 -0
  4. package/docs/integrations/sdlc-genius-boundary.md +76 -0
  5. package/docs/integrations/sdlc-genius-eval-matrix.md +75 -0
  6. package/docs/integrations/sdlc-genius-install-checklist.md +93 -0
  7. package/docs/integrations/sdlc-genius-review-samples.md +86 -0
  8. package/docs/pt/README.md +3 -0
  9. package/docs/pt/agentes.md +1 -0
  10. package/docs/pt/comandos-cli.md +888 -2
  11. package/docs/pt/design-hybrid-forge.md +255 -6
  12. package/docs/pt/devlog-pipeline.md +270 -0
  13. package/docs/pt/fluxo-artefatos.md +178 -0
  14. package/docs/pt/hooks-session-guard.md +454 -0
  15. package/docs/pt/monitor-de-contexto.md +59 -5
  16. package/docs/pt/sdd-automation-scripts.md +557 -0
  17. package/docs/pt/site-forge.md +309 -0
  18. package/docs/pt/spec-learnings-pipeline.md +265 -0
  19. package/package.json +1 -1
  20. package/src/a2a/client.js +165 -0
  21. package/src/a2a/server.js +223 -0
  22. package/src/cli.js +235 -1
  23. package/src/commands/agent-audit.js +397 -0
  24. package/src/commands/agent-export-skill.js +229 -0
  25. package/src/commands/artifact-validate.js +189 -0
  26. package/src/commands/brief-gen.js +405 -0
  27. package/src/commands/brief-validate.js +65 -0
  28. package/src/commands/classify.js +256 -0
  29. package/src/commands/context-compact.js +49 -0
  30. package/src/commands/context-health.js +175 -0
  31. package/src/commands/context-monitor.js +71 -0
  32. package/src/commands/context-trim.js +177 -0
  33. package/src/commands/detect-test-runner.js +55 -0
  34. package/src/commands/devlog-export-brains.js +27 -0
  35. package/src/commands/devlog-process.js +292 -0
  36. package/src/commands/devlog-watch.js +131 -0
  37. package/src/commands/feature-close.js +165 -0
  38. package/src/commands/gate-check.js +228 -0
  39. package/src/commands/hooks-emit.js +253 -0
  40. package/src/commands/hooks-install.js +347 -0
  41. package/src/commands/learning-auto-promote.js +195 -0
  42. package/src/commands/learning-evolve.js +18 -9
  43. package/src/commands/learning-export.js +103 -0
  44. package/src/commands/learning-rollback.js +164 -0
  45. package/src/commands/live.js +25 -1
  46. package/src/commands/pattern-detect.js +33 -0
  47. package/src/commands/preflight-context.js +30 -0
  48. package/src/commands/preflight.js +208 -0
  49. package/src/commands/pulse-update.js +130 -0
  50. package/src/commands/runner-daemon.js +274 -0
  51. package/src/commands/runner-plan.js +70 -0
  52. package/src/commands/runner-queue-from-plan.js +166 -0
  53. package/src/commands/runner-queue.js +189 -0
  54. package/src/commands/runner-run.js +129 -0
  55. package/src/commands/runtime.js +47 -1
  56. package/src/commands/self-implement-loop.js +256 -0
  57. package/src/commands/session-guard.js +218 -0
  58. package/src/commands/sizing.js +165 -0
  59. package/src/commands/skill.js +65 -0
  60. package/src/commands/spec-checkpoint.js +177 -0
  61. package/src/commands/spec-status.js +79 -0
  62. package/src/commands/spec-sync.js +190 -0
  63. package/src/commands/spec-tasks.js +288 -0
  64. package/src/commands/squad-autorun.js +1220 -0
  65. package/src/commands/squad-bus.js +217 -0
  66. package/src/commands/squad-card.js +149 -0
  67. package/src/commands/squad-daemon.js +134 -0
  68. package/src/commands/squad-dependency-graph.js +164 -0
  69. package/src/commands/squad-review.js +106 -0
  70. package/src/commands/squad-scaffold.js +55 -0
  71. package/src/commands/squad-tool-register.js +157 -0
  72. package/src/commands/state-save.js +122 -0
  73. package/src/commands/update.js +2 -0
  74. package/src/commands/verify-gate.js +572 -0
  75. package/src/commands/workflow-execute.js +241 -0
  76. package/src/constants.js +9 -0
  77. package/src/install-profile.js +2 -2
  78. package/src/install-wizard.js +3 -2
  79. package/src/installer.js +6 -0
  80. package/src/lib/health-check.js +158 -0
  81. package/src/lib/hook-protocol.js +76 -0
  82. package/src/mcp/apps/squad-dashboard/app.js +163 -0
  83. package/src/mcp/apps/squad-dashboard/index.html +261 -0
  84. package/src/mcp/apps/squad-dashboard/mcp-manifest.json +23 -0
  85. package/src/mcp/resources/squad-state.js +130 -0
  86. package/src/preflight-engine.js +443 -0
  87. package/src/runner/cascade.js +97 -0
  88. package/src/runner/cli-launcher.js +109 -0
  89. package/src/runner/plan-importer.js +63 -0
  90. package/src/runner/queue-store.js +159 -0
  91. package/src/runtime-store.js +61 -3
  92. package/src/squad/agent-teams-adapter.js +264 -0
  93. package/src/squad/brief-validator.js +350 -0
  94. package/src/squad/bus-bridge.js +140 -0
  95. package/src/squad/context-compactor.js +265 -0
  96. package/src/squad/cross-ai-synthesizer.js +250 -0
  97. package/src/squad/hooks-generator.js +196 -0
  98. package/src/squad/inter-squad-events.js +175 -0
  99. package/src/squad/intra-bus.js +345 -0
  100. package/src/squad/learning-extractor.js +213 -0
  101. package/src/squad/pattern-detector.js +365 -0
  102. package/src/squad/preflight-context.js +296 -0
  103. package/src/squad/recovery-context.js +242 -71
  104. package/src/squad/reflection.js +365 -0
  105. package/src/squad/squad-scaffold.js +177 -0
  106. package/src/squad/state-manager.js +310 -0
  107. package/src/squad/task-decomposer.js +652 -0
  108. package/src/squad/verify-gate.js +303 -0
  109. package/src/updater.js +4 -5
  110. package/src/worker-runner.js +186 -1
  111. package/template/.aioson/agents/analyst.md +62 -1
  112. package/template/.aioson/agents/architect.md +61 -1
  113. package/template/.aioson/agents/design-hybrid-forge.md +14 -0
  114. package/template/.aioson/agents/dev.md +242 -24
  115. package/template/.aioson/agents/deyvin.md +66 -8
  116. package/template/.aioson/agents/discovery-design-doc.md +44 -0
  117. package/template/.aioson/agents/genome.md +14 -0
  118. package/template/.aioson/agents/neo.md +78 -1
  119. package/template/.aioson/agents/orache.md +50 -4
  120. package/template/.aioson/agents/orchestrator.md +197 -1
  121. package/template/.aioson/agents/pm.md +35 -0
  122. package/template/.aioson/agents/product.md +50 -5
  123. package/template/.aioson/agents/profiler-enricher.md +14 -0
  124. package/template/.aioson/agents/profiler-forge.md +14 -0
  125. package/template/.aioson/agents/profiler-researcher.md +14 -0
  126. package/template/.aioson/agents/qa.md +172 -21
  127. package/template/.aioson/agents/setup.md +79 -9
  128. package/template/.aioson/agents/sheldon.md +131 -6
  129. package/template/.aioson/agents/site-forge.md +1753 -0
  130. package/template/.aioson/agents/squad.md +162 -0
  131. package/template/.aioson/agents/tester.md +53 -0
  132. package/template/.aioson/agents/ux-ui.md +34 -1
  133. package/template/.aioson/brains/README.md +128 -0
  134. package/template/.aioson/brains/_index.json +16 -0
  135. package/template/.aioson/brains/scripts/query.js +103 -0
  136. package/template/.aioson/brains/site-forge/visual-patterns.brain.json +205 -0
  137. package/template/.aioson/config.md +143 -13
  138. package/template/.aioson/constitution.md +33 -0
  139. package/template/.aioson/context/project-pulse.md +34 -0
  140. package/template/.aioson/docs/LAYERS.md +79 -0
  141. package/template/.aioson/docs/README.md +76 -0
  142. package/template/.aioson/docs/example-external-api-context.md +72 -0
  143. package/template/.aioson/locales/en/agents/architect.md +17 -0
  144. package/template/.aioson/locales/en/agents/dev.md +79 -13
  145. package/template/.aioson/locales/en/agents/orache.md +6 -0
  146. package/template/.aioson/locales/en/agents/orchestrator.md +24 -0
  147. package/template/.aioson/locales/en/agents/product.md +50 -0
  148. package/template/.aioson/locales/en/agents/sheldon.md +115 -0
  149. package/template/.aioson/locales/en/agents/squad.md +14 -0
  150. package/template/.aioson/locales/en/agents/tester.md +6 -0
  151. package/template/.aioson/locales/es/agents/analyst.md +2 -0
  152. package/template/.aioson/locales/es/agents/architect.md +19 -0
  153. package/template/.aioson/locales/es/agents/dev.md +64 -4
  154. package/template/.aioson/locales/es/agents/deyvin.md +2 -0
  155. package/template/.aioson/locales/es/agents/discovery-design-doc.md +2 -0
  156. package/template/.aioson/locales/es/agents/genome.md +2 -0
  157. package/template/.aioson/locales/es/agents/neo.md +2 -0
  158. package/template/.aioson/locales/es/agents/orache.md +2 -0
  159. package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
  160. package/template/.aioson/locales/es/agents/pair.md +2 -0
  161. package/template/.aioson/locales/es/agents/pm.md +2 -0
  162. package/template/.aioson/locales/es/agents/product.md +52 -0
  163. package/template/.aioson/locales/es/agents/profiler-enricher.md +2 -0
  164. package/template/.aioson/locales/es/agents/profiler-forge.md +2 -0
  165. package/template/.aioson/locales/es/agents/profiler-researcher.md +2 -0
  166. package/template/.aioson/locales/es/agents/qa.md +2 -0
  167. package/template/.aioson/locales/es/agents/setup.md +2 -0
  168. package/template/.aioson/locales/es/agents/sheldon.md +117 -0
  169. package/template/.aioson/locales/es/agents/squad.md +16 -0
  170. package/template/.aioson/locales/es/agents/tester.md +9 -0
  171. package/template/.aioson/locales/es/agents/ux-ui.md +2 -0
  172. package/template/.aioson/locales/fr/agents/analyst.md +2 -0
  173. package/template/.aioson/locales/fr/agents/architect.md +19 -0
  174. package/template/.aioson/locales/fr/agents/dev.md +64 -4
  175. package/template/.aioson/locales/fr/agents/deyvin.md +2 -0
  176. package/template/.aioson/locales/fr/agents/discovery-design-doc.md +2 -0
  177. package/template/.aioson/locales/fr/agents/genome.md +2 -0
  178. package/template/.aioson/locales/fr/agents/neo.md +2 -0
  179. package/template/.aioson/locales/fr/agents/orache.md +2 -0
  180. package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
  181. package/template/.aioson/locales/fr/agents/pair.md +2 -0
  182. package/template/.aioson/locales/fr/agents/pm.md +2 -0
  183. package/template/.aioson/locales/fr/agents/product.md +52 -0
  184. package/template/.aioson/locales/fr/agents/profiler-enricher.md +2 -0
  185. package/template/.aioson/locales/fr/agents/profiler-forge.md +2 -0
  186. package/template/.aioson/locales/fr/agents/profiler-researcher.md +2 -0
  187. package/template/.aioson/locales/fr/agents/qa.md +2 -0
  188. package/template/.aioson/locales/fr/agents/setup.md +2 -0
  189. package/template/.aioson/locales/fr/agents/sheldon.md +117 -0
  190. package/template/.aioson/locales/fr/agents/squad.md +16 -0
  191. package/template/.aioson/locales/fr/agents/tester.md +9 -0
  192. package/template/.aioson/locales/fr/agents/ux-ui.md +2 -0
  193. package/template/.aioson/locales/pt-BR/agents/analyst.md +64 -3
  194. package/template/.aioson/locales/pt-BR/agents/architect.md +42 -0
  195. package/template/.aioson/locales/pt-BR/agents/dev.md +147 -14
  196. package/template/.aioson/locales/pt-BR/agents/deyvin.md +47 -0
  197. package/template/.aioson/locales/pt-BR/agents/neo.md +62 -1
  198. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +158 -2
  199. package/template/.aioson/locales/pt-BR/agents/pm.md +95 -1
  200. package/template/.aioson/locales/pt-BR/agents/product.md +145 -18
  201. package/template/.aioson/locales/pt-BR/agents/qa.md +16 -0
  202. package/template/.aioson/locales/pt-BR/agents/setup.md +101 -18
  203. package/template/.aioson/locales/pt-BR/agents/sheldon.md +132 -1
  204. package/template/.aioson/locales/pt-BR/agents/squad.md +14 -0
  205. package/template/.aioson/locales/pt-BR/agents/tester.md +449 -0
  206. package/template/.aioson/rules/README.md +69 -0
  207. package/template/.aioson/rules/data-format-convention.md +136 -0
  208. package/template/.aioson/rules/example-monetary-values.md +30 -0
  209. package/template/.aioson/schemas/squad-manifest.schema.json +124 -3
  210. package/template/.aioson/skills/design/pt.squarespace.com/.skill-meta.json +31 -0
  211. package/template/.aioson/skills/design/pt.squarespace.com/SKILL.md +66 -0
  212. package/template/.aioson/skills/design/pt.squarespace.com/references/components.md +368 -0
  213. package/template/.aioson/skills/design/pt.squarespace.com/references/design-tokens.md +150 -0
  214. package/template/.aioson/skills/design/pt.squarespace.com/references/motion.md +270 -0
  215. package/template/.aioson/skills/design/pt.squarespace.com/references/patterns.md +189 -0
  216. package/template/.aioson/skills/design/pt.squarespace.com/references/websites.md +165 -0
  217. package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +1 -0
  218. package/template/.aioson/skills/process/aioson-spec-driven/references/analyst.md +30 -0
  219. package/template/.aioson/skills/process/aioson-spec-driven/references/architect.md +23 -0
  220. package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +47 -0
  221. package/template/.aioson/skills/process/aioson-spec-driven/references/deyvin.md +27 -0
  222. package/template/.aioson/skills/process/aioson-spec-driven/references/maintenance-and-state.md +35 -0
  223. package/template/.aioson/skills/process/aioson-spec-driven/references/product.md +25 -0
  224. package/template/.aioson/skills/process/aioson-spec-driven/references/qa.md +30 -0
  225. package/template/.aioson/skills/process/aioson-spec-driven/references/sheldon.md +25 -0
  226. package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +4 -1
  227. package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +15 -0
  228. package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +32 -0
  229. package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +20 -0
  230. package/template/.aioson/skills/process/simplify/SKILL.md +173 -0
  231. package/template/.aioson/skills/static/context-budget-guide.md +46 -0
  232. package/template/.aioson/skills/static/harness-sensors.md +74 -0
  233. package/template/.aioson/skills/static/multi-agent-patterns.md +43 -0
  234. package/template/.aioson/skills/static/react-motion-patterns.md +22 -0
  235. package/template/.aioson/skills/static/static-html-patterns/checklists.md +43 -0
  236. package/template/.aioson/skills/static/static-html-patterns/css-tokens.md +609 -0
  237. package/template/.aioson/skills/static/static-html-patterns/motion.md +193 -0
  238. package/template/.aioson/skills/static/static-html-patterns/premium.md +711 -0
  239. package/template/.aioson/skills/static/static-html-patterns/structure.md +209 -0
  240. package/template/.aioson/skills/static/static-html-patterns/utilities.md +190 -0
  241. package/template/.aioson/skills/static/static-html-patterns.md +58 -1913
  242. package/template/.aioson/skills/static/threejs-patterns.md +929 -0
  243. package/template/.aioson/skills/static/web-research-cache.md +112 -0
  244. package/template/.aioson/tasks/implementation-plan.md +21 -1
  245. package/template/.claude/commands/aioson/agent/design-hybrid-forge.md +5 -0
  246. package/template/.claude/commands/aioson/agent/orache.md +5 -0
  247. package/template/.claude/commands/aioson/agent/sheldon.md +5 -0
  248. package/template/.claude/commands/aioson/agent/site-forge.md +5 -0
  249. package/template/AGENTS.md +55 -3
  250. package/template/CLAUDE.md +30 -0
  251. package/template/OPENCODE.md +4 -0
  252. package/template/researchs/.gitkeep +0 -0
@@ -0,0 +1,296 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Preflight Context Estimator — Plan 80, Script 2
5
+ *
6
+ * Estimates the token budget consumed by system prompt components before
7
+ * a session starts. Provides visibility into truncation risk and detects
8
+ * duplicate content between context files.
9
+ *
10
+ * Usage:
11
+ * node preflight-context.js --agent=dev [--squad=<slug>] [--verbose]
12
+ *
13
+ * Output: visual bar chart of context budget per component.
14
+ */
15
+
16
+ const fs = require('node:fs/promises');
17
+ const path = require('node:path');
18
+ const crypto = require('node:crypto');
19
+
20
+ const SQUADS_DIR = path.join('.aioson', 'squads');
21
+
22
+ // Default limits (chars). These mirror what the Claude Code harness injects.
23
+ const DEFAULT_LIMITS = {
24
+ agent_file: 4000,
25
+ config: 4000,
26
+ project_context: 4000,
27
+ rules: 4000,
28
+ squad_files: 8000,
29
+ claude_md: 8000
30
+ };
31
+
32
+ // ─── File size reader ────────────────────────────────────────────────────────
33
+
34
+ async function readFileChars(filePath) {
35
+ try {
36
+ const content = await fs.readFile(filePath, 'utf8');
37
+ return { chars: content.length, content };
38
+ } catch {
39
+ return { chars: 0, content: '' };
40
+ }
41
+ }
42
+
43
+ // ─── Duplicate detection ─────────────────────────────────────────────────────
44
+
45
+ /**
46
+ * Build content fingerprints from chunks of text.
47
+ * Uses sliding window of ~200 chars to detect shared blocks.
48
+ */
49
+ function buildFingerprints(content, windowSize = 200) {
50
+ const hashes = new Set();
51
+ const text = content.replace(/\s+/g, ' ').trim();
52
+
53
+ for (let i = 0; i <= text.length - windowSize; i += windowSize / 2) {
54
+ const chunk = text.slice(i, i + windowSize);
55
+ const hash = crypto.createHash('md5').update(chunk).digest('hex').slice(0, 12);
56
+ hashes.add(hash);
57
+ }
58
+
59
+ return hashes;
60
+ }
61
+
62
+ /**
63
+ * Detect duplicate content blocks between files.
64
+ * Returns array of { file1, file2, overlapPercent }.
65
+ */
66
+ function detectDuplicates(fileEntries) {
67
+ const duplicates = [];
68
+
69
+ for (let i = 0; i < fileEntries.length; i++) {
70
+ for (let j = i + 1; j < fileEntries.length; j++) {
71
+ const a = fileEntries[i];
72
+ const b = fileEntries[j];
73
+
74
+ if (!a.fingerprints || !b.fingerprints) continue;
75
+ if (a.fingerprints.size === 0 || b.fingerprints.size === 0) continue;
76
+
77
+ let overlap = 0;
78
+ for (const hash of a.fingerprints) {
79
+ if (b.fingerprints.has(hash)) overlap++;
80
+ }
81
+
82
+ const smaller = Math.min(a.fingerprints.size, b.fingerprints.size);
83
+ if (smaller === 0) continue;
84
+
85
+ const overlapPercent = Math.round((overlap / smaller) * 100);
86
+ if (overlapPercent > 20) {
87
+ duplicates.push({
88
+ file1: a.label,
89
+ file2: b.label,
90
+ overlapPercent
91
+ });
92
+ }
93
+ }
94
+ }
95
+
96
+ return duplicates;
97
+ }
98
+
99
+ // ─── Bar renderer ────────────────────────────────────────────────────────────
100
+
101
+ function renderBar(used, limit, width = 10) {
102
+ const pct = limit > 0 ? Math.min(used / limit, 1) : 0;
103
+ const filled = Math.round(pct * width);
104
+ const empty = width - filled;
105
+ return '█'.repeat(filled) + '░'.repeat(empty);
106
+ }
107
+
108
+ function formatSize(chars) {
109
+ if (chars >= 1000) return `${(chars / 1000).toFixed(1)}k`;
110
+ return `${chars}`;
111
+ }
112
+
113
+ // ─── Public API ──────────────────────────────────────────────────────────────
114
+
115
+ /**
116
+ * Estimate the context budget for a given agent/squad session.
117
+ *
118
+ * @param {string} projectDir — Project root
119
+ * @param {object} options — { agent, squad, verbose }
120
+ * @returns {Promise<object>} — { components[], total, totalLimit, duplicates[], warnings[] }
121
+ */
122
+ async function estimateContext(projectDir, options = {}) {
123
+ const { agent = 'dev', squad, verbose } = options;
124
+ const components = [];
125
+ const warnings = [];
126
+
127
+ // 1. Agent file
128
+ const agentPath = path.join(projectDir, '.aioson', 'agents', `${agent}.md`);
129
+ const agentFile = await readFileChars(agentPath);
130
+ const agentEntry = {
131
+ label: 'agent file',
132
+ path: agentPath,
133
+ chars: agentFile.chars,
134
+ limit: DEFAULT_LIMITS.agent_file,
135
+ content: agentFile.content
136
+ };
137
+ components.push(agentEntry);
138
+ if (agentFile.chars > DEFAULT_LIMITS.agent_file) {
139
+ warnings.push(`${agent}.md is ${formatSize(agentFile.chars)} — will be TRUNCATED by harness`);
140
+ }
141
+
142
+ // 2. config.md
143
+ const configPath = path.join(projectDir, '.aioson', 'config.md');
144
+ const configFile = await readFileChars(configPath);
145
+ components.push({
146
+ label: 'config.md',
147
+ path: configPath,
148
+ chars: configFile.chars,
149
+ limit: DEFAULT_LIMITS.config,
150
+ content: configFile.content
151
+ });
152
+
153
+ // 3. project.context.md
154
+ const ctxPath = path.join(projectDir, '.aioson', 'context', 'project.context.md');
155
+ const ctxFile = await readFileChars(ctxPath);
156
+ components.push({
157
+ label: 'project.context.md',
158
+ path: ctxPath,
159
+ chars: ctxFile.chars,
160
+ limit: DEFAULT_LIMITS.project_context,
161
+ content: ctxFile.content
162
+ });
163
+
164
+ // 4. Active rules
165
+ const rulesDir = path.join(projectDir, '.aioson', 'rules');
166
+ let rulesChars = 0;
167
+ let rulesContent = '';
168
+ try {
169
+ const entries = await fs.readdir(rulesDir);
170
+ const mdFiles = entries.filter((f) => f.endsWith('.md'));
171
+ for (const f of mdFiles) {
172
+ const r = await readFileChars(path.join(rulesDir, f));
173
+ rulesChars += r.chars;
174
+ rulesContent += r.content + '\n';
175
+ }
176
+ } catch { /* no rules dir */ }
177
+ components.push({
178
+ label: 'active rules',
179
+ path: rulesDir,
180
+ chars: rulesChars,
181
+ limit: DEFAULT_LIMITS.rules,
182
+ content: rulesContent
183
+ });
184
+
185
+ // 5. Squad-specific files (if --squad provided)
186
+ if (squad) {
187
+ const squadDir = path.join(projectDir, SQUADS_DIR, squad);
188
+ const agentsFile = await readFileChars(path.join(squadDir, 'agents', 'agents.md'));
189
+ const squadFile = await readFileChars(path.join(squadDir, 'squad.md'));
190
+ const totalSquad = agentsFile.chars + squadFile.chars;
191
+ components.push({
192
+ label: `squad: ${squad}`,
193
+ path: squadDir,
194
+ chars: totalSquad,
195
+ limit: DEFAULT_LIMITS.squad_files,
196
+ content: agentsFile.content + '\n' + squadFile.content
197
+ });
198
+ }
199
+
200
+ // 6. CLAUDE.md + CLAUDE.local.md
201
+ const claudeMd = await readFileChars(path.join(projectDir, 'CLAUDE.md'));
202
+ const claudeLocal = await readFileChars(path.join(projectDir, 'CLAUDE.local.md'));
203
+ const claudeTotal = claudeMd.chars + claudeLocal.chars;
204
+ components.push({
205
+ label: 'CLAUDE.md',
206
+ path: path.join(projectDir, 'CLAUDE.md'),
207
+ chars: claudeTotal,
208
+ limit: DEFAULT_LIMITS.claude_md,
209
+ content: claudeMd.content + '\n' + claudeLocal.content
210
+ });
211
+
212
+ // Calculate totals
213
+ const totalChars = components.reduce((sum, c) => sum + c.chars, 0);
214
+ const totalLimit = components.reduce((sum, c) => sum + c.limit, 0);
215
+
216
+ // Duplicate detection (verbose mode)
217
+ let duplicates = [];
218
+ if (verbose) {
219
+ const withFingerprints = components
220
+ .filter((c) => c.content && c.chars > 100)
221
+ .map((c) => ({ ...c, fingerprints: buildFingerprints(c.content) }));
222
+ duplicates = detectDuplicates(withFingerprints);
223
+ }
224
+
225
+ // Budget warning
226
+ const remaining = totalLimit - totalChars;
227
+ if (remaining < 2000) {
228
+ warnings.push(`Budget leaves only ~${formatSize(remaining)} for working context`);
229
+ }
230
+
231
+ // Exit code semantics
232
+ let exitCode = 0;
233
+ if (warnings.some((w) => w.includes('TRUNCATED'))) exitCode = 1;
234
+ if (totalChars > totalLimit) exitCode = 2;
235
+
236
+ // Clean content from response (only needed internally)
237
+ const cleanComponents = components.map(({ content, ...rest }) => rest);
238
+
239
+ return {
240
+ agent,
241
+ squad: squad || null,
242
+ components: cleanComponents,
243
+ total: totalChars,
244
+ totalLimit,
245
+ remaining,
246
+ duplicates,
247
+ warnings,
248
+ exitCode
249
+ };
250
+ }
251
+
252
+ /**
253
+ * Format the estimation result as a human-readable report.
254
+ */
255
+ function formatReport(result) {
256
+ const lines = [];
257
+ lines.push(`Context budget estimate for @${result.agent} session`);
258
+ lines.push('─'.repeat(45));
259
+
260
+ for (const c of result.components) {
261
+ const pct = c.limit > 0 ? Math.round((c.chars / c.limit) * 100) : 0;
262
+ const bar = renderBar(c.chars, c.limit);
263
+ const label = c.label.padEnd(20);
264
+ const size = `${formatSize(c.chars)} / ${formatSize(c.limit)}`.padEnd(14);
265
+ lines.push(`${label} ${size} ${bar} ${pct}%`);
266
+ }
267
+
268
+ lines.push('─'.repeat(45));
269
+ const totalPct = result.totalLimit > 0 ? Math.round((result.total / result.totalLimit) * 100) : 0;
270
+ const totalBar = renderBar(result.total, result.totalLimit);
271
+ lines.push(`${'Total system prompt'.padEnd(20)} ~${formatSize(result.total)} / ${formatSize(result.totalLimit)}`.padEnd(35) + ` ${totalBar} ${totalPct}%`);
272
+ lines.push('');
273
+
274
+ for (const w of result.warnings) {
275
+ lines.push(`⚠ ${w}`);
276
+ }
277
+
278
+ if (result.remaining > 0 && !result.warnings.some((w) => w.includes('only'))) {
279
+ lines.push(`✓ Budget leaves ~${formatSize(result.remaining)} for working context`);
280
+ }
281
+
282
+ if (result.duplicates.length > 0) {
283
+ lines.push('');
284
+ lines.push('Duplicate content detected:');
285
+ for (const d of result.duplicates) {
286
+ lines.push(` ⚠ ${d.file1} ↔ ${d.file2}: ${d.overlapPercent}% overlap`);
287
+ }
288
+ }
289
+
290
+ return lines.join('\n');
291
+ }
292
+
293
+ module.exports = {
294
+ estimateContext,
295
+ formatReport
296
+ };