@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,128 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+
6
+ const LOCK_FILENAME = '.dossier.lock';
7
+ const DEFAULT_TIMEOUT_MS = 30_000;
8
+ const DEFAULT_POLL_INTERVAL_MS = 200;
9
+ const DEFAULT_STALE_TTL_MS = 60_000;
10
+
11
+ function defaultIsAlive(pid) {
12
+ if (typeof pid !== 'number' || !Number.isFinite(pid)) return false;
13
+ try {
14
+ process.kill(pid, 0);
15
+ return true;
16
+ } catch (err) {
17
+ return err && err.code === 'EPERM';
18
+ }
19
+ }
20
+
21
+ function sleep(ms) {
22
+ return new Promise((resolve) => setTimeout(resolve, ms));
23
+ }
24
+
25
+ function isStaleLock(payload, { staleTtlMs, now, isAlive }) {
26
+ if (!payload || typeof payload !== 'object') return true;
27
+ const acquiredMs = Date.parse(payload.acquired_at);
28
+ if (!Number.isFinite(acquiredMs)) return true;
29
+ if (now() - acquiredMs >= staleTtlMs) return true;
30
+ if (typeof payload.pid !== 'number' || !isAlive(payload.pid)) return true;
31
+ return false;
32
+ }
33
+
34
+ async function acquireLock(lockDir, section, opts = {}) {
35
+ if (typeof lockDir !== 'string' || !lockDir) {
36
+ throw new TypeError('acquireLock: lockDir must be a non-empty string');
37
+ }
38
+ if (typeof section !== 'string' || !section) {
39
+ throw new TypeError('acquireLock: section must be a non-empty string');
40
+ }
41
+
42
+ const {
43
+ timeoutMs = DEFAULT_TIMEOUT_MS,
44
+ pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,
45
+ staleTtlMs = DEFAULT_STALE_TTL_MS,
46
+ now = Date.now,
47
+ pid = process.pid,
48
+ isAlive = defaultIsAlive,
49
+ onWarn = (msg) => process.emitWarning(msg, 'DossierLockStale'),
50
+ sleepFn = sleep
51
+ } = opts;
52
+
53
+ const lockPath = path.join(lockDir, LOCK_FILENAME);
54
+ const start = now();
55
+
56
+ await fs.mkdir(lockDir, { recursive: true });
57
+
58
+ while (true) {
59
+ try {
60
+ const payload = {
61
+ pid,
62
+ section,
63
+ acquired_at: new Date(now()).toISOString()
64
+ };
65
+ const fh = await fs.open(lockPath, 'wx');
66
+ try {
67
+ await fh.writeFile(JSON.stringify(payload));
68
+ } finally {
69
+ await fh.close();
70
+ }
71
+
72
+ let released = false;
73
+ return async function release() {
74
+ if (released) return;
75
+ released = true;
76
+ try {
77
+ await fs.unlink(lockPath);
78
+ } catch (err) {
79
+ if (err && err.code !== 'ENOENT') throw err;
80
+ }
81
+ };
82
+ } catch (err) {
83
+ if (!err || err.code !== 'EEXIST') throw err;
84
+
85
+ let existing = null;
86
+ try {
87
+ const raw = await fs.readFile(lockPath, 'utf8');
88
+ existing = JSON.parse(raw);
89
+ } catch (_) {
90
+ existing = null;
91
+ }
92
+
93
+ if (isStaleLock(existing, { staleTtlMs, now, isAlive })) {
94
+ onWarn(
95
+ `stale dossier lockfile at ${lockPath} (pid=${existing && existing.pid != null ? existing.pid : '?'}); overriding`
96
+ );
97
+ try {
98
+ await fs.unlink(lockPath);
99
+ } catch (unlinkErr) {
100
+ if (unlinkErr && unlinkErr.code !== 'ENOENT') throw unlinkErr;
101
+ }
102
+ continue;
103
+ }
104
+
105
+ if (now() - start >= timeoutMs) {
106
+ const timeoutErr = new Error(
107
+ `timed out acquiring dossier lock at ${lockPath} after ${timeoutMs}ms`
108
+ );
109
+ timeoutErr.code = 'EDOSSIERLOCK';
110
+ timeoutErr.lockPath = lockPath;
111
+ timeoutErr.holder = existing;
112
+ throw timeoutErr;
113
+ }
114
+
115
+ await sleepFn(pollIntervalMs);
116
+ }
117
+ }
118
+ }
119
+
120
+ module.exports = {
121
+ LOCK_FILENAME,
122
+ DEFAULT_TIMEOUT_MS,
123
+ DEFAULT_POLL_INTERVAL_MS,
124
+ DEFAULT_STALE_TTL_MS,
125
+ acquireLock,
126
+ isStaleLock,
127
+ defaultIsAlive
128
+ };
@@ -0,0 +1,313 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('node:crypto');
4
+ const fs = require('node:fs/promises');
5
+ const path = require('node:path');
6
+
7
+ const { isCanonicalAgent, isValidSlug } = require('./schema');
8
+ const { shouldCompact, compact } = require('./dossier-compact');
9
+
10
+ const FEATURES_SUBDIR = 'features';
11
+ const REVISIONS_FILENAME = 'revisions.json';
12
+ const MAX_REVISION_ROUNDS = 3;
13
+
14
+ const VALID_SEVERITIES = new Set(['blocking', 'advisory']);
15
+ const VALID_STATUSES = new Set(['pending', 'approved', 'rejected', 'resolved']);
16
+
17
+ // Which workflow gate each agent "owns" (for anti-loop counter)
18
+ const GATE_BY_AGENT = {
19
+ product: 'requirements',
20
+ analyst: 'requirements',
21
+ architect: 'design',
22
+ 'ux-ui': 'design',
23
+ pm: 'plan',
24
+ dev: 'plan',
25
+ qa: 'execution'
26
+ };
27
+
28
+ function revisionsPath(contextDir, slug) {
29
+ return path.join(contextDir, FEATURES_SUBDIR, slug, REVISIONS_FILENAME);
30
+ }
31
+
32
+ function dossierPath(contextDir, slug) {
33
+ return path.join(contextDir, FEATURES_SUBDIR, slug, 'dossier.md');
34
+ }
35
+
36
+ function workflowStatePath(contextDir) {
37
+ return path.join(contextDir, 'workflow.state.json');
38
+ }
39
+
40
+ function nextRevId(existing) {
41
+ const n = existing.length + 1;
42
+ return `rev-${String(n).padStart(3, '0')}`;
43
+ }
44
+
45
+ async function readRevisions({ slug, contextDir }) {
46
+ const p = revisionsPath(contextDir, slug);
47
+ try {
48
+ const raw = await fs.readFile(p, 'utf8');
49
+ const parsed = JSON.parse(raw);
50
+ return Array.isArray(parsed) ? parsed : [];
51
+ } catch {
52
+ return [];
53
+ }
54
+ }
55
+
56
+ async function writeRevisions({ slug, contextDir, revisions }) {
57
+ const p = revisionsPath(contextDir, slug);
58
+ await fs.mkdir(path.dirname(p), { recursive: true });
59
+ await fs.writeFile(p, JSON.stringify(revisions, null, 2) + '\n', 'utf8');
60
+ }
61
+
62
+ // Replace the content of one ## section in a markdown file.
63
+ async function updateDossierSection(filePath, sectionName, newContent) {
64
+ let raw;
65
+ try {
66
+ raw = await fs.readFile(filePath, 'utf8');
67
+ } catch {
68
+ return; // dossier absent — skip silently
69
+ }
70
+
71
+ const lines = raw.split('\n');
72
+ let sectionStart = -1;
73
+ let sectionEnd = lines.length;
74
+
75
+ for (let i = 0; i < lines.length; i++) {
76
+ if (lines[i].trimEnd() === `## ${sectionName}`) {
77
+ sectionStart = i;
78
+ } else if (sectionStart !== -1 && i > sectionStart && /^## /.test(lines[i])) {
79
+ sectionEnd = i;
80
+ break;
81
+ }
82
+ }
83
+
84
+ let rebuilt;
85
+ if (sectionStart === -1) {
86
+ // Append section at end
87
+ rebuilt = raw.trimEnd() + `\n\n## ${sectionName}\n\n${newContent.trimEnd()}\n`;
88
+ } else {
89
+ const before = lines.slice(0, sectionStart + 1);
90
+ const after = lines.slice(sectionEnd);
91
+ rebuilt = [...before, '', newContent.trimEnd(), '', ...after].join('\n');
92
+ }
93
+
94
+ await fs.writeFile(filePath, rebuilt, 'utf8');
95
+ }
96
+
97
+ function buildRevisionTable(revisions) {
98
+ if (revisions.length === 0) {
99
+ return '_(sem revisões abertas)_';
100
+ }
101
+ const header = '| id | requested_by | target | severity | status |';
102
+ const divider = '|----|-------------|--------|----------|--------|';
103
+ const rows = revisions.map((r) =>
104
+ `| ${r.id} | ${r.requested_by} | ${r.target} | ${r.severity} | ${r.status} |`
105
+ );
106
+ return [header, divider, ...rows].join('\n');
107
+ }
108
+
109
+ async function readWorkflowState(contextDir) {
110
+ const p = workflowStatePath(contextDir);
111
+ try {
112
+ const raw = await fs.readFile(p, 'utf8');
113
+ return JSON.parse(raw);
114
+ } catch {
115
+ return null;
116
+ }
117
+ }
118
+
119
+ async function writeWorkflowState(contextDir, state) {
120
+ const p = workflowStatePath(contextDir);
121
+ await fs.writeFile(p, JSON.stringify(state, null, 2) + '\n', 'utf8');
122
+ }
123
+
124
+ async function open({
125
+ slug,
126
+ contextDir,
127
+ requestedBy,
128
+ target,
129
+ targetArtifact,
130
+ reason,
131
+ severity,
132
+ evidenceCodeRefs = [],
133
+ now = () => new Date()
134
+ } = {}) {
135
+ if (!isValidSlug(slug)) {
136
+ const err = new Error(`invalid slug: ${JSON.stringify(slug)}`);
137
+ err.code = 'EREVSLUG';
138
+ throw err;
139
+ }
140
+ if (!isCanonicalAgent(requestedBy)) {
141
+ const err = new Error(`requested_by must be a canonical agent id (got: ${JSON.stringify(requestedBy)})`);
142
+ err.code = 'EREVAGENT';
143
+ throw err;
144
+ }
145
+ if (!isCanonicalAgent(target)) {
146
+ const err = new Error(`target must be a canonical agent id (got: ${JSON.stringify(target)})`);
147
+ err.code = 'EREVAGENT';
148
+ throw err;
149
+ }
150
+ if (!VALID_SEVERITIES.has(severity)) {
151
+ const err = new Error(`severity must be 'blocking' or 'advisory' (got: ${JSON.stringify(severity)})`);
152
+ err.code = 'EREVSCHEMA';
153
+ throw err;
154
+ }
155
+ if (!targetArtifact || typeof targetArtifact !== 'string') {
156
+ const err = new Error('target_artifact must be a non-empty relative path');
157
+ err.code = 'EREVSCHEMA';
158
+ throw err;
159
+ }
160
+ if (!reason || typeof reason !== 'string' || !reason.trim()) {
161
+ const err = new Error('reason must be a non-empty string');
162
+ err.code = 'EREVSCHEMA';
163
+ throw err;
164
+ }
165
+
166
+ const revisions = await readRevisions({ slug, contextDir });
167
+ const id = nextRevId(revisions);
168
+ const createdAt = now().toISOString();
169
+
170
+ const revision = {
171
+ id,
172
+ status: 'pending',
173
+ requested_by: requestedBy,
174
+ target,
175
+ target_artifact: targetArtifact,
176
+ reason: reason.trim(),
177
+ severity,
178
+ evidence_code_refs: Array.isArray(evidenceCodeRefs) ? evidenceCodeRefs : [],
179
+ created_at: createdAt,
180
+ resolved_at: null,
181
+ resolution: null
182
+ };
183
+
184
+ revisions.push(revision);
185
+ await writeRevisions({ slug, contextDir, revisions });
186
+
187
+ // Update dossier ## Revision Requests
188
+ const dp = dossierPath(contextDir, slug);
189
+ await updateDossierSection(dp, 'Revision Requests', buildRevisionTable(revisions));
190
+
191
+ return revision;
192
+ }
193
+
194
+ async function list({ slug, contextDir, status } = {}) {
195
+ if (!isValidSlug(slug)) {
196
+ const err = new Error(`invalid slug: ${JSON.stringify(slug)}`);
197
+ err.code = 'EREVSLUG';
198
+ throw err;
199
+ }
200
+ const revisions = await readRevisions({ slug, contextDir });
201
+ if (status && VALID_STATUSES.has(status)) {
202
+ return revisions.filter((r) => r.status === status);
203
+ }
204
+ return revisions;
205
+ }
206
+
207
+ async function resolve({
208
+ slug,
209
+ contextDir,
210
+ revId,
211
+ action,
212
+ forceRevision = false,
213
+ now = () => new Date()
214
+ } = {}) {
215
+ if (!isValidSlug(slug)) {
216
+ const err = new Error(`invalid slug: ${JSON.stringify(slug)}`);
217
+ err.code = 'EREVSLUG';
218
+ throw err;
219
+ }
220
+ if (action !== 'approve' && action !== 'reject') {
221
+ const err = new Error(`action must be 'approve' or 'reject' (got: ${JSON.stringify(action)})`);
222
+ err.code = 'EREVSCHEMA';
223
+ throw err;
224
+ }
225
+
226
+ const revisions = await readRevisions({ slug, contextDir });
227
+ const idx = revisions.findIndex((r) => r.id === revId);
228
+ if (idx === -1) {
229
+ const err = new Error(`revision '${revId}' not found in ${slug}`);
230
+ err.code = 'EREVNOTFOUND';
231
+ throw err;
232
+ }
233
+
234
+ const rev = revisions[idx];
235
+ if (rev.status !== 'pending') {
236
+ const err = new Error(`revision '${revId}' is '${rev.status}' — only pending revisions can be resolved`);
237
+ err.code = 'EREVNOTPENDING';
238
+ throw err;
239
+ }
240
+
241
+ const resolvedAt = now().toISOString();
242
+
243
+ if (action === 'reject') {
244
+ revisions[idx] = { ...rev, status: 'rejected', resolved_at: resolvedAt, resolution: 'rejected' };
245
+ await writeRevisions({ slug, contextDir, revisions });
246
+ const dp = dossierPath(contextDir, slug);
247
+ await updateDossierSection(dp, 'Revision Requests', buildRevisionTable(revisions));
248
+ if (await shouldCompact({ slug, contextDir })) await compact({ slug, contextDir });
249
+ return { revision: revisions[idx], gateIncremented: null };
250
+ }
251
+
252
+ // approve path: check anti-loop limit
253
+ const gate = GATE_BY_AGENT[rev.target] || null;
254
+ let gateIncremented = null;
255
+ let newRounds = null;
256
+
257
+ if (gate) {
258
+ const wfState = await readWorkflowState(contextDir);
259
+ if (wfState) {
260
+ const rounds = (wfState.gate_revision_rounds || {})[gate] || 0;
261
+ if (rounds >= MAX_REVISION_ROUNDS && !forceRevision) {
262
+ const err = new Error(
263
+ `Anti-loop: gate '${gate}' already had ${rounds} revision round(s) (max ${MAX_REVISION_ROUNDS}). ` +
264
+ `Use --force-revision to override.`
265
+ );
266
+ err.code = 'EREVLOOP';
267
+ err.gate = gate;
268
+ err.rounds = rounds;
269
+ throw err;
270
+ }
271
+ newRounds = rounds + 1;
272
+ const updatedState = {
273
+ ...wfState,
274
+ gate_revision_rounds: {
275
+ ...(wfState.gate_revision_rounds || {}),
276
+ [gate]: newRounds
277
+ }
278
+ };
279
+ await writeWorkflowState(contextDir, updatedState);
280
+ gateIncremented = { gate, rounds: newRounds };
281
+ }
282
+ }
283
+
284
+ revisions[idx] = { ...rev, status: 'approved', resolved_at: resolvedAt, resolution: 'approved' };
285
+ await writeRevisions({ slug, contextDir, revisions });
286
+ const dp = dossierPath(contextDir, slug);
287
+ await updateDossierSection(dp, 'Revision Requests', buildRevisionTable(revisions));
288
+ if (await shouldCompact({ slug, contextDir })) await compact({ slug, contextDir });
289
+
290
+ return { revision: revisions[idx], gateIncremented };
291
+ }
292
+
293
+ async function getBlockingRevisions({ slug, contextDir }) {
294
+ if (!slug || !isValidSlug(slug)) return [];
295
+ const revisions = await readRevisions({ slug, contextDir });
296
+ return revisions.filter((r) => r.severity === 'blocking' && r.status === 'pending');
297
+ }
298
+
299
+ module.exports = {
300
+ VALID_SEVERITIES,
301
+ VALID_STATUSES,
302
+ GATE_BY_AGENT,
303
+ MAX_REVISION_ROUNDS,
304
+ revisionsPath,
305
+ readRevisions,
306
+ writeRevisions,
307
+ updateDossierSection,
308
+ buildRevisionTable,
309
+ open,
310
+ list,
311
+ resolve,
312
+ getBlockingRevisions
313
+ };
@@ -0,0 +1,155 @@
1
+ 'use strict';
2
+
3
+ const SCHEMA_VERSION = '1.0';
4
+
5
+ const CANONICAL_AGENT_IDS = Object.freeze(new Set([
6
+ 'analyst',
7
+ 'architect',
8
+ 'committer',
9
+ 'copywriter',
10
+ 'cypher',
11
+ 'design-hybrid-forge',
12
+ 'dev',
13
+ 'deyvin',
14
+ 'discover',
15
+ 'discovery-design-doc',
16
+ 'genome',
17
+ 'neo',
18
+ 'orache',
19
+ 'orchestrator',
20
+ 'pair',
21
+ 'pentester',
22
+ 'pm',
23
+ 'product',
24
+ 'profiler-enricher',
25
+ 'profiler-forge',
26
+ 'profiler-researcher',
27
+ 'qa',
28
+ 'setup',
29
+ 'sheldon',
30
+ 'site-forge',
31
+ 'squad',
32
+ 'tester',
33
+ 'ux-ui',
34
+ 'validator'
35
+ ]));
36
+
37
+ const ORIGIN_PSEUDO_IDS = Object.freeze(new Set(['dossier-init', 'dossier-init-prompt']));
38
+
39
+ const REQUIRED_FRONTMATTER_FIELDS = Object.freeze([
40
+ 'feature_slug',
41
+ 'schema_version',
42
+ 'created_by',
43
+ 'created_at',
44
+ 'status',
45
+ 'classification',
46
+ 'last_updated_by',
47
+ 'last_updated_at'
48
+ ]);
49
+
50
+ const ALLOWED_STATUSES = Object.freeze(new Set(['active', 'paused', 'closed']));
51
+ const ALLOWED_CLASSIFICATIONS = Object.freeze(new Set(['MICRO', 'SMALL', 'MEDIUM']));
52
+
53
+ const REQUIRED_SECTIONS = Object.freeze([
54
+ 'Why',
55
+ 'What',
56
+ 'Code Map',
57
+ 'Rules & Design-Docs aplicáveis',
58
+ 'Agent Trail',
59
+ 'Revision Requests'
60
+ ]);
61
+
62
+ const SLUG_REGEX = /^[a-z0-9][a-z0-9-]*$/;
63
+ const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
64
+
65
+ function isValidSlug(value) {
66
+ return typeof value === 'string' && SLUG_REGEX.test(value);
67
+ }
68
+
69
+ function isValidIsoDate(value) {
70
+ if (typeof value !== 'string' || !ISO_DATE_REGEX.test(value)) return false;
71
+ const ts = Date.parse(value);
72
+ return Number.isFinite(ts);
73
+ }
74
+
75
+ function isCanonicalAgent(value) {
76
+ return typeof value === 'string' && CANONICAL_AGENT_IDS.has(value);
77
+ }
78
+
79
+ function isAllowedAuthor(value) {
80
+ return isCanonicalAgent(value) || (typeof value === 'string' && ORIGIN_PSEUDO_IDS.has(value));
81
+ }
82
+
83
+ function validateFrontmatter(fm) {
84
+ const errors = [];
85
+
86
+ if (!fm || typeof fm !== 'object') {
87
+ return { valid: false, errors: ['frontmatter is missing or not an object'] };
88
+ }
89
+
90
+ for (const field of REQUIRED_FRONTMATTER_FIELDS) {
91
+ if (!Object.prototype.hasOwnProperty.call(fm, field) || fm[field] === '' || fm[field] === null || fm[field] === undefined) {
92
+ errors.push(`missing required field: ${field}`);
93
+ }
94
+ }
95
+
96
+ if (fm.feature_slug !== undefined && !isValidSlug(fm.feature_slug)) {
97
+ errors.push(`feature_slug must be kebab-case (got: ${JSON.stringify(fm.feature_slug)})`);
98
+ }
99
+
100
+ if (fm.schema_version !== undefined && fm.schema_version !== SCHEMA_VERSION) {
101
+ errors.push(`unsupported schema_version: ${JSON.stringify(fm.schema_version)} (expected ${SCHEMA_VERSION})`);
102
+ }
103
+
104
+ if (fm.status !== undefined && !ALLOWED_STATUSES.has(fm.status)) {
105
+ errors.push(`status must be one of [${[...ALLOWED_STATUSES].join(', ')}] (got: ${JSON.stringify(fm.status)})`);
106
+ }
107
+
108
+ if (fm.classification !== undefined && !ALLOWED_CLASSIFICATIONS.has(fm.classification)) {
109
+ errors.push(`classification must be one of [${[...ALLOWED_CLASSIFICATIONS].join(', ')}] (got: ${JSON.stringify(fm.classification)})`);
110
+ }
111
+
112
+ if (fm.created_by !== undefined && !isAllowedAuthor(fm.created_by)) {
113
+ errors.push(`created_by must be a canonical agent id or 'dossier-init' (got: ${JSON.stringify(fm.created_by)})`);
114
+ }
115
+
116
+ if (fm.last_updated_by !== undefined && !isAllowedAuthor(fm.last_updated_by)) {
117
+ errors.push(`last_updated_by must be a canonical agent id or 'dossier-init' (got: ${JSON.stringify(fm.last_updated_by)})`);
118
+ }
119
+
120
+ if (fm.created_at !== undefined && !isValidIsoDate(fm.created_at)) {
121
+ errors.push(`created_at must be ISO 8601 (got: ${JSON.stringify(fm.created_at)})`);
122
+ }
123
+
124
+ if (fm.last_updated_at !== undefined && !isValidIsoDate(fm.last_updated_at)) {
125
+ errors.push(`last_updated_at must be ISO 8601 (got: ${JSON.stringify(fm.last_updated_at)})`);
126
+ }
127
+
128
+ return { valid: errors.length === 0, errors };
129
+ }
130
+
131
+ function assertFrontmatter(fm) {
132
+ const result = validateFrontmatter(fm);
133
+ if (!result.valid) {
134
+ const err = new Error(`invalid dossier frontmatter: ${result.errors.join('; ')}`);
135
+ err.code = 'EDOSSIERSCHEMA';
136
+ err.errors = result.errors;
137
+ throw err;
138
+ }
139
+ }
140
+
141
+ module.exports = {
142
+ SCHEMA_VERSION,
143
+ CANONICAL_AGENT_IDS,
144
+ ORIGIN_PSEUDO_IDS,
145
+ REQUIRED_FRONTMATTER_FIELDS,
146
+ ALLOWED_STATUSES,
147
+ ALLOWED_CLASSIFICATIONS,
148
+ REQUIRED_SECTIONS,
149
+ isValidSlug,
150
+ isValidIsoDate,
151
+ isCanonicalAgent,
152
+ isAllowedAuthor,
153
+ validateFrontmatter,
154
+ assertFrontmatter
155
+ };