@jaimevalasek/aioson 1.7.2 → 1.8.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 (362) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +153 -10
  3. package/docs/en/cli-reference.md +56 -1
  4. package/docs/en/i18n.md +18 -18
  5. package/docs/en/schemas/index.json +10 -0
  6. package/docs/en/schemas/parallel-assign.schema.json +9 -0
  7. package/docs/en/schemas/parallel-doctor.schema.json +36 -0
  8. package/docs/en/schemas/parallel-guard.schema.json +63 -0
  9. package/docs/en/schemas/parallel-merge.schema.json +84 -0
  10. package/docs/en/schemas/parallel-status.schema.json +91 -1
  11. package/docs/integrations/apps-publish-marketplace.md +94 -0
  12. package/docs/pt/README.md +9 -0
  13. package/docs/pt/agentes.md +324 -3
  14. package/docs/pt/clientes-ai.md +7 -3
  15. package/docs/pt/comandos-cli.md +160 -13
  16. package/docs/pt/compress-agents.md +304 -0
  17. package/docs/pt/design-docs-governance.md +59 -0
  18. package/docs/pt/feature-archive.md +191 -0
  19. package/docs/pt/genome-3.0-spec.md +115 -4
  20. package/docs/pt/genome-distribution.md +232 -0
  21. package/docs/pt/inicio-rapido.md +1 -0
  22. package/docs/pt/motor-hardening.md +492 -0
  23. package/docs/pt/runner-system.md +113 -0
  24. package/package.json +2 -1
  25. package/src/agent-manifests.js +66 -0
  26. package/src/agents.js +27 -7
  27. package/src/autonomy-policy.js +139 -0
  28. package/src/brain-query.js +161 -0
  29. package/src/cli.js +1377 -1099
  30. package/src/commands/agents.js +102 -7
  31. package/src/commands/artifact-validate.js +33 -4
  32. package/src/commands/auth.js +272 -0
  33. package/src/commands/brain-query.js +44 -0
  34. package/src/commands/briefing.js +344 -0
  35. package/src/commands/commit-prepare.js +547 -0
  36. package/src/commands/compress-agents.js +416 -0
  37. package/src/commands/context-health.js +4 -2
  38. package/src/commands/context-trim.js +17 -11
  39. package/src/commands/design-hybrid-options.js +3 -3
  40. package/src/commands/devlog-process.js +6 -4
  41. package/src/commands/dossier.js +423 -0
  42. package/src/commands/feature-archive.js +513 -0
  43. package/src/commands/feature-close.js +123 -18
  44. package/src/commands/gate-approve.js +198 -0
  45. package/src/commands/gate-check.js +24 -5
  46. package/src/commands/genome-doctor.js +166 -9
  47. package/src/commands/git-guard.js +170 -0
  48. package/src/commands/harness.js +121 -0
  49. package/src/commands/implementation-plan.js +47 -20
  50. package/src/commands/init.js +6 -2
  51. package/src/commands/install.js +6 -2
  52. package/src/commands/live.js +497 -56
  53. package/src/commands/locale-apply.js +9 -6
  54. package/src/commands/locale-diff.js +11 -112
  55. package/src/commands/mcp-doctor.js +2 -1
  56. package/src/commands/mcp-init.js +4 -10
  57. package/src/commands/memory.js +234 -0
  58. package/src/commands/parallel-assign.js +107 -27
  59. package/src/commands/parallel-doctor.js +416 -3
  60. package/src/commands/parallel-guard.js +241 -0
  61. package/src/commands/parallel-init.js +66 -4
  62. package/src/commands/parallel-merge.js +299 -0
  63. package/src/commands/parallel-status.js +147 -3
  64. package/src/commands/preflight.js +63 -4
  65. package/src/commands/qa-init.js +10 -5
  66. package/src/commands/revision.js +235 -0
  67. package/src/commands/scaffold-complete.js +188 -0
  68. package/src/commands/security-audit.js +275 -0
  69. package/src/commands/security-scan.js +376 -0
  70. package/src/commands/self-implement-loop.js +46 -2
  71. package/src/commands/setup-context.js +11 -10
  72. package/src/commands/squad-agent-create.js +51 -9
  73. package/src/commands/squad-investigate.js +53 -0
  74. package/src/commands/squad-plan.js +33 -1
  75. package/src/commands/squad-scaffold.js +4 -3
  76. package/src/commands/squad-score.js +71 -14
  77. package/src/commands/squad-status.js +22 -1
  78. package/src/commands/squad-validate.js +93 -2
  79. package/src/commands/store-genome.js +304 -0
  80. package/src/commands/store-skill.js +247 -0
  81. package/src/commands/store-squad.js +431 -0
  82. package/src/commands/store-system.js +392 -0
  83. package/src/commands/tool-capabilities.js +63 -0
  84. package/src/commands/update.js +3 -3
  85. package/src/commands/verify-gate.js +40 -0
  86. package/src/commands/workflow-execute.js +644 -155
  87. package/src/commands/workflow-harden.js +231 -0
  88. package/src/commands/workflow-heal.js +136 -0
  89. package/src/commands/workflow-next.js +460 -22
  90. package/src/commands/workflow-status.js +328 -138
  91. package/src/commands/workspace.js +144 -0
  92. package/src/constants.js +42 -75
  93. package/src/context-memory.js +133 -4
  94. package/src/context-writer.js +2 -1
  95. package/src/context.js +32 -2
  96. package/src/doctor.js +46 -6
  97. package/src/dossier/codemap-store.js +267 -0
  98. package/src/dossier/dossier-bootstrap.js +222 -0
  99. package/src/dossier/dossier-compact.js +159 -0
  100. package/src/dossier/lock.js +128 -0
  101. package/src/dossier/revision-store.js +313 -0
  102. package/src/dossier/schema.js +155 -0
  103. package/src/dossier/store.js +400 -0
  104. package/src/execution-gateway.js +3 -0
  105. package/src/friction-scanner.js +202 -0
  106. package/src/genome-schema.js +24 -1
  107. package/src/genomes.js +33 -0
  108. package/src/handoff-contract.js +363 -0
  109. package/src/handoff-validator.js +45 -0
  110. package/src/harness/circuit-breaker.js +135 -0
  111. package/src/i18n/messages/en.js +317 -22
  112. package/src/i18n/messages/es.js +259 -18
  113. package/src/i18n/messages/fr.js +260 -18
  114. package/src/i18n/messages/pt-BR.js +313 -22
  115. package/src/install-profile.js +0 -16
  116. package/src/installer.js +70 -6
  117. package/src/lib/git-commit-guard.js +691 -0
  118. package/src/lib/security/artifact-reader.js +167 -0
  119. package/src/lib/security/exit-codes.js +51 -0
  120. package/src/lib/security/findings-writer.js +176 -0
  121. package/src/lib/security/runtime-events.js +77 -0
  122. package/src/lib/security/secrets-regex.js +115 -0
  123. package/src/lib/store/security-scan.js +173 -0
  124. package/src/lib/terminal-checkbox.js +130 -0
  125. package/src/lib/tmux-launcher.js +163 -0
  126. package/src/lib/tool-capabilities.js +102 -0
  127. package/src/locales.js +12 -8
  128. package/src/parallel-workspace.js +756 -0
  129. package/src/parser.js +8 -1
  130. package/src/path-guard.js +47 -0
  131. package/src/preflight-engine.js +237 -26
  132. package/src/self-healing.js +142 -0
  133. package/src/session-handoff.js +111 -1
  134. package/src/squad/squad-scaffold.js +183 -19
  135. package/src/test-briefing.js +226 -0
  136. package/src/updater.js +1 -1
  137. package/src/utils.js +3 -0
  138. package/src/workflow-gates.js +185 -0
  139. package/template/.aioson/agents/analyst.md +76 -130
  140. package/template/.aioson/agents/architect.md +53 -86
  141. package/template/.aioson/agents/committer.md +161 -0
  142. package/template/.aioson/agents/cypher.md +252 -0
  143. package/template/.aioson/agents/dev.md +112 -628
  144. package/template/.aioson/agents/deyvin.md +33 -236
  145. package/template/.aioson/agents/discover.md +235 -0
  146. package/template/.aioson/agents/discovery-design-doc.md +17 -252
  147. package/template/.aioson/agents/genome.md +76 -26
  148. package/template/.aioson/agents/manifests/analyst.manifest.json +26 -0
  149. package/template/.aioson/agents/manifests/architect.manifest.json +23 -0
  150. package/template/.aioson/agents/manifests/committer.manifest.json +23 -0
  151. package/template/.aioson/agents/manifests/dev.manifest.json +37 -0
  152. package/template/.aioson/agents/manifests/orchestrator.manifest.json +30 -0
  153. package/template/.aioson/agents/manifests/pentester.manifest.json +39 -0
  154. package/template/.aioson/agents/manifests/pm.manifest.json +26 -0
  155. package/template/.aioson/agents/manifests/product.manifest.json +23 -0
  156. package/template/.aioson/agents/manifests/qa.manifest.json +25 -0
  157. package/template/.aioson/agents/manifests/setup.manifest.json +20 -0
  158. package/template/.aioson/agents/manifests/ux-ui.manifest.json +24 -0
  159. package/template/.aioson/agents/neo.md +5 -7
  160. package/template/.aioson/agents/orache.md +2 -6
  161. package/template/.aioson/agents/orchestrator.md +81 -182
  162. package/template/.aioson/agents/pentester.md +235 -0
  163. package/template/.aioson/agents/pm.md +40 -104
  164. package/template/.aioson/agents/product.md +99 -344
  165. package/template/.aioson/agents/profiler-enricher.md +57 -6
  166. package/template/.aioson/agents/profiler-forge.md +17 -7
  167. package/template/.aioson/agents/profiler-researcher.md +29 -6
  168. package/template/.aioson/agents/qa.md +168 -514
  169. package/template/.aioson/agents/setup.md +52 -278
  170. package/template/.aioson/agents/sheldon.md +122 -754
  171. package/template/.aioson/agents/site-forge.md +111 -1583
  172. package/template/.aioson/agents/squad.md +139 -2010
  173. package/template/.aioson/agents/tester.md +10 -0
  174. package/template/.aioson/agents/ux-ui.md +104 -812
  175. package/template/.aioson/agents/validator.md +69 -0
  176. package/template/.aioson/brains/scripts/query.js +5 -1
  177. package/template/.aioson/config/autonomy-protocol.json +43 -0
  178. package/template/.aioson/config.md +43 -15
  179. package/template/.aioson/constitution.md +36 -33
  180. package/template/.aioson/context/design-doc.md +136 -0
  181. package/template/.aioson/context/project-map.md +57 -0
  182. package/template/.aioson/design-docs/code-reuse.md +48 -0
  183. package/template/.aioson/design-docs/componentization.md +47 -0
  184. package/template/.aioson/design-docs/file-size.md +52 -0
  185. package/template/.aioson/design-docs/folder-structure.md +51 -0
  186. package/template/.aioson/design-docs/naming.md +54 -0
  187. package/template/.aioson/docs/LAYERS.md +12 -2
  188. package/template/.aioson/docs/dev/execution-discipline.md +106 -0
  189. package/template/.aioson/docs/dev/stack-conventions.md +83 -0
  190. package/template/.aioson/docs/deyvin/continuity-recovery.md +57 -0
  191. package/template/.aioson/docs/deyvin/debugging-escalation.md +30 -0
  192. package/template/.aioson/docs/deyvin/pair-execution.md +44 -0
  193. package/template/.aioson/docs/deyvin/runtime-handoffs.md +36 -0
  194. package/template/.aioson/docs/product/conversation-playbook.md +116 -0
  195. package/template/.aioson/docs/product/prd-contract.md +107 -0
  196. package/template/.aioson/docs/product/quality-lens.md +57 -0
  197. package/template/.aioson/docs/product/research-loop.md +65 -0
  198. package/template/.aioson/docs/sheldon/enrichment-paths.md +134 -0
  199. package/template/.aioson/docs/sheldon/quality-lens.md +57 -0
  200. package/template/.aioson/docs/sheldon/research-loop.md +56 -0
  201. package/template/.aioson/docs/sheldon/web-intelligence.md +75 -0
  202. package/template/.aioson/docs/site-forge-build.md +195 -0
  203. package/template/.aioson/docs/site-forge-extraction.md +135 -0
  204. package/template/.aioson/docs/site-forge-qa.md +155 -0
  205. package/template/.aioson/docs/site-forge-recon.md +434 -0
  206. package/template/.aioson/docs/site-forge-transform.md +249 -0
  207. package/template/.aioson/docs/squad/content-output.md +91 -0
  208. package/template/.aioson/docs/squad/creation-flow.md +135 -0
  209. package/template/.aioson/docs/squad/domain-classification.md +117 -0
  210. package/template/.aioson/docs/squad/genome-bindings.md +47 -0
  211. package/template/.aioson/docs/squad/package-contract.md +234 -0
  212. package/template/.aioson/docs/squad/quality-lens.md +56 -0
  213. package/template/.aioson/docs/squad/research-loop.md +59 -0
  214. package/template/.aioson/docs/squad/session-operations.md +117 -0
  215. package/template/.aioson/docs/squad/workflow-quality.md +165 -0
  216. package/template/.aioson/docs/ux-ui/accessibility-audit.md +55 -0
  217. package/template/.aioson/docs/ux-ui/audit-mode.md +86 -0
  218. package/template/.aioson/docs/ux-ui/component-map.md +35 -0
  219. package/template/.aioson/docs/ux-ui/design-execution.md +111 -0
  220. package/template/.aioson/docs/ux-ui/design-gate.md +27 -0
  221. package/template/.aioson/docs/ux-ui/research-mode.md +39 -0
  222. package/template/.aioson/docs/ux-ui/site-delivery.md +156 -0
  223. package/template/.aioson/docs/ux-ui/token-contract.md +57 -0
  224. package/template/.aioson/genomes/copywriting.meta.json +48 -0
  225. package/template/.aioson/git-guard.json +11 -0
  226. package/template/.aioson/mcp/servers.md +0 -1
  227. package/template/.aioson/rules/agent-language-policy.md +93 -0
  228. package/template/.aioson/rules/aioson-context-boundary.md +63 -0
  229. package/template/.aioson/rules/canonical-path-contract.md +47 -0
  230. package/template/.aioson/rules/data-format-convention.md +24 -86
  231. package/template/.aioson/rules/disk-first-artifacts.md +44 -0
  232. package/template/.aioson/rules/output-brevity.md +44 -0
  233. package/template/.aioson/rules/prd-section-ownership.md +49 -0
  234. package/template/.aioson/rules/security-baseline.md +139 -0
  235. package/template/.aioson/rules/spec-level-ownership.md +61 -0
  236. package/template/.aioson/rules/squad-driver-pattern.md +81 -0
  237. package/template/.aioson/schemas/squad-blueprint.schema.json +24 -0
  238. package/template/.aioson/schemas/squad-manifest.schema.json +44 -0
  239. package/template/.aioson/skills/process/aioson-spec-driven/references/pm.md +30 -0
  240. package/template/.aioson/skills/process/secure-tdd/SKILL.md +97 -0
  241. package/template/.aioson/skills/process/secure-tdd/references/nextjs.md +81 -0
  242. package/template/.aioson/skills/process/secure-tdd/references/node-express.md +91 -0
  243. package/template/.aioson/skills/process/secure-tdd/references/planned-stacks.md +33 -0
  244. package/template/.aioson/skills/static/harness-validate/SKILL.md +46 -0
  245. package/template/.aioson/skills/static/web-research-cache.md +3 -0
  246. package/template/.aioson/tasks/squad-create.md +35 -8
  247. package/template/.aioson/tasks/squad-design.md +50 -2
  248. package/template/.aioson/tasks/squad-investigate.md +14 -1
  249. package/template/.claude/commands/aioson/agent/committer.md +5 -0
  250. package/template/.claude/commands/aioson/agent/copywriter.md +5 -0
  251. package/template/.claude/commands/aioson/agent/cypher.md +5 -0
  252. package/template/.claude/commands/aioson/agent/pair.md +5 -0
  253. package/template/.claude/commands/aioson/agent/validator.md +5 -0
  254. package/template/.gemini/commands/aios-analyst.toml +6 -3
  255. package/template/.gemini/commands/aios-architect.toml +7 -6
  256. package/template/.gemini/commands/aios-committer.toml +7 -0
  257. package/template/.gemini/commands/aios-copywriter.toml +7 -0
  258. package/template/.gemini/commands/aios-cypher.toml +7 -0
  259. package/template/.gemini/commands/aios-dev.toml +8 -7
  260. package/template/.gemini/commands/aios-deyvin.toml +6 -5
  261. package/template/.gemini/commands/aios-discovery-design-doc.toml +6 -3
  262. package/template/.gemini/commands/aios-genome.toml +7 -0
  263. package/template/.gemini/commands/aios-neo.toml +5 -3
  264. package/template/.gemini/commands/aios-orache.toml +7 -0
  265. package/template/.gemini/commands/aios-orchestrator.toml +8 -7
  266. package/template/.gemini/commands/aios-pair.toml +6 -5
  267. package/template/.gemini/commands/aios-pm.toml +8 -7
  268. package/template/.gemini/commands/aios-product.toml +5 -3
  269. package/template/.gemini/commands/aios-qa.toml +6 -5
  270. package/template/.gemini/commands/aios-setup.toml +5 -2
  271. package/template/.gemini/commands/aios-sheldon.toml +7 -0
  272. package/template/.gemini/commands/aios-site-forge.toml +7 -0
  273. package/template/.gemini/commands/aios-squad.toml +7 -0
  274. package/template/.gemini/commands/aios-tester.toml +6 -5
  275. package/template/.gemini/commands/aios-ux-ui.toml +8 -7
  276. package/template/.gemini/commands/aios-validator.toml +7 -0
  277. package/template/AGENTS.md +12 -1
  278. package/template/CLAUDE.md +5 -1
  279. package/template/.aioson/locales/en/agents/analyst.md +0 -244
  280. package/template/.aioson/locales/en/agents/architect.md +0 -245
  281. package/template/.aioson/locales/en/agents/dev.md +0 -397
  282. package/template/.aioson/locales/en/agents/deyvin.md +0 -137
  283. package/template/.aioson/locales/en/agents/discovery-design-doc.md +0 -27
  284. package/template/.aioson/locales/en/agents/genome.md +0 -212
  285. package/template/.aioson/locales/en/agents/neo.md +0 -8
  286. package/template/.aioson/locales/en/agents/orache.md +0 -6
  287. package/template/.aioson/locales/en/agents/orchestrator.md +0 -189
  288. package/template/.aioson/locales/en/agents/pair.md +0 -5
  289. package/template/.aioson/locales/en/agents/pm.md +0 -84
  290. package/template/.aioson/locales/en/agents/product.md +0 -378
  291. package/template/.aioson/locales/en/agents/profiler-enricher.md +0 -5
  292. package/template/.aioson/locales/en/agents/profiler-forge.md +0 -5
  293. package/template/.aioson/locales/en/agents/profiler-researcher.md +0 -5
  294. package/template/.aioson/locales/en/agents/qa.md +0 -270
  295. package/template/.aioson/locales/en/agents/setup.md +0 -421
  296. package/template/.aioson/locales/en/agents/sheldon.md +0 -455
  297. package/template/.aioson/locales/en/agents/squad.md +0 -449
  298. package/template/.aioson/locales/en/agents/tester.md +0 -6
  299. package/template/.aioson/locales/en/agents/ux-ui.md +0 -668
  300. package/template/.aioson/locales/es/agents/analyst.md +0 -225
  301. package/template/.aioson/locales/es/agents/architect.md +0 -245
  302. package/template/.aioson/locales/es/agents/dev.md +0 -370
  303. package/template/.aioson/locales/es/agents/deyvin.md +0 -99
  304. package/template/.aioson/locales/es/agents/discovery-design-doc.md +0 -21
  305. package/template/.aioson/locales/es/agents/genome.md +0 -104
  306. package/template/.aioson/locales/es/agents/neo.md +0 -50
  307. package/template/.aioson/locales/es/agents/orache.md +0 -105
  308. package/template/.aioson/locales/es/agents/orchestrator.md +0 -194
  309. package/template/.aioson/locales/es/agents/pair.md +0 -7
  310. package/template/.aioson/locales/es/agents/pm.md +0 -90
  311. package/template/.aioson/locales/es/agents/product.md +0 -372
  312. package/template/.aioson/locales/es/agents/profiler-enricher.md +0 -7
  313. package/template/.aioson/locales/es/agents/profiler-forge.md +0 -7
  314. package/template/.aioson/locales/es/agents/profiler-researcher.md +0 -7
  315. package/template/.aioson/locales/es/agents/qa.md +0 -198
  316. package/template/.aioson/locales/es/agents/setup.md +0 -405
  317. package/template/.aioson/locales/es/agents/sheldon.md +0 -309
  318. package/template/.aioson/locales/es/agents/squad.md +0 -532
  319. package/template/.aioson/locales/es/agents/tester.md +0 -9
  320. package/template/.aioson/locales/es/agents/ux-ui.md +0 -212
  321. package/template/.aioson/locales/fr/agents/analyst.md +0 -225
  322. package/template/.aioson/locales/fr/agents/architect.md +0 -245
  323. package/template/.aioson/locales/fr/agents/dev.md +0 -370
  324. package/template/.aioson/locales/fr/agents/deyvin.md +0 -99
  325. package/template/.aioson/locales/fr/agents/discovery-design-doc.md +0 -21
  326. package/template/.aioson/locales/fr/agents/genome.md +0 -104
  327. package/template/.aioson/locales/fr/agents/neo.md +0 -50
  328. package/template/.aioson/locales/fr/agents/orache.md +0 -106
  329. package/template/.aioson/locales/fr/agents/orchestrator.md +0 -194
  330. package/template/.aioson/locales/fr/agents/pair.md +0 -7
  331. package/template/.aioson/locales/fr/agents/pm.md +0 -90
  332. package/template/.aioson/locales/fr/agents/product.md +0 -372
  333. package/template/.aioson/locales/fr/agents/profiler-enricher.md +0 -7
  334. package/template/.aioson/locales/fr/agents/profiler-forge.md +0 -7
  335. package/template/.aioson/locales/fr/agents/profiler-researcher.md +0 -7
  336. package/template/.aioson/locales/fr/agents/qa.md +0 -198
  337. package/template/.aioson/locales/fr/agents/setup.md +0 -405
  338. package/template/.aioson/locales/fr/agents/sheldon.md +0 -309
  339. package/template/.aioson/locales/fr/agents/squad.md +0 -532
  340. package/template/.aioson/locales/fr/agents/tester.md +0 -9
  341. package/template/.aioson/locales/fr/agents/ux-ui.md +0 -212
  342. package/template/.aioson/locales/pt-BR/agents/analyst.md +0 -319
  343. package/template/.aioson/locales/pt-BR/agents/architect.md +0 -284
  344. package/template/.aioson/locales/pt-BR/agents/dev.md +0 -483
  345. package/template/.aioson/locales/pt-BR/agents/deyvin.md +0 -184
  346. package/template/.aioson/locales/pt-BR/agents/discovery-design-doc.md +0 -198
  347. package/template/.aioson/locales/pt-BR/agents/genome.md +0 -297
  348. package/template/.aioson/locales/pt-BR/agents/neo.md +0 -208
  349. package/template/.aioson/locales/pt-BR/agents/orache.md +0 -137
  350. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +0 -324
  351. package/template/.aioson/locales/pt-BR/agents/pair.md +0 -5
  352. package/template/.aioson/locales/pt-BR/agents/pm.md +0 -182
  353. package/template/.aioson/locales/pt-BR/agents/product.md +0 -466
  354. package/template/.aioson/locales/pt-BR/agents/profiler-enricher.md +0 -5
  355. package/template/.aioson/locales/pt-BR/agents/profiler-forge.md +0 -5
  356. package/template/.aioson/locales/pt-BR/agents/profiler-researcher.md +0 -5
  357. package/template/.aioson/locales/pt-BR/agents/qa.md +0 -300
  358. package/template/.aioson/locales/pt-BR/agents/setup.md +0 -533
  359. package/template/.aioson/locales/pt-BR/agents/sheldon.md +0 -323
  360. package/template/.aioson/locales/pt-BR/agents/squad.md +0 -1330
  361. package/template/.aioson/locales/pt-BR/agents/tester.md +0 -449
  362. package/template/.aioson/locales/pt-BR/agents/ux-ui.md +0 -669
@@ -0,0 +1,304 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { exists } = require('../utils');
6
+ const { readConfig } = require('./config');
7
+ const { readWorkspace, findProjectRoot } = require('./workspace');
8
+ const { scanPackage, formatScanReport } = require('../lib/store/security-scan');
9
+
10
+ const DEFAULT_BASE_URL = 'https://aioson.com';
11
+ const GENOMES_DIR = '.aioson/genomes';
12
+
13
+ function resolveBaseUrl(config) {
14
+ return String(config.aiosonBaseUrl || DEFAULT_BASE_URL).replace(/\/+$/, '');
15
+ }
16
+
17
+ function requireToken(config, t) {
18
+ const token = config.aiosonToken;
19
+ if (!token) throw new Error(t('store.error_not_authenticated'));
20
+ return token;
21
+ }
22
+
23
+ async function storeGet(url, token) {
24
+ const response = await fetch(url, {
25
+ headers: {
26
+ authorization: `Bearer ${token}`,
27
+ accept: 'application/json'
28
+ },
29
+ signal: AbortSignal.timeout(15000)
30
+ });
31
+ const text = await response.text();
32
+ let parsed = null;
33
+ try { parsed = JSON.parse(text); } catch { /* */ }
34
+ if (!response.ok) {
35
+ const detail = (parsed && parsed.error) ? String(parsed.error) : `${response.status} ${response.statusText}`;
36
+ throw new Error(`HTTP ${response.status}: ${detail}`);
37
+ }
38
+ return parsed;
39
+ }
40
+
41
+ async function storePost(url, payload, token) {
42
+ const response = await fetch(url, {
43
+ method: 'POST',
44
+ headers: {
45
+ authorization: `Bearer ${token}`,
46
+ 'content-type': 'application/json',
47
+ accept: 'application/json'
48
+ },
49
+ body: JSON.stringify(payload),
50
+ signal: AbortSignal.timeout(30000)
51
+ });
52
+
53
+ const text = await response.text();
54
+ let parsed = null;
55
+ try { parsed = JSON.parse(text); } catch { /* */ }
56
+
57
+ if (!response.ok) {
58
+ const detail = (parsed && parsed.error) ? String(parsed.error) : `${response.status} ${response.statusText}`;
59
+ throw new Error(`HTTP ${response.status}: ${detail}`);
60
+ }
61
+
62
+ return parsed;
63
+ }
64
+
65
+ async function collectRefs(refsDir) {
66
+ const refs = {};
67
+ if (!(await exists(refsDir))) return refs;
68
+ const entries = await fs.readdir(refsDir);
69
+ for (const entry of entries) {
70
+ try {
71
+ refs[entry] = await fs.readFile(path.join(refsDir, entry), 'utf8');
72
+ } catch { /* */ }
73
+ }
74
+ return refs;
75
+ }
76
+
77
+ // ── genome:publish ──────────────────────────────────────────────────────────
78
+
79
+ async function runGenomePublish({ args, options, logger, t }) {
80
+ const config = await readConfig();
81
+ const token = requireToken(config, t);
82
+ const projectDir = await findProjectRoot(path.resolve(process.cwd(), args[0] || '.'));
83
+ const slug = String(options.slug || '').trim();
84
+ if (!slug) throw new Error(t('store.error_missing_slug'));
85
+
86
+ const genomePath = path.join(projectDir, GENOMES_DIR, `${slug}.md`);
87
+ const metaPath = path.join(projectDir, GENOMES_DIR, `${slug}.meta.json`);
88
+ const refsDir = path.join(projectDir, GENOMES_DIR, `${slug}.refs`);
89
+
90
+ if (!(await exists(genomePath))) {
91
+ throw new Error(t('store.error_genome_not_found', { slug, path: genomePath }));
92
+ }
93
+
94
+ logger.log(t('store.publish_genome_validating'));
95
+ const content = await fs.readFile(genomePath, 'utf8');
96
+
97
+ let meta = {};
98
+ if (await exists(metaPath)) {
99
+ try { meta = JSON.parse(await fs.readFile(metaPath, 'utf8')); } catch { /* */ }
100
+ }
101
+
102
+ const refs = await collectRefs(refsDir);
103
+
104
+ // Security scan
105
+ const allFiles = { [`${slug}.md`]: content };
106
+ if (meta) allFiles[`${slug}.meta.json`] = JSON.stringify(meta);
107
+ for (const [k, v] of Object.entries(refs)) allFiles[`refs/${k}`] = v;
108
+
109
+ const scan = scanPackage(allFiles, 'genome');
110
+ formatScanReport(scan, logger);
111
+ if (!scan.ok) throw new Error(t('store.error_scan_failed'));
112
+ if (scan.warnings.length > 0 && !options.force) {
113
+ throw new Error(t('store.error_scan_warnings', { count: scan.warnings.length }));
114
+ }
115
+
116
+ const ws = await readWorkspace(projectDir);
117
+ const visibility = options.private ? 'private' : 'public';
118
+ const paid = Boolean(options.paid);
119
+
120
+ if (options['dry-run']) {
121
+ logger.log(t('store.publish_dry_run', { type: 'genome', slug, visibility }));
122
+ logger.log(t('store.publish_scan_ok', { hash: scan.hash.slice(0, 12) }));
123
+ return { ok: true, dryRun: true, slug, visibility, paid, hash: scan.hash };
124
+ }
125
+
126
+ logger.log(t('store.publish_scan_ok', { hash: scan.hash.slice(0, 12) }));
127
+ logger.log(t('store.publish_genome_sending'));
128
+ const baseUrl = resolveBaseUrl(config);
129
+ const response = await storePost(`${baseUrl}/api/store/genomes/publish`, {
130
+ kind: 'aioson.store.genome',
131
+ slug,
132
+ content,
133
+ meta,
134
+ refs,
135
+ visibility,
136
+ paid,
137
+ hash: scan.hash,
138
+ workspaceSlug: ws?.slug || null
139
+ }, token);
140
+
141
+ logger.log(t('store.publish_genome_done', { slug, url: `${baseUrl}/store/genomes/${slug}` }));
142
+ return { ok: true, slug, visibility, paid, response };
143
+ }
144
+
145
+ // ── genome:install (store) ──────────────────────────────────────────────────
146
+
147
+ async function runGenomeInstallStore({ args, options, logger, t }) {
148
+ const config = await readConfig();
149
+ const token = requireToken(config, t);
150
+ const projectDir = await findProjectRoot(path.resolve(process.cwd(), args[0] || '.'));
151
+ const baseUrl = resolveBaseUrl(config);
152
+
153
+ // Accept: --slug=X or positional code/slug
154
+ const ref = String(options.slug || options.code || args[1] || args[0] || '').trim();
155
+ if (!ref) throw new Error(t('store.error_missing_code_or_slug'));
156
+
157
+ const ws = await readWorkspace(projectDir);
158
+ logger.log(t('store.install_genome_fetching', { ref }));
159
+
160
+ const response = await storePost(`${baseUrl}/api/store/genomes/install`, {
161
+ ref,
162
+ workspaceSlug: ws?.slug || null
163
+ }, token);
164
+
165
+ const slug = response.slug;
166
+ if (!slug || !response.content) {
167
+ throw new Error(t('store.error_invalid_response'));
168
+ }
169
+
170
+ const destPath = path.join(projectDir, GENOMES_DIR, `${slug}.md`);
171
+ const metaDestPath = path.join(projectDir, GENOMES_DIR, `${slug}.meta.json`);
172
+
173
+ // Backup existing version if present
174
+ if ((await exists(destPath)) && !options.force) {
175
+ const backupPath = path.join(projectDir, GENOMES_DIR, `${slug}.backup.md`);
176
+ logger.log(t('store.install_backing_up', { path: backupPath }));
177
+ await fs.copyFile(destPath, backupPath);
178
+ }
179
+
180
+ await fs.mkdir(path.dirname(destPath), { recursive: true });
181
+ await fs.writeFile(destPath, response.content, 'utf8');
182
+
183
+ if (response.meta) {
184
+ await fs.writeFile(metaDestPath, `${JSON.stringify(response.meta, null, 2)}\n`, 'utf8');
185
+ }
186
+
187
+ // Write refs if present
188
+ if (response.refs && typeof response.refs === 'object') {
189
+ const refsDir = path.join(projectDir, GENOMES_DIR, `${slug}.refs`);
190
+ await fs.mkdir(refsDir, { recursive: true });
191
+ for (const [name, content] of Object.entries(response.refs)) {
192
+ await fs.writeFile(path.join(refsDir, name), content, 'utf8');
193
+ }
194
+ }
195
+
196
+ logger.log(t('store.install_genome_done', { slug, path: destPath }));
197
+ return { ok: true, slug, path: destPath };
198
+ }
199
+
200
+ // ── genome:install (user-facing alias for genome:install:store) ─────────────
201
+
202
+ async function runGenomeInstall({ args, options, logger, t }) {
203
+ // Accept: aioson genome:install <code-or-slug> or --slug=X or --code=X
204
+ const ref = String(options.slug || options.code || args[0] || '').trim();
205
+ if (!ref) throw new Error(t('store.error_missing_code_or_slug'));
206
+ return runGenomeInstallStore({ args, options: { ...options, slug: ref }, logger, t });
207
+ }
208
+
209
+ // ── genome:list ─────────────────────────────────────────────────────────────
210
+
211
+ async function runGenomeList({ args, options, logger, t }) {
212
+ // --remote: list published genomes on aioson.com
213
+ if (options.remote) {
214
+ const config = await readConfig();
215
+ const token = requireToken(config, t);
216
+ const baseUrl = resolveBaseUrl(config);
217
+ logger.log(t('store.list_remote_fetching', { type: 'genomes' }));
218
+ const response = await storeGet(`${baseUrl}/api/store/genomes`, token);
219
+ const genomes = response.genomes || [];
220
+ if (genomes.length === 0) {
221
+ logger.log(t('store.list_remote_empty', { type: 'genomes' }));
222
+ } else {
223
+ logger.log(t('store.list_remote_header', { count: genomes.length, type: 'genomes' }));
224
+ for (const g of genomes) {
225
+ logger.log(t('store.list_remote_item', { slug: g.slug, name: g.name || g.slug, visibility: g.visibility || '?' }));
226
+ }
227
+ }
228
+ return { ok: true, genomes, remote: true };
229
+ }
230
+
231
+ // local list
232
+ const projectDir = await findProjectRoot(path.resolve(process.cwd(), args[0] || '.'));
233
+ const genomesDir = path.join(projectDir, GENOMES_DIR);
234
+
235
+ if (!(await exists(genomesDir))) {
236
+ logger.log(t('store.list_genome_empty'));
237
+ return { ok: true, genomes: [] };
238
+ }
239
+
240
+ const entries = await fs.readdir(genomesDir);
241
+ const genomes = [];
242
+
243
+ for (const entry of entries) {
244
+ if (!entry.endsWith('.md') || entry.endsWith('.backup.md')) continue;
245
+ const slug = entry.replace(/\.md$/, '');
246
+ const metaPath = path.join(genomesDir, `${slug}.meta.json`);
247
+ let meta = {};
248
+ try {
249
+ if (await exists(metaPath)) {
250
+ meta = JSON.parse(await fs.readFile(metaPath, 'utf8'));
251
+ }
252
+ } catch { /* */ }
253
+ genomes.push({ slug, version: meta.version || null, name: meta.name || slug });
254
+ }
255
+
256
+ if (genomes.length === 0) {
257
+ logger.log(t('store.list_genome_empty'));
258
+ } else {
259
+ logger.log(t('store.list_genome_header', { count: genomes.length }));
260
+ for (const g of genomes) {
261
+ const ver = g.version ? ` (v${g.version})` : '';
262
+ logger.log(t('store.list_genome_item', { slug: g.slug, name: g.name, version: ver }));
263
+ }
264
+ }
265
+
266
+ return { ok: true, genomes };
267
+ }
268
+
269
+ // ── genome:remove ────────────────────────────────────────────────────────────
270
+
271
+ async function runGenomeRemove({ args, options, logger, t }) {
272
+ const projectDir = await findProjectRoot(path.resolve(process.cwd(), args[0] || '.'));
273
+ const slug = String(options.slug || args[0] || '').trim();
274
+ if (!slug) throw new Error(t('store.error_missing_slug'));
275
+
276
+ const genomesDir = path.join(projectDir, GENOMES_DIR);
277
+ const genomePath = path.join(genomesDir, `${slug}.md`);
278
+ const metaPath = path.join(genomesDir, `${slug}.meta.json`);
279
+ const refsDir = path.join(genomesDir, `${slug}.refs`);
280
+
281
+ if (!(await exists(genomePath))) {
282
+ throw new Error(t('store.error_genome_not_found', { slug, path: genomePath }));
283
+ }
284
+
285
+ if (!options.force) {
286
+ // Backup before removing
287
+ const backupPath = path.join(genomesDir, `${slug}.backup.md`);
288
+ await fs.copyFile(genomePath, backupPath);
289
+ logger.log(t('store.install_backing_up', { path: backupPath }));
290
+ }
291
+
292
+ await fs.unlink(genomePath);
293
+ if (await exists(metaPath)) await fs.unlink(metaPath);
294
+ if (await exists(refsDir)) {
295
+ const refs = await fs.readdir(refsDir);
296
+ for (const r of refs) await fs.unlink(path.join(refsDir, r));
297
+ await fs.rmdir(refsDir);
298
+ }
299
+
300
+ logger.log(t('store.remove_genome_done', { slug }));
301
+ return { ok: true, slug };
302
+ }
303
+
304
+ module.exports = { runGenomePublish, runGenomeInstallStore, runGenomeInstall, runGenomeList, runGenomeRemove };
@@ -0,0 +1,247 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { exists } = require('../utils');
6
+ const { readConfig } = require('./config');
7
+ const { readWorkspace, findProjectRoot } = require('./workspace');
8
+ const { scanPackage, formatScanReport } = require('../lib/store/security-scan');
9
+
10
+ const DEFAULT_BASE_URL = 'https://aioson.com';
11
+ const SKILLS_DIRS = ['.aioson/skills', '.aioson/installed-skills'];
12
+
13
+ function resolveBaseUrl(config) {
14
+ return String(config.aiosonBaseUrl || DEFAULT_BASE_URL).replace(/\/+$/, '');
15
+ }
16
+
17
+ function requireToken(config, t) {
18
+ const token = config.aiosonToken;
19
+ if (!token) throw new Error(t('store.error_not_authenticated'));
20
+ return token;
21
+ }
22
+
23
+ async function storeGet(url, token) {
24
+ const response = await fetch(url, {
25
+ headers: { authorization: `Bearer ${token}`, accept: 'application/json' },
26
+ signal: AbortSignal.timeout(15000)
27
+ });
28
+ const text = await response.text();
29
+ let parsed = null;
30
+ try { parsed = JSON.parse(text); } catch { /* */ }
31
+ if (!response.ok) {
32
+ const detail = (parsed && parsed.error) ? String(parsed.error) : `${response.status} ${response.statusText}`;
33
+ throw new Error(`HTTP ${response.status}: ${detail}`);
34
+ }
35
+ return parsed;
36
+ }
37
+
38
+ async function storePost(url, payload, token) {
39
+ const response = await fetch(url, {
40
+ method: 'POST',
41
+ headers: {
42
+ authorization: `Bearer ${token}`,
43
+ 'content-type': 'application/json',
44
+ accept: 'application/json'
45
+ },
46
+ body: JSON.stringify(payload),
47
+ signal: AbortSignal.timeout(30000)
48
+ });
49
+
50
+ const text = await response.text();
51
+ let parsed = null;
52
+ try { parsed = JSON.parse(text); } catch { /* */ }
53
+
54
+ if (!response.ok) {
55
+ const detail = (parsed && parsed.error) ? String(parsed.error) : `${response.status} ${response.statusText}`;
56
+ throw new Error(`HTTP ${response.status}: ${detail}`);
57
+ }
58
+
59
+ return parsed;
60
+ }
61
+
62
+ /**
63
+ * Collect all files in a skill directory recursively.
64
+ * Returns { relativePath: fileContent } for text files.
65
+ */
66
+ async function collectSkillFiles(skillDir) {
67
+ const files = {};
68
+
69
+ async function walk(dir, base) {
70
+ const entries = await fs.readdir(dir, { withFileTypes: true });
71
+ for (const entry of entries) {
72
+ const fullPath = path.join(dir, entry.name);
73
+ const relPath = base ? `${base}/${entry.name}` : entry.name;
74
+ if (entry.isDirectory()) {
75
+ await walk(fullPath, relPath);
76
+ } else {
77
+ try {
78
+ files[relPath] = await fs.readFile(fullPath, 'utf8');
79
+ } catch { /* binary files skipped */ }
80
+ }
81
+ }
82
+ }
83
+
84
+ await walk(skillDir, '');
85
+ return files;
86
+ }
87
+
88
+ async function findSkillDir(projectDir, slug) {
89
+ for (const base of SKILLS_DIRS) {
90
+ const skillDir = path.join(projectDir, base, slug);
91
+ if (await exists(path.join(skillDir, 'SKILL.md'))) {
92
+ return skillDir;
93
+ }
94
+ }
95
+ return null;
96
+ }
97
+
98
+ // ── skill:publish ───────────────────────────────────────────────────────────
99
+
100
+ async function runSkillPublish({ args, options, logger, t }) {
101
+ const config = await readConfig();
102
+ const token = requireToken(config, t);
103
+ const projectDir = await findProjectRoot(path.resolve(process.cwd(), args[0] || '.'));
104
+ const slug = String(options.slug || '').trim();
105
+ if (!slug) throw new Error(t('store.error_missing_slug'));
106
+
107
+ const skillDir = await findSkillDir(projectDir, slug);
108
+ if (!skillDir) {
109
+ throw new Error(t('store.error_skill_not_found', { slug }));
110
+ }
111
+
112
+ logger.log(t('store.publish_skill_collecting'));
113
+ const files = await collectSkillFiles(skillDir);
114
+ const fileCount = Object.keys(files).length;
115
+
116
+ if (!files['SKILL.md']) {
117
+ throw new Error(t('store.error_skill_missing_skillmd', { slug }));
118
+ }
119
+
120
+ // Security scan
121
+ const scan = scanPackage(files, 'skill');
122
+ formatScanReport(scan, logger);
123
+ if (!scan.ok) throw new Error(t('store.error_scan_failed'));
124
+ if (scan.warnings.length > 0 && !options.force) {
125
+ throw new Error(t('store.error_scan_warnings', { count: scan.warnings.length }));
126
+ }
127
+
128
+ const ws = await readWorkspace(projectDir);
129
+ const visibility = options.private ? 'private' : 'public';
130
+ const paid = Boolean(options.paid);
131
+
132
+ if (options['dry-run']) {
133
+ logger.log(t('store.publish_dry_run', { type: 'skill', slug, visibility }));
134
+ logger.log(t('store.publish_skill_files', { count: fileCount }));
135
+ logger.log(t('store.publish_scan_ok', { hash: scan.hash.slice(0, 12) }));
136
+ return { ok: true, dryRun: true, slug, visibility, paid, fileCount, hash: scan.hash };
137
+ }
138
+
139
+ logger.log(t('store.publish_scan_ok', { hash: scan.hash.slice(0, 12) }));
140
+ logger.log(t('store.publish_skill_sending', { count: fileCount }));
141
+ const baseUrl = resolveBaseUrl(config);
142
+ const response = await storePost(`${baseUrl}/api/store/skills/publish`, {
143
+ kind: 'aioson.store.skill',
144
+ slug,
145
+ files,
146
+ visibility,
147
+ paid,
148
+ hash: scan.hash,
149
+ workspaceSlug: ws?.slug || null
150
+ }, token);
151
+
152
+ logger.log(t('store.publish_skill_done', { slug, url: `${baseUrl}/store/skills/${slug}` }));
153
+ return { ok: true, slug, visibility, paid, fileCount, response };
154
+ }
155
+
156
+ // ── skill:install:store ─────────────────────────────────────────────────────
157
+
158
+ async function runSkillInstallStore({ args, options, logger, t }) {
159
+ const config = await readConfig();
160
+ const token = requireToken(config, t);
161
+ const projectDir = await findProjectRoot(path.resolve(process.cwd(), args[0] || '.'));
162
+ const baseUrl = resolveBaseUrl(config);
163
+
164
+ const ref = String(options.slug || options.code || args[1] || args[0] || '').trim();
165
+ if (!ref) throw new Error(t('store.error_missing_code_or_slug'));
166
+
167
+ logger.log(t('store.install_skill_fetching', { ref }));
168
+ const response = await storePost(`${baseUrl}/api/store/skills/install`, { ref }, token);
169
+
170
+ const slug = response.slug;
171
+ if (!slug || !response.files || typeof response.files !== 'object') {
172
+ throw new Error(t('store.error_invalid_response'));
173
+ }
174
+
175
+ // Install-side preview and scan
176
+ const publisher = response.publisher || 'unknown';
177
+ const version = response.version || '?';
178
+ const serverHash = response.hash || null;
179
+ const trusted = Boolean(response.trusted);
180
+ const downloads = response.downloads != null ? response.downloads : null;
181
+ const rating = response.rating != null ? `${Number(response.rating).toFixed(1)}/5` : null;
182
+
183
+ logger.log(t('store.install_preview_header', { slug, publisher, version }));
184
+ if (trusted) logger.log(t('store.install_preview_trusted'));
185
+ else logger.log(t('store.install_preview_unverified'));
186
+ if (downloads != null) logger.log(t('store.install_preview_downloads', { count: downloads }));
187
+ if (rating) logger.log(t('store.install_preview_rating', { rating }));
188
+ if (serverHash) logger.log(t('store.install_preview_hash', { hash: serverHash.slice(0, 12) }));
189
+
190
+ // Scan files received from server before writing
191
+ const stringFiles = Object.fromEntries(
192
+ Object.entries(response.files).filter(([, v]) => typeof v === 'string')
193
+ );
194
+ const scan = scanPackage(stringFiles, 'skill');
195
+ formatScanReport(scan, logger);
196
+ if (!scan.ok) throw new Error(t('store.error_install_scan_failed', { slug }));
197
+
198
+ // Verify hash integrity if server sent one
199
+ if (serverHash && scan.hash !== serverHash) {
200
+ throw new Error(t('store.error_hash_mismatch', { slug }));
201
+ }
202
+
203
+ if (options.inspect) {
204
+ logger.log(t('store.install_inspect_files', { count: Object.keys(stringFiles).length }));
205
+ for (const f of Object.keys(stringFiles).sort()) logger.log(` ${f}`);
206
+ logger.log(t('store.install_inspect_hint'));
207
+ return { ok: true, slug, inspect: true, files: Object.keys(stringFiles) };
208
+ }
209
+
210
+ if (!trusted && !options.force) {
211
+ logger.log(t('store.install_unverified_hint', { slug }));
212
+ }
213
+
214
+ const destDir = path.join(projectDir, '.aioson', 'installed-skills', slug);
215
+ await fs.mkdir(destDir, { recursive: true });
216
+
217
+ for (const [relPath, content] of Object.entries(stringFiles)) {
218
+ const filePath = path.join(destDir, relPath);
219
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
220
+ await fs.writeFile(filePath, content, 'utf8');
221
+ }
222
+
223
+ logger.log(t('store.install_skill_done', { slug, path: destDir }));
224
+ return { ok: true, slug, path: destDir };
225
+ }
226
+
227
+ // ── skill:list --remote ──────────────────────────────────────────────────────
228
+
229
+ async function runSkillListRemote({ args, options, logger, t }) {
230
+ const config = await readConfig();
231
+ const token = requireToken(config, t);
232
+ const baseUrl = resolveBaseUrl(config);
233
+ logger.log(t('store.list_remote_fetching', { type: 'skills' }));
234
+ const response = await storeGet(`${baseUrl}/api/store/skills`, token);
235
+ const skills = response.skills || [];
236
+ if (skills.length === 0) {
237
+ logger.log(t('store.list_remote_empty', { type: 'skills' }));
238
+ } else {
239
+ logger.log(t('store.list_remote_header', { count: skills.length, type: 'skills' }));
240
+ for (const s of skills) {
241
+ logger.log(t('store.list_remote_item', { slug: s.slug, name: s.title || s.slug, visibility: s.visibility || '?' }));
242
+ }
243
+ }
244
+ return { ok: true, skills, remote: true };
245
+ }
246
+
247
+ module.exports = { runSkillPublish, runSkillInstallStore, runSkillListRemote };