@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,347 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * aioson hooks:install [projectDir] --agent=<name> --tool=<claude|antigravity|codex|all>
5
+ *
6
+ * Installs AIOSON event hooks into the AI tool's settings file.
7
+ * After installation, every Write/Edit/Bash tool call and session stop
8
+ * automatically emits a runtime event to SQLite — no manual aioson calls needed.
9
+ *
10
+ * Supported tools:
11
+ * --tool=claude → ~/.claude/settings.json
12
+ * --tool=antigravity → ~/.gemini/antigravity/hooks.json (+ .agents/hooks.json in project)
13
+ * --tool=codex → ~/.codex/config.yaml (limited: no hook system, documents workaround)
14
+ * --tool=all → installs for all detected tools
15
+ */
16
+
17
+ const path = require('node:path');
18
+ const fs = require('node:fs/promises');
19
+ const os = require('node:os');
20
+
21
+ const HOME = os.homedir();
22
+
23
+ // ─── Config file paths ────────────────────────────────────────────────────────
24
+
25
+ const CONFIG_PATHS = {
26
+ claude: path.join(HOME, '.claude', 'settings.json'),
27
+ antigravity: path.join(HOME, '.gemini', 'antigravity', 'hooks.json'),
28
+ antigravity_workspace: '.agents/hooks.json' // relative to project
29
+ };
30
+
31
+ // ─── Hook command templates ───────────────────────────────────────────────────
32
+
33
+ function makeEmitCommand(agentName, source) {
34
+ // $PWD is the project directory at hook execution time
35
+ return `aioson hooks:emit "$PWD" --agent=${agentName} --source=${source} 2>/dev/null || true`;
36
+ }
37
+
38
+ function makeDoneCommand(agentName) {
39
+ return `aioson agent:done "$PWD" --agent=${agentName} --summary="Session ended via ${agentName} hook" 2>/dev/null || true`;
40
+ }
41
+
42
+ // ─── Claude Code ─────────────────────────────────────────────────────────────
43
+
44
+ function buildClaudeHooks(agentName) {
45
+ const emitCmd = makeEmitCommand(agentName, 'claude');
46
+ const doneCmd = makeDoneCommand(agentName);
47
+
48
+ return {
49
+ PostToolUse: [
50
+ {
51
+ matcher: 'Write|Edit|MultiEdit',
52
+ hooks: [{ type: 'command', command: emitCmd }]
53
+ },
54
+ {
55
+ matcher: 'Bash',
56
+ hooks: [{ type: 'command', command: emitCmd }]
57
+ },
58
+ {
59
+ matcher: 'Task|TodoWrite',
60
+ hooks: [{ type: 'command', command: emitCmd }]
61
+ }
62
+ ],
63
+ Stop: [
64
+ {
65
+ hooks: [{ type: 'command', command: doneCmd }]
66
+ }
67
+ ]
68
+ };
69
+ }
70
+
71
+ async function installClaudeHooks(agentName, dryRun, logger) {
72
+ const configPath = CONFIG_PATHS.claude;
73
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
74
+
75
+ let existing = {};
76
+ try {
77
+ existing = JSON.parse(await fs.readFile(configPath, 'utf8'));
78
+ } catch { /* file doesn't exist yet */ }
79
+
80
+ const newHooks = buildClaudeHooks(agentName);
81
+
82
+ // Merge: add AIOSON hooks without removing existing ones
83
+ const merged = { ...existing };
84
+ if (!merged.hooks) merged.hooks = {};
85
+
86
+ for (const [event, hookList] of Object.entries(newHooks)) {
87
+ if (!merged.hooks[event]) {
88
+ merged.hooks[event] = hookList;
89
+ } else {
90
+ // Remove any existing AIOSON hooks (to avoid duplicates on reinstall)
91
+ const filtered = merged.hooks[event].filter((entry) => {
92
+ const cmd = entry.hooks?.[0]?.command || '';
93
+ return !cmd.includes('aioson hooks:emit') && !cmd.includes('aioson agent:done');
94
+ });
95
+ merged.hooks[event] = [...filtered, ...hookList];
96
+ }
97
+ }
98
+
99
+ if (!dryRun) {
100
+ await fs.writeFile(configPath, JSON.stringify(merged, null, 2), 'utf8');
101
+ logger.log(` ✓ Claude Code — ${configPath}`);
102
+ } else {
103
+ logger.log(` [dry-run] Would write: ${configPath}`);
104
+ logger.log(` Hooks to add:`);
105
+ logger.log(` PostToolUse (Write|Edit|MultiEdit|Bash|Task|TodoWrite) → hooks:emit`);
106
+ logger.log(` Stop → agent:done`);
107
+ }
108
+
109
+ return { tool: 'claude', configPath, hooks: newHooks };
110
+ }
111
+
112
+ // ─── Antigravity ─────────────────────────────────────────────────────────────
113
+
114
+ function buildAntigravityHooks(agentName) {
115
+ const emitCmd = makeEmitCommand(agentName, 'antigravity');
116
+ const doneCmd = makeDoneCommand(agentName);
117
+ const startCmd = `aioson live:start "$PWD" --agent=${agentName} --tool=antigravity --no-launch 2>/dev/null || true`;
118
+
119
+ return {
120
+ SessionStart: [{ type: 'command', command: startCmd }],
121
+ PostToolUse: [
122
+ { matcher: 'Write|Edit|MultiEdit', hooks: [{ type: 'command', command: emitCmd }] },
123
+ { matcher: 'Bash', hooks: [{ type: 'command', command: emitCmd }] },
124
+ { matcher: 'Task|TodoWrite', hooks: [{ type: 'command', command: emitCmd }] }
125
+ ],
126
+ SessionEnd: [{ type: 'command', command: doneCmd }],
127
+ Stop: [{ type: 'command', command: doneCmd }]
128
+ };
129
+ }
130
+
131
+ async function installAntigravityHooks(agentName, projectDir, dryRun, logger) {
132
+ const globalPath = CONFIG_PATHS.antigravity;
133
+ const workspacePath = path.join(projectDir, CONFIG_PATHS.antigravity_workspace);
134
+
135
+ const hooks = buildAntigravityHooks(agentName);
136
+
137
+ if (!dryRun) {
138
+ await fs.mkdir(path.dirname(globalPath), { recursive: true });
139
+ await fs.mkdir(path.dirname(workspacePath), { recursive: true });
140
+
141
+ // Global hooks
142
+ let globalExisting = {};
143
+ try { globalExisting = JSON.parse(await fs.readFile(globalPath, 'utf8')); } catch { /* new file */ }
144
+ const mergedGlobal = mergeAntigravityHooks(globalExisting, hooks);
145
+ await fs.writeFile(globalPath, JSON.stringify(mergedGlobal, null, 2), 'utf8');
146
+ logger.log(` ✓ Antigravity global — ${globalPath}`);
147
+
148
+ // Workspace hooks (project-scoped, takes priority)
149
+ let wsExisting = {};
150
+ try { wsExisting = JSON.parse(await fs.readFile(workspacePath, 'utf8')); } catch { /* new file */ }
151
+ const mergedWs = mergeAntigravityHooks(wsExisting, hooks);
152
+ await fs.writeFile(workspacePath, JSON.stringify(mergedWs, null, 2), 'utf8');
153
+ logger.log(` ✓ Antigravity workspace — ${workspacePath}`);
154
+ } else {
155
+ logger.log(` [dry-run] Would write: ${globalPath}`);
156
+ logger.log(` [dry-run] Would write: ${workspacePath}`);
157
+ logger.log(` Hooks to add: SessionStart → live:start, PostToolUse → hooks:emit, SessionEnd/Stop → agent:done`);
158
+ }
159
+
160
+ return { tool: 'antigravity', globalPath, workspacePath, hooks };
161
+ }
162
+
163
+ function mergeAntigravityHooks(existing, newHooks) {
164
+ const merged = { ...existing };
165
+ if (!merged.hooks) merged.hooks = {};
166
+
167
+ for (const [event, entries] of Object.entries(newHooks)) {
168
+ const existingEntries = Array.isArray(merged.hooks[event]) ? merged.hooks[event] : [];
169
+ // Remove previous AIOSON entries
170
+ const filtered = existingEntries.filter((e) => {
171
+ const cmd = (e.command || e.hooks?.[0]?.command || '');
172
+ return !cmd.includes('aioson');
173
+ });
174
+ const newEntries = Array.isArray(entries) ? entries : [entries];
175
+ merged.hooks[event] = [...filtered, ...newEntries];
176
+ }
177
+
178
+ return merged;
179
+ }
180
+
181
+ // ─── Codex (OpenAI) ───────────────────────────────────────────────────────────
182
+
183
+ async function installCodexHooks(agentName, dryRun, logger) {
184
+ // Codex CLI does not have a native hook system as of 2026.
185
+ // The workaround: add a shell alias that wraps `codex` and calls live:start before / agent:done after.
186
+ const configPath = path.join(HOME, '.codex', 'config.yaml');
187
+ const wrapperPath = path.join(HOME, '.codex', 'aioson-wrapper.sh');
188
+
189
+ const wrapperScript = `#!/bin/bash
190
+ # AIOSON session wrapper for Codex CLI
191
+ # Generated by: aioson hooks:install --tool=codex --agent=${agentName}
192
+ # Usage: replace \`codex\` calls with \`codex-aioson\` OR add to .bashrc:
193
+ # alias codex='${wrapperPath}'
194
+
195
+ PROJECT_DIR="\${1:-$PWD}"
196
+ AGENT="${agentName}"
197
+
198
+ # Start live session before Codex runs
199
+ aioson live:start "$PROJECT_DIR" --agent="$AGENT" --tool=codex --no-launch 2>/dev/null || true
200
+
201
+ # Run Codex with all original arguments
202
+ codex-bin "$@"
203
+ EXIT_CODE=$?
204
+
205
+ # Register session end
206
+ aioson agent:done "$PROJECT_DIR" --agent="$AGENT" --summary="Codex session ended" 2>/dev/null || true
207
+
208
+ exit $EXIT_CODE
209
+ `;
210
+
211
+ if (!dryRun) {
212
+ await fs.mkdir(path.dirname(wrapperPath), { recursive: true });
213
+ await fs.writeFile(wrapperPath, wrapperScript, 'utf8');
214
+ await fs.chmod(wrapperPath, 0o755);
215
+ logger.log(` ✓ Codex wrapper — ${wrapperPath}`);
216
+ logger.log(` ⚠ Codex has no native hooks. Add this to ~/.bashrc:`);
217
+ logger.log(` alias codex='${wrapperPath}'`);
218
+ logger.log(` Or rename: mv $(which codex) $(which codex)-bin`);
219
+ } else {
220
+ logger.log(` [dry-run] Would write: ${wrapperPath}`);
221
+ logger.log(` ⚠ Codex has no native hook system. Wrapper script approach only.`);
222
+ }
223
+
224
+ return { tool: 'codex', wrapperPath, limited: true };
225
+ }
226
+
227
+ // ─── Detection ───────────────────────────────────────────────────────────────
228
+
229
+ async function detectInstalledTools() {
230
+ const detected = [];
231
+ const checks = [
232
+ { tool: 'claude', path: path.join(HOME, '.claude') },
233
+ { tool: 'antigravity', path: path.join(HOME, '.gemini', 'antigravity') },
234
+ { tool: 'codex', path: path.join(HOME, '.codex') }
235
+ ];
236
+
237
+ for (const { tool, path: p } of checks) {
238
+ try {
239
+ await fs.access(p);
240
+ detected.push(tool);
241
+ } catch { /* not installed */ }
242
+ }
243
+
244
+ return detected;
245
+ }
246
+
247
+ // ─── Main ─────────────────────────────────────────────────────────────────────
248
+
249
+ async function runHooksInstall({ args, options = {}, logger }) {
250
+ const projectDir = path.resolve(process.cwd(), args[0] || '.');
251
+ const agentName = options.agent ? String(options.agent).replace(/^@/, '') : 'dev';
252
+ const dryRun = options['dry-run'] || options.dryRun || false;
253
+ let tool = options.tool ? String(options.tool).trim().toLowerCase() : 'all';
254
+
255
+ if (tool === 'all') {
256
+ const detected = await detectInstalledTools();
257
+ if (detected.length === 0) {
258
+ logger.log('No supported AI tools detected. Install Claude Code, Antigravity, or Codex first.');
259
+ return { ok: false, reason: 'no_tools_detected' };
260
+ }
261
+ logger.log(`Detected tools: ${detected.join(', ')}`);
262
+ tool = detected.join(',');
263
+ }
264
+
265
+ const tools = tool.split(',').map((t) => t.trim()).filter(Boolean);
266
+ const results = [];
267
+
268
+ logger.log(`Hooks Install — agent: @${agentName}${dryRun ? ' [dry-run]' : ''}`);
269
+ logger.log('─'.repeat(50));
270
+
271
+ for (const t of tools) {
272
+ try {
273
+ if (t === 'claude') {
274
+ results.push(await installClaudeHooks(agentName, dryRun, logger));
275
+ } else if (t === 'antigravity') {
276
+ results.push(await installAntigravityHooks(agentName, projectDir, dryRun, logger));
277
+ } else if (t === 'codex') {
278
+ results.push(await installCodexHooks(agentName, dryRun, logger));
279
+ } else {
280
+ logger.log(` ⚠ Unknown tool: ${t} — supported: claude, antigravity, codex`);
281
+ }
282
+ } catch (err) {
283
+ logger.log(` ✗ ${t}: ${err.message}`);
284
+ results.push({ tool: t, error: err.message });
285
+ }
286
+ }
287
+
288
+ logger.log('─'.repeat(50));
289
+
290
+ if (!dryRun) {
291
+ logger.log('');
292
+ logger.log('Hooks installed. From now on:');
293
+ logger.log(' • Every file write/edit → logged as artifact event');
294
+ logger.log(' • Every bash command → logged as step_done event');
295
+ logger.log(' • Session end → logged as agent:done');
296
+ logger.log('');
297
+ logger.log('To verify: aioson live:status . --agent=' + agentName);
298
+ logger.log('To uninstall: aioson hooks:uninstall --tool=' + tools.join(','));
299
+ }
300
+
301
+ if (options.json) {
302
+ return { ok: true, results, agentName, dryRun };
303
+ }
304
+
305
+ return { ok: true, results, agentName, dryRun };
306
+ }
307
+
308
+ async function runHooksUninstall({ args, options = {}, logger }) {
309
+ const agentName = options.agent ? String(options.agent).replace(/^@/, '') : 'dev';
310
+ const dryRun = options['dry-run'] || options.dryRun || false;
311
+ const tool = options.tool ? String(options.tool).trim().toLowerCase() : 'claude';
312
+ const tools = tool.split(',').map((t) => t.trim()).filter(Boolean);
313
+
314
+ logger.log(`Hooks Uninstall — agent: @${agentName}${dryRun ? ' [dry-run]' : ''}`);
315
+ logger.log('─'.repeat(50));
316
+
317
+ for (const t of tools) {
318
+ if (t === 'claude') {
319
+ try {
320
+ const configPath = CONFIG_PATHS.claude;
321
+ const existing = JSON.parse(await fs.readFile(configPath, 'utf8'));
322
+ if (existing.hooks) {
323
+ for (const event of Object.keys(existing.hooks)) {
324
+ existing.hooks[event] = (existing.hooks[event] || []).filter((entry) => {
325
+ const cmd = entry.hooks?.[0]?.command || entry.command || '';
326
+ return !cmd.includes('aioson hooks:emit') && !cmd.includes('aioson agent:done') && !cmd.includes('aioson live:start');
327
+ });
328
+ if (existing.hooks[event].length === 0) delete existing.hooks[event];
329
+ }
330
+ if (Object.keys(existing.hooks).length === 0) delete existing.hooks;
331
+ }
332
+ if (!dryRun) {
333
+ await fs.writeFile(configPath, JSON.stringify(existing, null, 2), 'utf8');
334
+ logger.log(` ✓ Claude Code hooks removed — ${configPath}`);
335
+ } else {
336
+ logger.log(` [dry-run] Would remove AIOSON hooks from: ${configPath}`);
337
+ }
338
+ } catch {
339
+ logger.log(` Claude Code settings not found — nothing to remove`);
340
+ }
341
+ }
342
+ }
343
+
344
+ return { ok: true };
345
+ }
346
+
347
+ module.exports = { runHooksInstall, runHooksUninstall };
@@ -0,0 +1,195 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * aioson learning:auto-promote — auto-promote frequent learnings to rules files.
5
+ *
6
+ * Scans project_learnings with frequency >= threshold and promotes eligible ones
7
+ * to .aioson/rules/. Domain learnings (not promotable to universal rules) are noted
8
+ * but not written. No LLM calls.
9
+ *
10
+ * Usage:
11
+ * aioson learning:auto-promote .
12
+ * aioson learning:auto-promote . --threshold=3
13
+ * aioson learning:auto-promote . --threshold=2 --dry-run
14
+ * aioson learning:auto-promote . --json
15
+ */
16
+
17
+ const fs = require('node:fs/promises');
18
+ const path = require('node:path');
19
+ const { openRuntimeDb, listProjectLearnings } = require('../runtime-store');
20
+
21
+ const DEFAULT_THRESHOLD = 3;
22
+ const RULES_DIR = '.aioson/rules';
23
+ const BAR = '━'.repeat(30);
24
+
25
+ // Only 'process' and 'quality' type learnings become universal rules.
26
+ // 'domain' learnings are project-specific and should not become global rules.
27
+ // 'preference' learnings go to project.context.md (handled by learning:evolve).
28
+ const PROMOTABLE_TYPES = new Set(['process', 'quality']);
29
+
30
+ function slugify(title) {
31
+ return title
32
+ .toLowerCase()
33
+ .replace(/[^a-z0-9]+/g, '-')
34
+ .replace(/^-+|-+$/g, '')
35
+ .slice(0, 50);
36
+ }
37
+
38
+ function buildRuleContent(learning) {
39
+ const confidence = learning.confidence === 'high' ? ' (high confidence)' : learning.confidence === 'low' ? ' (low confidence)' : '';
40
+ const lines = [
41
+ '---',
42
+ `title: ${learning.title}`,
43
+ `type: ${learning.type}`,
44
+ `frequency: ${learning.frequency}`,
45
+ `source: auto-promoted`,
46
+ 'agents: all',
47
+ '---',
48
+ '',
49
+ `# ${learning.title}${confidence}`,
50
+ ''
51
+ ];
52
+
53
+ if (learning.evidence) {
54
+ lines.push(`> ${learning.evidence}`, '');
55
+ }
56
+
57
+ if (learning.type === 'process') {
58
+ lines.push('**When:** During any implementation or workflow session.');
59
+ lines.push(`**Rule:** ${learning.title}`);
60
+ } else if (learning.type === 'quality') {
61
+ lines.push('**When:** Before committing or delivering a deliverable.');
62
+ lines.push(`**Rule:** ${learning.title}`);
63
+ }
64
+
65
+ lines.push('');
66
+ return lines.join('\n');
67
+ }
68
+
69
+ async function runLearningAutoPromote({ args, options = {}, logger }) {
70
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
71
+ const threshold = options.threshold ? parseInt(options.threshold) : DEFAULT_THRESHOLD;
72
+ const dryRun = Boolean(options['dry-run'] || options.dry);
73
+
74
+ if (isNaN(threshold) || threshold < 1) {
75
+ if (options.json) return { ok: false, reason: 'invalid_threshold' };
76
+ logger.log('--threshold must be a positive integer (default: 3)');
77
+ return { ok: false };
78
+ }
79
+
80
+ const handle = await openRuntimeDb(targetDir, { mustExist: true });
81
+ if (!handle) {
82
+ if (options.json) return { ok: false, reason: 'no_runtime', message: 'No runtime database found. Run aioson runtime:init first.' };
83
+ logger.log('No runtime database found. Run aioson runtime:init first.');
84
+ return { ok: false };
85
+ }
86
+
87
+ let learnings;
88
+ try {
89
+ learnings = listProjectLearnings(handle.db, 'active');
90
+ } finally {
91
+ handle.db.close();
92
+ }
93
+
94
+ // Filter by threshold
95
+ const eligible = learnings.filter((l) => Number(l.frequency || 1) >= threshold);
96
+
97
+ if (!options.json) {
98
+ logger.log('');
99
+ logger.log('Learning Auto-Promotion');
100
+ logger.log(BAR);
101
+ logger.log(`Scanning project_learnings (frequency ≥ ${threshold}):`);
102
+ logger.log('');
103
+ }
104
+
105
+ const promoted = [];
106
+ const noted = [];
107
+ const skipped = [];
108
+
109
+ for (const learning of eligible) {
110
+ if (!PROMOTABLE_TYPES.has(learning.type)) {
111
+ noted.push({
112
+ title: learning.title,
113
+ type: learning.type,
114
+ frequency: learning.frequency,
115
+ reason: `${learning.type} learning — not promotable to universal rule`
116
+ });
117
+ continue;
118
+ }
119
+
120
+ const slug = slugify(learning.title);
121
+ const fileName = `process-${slug}.md`;
122
+ const filePath = path.join(targetDir, RULES_DIR, fileName);
123
+
124
+ // Check if already exists
125
+ let alreadyExists = false;
126
+ try {
127
+ await fs.access(filePath);
128
+ alreadyExists = true;
129
+ } catch { /* ok */ }
130
+
131
+ if (alreadyExists) {
132
+ skipped.push({ title: learning.title, file: fileName, reason: 'already exists' });
133
+ continue;
134
+ }
135
+
136
+ const content = buildRuleContent(learning);
137
+
138
+ if (!dryRun) {
139
+ await fs.mkdir(path.join(targetDir, RULES_DIR), { recursive: true });
140
+ await fs.writeFile(filePath, content, 'utf8');
141
+ }
142
+
143
+ promoted.push({
144
+ learning_id: learning.learning_id,
145
+ title: learning.title,
146
+ type: learning.type,
147
+ frequency: learning.frequency,
148
+ file: path.join(RULES_DIR, fileName)
149
+ });
150
+ }
151
+
152
+ const result = {
153
+ ok: true,
154
+ threshold,
155
+ dry_run: dryRun,
156
+ eligible: eligible.length,
157
+ promoted: promoted.length,
158
+ noted: noted.length,
159
+ skipped: skipped.length,
160
+ promoted_items: promoted,
161
+ noted_items: noted,
162
+ skipped_items: skipped
163
+ };
164
+
165
+ if (options.json) return result;
166
+
167
+ // Human output
168
+ for (const p of promoted) {
169
+ const dryStr = dryRun ? ' (dry-run)' : '';
170
+ logger.log(` ✓ "${p.title}" (freq: ${p.frequency}) → promoted to ${p.file}${dryStr}`);
171
+ }
172
+
173
+ for (const n of noted) {
174
+ logger.log(` ○ "${n.title}" (freq: ${n.frequency}) → ${n.reason}`);
175
+ }
176
+
177
+ for (const s of skipped) {
178
+ logger.log(` — "${s.title}" → ${s.reason}`);
179
+ }
180
+
181
+ logger.log('');
182
+ if (promoted.length > 0) {
183
+ logger.log(`${promoted.length} rule${promoted.length !== 1 ? 's' : ''} ${dryRun ? 'would be' : ''} created.`);
184
+ if (!dryRun) logger.log('Run: aioson learning:evolve to apply to agent genomes.');
185
+ } else if (eligible.length === 0) {
186
+ logger.log(`No learnings with frequency ≥ ${threshold} found.`);
187
+ } else {
188
+ logger.log('No new rules to create.');
189
+ }
190
+ logger.log('');
191
+
192
+ return result;
193
+ }
194
+
195
+ module.exports = { runLearningAutoPromote };
@@ -2,6 +2,7 @@
2
2
 
3
3
  const fs = require('node:fs/promises');
4
4
  const path = require('node:path');
5
+ const { randomUUID } = require('node:crypto');
5
6
  const { openRuntimeDb, listSquadLearnings, listProjectLearnings, promoteSquadLearning, promoteProjectLearning } = require('../runtime-store');
6
7
 
7
8
  const AGENTS_DIR = path.join('.aioson', 'agents');
@@ -265,17 +266,25 @@ async function applyProposed(proposed, projectDir, db, logger, quiet, squadSlug)
265
266
  if (!quiet) logger.log(` ✓ Aplicado: ${delta.file} (+${delta.count} learnings)`);
266
267
  }
267
268
 
268
- // Registra no log de evolução
269
+ // Registra no evolution-log.jsonl (5.5: per-delta entries with UUIDs for rollback)
269
270
  const evolutionDir = path.resolve(projectDir, EVOLUTION_DIR);
270
271
  await fs.mkdir(evolutionDir, { recursive: true });
271
- const logFile = path.join(evolutionDir, 'log.jsonl');
272
- const logEntry = JSON.stringify({
273
- appliedAt: new Date().toISOString(),
274
- deltasCount: evolved,
275
- squad: squadSlug || null,
276
- files: proposed.map((d) => d.file)
277
- });
278
- await fs.appendFile(logFile, `${logEntry}\n`, 'utf8');
272
+ const perDeltaLogFile = path.join(evolutionDir, 'evolution-log.jsonl');
273
+ const ts = new Date().toISOString();
274
+ for (const delta of proposed.slice(0, evolved)) {
275
+ const logEntry = JSON.stringify({
276
+ id: randomUUID(),
277
+ ts,
278
+ type: 'append',
279
+ file: delta.file,
280
+ section: delta.section || null,
281
+ content: delta.content,
282
+ learning_ids: delta.sourceIds || [],
283
+ squad: squadSlug || null,
284
+ status: 'applied'
285
+ });
286
+ await fs.appendFile(perDeltaLogFile, `${logEntry}\n`, 'utf8');
287
+ }
279
288
 
280
289
  if (!quiet) logger.log(`\n${evolved} delta(s) aplicado(s) com sucesso.`);
281
290
  } finally {
@@ -0,0 +1,103 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { openRuntimeDb } = require('../runtime-store');
6
+
7
+ function slugify(text) {
8
+ return String(text || '')
9
+ .toLowerCase()
10
+ .replace(/[^a-z0-9]+/g, '-')
11
+ .replace(/^-+|-+$/g, '')
12
+ .slice(0, 80) || 'learning';
13
+ }
14
+
15
+ function buildNodeContent(learning) {
16
+ const id = slugify(`${learning.type}-${learning.title}`);
17
+ const now = new Date().toISOString().slice(0, 10);
18
+ return {
19
+ id,
20
+ content: `---
21
+ id: ${id}
22
+ type: ${learning.type}
23
+ title: ${learning.title}
24
+ frequency: ${learning.frequency || 1}
25
+ last_reinforced: ${learning.last_reinforced ? learning.last_reinforced.slice(0, 10) : now}
26
+ source_feature: ${learning.feature_slug || 'project'}
27
+ promoted_to: null
28
+ created_at: ${learning.created_at ? learning.created_at.slice(0, 10) : now}
29
+ ---
30
+
31
+ # ${learning.title}
32
+
33
+ **Evidence:** ${learning.evidence || `Detected in ${learning.frequency || 1} session(s).`}
34
+
35
+ ## Applications
36
+ - Review and apply this learning in future sessions of type: ${learning.type}
37
+
38
+ ## Links
39
+ <!-- Add cross-references here -->
40
+ `
41
+ };
42
+ }
43
+
44
+ async function runLearningExport({ args, options = {}, logger }) {
45
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
46
+ const minFrequency = Number(options['min-frequency'] || options.minFrequency || 1);
47
+ const brainsDir = path.join(targetDir, '.aioson', 'brains');
48
+
49
+ const { db, dbPath } = await openRuntimeDb(targetDir, { mustExist: true });
50
+
51
+ if (!db) {
52
+ if (!options.json) logger.log('No runtime database found.');
53
+ return { ok: false, reason: 'no_db' };
54
+ }
55
+
56
+ try {
57
+ const learnings = db.prepare(`
58
+ SELECT learning_id, feature_slug, type, title, frequency, last_reinforced,
59
+ evidence, source_session, created_at
60
+ FROM project_learnings
61
+ WHERE status = 'active' AND frequency >= ?
62
+ ORDER BY frequency DESC, updated_at DESC
63
+ `).all(minFrequency);
64
+
65
+ if (learnings.length === 0) {
66
+ if (!options.json) logger.log(`No learnings with frequency >= ${minFrequency}.`);
67
+ return { ok: true, exported: 0, dbPath };
68
+ }
69
+
70
+ await fs.mkdir(brainsDir, { recursive: true });
71
+
72
+ const exported = [];
73
+ for (const learning of learnings) {
74
+ const { id, content } = buildNodeContent(learning);
75
+ const filePath = path.join(brainsDir, `${id}.md`);
76
+ await fs.writeFile(filePath, content, 'utf8');
77
+ exported.push({ id, filePath, frequency: learning.frequency });
78
+ }
79
+
80
+ const promotable = learnings.filter((l) => l.frequency >= 5).length;
81
+
82
+ if (options.json) {
83
+ return { ok: true, exported: exported.length, nodes: exported, promotable, dbPath };
84
+ }
85
+
86
+ logger.log(`Learning Export — min-frequency: ${minFrequency}`);
87
+ logger.log('─'.repeat(50));
88
+ for (const { id, frequency } of exported) {
89
+ logger.log(` ${id}.md ✓ (frequency: ${frequency})`);
90
+ }
91
+ logger.log('─'.repeat(50));
92
+ logger.log(`${exported.length} nodes written to .aioson/brains/`);
93
+ if (promotable > 0) {
94
+ logger.log(`${promotable} learning(s) with frequency ≥ 5 — run: aioson learning:evolve to promote to genome`);
95
+ }
96
+
97
+ return { ok: true, exported: exported.length, nodes: exported, promotable, dbPath };
98
+ } finally {
99
+ db.close();
100
+ }
101
+ }
102
+
103
+ module.exports = { runLearningExport };