@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
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* aioson gate:approve — approve a phase gate for a feature.
|
|
5
|
+
*
|
|
6
|
+
* Validates with gate:check before writing. If gate:check fails, blocks approval.
|
|
7
|
+
* Writes flat frontmatter fields to spec-{slug}.md:
|
|
8
|
+
* gate_requirements, gate_design, gate_plan, gate_execution
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* aioson gate:approve . --feature=checkout --gate=C
|
|
12
|
+
* aioson gate:approve . --feature=checkout --gate=C --json
|
|
13
|
+
*
|
|
14
|
+
* If gate:check fails, shows exact manual fallback: file, field, and value to set.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
const fs = require('node:fs/promises');
|
|
19
|
+
const {
|
|
20
|
+
contextDir,
|
|
21
|
+
readFileSafe,
|
|
22
|
+
fileExists,
|
|
23
|
+
parseFrontmatter,
|
|
24
|
+
parseGatesFromSpec,
|
|
25
|
+
GATE_NAMES,
|
|
26
|
+
GATE_ALIASES
|
|
27
|
+
} = require('../preflight-engine');
|
|
28
|
+
const { runGateCheck } = require('./gate-check');
|
|
29
|
+
|
|
30
|
+
const BAR = '━'.repeat(45);
|
|
31
|
+
|
|
32
|
+
const GATE_FLAT_FIELDS = {
|
|
33
|
+
A: 'gate_requirements',
|
|
34
|
+
B: 'gate_design',
|
|
35
|
+
C: 'gate_plan',
|
|
36
|
+
D: 'gate_execution'
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const GATE_NEXT_AGENTS = {
|
|
40
|
+
A: { agent: '@architect', action: '/architect', why: 'Gate A (requirements) approved — architecture can proceed' },
|
|
41
|
+
B: { agent: '@pm', action: '/pm', why: 'Gate B (design) approved — implementation plan can be written' },
|
|
42
|
+
C: { agent: '@orchestrator or @dev', action: '/orchestrator (MEDIUM) or /dev (MICRO/SMALL)', why: 'Gate C (plan) approved — implementation can proceed' },
|
|
43
|
+
D: { agent: 'feature complete', action: 'mark feature done in features.md', why: 'Gate D (execution) approved — feature is complete' }
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Update or insert a flat frontmatter field in a markdown file.
|
|
48
|
+
* Preserves all other frontmatter fields. Uses flat key: value format.
|
|
49
|
+
*/
|
|
50
|
+
function updateFrontmatterField(content, field, value) {
|
|
51
|
+
const fmMatch = content.match(/^(---\r?\n)([\s\S]*?)(\r?\n---)/);
|
|
52
|
+
if (!fmMatch) {
|
|
53
|
+
// No frontmatter — prepend it
|
|
54
|
+
return `---\n${field}: ${value}\n---\n\n${content}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const prefix = fmMatch[1];
|
|
58
|
+
const fmBody = fmMatch[2];
|
|
59
|
+
const suffix = fmMatch[3];
|
|
60
|
+
const rest = content.slice(fmMatch[0].length);
|
|
61
|
+
|
|
62
|
+
const lines = fmBody.split(/\r?\n/);
|
|
63
|
+
let found = false;
|
|
64
|
+
const updated = lines.map((line) => {
|
|
65
|
+
const colonIdx = line.indexOf(':');
|
|
66
|
+
if (colonIdx === -1) return line;
|
|
67
|
+
const key = line.slice(0, colonIdx).trim();
|
|
68
|
+
if (key === field) {
|
|
69
|
+
found = true;
|
|
70
|
+
return `${field}: ${value}`;
|
|
71
|
+
}
|
|
72
|
+
return line;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!found) updated.push(`${field}: ${value}`);
|
|
76
|
+
|
|
77
|
+
return `${prefix}${updated.join('\n')}${suffix}${rest}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function runGateApprove({ args, options = {}, logger }) {
|
|
81
|
+
const targetDir = path.resolve(process.cwd(), args[0] || '.');
|
|
82
|
+
const slug = options.feature ? String(options.feature) : null;
|
|
83
|
+
let gateLetter = options.gate ? String(options.gate).toUpperCase() : null;
|
|
84
|
+
|
|
85
|
+
if (!slug) {
|
|
86
|
+
if (options.json) return { ok: false, reason: 'missing_feature' };
|
|
87
|
+
logger.log('--feature=<slug> is required.');
|
|
88
|
+
return { ok: false };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!gateLetter) {
|
|
92
|
+
if (options.json) return { ok: false, reason: 'missing_gate' };
|
|
93
|
+
logger.log('--gate=<A|B|C|D> is required.');
|
|
94
|
+
return { ok: false };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Resolve aliases
|
|
98
|
+
if (GATE_ALIASES[gateLetter.toLowerCase()]) {
|
|
99
|
+
gateLetter = GATE_ALIASES[gateLetter.toLowerCase()];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!GATE_NAMES[gateLetter]) {
|
|
103
|
+
if (options.json) return { ok: false, reason: 'invalid_gate', gate: gateLetter };
|
|
104
|
+
logger.log(`Invalid gate: ${gateLetter}. Use A, B, C, or D.`);
|
|
105
|
+
return { ok: false };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Step 1: run gate:check first (AC-SDLC-06 — gate:approve fails if gate:check fails)
|
|
109
|
+
const silentLogger = { log: () => {} };
|
|
110
|
+
const check = await runGateCheck({
|
|
111
|
+
args: [targetDir],
|
|
112
|
+
options: { feature: slug, gate: gateLetter, json: true },
|
|
113
|
+
logger: silentLogger
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const specFile = path.join(contextDir(targetDir), `spec-${slug}.md`);
|
|
117
|
+
const specExists = await fileExists(specFile);
|
|
118
|
+
const specFieldName = GATE_FLAT_FIELDS[gateLetter];
|
|
119
|
+
const gateName = GATE_NAMES[gateLetter];
|
|
120
|
+
|
|
121
|
+
if (!check.ok) {
|
|
122
|
+
// Gate check failed — block approval and show manual fallback (AC-SDLC-08)
|
|
123
|
+
const result = {
|
|
124
|
+
ok: false,
|
|
125
|
+
blocked: true,
|
|
126
|
+
gate: gateLetter,
|
|
127
|
+
gate_name: gateName,
|
|
128
|
+
feature: slug,
|
|
129
|
+
reason: 'gate_check_failed',
|
|
130
|
+
missing: check.missing || [],
|
|
131
|
+
manual_fallback: specExists
|
|
132
|
+
? `To manually approve when prerequisites are met:\n File: .aioson/context/spec-${slug}.md\n Field: ${specFieldName}\n Value: approved`
|
|
133
|
+
: `spec-${slug}.md does not exist. Create it with ${specFieldName}: approved in frontmatter after prerequisites are satisfied.`
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
if (options.json) return result;
|
|
137
|
+
|
|
138
|
+
logger.log('');
|
|
139
|
+
logger.log(`Gate ${gateLetter} (${gateName}) — ${slug}`);
|
|
140
|
+
logger.log(BAR);
|
|
141
|
+
logger.log(`Result: ✗ BLOCKED — gate:check did not pass`);
|
|
142
|
+
logger.log('');
|
|
143
|
+
logger.log('Missing prerequisites:');
|
|
144
|
+
for (const m of check.missing || []) logger.log(` ✗ ${m}`);
|
|
145
|
+
logger.log('');
|
|
146
|
+
logger.log('Manual fallback (use only after all prerequisites are satisfied):');
|
|
147
|
+
logger.log(` File: .aioson/context/spec-${slug}.md`);
|
|
148
|
+
logger.log(` Field: ${specFieldName}`);
|
|
149
|
+
logger.log(` Value: approved`);
|
|
150
|
+
logger.log('');
|
|
151
|
+
logger.log('Tip: re-run gate:check after satisfying prerequisites, then gate:approve again.');
|
|
152
|
+
logger.log('');
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Step 2: write flat frontmatter field (AC-SDLC-07 — flat format, not nested phase_gates)
|
|
157
|
+
let content = specExists ? await readFileSafe(specFile) : null;
|
|
158
|
+
|
|
159
|
+
if (!content) {
|
|
160
|
+
// Create minimal spec with gate field
|
|
161
|
+
content = `---\nfeature: ${slug}\n${specFieldName}: approved\n---\n\n# Spec — ${slug}\n\nCreated by gate:approve.\n`;
|
|
162
|
+
} else {
|
|
163
|
+
content = updateFrontmatterField(content, specFieldName, 'approved');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
await fs.writeFile(specFile, content, 'utf8');
|
|
167
|
+
|
|
168
|
+
const nextInfo = GATE_NEXT_AGENTS[gateLetter];
|
|
169
|
+
const result = {
|
|
170
|
+
ok: true,
|
|
171
|
+
gate: gateLetter,
|
|
172
|
+
gate_name: gateName,
|
|
173
|
+
feature: slug,
|
|
174
|
+
field_written: specFieldName,
|
|
175
|
+
spec_file: `.aioson/context/spec-${slug}.md`,
|
|
176
|
+
next_agent: nextInfo.agent,
|
|
177
|
+
next_action: nextInfo.action,
|
|
178
|
+
why: nextInfo.why
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
if (options.json) return result;
|
|
182
|
+
|
|
183
|
+
logger.log('');
|
|
184
|
+
logger.log(`Gate ${gateLetter} (${gateName}) — ${slug}`);
|
|
185
|
+
logger.log(BAR);
|
|
186
|
+
logger.log(`Result: ✓ APPROVED`);
|
|
187
|
+
logger.log('');
|
|
188
|
+
logger.log(`Written: ${specFieldName}: approved → .aioson/context/spec-${slug}.md`);
|
|
189
|
+
logger.log('');
|
|
190
|
+
logger.log(`Next agent: ${nextInfo.agent}`);
|
|
191
|
+
logger.log(`Why: ${nextInfo.why}`);
|
|
192
|
+
logger.log(`Action: ${nextInfo.action}`);
|
|
193
|
+
logger.log('');
|
|
194
|
+
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
module.exports = { runGateApprove };
|
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
fileExists,
|
|
20
20
|
parseGatesFromSpec,
|
|
21
21
|
parseFrontmatter,
|
|
22
|
+
detectClassification,
|
|
22
23
|
GATE_NAMES,
|
|
23
24
|
GATE_ALIASES
|
|
24
25
|
} = require('../preflight-engine');
|
|
@@ -52,6 +53,7 @@ async function checkGate(targetDir, slug, gateLetter) {
|
|
|
52
53
|
const specContent = await readFileSafe(specFile);
|
|
53
54
|
const gates = specContent ? parseGatesFromSpec(specContent) : {};
|
|
54
55
|
const fm = specContent ? parseFrontmatter(specContent) : {};
|
|
56
|
+
const classification = await detectClassification(targetDir, slug);
|
|
55
57
|
|
|
56
58
|
const gateName = GATE_NAMES[gateLetter];
|
|
57
59
|
const gateStatus = gates[gateName] || 'pending';
|
|
@@ -79,12 +81,19 @@ async function checkGate(targetDir, slug, gateLetter) {
|
|
|
79
81
|
const exists = await fileExists(filePath);
|
|
80
82
|
if (exists) {
|
|
81
83
|
let detail = null;
|
|
84
|
+
let ok = true;
|
|
82
85
|
const content = await readFileSafe(filePath);
|
|
83
86
|
if (content) {
|
|
84
87
|
const fileFm = parseFrontmatter(content);
|
|
85
|
-
|
|
88
|
+
const status = fileFm.status ? String(fileFm.status).toLowerCase() : null;
|
|
89
|
+
if (status) detail = `status: ${status}`;
|
|
90
|
+
|
|
91
|
+
if (gateLetter === 'C' && status !== 'approved') {
|
|
92
|
+
ok = false;
|
|
93
|
+
missing.push(`${fileName} status is ${status || 'missing'} — @pm must approve the implementation plan`);
|
|
94
|
+
}
|
|
86
95
|
}
|
|
87
|
-
evidence.push({ type: 'artifact', file: fileName, exists: true, detail, ok
|
|
96
|
+
evidence.push({ type: 'artifact', file: fileName, exists: true, detail, ok });
|
|
88
97
|
} else {
|
|
89
98
|
evidence.push({ type: 'artifact', file: fileName, exists: false, ok: false });
|
|
90
99
|
missing.push(`${fileName} not found`);
|
|
@@ -130,11 +139,21 @@ async function checkGate(targetDir, slug, gateLetter) {
|
|
|
130
139
|
|
|
131
140
|
let recommendation = '';
|
|
132
141
|
if (result === 'PASS') {
|
|
133
|
-
const nextAgents = {
|
|
142
|
+
const nextAgents = {
|
|
143
|
+
A: '@architect',
|
|
144
|
+
B: classification === 'MEDIUM' ? '@pm' : '@dev',
|
|
145
|
+
C: classification === 'MEDIUM' ? '@orchestrator' : '@dev',
|
|
146
|
+
D: 'feature complete'
|
|
147
|
+
};
|
|
134
148
|
recommendation = `${nextAgents[gateLetter] || 'proceed'} can proceed`;
|
|
135
149
|
} else {
|
|
136
|
-
const fixAgents = {
|
|
137
|
-
|
|
150
|
+
const fixAgents = {
|
|
151
|
+
A: `activate @analyst to produce requirements-${slug}.md, then run: aioson gate:approve . --feature=${slug} --gate=A`,
|
|
152
|
+
B: `activate @architect to produce architecture.md, then run: aioson gate:approve . --feature=${slug} --gate=B`,
|
|
153
|
+
C: `activate @pm to produce and approve implementation-plan-${slug}.md, then run: aioson gate:approve . --feature=${slug} --gate=C`,
|
|
154
|
+
D: `activate @qa for final verification; if QA passes, run: aioson gate:approve . --feature=${slug} --gate=D`
|
|
155
|
+
};
|
|
156
|
+
recommendation = `BLOCKED — ${fixAgents[gateLetter] || 'resolve missing items'}`;
|
|
138
157
|
}
|
|
139
158
|
|
|
140
159
|
return {
|
|
@@ -4,6 +4,84 @@ const fs = require('node:fs/promises');
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const { loadCompatibleGenome } = require('../lib/genomes/compat');
|
|
6
6
|
|
|
7
|
+
const INSTALLED_SKILLS_DIR = '.aioson/installed-skills';
|
|
8
|
+
const BUILTIN_SKILLS_DIR = '.aioson/skills';
|
|
9
|
+
const GENOMES_DIR = '.aioson/genomes';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Walk up the directory tree from startDir looking for a dir that contains .aioson/.
|
|
13
|
+
* Falls back to process.cwd() if not found.
|
|
14
|
+
*/
|
|
15
|
+
async function findProjectRoot(startDir) {
|
|
16
|
+
let current = startDir;
|
|
17
|
+
for (let i = 0; i < 10; i++) {
|
|
18
|
+
try {
|
|
19
|
+
await fs.access(path.join(current, '.aioson'));
|
|
20
|
+
return current;
|
|
21
|
+
} catch {
|
|
22
|
+
const parent = path.dirname(current);
|
|
23
|
+
if (parent === current) break;
|
|
24
|
+
current = parent;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return process.cwd();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check whether a skill slug is available in the project.
|
|
32
|
+
* Checks installed skills AND built-in skills directories.
|
|
33
|
+
*/
|
|
34
|
+
async function isSkillAvailable(projectRoot, slug) {
|
|
35
|
+
const installedPath = path.join(projectRoot, INSTALLED_SKILLS_DIR, slug, 'SKILL.md');
|
|
36
|
+
const builtinExact = path.join(projectRoot, BUILTIN_SKILLS_DIR, slug, 'SKILL.md');
|
|
37
|
+
// Also check as a flat dir (some skills live directly under .aioson/skills/{slug}/)
|
|
38
|
+
const builtinDir = path.join(projectRoot, BUILTIN_SKILLS_DIR, slug);
|
|
39
|
+
|
|
40
|
+
for (const candidate of [installedPath, builtinExact]) {
|
|
41
|
+
try {
|
|
42
|
+
await fs.access(candidate);
|
|
43
|
+
return { found: true, path: path.relative(projectRoot, path.dirname(candidate)) };
|
|
44
|
+
} catch { /* not found */ }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const stat = await fs.stat(builtinDir);
|
|
49
|
+
if (stat.isDirectory()) {
|
|
50
|
+
return { found: true, path: path.relative(projectRoot, builtinDir) };
|
|
51
|
+
}
|
|
52
|
+
} catch { /* not found */ }
|
|
53
|
+
|
|
54
|
+
return { found: false };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check whether a genome slug is available in the project.
|
|
59
|
+
*/
|
|
60
|
+
async function isGenomeAvailable(projectRoot, slug) {
|
|
61
|
+
const genomePath = path.join(projectRoot, GENOMES_DIR, `${slug}.md`);
|
|
62
|
+
try {
|
|
63
|
+
await fs.access(genomePath);
|
|
64
|
+
return { found: true, path: path.relative(projectRoot, genomePath) };
|
|
65
|
+
} catch {
|
|
66
|
+
return { found: false };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Read and parse the .meta.json companion file for a genome .md.
|
|
72
|
+
* Returns null if absent or invalid JSON.
|
|
73
|
+
*/
|
|
74
|
+
async function readGenomeMeta(mdFilePath) {
|
|
75
|
+
const slug = path.basename(mdFilePath, '.md');
|
|
76
|
+
const metaPath = path.join(path.dirname(mdFilePath), `${slug}.meta.json`);
|
|
77
|
+
try {
|
|
78
|
+
const raw = await fs.readFile(metaPath, 'utf8');
|
|
79
|
+
return { meta: JSON.parse(raw), metaPath };
|
|
80
|
+
} catch {
|
|
81
|
+
return { meta: null, metaPath };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
7
85
|
async function runGenomeDoctor({ args, options = {}, logger }) {
|
|
8
86
|
const target = args[0];
|
|
9
87
|
if (!target) {
|
|
@@ -13,6 +91,7 @@ async function runGenomeDoctor({ args, options = {}, logger }) {
|
|
|
13
91
|
const filePath = path.resolve(process.cwd(), target);
|
|
14
92
|
const raw = await fs.readFile(filePath, 'utf8');
|
|
15
93
|
const loaded = loadCompatibleGenome(raw, { filePath });
|
|
94
|
+
|
|
16
95
|
const result = {
|
|
17
96
|
ok: true,
|
|
18
97
|
file: filePath,
|
|
@@ -21,18 +100,96 @@ async function runGenomeDoctor({ args, options = {}, logger }) {
|
|
|
21
100
|
slug: loaded.document.slug,
|
|
22
101
|
type: loaded.document.type,
|
|
23
102
|
depth: loaded.document.depth,
|
|
24
|
-
evidenceMode: loaded.document.evidenceMode
|
|
103
|
+
evidenceMode: loaded.document.evidenceMode,
|
|
104
|
+
dependencies: {
|
|
105
|
+
skills: [],
|
|
106
|
+
genomes: [],
|
|
107
|
+
missing: { skills: [], genomes: [] }
|
|
108
|
+
}
|
|
25
109
|
};
|
|
26
110
|
|
|
27
|
-
|
|
111
|
+
// Check for .meta.json companion
|
|
112
|
+
const { meta, metaPath } = await readGenomeMeta(filePath);
|
|
113
|
+
const hasMeta = Boolean(meta);
|
|
114
|
+
result.hasMeta = hasMeta;
|
|
115
|
+
|
|
116
|
+
if (!options.json) {
|
|
117
|
+
logger.log(`Genome file: ${filePath}`);
|
|
118
|
+
logger.log(`Format: ${result.detectedFormat}`);
|
|
119
|
+
logger.log(`Migrated internally: ${result.migrated ? 'yes' : 'no'}`);
|
|
120
|
+
logger.log(`Slug: ${result.slug}`);
|
|
121
|
+
logger.log(`Type: ${result.type}`);
|
|
122
|
+
logger.log(`Depth: ${result.depth}`);
|
|
123
|
+
logger.log(`Evidence mode: ${result.evidenceMode}`);
|
|
124
|
+
logger.log(`Meta file: ${hasMeta ? path.relative(process.cwd(), metaPath) : 'not found'}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Dependency check — requires meta.json with dependencies field
|
|
128
|
+
const deps = hasMeta && meta.dependencies
|
|
129
|
+
? {
|
|
130
|
+
skills: Array.isArray(meta.dependencies.skills) ? meta.dependencies.skills : [],
|
|
131
|
+
genomes: Array.isArray(meta.dependencies.genomes) ? meta.dependencies.genomes : []
|
|
132
|
+
}
|
|
133
|
+
: { skills: [], genomes: [] };
|
|
134
|
+
|
|
135
|
+
result.dependencies.skills = deps.skills;
|
|
136
|
+
result.dependencies.genomes = deps.genomes;
|
|
137
|
+
|
|
138
|
+
if (deps.skills.length === 0 && deps.genomes.length === 0) {
|
|
139
|
+
if (!options.json) logger.log('\nDependencies: none declared');
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const projectRoot = await findProjectRoot(path.dirname(filePath));
|
|
144
|
+
const missingSkills = [];
|
|
145
|
+
const missingGenomes = [];
|
|
146
|
+
|
|
147
|
+
if (!options.json) logger.log('\nChecking dependencies...');
|
|
148
|
+
|
|
149
|
+
for (const slug of deps.skills) {
|
|
150
|
+
const check = await isSkillAvailable(projectRoot, slug);
|
|
151
|
+
if (check.found) {
|
|
152
|
+
if (!options.json) logger.log(` skill "${slug}": OK (${check.path})`);
|
|
153
|
+
} else {
|
|
154
|
+
missingSkills.push(slug);
|
|
155
|
+
if (!options.json) logger.log(` skill "${slug}": MISSING`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (const slug of deps.genomes) {
|
|
160
|
+
const check = await isGenomeAvailable(projectRoot, slug);
|
|
161
|
+
if (check.found) {
|
|
162
|
+
if (!options.json) logger.log(` genome "${slug}": OK (${check.path})`);
|
|
163
|
+
} else {
|
|
164
|
+
missingGenomes.push(slug);
|
|
165
|
+
if (!options.json) logger.log(` genome "${slug}": MISSING`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
result.dependencies.missing = { skills: missingSkills, genomes: missingGenomes };
|
|
170
|
+
|
|
171
|
+
const hasMissing = missingSkills.length > 0 || missingGenomes.length > 0;
|
|
172
|
+
if (hasMissing) {
|
|
173
|
+
result.ok = false;
|
|
174
|
+
if (!options.json) {
|
|
175
|
+
logger.log('\nMissing dependencies detected.');
|
|
176
|
+
if (missingSkills.length > 0) {
|
|
177
|
+
logger.log('Install missing skills:');
|
|
178
|
+
for (const slug of missingSkills) {
|
|
179
|
+
logger.log(` aioson skill:install --slug=${slug}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (missingGenomes.length > 0) {
|
|
183
|
+
logger.log('Install missing genomes:');
|
|
184
|
+
for (const slug of missingGenomes) {
|
|
185
|
+
logger.log(` aioson genome:install --slug=${slug}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} else if (!options.json) {
|
|
190
|
+
logger.log('\nAll dependencies satisfied.');
|
|
191
|
+
}
|
|
28
192
|
|
|
29
|
-
logger.log(`Genome file: ${filePath}`);
|
|
30
|
-
logger.log(`Format: ${result.detectedFormat}`);
|
|
31
|
-
logger.log(`Migrated internally: ${result.migrated ? 'yes' : 'no'}`);
|
|
32
|
-
logger.log(`Slug: ${result.slug}`);
|
|
33
|
-
logger.log(`Type: ${result.type}`);
|
|
34
|
-
logger.log(`Depth: ${result.depth}`);
|
|
35
|
-
logger.log(`Evidence mode: ${result.evidenceMode}`);
|
|
36
193
|
return result;
|
|
37
194
|
}
|
|
38
195
|
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* aioson git:guard — inspect staged files before commit
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* aioson git:guard .
|
|
8
|
+
* aioson git:guard . --json
|
|
9
|
+
* aioson git:guard . --allow-warnings --json
|
|
10
|
+
* aioson git:guard . --install-hook
|
|
11
|
+
* aioson git:guard . --uninstall-hook
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const path = require('node:path');
|
|
15
|
+
const {
|
|
16
|
+
inspectStagedChanges,
|
|
17
|
+
installPreCommitHook,
|
|
18
|
+
uninstallPreCommitHook
|
|
19
|
+
} = require('../lib/git-commit-guard');
|
|
20
|
+
|
|
21
|
+
function formatFinding(prefix, finding) {
|
|
22
|
+
const line = finding.line ? `:${finding.line}` : '';
|
|
23
|
+
return `${prefix} ${finding.path}${line} — ${finding.reason} [${finding.id}]`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function runGitGuard({ args, options = {}, logger }) {
|
|
27
|
+
const targetDir = path.resolve(process.cwd(), args[0] || '.');
|
|
28
|
+
const allowWarnings = Boolean(options['allow-warnings'] || options.allowWarnings);
|
|
29
|
+
const installHook = Boolean(options['install-hook'] || options.installHook);
|
|
30
|
+
const uninstallHook = Boolean(options['uninstall-hook'] || options.uninstallHook || options['remove-hook'] || options.removeHook);
|
|
31
|
+
|
|
32
|
+
if (installHook && uninstallHook) {
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
const failure = {
|
|
35
|
+
ok: false,
|
|
36
|
+
error: 'git_guard_invalid_options',
|
|
37
|
+
message: 'Use either --install-hook or --uninstall-hook, not both.',
|
|
38
|
+
projectDir: targetDir
|
|
39
|
+
};
|
|
40
|
+
if (!options.json) logger.error(failure.message);
|
|
41
|
+
return failure;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (installHook || uninstallHook) {
|
|
45
|
+
let hookResult;
|
|
46
|
+
try {
|
|
47
|
+
hookResult = installHook
|
|
48
|
+
? await installPreCommitHook(targetDir, options)
|
|
49
|
+
: await uninstallPreCommitHook(targetDir, options);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
process.exitCode = 1;
|
|
52
|
+
const failure = {
|
|
53
|
+
ok: false,
|
|
54
|
+
error: 'git_guard_hook_failed',
|
|
55
|
+
message: error.message,
|
|
56
|
+
projectDir: targetDir
|
|
57
|
+
};
|
|
58
|
+
if (!options.json) logger.error(`Commit hook operation failed: ${error.message}`);
|
|
59
|
+
return failure;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!hookResult.ok) process.exitCode = 1;
|
|
63
|
+
if (options.json) return hookResult;
|
|
64
|
+
|
|
65
|
+
logger.log('');
|
|
66
|
+
logger.log(`Commit hook — ${hookResult.gitRoot}`);
|
|
67
|
+
if (!hookResult.ok) {
|
|
68
|
+
logger.error(hookResult.message);
|
|
69
|
+
return hookResult;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (installHook) {
|
|
73
|
+
logger.log(hookResult.dryRun ? 'Pre-commit hook dry-run complete.' : 'Pre-commit hook installed.');
|
|
74
|
+
logger.log(`Hook path: ${hookResult.hookPath}`);
|
|
75
|
+
if (hookResult.backedUpExistingHook) {
|
|
76
|
+
logger.log(`Existing hook backed up to: ${hookResult.backupPath}`);
|
|
77
|
+
}
|
|
78
|
+
if (hookResult.chainedBackupHook) {
|
|
79
|
+
logger.log('AIOSON will chain the backed-up hook after the guard passes.');
|
|
80
|
+
}
|
|
81
|
+
return hookResult;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (hookResult.removed || hookResult.dryRun) {
|
|
85
|
+
logger.log(hookResult.dryRun ? 'Pre-commit hook uninstall dry-run complete.' : 'Pre-commit hook removed.');
|
|
86
|
+
if (hookResult.restoredBackup) {
|
|
87
|
+
logger.log(`Restored previous hook from: ${hookResult.backupPath}`);
|
|
88
|
+
}
|
|
89
|
+
return hookResult;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
logger.log(hookResult.message);
|
|
93
|
+
return hookResult;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let result;
|
|
97
|
+
try {
|
|
98
|
+
result = await inspectStagedChanges(targetDir, {
|
|
99
|
+
allowWarnings,
|
|
100
|
+
config: options.config
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
const failure = {
|
|
105
|
+
ok: false,
|
|
106
|
+
error: 'git_guard_failed',
|
|
107
|
+
message: error.message,
|
|
108
|
+
projectDir: targetDir
|
|
109
|
+
};
|
|
110
|
+
if (!options.json) logger.error(`Commit guard failed: ${error.message}`);
|
|
111
|
+
return failure;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const output = {
|
|
115
|
+
ok: result.ok,
|
|
116
|
+
projectDir: targetDir,
|
|
117
|
+
gitRoot: result.gitRoot,
|
|
118
|
+
strict: result.strict,
|
|
119
|
+
policy: result.policy,
|
|
120
|
+
stagedFiles: result.stagedFiles,
|
|
121
|
+
files: result.files,
|
|
122
|
+
errors: result.errors,
|
|
123
|
+
warnings: result.warnings,
|
|
124
|
+
suggestedCommands: result.suggestedCommands,
|
|
125
|
+
summary: result.summary
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
if (!result.ok) process.exitCode = 1;
|
|
129
|
+
|
|
130
|
+
if (options.json) return output;
|
|
131
|
+
|
|
132
|
+
logger.log('');
|
|
133
|
+
logger.log(`Commit guard — ${result.gitRoot}`);
|
|
134
|
+
logger.log(`Staged files: ${result.summary.stagedCount}`);
|
|
135
|
+
logger.log(`Policy: ${result.policy.loaded ? result.policy.path : 'default built-in policy (no project config found)'}`);
|
|
136
|
+
|
|
137
|
+
if (result.summary.stagedCount === 0) {
|
|
138
|
+
logger.error('No staged files found. Stage explicit files before committing.');
|
|
139
|
+
return output;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (result.ok) {
|
|
143
|
+
logger.log('Commit guard passed.');
|
|
144
|
+
return output;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
logger.error('Commit guard blocked this commit.');
|
|
148
|
+
if (result.errors.length > 0) {
|
|
149
|
+
logger.error('Errors:');
|
|
150
|
+
for (const finding of result.errors) {
|
|
151
|
+
logger.error(formatFinding(' [ERROR]', finding));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (result.warnings.length > 0) {
|
|
155
|
+
logger.error('Warnings:');
|
|
156
|
+
for (const finding of result.warnings) {
|
|
157
|
+
logger.error(formatFinding(' [WARN] ', finding));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (result.suggestedCommands.length > 0) {
|
|
161
|
+
logger.error('Suggested next commands:');
|
|
162
|
+
for (const command of result.suggestedCommands) {
|
|
163
|
+
logger.error(` ${command}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return output;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = { runGitGuard };
|