@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,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 };
@@ -0,0 +1,364 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { randomUUID } = require('node:crypto');
6
+ const { openRuntimeDb, listSquadLearnings, listProjectLearnings, promoteSquadLearning, promoteProjectLearning } = require('../runtime-store');
7
+
8
+ const AGENTS_DIR = path.join('.aioson', 'agents');
9
+ const EVOLUTION_DIR = path.join('.aioson', 'evolution');
10
+ const CONTEXT_FILE = path.join('.aioson', 'context', 'project.context.md');
11
+ const MAX_FILE_LINES = 300;
12
+ const MIN_FREQUENCY = 2;
13
+
14
+ /**
15
+ * Mapeia tipo de learning para seção e arquivo alvo.
16
+ */
17
+ function resolveDeltaTarget(type, squadSlug) {
18
+ const rulesDir = path.join('.aioson', 'rules');
19
+ if (type === 'preference') {
20
+ return { file: CONTEXT_FILE, section: '## Preferências dos Agentes' };
21
+ }
22
+ if (type === 'process') {
23
+ const filename = squadSlug ? `${squadSlug}-process.md` : 'project-process.md';
24
+ return { file: path.join(rulesDir, filename), section: null };
25
+ }
26
+ if (type === 'domain') {
27
+ return { file: CONTEXT_FILE, section: '## Conhecimento de Domínio' };
28
+ }
29
+ if (type === 'quality') {
30
+ return { file: CONTEXT_FILE, section: '## Padrões de Qualidade' };
31
+ }
32
+ return { file: CONTEXT_FILE, section: '## Observações' };
33
+ }
34
+
35
+ /**
36
+ * Gate 1: arquivo não pode estar dentro de .aioson/agents/
37
+ */
38
+ function passesConstitutionGate(filePath, projectDir) {
39
+ const absolute = path.isAbsolute(filePath) ? filePath : path.resolve(projectDir, filePath);
40
+ const agentsAbsolute = path.resolve(projectDir, AGENTS_DIR);
41
+ return !absolute.startsWith(agentsAbsolute + path.sep) && absolute !== agentsAbsolute;
42
+ }
43
+
44
+ /**
45
+ * Gate 2: arquivo alvo não pode ultrapassar MAX_FILE_LINES após append.
46
+ */
47
+ async function passesSizeGate(filePath, projectDir, newContent) {
48
+ const absolute = path.isAbsolute(filePath) ? filePath : path.resolve(projectDir, filePath);
49
+ let existingLines = 0;
50
+ try {
51
+ const content = await fs.readFile(absolute, 'utf8');
52
+ existingLines = content.split('\n').length;
53
+ } catch {
54
+ existingLines = 0;
55
+ }
56
+ const newLines = String(newContent || '').split('\n').length;
57
+ return (existingLines + newLines) <= MAX_FILE_LINES;
58
+ }
59
+
60
+ /**
61
+ * Gera o conteúdo textual de um delta a partir de um grupo de learnings.
62
+ */
63
+ function buildDeltaContent(learnings, section) {
64
+ const lines = [];
65
+ if (section) {
66
+ lines.push('');
67
+ }
68
+ for (const l of learnings) {
69
+ const confidence = l.confidence === 'high' ? '(alta confiança)' : l.confidence === 'low' ? '(baixa confiança)' : '';
70
+ lines.push(`- ${l.title} ${confidence}`.trim());
71
+ if (l.evidence) {
72
+ lines.push(` > ${l.evidence}`);
73
+ }
74
+ }
75
+ return lines.join('\n');
76
+ }
77
+
78
+ /**
79
+ * Aplica um delta aprovado no sistema de arquivos.
80
+ */
81
+ async function applyDelta(delta, projectDir) {
82
+ const absolute = path.isAbsolute(delta.file) ? delta.file : path.resolve(projectDir, delta.file);
83
+
84
+ await fs.mkdir(path.dirname(absolute), { recursive: true });
85
+
86
+ let existing = '';
87
+ try {
88
+ existing = await fs.readFile(absolute, 'utf8');
89
+ } catch {
90
+ existing = '';
91
+ }
92
+
93
+ if (delta.section && existing) {
94
+ if (existing.includes(delta.section)) {
95
+ // Insere após o cabeçalho da seção
96
+ const updated = existing.replace(delta.section, `${delta.section}\n${delta.content}`);
97
+ await fs.writeFile(absolute, updated, 'utf8');
98
+ } else {
99
+ // Adiciona a seção no final
100
+ await fs.writeFile(absolute, `${existing}\n${delta.section}\n${delta.content}\n`, 'utf8');
101
+ }
102
+ } else {
103
+ // Append simples
104
+ await fs.writeFile(absolute, `${existing}\n${delta.content}\n`, 'utf8');
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Ponto de entrada principal.
110
+ */
111
+ async function runLearningEvolve({ args = [], options = {}, logger = console, t = (k) => k } = {}) {
112
+ const projectDir = path.resolve(process.cwd(), args[0] || '.');
113
+ const squadSlug = options.squad || null;
114
+ const dryRun = Boolean(options['dry-run'] || options.dry);
115
+ const autoApply = Boolean(options['auto-apply'] || options.auto);
116
+ const quiet = Boolean(options.quiet);
117
+
118
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
119
+ if (!handle) {
120
+ logger.error('Runtime store não encontrado. Execute aioson runtime:init primeiro.');
121
+ return { ok: false, error: 'no_runtime' };
122
+ }
123
+ const { db } = handle;
124
+
125
+ let learnings;
126
+ try {
127
+ if (squadSlug) {
128
+ learnings = listSquadLearnings(db, squadSlug, 'active');
129
+ } else {
130
+ const squad = listSquadLearnings(db, null, 'active');
131
+ const project = listProjectLearnings(db, 'active');
132
+ learnings = [...squad, ...project];
133
+ }
134
+ } finally {
135
+ db.close();
136
+ }
137
+
138
+ // Filtra por frequência mínima
139
+ const eligible = learnings.filter((l) => Number(l.frequency || 1) >= MIN_FREQUENCY);
140
+
141
+ if (eligible.length === 0) {
142
+ if (!quiet) logger.log('Nenhum learning com frequência suficiente para evoluir (mínimo: 2 ocorrências).');
143
+ return { ok: true, evolved: 0, skipped: 0, proposed: [] };
144
+ }
145
+
146
+ if (!quiet) logger.log(`Analisando ${eligible.length} learnings elegíveis...`);
147
+
148
+ // Agrupa por tipo
149
+ const grouped = {};
150
+ for (const l of eligible) {
151
+ const key = l.type;
152
+ if (!grouped[key]) grouped[key] = [];
153
+ grouped[key].push(l);
154
+ }
155
+
156
+ // Gera deltas
157
+ const proposed = [];
158
+ const rejected = [];
159
+
160
+ for (const [type, group] of Object.entries(grouped)) {
161
+ const { file, section } = resolveDeltaTarget(type, group[0].squad_slug || null);
162
+ const content = buildDeltaContent(group, section);
163
+
164
+ // Gate 1: Constitution
165
+ if (!passesConstitutionGate(file, projectDir)) {
166
+ rejected.push({ type, reason: 'constitution_gate', file, count: group.length });
167
+ continue;
168
+ }
169
+
170
+ // Gate 2: Size
171
+ const sizeOk = await passesSizeGate(file, projectDir, content);
172
+ if (!sizeOk) {
173
+ rejected.push({ type, reason: 'size_gate', file, count: group.length });
174
+ continue;
175
+ }
176
+
177
+ proposed.push({
178
+ type,
179
+ file,
180
+ section,
181
+ content,
182
+ sourceIds: group.map((l) => l.learning_id),
183
+ count: group.length
184
+ });
185
+ }
186
+
187
+ // Exibe resultados
188
+ if (!quiet) {
189
+ logger.log('');
190
+ logger.log(`Deltas propostos: ${proposed.length} aprovados, ${rejected.length} rejeitados`);
191
+ logger.log('');
192
+
193
+ for (let i = 0; i < proposed.length; i++) {
194
+ const d = proposed[i];
195
+ logger.log(` [${i + 1}] APPEND → ${d.file}`);
196
+ if (d.section) logger.log(` Seção: ${d.section}`);
197
+ logger.log(` Learnings: ${d.count} (${d.type})`);
198
+ logger.log(d.content.split('\n').map((l) => ` ${l}`).join('\n'));
199
+ logger.log('');
200
+ }
201
+
202
+ for (const r of rejected) {
203
+ const reason = r.reason === 'constitution_gate' ? 'gate constitucional (arquivo imutável)' : `gate de tamanho (>${MAX_FILE_LINES} linhas)`;
204
+ logger.log(` ✗ Rejeitado [${r.type}] → ${r.file}: ${reason}`);
205
+ }
206
+ }
207
+
208
+ if (proposed.length === 0) {
209
+ return { ok: true, evolved: 0, skipped: rejected.length, proposed: [] };
210
+ }
211
+
212
+ // Dry-run: só exibe, não aplica
213
+ if (dryRun) {
214
+ logger.log('Modo dry-run: nenhuma alteração foi feita.');
215
+ return { ok: true, evolved: 0, skipped: rejected.length, proposed };
216
+ }
217
+
218
+ // Auto-apply: aplica diretamente
219
+ if (autoApply) {
220
+ return applyProposed(proposed, projectDir, db, logger, quiet, squadSlug);
221
+ }
222
+
223
+ // Modo padrão: salva arquivo pendente
224
+ const evolutionDir = path.resolve(projectDir, EVOLUTION_DIR);
225
+ await fs.mkdir(evolutionDir, { recursive: true });
226
+
227
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
228
+ const pendingFile = path.join(evolutionDir, `pending-${timestamp}.json`);
229
+ await fs.writeFile(pendingFile, JSON.stringify({ createdAt: new Date().toISOString(), projectDir, proposed, rejected }, null, 2), 'utf8');
230
+
231
+ if (!quiet) {
232
+ logger.log(`Proposta salva em: ${pendingFile}`);
233
+ logger.log('Para aplicar: aioson learning:apply . --file=' + path.relative(projectDir, pendingFile));
234
+ logger.log('Para aplicar automaticamente: aioson learning:evolve . --auto-apply');
235
+ }
236
+
237
+ return { ok: true, evolved: 0, skipped: rejected.length, proposed, pendingFile };
238
+ }
239
+
240
+ async function applyProposed(proposed, projectDir, db, logger, quiet, squadSlug) {
241
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
242
+ if (!handle) return { ok: false, error: 'no_runtime' };
243
+ const applyDb = handle.db;
244
+
245
+ let evolved = 0;
246
+ try {
247
+ for (const delta of proposed) {
248
+ await applyDelta(delta, projectDir);
249
+
250
+ // Promove learnings no DB
251
+ for (const id of delta.sourceIds) {
252
+ try {
253
+ if (id.startsWith('sl-') || id.startsWith('pl-')) {
254
+ if (id.startsWith('sl-')) {
255
+ promoteSquadLearning(applyDb, id, delta.file);
256
+ } else {
257
+ promoteProjectLearning(applyDb, id, delta.file);
258
+ }
259
+ } else {
260
+ promoteSquadLearning(applyDb, id, delta.file);
261
+ }
262
+ } catch { /* learning pode já ter sido promovido */ }
263
+ }
264
+
265
+ evolved++;
266
+ if (!quiet) logger.log(` ✓ Aplicado: ${delta.file} (+${delta.count} learnings)`);
267
+ }
268
+
269
+ // Registra no evolution-log.jsonl (5.5: per-delta entries with UUIDs for rollback)
270
+ const evolutionDir = path.resolve(projectDir, EVOLUTION_DIR);
271
+ await fs.mkdir(evolutionDir, { recursive: true });
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
+ }
288
+
289
+ if (!quiet) logger.log(`\n${evolved} delta(s) aplicado(s) com sucesso.`);
290
+ } finally {
291
+ applyDb.close();
292
+ }
293
+
294
+ return { ok: true, evolved, skipped: 0, proposed };
295
+ }
296
+
297
+ /**
298
+ * Subcomando: apply — aplica um arquivo de deltas pendentes.
299
+ */
300
+ async function runLearningApply({ args = [], options = {}, logger = console } = {}) {
301
+ const projectDir = path.resolve(process.cwd(), args[0] || '.');
302
+ const filePath = options.file ? String(options.file) : null;
303
+
304
+ if (!filePath) {
305
+ logger.error('--file é obrigatório. Exemplo: aioson learning:apply . --file=.aioson/evolution/pending-XXX.json');
306
+ return { ok: false, error: 'file_required' };
307
+ }
308
+
309
+ const absolute = path.isAbsolute(filePath) ? filePath : path.resolve(projectDir, filePath);
310
+
311
+ let pendingData;
312
+ try {
313
+ pendingData = JSON.parse(await fs.readFile(absolute, 'utf8'));
314
+ } catch (err) {
315
+ logger.error(`Não foi possível ler o arquivo: ${absolute}\n${err.message}`);
316
+ return { ok: false, error: 'file_not_readable' };
317
+ }
318
+
319
+ const { proposed = [], rejected = [] } = pendingData;
320
+
321
+ if (proposed.length === 0) {
322
+ logger.log('Nenhum delta para aplicar neste arquivo.');
323
+ return { ok: true, evolved: 0 };
324
+ }
325
+
326
+ logger.log(`Aplicando ${proposed.length} delta(s)...`);
327
+
328
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
329
+ const db = handle ? handle.db : null;
330
+
331
+ let evolved = 0;
332
+ for (const delta of proposed) {
333
+ try {
334
+ await applyDelta(delta, projectDir);
335
+ if (db) {
336
+ for (const id of (delta.sourceIds || [])) {
337
+ try {
338
+ if (id.startsWith('pl-')) {
339
+ promoteProjectLearning(db, id, delta.file);
340
+ } else {
341
+ promoteSquadLearning(db, id, delta.file);
342
+ }
343
+ } catch { /* ok */ }
344
+ }
345
+ }
346
+ evolved++;
347
+ logger.log(` ✓ ${delta.file}`);
348
+ } catch (err) {
349
+ logger.error(` ✗ Falha em ${delta.file}: ${err.message}`);
350
+ }
351
+ }
352
+
353
+ if (db) db.close();
354
+
355
+ // Remove o arquivo pendente após aplicar
356
+ try { await fs.unlink(absolute); } catch { /* ok */ }
357
+
358
+ logger.log(`\n${evolved}/${proposed.length} delta(s) aplicado(s).`);
359
+ if (rejected.length > 0) logger.log(`${rejected.length} rejeitado(s) previamente pelos gates.`);
360
+
361
+ return { ok: true, evolved };
362
+ }
363
+
364
+ module.exports = { runLearningEvolve, runLearningApply };
@@ -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 };