@jaimevalasek/aioson 1.7.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -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 +42 -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/cypher.md +252 -0
- package/template/.aioson/agents/dev.md +112 -628
- package/template/.aioson/agents/deyvin.md +33 -236
- 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 +5 -7
- 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 +168 -514
- package/template/.aioson/agents/setup.md +52 -278
- package/template/.aioson/agents/sheldon.md +122 -754
- package/template/.aioson/agents/site-forge.md +111 -1583
- package/template/.aioson/agents/squad.md +139 -2010
- package/template/.aioson/agents/tester.md +10 -0
- package/template/.aioson/agents/ux-ui.md +104 -812
- 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.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/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/web-research-cache.md +3 -0
- package/template/.aioson/tasks/squad-create.md +35 -8
- package/template/.aioson/tasks/squad-design.md +50 -2
- package/template/.aioson/tasks/squad-investigate.md +14 -1
- 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 +5 -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
|
@@ -0,0 +1,400 @@
|
|
|
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 {
|
|
8
|
+
SCHEMA_VERSION,
|
|
9
|
+
REQUIRED_SECTIONS,
|
|
10
|
+
isValidSlug,
|
|
11
|
+
assertFrontmatter,
|
|
12
|
+
validateFrontmatter
|
|
13
|
+
} = require('./schema');
|
|
14
|
+
|
|
15
|
+
const FEATURES_SUBDIR = 'features';
|
|
16
|
+
const DOSSIER_FILENAME = 'dossier.md';
|
|
17
|
+
const DEFAULT_AUTHOR = 'dossier-init';
|
|
18
|
+
|
|
19
|
+
function featureDir(contextDir, slug) {
|
|
20
|
+
return path.join(contextDir, FEATURES_SUBDIR, slug);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function dossierPath(contextDir, slug) {
|
|
24
|
+
return path.join(featureDir(contextDir, slug), DOSSIER_FILENAME);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function prdPath(contextDir, slug) {
|
|
28
|
+
return path.join(contextDir, `prd-${slug}.md`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function fileExists(p) {
|
|
32
|
+
try {
|
|
33
|
+
await fs.access(p);
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseFrontmatter(markdown) {
|
|
41
|
+
const text = String(markdown || '');
|
|
42
|
+
if (!text.startsWith('---\n') && !text.startsWith('---\r\n')) {
|
|
43
|
+
return { ok: false, data: null, body: text, reason: 'missing_frontmatter' };
|
|
44
|
+
}
|
|
45
|
+
const lines = text.split(/\r?\n/);
|
|
46
|
+
let closingIndex = -1;
|
|
47
|
+
for (let i = 1; i < lines.length; i += 1) {
|
|
48
|
+
if (lines[i].trim() === '---') {
|
|
49
|
+
closingIndex = i;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (closingIndex === -1) {
|
|
54
|
+
return { ok: false, data: null, body: text, reason: 'unclosed_frontmatter' };
|
|
55
|
+
}
|
|
56
|
+
const data = {};
|
|
57
|
+
for (let i = 1; i < closingIndex; i += 1) {
|
|
58
|
+
const line = lines[i].trim();
|
|
59
|
+
if (!line || line.startsWith('#')) continue;
|
|
60
|
+
const match = line.match(/^([a-zA-Z0-9_]+)\s*:\s*(.*)$/);
|
|
61
|
+
if (!match) {
|
|
62
|
+
return { ok: false, data: null, body: text, reason: 'invalid_frontmatter_line', line };
|
|
63
|
+
}
|
|
64
|
+
let val = match[2].trim();
|
|
65
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
66
|
+
val = val.slice(1, -1);
|
|
67
|
+
}
|
|
68
|
+
data[match[1]] = val;
|
|
69
|
+
}
|
|
70
|
+
const body = lines.slice(closingIndex + 1).join('\n');
|
|
71
|
+
return { ok: true, data, body };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function parseSections(markdown) {
|
|
75
|
+
const text = String(markdown || '');
|
|
76
|
+
const parsed = parseFrontmatter(text);
|
|
77
|
+
const body = parsed.ok ? parsed.body : text;
|
|
78
|
+
const sections = Object.create(null);
|
|
79
|
+
|
|
80
|
+
const lines = body.split(/\r?\n/);
|
|
81
|
+
let current = null;
|
|
82
|
+
let buf = [];
|
|
83
|
+
|
|
84
|
+
const flush = () => {
|
|
85
|
+
if (current !== null) {
|
|
86
|
+
sections[current] = buf.join('\n').replace(/\s+$/, '');
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
for (const line of lines) {
|
|
91
|
+
const m = line.match(/^##\s+(.+?)\s*$/);
|
|
92
|
+
if (m) {
|
|
93
|
+
flush();
|
|
94
|
+
current = m[1].trim();
|
|
95
|
+
buf = [];
|
|
96
|
+
} else if (current !== null) {
|
|
97
|
+
buf.push(line);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
flush();
|
|
101
|
+
|
|
102
|
+
return sections;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function extractPrdSection(prdMarkdown, headingNames) {
|
|
106
|
+
if (!prdMarkdown) return null;
|
|
107
|
+
const sections = parseSections(prdMarkdown);
|
|
108
|
+
for (const name of headingNames) {
|
|
109
|
+
if (sections[name]) {
|
|
110
|
+
const trimmed = sections[name].trim();
|
|
111
|
+
if (trimmed) return trimmed;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function buildDossierMarkdown({
|
|
118
|
+
slug,
|
|
119
|
+
classification,
|
|
120
|
+
createdAt,
|
|
121
|
+
author,
|
|
122
|
+
why,
|
|
123
|
+
what
|
|
124
|
+
}) {
|
|
125
|
+
const fm = [
|
|
126
|
+
'---',
|
|
127
|
+
`feature_slug: ${slug}`,
|
|
128
|
+
`schema_version: "${SCHEMA_VERSION}"`,
|
|
129
|
+
`created_by: ${author}`,
|
|
130
|
+
`created_at: ${createdAt}`,
|
|
131
|
+
'status: active',
|
|
132
|
+
`classification: ${classification}`,
|
|
133
|
+
`last_updated_by: ${author}`,
|
|
134
|
+
`last_updated_at: ${createdAt}`,
|
|
135
|
+
'---',
|
|
136
|
+
''
|
|
137
|
+
].join('\n');
|
|
138
|
+
|
|
139
|
+
const body = [
|
|
140
|
+
'## Why',
|
|
141
|
+
'',
|
|
142
|
+
why || '_(preencher manualmente — PRD não encontrado ou sem seção de Vision/Problem)_',
|
|
143
|
+
'',
|
|
144
|
+
'## What',
|
|
145
|
+
'',
|
|
146
|
+
what || '_(preencher manualmente — PRD não encontrado ou sem seção de Escopo)_',
|
|
147
|
+
'',
|
|
148
|
+
'## Code Map',
|
|
149
|
+
'',
|
|
150
|
+
'```yaml',
|
|
151
|
+
'files: []',
|
|
152
|
+
'modules: []',
|
|
153
|
+
'patterns: []',
|
|
154
|
+
'```',
|
|
155
|
+
'',
|
|
156
|
+
'## Rules & Design-Docs aplicáveis',
|
|
157
|
+
'',
|
|
158
|
+
'_(vazio — populado a partir da Phase 2)_',
|
|
159
|
+
'',
|
|
160
|
+
'## Agent Trail',
|
|
161
|
+
'',
|
|
162
|
+
'_(vazio — populado a partir da Phase 2)_',
|
|
163
|
+
'',
|
|
164
|
+
'## Revision Requests',
|
|
165
|
+
'',
|
|
166
|
+
'_(vazio — populado a partir da Phase 2)_',
|
|
167
|
+
''
|
|
168
|
+
].join('\n');
|
|
169
|
+
|
|
170
|
+
return fm + body;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function init({
|
|
174
|
+
slug,
|
|
175
|
+
contextDir,
|
|
176
|
+
classification = 'MEDIUM',
|
|
177
|
+
author = DEFAULT_AUTHOR,
|
|
178
|
+
now = () => new Date(),
|
|
179
|
+
prdContent,
|
|
180
|
+
whyText,
|
|
181
|
+
whatText
|
|
182
|
+
} = {}) {
|
|
183
|
+
if (!isValidSlug(slug)) {
|
|
184
|
+
const err = new Error(`invalid slug (must be kebab-case): ${JSON.stringify(slug)}`);
|
|
185
|
+
err.code = 'EDOSSIERSLUG';
|
|
186
|
+
throw err;
|
|
187
|
+
}
|
|
188
|
+
if (typeof contextDir !== 'string' || !contextDir) {
|
|
189
|
+
throw new TypeError('init: contextDir must be a non-empty string');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const dir = featureDir(contextDir, slug);
|
|
193
|
+
const filePath = dossierPath(contextDir, slug);
|
|
194
|
+
|
|
195
|
+
let prd = prdContent;
|
|
196
|
+
if (prd === undefined) {
|
|
197
|
+
const candidate = prdPath(contextDir, slug);
|
|
198
|
+
if (await fileExists(candidate)) {
|
|
199
|
+
prd = await fs.readFile(candidate, 'utf8');
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const why = whyText !== undefined ? whyText : extractPrdSection(prd, ['Problem', 'Why', 'Vision']);
|
|
204
|
+
const what = whatText !== undefined ? whatText : extractPrdSection(prd, ['Escopo do MVP', 'Scope', 'What']);
|
|
205
|
+
|
|
206
|
+
const createdAt = now().toISOString();
|
|
207
|
+
const markdown = buildDossierMarkdown({
|
|
208
|
+
slug,
|
|
209
|
+
classification,
|
|
210
|
+
createdAt,
|
|
211
|
+
author,
|
|
212
|
+
why,
|
|
213
|
+
what
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Validate before writing — fail fast if our own builder produces invalid output.
|
|
217
|
+
const fmValidation = validateFrontmatter({
|
|
218
|
+
feature_slug: slug,
|
|
219
|
+
schema_version: SCHEMA_VERSION,
|
|
220
|
+
created_by: author,
|
|
221
|
+
created_at: createdAt,
|
|
222
|
+
status: 'active',
|
|
223
|
+
classification,
|
|
224
|
+
last_updated_by: author,
|
|
225
|
+
last_updated_at: createdAt
|
|
226
|
+
});
|
|
227
|
+
if (!fmValidation.valid) {
|
|
228
|
+
const err = new Error(`refusing to write invalid dossier: ${fmValidation.errors.join('; ')}`);
|
|
229
|
+
err.code = 'EDOSSIERSCHEMA';
|
|
230
|
+
err.errors = fmValidation.errors;
|
|
231
|
+
throw err;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
await fs.mkdir(dir, { recursive: true });
|
|
235
|
+
let fh;
|
|
236
|
+
try {
|
|
237
|
+
fh = await fs.open(filePath, 'wx');
|
|
238
|
+
} catch (err) {
|
|
239
|
+
if (err && err.code === 'EEXIST') {
|
|
240
|
+
const e = new Error(`dossier already exists at ${filePath}`);
|
|
241
|
+
e.code = 'EDOSSIEREXISTS';
|
|
242
|
+
e.path = filePath;
|
|
243
|
+
throw e;
|
|
244
|
+
}
|
|
245
|
+
throw err;
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
await fh.writeFile(markdown);
|
|
249
|
+
} finally {
|
|
250
|
+
await fh.close();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return { path: filePath, dir, frontmatter: fmValidation, sections: parseSections(markdown) };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function read({ slug, contextDir } = {}) {
|
|
257
|
+
if (!isValidSlug(slug)) {
|
|
258
|
+
const err = new Error(`invalid slug (must be kebab-case): ${JSON.stringify(slug)}`);
|
|
259
|
+
err.code = 'EDOSSIERSLUG';
|
|
260
|
+
throw err;
|
|
261
|
+
}
|
|
262
|
+
const filePath = dossierPath(contextDir, slug);
|
|
263
|
+
let raw;
|
|
264
|
+
try {
|
|
265
|
+
raw = await fs.readFile(filePath, 'utf8');
|
|
266
|
+
} catch (err) {
|
|
267
|
+
if (err && err.code === 'ENOENT') {
|
|
268
|
+
const e = new Error(`dossier not found for slug "${slug}" at ${filePath}`);
|
|
269
|
+
e.code = 'EDOSSIERMISSING';
|
|
270
|
+
e.path = filePath;
|
|
271
|
+
throw e;
|
|
272
|
+
}
|
|
273
|
+
throw err;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const fmParse = parseFrontmatter(raw);
|
|
277
|
+
if (!fmParse.ok) {
|
|
278
|
+
const err = new Error(`malformed dossier frontmatter at ${filePath}: ${fmParse.reason}`);
|
|
279
|
+
err.code = 'EDOSSIERPARSE';
|
|
280
|
+
err.path = filePath;
|
|
281
|
+
throw err;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Schema validation — fail loud, never silent.
|
|
285
|
+
assertFrontmatter(fmParse.data);
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
path: filePath,
|
|
289
|
+
raw,
|
|
290
|
+
frontmatter: fmParse.data,
|
|
291
|
+
sections: parseSections(raw)
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function show({ slug, contextDir } = {}) {
|
|
296
|
+
const { raw, frontmatter, sections, path: p } = await read({ slug, contextDir });
|
|
297
|
+
|
|
298
|
+
// Check for corrupted dossier-history.md (if present)
|
|
299
|
+
let historyWarn = null;
|
|
300
|
+
const hp = path.join(path.dirname(p), 'dossier-history.md');
|
|
301
|
+
try {
|
|
302
|
+
const histRaw = await fs.readFile(hp, 'utf8');
|
|
303
|
+
if (typeof histRaw !== 'string') historyWarn = 'history_corrupted';
|
|
304
|
+
} catch (err) {
|
|
305
|
+
if (err && err.code !== 'ENOENT') historyWarn = 'history_corrupted';
|
|
306
|
+
// ENOENT = absent, not corrupted — silently ignore
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const header = [
|
|
310
|
+
`# Dossier — ${frontmatter.feature_slug} (${frontmatter.classification})`,
|
|
311
|
+
`status=${frontmatter.status} schema=${frontmatter.schema_version} updated=${frontmatter.last_updated_at}`,
|
|
312
|
+
`path: ${p}`,
|
|
313
|
+
''
|
|
314
|
+
].join('\n');
|
|
315
|
+
return { header, raw, frontmatter, sections, path: p, warn: historyWarn };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Append-only write to a named ## section in dossier.md.
|
|
319
|
+
// Uses SHA-256 of (section + content) for dedup — repeated calls with same args are no-ops.
|
|
320
|
+
async function addFinding({ slug, contextDir, agent, section, content, now = () => new Date() } = {}) {
|
|
321
|
+
if (!isValidSlug(slug)) {
|
|
322
|
+
const err = new Error(`invalid slug: ${JSON.stringify(slug)}`);
|
|
323
|
+
err.code = 'EDOSSIERSLUG';
|
|
324
|
+
throw err;
|
|
325
|
+
}
|
|
326
|
+
const filePath = dossierPath(contextDir, slug);
|
|
327
|
+
let raw;
|
|
328
|
+
try {
|
|
329
|
+
raw = await fs.readFile(filePath, 'utf8');
|
|
330
|
+
} catch (err) {
|
|
331
|
+
if (err && err.code === 'ENOENT') {
|
|
332
|
+
const e = new Error(`dossier not found for slug "${slug}" at ${filePath}`);
|
|
333
|
+
e.code = 'EDOSSIERMISSING';
|
|
334
|
+
e.path = filePath;
|
|
335
|
+
throw e;
|
|
336
|
+
}
|
|
337
|
+
throw err;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const hash = crypto.createHash('sha256').update(`${section}\0${content}`).digest('hex');
|
|
341
|
+
const hashMarker = `<!-- sha256:${hash} -->`;
|
|
342
|
+
|
|
343
|
+
// Idempotency: if hash already present, no-op silently
|
|
344
|
+
if (raw.includes(hashMarker)) {
|
|
345
|
+
return { added: false, hash };
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const timestamp = now().toISOString();
|
|
349
|
+
const entry = [
|
|
350
|
+
hashMarker,
|
|
351
|
+
`**${timestamp}** | @${agent} | _${section}_`,
|
|
352
|
+
'',
|
|
353
|
+
content.trim(),
|
|
354
|
+
''
|
|
355
|
+
].join('\n');
|
|
356
|
+
|
|
357
|
+
// Append inside ## Agent Trail (or after it if not found)
|
|
358
|
+
const lines = raw.split('\n');
|
|
359
|
+
let trailEnd = lines.length;
|
|
360
|
+
let inTrail = false;
|
|
361
|
+
|
|
362
|
+
for (let i = 0; i < lines.length; i++) {
|
|
363
|
+
if (lines[i].trimEnd() === '## Agent Trail') {
|
|
364
|
+
inTrail = true;
|
|
365
|
+
} else if (inTrail && /^## /.test(lines[i])) {
|
|
366
|
+
trailEnd = i;
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Insert before the next section (or at end of file)
|
|
372
|
+
const before = lines.slice(0, trailEnd);
|
|
373
|
+
const after = lines.slice(trailEnd);
|
|
374
|
+
|
|
375
|
+
// Trim trailing blank lines from before to avoid double blanks
|
|
376
|
+
while (before.length > 0 && before[before.length - 1].trim() === '') before.pop();
|
|
377
|
+
before.push('', entry.trimEnd());
|
|
378
|
+
|
|
379
|
+
const rebuilt = [...before, '', ...after].join('\n');
|
|
380
|
+
await fs.writeFile(filePath, rebuilt, 'utf8');
|
|
381
|
+
|
|
382
|
+
return { added: true, hash };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
module.exports = {
|
|
386
|
+
FEATURES_SUBDIR,
|
|
387
|
+
DOSSIER_FILENAME,
|
|
388
|
+
DEFAULT_AUTHOR,
|
|
389
|
+
REQUIRED_SECTIONS,
|
|
390
|
+
featureDir,
|
|
391
|
+
dossierPath,
|
|
392
|
+
prdPath,
|
|
393
|
+
parseFrontmatter,
|
|
394
|
+
parseSections,
|
|
395
|
+
buildDossierMarkdown,
|
|
396
|
+
addFinding,
|
|
397
|
+
init,
|
|
398
|
+
read,
|
|
399
|
+
show
|
|
400
|
+
};
|
package/src/execution-gateway.js
CHANGED
|
@@ -220,6 +220,9 @@ async function syncWorkflowRuntime(targetDir, input) {
|
|
|
220
220
|
try {
|
|
221
221
|
const state = input.state || {};
|
|
222
222
|
const eventPayload = input.eventPayload || {};
|
|
223
|
+
if (!eventPayload.autonomy_mode && eventPayload.autonomyMode) {
|
|
224
|
+
eventPayload.autonomy_mode = eventPayload.autonomyMode;
|
|
225
|
+
}
|
|
223
226
|
const activationAgent = normalizeText(input.activationAgent);
|
|
224
227
|
const completedStage = normalizeText(input.completedStage);
|
|
225
228
|
const sessionKey = makeWorkflowSessionKey(state);
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* friction-scanner — analyzes workflow error logs to detect recurring friction patterns.
|
|
5
|
+
*
|
|
6
|
+
* Reads .aioson/context/workflow.errors.jsonl and produces structured
|
|
7
|
+
* recommendations for codebase hardening.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('node:path');
|
|
11
|
+
const fs = require('node:fs/promises');
|
|
12
|
+
const { readFileSafe, fileExists } = require('./preflight-engine');
|
|
13
|
+
|
|
14
|
+
const ERRORS_PATH = '.aioson/context/workflow.errors.jsonl';
|
|
15
|
+
|
|
16
|
+
const PATTERNS = [
|
|
17
|
+
{
|
|
18
|
+
id: 'typescript_compile',
|
|
19
|
+
name: 'TypeScript compilation errors',
|
|
20
|
+
test: (err) => /tsc|typescript|type mismatch|cannot find module|unused import/i.test(err)
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'rust_compile',
|
|
24
|
+
name: 'Rust compilation errors',
|
|
25
|
+
test: (err) => /cargo check|rustc|sqlx|actix|tokio/i.test(err)
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'jsx_structure',
|
|
29
|
+
name: 'JSX / React structure errors',
|
|
30
|
+
test: (err) => /jsx|react|component|element type is invalid|nested|display-property/i.test(err)
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'test_mock_order',
|
|
34
|
+
name: 'Mock ordering / test helper issues',
|
|
35
|
+
test: (err) => /vi\.mock|vi\.fn|jest\.mock|mock ordering|hoisted|getByText/i.test(err)
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'ui_text_mismatch',
|
|
39
|
+
name: 'UI text mismatch in tests',
|
|
40
|
+
test: (err) => /confirmar|vincular|button label|tab name|ui text|assertion/i.test(err)
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'git_staging',
|
|
44
|
+
name: 'Git staging accidents',
|
|
45
|
+
test: (err) => /node_modules|build artifact|dev\.db|\.next|unwanted files staged/i.test(err)
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'path_misunderstanding',
|
|
49
|
+
name: 'Path / directory misinterpretation',
|
|
50
|
+
test: (err) => /wrong directory|path misunderstanding|bootstrap|\.aioson\/docs/i.test(err)
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'handoff_contract',
|
|
54
|
+
name: 'Handoff contract violations',
|
|
55
|
+
test: (err) => /handoff contract|missing artifact|gate .* not approved/i.test(err)
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
async function loadErrors(targetDir) {
|
|
60
|
+
const errorsPath = path.join(targetDir, ERRORS_PATH);
|
|
61
|
+
if (!(await fileExists(errorsPath))) return [];
|
|
62
|
+
const content = await readFileSafe(errorsPath);
|
|
63
|
+
if (!content) return [];
|
|
64
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
65
|
+
const entries = [];
|
|
66
|
+
for (const line of lines) {
|
|
67
|
+
try {
|
|
68
|
+
entries.push(JSON.parse(line));
|
|
69
|
+
} catch {
|
|
70
|
+
// ignore malformed lines
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return entries;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function classifyError(entry) {
|
|
77
|
+
const text = `${entry.error || ''} ${entry.gateType || ''}`;
|
|
78
|
+
for (const pattern of PATTERNS) {
|
|
79
|
+
if (pattern.test(text)) {
|
|
80
|
+
return pattern.id;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return 'unknown';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function scanFriction(targetDir) {
|
|
87
|
+
const entries = await loadErrors(targetDir);
|
|
88
|
+
const counts = {};
|
|
89
|
+
const byPattern = {};
|
|
90
|
+
const recent = entries.slice(-20);
|
|
91
|
+
|
|
92
|
+
for (const entry of entries) {
|
|
93
|
+
const pid = classifyError(entry);
|
|
94
|
+
counts[pid] = (counts[pid] || 0) + 1;
|
|
95
|
+
if (!byPattern[pid]) byPattern[pid] = [];
|
|
96
|
+
byPattern[pid].push(entry);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const sorted = Object.entries(counts)
|
|
100
|
+
.sort((a, b) => b[1] - a[1])
|
|
101
|
+
.map(([pid, count]) => {
|
|
102
|
+
const pattern = PATTERNS.find((p) => p.id === pid);
|
|
103
|
+
return {
|
|
104
|
+
id: pid,
|
|
105
|
+
name: pattern ? pattern.name : pid,
|
|
106
|
+
count,
|
|
107
|
+
examples: (byPattern[pid] || []).slice(-3).map((e) => e.error?.substring(0, 200) || '')
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
total: entries.length,
|
|
113
|
+
recentCount: recent.length,
|
|
114
|
+
patterns: sorted
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function buildRecommendations(analysis) {
|
|
119
|
+
const recs = [];
|
|
120
|
+
for (const p of analysis.patterns) {
|
|
121
|
+
switch (p.id) {
|
|
122
|
+
case 'typescript_compile':
|
|
123
|
+
recs.push({
|
|
124
|
+
pattern: p.name,
|
|
125
|
+
action: 'Enable strict type-checking hooks or add "tsc --noEmit" to pre-commit checks.',
|
|
126
|
+
autoFixable: false,
|
|
127
|
+
priority: p.count >= 3 ? 'high' : 'medium'
|
|
128
|
+
});
|
|
129
|
+
break;
|
|
130
|
+
case 'rust_compile':
|
|
131
|
+
recs.push({
|
|
132
|
+
pattern: p.name,
|
|
133
|
+
action: 'Add "cargo check" as a mandatory step in the dev agent prompt or CI pipeline.',
|
|
134
|
+
autoFixable: false,
|
|
135
|
+
priority: p.count >= 3 ? 'high' : 'medium'
|
|
136
|
+
});
|
|
137
|
+
break;
|
|
138
|
+
case 'jsx_structure':
|
|
139
|
+
recs.push({
|
|
140
|
+
pattern: p.name,
|
|
141
|
+
action: 'Add an ESLint plugin for JSX nesting rules (e.g., eslint-plugin-react).',
|
|
142
|
+
autoFixable: false,
|
|
143
|
+
priority: 'medium'
|
|
144
|
+
});
|
|
145
|
+
break;
|
|
146
|
+
case 'test_mock_order':
|
|
147
|
+
recs.push({
|
|
148
|
+
pattern: p.name,
|
|
149
|
+
action: 'Create a shared mock factory in tests/helpers/mocks.ts and update test conventions.',
|
|
150
|
+
autoFixable: true,
|
|
151
|
+
priority: p.count >= 2 ? 'high' : 'medium'
|
|
152
|
+
});
|
|
153
|
+
break;
|
|
154
|
+
case 'ui_text_mismatch':
|
|
155
|
+
recs.push({
|
|
156
|
+
pattern: p.name,
|
|
157
|
+
action: 'Enforce the test-briefing injection (already active) and add a rule to prefer getByRole.',
|
|
158
|
+
autoFixable: false,
|
|
159
|
+
priority: 'medium'
|
|
160
|
+
});
|
|
161
|
+
break;
|
|
162
|
+
case 'git_staging':
|
|
163
|
+
recs.push({
|
|
164
|
+
pattern: p.name,
|
|
165
|
+
action: 'Ensure .gitignore covers node_modules, dist, .next, *.db and install the pre-commit hook.',
|
|
166
|
+
autoFixable: true,
|
|
167
|
+
priority: 'high'
|
|
168
|
+
});
|
|
169
|
+
break;
|
|
170
|
+
case 'path_misunderstanding':
|
|
171
|
+
recs.push({
|
|
172
|
+
pattern: p.name,
|
|
173
|
+
action: 'Verify project-map.md exists and is loaded by implementation agents (already active).',
|
|
174
|
+
autoFixable: false,
|
|
175
|
+
priority: 'medium'
|
|
176
|
+
});
|
|
177
|
+
break;
|
|
178
|
+
case 'handoff_contract':
|
|
179
|
+
recs.push({
|
|
180
|
+
pattern: p.name,
|
|
181
|
+
action: 'Review agent prompts to ensure they set the correct gates and produce required artifacts.',
|
|
182
|
+
autoFixable: false,
|
|
183
|
+
priority: 'high'
|
|
184
|
+
});
|
|
185
|
+
break;
|
|
186
|
+
default:
|
|
187
|
+
recs.push({
|
|
188
|
+
pattern: p.name,
|
|
189
|
+
action: 'Investigate root cause manually and add a preventive rule.',
|
|
190
|
+
autoFixable: false,
|
|
191
|
+
priority: 'low'
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return recs;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
module.exports = {
|
|
199
|
+
scanFriction,
|
|
200
|
+
buildRecommendations,
|
|
201
|
+
PATTERNS
|
|
202
|
+
};
|
package/src/genome-schema.js
CHANGED
|
@@ -6,6 +6,7 @@ const {
|
|
|
6
6
|
GENOME_FORMATS,
|
|
7
7
|
GENOME_CONFIDENCE_LEVELS,
|
|
8
8
|
GENOME_TYPES,
|
|
9
|
+
GENOME_RELATION_TYPES,
|
|
9
10
|
countGenomeSections,
|
|
10
11
|
normalizeGenome,
|
|
11
12
|
normalizeGenomeMeta
|
|
@@ -79,6 +80,18 @@ function validatePersonaV3Requirements(normalized, sections, errors) {
|
|
|
79
80
|
errors.push(`sections.${key} must contain at least one entry for genome-v3 persona profiles`);
|
|
80
81
|
}
|
|
81
82
|
}
|
|
83
|
+
|
|
84
|
+
// track 4.0 — optional, validate only if present
|
|
85
|
+
if (normalized.hexacoH && !GENOME_CONFIDENCE_LEVELS.includes(normalized.hexacoH)) {
|
|
86
|
+
errors.push(`hexaco_h must be one of: ${GENOME_CONFIDENCE_LEVELS.join(', ')}`);
|
|
87
|
+
}
|
|
88
|
+
if (Array.isArray(normalized.relations)) {
|
|
89
|
+
for (const rel of normalized.relations) {
|
|
90
|
+
if (rel.type && !GENOME_RELATION_TYPES.includes(rel.type)) {
|
|
91
|
+
errors.push(`relations[].type must be one of: ${GENOME_RELATION_TYPES.join(', ')}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
82
95
|
}
|
|
83
96
|
|
|
84
97
|
function validateGenomeObject(input) {
|
|
@@ -155,6 +168,7 @@ function validateGenomeMeta(input) {
|
|
|
155
168
|
cognitiveProfile: Array(meta.counts.cognitiveProfile).fill('x'),
|
|
156
169
|
communicationStyle: Array(meta.counts.communicationStyle).fill('x'),
|
|
157
170
|
biases: Array(meta.counts.biases).fill('x'),
|
|
171
|
+
traitInteractions: Array(meta.counts.traitInteractions || 0).fill('x'),
|
|
158
172
|
conflictResolution: Array(meta.counts.conflictResolution).fill('x'),
|
|
159
173
|
evidence: Array(meta.counts.evidence).fill('x'),
|
|
160
174
|
applicationNotes: Array(meta.counts.applicationNotes).fill('x')
|
|
@@ -168,6 +182,14 @@ function validateGenomeMeta(input) {
|
|
|
168
182
|
}
|
|
169
183
|
if (!meta.createdAt) errors.push('createdAt is required');
|
|
170
184
|
if (!meta.updatedAt) errors.push('updatedAt is required');
|
|
185
|
+
if (hasOwn(input, 'dependencies') && input.dependencies !== null && typeof input.dependencies === 'object') {
|
|
186
|
+
if ('skills' in input.dependencies && !Array.isArray(input.dependencies.skills)) {
|
|
187
|
+
errors.push('dependencies.skills must be an array of strings');
|
|
188
|
+
}
|
|
189
|
+
if ('genomes' in input.dependencies && !Array.isArray(input.dependencies.genomes)) {
|
|
190
|
+
errors.push('dependencies.genomes must be an array of strings');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
171
193
|
validatePersonaV3Requirements(
|
|
172
194
|
{
|
|
173
195
|
version: meta.version,
|
|
@@ -183,7 +205,8 @@ function validateGenomeMeta(input) {
|
|
|
183
205
|
{
|
|
184
206
|
cognitiveProfile: Array(meta.counts.cognitiveProfile).fill('x'),
|
|
185
207
|
communicationStyle: Array(meta.counts.communicationStyle).fill('x'),
|
|
186
|
-
biases: Array(meta.counts.biases).fill('x')
|
|
208
|
+
biases: Array(meta.counts.biases).fill('x'),
|
|
209
|
+
traitInteractions: Array(meta.counts.traitInteractions || 0).fill('x')
|
|
187
210
|
},
|
|
188
211
|
errors
|
|
189
212
|
);
|