@jaimevalasek/aioson 1.6.0 → 1.7.2

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 (275) hide show
  1. package/CHANGELOG.md +74 -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 +22 -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/copywriter.md +463 -0
  114. package/template/.aioson/agents/design-hybrid-forge.md +14 -0
  115. package/template/.aioson/agents/dev.md +271 -25
  116. package/template/.aioson/agents/deyvin.md +67 -8
  117. package/template/.aioson/agents/discovery-design-doc.md +44 -0
  118. package/template/.aioson/agents/genome.md +14 -0
  119. package/template/.aioson/agents/neo.md +83 -2
  120. package/template/.aioson/agents/orache.md +50 -4
  121. package/template/.aioson/agents/orchestrator.md +197 -1
  122. package/template/.aioson/agents/pm.md +35 -0
  123. package/template/.aioson/agents/product.md +50 -5
  124. package/template/.aioson/agents/profiler-enricher.md +14 -0
  125. package/template/.aioson/agents/profiler-forge.md +14 -0
  126. package/template/.aioson/agents/profiler-researcher.md +14 -0
  127. package/template/.aioson/agents/qa.md +273 -21
  128. package/template/.aioson/agents/setup.md +96 -10
  129. package/template/.aioson/agents/sheldon.md +131 -6
  130. package/template/.aioson/agents/site-forge.md +1753 -0
  131. package/template/.aioson/agents/squad.md +352 -0
  132. package/template/.aioson/agents/tester.md +53 -0
  133. package/template/.aioson/agents/ux-ui.md +203 -4
  134. package/template/.aioson/brains/README.md +128 -0
  135. package/template/.aioson/brains/_index.json +16 -0
  136. package/template/.aioson/brains/scripts/query.js +103 -0
  137. package/template/.aioson/brains/site-forge/visual-patterns.brain.json +205 -0
  138. package/template/.aioson/config.md +143 -13
  139. package/template/.aioson/constitution.md +33 -0
  140. package/template/.aioson/context/project-pulse.md +34 -0
  141. package/template/.aioson/docs/LAYERS.md +79 -0
  142. package/template/.aioson/docs/README.md +76 -0
  143. package/template/.aioson/docs/example-external-api-context.md +72 -0
  144. package/template/.aioson/genomes/copywriting.md +204 -0
  145. package/template/.aioson/locales/en/agents/architect.md +17 -0
  146. package/template/.aioson/locales/en/agents/dev.md +79 -13
  147. package/template/.aioson/locales/en/agents/orache.md +6 -0
  148. package/template/.aioson/locales/en/agents/orchestrator.md +24 -0
  149. package/template/.aioson/locales/en/agents/product.md +50 -0
  150. package/template/.aioson/locales/en/agents/sheldon.md +115 -0
  151. package/template/.aioson/locales/en/agents/squad.md +14 -0
  152. package/template/.aioson/locales/en/agents/tester.md +6 -0
  153. package/template/.aioson/locales/es/agents/analyst.md +2 -0
  154. package/template/.aioson/locales/es/agents/architect.md +19 -0
  155. package/template/.aioson/locales/es/agents/dev.md +64 -4
  156. package/template/.aioson/locales/es/agents/deyvin.md +2 -0
  157. package/template/.aioson/locales/es/agents/discovery-design-doc.md +2 -0
  158. package/template/.aioson/locales/es/agents/genome.md +2 -0
  159. package/template/.aioson/locales/es/agents/neo.md +2 -0
  160. package/template/.aioson/locales/es/agents/orache.md +2 -0
  161. package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
  162. package/template/.aioson/locales/es/agents/pair.md +2 -0
  163. package/template/.aioson/locales/es/agents/pm.md +2 -0
  164. package/template/.aioson/locales/es/agents/product.md +52 -0
  165. package/template/.aioson/locales/es/agents/profiler-enricher.md +2 -0
  166. package/template/.aioson/locales/es/agents/profiler-forge.md +2 -0
  167. package/template/.aioson/locales/es/agents/profiler-researcher.md +2 -0
  168. package/template/.aioson/locales/es/agents/qa.md +2 -0
  169. package/template/.aioson/locales/es/agents/setup.md +2 -0
  170. package/template/.aioson/locales/es/agents/sheldon.md +117 -0
  171. package/template/.aioson/locales/es/agents/squad.md +16 -0
  172. package/template/.aioson/locales/es/agents/tester.md +9 -0
  173. package/template/.aioson/locales/es/agents/ux-ui.md +2 -0
  174. package/template/.aioson/locales/fr/agents/analyst.md +2 -0
  175. package/template/.aioson/locales/fr/agents/architect.md +19 -0
  176. package/template/.aioson/locales/fr/agents/dev.md +64 -4
  177. package/template/.aioson/locales/fr/agents/deyvin.md +2 -0
  178. package/template/.aioson/locales/fr/agents/discovery-design-doc.md +2 -0
  179. package/template/.aioson/locales/fr/agents/genome.md +2 -0
  180. package/template/.aioson/locales/fr/agents/neo.md +2 -0
  181. package/template/.aioson/locales/fr/agents/orache.md +2 -0
  182. package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
  183. package/template/.aioson/locales/fr/agents/pair.md +2 -0
  184. package/template/.aioson/locales/fr/agents/pm.md +2 -0
  185. package/template/.aioson/locales/fr/agents/product.md +52 -0
  186. package/template/.aioson/locales/fr/agents/profiler-enricher.md +2 -0
  187. package/template/.aioson/locales/fr/agents/profiler-forge.md +2 -0
  188. package/template/.aioson/locales/fr/agents/profiler-researcher.md +2 -0
  189. package/template/.aioson/locales/fr/agents/qa.md +2 -0
  190. package/template/.aioson/locales/fr/agents/setup.md +2 -0
  191. package/template/.aioson/locales/fr/agents/sheldon.md +117 -0
  192. package/template/.aioson/locales/fr/agents/squad.md +16 -0
  193. package/template/.aioson/locales/fr/agents/tester.md +9 -0
  194. package/template/.aioson/locales/fr/agents/ux-ui.md +2 -0
  195. package/template/.aioson/locales/pt-BR/agents/analyst.md +64 -3
  196. package/template/.aioson/locales/pt-BR/agents/architect.md +42 -0
  197. package/template/.aioson/locales/pt-BR/agents/dev.md +147 -14
  198. package/template/.aioson/locales/pt-BR/agents/deyvin.md +47 -0
  199. package/template/.aioson/locales/pt-BR/agents/neo.md +62 -1
  200. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +158 -2
  201. package/template/.aioson/locales/pt-BR/agents/pm.md +95 -1
  202. package/template/.aioson/locales/pt-BR/agents/product.md +145 -18
  203. package/template/.aioson/locales/pt-BR/agents/qa.md +16 -0
  204. package/template/.aioson/locales/pt-BR/agents/setup.md +101 -18
  205. package/template/.aioson/locales/pt-BR/agents/sheldon.md +132 -1
  206. package/template/.aioson/locales/pt-BR/agents/squad.md +14 -0
  207. package/template/.aioson/locales/pt-BR/agents/tester.md +449 -0
  208. package/template/.aioson/rules/README.md +69 -0
  209. package/template/.aioson/rules/data-format-convention.md +136 -0
  210. package/template/.aioson/rules/example-monetary-values.md +30 -0
  211. package/template/.aioson/schemas/squad-manifest.schema.json +124 -3
  212. package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +2 -0
  213. package/template/.aioson/skills/design/pt.squarespace.com/.skill-meta.json +31 -0
  214. package/template/.aioson/skills/design/pt.squarespace.com/SKILL.md +66 -0
  215. package/template/.aioson/skills/design/pt.squarespace.com/references/components.md +368 -0
  216. package/template/.aioson/skills/design/pt.squarespace.com/references/design-tokens.md +150 -0
  217. package/template/.aioson/skills/design/pt.squarespace.com/references/motion.md +270 -0
  218. package/template/.aioson/skills/design/pt.squarespace.com/references/patterns.md +189 -0
  219. package/template/.aioson/skills/design/pt.squarespace.com/references/websites.md +165 -0
  220. package/template/.aioson/skills/marketing/references/anti-patterns.md +254 -0
  221. package/template/.aioson/skills/marketing/references/fascinations.md +192 -0
  222. package/template/.aioson/skills/marketing/references/five-acts.md +248 -0
  223. package/template/.aioson/skills/marketing/references/market-intelligence.md +198 -0
  224. package/template/.aioson/skills/marketing/references/offer-structure.md +203 -0
  225. package/template/.aioson/skills/marketing/references/one-belief.md +149 -0
  226. package/template/.aioson/skills/marketing/references/patterns.md +218 -0
  227. package/template/.aioson/skills/marketing/references/pms-research.md +193 -0
  228. package/template/.aioson/skills/marketing/vsl-craft.md +385 -0
  229. package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +1 -0
  230. package/template/.aioson/skills/process/aioson-spec-driven/references/analyst.md +30 -0
  231. package/template/.aioson/skills/process/aioson-spec-driven/references/architect.md +23 -0
  232. package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +47 -0
  233. package/template/.aioson/skills/process/aioson-spec-driven/references/deyvin.md +27 -0
  234. package/template/.aioson/skills/process/aioson-spec-driven/references/maintenance-and-state.md +35 -0
  235. package/template/.aioson/skills/process/aioson-spec-driven/references/product.md +25 -0
  236. package/template/.aioson/skills/process/aioson-spec-driven/references/qa.md +30 -0
  237. package/template/.aioson/skills/process/aioson-spec-driven/references/sheldon.md +25 -0
  238. package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +4 -1
  239. package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +15 -0
  240. package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +32 -0
  241. package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +20 -0
  242. package/template/.aioson/skills/process/simplify/SKILL.md +173 -0
  243. package/template/.aioson/skills/static/context-budget-guide.md +46 -0
  244. package/template/.aioson/skills/static/harness-sensors.md +74 -0
  245. package/template/.aioson/skills/static/landing-page-deploy.md +192 -0
  246. package/template/.aioson/skills/static/landing-page-forge.md +730 -0
  247. package/template/.aioson/skills/static/multi-agent-patterns.md +43 -0
  248. package/template/.aioson/skills/static/react-motion-patterns.md +22 -0
  249. package/template/.aioson/skills/static/static-html-patterns/checklists.md +43 -0
  250. package/template/.aioson/skills/static/static-html-patterns/css-tokens.md +609 -0
  251. package/template/.aioson/skills/static/static-html-patterns/motion.md +193 -0
  252. package/template/.aioson/skills/static/static-html-patterns/premium.md +711 -0
  253. package/template/.aioson/skills/static/static-html-patterns/structure.md +209 -0
  254. package/template/.aioson/skills/static/static-html-patterns/utilities.md +190 -0
  255. package/template/.aioson/skills/static/static-html-patterns.md +58 -1913
  256. package/template/.aioson/skills/static/threejs-patterns.md +929 -0
  257. package/template/.aioson/skills/static/ui-ux-modern.md +1 -0
  258. package/template/.aioson/skills/static/web-research-cache.md +112 -0
  259. package/template/.aioson/tasks/implementation-plan.md +21 -1
  260. package/template/.aioson/tasks/squad-create.md +22 -0
  261. package/template/.aioson/tasks/squad-design.md +30 -0
  262. package/template/.aioson/templates/squads/digital-marketing-agency/template.json +96 -0
  263. package/template/.claude/commands/aioson/agent/design-hybrid-forge.md +5 -0
  264. package/template/.claude/commands/aioson/agent/orache.md +5 -0
  265. package/template/.claude/commands/aioson/agent/sheldon.md +5 -0
  266. package/template/.claude/commands/aioson/agent/site-forge.md +5 -0
  267. package/template/AGENTS.md +55 -3
  268. package/template/CLAUDE.md +31 -0
  269. package/template/OPENCODE.md +4 -0
  270. package/template/researchs/.gitkeep +0 -0
  271. package/template/.aioson/skills/design-system/components/SKILL.md:Zone.Identifier +0 -0
  272. package/template/.aioson/skills/design-system/dashboards/SKILL.md:Zone.Identifier +0 -0
  273. package/template/.aioson/skills/design-system/foundations/SKILL.md:Zone.Identifier +0 -0
  274. package/template/.aioson/skills/design-system/motion/SKILL.md:Zone.Identifier +0 -0
  275. package/template/.aioson/skills/design-system/patterns/SKILL.md:Zone.Identifier +0 -0
@@ -0,0 +1,265 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Context Compactor — Plan 80, Script 5
5
+ *
6
+ * Standalone context compaction for any agent session (not just squads).
7
+ * Produces a last-handoff.json compatible with recovery-context.js.
8
+ *
9
+ * Usage:
10
+ * node context-compactor.js --agent=<agent> [--input=devlog.md] [--session=<id>]
11
+ *
12
+ * Reads: stdin or --input (devlog, notes, partial output)
13
+ * Produces: .aioson/context/last-handoff.json
14
+ */
15
+
16
+ const fs = require('node:fs/promises');
17
+ const path = require('node:path');
18
+ const { randomUUID } = require('node:crypto');
19
+
20
+ const CONTEXT_DIR = path.join('.aioson', 'context');
21
+
22
+ // ─── Content extractors ──────────────────────────────────────────────────────
23
+
24
+ /**
25
+ * Extract tool usage from session content.
26
+ * Looks for patterns like "Used X tool", "Called X", tool names in backticks.
27
+ */
28
+ function extractToolsUsed(content) {
29
+ const tools = new Set();
30
+ const patterns = [
31
+ /\b(Read|Write|Edit|Glob|Grep|Bash|Agent)\b/g,
32
+ /tool[:\s]+["`](\w+)["`]/gi,
33
+ /using\s+(\w+)\s+tool/gi
34
+ ];
35
+
36
+ for (const pattern of patterns) {
37
+ let match;
38
+ while ((match = pattern.exec(content)) !== null) {
39
+ tools.add(match[1]);
40
+ }
41
+ }
42
+
43
+ return [...tools];
44
+ }
45
+
46
+ /**
47
+ * Extract recent requests/instructions from content.
48
+ * Looks for imperative sentences, commands, task descriptions.
49
+ */
50
+ function extractRecentRequests(content) {
51
+ const requests = [];
52
+ const lines = content.split(/\r?\n/);
53
+
54
+ for (const line of lines) {
55
+ const trimmed = line.trim();
56
+ // Match task-like lines
57
+ if (/^[-*]\s*\[.\]/.test(trimmed)) {
58
+ requests.push(trimmed.replace(/^[-*]\s*\[.\]\s*/, ''));
59
+ }
60
+ // Match "Task:" or "Goal:" prefixed lines
61
+ if (/^(task|goal|objective|instruction):/i.test(trimmed)) {
62
+ requests.push(trimmed);
63
+ }
64
+ }
65
+
66
+ return requests.slice(-10);
67
+ }
68
+
69
+ /**
70
+ * Extract pending/incomplete work from content.
71
+ */
72
+ function extractPendingWork(content) {
73
+ const pending = [];
74
+ const lines = content.split(/\r?\n/);
75
+
76
+ for (const line of lines) {
77
+ const trimmed = line.trim();
78
+ // Unchecked checkboxes
79
+ if (/^[-*]\s*\[\s\]/.test(trimmed)) {
80
+ pending.push(trimmed.replace(/^[-*]\s*\[\s\]\s*/, ''));
81
+ }
82
+ // Lines with TODO/FIXME/HACK
83
+ if (/\b(TODO|FIXME|HACK|PENDING)\b/i.test(trimmed)) {
84
+ pending.push(trimmed);
85
+ }
86
+ }
87
+
88
+ return pending.slice(-10);
89
+ }
90
+
91
+ /**
92
+ * Extract key file references from content.
93
+ */
94
+ function extractKeyFiles(content) {
95
+ const files = new Set();
96
+ // Match file paths with extensions
97
+ const pattern = /(?:^|\s)([\w./\\-]+\.\w{1,8})\b/g;
98
+ let match;
99
+
100
+ while ((match = pattern.exec(content)) !== null) {
101
+ const candidate = match[1];
102
+ // Filter out obvious non-paths
103
+ if (candidate.includes('/') || candidate.includes('\\')) {
104
+ files.add(candidate);
105
+ }
106
+ }
107
+
108
+ return [...files].slice(0, 20);
109
+ }
110
+
111
+ /**
112
+ * Extract timeline events from content.
113
+ * Looks for timestamps, "Step N:", numbered lists with actions.
114
+ */
115
+ function extractTimeline(content) {
116
+ const events = [];
117
+ const lines = content.split(/\r?\n/);
118
+
119
+ for (const line of lines) {
120
+ const trimmed = line.trim();
121
+ // ISO timestamps
122
+ if (/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(trimmed)) {
123
+ events.push(trimmed.slice(0, 120));
124
+ }
125
+ // "Step N:" pattern
126
+ if (/^step\s+\d+/i.test(trimmed)) {
127
+ events.push(trimmed.slice(0, 120));
128
+ }
129
+ // Section headers that indicate progress
130
+ if (/^#+\s*(phase|step|stage|sprint|iteration)\b/i.test(trimmed)) {
131
+ events.push(trimmed.replace(/^#+\s*/, ''));
132
+ }
133
+ }
134
+
135
+ return events.slice(-15);
136
+ }
137
+
138
+ // ─── XML format ──────────────────────────────────────────────────────────────
139
+
140
+ /**
141
+ * Generate <summary> XML compatible with Claude Code internal format.
142
+ */
143
+ function toSummaryXml(summary, agent, sessionId) {
144
+ const lines = [
145
+ '<summary>',
146
+ `<agent>${agent}</agent>`,
147
+ `<session_id>${sessionId}</session_id>`,
148
+ '<tools_used>'
149
+ ];
150
+
151
+ for (const tool of summary.tools_used) {
152
+ lines.push(` <tool>${tool}</tool>`);
153
+ }
154
+ lines.push('</tools_used>');
155
+
156
+ if (summary.recent_requests.length > 0) {
157
+ lines.push('<recent_requests>');
158
+ for (const req of summary.recent_requests) {
159
+ lines.push(` <request>${escapeXml(req)}</request>`);
160
+ }
161
+ lines.push('</recent_requests>');
162
+ }
163
+
164
+ if (summary.pending_work.length > 0) {
165
+ lines.push('<pending_work>');
166
+ for (const item of summary.pending_work) {
167
+ lines.push(` <item>${escapeXml(item)}</item>`);
168
+ }
169
+ lines.push('</pending_work>');
170
+ }
171
+
172
+ if (summary.key_files.length > 0) {
173
+ lines.push('<key_files>');
174
+ for (const f of summary.key_files) {
175
+ lines.push(` <file>${escapeXml(f)}</file>`);
176
+ }
177
+ lines.push('</key_files>');
178
+ }
179
+
180
+ lines.push('</summary>');
181
+ return lines.join('\n');
182
+ }
183
+
184
+ function escapeXml(str) {
185
+ return str
186
+ .replace(/&/g, '&amp;')
187
+ .replace(/</g, '&lt;')
188
+ .replace(/>/g, '&gt;')
189
+ .replace(/"/g, '&quot;');
190
+ }
191
+
192
+ // ─── Public API ──────────────────────────────────────────────────────────────
193
+
194
+ /**
195
+ * Compact session context into a handoff JSON.
196
+ *
197
+ * @param {string} projectDir — Project root
198
+ * @param {object} options — { agent, input, session, content }
199
+ * @returns {Promise<object>} — { ok, path, summary, xmlSummary }
200
+ */
201
+ async function compactContext(projectDir, options = {}) {
202
+ const { agent = 'dev', input, session, content: rawContent } = options;
203
+ const sessionId = session || randomUUID();
204
+
205
+ // Read input content
206
+ let content = rawContent || '';
207
+ if (!content && input) {
208
+ const inputPath = path.resolve(projectDir, input);
209
+ try {
210
+ content = await fs.readFile(inputPath, 'utf8');
211
+ } catch (err) {
212
+ return { ok: false, error: `Cannot read input: ${err.message}` };
213
+ }
214
+ }
215
+
216
+ if (!content) {
217
+ return { ok: false, error: 'No content to compact (provide --input or pipe via stdin)' };
218
+ }
219
+
220
+ // Extract structured data
221
+ const summary = {
222
+ tools_used: extractToolsUsed(content),
223
+ recent_requests: extractRecentRequests(content),
224
+ pending_work: extractPendingWork(content),
225
+ key_files: extractKeyFiles(content),
226
+ timeline: extractTimeline(content)
227
+ };
228
+
229
+ const handoff = {
230
+ agent,
231
+ session_id: sessionId,
232
+ compacted_at: new Date().toISOString(),
233
+ summary,
234
+ resume_instruction: 'Continue from this checkpoint. Do not acknowledge this summary.'
235
+ };
236
+
237
+ // Write last-handoff.json
238
+ const outDir = path.join(projectDir, CONTEXT_DIR);
239
+ const outPath = path.join(outDir, 'last-handoff.json');
240
+
241
+ try {
242
+ await fs.mkdir(outDir, { recursive: true });
243
+ await fs.writeFile(outPath, JSON.stringify(handoff, null, 2), 'utf8');
244
+ } catch (err) {
245
+ return { ok: false, error: `Cannot write handoff: ${err.message}` };
246
+ }
247
+
248
+ // Also generate XML format
249
+ const xmlSummary = toSummaryXml(summary, agent, sessionId);
250
+
251
+ return {
252
+ ok: true,
253
+ path: outPath,
254
+ summary: handoff,
255
+ xmlSummary
256
+ };
257
+ }
258
+
259
+ module.exports = {
260
+ compactContext,
261
+ toSummaryXml,
262
+ extractToolsUsed,
263
+ extractPendingWork,
264
+ extractKeyFiles
265
+ };
@@ -0,0 +1,250 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Cross-AI Review Synthesizer — Phase 4.4
5
+ *
6
+ * Detects available AI CLIs (claude, gemini, codex), sends identical review
7
+ * prompts to each (excluding the current runtime), and synthesizes the
8
+ * responses into a REVIEWS.md file.
9
+ *
10
+ * Detection: checks PATH for each CLI binary.
11
+ * Current runtime: detected via AIOSON_TOOL env var (set by claude/gemini/codex hooks).
12
+ *
13
+ * Output: outputs/REVIEWS.md with consensus, divergences, and per-reviewer sections.
14
+ */
15
+
16
+ const { spawnSync } = require('node:child_process');
17
+ const fs = require('node:fs/promises');
18
+ const path = require('node:path');
19
+
20
+ // CLI invocation patterns for each supported tool
21
+ const CLI_RUNNERS = {
22
+ claude: {
23
+ bin: 'claude',
24
+ buildArgs: (prompt) => ['--print', '--model', 'claude-haiku-4-5-20251001', prompt],
25
+ parseOutput: (stdout) => stdout.trim()
26
+ },
27
+ gemini: {
28
+ bin: 'gemini',
29
+ buildArgs: (prompt) => ['-p', prompt],
30
+ parseOutput: (stdout) => stdout.trim()
31
+ },
32
+ codex: {
33
+ bin: 'codex',
34
+ buildArgs: (prompt) => ['-q', prompt],
35
+ parseOutput: (stdout) => stdout.trim()
36
+ }
37
+ };
38
+
39
+ /**
40
+ * Check if a binary is available in PATH.
41
+ */
42
+ function binaryExists(bin) {
43
+ const result = spawnSync('which', [bin], { encoding: 'utf8', timeout: 3000 });
44
+ return result.status === 0 && Boolean(result.stdout.trim());
45
+ }
46
+
47
+ /**
48
+ * Detect which AI CLIs are available in the current environment.
49
+ * Excludes the current runtime (detected from AIOSON_TOOL or process title).
50
+ */
51
+ function detectAvailableCLIs({ excludeCurrent = true } = {}) {
52
+ const currentTool = process.env.AIOSON_TOOL || '';
53
+ const available = [];
54
+
55
+ for (const [name, runner] of Object.entries(CLI_RUNNERS)) {
56
+ if (excludeCurrent && currentTool && currentTool.toLowerCase().includes(name)) continue;
57
+ if (binaryExists(runner.bin)) {
58
+ available.push(name);
59
+ }
60
+ }
61
+
62
+ return available;
63
+ }
64
+
65
+ /**
66
+ * Send a prompt to a specific CLI and return its response.
67
+ */
68
+ function queryCliReviewer(cliName, prompt, timeoutMs = 60_000) {
69
+ const runner = CLI_RUNNERS[cliName];
70
+ if (!runner) return { ok: false, error: `Unknown CLI: ${cliName}` };
71
+
72
+ try {
73
+ const result = spawnSync(runner.bin, runner.buildArgs(prompt), {
74
+ encoding: 'utf8',
75
+ timeout: timeoutMs,
76
+ stdio: 'pipe'
77
+ });
78
+
79
+ if (result.status !== 0) {
80
+ return {
81
+ ok: false,
82
+ cli: cliName,
83
+ error: (result.stderr || '').trim() || `exited with code ${result.status}`
84
+ };
85
+ }
86
+
87
+ const response = runner.parseOutput(result.stdout || '');
88
+ return { ok: true, cli: cliName, response };
89
+ } catch (err) {
90
+ return { ok: false, cli: cliName, error: err.message };
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Build a review prompt for a given output file.
96
+ */
97
+ function buildReviewPrompt(outputContent, reviewCriteria = []) {
98
+ const criteria = reviewCriteria.length > 0
99
+ ? reviewCriteria.map((c, i) => `${i + 1}. ${c}`).join('\n')
100
+ : '1. Is the content complete and accurate?\n2. Are there any logical errors or gaps?\n3. What could be improved?';
101
+
102
+ return `You are reviewing the following output. Be concise and direct.
103
+
104
+ ## Content to Review
105
+
106
+ \`\`\`
107
+ ${outputContent.slice(0, 4000)}${outputContent.length > 4000 ? '\n... (truncated)' : ''}
108
+ \`\`\`
109
+
110
+ ## Review Criteria
111
+
112
+ ${criteria}
113
+
114
+ ## Your Review
115
+
116
+ Provide your review in 3 sections:
117
+ - **Strengths**: what works well
118
+ - **Issues**: specific problems found (if any)
119
+ - **Recommendations**: concrete improvements (max 3)
120
+
121
+ Keep total response under 400 words.`;
122
+ }
123
+
124
+ /**
125
+ * Synthesize multiple reviews into a REVIEWS.md document.
126
+ */
127
+ function synthesizeReviews(reviews, outputPath, squadSlug) {
128
+ const ts = new Date().toISOString();
129
+ const successful = reviews.filter((r) => r.ok);
130
+ const failed = reviews.filter((r) => !r.ok);
131
+
132
+ const lines = [
133
+ `# Cross-AI Review`,
134
+ ``,
135
+ `**Squad:** ${squadSlug || 'unknown'} `,
136
+ `**Output:** ${outputPath || 'unknown'} `,
137
+ `**Generated:** ${ts} `,
138
+ `**Reviewers:** ${successful.map((r) => r.cli).join(', ') || 'none'}`,
139
+ ``
140
+ ];
141
+
142
+ if (successful.length === 0) {
143
+ lines.push('> No AI reviewers were available or all failed.');
144
+ if (failed.length > 0) {
145
+ lines.push('');
146
+ lines.push('**Failed reviewers:**');
147
+ for (const f of failed) {
148
+ lines.push(`- ${f.cli}: ${f.error}`);
149
+ }
150
+ }
151
+ return lines.join('\n');
152
+ }
153
+
154
+ // Per-reviewer sections
155
+ for (const review of successful) {
156
+ lines.push(`## ${review.cli.charAt(0).toUpperCase() + review.cli.slice(1)}'s Review`);
157
+ lines.push('');
158
+ lines.push(review.response);
159
+ lines.push('');
160
+ }
161
+
162
+ // Divergence note (heuristic: reviewers that mention different keywords)
163
+ if (successful.length > 1) {
164
+ const issueKeywords = ['problem', 'error', 'issue', 'missing', 'incorrect', 'wrong', 'fail'];
165
+ const reviewersWithIssues = successful.filter((r) =>
166
+ issueKeywords.some((kw) => r.response.toLowerCase().includes(kw))
167
+ );
168
+
169
+ lines.push('---');
170
+ lines.push('');
171
+ lines.push('## Synthesis');
172
+ lines.push('');
173
+
174
+ if (reviewersWithIssues.length === 0) {
175
+ lines.push('**Consensus:** All reviewers found no significant issues.');
176
+ } else if (reviewersWithIssues.length === successful.length) {
177
+ lines.push(`**Consensus:** All reviewers identified issues — address before shipping.`);
178
+ } else {
179
+ lines.push(`**Divergence:** ${reviewersWithIssues.map((r) => r.cli).join(', ')} found issues; others approved.`);
180
+ }
181
+ lines.push('');
182
+ }
183
+
184
+ if (failed.length > 0) {
185
+ lines.push('> **Note:** The following reviewers failed: ' + failed.map((f) => `${f.cli} (${f.error})`).join(', '));
186
+ lines.push('');
187
+ }
188
+
189
+ return lines.join('\n');
190
+ }
191
+
192
+ /**
193
+ * Run the full cross-AI review pipeline.
194
+ *
195
+ * @param {{ projectDir, outputFile, reviewCriteria?, squadSlug?, excludeCurrent?, synthesizeTo? }} opts
196
+ */
197
+ async function runCrossAIReview({
198
+ projectDir,
199
+ outputFile,
200
+ reviewCriteria = [],
201
+ squadSlug = '',
202
+ excludeCurrent = true,
203
+ synthesizeTo = null,
204
+ timeoutMs = 60_000
205
+ }) {
206
+ // Read output content
207
+ let outputContent;
208
+ try {
209
+ outputContent = await fs.readFile(outputFile, 'utf8');
210
+ } catch {
211
+ return { ok: false, error: `Output file not found: ${outputFile}` };
212
+ }
213
+
214
+ // Detect available CLIs
215
+ const clis = detectAvailableCLIs({ excludeCurrent });
216
+ if (clis.length === 0) {
217
+ return {
218
+ ok: false,
219
+ error: 'No AI CLIs detected. Install claude, gemini, or codex in PATH.',
220
+ detectedCLIs: []
221
+ };
222
+ }
223
+
224
+ // Build review prompt
225
+ const prompt = buildReviewPrompt(outputContent, reviewCriteria);
226
+
227
+ // Query each reviewer
228
+ const reviews = clis.map((cli) => queryCliReviewer(cli, prompt, timeoutMs));
229
+
230
+ // Synthesize
231
+ const markdown = synthesizeReviews(reviews, outputFile, squadSlug);
232
+
233
+ // Write REVIEWS.md
234
+ const reviewsPath = synthesizeTo ||
235
+ path.join(path.dirname(outputFile), 'REVIEWS.md');
236
+
237
+ await fs.mkdir(path.dirname(reviewsPath), { recursive: true });
238
+ await fs.writeFile(reviewsPath, markdown, 'utf8');
239
+
240
+ return {
241
+ ok: true,
242
+ reviewers: clis,
243
+ successCount: reviews.filter((r) => r.ok).length,
244
+ failCount: reviews.filter((r) => !r.ok).length,
245
+ reviewsPath,
246
+ reviews
247
+ };
248
+ }
249
+
250
+ module.exports = { runCrossAIReview, detectAvailableCLIs, synthesizeReviews, buildReviewPrompt };
@@ -0,0 +1,196 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Hooks Generator — Plan 81, Phase 1.2
5
+ *
6
+ * Generates Claude Code .claude/settings.json hooks for squad execution.
7
+ * Uses hook types: command, agent.
8
+ *
9
+ * Generated hooks:
10
+ * - Stop hook: quality gate agent that blocks incomplete work
11
+ * - TaskCompleted hook: learning extraction after each task
12
+ * - PostToolUse hook: mailbox-to-bus bridge (SendMessage interception)
13
+ *
14
+ * The hooks are session-scoped: written before squad:autorun starts,
15
+ * cleaned up after the session ends (or left for the next session).
16
+ */
17
+
18
+ const fs = require('node:fs/promises');
19
+ const path = require('node:path');
20
+
21
+ const CLAUDE_DIR = '.claude';
22
+ const SETTINGS_FILE = 'settings.json';
23
+ const AGENTS_DIR = path.join(CLAUDE_DIR, 'agents');
24
+
25
+ // ─── Quality Gate Agent Template ─────────────────────────────────────────────
26
+
27
+ function qualityGateAgentTemplate(squadSlug) {
28
+ return `# Squad Quality Gate — ${squadSlug}
29
+
30
+ You are a verification agent for the "${squadSlug}" squad. Before allowing a worker to finish:
31
+
32
+ 1. Read the worker's output files listed in the task brief
33
+ 2. Check must_haves from the task specification:
34
+ - **Tier 1 (Exists):** All required files exist on disk
35
+ - **Tier 2 (Substantive):** Files have meaningful content (no stubs, TODOs, or placeholders)
36
+ - **Tier 3 (Wired):** Files are referenced/imported where they should be
37
+ 3. If any tier fails: respond with BLOCK and specific feedback on what is missing
38
+ 4. If all pass: respond with ALLOW
39
+
40
+ ## Output Format
41
+
42
+ \`\`\`json
43
+ {
44
+ "decision": "allow|block",
45
+ "tier_results": {
46
+ "exists": true,
47
+ "substantive": true,
48
+ "wired": true
49
+ },
50
+ "reason": "All tiers passed — output meets acceptance criteria",
51
+ "issues": []
52
+ }
53
+ \`\`\`
54
+
55
+ ## Important
56
+
57
+ - You have NO context from the generating agent's conversation
58
+ - Judge ONLY the artifacts on disk vs the spec requirements
59
+ - Be strict on Tier 1 (exists) and Tier 2 (substantive)
60
+ - Be lenient on Tier 3 (wired) — warn but don't block for minor wiring issues
61
+ `;
62
+ }
63
+
64
+ // ─── Settings Generator ──────────────────────────────────────────────────────
65
+
66
+ /**
67
+ * Generate Claude Code hooks configuration for a squad session.
68
+ *
69
+ * @param {string} projectDir
70
+ * @param {string} squadSlug
71
+ * @param {object} [options] — { enableQualityGate, enableLearning, enableBusBridge }
72
+ * @returns {object} — hooks configuration object
73
+ */
74
+ function generateHooksConfig(squadSlug, options = {}) {
75
+ const {
76
+ enableQualityGate = true,
77
+ enableLearning = true,
78
+ enableBusBridge = false
79
+ } = options;
80
+
81
+ const hooks = {};
82
+
83
+ // Stop hook: quality gate (blocks workers from finishing with incomplete work)
84
+ if (enableQualityGate) {
85
+ hooks.Stop = [{
86
+ type: 'agent',
87
+ agent: `.claude/agents/squad-quality-gate-${squadSlug}.md`,
88
+ matcher: `agent:squad-worker-*`
89
+ }];
90
+ }
91
+
92
+ // TaskCompleted hook: automatic learning extraction
93
+ if (enableLearning) {
94
+ if (!hooks.TaskCompleted) hooks.TaskCompleted = [];
95
+ hooks.TaskCompleted.push({
96
+ type: 'command',
97
+ command: `node -e "require('./src/squad/learning-extractor').extractLearnings(process.cwd(), '${squadSlug}', process.env.SESSION_ID || 'unknown', { busMessages: [], taskResults: [], reflectionReports: [] }).catch(() => {})"`
98
+ });
99
+ }
100
+
101
+ // PostToolUse hook: mailbox-to-bus bridge (intercepts SendMessage)
102
+ if (enableBusBridge) {
103
+ if (!hooks.PostToolUse) hooks.PostToolUse = [];
104
+ hooks.PostToolUse.push({
105
+ type: 'command',
106
+ matcher: 'SendMessage',
107
+ command: `node -e "require('./src/squad/bus-bridge').bridgeMailboxToBus(process.cwd(), '${squadSlug}', process.env.SESSION_ID || 'unknown', process.env.TOOL_INPUT || '{}').catch(() => {})"`
108
+ });
109
+ }
110
+
111
+ return hooks;
112
+ }
113
+
114
+ /**
115
+ * Write hooks to .claude/settings.json (merge with existing).
116
+ *
117
+ * @param {string} projectDir
118
+ * @param {string} squadSlug
119
+ * @param {object} [options]
120
+ * @returns {Promise<object>} — { settingsPath, agentPath, hooks }
121
+ */
122
+ async function writeSquadHooks(projectDir, squadSlug, options = {}) {
123
+ const settingsDir = path.join(projectDir, CLAUDE_DIR);
124
+ const settingsPath = path.join(settingsDir, SETTINGS_FILE);
125
+ const agentsDir = path.join(projectDir, AGENTS_DIR);
126
+
127
+ // Ensure directories
128
+ await fs.mkdir(settingsDir, { recursive: true });
129
+ await fs.mkdir(agentsDir, { recursive: true });
130
+
131
+ // Read existing settings
132
+ let existing = {};
133
+ try {
134
+ existing = JSON.parse(await fs.readFile(settingsPath, 'utf8'));
135
+ } catch { /* no existing settings */ }
136
+
137
+ // Generate hooks
138
+ const hooks = generateHooksConfig(squadSlug, options);
139
+
140
+ // Merge hooks (squad hooks go into a namespaced section)
141
+ const merged = {
142
+ ...existing,
143
+ hooks: {
144
+ ...(existing.hooks || {}),
145
+ ...hooks
146
+ },
147
+ _squadHooksFor: squadSlug,
148
+ _squadHooksAt: new Date().toISOString()
149
+ };
150
+
151
+ await fs.writeFile(settingsPath, JSON.stringify(merged, null, 2), 'utf8');
152
+
153
+ // Write quality gate agent template
154
+ let agentPath = null;
155
+ if (options.enableQualityGate !== false) {
156
+ agentPath = path.join(agentsDir, `squad-quality-gate-${squadSlug}.md`);
157
+ await fs.writeFile(agentPath, qualityGateAgentTemplate(squadSlug), 'utf8');
158
+ }
159
+
160
+ return {
161
+ settingsPath,
162
+ agentPath,
163
+ hooks,
164
+ merged: Object.keys(hooks).length
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Remove squad-specific hooks from .claude/settings.json.
170
+ */
171
+ async function cleanupSquadHooks(projectDir, squadSlug) {
172
+ const settingsPath = path.join(projectDir, CLAUDE_DIR, SETTINGS_FILE);
173
+
174
+ try {
175
+ const existing = JSON.parse(await fs.readFile(settingsPath, 'utf8'));
176
+
177
+ if (existing._squadHooksFor === squadSlug) {
178
+ // Remove squad-specific hooks
179
+ delete existing.hooks;
180
+ delete existing._squadHooksFor;
181
+ delete existing._squadHooksAt;
182
+ await fs.writeFile(settingsPath, JSON.stringify(existing, null, 2), 'utf8');
183
+ }
184
+
185
+ // Remove quality gate agent
186
+ const agentPath = path.join(projectDir, AGENTS_DIR, `squad-quality-gate-${squadSlug}.md`);
187
+ await fs.unlink(agentPath).catch(() => {});
188
+ } catch { /* no settings to clean */ }
189
+ }
190
+
191
+ module.exports = {
192
+ generateHooksConfig,
193
+ writeSquadHooks,
194
+ cleanupSquadHooks,
195
+ qualityGateAgentTemplate
196
+ };