@williambeto/ai-workflow 1.19.1 → 2.2.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 +56 -838
- package/PUBLISH_MANIFEST.json +34 -0
- package/README.md +78 -148
- package/{packages/ai-workflow/bin → bin}/ai-workflow.js +0 -0
- package/dist-assets/AGENTS.md +27 -0
- package/dist-assets/agents/astra.md +63 -0
- package/dist-assets/agents/atlas.md +169 -0
- package/dist-assets/agents/nexus.md +42 -0
- package/dist-assets/agents/orion.md +44 -0
- package/dist-assets/agents/phoenix.md +42 -0
- package/dist-assets/agents/sage.md +54 -0
- package/dist-assets/commands/README.md +14 -0
- package/dist-assets/commands/atlas.md +12 -0
- package/dist-assets/commands/audit.md +10 -0
- package/dist-assets/commands/deploy.md +12 -0
- package/dist-assets/commands/discover.md +10 -0
- package/dist-assets/commands/implement.md +28 -0
- package/dist-assets/commands/optimize-tokens.md +10 -0
- package/dist-assets/commands/plan.md +10 -0
- package/dist-assets/commands/release.md +12 -0
- package/dist-assets/commands/run.md +26 -0
- package/dist-assets/commands/spec-create.md +10 -0
- package/dist-assets/commands/spec-implement.md +10 -0
- package/dist-assets/commands/spec-review.md +10 -0
- package/dist-assets/commands/update-memory.md +10 -0
- package/dist-assets/commands/validate.md +12 -0
- package/dist-assets/docs/INDEX.md +21 -0
- package/dist-assets/docs/QUICKSTART.md +23 -0
- package/dist-assets/docs/adr/ADR-0000.md +19 -0
- package/dist-assets/docs/adr/ADR-0001.md +45 -0
- package/dist-assets/docs/adr/ADR-0002.md +62 -0
- package/dist-assets/docs/adr/ADR-0003.md +60 -0
- package/dist-assets/docs/adr/ADR-0004.md +71 -0
- package/dist-assets/docs/adr/ADR-0005.md +22 -0
- package/dist-assets/docs/adr/ADR-0006.md +82 -0
- package/dist-assets/docs/adr/ADR-0007.md +78 -0
- package/dist-assets/docs/api-engine-reference.md +7 -0
- package/{docs → dist-assets/docs}/architecture-policy.md +1 -1
- package/dist-assets/docs/cli-reference.md +27 -0
- package/dist-assets/docs/compatibility/provider-usage.md +38 -0
- package/dist-assets/docs/compatibility/runtime-matrix.md +30 -0
- package/dist-assets/docs/consumer-onboarding.md +17 -0
- package/dist-assets/docs/contributing-guide.md +11 -0
- package/{docs → dist-assets/docs}/design-patterns-policy.md +2 -2
- package/dist-assets/docs/full-documentation.md +113 -0
- package/{docs → dist-assets/docs}/npm-consumer-quickstart.md +18 -46
- package/dist-assets/docs/opencode-readme.md +8 -0
- package/dist-assets/docs/policies/01-BRANCH_GATE.md +63 -0
- package/dist-assets/docs/policies/02-SDD_METHODOLOGY.md +95 -0
- package/dist-assets/docs/policies/03-QUALITY_GATE.md +22 -0
- package/dist-assets/docs/policies/05-AGENT_CONTRACT.md +7 -0
- package/dist-assets/docs/policies/06-FINAL_EVIDENCE_CONTRACT.md +31 -0
- package/dist-assets/docs/policies/07-RELEASE_GATE.md +47 -0
- package/dist-assets/docs/policies/08-PRODUCT_TRUTHFULNESS_AND_PROJECT_DOCS.md +18 -0
- package/dist-assets/docs/policies/09-SPEC_VISIBILITY_AND_PUBLICATION.md +28 -0
- package/dist-assets/docs/policies/10-BEHAVIORAL_CONTRACT_HARDENING.md +9 -0
- package/dist-assets/docs/policies/11-EXECUTABLE_DELEGATION_AND_TRUTHFULNESS.md +7 -0
- package/dist-assets/docs/policies/ORCHESTRATION_PROTOCOL.md +15 -0
- package/dist-assets/docs/policies/PROCEDURE_DELIVERY_ARTIFACTS.md +21 -0
- package/dist-assets/docs/policies/PROCEDURE_DOCUMENTATION_CHECKLIST.md +24 -0
- package/dist-assets/docs/policies/PROCEDURE_UI_CHECKLIST.md +54 -0
- package/dist-assets/docs/profiles/README.md +19 -0
- package/dist-assets/docs/profiles/backend-api.md +5 -0
- package/dist-assets/docs/profiles/documentation.md +3 -0
- package/dist-assets/docs/profiles/frontend-product.md +19 -0
- package/dist-assets/docs/profiles/frontend-utility.md +19 -0
- package/dist-assets/docs/profiles/refactor.md +3 -0
- package/dist-assets/docs/profiles/security-review.md +3 -0
- package/dist-assets/docs/references/frontend-quality/landing-page-quality-checklist.md +11 -0
- package/dist-assets/docs/references/frontend-quality/product-copy-truthfulness.md +7 -0
- package/dist-assets/docs/references/frontend-quality/quality-failure-examples.md +20 -0
- package/dist-assets/docs/references/frontend-quality/visual-composition-patterns.md +10 -0
- package/dist-assets/docs/specs/runtime-operational-contract.md +39 -0
- package/dist-assets/docs/troubleshooting-guide.md +21 -0
- package/dist-assets/docs/visual-validation-guide.md +76 -0
- package/dist-assets/examples/README.md +10 -0
- package/dist-assets/examples/autopilot-cycle/00-CONTEXT.md +3 -0
- package/dist-assets/examples/autopilot-cycle/01-requirement.md +16 -0
- package/dist-assets/examples/autopilot-cycle/02-gate-a-check.md +23 -0
- package/dist-assets/examples/autopilot-cycle/03-orion-planning.md +20 -0
- package/dist-assets/examples/autopilot-cycle/04-astra-implementation.md +17 -0
- package/dist-assets/examples/autopilot-cycle/05-sage-validation.md +15 -0
- package/dist-assets/examples/autopilot-cycle/06-phoenix-healing.md +12 -0
- package/dist-assets/examples/autopilot-cycle/07-orchestration-report.md +18 -0
- package/dist-assets/examples/backend-api/00-CONTEXT.md +12 -0
- package/dist-assets/examples/backend-api/01-requirement.md +19 -0
- package/dist-assets/examples/backend-api/02-functional-spec.md +20 -0
- package/dist-assets/examples/backend-api/03-technical-plan.md +15 -0
- package/dist-assets/examples/backend-api/04-pr-breakdown.md +10 -0
- package/dist-assets/examples/backend-api/05-execution-handoff.md +13 -0
- package/dist-assets/examples/backend-api/06-validation-report.md +11 -0
- package/dist-assets/examples/backend-api/07-orchestration-report.md +7 -0
- package/dist-assets/examples/blocked-scenarios/00-CONTEXT.md +9 -0
- package/dist-assets/examples/blocked-scenarios/01-branch-gate-block.md +12 -0
- package/dist-assets/examples/blocked-scenarios/02-quality-gate-block.md +13 -0
- package/dist-assets/examples/blocked-scenarios/03-scope-creep-block.md +13 -0
- package/dist-assets/examples/blocked-scenarios/04-unblock-resolution.md +9 -0
- package/dist-assets/examples/blocked-scenarios/05-orchestration-decision-log.md +11 -0
- package/dist-assets/examples/bugfix-critical/00-CONTEXT.md +12 -0
- package/dist-assets/examples/bugfix-critical/01-bug-report.md +11 -0
- package/dist-assets/examples/bugfix-critical/02-diagnosis-hypothesis.md +11 -0
- package/dist-assets/examples/bugfix-critical/03-technical-plan.md +12 -0
- package/dist-assets/examples/bugfix-critical/04-implementation-handoff.md +8 -0
- package/dist-assets/examples/bugfix-critical/05-validation-report.md +10 -0
- package/dist-assets/examples/bugfix-critical/06-orchestration-report.md +7 -0
- package/dist-assets/examples/cli-package/00-CONTEXT.md +9 -0
- package/dist-assets/examples/cli-package/01-requirement.md +14 -0
- package/dist-assets/examples/cli-package/02-technical-spec.md +16 -0
- package/dist-assets/examples/cli-package/03-technical-plan.md +12 -0
- package/dist-assets/examples/cli-package/04-pr-breakdown.md +9 -0
- package/dist-assets/examples/cli-package/05-release-report.md +15 -0
- package/dist-assets/examples/docs-only-repo/01-requirement.md +31 -0
- package/dist-assets/examples/docs-only-repo/02-functional-spec.md +25 -0
- package/dist-assets/examples/docs-only-repo/03-technical-plan.md +21 -0
- package/dist-assets/examples/docs-only-repo/04-pr-breakdown.md +13 -0
- package/dist-assets/examples/docs-only-repo/05-execution-handoff.md +17 -0
- package/dist-assets/examples/docs-only-repo/06-validation-report.md +16 -0
- package/dist-assets/examples/docs-only-repo/README.md +26 -0
- package/dist-assets/examples/full-stack-checkout/00-CONTEXT.md +9 -0
- package/dist-assets/examples/full-stack-checkout/01-requirement.md +12 -0
- package/dist-assets/examples/full-stack-checkout/02-functional-spec.md +15 -0
- package/dist-assets/examples/full-stack-checkout/03-technical-plan.md +15 -0
- package/dist-assets/examples/full-stack-checkout/04-pr-breakdown.md +8 -0
- package/dist-assets/examples/full-stack-checkout/05-execution-handoff.md +14 -0
- package/dist-assets/examples/full-stack-checkout/06-validation-report.md +12 -0
- package/dist-assets/examples/healing-cycle/00-CONTEXT.md +15 -0
- package/dist-assets/examples/healing-cycle/01-broken-implementation.md +10 -0
- package/dist-assets/examples/healing-cycle/02-sage-fails.md +14 -0
- package/dist-assets/examples/healing-cycle/03-phoenix-diagnosis.md +17 -0
- package/dist-assets/examples/healing-cycle/04-phoenix-fix.md +18 -0
- package/dist-assets/examples/healing-cycle/05-sage-revalidation.md +12 -0
- package/dist-assets/examples/healing-cycle/06-orchestration-log.md +14 -0
- package/dist-assets/examples/infra-deploy/00-CONTEXT.md +9 -0
- package/dist-assets/examples/infra-deploy/01-operational-goal.md +12 -0
- package/dist-assets/examples/infra-deploy/02-architecture-specs.md +15 -0
- package/dist-assets/examples/infra-deploy/03-implementation-plan.md +14 -0
- package/dist-assets/examples/infra-deploy/04-step-breakdown.md +9 -0
- package/dist-assets/examples/infra-deploy/05-execution-handoff.md +13 -0
- package/dist-assets/examples/infra-deploy/06-operational-report.md +11 -0
- package/dist-assets/examples/multi-pr-release/00-CONTEXT.md +9 -0
- package/dist-assets/examples/multi-pr-release/01-requirement.md +13 -0
- package/dist-assets/examples/multi-pr-release/02-strategic-plan.md +13 -0
- package/dist-assets/examples/multi-pr-release/03-pr-breakdown.md +14 -0
- package/dist-assets/examples/multi-pr-release/04-release-plan.md +12 -0
- package/dist-assets/examples/multi-pr-release/05-orchestration-report.md +7 -0
- package/dist-assets/examples/nuxt-dashboard/01-requirement.md +81 -0
- package/dist-assets/examples/nuxt-dashboard/02-functional-spec.md +88 -0
- package/dist-assets/examples/nuxt-dashboard/03-technical-plan.md +76 -0
- package/dist-assets/examples/nuxt-dashboard/04-pr-breakdown.md +219 -0
- package/dist-assets/examples/nuxt-dashboard/05-execution-handoff.md +88 -0
- package/dist-assets/examples/nuxt-dashboard/06-validation-report.md +56 -0
- package/dist-assets/examples/nuxt-dashboard/07-orchestration-report.md +79 -0
- package/dist-assets/examples/nuxt-dashboard/README.md +52 -0
- package/dist-assets/examples/react-dashboard/01-requirement.md +84 -0
- package/dist-assets/examples/react-dashboard/02-functional-spec.md +88 -0
- package/dist-assets/examples/react-dashboard/03-technical-plan.md +76 -0
- package/dist-assets/examples/react-dashboard/04-pr-breakdown.md +218 -0
- package/dist-assets/examples/react-dashboard/05-execution-handoff.md +13 -0
- package/dist-assets/examples/react-dashboard/06-validation-report.md +12 -0
- package/dist-assets/examples/react-dashboard/07-orchestration-report.md +7 -0
- package/dist-assets/examples/react-dashboard/README.md +70 -0
- package/dist-assets/examples/refactoring-service/00-CONTEXT.md +9 -0
- package/dist-assets/examples/refactoring-service/01-debt-report.md +12 -0
- package/dist-assets/examples/refactoring-service/02-behavior-spec.md +11 -0
- package/dist-assets/examples/refactoring-service/03-technical-plan.md +13 -0
- package/dist-assets/examples/refactoring-service/04-pr-breakdown.md +9 -0
- package/dist-assets/examples/refactoring-service/05-execution-handoff.md +14 -0
- package/dist-assets/examples/refactoring-service/06-stability-report.md +12 -0
- package/dist-assets/examples/sdd-cycle/00-CONTEXT.md +12 -0
- package/dist-assets/examples/sdd-cycle/01-raw-request.md +13 -0
- package/dist-assets/examples/sdd-cycle/02-spec-creation.md +18 -0
- package/dist-assets/examples/sdd-cycle/03-spec-review.md +12 -0
- package/dist-assets/examples/sdd-cycle/04-technical-plan.md +16 -0
- package/dist-assets/examples/sdd-cycle/05-pr-breakdown.md +9 -0
- package/dist-assets/examples/sdd-cycle/06-spec-implement.md +13 -0
- package/dist-assets/examples/sdd-cycle/07-validation-against-spec.md +13 -0
- package/dist-assets/examples/wordpress-theme/01-requirement.md +29 -0
- package/dist-assets/examples/wordpress-theme/02-functional-spec.md +22 -0
- package/dist-assets/examples/wordpress-theme/03-technical-plan.md +22 -0
- package/dist-assets/examples/wordpress-theme/04-pr-breakdown.md +14 -0
- package/dist-assets/examples/wordpress-theme/05-execution-handoff.md +17 -0
- package/dist-assets/examples/wordpress-theme/06-validation-report.md +16 -0
- package/dist-assets/examples/wordpress-theme/README.md +32 -0
- package/{harness → dist-assets/harness}/handoffs/HANDOFF.template.md +2 -2
- package/{harness → dist-assets/harness}/workflows/agent-evaluation-checklist.md +5 -5
- package/{harness → dist-assets/harness}/workflows/implement-review-validate.md +24 -0
- package/{harness → dist-assets/harness}/workflows/multi-agent-handoff.md +4 -4
- package/{harness → dist-assets/harness}/workflows/planner-executor-workflow.md +5 -5
- package/{harness → dist-assets/harness}/workflows/requirement-to-pr.md +1 -1
- package/dist-assets/runbooks/agent-delegation-workflow.md +50 -0
- package/dist-assets/runbooks/apply-starter-to-real-project.md +45 -0
- package/dist-assets/runbooks/commands-cheatsheet.md +44 -0
- package/dist-assets/runbooks/how-to-use-skills.md +44 -0
- package/dist-assets/runbooks/private-spec-publication-safety.md +35 -0
- package/{runbooks → dist-assets/runbooks}/spec-driven-development.md +3 -6
- package/dist-assets/runbooks/tutorial-walkthroughs.md +23 -0
- package/dist-assets/runbooks/use-linear-for-operational-planning.md +45 -0
- package/dist-assets/runbooks/use-napkin-project-memory.md +33 -0
- package/dist-assets/skills/architecture/SKILL.md +166 -0
- package/dist-assets/skills/backend-development/SKILL.md +166 -0
- package/dist-assets/skills/deployment/SKILL.md +166 -0
- package/dist-assets/skills/design-principles/SKILL.md +166 -0
- package/dist-assets/skills/documentation/SKILL.md +171 -0
- package/dist-assets/skills/frontend-development/SKILL.md +225 -0
- package/dist-assets/skills/full-stack-development/SKILL.md +166 -0
- package/dist-assets/skills/optimize-tokens/SKILL.md +166 -0
- package/dist-assets/skills/pr-workflow/SKILL.md +166 -0
- package/dist-assets/skills/product-discovery/SKILL.md +166 -0
- package/dist-assets/skills/product-planning/SKILL.md +166 -0
- package/dist-assets/skills/project-memory/SKILL.md +166 -0
- package/dist-assets/skills/prompt-engineer/SKILL.md +166 -0
- package/dist-assets/skills/qa-workflow/SKILL.md +186 -0
- package/dist-assets/skills/refactoring/SKILL.md +166 -0
- package/dist-assets/skills/release-workflow/SKILL.md +166 -0
- package/dist-assets/skills/spec-driven-development/SKILL.md +166 -0
- package/dist-assets/skills/technical-leadership/SKILL.md +166 -0
- package/dist-assets/skills/ui-ux-design/SKILL.md +202 -0
- package/dist-assets/templates/.geminiignore.template +8 -0
- package/dist-assets/templates/CLAUDE.md.template +20 -0
- package/dist-assets/templates/CODEX.md.template +20 -0
- package/dist-assets/templates/GEMINI.md.template +20 -0
- package/dist-assets/templates/HANDOFF.template.md +45 -0
- package/dist-assets/templates/SPEC.template.md +38 -0
- package/dist-assets/templates/change-proposal.template.md +14 -0
- package/dist-assets/templates/owner-evidence/astra-implementation.json +10 -0
- package/dist-assets/templates/owner-evidence/phoenix-remediation.json +8 -0
- package/dist-assets/templates/owner-evidence/sage-revalidation.json +8 -0
- package/dist-assets/templates/owner-evidence/sage-validation.json +8 -0
- package/dist-assets/templates/specs/deep.md +48 -0
- package/dist-assets/templates/specs/standard.md +38 -0
- package/dist-assets/templates/specs/tiny.md +19 -0
- package/package.json +43 -47
- package/src/adapters/index.js +3 -0
- package/src/adapters/platforms/claude.js +126 -0
- package/src/adapters/platforms/codex.js +100 -0
- package/src/adapters/platforms/gemini.js +232 -0
- package/src/cli.js +114 -0
- package/src/commands/collect-evidence.js +61 -0
- package/src/commands/doctor.js +186 -0
- package/src/commands/execute.js +172 -0
- package/{packages/ai-workflow/src → src}/commands/init.js +119 -20
- package/src/commands/run.js +112 -0
- package/src/core/completion-contract.js +35 -0
- package/src/core/execution-planner.js +59 -0
- package/src/core/gates/branch-gate.js +146 -0
- package/src/core/handoff/handoff-engine.js +104 -0
- package/src/core/healing/cli-remediation-executor.js +151 -0
- package/src/core/healing/healer-engine.js +179 -0
- package/src/core/identity.js +43 -0
- package/{packages/ai-workflow/src → src}/core/install-plan.js +3 -3
- package/src/core/opencode-merge.js +149 -0
- package/{packages/ai-workflow/src → src}/core/package-assets.js +29 -10
- package/src/core/request-classifier.js +58 -0
- package/src/core/runtime/opencode-adapter.js +94 -0
- package/src/core/sdd/validator.js +67 -0
- package/src/core/statuses.js +29 -0
- package/src/core/symlink-layout.js +93 -0
- package/src/core/templates.js +221 -0
- package/src/core/validation/canonical-finalization.js +43 -0
- package/src/core/validation/evidence-collector.js +109 -0
- package/src/core/validation/quality-guard.js +243 -0
- package/src/core/workflow-profiles.js +107 -0
- package/src/core/workflow-state-machine.js +46 -0
- package/.agents/napkin.md +0 -89
- package/.agents/skills/backend-implementer/SKILL.md +0 -490
- package/.agents/skills/build-and-validate/SKILL.md +0 -442
- package/.agents/skills/deploy-engineer/SKILL.md +0 -541
- package/.agents/skills/docs-writer/SKILL.md +0 -430
- package/.agents/skills/frontend-implementer/SKILL.md +0 -488
- package/.agents/skills/interface-design/SKILL.md +0 -428
- package/.agents/skills/interface-design/references/critique.md +0 -67
- package/.agents/skills/interface-design/references/example.md +0 -86
- package/.agents/skills/interface-design/references/principles.md +0 -235
- package/.agents/skills/interface-design/references/validation.md +0 -48
- package/.agents/skills/minimal-context/SKILL.md +0 -177
- package/.agents/skills/napkin/SKILL.md +0 -84
- package/.agents/skills/opencode-agent-design/SKILL.md +0 -77
- package/.agents/skills/playwright-cli/SKILL.md +0 -62
- package/.agents/skills/pr-orchestrator/SKILL.md +0 -366
- package/.agents/skills/product-manager/SKILL.md +0 -519
- package/.agents/skills/seo-audit/SKILL.md +0 -176
- package/.agents/skills/stack-variant-creator/SKILL.md +0 -265
- package/.agents/skills/tech-lead/SKILL.md +0 -453
- package/.agents/skills/tester/SKILL.md +0 -399
- package/.agents/skills/token-economy/SKILL.md +0 -137
- package/.agents/skills/vue-nuxt/SKILL.md +0 -102
- package/.agents/skills/wordpress-engineer/SKILL.md +0 -75
- package/.codex/prompts/README.md +0 -44
- package/.codex/prompts/autopilot.md +0 -50
- package/.codex/prompts/deploy.md +0 -33
- package/.codex/prompts/execute-selected-pr.md +0 -35
- package/.codex/prompts/fix-issue.md +0 -34
- package/.codex/prompts/minimal-context-mode.md +0 -55
- package/.codex/prompts/orchestrate-next.md +0 -33
- package/.codex/prompts/plan-from-requirement.md +0 -37
- package/.codex/prompts/review-implementation.md +0 -33
- package/.codex/prompts/roadmap-audit.md +0 -22
- package/.codex/prompts/specs/create-spec-from-requirement.md +0 -26
- package/.codex/prompts/specs/review-spec.md +0 -29
- package/.codex/prompts/specs/spec-to-pr-breakdown.md +0 -23
- package/.codex/prompts/specs/spec-to-technical-plan.md +0 -28
- package/.codex/prompts/start-project.md +0 -29
- package/.codex/prompts/token-economy-mode.md +0 -48
- package/.codex/prompts/validate-work.md +0 -28
- package/checklists/change-spec-readiness-checklist.md +0 -34
- package/docs/full-documentation.md +0 -661
- package/docs/setup-codex-opencode.md +0 -313
- package/harness/README.md +0 -106
- package/opencode/README.md +0 -84
- package/opencode/agents/README.md +0 -113
- package/opencode/agents/atlas.md +0 -127
- package/opencode/agents/discovery.md +0 -61
- package/opencode/agents/fixer.md +0 -51
- package/opencode/agents/implementer.md +0 -61
- package/opencode/agents/orchestrator.md +0 -145
- package/opencode/agents/planner.md +0 -60
- package/opencode/agents/prompt-engineer.md +0 -50
- package/opencode/agents/release-manager.md +0 -50
- package/opencode/agents/reviewer.md +0 -51
- package/opencode/agents/spec-engineer.md +0 -85
- package/opencode/agents/validator.md +0 -50
- package/opencode/agents/wordpress-engineer.md +0 -49
- package/opencode/commands/README.md +0 -48
- package/opencode/commands/autopilot.md +0 -50
- package/opencode/commands/deploy.md +0 -35
- package/opencode/commands/execute.md +0 -47
- package/opencode/commands/orchestrate.md +0 -37
- package/opencode/commands/plan.md +0 -39
- package/opencode/commands/review.md +0 -33
- package/opencode/commands/roadmap-audit.md +0 -30
- package/opencode/commands/ship.md +0 -48
- package/opencode/commands/specs/create-spec-from-request.md +0 -27
- package/opencode/commands/specs/create-spec-from-requirement.md +0 -25
- package/opencode/commands/specs/review-spec.md +0 -26
- package/opencode/commands/specs/spec-to-pr-breakdown.md +0 -19
- package/opencode/commands/specs/spec-to-tasks.md +0 -26
- package/opencode/commands/specs/spec-to-technical-plan.md +0 -27
- package/opencode/commands/start.md +0 -45
- package/opencode/commands/token-economy.md +0 -29
- package/opencode/commands/validate.md +0 -33
- package/opencode.jsonc +0 -235
- package/packages/ai-workflow/README.md +0 -82
- package/packages/ai-workflow/src/cli.js +0 -70
- package/packages/ai-workflow/src/commands/codex.js +0 -37
- package/packages/ai-workflow/src/commands/doctor.js +0 -168
- package/packages/ai-workflow/src/commands/guide.js +0 -194
- package/packages/ai-workflow/src/core/opencode-merge.js +0 -172
- package/packages/ai-workflow/src/core/symlink-layout.js +0 -54
- package/packages/ai-workflow/src/core/templates.js +0 -276
- package/runbooks/agent-delegation-workflow.md +0 -111
- package/runbooks/apply-starter-to-real-project.md +0 -445
- package/runbooks/commands-cheatsheet.md +0 -71
- package/runbooks/how-to-use-skills.md +0 -713
- package/runbooks/quick-start-guide.md +0 -213
- package/runbooks/tutorial-walkthroughs.md +0 -416
- package/runbooks/use-linear-for-operational-planning.md +0 -185
- package/runbooks/use-napkin-project-memory.md +0 -77
- package/templates/AGENTS.template.md +0 -397
- package/templates/DESIGN.template.md +0 -484
- package/templates/PR-PLAN.template.md +0 -172
- package/templates/README.template.md +0 -293
- package/templates/REQUIREMENT.template.md +0 -165
- package/templates/SPEC.template.md +0 -397
- package/templates/TECH-PLAN.template.md +0 -244
- package/templates/change-proposal.template.md +0 -97
- /package/{checklists/spec-readiness-checklist.md → dist-assets/docs/policies/SPEC_READINESS.md} +0 -0
- /package/{prompts → dist-assets/prompts}/00-bootstrap-project.md +0 -0
- /package/{prompts → dist-assets/prompts}/01-create-requirement.md +0 -0
- /package/{prompts → dist-assets/prompts}/02-create-spec.md +0 -0
- /package/{prompts → dist-assets/prompts}/03-create-tech-plan.md +0 -0
- /package/{prompts → dist-assets/prompts}/04-breakdown-prs.md +0 -0
- /package/{prompts → dist-assets/prompts}/05-implement-pr.md +0 -0
- /package/{prompts → dist-assets/prompts}/06-review-and-fix.md +0 -0
- /package/{prompts → dist-assets/prompts}/07-apply-design.md +0 -0
- /package/{prompts → dist-assets/prompts}/08-validate.md +0 -0
- /package/{prompts → dist-assets/prompts}/09-deploy.md +0 -0
- /package/{prompts → dist-assets/prompts}/commands/implement.md +0 -0
- /package/{prompts → dist-assets/prompts}/commands/requirement.md +0 -0
- /package/{prompts → dist-assets/prompts}/commands/spec.md +0 -0
- /package/{prompts → dist-assets/prompts}/commands/tech-plan.md +0 -0
- /package/{prompts → dist-assets/prompts}/commands/validate.md +0 -0
- /package/{runbooks → dist-assets/runbooks}/branch-cleanup.md +0 -0
- /package/{runbooks → dist-assets/runbooks}/deploy-checklist.md +0 -0
- /package/{runbooks → dist-assets/runbooks}/publication-readiness-checklist.md +0 -0
- /package/{runbooks → dist-assets/runbooks}/publish-package-checklist.md +0 -0
- /package/{runbooks → dist-assets/runbooks}/team-governance-pr-readiness.md +0 -0
- /package/{runbooks → dist-assets/runbooks}/validate-starter-in-real-project.md +0 -0
- /package/{runbooks → dist-assets/runbooks}/validation-checklist.md +0 -0
- /package/{schemas → dist-assets/schemas}/README.md +0 -0
- /package/{schemas → dist-assets/schemas}/functional-spec.schema.json +0 -0
- /package/{schemas → dist-assets/schemas}/handoff.schema.json +0 -0
- /package/{schemas → dist-assets/schemas}/pr-breakdown.schema.json +0 -0
- /package/{schemas → dist-assets/schemas}/requirement.schema.json +0 -0
- /package/{schemas → dist-assets/schemas}/technical-plan.schema.json +0 -0
- /package/{schemas → dist-assets/schemas}/validation-report.schema.json +0 -0
- /package/{packages/ai-workflow/src → src}/core/backup.js +0 -0
- /package/{packages/ai-workflow/src → src}/core/filesystem.js +0 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { getFullAgentContent, getFullSkillFiles, discoverPackageFiles, readPackageFile, getPackageVersion } from "./package-assets.js";
|
|
2
|
+
import { getCanonicalAgentName } from "./identity.js";
|
|
3
|
+
|
|
4
|
+
const COMMON_FILES = {
|
|
5
|
+
"opencode/README.md": `# OpenCode Setup\n\nThis directory is managed by \`ai-workflow\` init.\nAdd agent and command files required by your project workflow.\n`
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const FULL_PRIMARY_AGENTS = [
|
|
9
|
+
"atlas",
|
|
10
|
+
"nexus",
|
|
11
|
+
"orion",
|
|
12
|
+
"astra",
|
|
13
|
+
"sage",
|
|
14
|
+
"phoenix"
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const FULL_SKILLS = [
|
|
18
|
+
"project-memory",
|
|
19
|
+
"optimize-tokens",
|
|
20
|
+
"documentation",
|
|
21
|
+
"architecture",
|
|
22
|
+
"technical-leadership",
|
|
23
|
+
"product-discovery",
|
|
24
|
+
"product-planning",
|
|
25
|
+
"spec-driven-development",
|
|
26
|
+
"pr-workflow",
|
|
27
|
+
"qa-workflow",
|
|
28
|
+
"release-workflow",
|
|
29
|
+
"deployment",
|
|
30
|
+
"ui-ux-design",
|
|
31
|
+
"design-principles",
|
|
32
|
+
"frontend-development",
|
|
33
|
+
"backend-development",
|
|
34
|
+
"full-stack-development",
|
|
35
|
+
"refactoring",
|
|
36
|
+
"prompt-engineer"
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const KIT_VERSION = getPackageVersion() ?? "unknown";
|
|
40
|
+
|
|
41
|
+
function buildRuntimeFiles({ includeFormalEvidence = false } = {}) {
|
|
42
|
+
const files = {};
|
|
43
|
+
|
|
44
|
+
for (const agent of FULL_PRIMARY_AGENTS) {
|
|
45
|
+
const content = getFullAgentContent(agent);
|
|
46
|
+
if (content === null) {
|
|
47
|
+
throw new Error(`CRITICAL FAILURE: Missing required agent asset: dist-assets/agents/${agent}.md`);
|
|
48
|
+
}
|
|
49
|
+
files[`opencode/agents/${agent}.md`] = content;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
for (const skill of FULL_SKILLS) {
|
|
53
|
+
const skillFiles = getFullSkillFiles(skill);
|
|
54
|
+
if (Object.keys(skillFiles).length === 0) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
for (const [relPath, content] of Object.entries(skillFiles)) {
|
|
58
|
+
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
59
|
+
if (!files[targetPath]) {
|
|
60
|
+
files[targetPath] = content;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const commandFiles = discoverPackageFiles("dist-assets/commands");
|
|
66
|
+
for (const [relPath, content] of Object.entries(commandFiles)) {
|
|
67
|
+
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
68
|
+
if (!files[targetPath]) {
|
|
69
|
+
files[targetPath] = content;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const opencodeSkillFiles = discoverPackageFiles("dist-assets/skills");
|
|
74
|
+
for (const [relPath, content] of Object.entries(opencodeSkillFiles)) {
|
|
75
|
+
const targetPath = relPath.replace(/^dist-assets\//, "opencode/");
|
|
76
|
+
if (!files[targetPath]) {
|
|
77
|
+
files[targetPath] = content;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const harnessWorkflowFiles = discoverPackageFiles("dist-assets/harness/workflows");
|
|
82
|
+
for (const [relPath, content] of Object.entries(harnessWorkflowFiles)) {
|
|
83
|
+
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
84
|
+
files[targetPath] = content;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const harnessHandoffFiles = discoverPackageFiles("dist-assets/harness/handoffs");
|
|
88
|
+
for (const [relPath, content] of Object.entries(harnessHandoffFiles)) {
|
|
89
|
+
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
90
|
+
files[targetPath] = content;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const agentsContent = readPackageFile("dist-assets/AGENTS.md");
|
|
94
|
+
if (agentsContent !== null) files["AGENTS.md"] = agentsContent;
|
|
95
|
+
|
|
96
|
+
const consumerQuickstart = readPackageFile("dist-assets/docs/QUICKSTART.md");
|
|
97
|
+
if (consumerQuickstart !== null) files["QUICKSTART.md"] = consumerQuickstart;
|
|
98
|
+
|
|
99
|
+
const policyContent = readPackageFile("dist-assets/docs/architecture-policy.md");
|
|
100
|
+
if (policyContent !== null) files["docs/architecture-policy.md"] = policyContent;
|
|
101
|
+
|
|
102
|
+
const dpContent = readPackageFile("dist-assets/docs/design-patterns-policy.md");
|
|
103
|
+
if (dpContent !== null) files["docs/design-patterns-policy.md"] = dpContent;
|
|
104
|
+
|
|
105
|
+
const visualValidationGuide = readPackageFile("dist-assets/docs/visual-validation-guide.md");
|
|
106
|
+
if (visualValidationGuide !== null) files["docs/visual-validation-guide.md"] = visualValidationGuide;
|
|
107
|
+
|
|
108
|
+
const governancePolicies = discoverPackageFiles("dist-assets/docs/policies");
|
|
109
|
+
for (const [relPath, content] of Object.entries(governancePolicies)) {
|
|
110
|
+
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
111
|
+
files[targetPath] = content;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const compatibilityDocs = discoverPackageFiles("dist-assets/docs/compatibility");
|
|
115
|
+
for (const [relPath, content] of Object.entries(compatibilityDocs)) {
|
|
116
|
+
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
117
|
+
files[targetPath] = content;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const qualityReferenceFiles = discoverPackageFiles("dist-assets/docs/references");
|
|
121
|
+
for (const [relPath, content] of Object.entries(qualityReferenceFiles)) {
|
|
122
|
+
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
123
|
+
files[targetPath] = content;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const workflowProfileFiles = discoverPackageFiles("dist-assets/docs/profiles");
|
|
127
|
+
for (const [relPath, content] of Object.entries(workflowProfileFiles)) {
|
|
128
|
+
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
129
|
+
files[targetPath] = content;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const templateFiles = discoverPackageFiles("dist-assets/templates");
|
|
133
|
+
for (const [relPath, content] of Object.entries(templateFiles)) {
|
|
134
|
+
if (!includeFormalEvidence && relPath.includes("/owner-evidence/")) continue;
|
|
135
|
+
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
136
|
+
files[targetPath] = content;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const schemaFiles = discoverPackageFiles("dist-assets/schemas");
|
|
140
|
+
for (const [relPath, content] of Object.entries(schemaFiles)) {
|
|
141
|
+
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
142
|
+
files[targetPath] = content;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return files;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function buildExtraFiles() {
|
|
149
|
+
const files = {};
|
|
150
|
+
const exampleFiles = discoverPackageFiles("dist-assets/examples");
|
|
151
|
+
for (const [relPath, content] of Object.entries(exampleFiles)) {
|
|
152
|
+
const targetPath = relPath.replace(/^dist-assets\//, "");
|
|
153
|
+
files[targetPath] = content;
|
|
154
|
+
}
|
|
155
|
+
return files;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const STANDARD_FILES = buildRuntimeFiles();
|
|
159
|
+
const FULL_FILES = { ...buildRuntimeFiles({ includeFormalEvidence: true }), ...buildExtraFiles() };
|
|
160
|
+
|
|
161
|
+
export const PROFILE_FILES = {
|
|
162
|
+
standard: {
|
|
163
|
+
...COMMON_FILES,
|
|
164
|
+
...STANDARD_FILES
|
|
165
|
+
},
|
|
166
|
+
full: {
|
|
167
|
+
...COMMON_FILES,
|
|
168
|
+
...FULL_FILES
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export function isValidProfile(profile) {
|
|
173
|
+
return profile === "standard" || profile === "full";
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function getTemplateFiles(profile = "standard") {
|
|
177
|
+
return PROFILE_FILES[profile] ?? PROFILE_FILES.standard;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function getManagedBlocks() {
|
|
181
|
+
return [
|
|
182
|
+
...FULL_PRIMARY_AGENTS.map((agent) => `opencode.jsonc:agent.${getCanonicalAgentName(agent)}`),
|
|
183
|
+
...FULL_SKILLS.map((skill) => `opencode.jsonc:agent.${getCanonicalAgentName(skill)}`),
|
|
184
|
+
"opencode.jsonc:command.atlas",
|
|
185
|
+
"opencode.jsonc:command.run",
|
|
186
|
+
"opencode.jsonc:command.discover",
|
|
187
|
+
"opencode.jsonc:command.spec-create",
|
|
188
|
+
"opencode.jsonc:command.spec-review",
|
|
189
|
+
"opencode.jsonc:command.spec-implement",
|
|
190
|
+
"opencode.jsonc:command.plan",
|
|
191
|
+
"opencode.jsonc:command.implement",
|
|
192
|
+
"opencode.jsonc:command.validate",
|
|
193
|
+
"opencode.jsonc:command.audit",
|
|
194
|
+
"opencode.jsonc:command.optimize-tokens",
|
|
195
|
+
"opencode.jsonc:command.update-memory",
|
|
196
|
+
"opencode.jsonc:command.release",
|
|
197
|
+
"opencode.jsonc:command.deploy"
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function buildAiWorkflowConfig({ profile, managedFiles, managedLinks = [] }) {
|
|
202
|
+
const version = getPackageVersion();
|
|
203
|
+
const now = new Date().toISOString();
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
package: "@williambeto/ai-workflow",
|
|
207
|
+
version,
|
|
208
|
+
runtime: "opencode",
|
|
209
|
+
profile,
|
|
210
|
+
installMode: "project-local",
|
|
211
|
+
generatedAt: now,
|
|
212
|
+
paths: {
|
|
213
|
+
agents: ".ai-workflow/opencode/agents",
|
|
214
|
+
commands: ".ai-workflow/opencode/commands",
|
|
215
|
+
skills: ".ai-workflow/opencode/skills",
|
|
216
|
+
policies: ".ai-workflow/docs/policies",
|
|
217
|
+
harness: ".ai-workflow/harness",
|
|
218
|
+
schemas: ".ai-workflow/schemas"
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const DELIVERY_SUMMARY_FIELDS = Object.freeze([
|
|
2
|
+
"Status",
|
|
3
|
+
"Branch",
|
|
4
|
+
"Changes",
|
|
5
|
+
"Validation",
|
|
6
|
+
"Known limitations"
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
export function buildDeliverySummary({ evidence }) {
|
|
10
|
+
const commands = (evidence?.commands || []).map((item) => `${item.command || item.name}: ${item.status}`).join("; ") || "No validation commands recorded";
|
|
11
|
+
return {
|
|
12
|
+
Status: evidence?.status || "BLOCKED",
|
|
13
|
+
Branch: evidence?.branch || "unknown",
|
|
14
|
+
Changes: (evidence?.changedFiles || []).join(", ") || "No changed files recorded",
|
|
15
|
+
Validation: commands,
|
|
16
|
+
"Known limitations": (evidence?.limitations || []).join("; ") || "None recorded"
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function validateDeliverySummary(summary) {
|
|
21
|
+
const missing = DELIVERY_SUMMARY_FIELDS.filter((field) => !String(summary?.[field] || "").trim());
|
|
22
|
+
const status = String(summary?.Status || "").trim();
|
|
23
|
+
const invalid = [];
|
|
24
|
+
if (!["COMPLETED", "COMPLETED_WITH_NOTES", "BLOCKED"].includes(status)) invalid.push("Status");
|
|
25
|
+
if (status !== "BLOCKED" && /FAIL|BLOCKED/.test(String(summary?.Validation || ""))) invalid.push("successful status with failed validation");
|
|
26
|
+
return { status: missing.length || invalid.length ? "FAIL" : "PASS", missing, invalid };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function formatDeliverySummary(summary) {
|
|
30
|
+
return DELIVERY_SUMMARY_FIELDS.map((field) => `${field}: ${summary?.[field] || "NOT_RECORDED"}`).join("\n");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Backward-compatible aliases for integrations that imported the previous API.
|
|
34
|
+
export const CANONICAL_FINALIZATION_FIELDS = DELIVERY_SUMMARY_FIELDS;
|
|
35
|
+
export const buildCanonicalFinalization = ({ collectorStatus = "BLOCKED", branchRecovery = "unknown" } = {}) => ({
|
|
36
|
+
Status: collectorStatus === "PASS" ? "COMPLETED" : collectorStatus === "PASS_WITH_NOTES" ? "COMPLETED_WITH_NOTES" : "BLOCKED",
|
|
37
|
+
Branch: branchRecovery,
|
|
38
|
+
Changes: "See observed diff",
|
|
39
|
+
Validation: collectorStatus,
|
|
40
|
+
"Known limitations": "See evidence"
|
|
41
|
+
});
|
|
42
|
+
export const validateCanonicalFinalization = validateDeliverySummary;
|
|
43
|
+
export const formatCanonicalFinalization = formatDeliverySummary;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { QualityGuard } from "./quality-guard.js";
|
|
5
|
+
|
|
6
|
+
const VALIDATION_KINDS = new Set(["test", "build", "typecheck", "lint", "security", "smoke", "validate", "other"]);
|
|
7
|
+
|
|
8
|
+
function publicStatus(status) {
|
|
9
|
+
if (status === "PASS") return "COMPLETED";
|
|
10
|
+
if (status === "PASS_WITH_NOTES") return "COMPLETED_WITH_NOTES";
|
|
11
|
+
return "BLOCKED";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function inferTaskKind(task = {}) {
|
|
15
|
+
const explicit = String(task.kind || "").trim().toLowerCase();
|
|
16
|
+
if (VALIDATION_KINDS.has(explicit)) return explicit;
|
|
17
|
+
|
|
18
|
+
const value = `${task.name || ""} ${task.command || ""}`.toLowerCase();
|
|
19
|
+
if (/\b(test|tests|vitest|jest|mocha|ava|tap|pytest|phpunit|rspec|cypress|playwright)\b/.test(value)) return "test";
|
|
20
|
+
if (/\b(typecheck|tsc\b|mypy|pyright)\b/.test(value)) return "typecheck";
|
|
21
|
+
if (/\b(lint|eslint|stylelint|ruff|flake8|pylint)\b/.test(value)) return "lint";
|
|
22
|
+
if (/\b(build|bundle|compile)\b/.test(value)) return "build";
|
|
23
|
+
if (/\b(audit|security|sast|scan)\b/.test(value)) return "security";
|
|
24
|
+
if (/\b(smoke|curl|preview|serve)\b/.test(value)) return "smoke";
|
|
25
|
+
if (/\bvalidate\b/.test(value)) return "validate";
|
|
26
|
+
return "other";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class EvidenceCollector {
|
|
30
|
+
constructor({ cwd, maxLogLength = 2000, timeout = 60000, taskSlug = null, mode = null, profile = "generic", branchRecovery = "NOT_RECORDED" } = {}) {
|
|
31
|
+
this.cwd = cwd;
|
|
32
|
+
this.maxLogLength = maxLogLength;
|
|
33
|
+
this.timeout = timeout;
|
|
34
|
+
this.taskSlug = taskSlug;
|
|
35
|
+
this.mode = mode;
|
|
36
|
+
this.profile = profile;
|
|
37
|
+
this.branchRecovery = branchRecovery;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
runTask(task) {
|
|
41
|
+
const kind = inferTaskKind(task);
|
|
42
|
+
if (task.presetStatus) {
|
|
43
|
+
return { name: task.name, command: task.command, kind, status: task.presetStatus, exitCode: null, summary: task.summary, output: "" };
|
|
44
|
+
}
|
|
45
|
+
const result = spawnSync(task.command, {
|
|
46
|
+
cwd: this.cwd,
|
|
47
|
+
shell: true,
|
|
48
|
+
encoding: "utf8",
|
|
49
|
+
timeout: this.timeout
|
|
50
|
+
});
|
|
51
|
+
let status = result.status === 0 ? "PASS" : "FAIL";
|
|
52
|
+
let summary = status === "PASS" ? "Command completed successfully." : "Command failed.";
|
|
53
|
+
if (result.error?.code === "ETIMEDOUT") {
|
|
54
|
+
status = "BLOCKED";
|
|
55
|
+
summary = `Command timed out after ${this.timeout / 1000}s.`;
|
|
56
|
+
}
|
|
57
|
+
const rawOutput = `${result.stdout || ""}${result.stderr || ""}`.trim();
|
|
58
|
+
const output = rawOutput.length > this.maxLogLength ? `${rawOutput.slice(0, this.maxLogLength)}\n... [TRUNCATED]` : rawOutput;
|
|
59
|
+
return { name: task.name, command: task.command, kind, status, exitCode: result.status, signal: result.signal, summary, output };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async collect(tasks, { writeArtifact = false } = {}) {
|
|
63
|
+
const qualityGuard = new QualityGuard({ cwd: this.cwd, taskSlug: this.taskSlug, mode: this.mode, evidenceWillBePersisted: writeArtifact });
|
|
64
|
+
const implementation = qualityGuard.isImplementationTask();
|
|
65
|
+
const executableBehavior = await qualityGuard.hasExecutableBehaviorChanges();
|
|
66
|
+
const results = tasks.map((task) => this.runTask(task));
|
|
67
|
+
const policyValidation = await qualityGuard.verify();
|
|
68
|
+
const behaviorTests = results.filter((result) => result.kind === "test");
|
|
69
|
+
const passingBehaviorTest = behaviorTests.some((result) => result.status === "PASS");
|
|
70
|
+
|
|
71
|
+
let overallStatus = "PASS";
|
|
72
|
+
if (implementation && executableBehavior && tasks.length === 0) overallStatus = "BLOCKED";
|
|
73
|
+
else if (results.some((result) => ["FAIL", "BLOCKED", "FAIL_QUALITY_GATE"].includes(result.status))) overallStatus = "FAIL_QUALITY_GATE";
|
|
74
|
+
else if (executableBehavior && !passingBehaviorTest) overallStatus = "BLOCKED";
|
|
75
|
+
else if (["FAIL", "BLOCKED", "FAIL_QUALITY_GATE"].includes(policyValidation.overallStatus)) overallStatus = "FAIL_QUALITY_GATE";
|
|
76
|
+
else if (results.some((result) => result.status === "PASS_WITH_NOTES") || policyValidation.overallStatus === "PASS_WITH_NOTES") overallStatus = "PASS_WITH_NOTES";
|
|
77
|
+
|
|
78
|
+
const limitations = [];
|
|
79
|
+
if (implementation && executableBehavior && tasks.length === 0) limitations.push("No meaningful validation command was available for executable implementation work.");
|
|
80
|
+
if (executableBehavior && behaviorTests.length === 0) {
|
|
81
|
+
limitations.push("Executable behavior changed without a proportional automated behavior test. Build, lint, typecheck, smoke, screenshots, and manual review do not replace behavior tests.");
|
|
82
|
+
}
|
|
83
|
+
for (const [name, check] of Object.entries(policyValidation.checks || {})) {
|
|
84
|
+
if (check.status === "PASS_WITH_NOTES" && check.reason) limitations.push(`${name}: ${check.reason}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const evidence = {
|
|
88
|
+
timestamp: new Date().toISOString(),
|
|
89
|
+
taskSlug: this.taskSlug,
|
|
90
|
+
executionMode: this.mode,
|
|
91
|
+
workflowProfile: this.profile,
|
|
92
|
+
branchRecovery: this.branchRecovery,
|
|
93
|
+
branch: policyValidation.git?.branch || "unknown",
|
|
94
|
+
changedFiles: policyValidation.git?.changedFiles || [],
|
|
95
|
+
status: publicStatus(overallStatus),
|
|
96
|
+
internalStatus: overallStatus,
|
|
97
|
+
commands: results,
|
|
98
|
+
checks: policyValidation.checks,
|
|
99
|
+
limitations
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
if (writeArtifact) {
|
|
103
|
+
await fs.writeFile(path.join(this.cwd, "EVIDENCE.json"), JSON.stringify(evidence, null, 2));
|
|
104
|
+
}
|
|
105
|
+
return evidence;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { inferTaskKind };
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const PROTECTED_BRANCHES = new Set(["main", "master"]);
|
|
6
|
+
|
|
7
|
+
function normalizePath(file) {
|
|
8
|
+
return String(file || "").trim().replaceAll("\\", "/");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function parseStatusLine(line) {
|
|
12
|
+
const value = String(line || "");
|
|
13
|
+
const match = value.match(/^(?:[ MADRCU?!]{2}\s+|[MADRCU?!]\s+)(.+)$/);
|
|
14
|
+
if (!match) return "";
|
|
15
|
+
const candidate = match[1].trim();
|
|
16
|
+
const renamed = candidate.includes(" -> ") ? candidate.split(" -> ").at(-1) : candidate;
|
|
17
|
+
return normalizePath(renamed);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isManagedChangePath(file = "") {
|
|
21
|
+
return /(^|\/)(node_modules|vendor|dist|coverage|\.cache)(\/|$)/.test(normalizePath(file));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function exists(target) {
|
|
25
|
+
try {
|
|
26
|
+
await fs.access(target);
|
|
27
|
+
return true;
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { parseStatusLine };
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* QualityGuard verifies objective delivery safety. It does not grade prose or
|
|
37
|
+
* require ceremonial workflow files.
|
|
38
|
+
*/
|
|
39
|
+
export class QualityGuard {
|
|
40
|
+
constructor({ cwd, taskSlug = null, mode = null, evidenceWillBePersisted = false } = {}) {
|
|
41
|
+
this.cwd = cwd;
|
|
42
|
+
this.taskSlug = taskSlug;
|
|
43
|
+
this.mode = mode;
|
|
44
|
+
this._changedFiles = null;
|
|
45
|
+
this._hasHead = null;
|
|
46
|
+
this.evidenceWillBePersisted = evidenceWillBePersisted;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
runGit(command) {
|
|
50
|
+
return execSync(command, {
|
|
51
|
+
cwd: this.cwd,
|
|
52
|
+
encoding: "utf8",
|
|
53
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
54
|
+
}).trim();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
hasGitHead() {
|
|
58
|
+
if (this._hasHead !== null) return this._hasHead;
|
|
59
|
+
try {
|
|
60
|
+
this.runGit("git rev-parse --verify HEAD");
|
|
61
|
+
this._hasHead = true;
|
|
62
|
+
} catch {
|
|
63
|
+
this._hasHead = false;
|
|
64
|
+
}
|
|
65
|
+
return this._hasHead;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getCurrentBranch() {
|
|
69
|
+
try {
|
|
70
|
+
return this.runGit("git branch --show-current") || "unknown";
|
|
71
|
+
} catch {
|
|
72
|
+
return "unknown";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getChangedFiles() {
|
|
77
|
+
if (this._changedFiles) return this._changedFiles;
|
|
78
|
+
const files = new Set();
|
|
79
|
+
try {
|
|
80
|
+
if (this.hasGitHead()) {
|
|
81
|
+
this.runGit("git diff --name-only HEAD")
|
|
82
|
+
.split("\n")
|
|
83
|
+
.map(normalizePath)
|
|
84
|
+
.filter(Boolean)
|
|
85
|
+
.forEach((file) => files.add(file));
|
|
86
|
+
}
|
|
87
|
+
this.runGit("git status --short --untracked-files=all")
|
|
88
|
+
.split("\n")
|
|
89
|
+
.map(parseStatusLine)
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.filter((file) => !isManagedChangePath(file))
|
|
92
|
+
.forEach((file) => files.add(file));
|
|
93
|
+
} catch {
|
|
94
|
+
// A non-git consumer may still run validation commands. Branch safety is
|
|
95
|
+
// reported as unavailable rather than fabricated.
|
|
96
|
+
}
|
|
97
|
+
this._changedFiles = [...files].sort();
|
|
98
|
+
return this._changedFiles;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
isDocsTask() {
|
|
102
|
+
const files = this.getChangedFiles();
|
|
103
|
+
return files.length > 0 && files.every((file) => file.endsWith(".md") || file.endsWith(".txt") || file.startsWith("docs/"));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
isUiTask() {
|
|
107
|
+
return this.getChangedFiles().some((file) =>
|
|
108
|
+
/\.(tsx|jsx|vue|html|css|scss|less|blade\.php)$/.test(file) ||
|
|
109
|
+
/(^|\/)(components|pages|views)\//.test(file)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
isImplementationTask() {
|
|
114
|
+
const files = this.getChangedFiles();
|
|
115
|
+
if (files.length === 0 || this.isDocsTask()) return false;
|
|
116
|
+
return files.some((file) =>
|
|
117
|
+
file === "package.json" ||
|
|
118
|
+
/^(src|app|pages|components|server|api)\//.test(file) ||
|
|
119
|
+
/\.(js|jsx|ts|tsx|vue|php|py|rb|go|java|css|scss|html)$/.test(file)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
async hasExecutableBehaviorChanges() {
|
|
125
|
+
const files = this.getChangedFiles().filter((file) => !isManagedChangePath(file));
|
|
126
|
+
const executableExtensions = /\.(js|jsx|mjs|cjs|ts|tsx|vue|php|py|rb|go|java|cs|rs|swift|kt|kts|sh|bash)$/i;
|
|
127
|
+
if (files.some((file) => executableExtensions.test(file))) return true;
|
|
128
|
+
|
|
129
|
+
for (const file of files.filter((item) => /\.(html?|xhtml)$/i.test(item))) {
|
|
130
|
+
try {
|
|
131
|
+
const content = await fs.readFile(path.join(this.cwd, file), "utf8");
|
|
132
|
+
if (/<script\b|\son(?:click|change|input|submit|keydown|keyup|load|error)\s*=|javascript:/i.test(content)) return true;
|
|
133
|
+
} catch {
|
|
134
|
+
// Missing or unreadable changed files are handled by the normal validation path.
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
verifyBranchSafety() {
|
|
141
|
+
const branch = this.getCurrentBranch();
|
|
142
|
+
const implementation = this.isImplementationTask();
|
|
143
|
+
if (!this.hasGitHead()) {
|
|
144
|
+
return { status: "PASS_WITH_NOTES", branch, reason: "Git HEAD unavailable; branch safety could not be verified." };
|
|
145
|
+
}
|
|
146
|
+
if (implementation && PROTECTED_BRANCHES.has(branch)) {
|
|
147
|
+
return { status: "FAIL_QUALITY_GATE", branch, reason: `Implementation changes are present on protected branch '${branch}'.` };
|
|
148
|
+
}
|
|
149
|
+
return { status: "PASS", branch };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async verifyDocumentationNeed() {
|
|
153
|
+
if (!this.isImplementationTask()) return { status: "NOT_RUN", reason: "No implementation changes detected." };
|
|
154
|
+
const files = this.getChangedFiles();
|
|
155
|
+
const docsChanged = files.some((file) => file === "README.md" || file.startsWith("docs/") || file.endsWith(".md"));
|
|
156
|
+
const publicSurfaceChanged = files.some((file) =>
|
|
157
|
+
file === "package.json" ||
|
|
158
|
+
/(^|\/)(routes?|api|cli|commands?|config|public)\//.test(file) ||
|
|
159
|
+
/README|CHANGELOG/.test(file)
|
|
160
|
+
);
|
|
161
|
+
if (publicSurfaceChanged && !docsChanged) {
|
|
162
|
+
return { status: "PASS_WITH_NOTES", reason: "Public behavior may have changed without a documentation update." };
|
|
163
|
+
}
|
|
164
|
+
return { status: "PASS" };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async verifyUiEvidence() {
|
|
168
|
+
if (!this.isUiTask()) return { status: "NOT_RUN", reason: "Non-UI task." };
|
|
169
|
+
const candidates = ["docs", "screenshots", "evidence"];
|
|
170
|
+
const found = [];
|
|
171
|
+
for (const dir of candidates) {
|
|
172
|
+
const root = path.join(this.cwd, dir);
|
|
173
|
+
if (!(await exists(root))) continue;
|
|
174
|
+
const entries = await fs.readdir(root, { recursive: true }).catch(() => []);
|
|
175
|
+
for (const entry of entries) {
|
|
176
|
+
if (/\.(png|jpe?g|webp)$/i.test(String(entry))) found.push(normalizePath(path.join(dir, String(entry))));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return found.length
|
|
180
|
+
? { status: "PASS", screenshots: found.slice(0, 20) }
|
|
181
|
+
: { status: "PASS_WITH_NOTES", reason: "UI changed without persisted screenshots; rely on the stated browser/manual review only if it actually occurred." };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async verifyFullModeEvidence() {
|
|
185
|
+
if (this.mode !== "full") return { status: "NOT_RUN", reason: "Persisted evidence is optional outside full mode." };
|
|
186
|
+
if (this.evidenceWillBePersisted) return { status: "PASS", reason: "Collector will persist observed full-mode evidence." };
|
|
187
|
+
const candidates = [
|
|
188
|
+
path.join(this.cwd, "EVIDENCE.json"),
|
|
189
|
+
this.taskSlug ? path.join(this.cwd, "docs/workflows", this.taskSlug, "evidence.json") : null
|
|
190
|
+
].filter(Boolean);
|
|
191
|
+
for (const file of candidates) {
|
|
192
|
+
if (await exists(file)) return { status: "PASS", file: path.relative(this.cwd, file) };
|
|
193
|
+
}
|
|
194
|
+
return { status: "FAIL_QUALITY_GATE", reason: "Full mode requires persisted evidence generated from observed execution." };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
verifyTruthfulnessBoundary() {
|
|
198
|
+
if (!this.isUiTask()) return { status: "NOT_RUN", reason: "Non-UI task." };
|
|
199
|
+
return {
|
|
200
|
+
status: "NOT_RUN",
|
|
201
|
+
reason: "Product truthfulness requires semantic review; keyword matching is intentionally not used as proof."
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async verify() {
|
|
206
|
+
const branchSafety = this.verifyBranchSafety();
|
|
207
|
+
const documentation = await this.verifyDocumentationNeed();
|
|
208
|
+
const uiEvidence = await this.verifyUiEvidence();
|
|
209
|
+
const fullModeEvidence = await this.verifyFullModeEvidence();
|
|
210
|
+
const productTruthfulness = this.verifyTruthfulnessBoundary();
|
|
211
|
+
|
|
212
|
+
// productTruthfulness is intentionally excluded from the blocking checks array.
|
|
213
|
+
// Semantic truthfulness cannot be verified by keyword matching; it always returns
|
|
214
|
+
// NOT_RUN by design. It appears in the output as an observational signal for
|
|
215
|
+
// human reviewers, but never affects overallStatus.
|
|
216
|
+
const checks = [branchSafety, documentation, uiEvidence, fullModeEvidence];
|
|
217
|
+
let overallStatus = "PASS";
|
|
218
|
+
if (checks.some((check) => ["FAIL", "FAIL_QUALITY_GATE", "BLOCKED"].includes(check.status))) {
|
|
219
|
+
overallStatus = "FAIL_QUALITY_GATE";
|
|
220
|
+
} else if (checks.some((check) => check.status === "PASS_WITH_NOTES")) {
|
|
221
|
+
overallStatus = "PASS_WITH_NOTES";
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
overallStatus,
|
|
226
|
+
isUiTask: this.isUiTask(),
|
|
227
|
+
isDocsTask: this.isDocsTask(),
|
|
228
|
+
isImplementationTask: this.isImplementationTask(),
|
|
229
|
+
git: {
|
|
230
|
+
hasHead: this.hasGitHead(),
|
|
231
|
+
branch: this.getCurrentBranch(),
|
|
232
|
+
changedFiles: this.getChangedFiles()
|
|
233
|
+
},
|
|
234
|
+
checks: {
|
|
235
|
+
branchSafety,
|
|
236
|
+
documentation,
|
|
237
|
+
uiEvidence,
|
|
238
|
+
fullModeEvidence,
|
|
239
|
+
productTruthfulness
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|