@jaimevalasek/aioson 1.7.0 → 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.
- package/CHANGELOG.md +60 -0
- package/README.md +153 -10
- package/docs/en/cli-reference.md +56 -1
- package/docs/en/i18n.md +18 -18
- package/docs/en/schemas/index.json +10 -0
- package/docs/en/schemas/parallel-assign.schema.json +9 -0
- package/docs/en/schemas/parallel-doctor.schema.json +36 -0
- package/docs/en/schemas/parallel-guard.schema.json +63 -0
- package/docs/en/schemas/parallel-merge.schema.json +84 -0
- package/docs/en/schemas/parallel-status.schema.json +91 -1
- package/docs/integrations/apps-publish-marketplace.md +94 -0
- package/docs/pt/README.md +9 -0
- package/docs/pt/agentes.md +324 -3
- package/docs/pt/clientes-ai.md +7 -3
- package/docs/pt/comandos-cli.md +160 -13
- package/docs/pt/compress-agents.md +304 -0
- package/docs/pt/design-docs-governance.md +59 -0
- package/docs/pt/feature-archive.md +191 -0
- package/docs/pt/genome-3.0-spec.md +115 -4
- package/docs/pt/genome-distribution.md +232 -0
- package/docs/pt/inicio-rapido.md +1 -0
- package/docs/pt/motor-hardening.md +492 -0
- package/docs/pt/runner-system.md +113 -0
- package/package.json +2 -1
- package/src/agent-manifests.js +66 -0
- package/src/agents.js +27 -7
- package/src/autonomy-policy.js +139 -0
- package/src/brain-query.js +161 -0
- package/src/cli.js +1377 -1099
- package/src/commands/agents.js +102 -7
- package/src/commands/artifact-validate.js +33 -4
- package/src/commands/auth.js +272 -0
- package/src/commands/brain-query.js +44 -0
- package/src/commands/briefing.js +344 -0
- package/src/commands/commit-prepare.js +547 -0
- package/src/commands/compress-agents.js +416 -0
- package/src/commands/context-health.js +4 -2
- package/src/commands/context-trim.js +17 -11
- package/src/commands/design-hybrid-options.js +3 -3
- package/src/commands/devlog-process.js +6 -4
- package/src/commands/dossier.js +423 -0
- package/src/commands/feature-archive.js +513 -0
- package/src/commands/feature-close.js +123 -18
- package/src/commands/gate-approve.js +198 -0
- package/src/commands/gate-check.js +24 -5
- package/src/commands/genome-doctor.js +166 -9
- package/src/commands/git-guard.js +170 -0
- package/src/commands/harness.js +121 -0
- package/src/commands/implementation-plan.js +47 -20
- package/src/commands/init.js +6 -2
- package/src/commands/install.js +6 -2
- package/src/commands/live.js +497 -56
- package/src/commands/locale-apply.js +9 -6
- package/src/commands/locale-diff.js +11 -112
- package/src/commands/mcp-doctor.js +2 -1
- package/src/commands/mcp-init.js +4 -10
- package/src/commands/memory.js +234 -0
- package/src/commands/parallel-assign.js +107 -27
- package/src/commands/parallel-doctor.js +416 -3
- package/src/commands/parallel-guard.js +241 -0
- package/src/commands/parallel-init.js +66 -4
- package/src/commands/parallel-merge.js +299 -0
- package/src/commands/parallel-status.js +147 -3
- package/src/commands/preflight.js +63 -4
- package/src/commands/qa-init.js +10 -5
- package/src/commands/revision.js +235 -0
- package/src/commands/scaffold-complete.js +188 -0
- package/src/commands/security-audit.js +275 -0
- package/src/commands/security-scan.js +376 -0
- package/src/commands/self-implement-loop.js +46 -2
- package/src/commands/setup-context.js +11 -10
- package/src/commands/squad-agent-create.js +51 -9
- package/src/commands/squad-investigate.js +53 -0
- package/src/commands/squad-plan.js +33 -1
- package/src/commands/squad-scaffold.js +4 -3
- package/src/commands/squad-score.js +71 -14
- package/src/commands/squad-status.js +22 -1
- package/src/commands/squad-validate.js +93 -2
- package/src/commands/store-genome.js +304 -0
- package/src/commands/store-skill.js +247 -0
- package/src/commands/store-squad.js +431 -0
- package/src/commands/store-system.js +392 -0
- package/src/commands/tool-capabilities.js +63 -0
- package/src/commands/update.js +3 -3
- package/src/commands/verify-gate.js +40 -0
- package/src/commands/workflow-execute.js +644 -155
- package/src/commands/workflow-harden.js +231 -0
- package/src/commands/workflow-heal.js +136 -0
- package/src/commands/workflow-next.js +460 -22
- package/src/commands/workflow-status.js +328 -138
- package/src/commands/workspace.js +144 -0
- package/src/constants.js +55 -75
- package/src/context-memory.js +133 -4
- package/src/context-writer.js +2 -1
- package/src/context.js +32 -2
- package/src/doctor.js +46 -6
- package/src/dossier/codemap-store.js +267 -0
- package/src/dossier/dossier-bootstrap.js +222 -0
- package/src/dossier/dossier-compact.js +159 -0
- package/src/dossier/lock.js +128 -0
- package/src/dossier/revision-store.js +313 -0
- package/src/dossier/schema.js +155 -0
- package/src/dossier/store.js +400 -0
- package/src/execution-gateway.js +3 -0
- package/src/friction-scanner.js +202 -0
- package/src/genome-schema.js +24 -1
- package/src/genomes.js +33 -0
- package/src/handoff-contract.js +363 -0
- package/src/handoff-validator.js +45 -0
- package/src/harness/circuit-breaker.js +135 -0
- package/src/i18n/messages/en.js +317 -22
- package/src/i18n/messages/es.js +259 -18
- package/src/i18n/messages/fr.js +260 -18
- package/src/i18n/messages/pt-BR.js +313 -22
- package/src/install-profile.js +0 -16
- package/src/installer.js +70 -6
- package/src/lib/git-commit-guard.js +691 -0
- package/src/lib/security/artifact-reader.js +167 -0
- package/src/lib/security/exit-codes.js +51 -0
- package/src/lib/security/findings-writer.js +176 -0
- package/src/lib/security/runtime-events.js +77 -0
- package/src/lib/security/secrets-regex.js +115 -0
- package/src/lib/store/security-scan.js +173 -0
- package/src/lib/terminal-checkbox.js +130 -0
- package/src/lib/tmux-launcher.js +163 -0
- package/src/lib/tool-capabilities.js +102 -0
- package/src/locales.js +12 -8
- package/src/parallel-workspace.js +756 -0
- package/src/parser.js +8 -1
- package/src/path-guard.js +47 -0
- package/src/preflight-engine.js +237 -26
- package/src/self-healing.js +142 -0
- package/src/session-handoff.js +111 -1
- package/src/squad/squad-scaffold.js +183 -19
- package/src/test-briefing.js +226 -0
- package/src/updater.js +1 -1
- package/src/utils.js +3 -0
- package/src/workflow-gates.js +185 -0
- package/template/.aioson/agents/analyst.md +76 -130
- package/template/.aioson/agents/architect.md +53 -86
- package/template/.aioson/agents/committer.md +161 -0
- package/template/.aioson/agents/copywriter.md +463 -0
- package/template/.aioson/agents/cypher.md +252 -0
- package/template/.aioson/agents/dev.md +112 -600
- package/template/.aioson/agents/deyvin.md +33 -235
- package/template/.aioson/agents/discover.md +235 -0
- package/template/.aioson/agents/discovery-design-doc.md +17 -252
- package/template/.aioson/agents/genome.md +76 -26
- package/template/.aioson/agents/manifests/analyst.manifest.json +26 -0
- package/template/.aioson/agents/manifests/architect.manifest.json +23 -0
- package/template/.aioson/agents/manifests/committer.manifest.json +23 -0
- package/template/.aioson/agents/manifests/dev.manifest.json +37 -0
- package/template/.aioson/agents/manifests/orchestrator.manifest.json +30 -0
- package/template/.aioson/agents/manifests/pentester.manifest.json +39 -0
- package/template/.aioson/agents/manifests/pm.manifest.json +26 -0
- package/template/.aioson/agents/manifests/product.manifest.json +23 -0
- package/template/.aioson/agents/manifests/qa.manifest.json +25 -0
- package/template/.aioson/agents/manifests/setup.manifest.json +20 -0
- package/template/.aioson/agents/manifests/ux-ui.manifest.json +24 -0
- package/template/.aioson/agents/neo.md +10 -8
- package/template/.aioson/agents/orache.md +2 -6
- package/template/.aioson/agents/orchestrator.md +81 -182
- package/template/.aioson/agents/pentester.md +235 -0
- package/template/.aioson/agents/pm.md +40 -104
- package/template/.aioson/agents/product.md +99 -344
- package/template/.aioson/agents/profiler-enricher.md +57 -6
- package/template/.aioson/agents/profiler-forge.md +17 -7
- package/template/.aioson/agents/profiler-researcher.md +29 -6
- package/template/.aioson/agents/qa.md +165 -410
- package/template/.aioson/agents/setup.md +52 -262
- package/template/.aioson/agents/sheldon.md +122 -754
- package/template/.aioson/agents/site-forge.md +111 -1583
- package/template/.aioson/agents/squad.md +139 -1820
- package/template/.aioson/agents/tester.md +10 -0
- package/template/.aioson/agents/ux-ui.md +103 -645
- package/template/.aioson/agents/validator.md +69 -0
- package/template/.aioson/brains/scripts/query.js +5 -1
- package/template/.aioson/config/autonomy-protocol.json +43 -0
- package/template/.aioson/config.md +43 -15
- package/template/.aioson/constitution.md +36 -33
- package/template/.aioson/context/design-doc.md +136 -0
- package/template/.aioson/context/project-map.md +57 -0
- package/template/.aioson/design-docs/code-reuse.md +48 -0
- package/template/.aioson/design-docs/componentization.md +47 -0
- package/template/.aioson/design-docs/file-size.md +52 -0
- package/template/.aioson/design-docs/folder-structure.md +51 -0
- package/template/.aioson/design-docs/naming.md +54 -0
- package/template/.aioson/docs/LAYERS.md +12 -2
- package/template/.aioson/docs/dev/execution-discipline.md +106 -0
- package/template/.aioson/docs/dev/stack-conventions.md +83 -0
- package/template/.aioson/docs/deyvin/continuity-recovery.md +57 -0
- package/template/.aioson/docs/deyvin/debugging-escalation.md +30 -0
- package/template/.aioson/docs/deyvin/pair-execution.md +44 -0
- package/template/.aioson/docs/deyvin/runtime-handoffs.md +36 -0
- package/template/.aioson/docs/product/conversation-playbook.md +116 -0
- package/template/.aioson/docs/product/prd-contract.md +107 -0
- package/template/.aioson/docs/product/quality-lens.md +57 -0
- package/template/.aioson/docs/product/research-loop.md +65 -0
- package/template/.aioson/docs/sheldon/enrichment-paths.md +134 -0
- package/template/.aioson/docs/sheldon/quality-lens.md +57 -0
- package/template/.aioson/docs/sheldon/research-loop.md +56 -0
- package/template/.aioson/docs/sheldon/web-intelligence.md +75 -0
- package/template/.aioson/docs/site-forge-build.md +195 -0
- package/template/.aioson/docs/site-forge-extraction.md +135 -0
- package/template/.aioson/docs/site-forge-qa.md +155 -0
- package/template/.aioson/docs/site-forge-recon.md +434 -0
- package/template/.aioson/docs/site-forge-transform.md +249 -0
- package/template/.aioson/docs/squad/content-output.md +91 -0
- package/template/.aioson/docs/squad/creation-flow.md +135 -0
- package/template/.aioson/docs/squad/domain-classification.md +117 -0
- package/template/.aioson/docs/squad/genome-bindings.md +47 -0
- package/template/.aioson/docs/squad/package-contract.md +234 -0
- package/template/.aioson/docs/squad/quality-lens.md +56 -0
- package/template/.aioson/docs/squad/research-loop.md +59 -0
- package/template/.aioson/docs/squad/session-operations.md +117 -0
- package/template/.aioson/docs/squad/workflow-quality.md +165 -0
- package/template/.aioson/docs/ux-ui/accessibility-audit.md +55 -0
- package/template/.aioson/docs/ux-ui/audit-mode.md +86 -0
- package/template/.aioson/docs/ux-ui/component-map.md +35 -0
- package/template/.aioson/docs/ux-ui/design-execution.md +111 -0
- package/template/.aioson/docs/ux-ui/design-gate.md +27 -0
- package/template/.aioson/docs/ux-ui/research-mode.md +39 -0
- package/template/.aioson/docs/ux-ui/site-delivery.md +156 -0
- package/template/.aioson/docs/ux-ui/token-contract.md +57 -0
- package/template/.aioson/genomes/copywriting.md +204 -0
- package/template/.aioson/genomes/copywriting.meta.json +48 -0
- package/template/.aioson/git-guard.json +11 -0
- package/template/.aioson/mcp/servers.md +0 -1
- package/template/.aioson/rules/agent-language-policy.md +93 -0
- package/template/.aioson/rules/aioson-context-boundary.md +63 -0
- package/template/.aioson/rules/canonical-path-contract.md +47 -0
- package/template/.aioson/rules/data-format-convention.md +24 -86
- package/template/.aioson/rules/disk-first-artifacts.md +44 -0
- package/template/.aioson/rules/output-brevity.md +44 -0
- package/template/.aioson/rules/prd-section-ownership.md +49 -0
- package/template/.aioson/rules/security-baseline.md +139 -0
- package/template/.aioson/rules/spec-level-ownership.md +61 -0
- package/template/.aioson/rules/squad-driver-pattern.md +81 -0
- package/template/.aioson/schemas/squad-blueprint.schema.json +24 -0
- package/template/.aioson/schemas/squad-manifest.schema.json +44 -0
- package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +2 -0
- package/template/.aioson/skills/marketing/references/anti-patterns.md +254 -0
- package/template/.aioson/skills/marketing/references/fascinations.md +192 -0
- package/template/.aioson/skills/marketing/references/five-acts.md +248 -0
- package/template/.aioson/skills/marketing/references/market-intelligence.md +198 -0
- package/template/.aioson/skills/marketing/references/offer-structure.md +203 -0
- package/template/.aioson/skills/marketing/references/one-belief.md +149 -0
- package/template/.aioson/skills/marketing/references/patterns.md +218 -0
- package/template/.aioson/skills/marketing/references/pms-research.md +193 -0
- package/template/.aioson/skills/marketing/vsl-craft.md +385 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/pm.md +30 -0
- package/template/.aioson/skills/process/secure-tdd/SKILL.md +97 -0
- package/template/.aioson/skills/process/secure-tdd/references/nextjs.md +81 -0
- package/template/.aioson/skills/process/secure-tdd/references/node-express.md +91 -0
- package/template/.aioson/skills/process/secure-tdd/references/planned-stacks.md +33 -0
- package/template/.aioson/skills/static/harness-validate/SKILL.md +46 -0
- package/template/.aioson/skills/static/landing-page-deploy.md +192 -0
- package/template/.aioson/skills/static/landing-page-forge.md +730 -0
- package/template/.aioson/skills/static/ui-ux-modern.md +1 -0
- package/template/.aioson/skills/static/web-research-cache.md +3 -0
- package/template/.aioson/tasks/squad-create.md +56 -7
- package/template/.aioson/tasks/squad-design.md +80 -2
- package/template/.aioson/tasks/squad-investigate.md +14 -1
- package/template/.aioson/templates/squads/digital-marketing-agency/template.json +96 -0
- package/template/.claude/commands/aioson/agent/committer.md +5 -0
- package/template/.claude/commands/aioson/agent/copywriter.md +5 -0
- package/template/.claude/commands/aioson/agent/cypher.md +5 -0
- package/template/.claude/commands/aioson/agent/pair.md +5 -0
- package/template/.claude/commands/aioson/agent/validator.md +5 -0
- package/template/.gemini/commands/aios-analyst.toml +6 -3
- package/template/.gemini/commands/aios-architect.toml +7 -6
- package/template/.gemini/commands/aios-committer.toml +7 -0
- package/template/.gemini/commands/aios-copywriter.toml +7 -0
- package/template/.gemini/commands/aios-cypher.toml +7 -0
- package/template/.gemini/commands/aios-dev.toml +8 -7
- package/template/.gemini/commands/aios-deyvin.toml +6 -5
- package/template/.gemini/commands/aios-discovery-design-doc.toml +6 -3
- package/template/.gemini/commands/aios-genome.toml +7 -0
- package/template/.gemini/commands/aios-neo.toml +5 -3
- package/template/.gemini/commands/aios-orache.toml +7 -0
- package/template/.gemini/commands/aios-orchestrator.toml +8 -7
- package/template/.gemini/commands/aios-pair.toml +6 -5
- package/template/.gemini/commands/aios-pm.toml +8 -7
- package/template/.gemini/commands/aios-product.toml +5 -3
- package/template/.gemini/commands/aios-qa.toml +6 -5
- package/template/.gemini/commands/aios-setup.toml +5 -2
- package/template/.gemini/commands/aios-sheldon.toml +7 -0
- package/template/.gemini/commands/aios-site-forge.toml +7 -0
- package/template/.gemini/commands/aios-squad.toml +7 -0
- package/template/.gemini/commands/aios-tester.toml +6 -5
- package/template/.gemini/commands/aios-ux-ui.toml +8 -7
- package/template/.gemini/commands/aios-validator.toml +7 -0
- package/template/AGENTS.md +12 -1
- package/template/CLAUDE.md +6 -1
- package/template/.aioson/locales/en/agents/analyst.md +0 -244
- package/template/.aioson/locales/en/agents/architect.md +0 -245
- package/template/.aioson/locales/en/agents/dev.md +0 -397
- package/template/.aioson/locales/en/agents/deyvin.md +0 -137
- package/template/.aioson/locales/en/agents/discovery-design-doc.md +0 -27
- package/template/.aioson/locales/en/agents/genome.md +0 -212
- package/template/.aioson/locales/en/agents/neo.md +0 -8
- package/template/.aioson/locales/en/agents/orache.md +0 -6
- package/template/.aioson/locales/en/agents/orchestrator.md +0 -189
- package/template/.aioson/locales/en/agents/pair.md +0 -5
- package/template/.aioson/locales/en/agents/pm.md +0 -84
- package/template/.aioson/locales/en/agents/product.md +0 -378
- package/template/.aioson/locales/en/agents/profiler-enricher.md +0 -5
- package/template/.aioson/locales/en/agents/profiler-forge.md +0 -5
- package/template/.aioson/locales/en/agents/profiler-researcher.md +0 -5
- package/template/.aioson/locales/en/agents/qa.md +0 -270
- package/template/.aioson/locales/en/agents/setup.md +0 -421
- package/template/.aioson/locales/en/agents/sheldon.md +0 -455
- package/template/.aioson/locales/en/agents/squad.md +0 -449
- package/template/.aioson/locales/en/agents/tester.md +0 -6
- package/template/.aioson/locales/en/agents/ux-ui.md +0 -668
- package/template/.aioson/locales/es/agents/analyst.md +0 -225
- package/template/.aioson/locales/es/agents/architect.md +0 -245
- package/template/.aioson/locales/es/agents/dev.md +0 -370
- package/template/.aioson/locales/es/agents/deyvin.md +0 -99
- package/template/.aioson/locales/es/agents/discovery-design-doc.md +0 -21
- package/template/.aioson/locales/es/agents/genome.md +0 -104
- package/template/.aioson/locales/es/agents/neo.md +0 -50
- package/template/.aioson/locales/es/agents/orache.md +0 -105
- package/template/.aioson/locales/es/agents/orchestrator.md +0 -194
- package/template/.aioson/locales/es/agents/pair.md +0 -7
- package/template/.aioson/locales/es/agents/pm.md +0 -90
- package/template/.aioson/locales/es/agents/product.md +0 -372
- package/template/.aioson/locales/es/agents/profiler-enricher.md +0 -7
- package/template/.aioson/locales/es/agents/profiler-forge.md +0 -7
- package/template/.aioson/locales/es/agents/profiler-researcher.md +0 -7
- package/template/.aioson/locales/es/agents/qa.md +0 -198
- package/template/.aioson/locales/es/agents/setup.md +0 -405
- package/template/.aioson/locales/es/agents/sheldon.md +0 -309
- package/template/.aioson/locales/es/agents/squad.md +0 -532
- package/template/.aioson/locales/es/agents/tester.md +0 -9
- package/template/.aioson/locales/es/agents/ux-ui.md +0 -212
- package/template/.aioson/locales/fr/agents/analyst.md +0 -225
- package/template/.aioson/locales/fr/agents/architect.md +0 -245
- package/template/.aioson/locales/fr/agents/dev.md +0 -370
- package/template/.aioson/locales/fr/agents/deyvin.md +0 -99
- package/template/.aioson/locales/fr/agents/discovery-design-doc.md +0 -21
- package/template/.aioson/locales/fr/agents/genome.md +0 -104
- package/template/.aioson/locales/fr/agents/neo.md +0 -50
- package/template/.aioson/locales/fr/agents/orache.md +0 -106
- package/template/.aioson/locales/fr/agents/orchestrator.md +0 -194
- package/template/.aioson/locales/fr/agents/pair.md +0 -7
- package/template/.aioson/locales/fr/agents/pm.md +0 -90
- package/template/.aioson/locales/fr/agents/product.md +0 -372
- package/template/.aioson/locales/fr/agents/profiler-enricher.md +0 -7
- package/template/.aioson/locales/fr/agents/profiler-forge.md +0 -7
- package/template/.aioson/locales/fr/agents/profiler-researcher.md +0 -7
- package/template/.aioson/locales/fr/agents/qa.md +0 -198
- package/template/.aioson/locales/fr/agents/setup.md +0 -405
- package/template/.aioson/locales/fr/agents/sheldon.md +0 -309
- package/template/.aioson/locales/fr/agents/squad.md +0 -532
- package/template/.aioson/locales/fr/agents/tester.md +0 -9
- package/template/.aioson/locales/fr/agents/ux-ui.md +0 -212
- package/template/.aioson/locales/pt-BR/agents/analyst.md +0 -319
- package/template/.aioson/locales/pt-BR/agents/architect.md +0 -284
- package/template/.aioson/locales/pt-BR/agents/dev.md +0 -483
- package/template/.aioson/locales/pt-BR/agents/deyvin.md +0 -184
- package/template/.aioson/locales/pt-BR/agents/discovery-design-doc.md +0 -198
- package/template/.aioson/locales/pt-BR/agents/genome.md +0 -297
- package/template/.aioson/locales/pt-BR/agents/neo.md +0 -208
- package/template/.aioson/locales/pt-BR/agents/orache.md +0 -137
- package/template/.aioson/locales/pt-BR/agents/orchestrator.md +0 -324
- package/template/.aioson/locales/pt-BR/agents/pair.md +0 -5
- package/template/.aioson/locales/pt-BR/agents/pm.md +0 -182
- package/template/.aioson/locales/pt-BR/agents/product.md +0 -466
- package/template/.aioson/locales/pt-BR/agents/profiler-enricher.md +0 -5
- package/template/.aioson/locales/pt-BR/agents/profiler-forge.md +0 -5
- package/template/.aioson/locales/pt-BR/agents/profiler-researcher.md +0 -5
- package/template/.aioson/locales/pt-BR/agents/qa.md +0 -300
- package/template/.aioson/locales/pt-BR/agents/setup.md +0 -533
- package/template/.aioson/locales/pt-BR/agents/sheldon.md +0 -323
- package/template/.aioson/locales/pt-BR/agents/squad.md +0 -1330
- package/template/.aioson/locales/pt-BR/agents/tester.md +0 -449
- package/template/.aioson/locales/pt-BR/agents/ux-ui.md +0 -669
- package/template/.aioson/skills/design-system/components/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/dashboards/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/foundations/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/motion/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/patterns/SKILL.md:Zone.Identifier +0 -0
package/src/doctor.js
CHANGED
|
@@ -5,8 +5,8 @@ const path = require('node:path');
|
|
|
5
5
|
const { REQUIRED_FILES } = require('./constants');
|
|
6
6
|
const { installTemplate, TEMPLATE_DIR } = require('./installer');
|
|
7
7
|
const { exists, copyFileWithDir } = require('./utils');
|
|
8
|
-
const { validateProjectContextFile } = require('./context');
|
|
9
|
-
const { applyAgentLocale
|
|
8
|
+
const { validateProjectContextFile, getInteractionLanguage } = require('./context');
|
|
9
|
+
const { applyAgentLocale } = require('./locales');
|
|
10
10
|
|
|
11
11
|
function parseMajor(version) {
|
|
12
12
|
const cleaned = String(version || '').replace(/^v/, '');
|
|
@@ -34,6 +34,14 @@ const GEMINI_COMMAND_EXPECTATIONS = [
|
|
|
34
34
|
{ file: '.gemini/commands/aios-orchestrator.toml', agent: 'orchestrator' }
|
|
35
35
|
];
|
|
36
36
|
|
|
37
|
+
const DESIGN_GOVERNANCE_FILES = [
|
|
38
|
+
'.aioson/design-docs/code-reuse.md',
|
|
39
|
+
'.aioson/design-docs/componentization.md',
|
|
40
|
+
'.aioson/design-docs/file-size.md',
|
|
41
|
+
'.aioson/design-docs/folder-structure.md',
|
|
42
|
+
'.aioson/design-docs/naming.md'
|
|
43
|
+
];
|
|
44
|
+
|
|
37
45
|
const GATEWAY_FILE_BY_CHECK_ID = {
|
|
38
46
|
'gateway:claude:contract': 'CLAUDE.md',
|
|
39
47
|
'gateway:codex:contract': 'AGENTS.md',
|
|
@@ -79,6 +87,16 @@ async function runDoctor(targetDir) {
|
|
|
79
87
|
});
|
|
80
88
|
}
|
|
81
89
|
|
|
90
|
+
for (const rel of DESIGN_GOVERNANCE_FILES) {
|
|
91
|
+
checks.push({
|
|
92
|
+
id: `design-governance:${rel}`,
|
|
93
|
+
key: 'doctor.required_file',
|
|
94
|
+
params: { rel },
|
|
95
|
+
ok: await exists(path.join(targetDir, rel)),
|
|
96
|
+
hintKey: 'doctor.context_hint'
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
82
100
|
const gatewayChecks = [
|
|
83
101
|
{
|
|
84
102
|
id: 'gateway:claude:contract',
|
|
@@ -130,7 +148,7 @@ async function runDoctor(targetDir) {
|
|
|
130
148
|
key: 'doctor.gateway_gemini_command_pointer',
|
|
131
149
|
params: { file: expectation.file },
|
|
132
150
|
ok: await fileContainsAll(commandPath, [
|
|
133
|
-
|
|
151
|
+
`@{ .aioson/agents/${expectation.agent}.md }`
|
|
134
152
|
]),
|
|
135
153
|
hintKey: 'doctor.gateway_gemini_command_pointer_hint',
|
|
136
154
|
hintParams: {
|
|
@@ -250,14 +268,36 @@ async function applyDoctorFixes(targetDir, report, options = {}) {
|
|
|
250
268
|
});
|
|
251
269
|
}
|
|
252
270
|
|
|
271
|
+
const missingDesignGovernanceFiles = report.checks
|
|
272
|
+
.filter((check) => !check.ok && check.id.startsWith('design-governance:'))
|
|
273
|
+
.map((check) => check.params.rel);
|
|
274
|
+
|
|
275
|
+
if (missingDesignGovernanceFiles.length > 0) {
|
|
276
|
+
const restored = await restoreTemplateFiles(targetDir, missingDesignGovernanceFiles, { dryRun });
|
|
277
|
+
if (restored.length > 0) changedCount += restored.length;
|
|
278
|
+
actions.push({
|
|
279
|
+
id: 'design_governance',
|
|
280
|
+
applied: restored.length > 0,
|
|
281
|
+
count: restored.length,
|
|
282
|
+
missingCount: missingDesignGovernanceFiles.length
|
|
283
|
+
});
|
|
284
|
+
} else {
|
|
285
|
+
actions.push({
|
|
286
|
+
id: 'design_governance',
|
|
287
|
+
applied: false,
|
|
288
|
+
skipped: true,
|
|
289
|
+
count: 0,
|
|
290
|
+
missingCount: 0
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
253
294
|
if (
|
|
254
295
|
report.contextValidation &&
|
|
255
296
|
report.contextValidation.parsed &&
|
|
256
297
|
report.contextValidation.valid &&
|
|
257
|
-
report.contextValidation.data
|
|
258
|
-
report.contextValidation.data.conversation_language
|
|
298
|
+
report.contextValidation.data
|
|
259
299
|
) {
|
|
260
|
-
const locale =
|
|
300
|
+
const locale = getInteractionLanguage(report.contextValidation.data, 'en');
|
|
261
301
|
const localeResult = await applyAgentLocale(targetDir, locale, { dryRun });
|
|
262
302
|
if (localeResult.copied.length > 0) changedCount += localeResult.copied.length;
|
|
263
303
|
actions.push({
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { isValidSlug } = require('./schema');
|
|
6
|
+
|
|
7
|
+
const FEATURES_SUBDIR = 'features';
|
|
8
|
+
const DOSSIER_FILENAME = 'dossier.md';
|
|
9
|
+
|
|
10
|
+
const VALID_ROLES = new Set([
|
|
11
|
+
'command-entry', 'core-module', 'io-layer', 'store', 'schema',
|
|
12
|
+
'test', 'util', 'config', 'integration', 'cli', 'other'
|
|
13
|
+
]);
|
|
14
|
+
const VALID_COUPLING = new Set(['low', 'medium', 'high']);
|
|
15
|
+
const LINES_REGEX = /^\d+-\d+$/;
|
|
16
|
+
|
|
17
|
+
function dossierPath(contextDir, slug) {
|
|
18
|
+
return path.join(contextDir, FEATURES_SUBDIR, slug, DOSSIER_FILENAME);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function parseCodeMapBlock(raw) {
|
|
22
|
+
const marker = '```yaml\n';
|
|
23
|
+
const start = raw.indexOf('## Code Map\n');
|
|
24
|
+
if (start === -1) return null;
|
|
25
|
+
const blockStart = raw.indexOf(marker, start);
|
|
26
|
+
if (blockStart === -1) return null;
|
|
27
|
+
const codeStart = blockStart + marker.length;
|
|
28
|
+
const codeEnd = raw.indexOf('\n```', codeStart);
|
|
29
|
+
if (codeEnd === -1) return null;
|
|
30
|
+
return { blockStart, codeStart, codeEnd };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parseYamlCodeMap(yamlText) {
|
|
34
|
+
const result = { files: [], modules: [], patterns: [] };
|
|
35
|
+
const lines = yamlText.split('\n');
|
|
36
|
+
let currentSection = null;
|
|
37
|
+
let currentItem = null;
|
|
38
|
+
|
|
39
|
+
const flushItem = () => {
|
|
40
|
+
if (currentItem !== null && currentSection !== null) {
|
|
41
|
+
result[currentSection].push(currentItem);
|
|
42
|
+
currentItem = null;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
for (const line of lines) {
|
|
47
|
+
if (!line.trim()) continue;
|
|
48
|
+
|
|
49
|
+
// Section header
|
|
50
|
+
const sectionMatch = line.match(/^(files|modules|patterns):\s*(\[\])?$/);
|
|
51
|
+
if (sectionMatch) {
|
|
52
|
+
flushItem();
|
|
53
|
+
currentSection = sectionMatch[1];
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// List item start: "- key: value"
|
|
58
|
+
const listItemMatch = line.match(/^-\s+(\w+):\s*(.*)$/);
|
|
59
|
+
if (listItemMatch && currentSection) {
|
|
60
|
+
flushItem();
|
|
61
|
+
currentItem = {};
|
|
62
|
+
currentItem[listItemMatch[1]] = listItemMatch[2].replace(/^["']|["']$/g, '');
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Continuation: " key: value"
|
|
67
|
+
const kvMatch = line.match(/^\s+(\w+):\s*(.*)$/);
|
|
68
|
+
if (kvMatch && currentItem !== null) {
|
|
69
|
+
currentItem[kvMatch[1]] = kvMatch[2].replace(/^["']|["']$/g, '');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
flushItem();
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function serializeCodeMap(map) {
|
|
77
|
+
const lines = [];
|
|
78
|
+
const serializeList = (key, items) => {
|
|
79
|
+
if (!items || items.length === 0) {
|
|
80
|
+
lines.push(`${key}: []`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
lines.push(`${key}:`);
|
|
84
|
+
for (const item of items) {
|
|
85
|
+
const entries = Object.entries(item);
|
|
86
|
+
if (entries.length === 0) continue;
|
|
87
|
+
lines.push(`- ${entries[0][0]}: ${entries[0][1]}`);
|
|
88
|
+
for (const [k, v] of entries.slice(1)) {
|
|
89
|
+
lines.push(` ${k}: ${v}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
serializeList('files', map.files);
|
|
94
|
+
serializeList('modules', map.modules);
|
|
95
|
+
serializeList('patterns', map.patterns);
|
|
96
|
+
return lines.join('\n');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function validateFileEntry(entry) {
|
|
100
|
+
const errors = [];
|
|
101
|
+
if (!entry.path || typeof entry.path !== 'string') errors.push('path is required');
|
|
102
|
+
if (entry.lines && !LINES_REGEX.test(entry.lines)) errors.push(`lines must be int-int (got: ${entry.lines})`);
|
|
103
|
+
if (entry.role && !VALID_ROLES.has(entry.role)) errors.push(`role must be one of [${[...VALID_ROLES].join(', ')}]`);
|
|
104
|
+
if (entry.coupling_risk && !VALID_COUPLING.has(entry.coupling_risk)) errors.push(`coupling_risk must be low|medium|high`);
|
|
105
|
+
return errors;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function addCodemap({ slug, contextDir, filePath, lines, role, coupling, addedBy, now = () => new Date() }) {
|
|
109
|
+
if (!isValidSlug(slug)) {
|
|
110
|
+
const err = new Error(`invalid slug: ${JSON.stringify(slug)}`);
|
|
111
|
+
err.code = 'EDOSSIERSLUG';
|
|
112
|
+
throw err;
|
|
113
|
+
}
|
|
114
|
+
const errors = validateFileEntry({ path: filePath, lines, role, coupling_risk: coupling });
|
|
115
|
+
if (errors.length > 0) {
|
|
116
|
+
const err = new Error(`invalid codemap entry: ${errors.join('; ')}`);
|
|
117
|
+
err.code = 'ECODEMAPVALIDATION';
|
|
118
|
+
err.errors = errors;
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Warn (not error) when the file path doesn't exist on disk — may be a planned file
|
|
123
|
+
let fileWarn = null;
|
|
124
|
+
try {
|
|
125
|
+
await fs.access(path.resolve(contextDir, '..', '..', filePath));
|
|
126
|
+
} catch {
|
|
127
|
+
fileWarn = 'file_not_found';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const p = dossierPath(contextDir, slug);
|
|
131
|
+
let raw;
|
|
132
|
+
try {
|
|
133
|
+
raw = await fs.readFile(p, 'utf8');
|
|
134
|
+
} catch (err) {
|
|
135
|
+
if (err && err.code === 'ENOENT') {
|
|
136
|
+
const e = new Error(`dossier not found for slug "${slug}"`);
|
|
137
|
+
e.code = 'EDOSSIERMISSING';
|
|
138
|
+
throw e;
|
|
139
|
+
}
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const parsed = parseCodeMapBlock(raw);
|
|
144
|
+
let map = { files: [], modules: [], patterns: [] };
|
|
145
|
+
if (parsed) {
|
|
146
|
+
const yamlText = raw.slice(parsed.codeStart, parsed.codeEnd);
|
|
147
|
+
map = parseYamlCodeMap(yamlText);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Idempotency: dedupe by (path, lines)
|
|
151
|
+
const existing = map.files.find(f => f.path === filePath && f.lines === (lines || ''));
|
|
152
|
+
if (existing) {
|
|
153
|
+
return { added: false, path: filePath, warn: fileWarn };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const entry = { path: filePath };
|
|
157
|
+
if (lines) entry.lines = lines;
|
|
158
|
+
if (role) entry.role = role;
|
|
159
|
+
if (coupling) entry.coupling_risk = coupling;
|
|
160
|
+
if (addedBy) entry.added_by = addedBy;
|
|
161
|
+
entry.added_at = now().toISOString();
|
|
162
|
+
|
|
163
|
+
map.files.push(entry);
|
|
164
|
+
const newYaml = serializeCodeMap(map);
|
|
165
|
+
|
|
166
|
+
let newRaw;
|
|
167
|
+
if (parsed) {
|
|
168
|
+
newRaw = raw.slice(0, parsed.codeStart) + newYaml + raw.slice(parsed.codeEnd);
|
|
169
|
+
} else {
|
|
170
|
+
// Insert a Code Map section if absent (shouldn't happen in well-formed dossiers)
|
|
171
|
+
newRaw = raw + `\n## Code Map\n\n\`\`\`yaml\n${newYaml}\n\`\`\`\n`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await fs.writeFile(p, newRaw, 'utf8');
|
|
175
|
+
return { added: true, path: filePath, warn: fileWarn };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function linkRule({ slug, contextDir, rulePath, reason, targetDir }) {
|
|
179
|
+
if (!isValidSlug(slug)) {
|
|
180
|
+
const err = new Error(`invalid slug: ${JSON.stringify(slug)}`);
|
|
181
|
+
err.code = 'EDOSSIERSLUG';
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Validate rule path exists in .aioson/rules/ or .aioson/design-docs/
|
|
186
|
+
const base = targetDir || process.cwd();
|
|
187
|
+
const absRule = path.resolve(base, rulePath);
|
|
188
|
+
const rulesDir = path.join(base, '.aioson', 'rules');
|
|
189
|
+
const designDocsDir = path.join(base, '.aioson', 'design-docs');
|
|
190
|
+
|
|
191
|
+
const inRules = absRule.startsWith(rulesDir + path.sep) || absRule.startsWith(rulesDir + '/');
|
|
192
|
+
const inDesignDocs = absRule.startsWith(designDocsDir + path.sep) || absRule.startsWith(designDocsDir + '/');
|
|
193
|
+
|
|
194
|
+
if (!inRules && !inDesignDocs) {
|
|
195
|
+
const err = new Error(`rule path must be under .aioson/rules/ or .aioson/design-docs/ (got: ${rulePath})`);
|
|
196
|
+
err.code = 'ELINKREULEPATH';
|
|
197
|
+
throw err;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
await fs.access(absRule);
|
|
202
|
+
} catch {
|
|
203
|
+
const err = new Error(`rule file not found: ${absRule}`);
|
|
204
|
+
err.code = 'ELINKREULENOTFOUND';
|
|
205
|
+
err.path = absRule;
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const p = dossierPath(contextDir, slug);
|
|
210
|
+
let raw;
|
|
211
|
+
try {
|
|
212
|
+
raw = await fs.readFile(p, 'utf8');
|
|
213
|
+
} catch (err) {
|
|
214
|
+
if (err && err.code === 'ENOENT') {
|
|
215
|
+
const e = new Error(`dossier not found for slug "${slug}"`);
|
|
216
|
+
e.code = 'EDOSSIERMISSING';
|
|
217
|
+
throw e;
|
|
218
|
+
}
|
|
219
|
+
throw err;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const rulesSection = '## Rules & Design-Docs aplicáveis';
|
|
223
|
+
const entry = `- [${rulePath}](${rulePath})${reason ? ` — ${reason}` : ''}`;
|
|
224
|
+
|
|
225
|
+
// Idempotency: don't duplicate same path
|
|
226
|
+
if (raw.includes(`[${rulePath}]`)) {
|
|
227
|
+
return { added: false, path: rulePath };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const lines = raw.split('\n');
|
|
231
|
+
let sectionEnd = lines.length;
|
|
232
|
+
let inSection = false;
|
|
233
|
+
|
|
234
|
+
for (let i = 0; i < lines.length; i++) {
|
|
235
|
+
if (lines[i].trimEnd() === rulesSection) {
|
|
236
|
+
inSection = true;
|
|
237
|
+
} else if (inSection && /^## /.test(lines[i])) {
|
|
238
|
+
sectionEnd = i;
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const before = lines.slice(0, sectionEnd);
|
|
244
|
+
const after = lines.slice(sectionEnd);
|
|
245
|
+
|
|
246
|
+
// Remove placeholder line if present
|
|
247
|
+
const placeholderIdx = before.findIndex(l => l.includes('_(vazio —') || l.includes('_(empty'));
|
|
248
|
+
if (placeholderIdx !== -1) before.splice(placeholderIdx, 1);
|
|
249
|
+
|
|
250
|
+
while (before.length > 0 && before[before.length - 1].trim() === '') before.pop();
|
|
251
|
+
before.push('', entry);
|
|
252
|
+
|
|
253
|
+
const newRaw = [...before, '', ...after].join('\n');
|
|
254
|
+
await fs.writeFile(p, newRaw, 'utf8');
|
|
255
|
+
return { added: true, path: rulePath };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
module.exports = {
|
|
259
|
+
addCodemap,
|
|
260
|
+
linkRule,
|
|
261
|
+
parseCodeMapBlock,
|
|
262
|
+
parseYamlCodeMap,
|
|
263
|
+
serializeCodeMap,
|
|
264
|
+
validateFileEntry,
|
|
265
|
+
VALID_ROLES,
|
|
266
|
+
VALID_COUPLING
|
|
267
|
+
};
|
|
@@ -0,0 +1,222 @@
|
|
|
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 { isValidSlug, SCHEMA_VERSION, ALLOWED_CLASSIFICATIONS, validateFrontmatter } = require('./schema');
|
|
8
|
+
const { featureDir, dossierPath, parseSections, parseFrontmatter } = require('./store');
|
|
9
|
+
|
|
10
|
+
// Maps canonical artifact filenames to the agent that typically produces them.
|
|
11
|
+
const ARTIFACT_AGENTS = {
|
|
12
|
+
[`prd`]: 'product',
|
|
13
|
+
[`spec`]: 'architect',
|
|
14
|
+
[`sheldon-enrichment`]: 'sheldon',
|
|
15
|
+
[`requirements`]: 'analyst',
|
|
16
|
+
[`architecture`]: 'architect'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
async function fileExists(p) {
|
|
20
|
+
try { await fs.access(p); return true; } catch { return false; }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function readText(p) {
|
|
24
|
+
try { return await fs.readFile(p, 'utf8'); } catch { return null; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function extractSection(markdown, headingNames) {
|
|
28
|
+
if (!markdown) return null;
|
|
29
|
+
const sections = parseSections(markdown);
|
|
30
|
+
for (const name of headingNames) {
|
|
31
|
+
if (sections[name]) {
|
|
32
|
+
const t = sections[name].trim();
|
|
33
|
+
if (t) return t;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function artifactHash(artifacts) {
|
|
40
|
+
const keys = Object.keys(artifacts).sort();
|
|
41
|
+
const payload = keys.map(k => `${k}=${artifacts[k] ? artifacts[k].length : 0}`).join(';');
|
|
42
|
+
return crypto.createHash('sha256').update(payload).digest('hex').slice(0, 12);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function buildBootstrapDossier({ slug, classification, createdAt, artifacts, why, what, agentTrail }) {
|
|
46
|
+
const fm = [
|
|
47
|
+
'---',
|
|
48
|
+
`feature_slug: ${slug}`,
|
|
49
|
+
`schema_version: "${SCHEMA_VERSION}"`,
|
|
50
|
+
`created_by: dossier-init`,
|
|
51
|
+
`created_at: ${createdAt}`,
|
|
52
|
+
`status: ${artifacts.done ? 'closed' : 'active'}`,
|
|
53
|
+
`classification: ${classification}`,
|
|
54
|
+
`last_updated_by: dossier-init`,
|
|
55
|
+
`last_updated_at: ${createdAt}`,
|
|
56
|
+
`bootstrap_hash: ${artifactHash(artifacts)}`,
|
|
57
|
+
'---',
|
|
58
|
+
''
|
|
59
|
+
].join('\n');
|
|
60
|
+
|
|
61
|
+
const whyText = why || '_(não encontrado — preencher manualmente)_';
|
|
62
|
+
const whatText = what || '_(não encontrado — preencher manualmente)_';
|
|
63
|
+
|
|
64
|
+
const trailLines = agentTrail.length > 0
|
|
65
|
+
? agentTrail.map(e => `- **${e.timestamp}** | @${e.agent} | _${e.artifact}_`).join('\n')
|
|
66
|
+
: '_(sintetizado a partir de artefatos existentes)_';
|
|
67
|
+
|
|
68
|
+
const body = [
|
|
69
|
+
'## Why',
|
|
70
|
+
'',
|
|
71
|
+
whyText,
|
|
72
|
+
'',
|
|
73
|
+
'## What',
|
|
74
|
+
'',
|
|
75
|
+
whatText,
|
|
76
|
+
'',
|
|
77
|
+
'## Code Map',
|
|
78
|
+
'',
|
|
79
|
+
'```yaml',
|
|
80
|
+
'files: []',
|
|
81
|
+
'modules: []',
|
|
82
|
+
'patterns: []',
|
|
83
|
+
'```',
|
|
84
|
+
'',
|
|
85
|
+
'## Rules & Design-Docs aplicáveis',
|
|
86
|
+
'',
|
|
87
|
+
'_(populado via dossier:link-rule)_',
|
|
88
|
+
'',
|
|
89
|
+
'## Agent Trail',
|
|
90
|
+
'',
|
|
91
|
+
trailLines,
|
|
92
|
+
'',
|
|
93
|
+
'## Revision Requests',
|
|
94
|
+
'',
|
|
95
|
+
'_(vazio)_',
|
|
96
|
+
''
|
|
97
|
+
].join('\n');
|
|
98
|
+
|
|
99
|
+
return fm + body;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function initFromExisting({ slug, contextDir, classification, targetDir, now = () => new Date() } = {}) {
|
|
103
|
+
if (!isValidSlug(slug)) {
|
|
104
|
+
const err = new Error(`invalid slug (must be kebab-case): ${JSON.stringify(slug)}`);
|
|
105
|
+
err.code = 'EDOSSIERSLUG';
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const dir = featureDir(contextDir, slug);
|
|
110
|
+
const p = dossierPath(contextDir, slug);
|
|
111
|
+
|
|
112
|
+
// Check if dossier already exists (idempotency guard)
|
|
113
|
+
if (await fileExists(p)) {
|
|
114
|
+
// Check if it has the same bootstrap_hash — if artifacts unchanged, it's a no-op
|
|
115
|
+
const raw = await readText(p);
|
|
116
|
+
const fmParse = raw ? parseFrontmatter(raw) : { ok: false };
|
|
117
|
+
if (fmParse.ok && fmParse.data.bootstrap_hash) {
|
|
118
|
+
// Re-compute hash to detect changes
|
|
119
|
+
const artifacts = await gatherArtifacts(slug, contextDir, targetDir);
|
|
120
|
+
const newHash = artifactHash(artifacts);
|
|
121
|
+
if (newHash === fmParse.data.bootstrap_hash) {
|
|
122
|
+
return { created: false, reason: 'unchanged', path: p };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const err = new Error(`dossier already exists at ${p} — use dossier:show to inspect`);
|
|
126
|
+
err.code = 'EDOSSIEREXISTS';
|
|
127
|
+
err.path = p;
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const artifacts = await gatherArtifacts(slug, contextDir, targetDir);
|
|
132
|
+
|
|
133
|
+
// Must have at least one artifact to synthesize from
|
|
134
|
+
const hasAny = Object.values(artifacts).some(Boolean);
|
|
135
|
+
if (!hasAny) {
|
|
136
|
+
const err = new Error(`no artifacts found for slug "${slug}" — use dossier:init without --from-existing`);
|
|
137
|
+
err.code = 'EBOOTSTRAPEMPTY';
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Resolve classification
|
|
142
|
+
let cls = classification;
|
|
143
|
+
if (!cls) {
|
|
144
|
+
const ctxPath = path.join(contextDir, 'project.context.md');
|
|
145
|
+
const ctxRaw = await readText(ctxPath);
|
|
146
|
+
if (ctxRaw) {
|
|
147
|
+
const m = ctxRaw.match(/^classification:\s*"?([A-Z]+)"?\s*$/m);
|
|
148
|
+
if (m && ALLOWED_CLASSIFICATIONS.has(m[1])) cls = m[1];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
cls = cls || 'MEDIUM';
|
|
152
|
+
|
|
153
|
+
// Extract Why/What
|
|
154
|
+
const prdContent = artifacts.prd || artifacts.prdGlobal;
|
|
155
|
+
const why = extractSection(prdContent, ['Problem', 'Why', 'Vision', 'Problema']);
|
|
156
|
+
const what = extractSection(prdContent, ['Escopo do MVP', 'Scope', 'What', 'Escopo']);
|
|
157
|
+
|
|
158
|
+
// Build agent trail from artifact metadata
|
|
159
|
+
const createdAt = now().toISOString();
|
|
160
|
+
const agentTrail = buildAgentTrail(artifacts, createdAt);
|
|
161
|
+
|
|
162
|
+
// Validate frontmatter before writing
|
|
163
|
+
const fmCheck = validateFrontmatter({
|
|
164
|
+
feature_slug: slug,
|
|
165
|
+
schema_version: SCHEMA_VERSION,
|
|
166
|
+
created_by: 'dossier-init',
|
|
167
|
+
created_at: createdAt,
|
|
168
|
+
status: artifacts.done ? 'closed' : 'active',
|
|
169
|
+
classification: cls,
|
|
170
|
+
last_updated_by: 'dossier-init',
|
|
171
|
+
last_updated_at: createdAt
|
|
172
|
+
});
|
|
173
|
+
if (!fmCheck.valid) {
|
|
174
|
+
const err = new Error(`schema error: ${fmCheck.errors.join('; ')}`);
|
|
175
|
+
err.code = 'EDOSSIERSCHEMA';
|
|
176
|
+
throw err;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const markdown = buildBootstrapDossier({ slug, classification: cls, createdAt, artifacts, why, what, agentTrail });
|
|
180
|
+
|
|
181
|
+
await fs.mkdir(dir, { recursive: true });
|
|
182
|
+
await fs.writeFile(p, markdown, 'utf8');
|
|
183
|
+
|
|
184
|
+
return { created: true, path: p, classification: cls, artifactsFound: Object.keys(artifacts).filter(k => artifacts[k]) };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function gatherArtifacts(slug, contextDir, targetDir) {
|
|
188
|
+
const base = targetDir || path.join(contextDir, '..', '..');
|
|
189
|
+
const artifacts = {};
|
|
190
|
+
|
|
191
|
+
// Per-slug artifacts
|
|
192
|
+
artifacts.prd = await readText(path.join(contextDir, `prd-${slug}.md`));
|
|
193
|
+
artifacts.spec = await readText(path.join(contextDir, `spec-${slug}.md`));
|
|
194
|
+
artifacts.sheldonEnrichment = await readText(path.join(contextDir, `sheldon-enrichment-${slug}.md`));
|
|
195
|
+
artifacts.requirements = await readText(path.join(contextDir, `requirements-${slug}.md`));
|
|
196
|
+
artifacts.architecture = await readText(path.join(contextDir, `architecture-${slug}.md`));
|
|
197
|
+
|
|
198
|
+
// Global PRD fallback
|
|
199
|
+
artifacts.prdGlobal = !artifacts.prd ? await readText(path.join(contextDir, 'prd.md')) : null;
|
|
200
|
+
|
|
201
|
+
// done/ directory (feature already closed)
|
|
202
|
+
const doneDir = path.join(contextDir, 'done', slug);
|
|
203
|
+
artifacts.done = await fileExists(doneDir) ? doneDir : null;
|
|
204
|
+
|
|
205
|
+
return artifacts;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function buildAgentTrail(artifacts, fallbackTimestamp) {
|
|
209
|
+
const trail = [];
|
|
210
|
+
const add = (artifact, agent) => {
|
|
211
|
+
if (artifacts[artifact]) trail.push({ artifact, agent, timestamp: fallbackTimestamp });
|
|
212
|
+
};
|
|
213
|
+
add('prd', 'product');
|
|
214
|
+
add('prdGlobal', 'product');
|
|
215
|
+
add('requirements', 'analyst');
|
|
216
|
+
add('sheldonEnrichment', 'sheldon');
|
|
217
|
+
add('architecture', 'architect');
|
|
218
|
+
add('spec', 'architect');
|
|
219
|
+
return trail;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = { initFromExisting };
|