@jaimevalasek/aioson 1.5.1 → 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 (341) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +729 -226
  3. package/docs/design-previews/aurora-command-ui-website.html +884 -0
  4. package/docs/design-previews/aurora-command-ui.html +682 -0
  5. package/docs/design-previews/bold-editorial-ui-website.html +658 -0
  6. package/docs/design-previews/bold-editorial-ui.html +717 -0
  7. package/docs/design-previews/clean-saas-ui-website.html +1202 -0
  8. package/docs/design-previews/clean-saas-ui.html +549 -0
  9. package/docs/design-previews/cognitive-core-ui-website.html +1009 -0
  10. package/docs/design-previews/cognitive-core-ui.html +463 -0
  11. package/docs/design-previews/glassmorphism-ui-website.html +572 -0
  12. package/docs/design-previews/glassmorphism-ui.html +886 -0
  13. package/docs/design-previews/index.html +699 -0
  14. package/docs/design-previews/interface-design-website.html +1187 -0
  15. package/docs/design-previews/interface-design.html +513 -0
  16. package/docs/design-previews/neo-brutalist-ui-website.html +621 -0
  17. package/docs/design-previews/neo-brutalist-ui.html +797 -0
  18. package/docs/design-previews/premium-command-center-ui-website.html +1217 -0
  19. package/docs/design-previews/premium-command-center-ui.html +552 -0
  20. package/docs/design-previews/pt.squarespace.com-homepage.html +889 -0
  21. package/docs/design-previews/warm-craft-ui-website.html +684 -0
  22. package/docs/design-previews/warm-craft-ui.html +739 -0
  23. package/docs/en/cli-reference.md +20 -9
  24. package/docs/integrations/sdlc-genius-boundary.md +76 -0
  25. package/docs/integrations/sdlc-genius-eval-matrix.md +75 -0
  26. package/docs/integrations/sdlc-genius-install-checklist.md +93 -0
  27. package/docs/integrations/sdlc-genius-review-samples.md +86 -0
  28. package/docs/pt/README.md +10 -0
  29. package/docs/pt/agent-sharding.md +132 -0
  30. package/docs/pt/agentes.md +9 -2
  31. package/docs/pt/busca-de-contexto.md +129 -0
  32. package/docs/pt/cache-de-contexto.md +156 -0
  33. package/docs/pt/comandos-cli.md +915 -1
  34. package/docs/pt/design-hybrid-forge.md +356 -0
  35. package/docs/pt/devlog-pipeline.md +270 -0
  36. package/docs/pt/fluxo-artefatos.md +178 -0
  37. package/docs/pt/hooks-session-guard.md +454 -0
  38. package/docs/pt/inicio-rapido.md +54 -3
  39. package/docs/pt/inteligencia-adaptativa.md +324 -0
  40. package/docs/pt/monitor-de-contexto.md +158 -0
  41. package/docs/pt/recuperacao-de-sessao.md +125 -0
  42. package/docs/pt/sandbox.md +125 -0
  43. package/docs/pt/sdd-automation-scripts.md +557 -0
  44. package/docs/pt/site-forge.md +309 -0
  45. package/docs/pt/skills.md +98 -6
  46. package/docs/pt/spec-learnings-pipeline.md +265 -0
  47. package/package.json +1 -1
  48. package/src/a2a/client.js +165 -0
  49. package/src/a2a/server.js +223 -0
  50. package/src/agent-loader.js +280 -0
  51. package/src/cli.js +329 -1
  52. package/src/commands/agent-audit.js +397 -0
  53. package/src/commands/agent-export-skill.js +229 -0
  54. package/src/commands/agent-loader.js +85 -0
  55. package/src/commands/artifact-validate.js +189 -0
  56. package/src/commands/brief-gen.js +405 -0
  57. package/src/commands/brief-validate.js +65 -0
  58. package/src/commands/classify.js +256 -0
  59. package/src/commands/context-cache.js +90 -0
  60. package/src/commands/context-compact.js +49 -0
  61. package/src/commands/context-health.js +175 -0
  62. package/src/commands/context-monitor.js +163 -0
  63. package/src/commands/context-search.js +66 -0
  64. package/src/commands/context-trim.js +177 -0
  65. package/src/commands/design-hybrid-options.js +385 -0
  66. package/src/commands/detect-test-runner.js +55 -0
  67. package/src/commands/devlog-export-brains.js +27 -0
  68. package/src/commands/devlog-process.js +292 -0
  69. package/src/commands/devlog-watch.js +131 -0
  70. package/src/commands/feature-close.js +165 -0
  71. package/src/commands/gate-check.js +228 -0
  72. package/src/commands/health.js +214 -0
  73. package/src/commands/hooks-emit.js +253 -0
  74. package/src/commands/hooks-install.js +347 -0
  75. package/src/commands/init.js +54 -13
  76. package/src/commands/install.js +52 -13
  77. package/src/commands/learning-auto-promote.js +195 -0
  78. package/src/commands/learning-evolve.js +364 -0
  79. package/src/commands/learning-export.js +103 -0
  80. package/src/commands/learning-rollback.js +164 -0
  81. package/src/commands/live.js +59 -1
  82. package/src/commands/pattern-detect.js +33 -0
  83. package/src/commands/preflight-context.js +30 -0
  84. package/src/commands/preflight.js +208 -0
  85. package/src/commands/pulse-update.js +130 -0
  86. package/src/commands/recovery.js +43 -0
  87. package/src/commands/runner-daemon.js +274 -0
  88. package/src/commands/runner-plan.js +70 -0
  89. package/src/commands/runner-queue-from-plan.js +166 -0
  90. package/src/commands/runner-queue.js +189 -0
  91. package/src/commands/runner-run.js +129 -0
  92. package/src/commands/runtime.js +47 -1
  93. package/src/commands/sandbox.js +37 -0
  94. package/src/commands/self-implement-loop.js +256 -0
  95. package/src/commands/session-guard.js +218 -0
  96. package/src/commands/setup-context.js +22 -2
  97. package/src/commands/setup.js +178 -0
  98. package/src/commands/sizing.js +165 -0
  99. package/src/commands/skill.js +144 -32
  100. package/src/commands/spec-checkpoint.js +177 -0
  101. package/src/commands/spec-status.js +79 -0
  102. package/src/commands/spec-sync.js +190 -0
  103. package/src/commands/spec-tasks.js +288 -0
  104. package/src/commands/squad-autorun.js +1220 -0
  105. package/src/commands/squad-bus.js +217 -0
  106. package/src/commands/squad-card.js +149 -0
  107. package/src/commands/squad-daemon.js +134 -0
  108. package/src/commands/squad-dependency-graph.js +164 -0
  109. package/src/commands/squad-review.js +106 -0
  110. package/src/commands/squad-scaffold.js +55 -0
  111. package/src/commands/squad-tool-register.js +157 -0
  112. package/src/commands/state-save.js +122 -0
  113. package/src/commands/tool-registry-cmd.js +232 -0
  114. package/src/commands/update.js +9 -0
  115. package/src/commands/verify-gate.js +572 -0
  116. package/src/commands/workflow-execute.js +241 -0
  117. package/src/constants.js +18 -0
  118. package/src/context-cache.js +159 -0
  119. package/src/context-search.js +326 -0
  120. package/src/design-variation-catalog.js +503 -0
  121. package/src/i18n/messages/en.js +32 -2
  122. package/src/i18n/messages/es.js +30 -2
  123. package/src/i18n/messages/fr.js +30 -2
  124. package/src/i18n/messages/pt-BR.js +32 -2
  125. package/src/install-animation.js +260 -0
  126. package/src/install-profile.js +143 -0
  127. package/src/install-wizard.js +475 -0
  128. package/src/installer.js +44 -10
  129. package/src/lib/health-check.js +158 -0
  130. package/src/lib/hook-protocol.js +76 -0
  131. package/src/mcp/apps/squad-dashboard/app.js +163 -0
  132. package/src/mcp/apps/squad-dashboard/index.html +261 -0
  133. package/src/mcp/apps/squad-dashboard/mcp-manifest.json +23 -0
  134. package/src/mcp/resources/squad-state.js +130 -0
  135. package/src/parser.js +7 -1
  136. package/src/preflight-engine.js +443 -0
  137. package/src/recovery-context-session.js +154 -0
  138. package/src/runner/cascade.js +97 -0
  139. package/src/runner/cli-launcher.js +109 -0
  140. package/src/runner/plan-importer.js +63 -0
  141. package/src/runner/queue-store.js +159 -0
  142. package/src/runtime-store.js +158 -4
  143. package/src/sandbox.js +177 -0
  144. package/src/squad/agent-teams-adapter.js +264 -0
  145. package/src/squad/brief-validator.js +350 -0
  146. package/src/squad/bus-bridge.js +140 -0
  147. package/src/squad/context-compactor.js +265 -0
  148. package/src/squad/cross-ai-synthesizer.js +250 -0
  149. package/src/squad/hooks-generator.js +196 -0
  150. package/src/squad/inter-squad-events.js +175 -0
  151. package/src/squad/intra-bus.js +345 -0
  152. package/src/squad/learning-extractor.js +213 -0
  153. package/src/squad/pattern-detector.js +365 -0
  154. package/src/squad/preflight-context.js +296 -0
  155. package/src/squad/recovery-context.js +242 -71
  156. package/src/squad/reflection.js +365 -0
  157. package/src/squad/squad-scaffold.js +177 -0
  158. package/src/squad/state-manager.js +310 -0
  159. package/src/squad/task-decomposer.js +652 -0
  160. package/src/squad/verify-gate.js +303 -0
  161. package/src/tool-executor.js +94 -0
  162. package/src/updater.js +10 -3
  163. package/src/worker-runner.js +186 -1
  164. package/template/.aioson/agents/analyst.md +119 -3
  165. package/template/.aioson/agents/architect.md +98 -0
  166. package/template/.aioson/agents/design-hybrid-forge.md +141 -0
  167. package/template/.aioson/agents/dev.md +335 -14
  168. package/template/.aioson/agents/deyvin.md +117 -2
  169. package/template/.aioson/agents/discovery-design-doc.md +44 -0
  170. package/template/.aioson/agents/genome.md +14 -0
  171. package/template/.aioson/agents/neo.md +78 -1
  172. package/template/.aioson/agents/orache.md +50 -4
  173. package/template/.aioson/agents/orchestrator.md +197 -1
  174. package/template/.aioson/agents/pm.md +93 -0
  175. package/template/.aioson/agents/product.md +77 -4
  176. package/template/.aioson/agents/profiler-enricher.md +14 -0
  177. package/template/.aioson/agents/profiler-forge.md +14 -0
  178. package/template/.aioson/agents/profiler-researcher.md +14 -0
  179. package/template/.aioson/agents/qa.md +249 -19
  180. package/template/.aioson/agents/setup.md +144 -12
  181. package/template/.aioson/agents/sheldon.md +237 -11
  182. package/template/.aioson/agents/site-forge.md +1753 -0
  183. package/template/.aioson/agents/squad.md +162 -0
  184. package/template/.aioson/agents/tester.md +209 -0
  185. package/template/.aioson/agents/ux-ui.md +34 -1
  186. package/template/.aioson/brains/README.md +128 -0
  187. package/template/.aioson/brains/_index.json +16 -0
  188. package/template/.aioson/brains/scripts/query.js +103 -0
  189. package/template/.aioson/brains/site-forge/visual-patterns.brain.json +205 -0
  190. package/template/.aioson/config.md +158 -13
  191. package/template/.aioson/constitution.md +33 -0
  192. package/template/.aioson/context/forensics/.gitkeep +0 -0
  193. package/template/.aioson/context/project-pulse.md +34 -0
  194. package/template/.aioson/context/seeds/seed-example.md +27 -0
  195. package/template/.aioson/context/user-profile.md +42 -0
  196. package/template/.aioson/docs/LAYERS.md +79 -0
  197. package/template/.aioson/docs/README.md +76 -0
  198. package/template/.aioson/docs/example-external-api-context.md +72 -0
  199. package/template/.aioson/locales/en/agents/architect.md +17 -0
  200. package/template/.aioson/locales/en/agents/dev.md +79 -13
  201. package/template/.aioson/locales/en/agents/orache.md +6 -0
  202. package/template/.aioson/locales/en/agents/orchestrator.md +24 -0
  203. package/template/.aioson/locales/en/agents/product.md +50 -0
  204. package/template/.aioson/locales/en/agents/setup.md +33 -1
  205. package/template/.aioson/locales/en/agents/sheldon.md +115 -0
  206. package/template/.aioson/locales/en/agents/squad.md +14 -0
  207. package/template/.aioson/locales/en/agents/tester.md +6 -0
  208. package/template/.aioson/locales/es/agents/analyst.md +2 -0
  209. package/template/.aioson/locales/es/agents/architect.md +19 -0
  210. package/template/.aioson/locales/es/agents/dev.md +64 -4
  211. package/template/.aioson/locales/es/agents/deyvin.md +2 -0
  212. package/template/.aioson/locales/es/agents/discovery-design-doc.md +2 -0
  213. package/template/.aioson/locales/es/agents/genome.md +2 -0
  214. package/template/.aioson/locales/es/agents/neo.md +2 -0
  215. package/template/.aioson/locales/es/agents/orache.md +2 -0
  216. package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
  217. package/template/.aioson/locales/es/agents/pair.md +2 -0
  218. package/template/.aioson/locales/es/agents/pm.md +2 -0
  219. package/template/.aioson/locales/es/agents/product.md +52 -0
  220. package/template/.aioson/locales/es/agents/profiler-enricher.md +2 -0
  221. package/template/.aioson/locales/es/agents/profiler-forge.md +2 -0
  222. package/template/.aioson/locales/es/agents/profiler-researcher.md +2 -0
  223. package/template/.aioson/locales/es/agents/qa.md +2 -0
  224. package/template/.aioson/locales/es/agents/setup.md +35 -1
  225. package/template/.aioson/locales/es/agents/sheldon.md +117 -0
  226. package/template/.aioson/locales/es/agents/squad.md +16 -0
  227. package/template/.aioson/locales/es/agents/tester.md +9 -0
  228. package/template/.aioson/locales/es/agents/ux-ui.md +2 -0
  229. package/template/.aioson/locales/fr/agents/analyst.md +2 -0
  230. package/template/.aioson/locales/fr/agents/architect.md +19 -0
  231. package/template/.aioson/locales/fr/agents/dev.md +64 -4
  232. package/template/.aioson/locales/fr/agents/deyvin.md +2 -0
  233. package/template/.aioson/locales/fr/agents/discovery-design-doc.md +2 -0
  234. package/template/.aioson/locales/fr/agents/genome.md +2 -0
  235. package/template/.aioson/locales/fr/agents/neo.md +2 -0
  236. package/template/.aioson/locales/fr/agents/orache.md +2 -0
  237. package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
  238. package/template/.aioson/locales/fr/agents/pair.md +2 -0
  239. package/template/.aioson/locales/fr/agents/pm.md +2 -0
  240. package/template/.aioson/locales/fr/agents/product.md +52 -0
  241. package/template/.aioson/locales/fr/agents/profiler-enricher.md +2 -0
  242. package/template/.aioson/locales/fr/agents/profiler-forge.md +2 -0
  243. package/template/.aioson/locales/fr/agents/profiler-researcher.md +2 -0
  244. package/template/.aioson/locales/fr/agents/qa.md +2 -0
  245. package/template/.aioson/locales/fr/agents/setup.md +35 -1
  246. package/template/.aioson/locales/fr/agents/sheldon.md +117 -0
  247. package/template/.aioson/locales/fr/agents/squad.md +16 -0
  248. package/template/.aioson/locales/fr/agents/tester.md +9 -0
  249. package/template/.aioson/locales/fr/agents/ux-ui.md +2 -0
  250. package/template/.aioson/locales/pt-BR/agents/analyst.md +64 -3
  251. package/template/.aioson/locales/pt-BR/agents/architect.md +42 -0
  252. package/template/.aioson/locales/pt-BR/agents/dev.md +147 -14
  253. package/template/.aioson/locales/pt-BR/agents/deyvin.md +47 -0
  254. package/template/.aioson/locales/pt-BR/agents/neo.md +62 -1
  255. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +158 -2
  256. package/template/.aioson/locales/pt-BR/agents/pm.md +95 -1
  257. package/template/.aioson/locales/pt-BR/agents/product.md +145 -18
  258. package/template/.aioson/locales/pt-BR/agents/qa.md +16 -0
  259. package/template/.aioson/locales/pt-BR/agents/setup.md +134 -19
  260. package/template/.aioson/locales/pt-BR/agents/sheldon.md +132 -1
  261. package/template/.aioson/locales/pt-BR/agents/squad.md +14 -0
  262. package/template/.aioson/locales/pt-BR/agents/tester.md +449 -0
  263. package/template/.aioson/rules/README.md +69 -0
  264. package/template/.aioson/rules/data-format-convention.md +136 -0
  265. package/template/.aioson/rules/example-monetary-values.md +30 -0
  266. package/template/.aioson/schemas/squad-manifest.schema.json +124 -3
  267. package/template/.aioson/skills/design/aurora-command-ui/SKILL.md +243 -0
  268. package/template/.aioson/skills/design/aurora-command-ui/references/art-direction.md +293 -0
  269. package/template/.aioson/skills/design/aurora-command-ui/references/components.md +827 -0
  270. package/template/.aioson/skills/design/aurora-command-ui/references/dashboards.md +250 -0
  271. package/template/.aioson/skills/design/aurora-command-ui/references/design-tokens.md +585 -0
  272. package/template/.aioson/skills/design/aurora-command-ui/references/motion.md +365 -0
  273. package/template/.aioson/skills/design/aurora-command-ui/references/patterns.md +482 -0
  274. package/template/.aioson/skills/design/aurora-command-ui/references/websites.md +387 -0
  275. package/template/.aioson/skills/design/glassmorphism-ui/SKILL.md +222 -0
  276. package/template/.aioson/skills/design/glassmorphism-ui/references/art-direction.md +159 -0
  277. package/template/.aioson/skills/design/glassmorphism-ui/references/components.md +498 -0
  278. package/template/.aioson/skills/design/glassmorphism-ui/references/dashboards.md +236 -0
  279. package/template/.aioson/skills/design/glassmorphism-ui/references/design-tokens.md +274 -0
  280. package/template/.aioson/skills/design/glassmorphism-ui/references/motion.md +355 -0
  281. package/template/.aioson/skills/design/glassmorphism-ui/references/patterns.md +198 -0
  282. package/template/.aioson/skills/design/glassmorphism-ui/references/websites.md +307 -0
  283. package/template/.aioson/skills/design/neo-brutalist-ui/SKILL.md +213 -0
  284. package/template/.aioson/skills/design/neo-brutalist-ui/references/art-direction.md +228 -0
  285. package/template/.aioson/skills/design/neo-brutalist-ui/references/components.md +855 -0
  286. package/template/.aioson/skills/design/neo-brutalist-ui/references/dashboards.md +334 -0
  287. package/template/.aioson/skills/design/neo-brutalist-ui/references/design-tokens.md +342 -0
  288. package/template/.aioson/skills/design/neo-brutalist-ui/references/motion.md +286 -0
  289. package/template/.aioson/skills/design/neo-brutalist-ui/references/patterns.md +458 -0
  290. package/template/.aioson/skills/design/neo-brutalist-ui/references/websites.md +723 -0
  291. package/template/.aioson/skills/design/pt.squarespace.com/.skill-meta.json +31 -0
  292. package/template/.aioson/skills/design/pt.squarespace.com/SKILL.md +66 -0
  293. package/template/.aioson/skills/design/pt.squarespace.com/references/components.md +368 -0
  294. package/template/.aioson/skills/design/pt.squarespace.com/references/design-tokens.md +150 -0
  295. package/template/.aioson/skills/design/pt.squarespace.com/references/motion.md +270 -0
  296. package/template/.aioson/skills/design/pt.squarespace.com/references/patterns.md +189 -0
  297. package/template/.aioson/skills/design/pt.squarespace.com/references/websites.md +165 -0
  298. package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +46 -0
  299. package/template/.aioson/skills/process/aioson-spec-driven/references/analyst.md +30 -0
  300. package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +109 -0
  301. package/template/.aioson/skills/process/aioson-spec-driven/references/architect.md +23 -0
  302. package/template/.aioson/skills/process/aioson-spec-driven/references/artifact-map.md +44 -0
  303. package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +37 -0
  304. package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +47 -0
  305. package/template/.aioson/skills/process/aioson-spec-driven/references/deyvin.md +27 -0
  306. package/template/.aioson/skills/process/aioson-spec-driven/references/hardening-lane.md +49 -0
  307. package/template/.aioson/skills/process/aioson-spec-driven/references/maintenance-and-state.md +101 -0
  308. package/template/.aioson/skills/process/aioson-spec-driven/references/product.md +25 -0
  309. package/template/.aioson/skills/process/aioson-spec-driven/references/qa.md +30 -0
  310. package/template/.aioson/skills/process/aioson-spec-driven/references/sheldon.md +25 -0
  311. package/template/.aioson/skills/process/aioson-spec-driven/references/ui-language.md +75 -0
  312. package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +147 -0
  313. package/template/.aioson/skills/process/design-hybrid-forge/references/crossover-protocol.md +221 -0
  314. package/template/.aioson/skills/process/design-hybrid-forge/references/naming-registry.md +88 -0
  315. package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +306 -0
  316. package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +149 -0
  317. package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +208 -0
  318. package/template/.aioson/skills/process/design-hybrid-forge/references/variation-library.md +125 -0
  319. package/template/.aioson/skills/process/simplify/SKILL.md +173 -0
  320. package/template/.aioson/skills/static/context-budget-guide.md +46 -0
  321. package/template/.aioson/skills/static/harness-sensors.md +74 -0
  322. package/template/.aioson/skills/static/multi-agent-patterns.md +43 -0
  323. package/template/.aioson/skills/static/react-motion-patterns.md +22 -0
  324. package/template/.aioson/skills/static/static-html-patterns/checklists.md +43 -0
  325. package/template/.aioson/skills/static/static-html-patterns/css-tokens.md +609 -0
  326. package/template/.aioson/skills/static/static-html-patterns/motion.md +193 -0
  327. package/template/.aioson/skills/static/static-html-patterns/premium.md +711 -0
  328. package/template/.aioson/skills/static/static-html-patterns/structure.md +209 -0
  329. package/template/.aioson/skills/static/static-html-patterns/utilities.md +190 -0
  330. package/template/.aioson/skills/static/static-html-patterns.md +58 -1913
  331. package/template/.aioson/skills/static/threejs-patterns.md +929 -0
  332. package/template/.aioson/skills/static/web-research-cache.md +112 -0
  333. package/template/.aioson/tasks/implementation-plan.md +21 -1
  334. package/template/.claude/commands/aioson/agent/design-hybrid-forge.md +5 -0
  335. package/template/.claude/commands/aioson/agent/orache.md +5 -0
  336. package/template/.claude/commands/aioson/agent/sheldon.md +5 -0
  337. package/template/.claude/commands/aioson/agent/site-forge.md +5 -0
  338. package/template/AGENTS.md +75 -1
  339. package/template/CLAUDE.md +31 -0
  340. package/template/OPENCODE.md +4 -0
  341. package/template/researchs/.gitkeep +0 -0
@@ -0,0 +1,228 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * aioson gate:check — check if a phase gate is approved for a feature.
5
+ *
6
+ * Reads spec-{slug}.md frontmatter and artifact chain to verify gate status.
7
+ * Returns PASS or BLOCKED with evidence. No LLM calls.
8
+ *
9
+ * Usage:
10
+ * aioson gate:check . --feature=checkout --gate=C
11
+ * aioson gate:check . --feature=checkout --gate=D
12
+ * aioson gate:check . --feature=checkout --gate=C --json
13
+ */
14
+
15
+ const path = require('node:path');
16
+ const {
17
+ contextDir,
18
+ readFileSafe,
19
+ fileExists,
20
+ parseGatesFromSpec,
21
+ parseFrontmatter,
22
+ GATE_NAMES,
23
+ GATE_ALIASES
24
+ } = require('../preflight-engine');
25
+
26
+ const BAR = '━'.repeat(35);
27
+
28
+ const GATE_PREREQUISITES = {
29
+ A: [],
30
+ B: ['A'],
31
+ C: ['A', 'B'],
32
+ D: ['A', 'B', 'C']
33
+ };
34
+
35
+ const GATE_REQUIRED_ARTIFACTS = {
36
+ A: (slug) => [`requirements-${slug}.md`],
37
+ B: (slug) => ['architecture.md'],
38
+ C: (slug) => [`implementation-plan-${slug}.md`],
39
+ D: (slug) => [] // Gate D validated by QA sign-off in spec
40
+ };
41
+
42
+ const GATE_DESCRIPTIONS = {
43
+ A: 'requirements',
44
+ B: 'design',
45
+ C: 'plan',
46
+ D: 'execution'
47
+ };
48
+
49
+ async function checkGate(targetDir, slug, gateLetter) {
50
+ const dir = contextDir(targetDir);
51
+ const specFile = path.join(dir, `spec-${slug}.md`);
52
+ const specContent = await readFileSafe(specFile);
53
+ const gates = specContent ? parseGatesFromSpec(specContent) : {};
54
+ const fm = specContent ? parseFrontmatter(specContent) : {};
55
+
56
+ const gateName = GATE_NAMES[gateLetter];
57
+ const gateStatus = gates[gateName] || 'pending';
58
+ const prerequisites = GATE_PREREQUISITES[gateLetter] || [];
59
+
60
+ const evidence = [];
61
+ const missing = [];
62
+
63
+ // Check prerequisites
64
+ for (const prereq of prerequisites) {
65
+ const prereqName = GATE_NAMES[prereq];
66
+ const prereqStatus = gates[prereqName] || 'pending';
67
+ if (prereqStatus === 'approved') {
68
+ evidence.push({ type: 'prereq', gate: prereq, name: prereqName, status: 'approved', ok: true });
69
+ } else {
70
+ evidence.push({ type: 'prereq', gate: prereq, name: prereqName, status: prereqStatus, ok: false });
71
+ missing.push(`Gate ${prereq} (${prereqName}) not approved: ${prereqStatus}`);
72
+ }
73
+ }
74
+
75
+ // Check required artifacts
76
+ const requiredFiles = GATE_REQUIRED_ARTIFACTS[gateLetter](slug);
77
+ for (const fileName of requiredFiles) {
78
+ const filePath = path.join(dir, fileName);
79
+ const exists = await fileExists(filePath);
80
+ if (exists) {
81
+ let detail = null;
82
+ const content = await readFileSafe(filePath);
83
+ if (content) {
84
+ const fileFm = parseFrontmatter(content);
85
+ if (fileFm.status) detail = `status: ${fileFm.status}`;
86
+ }
87
+ evidence.push({ type: 'artifact', file: fileName, exists: true, detail, ok: true });
88
+ } else {
89
+ evidence.push({ type: 'artifact', file: fileName, exists: false, ok: false });
90
+ missing.push(`${fileName} not found`);
91
+ }
92
+ }
93
+
94
+ // Gate D: check for QA sign-off in spec
95
+ if (gateLetter === 'D') {
96
+ if (specContent && specContent.includes('## QA Sign-off')) {
97
+ // Check verdict
98
+ const passMatch = specContent.match(/\*\*Verdict:\*\*\s*(PASS|FAIL)/i);
99
+ const passVerdict = passMatch ? passMatch[1].toUpperCase() : null;
100
+ if (passVerdict === 'PASS') {
101
+ evidence.push({ type: 'qa_signoff', verdict: 'PASS', ok: true });
102
+ } else if (passVerdict === 'FAIL') {
103
+ evidence.push({ type: 'qa_signoff', verdict: 'FAIL', ok: false });
104
+ missing.push('QA sign-off verdict: FAIL');
105
+ } else {
106
+ evidence.push({ type: 'qa_signoff', verdict: null, ok: false });
107
+ missing.push('QA sign-off found but verdict unclear');
108
+ }
109
+ } else {
110
+ // Check spec last_checkpoint for completion indicators
111
+ const checkpoint = fm.last_checkpoint || '';
112
+ if (checkpoint.toLowerCase().includes('complet') || checkpoint.toLowerCase().includes('done')) {
113
+ evidence.push({ type: 'checkpoint', value: checkpoint, ok: true });
114
+ } else {
115
+ evidence.push({ type: 'qa_signoff', exists: false, ok: false });
116
+ missing.push('No QA sign-off in spec file');
117
+ }
118
+ }
119
+
120
+ // Also check spec version for explicit gate_execution
121
+ if (gates.execution && gates.execution !== 'pending') {
122
+ const gateD = gates.execution;
123
+ evidence.push({ type: 'gate_field', field: 'gate_execution', value: gateD, ok: gateD === 'approved' });
124
+ if (gateD !== 'approved') missing.push(`gate_execution: ${gateD}`);
125
+ }
126
+ }
127
+
128
+ const allOk = missing.length === 0;
129
+ const result = allOk ? 'PASS' : 'BLOCKED';
130
+
131
+ let recommendation = '';
132
+ if (result === 'PASS') {
133
+ const nextAgents = { A: '@architect', B: '@dev or @pm', C: '@dev', D: 'feature complete' };
134
+ recommendation = `${nextAgents[gateLetter] || 'proceed'} can proceed`;
135
+ } else {
136
+ const fixAgents = { A: 'complete requirements (@analyst)', B: 'complete design (@architect)', C: 'approve implementation plan', D: 'complete implementation and run @qa' };
137
+ recommendation = `BLOCKED — ${fixAgents[gateLetter] || 'resolve missing items'} first`;
138
+ }
139
+
140
+ return {
141
+ gate: gateLetter,
142
+ gate_name: gateName,
143
+ feature: slug,
144
+ status: gateStatus,
145
+ result,
146
+ evidence,
147
+ missing,
148
+ recommendation
149
+ };
150
+ }
151
+
152
+ async function runGateCheck({ args, options = {}, logger }) {
153
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
154
+ const slug = options.feature ? String(options.feature) : null;
155
+ let gateLetter = options.gate ? String(options.gate).toUpperCase() : null;
156
+
157
+ if (!slug) {
158
+ if (options.json) return { ok: false, reason: 'missing_feature' };
159
+ logger.log('--feature=<slug> is required.');
160
+ return { ok: false };
161
+ }
162
+
163
+ if (!gateLetter) {
164
+ if (options.json) return { ok: false, reason: 'missing_gate' };
165
+ logger.log('--gate=<A|B|C|D> is required.');
166
+ return { ok: false };
167
+ }
168
+
169
+ // Allow gate name aliases (requirements → A, etc.)
170
+ if (GATE_ALIASES[gateLetter.toLowerCase()]) {
171
+ gateLetter = GATE_ALIASES[gateLetter.toLowerCase()];
172
+ }
173
+
174
+ if (!GATE_NAMES[gateLetter]) {
175
+ if (options.json) return { ok: false, reason: 'invalid_gate', gate: gateLetter };
176
+ logger.log(`Invalid gate: ${gateLetter}. Use A, B, C, or D.`);
177
+ return { ok: false };
178
+ }
179
+
180
+ const check = await checkGate(targetDir, slug, gateLetter);
181
+
182
+ const result = { ok: check.result === 'PASS', ...check };
183
+
184
+ if (options.json) return result;
185
+
186
+ logger.log('');
187
+ logger.log(`Gate ${gateLetter} (${check.gate_name}) — ${slug}`);
188
+ logger.log(BAR);
189
+ logger.log(`Status: ${check.status}`);
190
+
191
+ const prereqs = check.evidence.filter((e) => e.type === 'prereq');
192
+ if (prereqs.length > 0) {
193
+ logger.log('Prerequisites met:');
194
+ for (const p of prereqs) {
195
+ const icon = p.ok ? ' ✓' : ' ✗';
196
+ logger.log(`${icon} Gate ${p.gate} (${p.name}): ${p.status}`);
197
+ }
198
+ }
199
+
200
+ const artifacts = check.evidence.filter((e) => e.type === 'artifact');
201
+ if (artifacts.length > 0) {
202
+ logger.log('Artifacts:');
203
+ for (const a of artifacts) {
204
+ const icon = a.ok ? ' ✓' : ' ✗';
205
+ const detail = a.detail ? ` (${a.detail})` : '';
206
+ logger.log(`${icon} ${a.file}${a.ok ? ' exists' : ' missing'}${detail}`);
207
+ }
208
+ }
209
+
210
+ const qaEvidence = check.evidence.filter((e) => e.type === 'qa_signoff' || e.type === 'checkpoint' || e.type === 'gate_field');
211
+ if (qaEvidence.length > 0) {
212
+ for (const q of qaEvidence) {
213
+ const icon = q.ok ? ' ✓' : ' ✗';
214
+ if (q.type === 'qa_signoff') logger.log(`${icon} QA sign-off: ${q.exists === false ? 'missing' : `verdict ${q.verdict || 'unclear'}`}`);
215
+ if (q.type === 'checkpoint') logger.log(` ✓ last_checkpoint: "${q.value}"`);
216
+ if (q.type === 'gate_field') logger.log(`${icon} gate_execution: ${q.value}`);
217
+ }
218
+ }
219
+
220
+ logger.log('');
221
+ const resultIcon = check.result === 'PASS' ? '✓' : '✗';
222
+ logger.log(`Result: ${resultIcon} ${check.result} — ${check.recommendation}`);
223
+ logger.log('');
224
+
225
+ return result;
226
+ }
227
+
228
+ module.exports = { runGateCheck };
@@ -0,0 +1,214 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const fs = require('node:fs/promises');
5
+ const { openRuntimeDb, listSquadLearnings, listProjectLearnings, listDynamicTools } = require('../runtime-store');
6
+
7
+ const MIN_FREQUENCY = 2;
8
+ const INACTIVITY_DAYS = 14;
9
+ const EVOLUTION_DIR = path.join('.aioson', 'evolution');
10
+
11
+ /**
12
+ * Verifica se há tools com handlers inválidos.
13
+ */
14
+ async function checkBrokenTools(tools, projectDir) {
15
+ const broken = [];
16
+ for (const tool of tools) {
17
+ if (tool.handler_type === 'script' && tool.handler_path) {
18
+ const absPath = path.isAbsolute(tool.handler_path)
19
+ ? tool.handler_path
20
+ : path.resolve(projectDir, tool.handler_path);
21
+ try {
22
+ await fs.access(absPath);
23
+ } catch {
24
+ broken.push(tool.name);
25
+ }
26
+ }
27
+ }
28
+ return broken;
29
+ }
30
+
31
+ /**
32
+ * Conta learnings prontos para evoluir.
33
+ */
34
+ function countEvolvablelearnings(db) {
35
+ const squadLearnings = listSquadLearnings(db, null, 'active').filter(
36
+ (l) => Number(l.frequency || 1) >= MIN_FREQUENCY
37
+ );
38
+ const projectLearnings = listProjectLearnings(db, 'active').filter(
39
+ (l) => Number(l.frequency || 1) >= MIN_FREQUENCY
40
+ );
41
+ return squadLearnings.length + projectLearnings.length;
42
+ }
43
+
44
+ /**
45
+ * Verifica squads inativos (sem sessão recente).
46
+ */
47
+ function getInactiveSquads(db) {
48
+ const cutoff = new Date(Date.now() - INACTIVITY_DAYS * 24 * 60 * 60 * 1000).toISOString();
49
+ const rows = db.prepare(`
50
+ SELECT squad_slug, updated_at FROM squads
51
+ WHERE status = 'active' AND (updated_at IS NULL OR updated_at < ?)
52
+ ORDER BY updated_at ASC
53
+ LIMIT 5
54
+ `).all(cutoff);
55
+ return rows;
56
+ }
57
+
58
+ /**
59
+ * Conta arquivos pendentes de evolução.
60
+ */
61
+ async function countPendingEvolutions(projectDir) {
62
+ const evolutionDir = path.resolve(projectDir, EVOLUTION_DIR);
63
+ try {
64
+ const files = await fs.readdir(evolutionDir);
65
+ return files.filter((f) => f.startsWith('pending-') && f.endsWith('.json')).length;
66
+ } catch {
67
+ return 0;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Comando principal: aioson health [project-dir]
73
+ */
74
+ async function runHealth({ args = [], options = {}, logger = console } = {}) {
75
+ const projectDir = path.resolve(process.cwd(), args[0] || '.');
76
+ const quiet = Boolean(options.quiet);
77
+ const json = Boolean(options.json);
78
+
79
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
80
+
81
+ const report = {
82
+ ok: true,
83
+ projectDir,
84
+ items: [],
85
+ evolvableLearnings: 0,
86
+ brokenTools: [],
87
+ inactiveSquads: [],
88
+ pendingEvolutions: 0,
89
+ hasRuntimeStore: Boolean(handle)
90
+ };
91
+
92
+ if (!handle) {
93
+ if (!json && !quiet) {
94
+ logger.log('AIOSON Health — nenhum runtime store encontrado neste projeto.');
95
+ logger.log('Execute: aioson runtime:init .');
96
+ }
97
+ return report;
98
+ }
99
+
100
+ const { db } = handle;
101
+
102
+ try {
103
+ // 1. Learnings evoluíveis
104
+ report.evolvableLearnings = countEvolvablelearnings(db);
105
+ if (report.evolvableLearnings > 0) {
106
+ report.items.push({
107
+ type: 'learnings',
108
+ level: 'info',
109
+ message: `${report.evolvableLearnings} learning(s) prontos para evoluir`,
110
+ action: 'aioson learning:evolve .'
111
+ });
112
+ }
113
+
114
+ // 2. Squads inativos
115
+ const inactiveSquads = getInactiveSquads(db);
116
+ report.inactiveSquads = inactiveSquads.map((s) => s.squad_slug);
117
+ if (inactiveSquads.length > 0) {
118
+ for (const squad of inactiveSquads) {
119
+ const days = squad.updated_at
120
+ ? Math.floor((Date.now() - new Date(squad.updated_at).getTime()) / (1000 * 60 * 60 * 24))
121
+ : '?';
122
+ report.items.push({
123
+ type: 'squad_inactive',
124
+ level: 'warn',
125
+ message: `Squad "${squad.squad_slug}" inativo há ${days} dia(s)`,
126
+ action: null
127
+ });
128
+ }
129
+ }
130
+
131
+ // 3. Dynamic tools com handlers quebrados
132
+ const tools = listDynamicTools(db, null);
133
+ const broken = await checkBrokenTools(tools, projectDir);
134
+ report.brokenTools = broken;
135
+ if (broken.length > 0) {
136
+ report.items.push({
137
+ type: 'broken_tools',
138
+ level: 'error',
139
+ message: `${broken.length} tool(s) com handler inválido: ${broken.join(', ')}`,
140
+ action: 'aioson tool:unregister . --name=<nome>'
141
+ });
142
+ }
143
+
144
+ // 4. Evoluções pendentes
145
+ report.pendingEvolutions = await countPendingEvolutions(projectDir);
146
+ if (report.pendingEvolutions > 0) {
147
+ report.items.push({
148
+ type: 'pending_evolutions',
149
+ level: 'info',
150
+ message: `${report.pendingEvolutions} proposta(s) de evolução aguardando revisão`,
151
+ action: `aioson learning:apply . --file=.aioson/evolution/pending-XXX.json`
152
+ });
153
+ }
154
+ } finally {
155
+ db.close();
156
+ }
157
+
158
+ if (json) return report;
159
+
160
+ // Output formatado
161
+ if (report.items.length === 0) {
162
+ if (!quiet) logger.log('AIOSON Health — tudo em ordem. Nenhum item pendente.');
163
+ return report;
164
+ }
165
+
166
+ logger.log('');
167
+ logger.log('AIOSON Health — itens que precisam de atenção:');
168
+ logger.log('');
169
+
170
+ const icons = { info: '●', warn: '○', error: '✗' };
171
+ for (const item of report.items) {
172
+ const icon = icons[item.level] || '●';
173
+ logger.log(` ${icon} ${item.message}`);
174
+ if (item.action) logger.log(` → ${item.action}`);
175
+ }
176
+ logger.log('');
177
+
178
+ return report;
179
+ }
180
+
181
+ /**
182
+ * Versão compacta para injeção silenciosa no live:start e live:close.
183
+ * Retorna string de aviso ou null se tudo ok.
184
+ */
185
+ async function getHealthDigest(projectDir) {
186
+ try {
187
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
188
+ if (!handle) return null;
189
+
190
+ const { db } = handle;
191
+ const items = [];
192
+
193
+ try {
194
+ const count = countEvolvablelearnings(db);
195
+ if (count > 0) items.push(`${count} learning(s) prontos para evoluir`);
196
+
197
+ const tools = listDynamicTools(db, null);
198
+ const broken = await checkBrokenTools(tools, projectDir);
199
+ if (broken.length > 0) items.push(`${broken.length} tool(s) com handler quebrado`);
200
+
201
+ const pending = await countPendingEvolutions(projectDir);
202
+ if (pending > 0) items.push(`${pending} evolução(ões) pendente(s)`);
203
+ } finally {
204
+ db.close();
205
+ }
206
+
207
+ if (items.length === 0) return null;
208
+ return items;
209
+ } catch {
210
+ return null;
211
+ }
212
+ }
213
+
214
+ module.exports = { runHealth, getHealthDigest };
@@ -0,0 +1,253 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * aioson hooks:emit [projectDir] --agent=<name> --source=<tool>
5
+ *
6
+ * Called by Claude Code / Antigravity / Codex hooks on every tool use.
7
+ * Reads the hook payload from stdin (JSON), maps it to an AIOSON runtime event,
8
+ * and writes it to the active live session in SQLite + events.ndjson.
9
+ *
10
+ * If no live session exists, auto-starts one (--no-launch mode) before emitting.
11
+ *
12
+ * Designed to be fast (< 50ms hot path) and never block the agent.
13
+ */
14
+
15
+ const path = require('node:path');
16
+ const fs = require('node:fs/promises');
17
+ const { execFileSync } = require('node:child_process');
18
+ const {
19
+ openRuntimeDb,
20
+ resolveRuntimePaths,
21
+ readAgentSession,
22
+ appendRunEvent,
23
+ startTask,
24
+ startRun,
25
+ writeAgentSession
26
+ } = require('../runtime-store');
27
+
28
+ const HOOKS_EMIT_VERSION = '1';
29
+
30
+ // Tool name → event_type mapping
31
+ const TOOL_EVENT_MAP = {
32
+ Write: 'artifact',
33
+ Edit: 'artifact',
34
+ MultiEdit: 'artifact',
35
+ Bash: 'step_done',
36
+ Task: 'note',
37
+ TodoWrite: 'note',
38
+ WebSearch: 'note',
39
+ WebFetch: 'note'
40
+ };
41
+
42
+ // Tools to skip — too noisy, no meaningful event
43
+ const SKIP_TOOLS = new Set(['Read', 'Glob', 'Grep', 'LS', 'NotebookRead', 'mcp__']);
44
+
45
+ function nowIso() {
46
+ return new Date().toISOString();
47
+ }
48
+
49
+ function readStdin() {
50
+ return new Promise((resolve) => {
51
+ let data = '';
52
+ if (process.stdin.isTTY) { resolve(null); return; }
53
+ process.stdin.setEncoding('utf8');
54
+ process.stdin.on('data', (chunk) => { data += chunk; });
55
+ process.stdin.on('end', () => {
56
+ try { resolve(JSON.parse(data)); }
57
+ catch { resolve(null); }
58
+ });
59
+ // Timeout: don't block if stdin is empty
60
+ setTimeout(() => resolve(null), 500);
61
+ });
62
+ }
63
+
64
+ function buildEventFromPayload(payload, source) {
65
+ if (!payload) return null;
66
+
67
+ const toolName = payload.tool_name || payload.toolName || null;
68
+ if (!toolName) return null;
69
+
70
+ // Skip noisy read-only tools
71
+ if (SKIP_TOOLS.has(toolName) || [...SKIP_TOOLS].some((p) => toolName.startsWith(p))) {
72
+ return null;
73
+ }
74
+
75
+ const eventType = TOOL_EVENT_MAP[toolName] || 'note';
76
+ const input = payload.tool_input || payload.toolInput || {};
77
+
78
+ let message = `[${source}] ${toolName}`;
79
+ let filePath = null;
80
+
81
+ if (toolName === 'Write' || toolName === 'Edit' || toolName === 'MultiEdit') {
82
+ filePath = input.file_path || input.path || null;
83
+ message = filePath ? `${toolName}: ${path.basename(filePath)}` : `${toolName}`;
84
+ } else if (toolName === 'Bash') {
85
+ const cmd = String(input.command || input.cmd || '').trim().slice(0, 80);
86
+ message = cmd ? `$ ${cmd}` : 'Bash';
87
+ } else if (toolName === 'Task') {
88
+ const desc = String(input.description || input.prompt || '').slice(0, 80);
89
+ message = desc || 'Task launched';
90
+ } else if (toolName === 'TodoWrite') {
91
+ message = 'Task list updated';
92
+ }
93
+
94
+ return {
95
+ eventType,
96
+ message,
97
+ filePath,
98
+ toolName,
99
+ sessionId: payload.session_id || payload.sessionId || null
100
+ };
101
+ }
102
+
103
+ async function ensureOrCreateLiveSession(targetDir, agentName, source, runtimeDir) {
104
+ // Fast path: check existing session file
105
+ const session = await readAgentSession(runtimeDir, agentName);
106
+ if (session && !session.finished && session.source === 'live' && session.runKey) {
107
+ return session.runKey;
108
+ }
109
+
110
+ // No session — auto-start one inline (no-launch mode)
111
+ const now = nowIso();
112
+ const sessionKey = `hooks-${agentName}-${Date.now()}`;
113
+ const title = `[hooks] ${agentName} via ${source}`;
114
+
115
+ const { db } = await openRuntimeDb(targetDir);
116
+ try {
117
+ const taskKey = startTask(db, {
118
+ sessionKey,
119
+ title,
120
+ status: 'running',
121
+ createdBy: agentName,
122
+ taskKind: 'live_session',
123
+ metaJson: { tool_session: source, path: targetDir, auto_started: true }
124
+ });
125
+
126
+ const runKey = startRun(db, {
127
+ taskKey,
128
+ agentName,
129
+ agentKind: 'official',
130
+ sessionKey,
131
+ source: 'live',
132
+ title,
133
+ eventType: 'session_started',
134
+ phase: 'live',
135
+ message: `Auto-started by hooks:emit (${source})`,
136
+ payload: { tool_session: source, path: targetDir, hooks_version: HOOKS_EMIT_VERSION }
137
+ });
138
+
139
+ await writeAgentSession(runtimeDir, agentName, {
140
+ runKey,
141
+ taskKey,
142
+ sessionKey,
143
+ startedAt: now,
144
+ finished: false,
145
+ source: 'live'
146
+ });
147
+
148
+ // Write state.json for dashboard live view
149
+ const stateDir = path.join(runtimeDir, 'live', sessionKey);
150
+ await fs.mkdir(stateDir, { recursive: true });
151
+ await fs.writeFile(path.join(stateDir, 'state.json'), JSON.stringify({
152
+ session_key: sessionKey,
153
+ run_key: runKey,
154
+ task_key: taskKey,
155
+ agent_name: agentName,
156
+ tool_session: source,
157
+ status: 'running',
158
+ started_at: now,
159
+ updated_at: now,
160
+ auto_started: true,
161
+ last_events: [{ ts: now, type: 'session_started', summary: `Auto-started by hooks:emit (${source})` }]
162
+ }, null, 2), 'utf8');
163
+
164
+ return runKey;
165
+ } finally {
166
+ db.close();
167
+ }
168
+ }
169
+
170
+ async function appendLiveEventFile(runtimeDir, runKey, event) {
171
+ // Find the session dir for this runKey
172
+ try {
173
+ const liveRoot = path.join(runtimeDir, 'live');
174
+ const entries = await fs.readdir(liveRoot).catch(() => []);
175
+ for (const entry of entries) {
176
+ const statePath = path.join(liveRoot, entry, 'state.json');
177
+ try {
178
+ const state = JSON.parse(await fs.readFile(statePath, 'utf8'));
179
+ if (state.run_key === runKey) {
180
+ const eventsPath = path.join(liveRoot, entry, 'events.ndjson');
181
+ await fs.appendFile(eventsPath, JSON.stringify(event) + '\n', 'utf8');
182
+
183
+ // Update state.json updated_at + last_events
184
+ state.updated_at = event.ts;
185
+ const lastEvents = state.last_events || [];
186
+ lastEvents.push({ ts: event.ts, type: event.type, summary: event.summary || event.message });
187
+ state.last_events = lastEvents.slice(-10); // keep last 10
188
+ await fs.writeFile(statePath, JSON.stringify(state, null, 2), 'utf8');
189
+ break;
190
+ }
191
+ } catch { /* skip */ }
192
+ }
193
+ } catch { /* non-fatal */ }
194
+ }
195
+
196
+ async function runHooksEmit({ args, options = {} }) {
197
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
198
+ const agentName = options.agent ? String(options.agent).replace(/^@/, '') : 'dev';
199
+ const source = options.source ? String(options.source).trim() : 'claude';
200
+
201
+ // Silence all output — hooks must be silent
202
+ const logger = { log: () => {}, error: () => {} };
203
+
204
+ try {
205
+ const { runtimeDir } = resolveRuntimePaths(targetDir);
206
+
207
+ // Read hook payload from stdin
208
+ const payload = await readStdin();
209
+ const event = buildEventFromPayload(payload, source);
210
+
211
+ // Skip if no meaningful event (read-only tools, etc.)
212
+ if (!event) return { ok: true, skipped: true };
213
+
214
+ const now = nowIso();
215
+
216
+ // Ensure live session exists (fast path: session file read)
217
+ const runKey = await ensureOrCreateLiveSession(targetDir, agentName, source, runtimeDir);
218
+
219
+ // Write to SQLite
220
+ const { db } = await openRuntimeDb(targetDir, { mustExist: true });
221
+ try {
222
+ appendRunEvent(db, {
223
+ runKey,
224
+ eventType: event.eventType,
225
+ phase: 'live',
226
+ status: 'running',
227
+ message: event.message,
228
+ payload: event.filePath ? { file: event.filePath, tool: event.toolName } : { tool: event.toolName },
229
+ createdAt: now
230
+ });
231
+ } finally {
232
+ db.close();
233
+ }
234
+
235
+ // Append to events.ndjson for real-time dashboard view
236
+ await appendLiveEventFile(runtimeDir, runKey, {
237
+ ts: now,
238
+ type: event.eventType,
239
+ message: event.message,
240
+ tool: event.toolName,
241
+ file: event.filePath || undefined,
242
+ source,
243
+ agent: agentName
244
+ });
245
+
246
+ return { ok: true, runKey, event: event.eventType, message: event.message };
247
+ } catch {
248
+ // Never fail — hooks must not block the agent
249
+ return { ok: false };
250
+ }
251
+ }
252
+
253
+ module.exports = { runHooksEmit };