@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,146 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
function slugify(value = "task") {
|
|
6
|
+
return String(value)
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
9
|
+
.replace(/^-+|-+$/g, "")
|
|
10
|
+
.slice(0, 48) || "task";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* BranchGate - Protects main/master and can recover safely onto a scoped branch.
|
|
15
|
+
*/
|
|
16
|
+
export class BranchGate {
|
|
17
|
+
constructor({ protectedBranches = ["main", "master"], memoryDir, cwd = process.cwd() } = {}) {
|
|
18
|
+
this.protectedBranches = protectedBranches;
|
|
19
|
+
this.logPath = memoryDir ? path.join(memoryDir, "GATE_ALERTS.log") : null;
|
|
20
|
+
this.cwd = cwd;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
run(command) {
|
|
24
|
+
return execSync(command, {
|
|
25
|
+
cwd: this.cwd,
|
|
26
|
+
encoding: "utf8",
|
|
27
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
28
|
+
}).trim();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
log(event) {
|
|
32
|
+
if (!this.logPath) return;
|
|
33
|
+
const timestamp = new Date().toISOString();
|
|
34
|
+
const logEntry = `[${timestamp}] ${event}\n`;
|
|
35
|
+
try {
|
|
36
|
+
if (!fs.existsSync(path.dirname(this.logPath))) fs.mkdirSync(path.dirname(this.logPath), { recursive: true });
|
|
37
|
+
fs.appendFileSync(this.logPath, logEntry);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error(`Failed to write to gate log: ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getDirtyState() {
|
|
44
|
+
const lines = this.run("git status --short").split("\n").filter(Boolean);
|
|
45
|
+
const tracked = lines.filter((line) => !line.startsWith("??"));
|
|
46
|
+
const untracked = lines.filter((line) => line.startsWith("??"));
|
|
47
|
+
return { clean: lines.length === 0, tracked, untracked, lines };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
createScopedBranch(taskSlug) {
|
|
51
|
+
const base = `feat/${slugify(taskSlug)}`;
|
|
52
|
+
let candidate = base;
|
|
53
|
+
let suffix = 2;
|
|
54
|
+
while (true) {
|
|
55
|
+
try {
|
|
56
|
+
this.run(`git show-ref --verify --quiet refs/heads/${candidate}`);
|
|
57
|
+
candidate = `${base}-${suffix++}`;
|
|
58
|
+
} catch {
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
this.run(`git switch -c ${candidate}`);
|
|
63
|
+
return candidate;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getCurrentBranch() {
|
|
67
|
+
try {
|
|
68
|
+
return this.run("git branch --show-current") || this.run("git symbolic-ref --short HEAD") || "unknown";
|
|
69
|
+
} catch {
|
|
70
|
+
try {
|
|
71
|
+
return this.run("git symbolic-ref --short HEAD") || "unknown";
|
|
72
|
+
} catch {
|
|
73
|
+
return "unknown";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {string} override
|
|
80
|
+
* @param {{autoRecover?: boolean, taskSlug?: string, readOnly?: boolean}} options
|
|
81
|
+
*/
|
|
82
|
+
check(override = "", { autoRecover = false, taskSlug = "implementation", readOnly = false } = {}) {
|
|
83
|
+
if (readOnly) {
|
|
84
|
+
const currentBranch = this.getCurrentBranch();
|
|
85
|
+
return { blocked: false, branch: currentBranch, recovered: false, readOnly: true };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
// Enforce fail-closed: verify inside worktree first
|
|
90
|
+
try {
|
|
91
|
+
this.run("git rev-parse --is-inside-work-tree");
|
|
92
|
+
} catch (e) {
|
|
93
|
+
const reason = "Not inside a Git repository or Git is unavailable. Implementation work is blocked.";
|
|
94
|
+
this.log(`BLOCKED: ${reason}`);
|
|
95
|
+
return { blocked: true, branch: "unknown", reason };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const currentBranch = this.getCurrentBranch();
|
|
99
|
+
if (currentBranch === "unknown") {
|
|
100
|
+
const reason = "Could not determine current Git branch name. Implementation work is blocked.";
|
|
101
|
+
this.log(`BLOCKED: ${reason}`);
|
|
102
|
+
return { blocked: true, branch: "unknown", reason };
|
|
103
|
+
}
|
|
104
|
+
const isProtected = this.protectedBranches.includes(currentBranch);
|
|
105
|
+
if (!isProtected) return { blocked: false, branch: currentBranch, recovered: false };
|
|
106
|
+
|
|
107
|
+
if (override && override.trim().length >= 20) {
|
|
108
|
+
this.log(`AUTHORIZED BYPASS on '${currentBranch}': ${override.trim()}`);
|
|
109
|
+
return { blocked: false, branch: currentBranch, authorized: true, recovered: false };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (autoRecover) {
|
|
113
|
+
const dirty = this.getDirtyState();
|
|
114
|
+
if (dirty.tracked.length > 0) {
|
|
115
|
+
const reason = `Unsafe tracked changes prevent Branch Gate Auto-Recovery: ${dirty.tracked.join(", ")}`;
|
|
116
|
+
this.log(`BLOCKED AUTO-RECOVERY on '${currentBranch}': ${reason}`);
|
|
117
|
+
return { blocked: true, branch: currentBranch, reason, dirtyState: dirty };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const recoveredBranch = this.createScopedBranch(taskSlug);
|
|
121
|
+
this.log(`AUTO-RECOVERED '${currentBranch}' -> '${recoveredBranch}'`);
|
|
122
|
+
return {
|
|
123
|
+
blocked: false,
|
|
124
|
+
branch: recoveredBranch,
|
|
125
|
+
branchBefore: currentBranch,
|
|
126
|
+
recovered: true,
|
|
127
|
+
dirtyState: dirty
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const reason = override && override.trim().length < 20
|
|
132
|
+
? "Override justification too short (min 20 chars)."
|
|
133
|
+
: `Direct commits to '${currentBranch}' are prohibited.`;
|
|
134
|
+
this.log(`BLOCKED ATTEMPT on '${currentBranch}': ${reason}`);
|
|
135
|
+
return {
|
|
136
|
+
blocked: true,
|
|
137
|
+
branch: currentBranch,
|
|
138
|
+
reason: `${reason} Enable safe auto-recovery or use AI_OVERRIDE with a concrete justification.`
|
|
139
|
+
};
|
|
140
|
+
} catch (error) {
|
|
141
|
+
const reason = `Git command failure on branch gate check: ${error.message}`;
|
|
142
|
+
this.log(`BLOCKED: ${reason}`);
|
|
143
|
+
return { blocked: true, branch: "unknown", error: error.message, reason, recovered: false };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* HandoffEngine - Bundles project context into a single Markdown packet.
|
|
7
|
+
*/
|
|
8
|
+
export class HandoffEngine {
|
|
9
|
+
constructor({ cwd }) {
|
|
10
|
+
this.cwd = cwd;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generates a handoff packet.
|
|
15
|
+
* @param {Object} data - Context data.
|
|
16
|
+
* @returns {Promise<string>} The generated packet content.
|
|
17
|
+
*/
|
|
18
|
+
async generate({ taskId, author, status, specPaths = [], nextActions, evidence = null }) {
|
|
19
|
+
const timestamp = new Date().toISOString();
|
|
20
|
+
|
|
21
|
+
// 1. Collect Specs content
|
|
22
|
+
let specsContent = "";
|
|
23
|
+
for (const specPath of specPaths) {
|
|
24
|
+
const absolutePath = path.isAbsolute(specPath) ? specPath : path.join(this.cwd, specPath);
|
|
25
|
+
try {
|
|
26
|
+
const content = await fs.readFile(absolutePath, "utf8");
|
|
27
|
+
specsContent += `### File: ${specPath}\n\n${content}\n\n`;
|
|
28
|
+
} catch (err) {
|
|
29
|
+
specsContent += `### File: ${specPath}\n\n[Error reading file: ${err.message}]\n\n`;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 2. Capture Git Diff
|
|
34
|
+
let gitDiff = "No changes detected.";
|
|
35
|
+
try {
|
|
36
|
+
gitDiff = execSync("git diff HEAD", { cwd: this.cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || "No changes detected.";
|
|
37
|
+
} catch (err) {
|
|
38
|
+
gitDiff = `[Error capturing diff: ${err.message}]`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 3. Read EVIDENCE.json or use provided in-memory evidence
|
|
42
|
+
let evidenceJson = "{}";
|
|
43
|
+
let evidenceSummary = "No evidence found.";
|
|
44
|
+
if (evidence) {
|
|
45
|
+
const data = typeof evidence === "string" ? JSON.parse(evidence) : evidence;
|
|
46
|
+
evidenceJson = JSON.stringify(data, null, 2);
|
|
47
|
+
evidenceSummary = `Status: ${data.status || data.internalStatus || "unknown"}, Commands: ${data.commands?.length || 0}`;
|
|
48
|
+
} else {
|
|
49
|
+
const evidencePath = path.join(this.cwd, "EVIDENCE.json");
|
|
50
|
+
try {
|
|
51
|
+
const content = await fs.readFile(evidencePath, "utf8");
|
|
52
|
+
const data = JSON.parse(content);
|
|
53
|
+
evidenceJson = JSON.stringify(data, null, 2);
|
|
54
|
+
evidenceSummary = `Status: ${data.status}, Commands: ${data.commands?.length || 0}`;
|
|
55
|
+
} catch (err) {
|
|
56
|
+
// Optional: don't fail if evidence is missing
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 4. Populate Template
|
|
61
|
+
const possibleTemplatePaths = [
|
|
62
|
+
path.join(this.cwd, ".ai-workflow/templates/HANDOFF.template.md"),
|
|
63
|
+
path.join(this.cwd, ".ai-workflow/harness/handoffs/HANDOFF.template.md"),
|
|
64
|
+
path.join(import.meta.dirname, "../../../dist-assets/templates/HANDOFF.template.md"),
|
|
65
|
+
path.join(import.meta.dirname, "../../../assets/templates/HANDOFF.template.md")
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
let template = null;
|
|
69
|
+
let lastError = null;
|
|
70
|
+
|
|
71
|
+
for (const tPath of possibleTemplatePaths) {
|
|
72
|
+
try {
|
|
73
|
+
template = await fs.readFile(tPath, "utf8");
|
|
74
|
+
break;
|
|
75
|
+
} catch (err) {
|
|
76
|
+
lastError = err;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!template) {
|
|
81
|
+
throw new Error(`CRITICAL FAILURE: Could not locate HANDOFF.template.md. Tried paths:\n${possibleTemplatePaths.join("\n")}\nLast error: ${lastError?.message}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const packet = template
|
|
85
|
+
.replace("${TASK_ID}", taskId || "unknown")
|
|
86
|
+
.replace("${TIMESTAMP}", timestamp)
|
|
87
|
+
.replace("${AUTHOR}", author || "AI Workflow Kit")
|
|
88
|
+
.replace("${STATUS}", status || "IN_PROGRESS")
|
|
89
|
+
.replace("${SPECS_CONTENT}", specsContent || "No specs provided.")
|
|
90
|
+
.replace("${EVIDENCE_SUMMARY}", evidenceSummary)
|
|
91
|
+
.replace("${EVIDENCE_JSON}", evidenceJson)
|
|
92
|
+
.replace("${GIT_DIFF}", gitDiff)
|
|
93
|
+
.replace("${NEXT_ACTIONS}", nextActions || "Awaiting manager assignment.");
|
|
94
|
+
|
|
95
|
+
const handoffDir = path.join(this.cwd, ".ai-workflow/handoffs");
|
|
96
|
+
await fs.mkdir(handoffDir, { recursive: true });
|
|
97
|
+
|
|
98
|
+
const packetName = `HANDOFF-${taskId || "TMP"}-${Date.now()}.md`;
|
|
99
|
+
const packetPath = path.join(handoffDir, packetName);
|
|
100
|
+
await fs.writeFile(packetPath, packet);
|
|
101
|
+
|
|
102
|
+
return packetPath;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { spawnSync, execSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Determines which npm scripts are available in the consumer project.
|
|
7
|
+
* @param {string} cwd
|
|
8
|
+
* @returns {Promise<Record<string, string>>}
|
|
9
|
+
*/
|
|
10
|
+
async function readScripts(cwd) {
|
|
11
|
+
try {
|
|
12
|
+
const pkg = JSON.parse(await fs.readFile(path.join(cwd, "package.json"), "utf8"));
|
|
13
|
+
return pkg.scripts || {};
|
|
14
|
+
} catch {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Returns the list of files changed since the last commit.
|
|
21
|
+
* Used to detect whether remediation produced any observable change.
|
|
22
|
+
* @param {string} cwd
|
|
23
|
+
* @returns {string[]}
|
|
24
|
+
*/
|
|
25
|
+
function getChangedFiles(cwd) {
|
|
26
|
+
try {
|
|
27
|
+
const output = execSync("git status --short", { cwd, encoding: "utf8" });
|
|
28
|
+
return output
|
|
29
|
+
.split("\n")
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.map((line) => line.slice(3).trim())
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
} catch {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Maps an affectedCheck source string to likely npm script names to re-run.
|
|
40
|
+
* e.g. "check:branchSafety" → [] (no command can fix a branch issue)
|
|
41
|
+
* "evidence" with failed test → ["test"]
|
|
42
|
+
* @param {string[]} affectedChecks
|
|
43
|
+
* @param {Record<string, string>} scripts
|
|
44
|
+
* @returns {string[]} ordered list of script names to retry
|
|
45
|
+
*/
|
|
46
|
+
function resolveRetryScripts(affectedChecks, scripts) {
|
|
47
|
+
const retry = new Set();
|
|
48
|
+
|
|
49
|
+
for (const check of affectedChecks) {
|
|
50
|
+
// Evidence failures → re-run the failing validation scripts
|
|
51
|
+
if (check === "evidence" || check === "result") {
|
|
52
|
+
if (scripts.test) retry.add("test");
|
|
53
|
+
if (scripts.lint) retry.add("lint");
|
|
54
|
+
if (scripts.build) retry.add("build");
|
|
55
|
+
if (scripts.typecheck) retry.add("typecheck");
|
|
56
|
+
}
|
|
57
|
+
// Specific check types
|
|
58
|
+
if (check.includes("test")) retry.add("test");
|
|
59
|
+
if (check.includes("lint")) retry.add("lint");
|
|
60
|
+
if (check.includes("build")) retry.add("build");
|
|
61
|
+
if (check.includes("typecheck")) retry.add("typecheck");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Filter to only scripts that actually exist
|
|
65
|
+
return [...retry].filter((name) => !!scripts[name]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* CLI Remediation Executor — default implementation for `ai-workflow run`.
|
|
70
|
+
*
|
|
71
|
+
* Plugs into HealerEngine as the `remediate` function. On each attempt it:
|
|
72
|
+
* 1. Reads the remediation-request.json to understand what failed
|
|
73
|
+
* 2. Re-runs the affected npm scripts (lint, test, build, typecheck)
|
|
74
|
+
* 3. Reports changed files and whether any script now passes
|
|
75
|
+
*
|
|
76
|
+
* Design constraints:
|
|
77
|
+
* - This executor does NOT modify source code — it only re-runs validation commands.
|
|
78
|
+
* - It is effective for transient failures (e.g. flaky tests, stale build artifacts).
|
|
79
|
+
* - For structural failures (missing tests, broken implementation), it will exhaust
|
|
80
|
+
* its attempt budget and return applied:false, triggering a BLOCKED status.
|
|
81
|
+
* - The user is shown clear output on each attempt.
|
|
82
|
+
*
|
|
83
|
+
* @param {Object} ctx - Context provided by HealerEngine.run()
|
|
84
|
+
* @param {number} ctx.attempt
|
|
85
|
+
* @param {number} ctx.maxAttempts
|
|
86
|
+
* @param {string} ctx.mode
|
|
87
|
+
* @param {string} ctx.taskSlug
|
|
88
|
+
* @param {Object} ctx.result - Current combined validation result
|
|
89
|
+
* @param {string[]} ctx.affectedChecks
|
|
90
|
+
* @param {string} ctx.requestPath - Path to remediation-request.json
|
|
91
|
+
* @param {string} cwd - Consumer project working directory (bound via closure)
|
|
92
|
+
* @returns {Promise<{applied: boolean, changedFiles?: string[], evidenceAdded?: string[], reason?: string}>}
|
|
93
|
+
*/
|
|
94
|
+
export function createCliRemediationExecutor(cwd) {
|
|
95
|
+
return async function remediationExecutor({ attempt, maxAttempts, mode, affectedChecks, requestPath }) {
|
|
96
|
+
console.log(`\n[HEALER] Attempt ${attempt}/${maxAttempts} — mode: ${mode}`);
|
|
97
|
+
console.log(`[HEALER] Affected: ${affectedChecks.join(", ")}`);
|
|
98
|
+
|
|
99
|
+
const scripts = await readScripts(cwd);
|
|
100
|
+
const filesBefore = getChangedFiles(cwd);
|
|
101
|
+
const toRetry = resolveRetryScripts(affectedChecks, scripts);
|
|
102
|
+
|
|
103
|
+
if (toRetry.length === 0) {
|
|
104
|
+
console.log("[HEALER] No retryable scripts found for the affected checks.");
|
|
105
|
+
console.log(`[HEALER] Review ${requestPath} for details.`);
|
|
106
|
+
return {
|
|
107
|
+
applied: false,
|
|
108
|
+
reason: `No npm scripts available to address: ${affectedChecks.join(", ")}. Manual remediation required.`
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log(`[HEALER] Re-running: ${toRetry.map((s) => `npm run ${s}`).join(", ")}`);
|
|
113
|
+
|
|
114
|
+
const results = [];
|
|
115
|
+
let anyPassed = false;
|
|
116
|
+
|
|
117
|
+
for (const scriptName of toRetry) {
|
|
118
|
+
process.stdout.write(` → npm run ${scriptName} ... `);
|
|
119
|
+
const result = spawnSync("npm", ["run", scriptName], {
|
|
120
|
+
cwd,
|
|
121
|
+
shell: true,
|
|
122
|
+
encoding: "utf8",
|
|
123
|
+
timeout: 120000
|
|
124
|
+
});
|
|
125
|
+
const passed = result.status === 0;
|
|
126
|
+
if (passed) anyPassed = true;
|
|
127
|
+
console.log(passed ? "PASS" : "FAIL");
|
|
128
|
+
results.push({ script: scriptName, passed, exitCode: result.status });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const filesAfter = getChangedFiles(cwd);
|
|
132
|
+
const changedFiles = filesAfter.filter((f) => !filesBefore.includes(f));
|
|
133
|
+
|
|
134
|
+
if (!anyPassed && changedFiles.length === 0) {
|
|
135
|
+
console.log("[HEALER] No scripts passed and no files changed — remediation had no effect.");
|
|
136
|
+
return {
|
|
137
|
+
applied: false,
|
|
138
|
+
reason: `Re-ran ${toRetry.join(", ")} — all failed. Structural fix required by the implementing agent.`
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log(`[HEALER] Applied: ${results.filter((r) => r.passed).map((r) => r.script).join(", ") || "none passed"}`);
|
|
143
|
+
if (changedFiles.length) console.log(`[HEALER] Changed files: ${changedFiles.join(", ")}`);
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
applied: true,
|
|
147
|
+
changedFiles,
|
|
148
|
+
evidenceAdded: anyPassed ? ["validation-output"] : []
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import { isRecoverableGateFailure } from "../statuses.js";
|
|
5
|
+
|
|
6
|
+
const MODE_ATTEMPTS = Object.freeze({ quick: 1, standard: 2, full: 3 });
|
|
7
|
+
|
|
8
|
+
function stableJson(value) {
|
|
9
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
10
|
+
if (value && typeof value === "object") {
|
|
11
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
|
|
12
|
+
}
|
|
13
|
+
return JSON.stringify(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function collectBlockingFindings(result = {}) {
|
|
17
|
+
const findings = [];
|
|
18
|
+
const add = (source, value) => {
|
|
19
|
+
if (!value || typeof value !== "object") return;
|
|
20
|
+
const status = value.status || value.overallStatus;
|
|
21
|
+
if (!["FAIL", "FAIL_QUALITY_GATE", "FAIL_DELEGATION_GATE", "BLOCKED"].includes(status)) return;
|
|
22
|
+
findings.push({
|
|
23
|
+
source,
|
|
24
|
+
status,
|
|
25
|
+
missing: Array.isArray(value.missing) ? [...value.missing].sort() : [],
|
|
26
|
+
reason: value.reason || value.summary || ""
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
add("result", result);
|
|
31
|
+
add("evidence", result.evidence);
|
|
32
|
+
add("quality", result.quality);
|
|
33
|
+
const checks = result.quality?.checks || result.policyValidation?.checks || result.checks || {};
|
|
34
|
+
for (const [name, check] of Object.entries(checks)) add(`check:${name}`, check);
|
|
35
|
+
return findings.sort((a, b) => a.source.localeCompare(b.source));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class HealerEngine {
|
|
39
|
+
constructor({ cwd, mode = "standard", taskSlug = "implementation", maxAttempts = null } = {}) {
|
|
40
|
+
this.cwd = cwd;
|
|
41
|
+
this.mode = MODE_ATTEMPTS[mode] ? mode : "standard";
|
|
42
|
+
this.taskSlug = taskSlug;
|
|
43
|
+
this.maxAttempts = maxAttempts ?? MODE_ATTEMPTS[this.mode];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get statePath() {
|
|
47
|
+
return path.join(this.cwd, ".ai-workflow", `healing-state-${this.taskSlug}.json`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fingerprint(result) {
|
|
51
|
+
return crypto.createHash("sha256").update(stableJson(collectBlockingFindings(result))).digest("hex");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
blockingCount(result) {
|
|
55
|
+
return collectBlockingFindings(result).length;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
affectedChecks(result) {
|
|
59
|
+
return collectBlockingFindings(result).map((finding) => finding.source);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
hasProgress(before, after, remediation = {}) {
|
|
63
|
+
if (!isRecoverableGateFailure(after.overallStatus)) return true;
|
|
64
|
+
const beforeCount = this.blockingCount(before);
|
|
65
|
+
const afterCount = this.blockingCount(after);
|
|
66
|
+
if (afterCount < beforeCount) return true;
|
|
67
|
+
if (this.fingerprint(before) !== this.fingerprint(after) && (remediation.changedFiles?.length || remediation.evidenceAdded?.length)) return true;
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async writeState(state) {
|
|
72
|
+
await fs.mkdir(path.dirname(this.statePath), { recursive: true });
|
|
73
|
+
await fs.writeFile(this.statePath, JSON.stringify(state, null, 2));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async resetState() {
|
|
77
|
+
try { await fs.unlink(this.statePath); } catch {}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async writeRemediationRequest({ attempt, result }) {
|
|
81
|
+
const requestPath = path.join(this.cwd, ".ai-workflow", "remediation-request.json");
|
|
82
|
+
await fs.mkdir(path.dirname(requestPath), { recursive: true });
|
|
83
|
+
const request = {
|
|
84
|
+
taskSlug: this.taskSlug,
|
|
85
|
+
executionMode: this.mode,
|
|
86
|
+
attempt,
|
|
87
|
+
maxAttempts: this.maxAttempts,
|
|
88
|
+
status: result.overallStatus,
|
|
89
|
+
affectedChecks: this.affectedChecks(result),
|
|
90
|
+
findingsFingerprint: this.fingerprint(result),
|
|
91
|
+
instruction: "Apply the smallest safe correction for the listed gates, then rerun validation. Do not broaden scope."
|
|
92
|
+
};
|
|
93
|
+
await fs.writeFile(requestPath, JSON.stringify(request, null, 2));
|
|
94
|
+
return requestPath;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async run({ initialResult, validate, remediate }) {
|
|
98
|
+
let current = initialResult;
|
|
99
|
+
const history = [];
|
|
100
|
+
const seen = new Set();
|
|
101
|
+
|
|
102
|
+
if (!isRecoverableGateFailure(current.overallStatus)) {
|
|
103
|
+
await this.resetState();
|
|
104
|
+
return { ...current, remediation: { status: "NOT_REQUIRED", attempts: 0, history } };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (let attempt = 1; attempt <= this.maxAttempts; attempt += 1) {
|
|
108
|
+
const fingerprint = this.fingerprint(current);
|
|
109
|
+
if (seen.has(fingerprint)) {
|
|
110
|
+
const blocked = { overallStatus: "BLOCKED", reason: "Repeated unresolved finding", previousResult: current };
|
|
111
|
+
await this.writeState({ status: "BLOCKED", reason: blocked.reason, attempts: attempt - 1, history });
|
|
112
|
+
return { ...blocked, remediation: { status: "BLOCKED", attempts: attempt - 1, history } };
|
|
113
|
+
}
|
|
114
|
+
seen.add(fingerprint);
|
|
115
|
+
|
|
116
|
+
const requestPath = await this.writeRemediationRequest({ attempt, result: current });
|
|
117
|
+
if (typeof remediate !== "function") {
|
|
118
|
+
const blocked = { overallStatus: "BLOCKED", reason: "No remediation executor available", previousResult: current, requestPath };
|
|
119
|
+
await this.writeState({ status: "BLOCKED", reason: blocked.reason, attempts: attempt - 1, history, requestPath });
|
|
120
|
+
return { ...blocked, remediation: { status: "BLOCKED", attempts: attempt - 1, history, requestPath } };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const remediation = await remediate({
|
|
124
|
+
attempt,
|
|
125
|
+
maxAttempts: this.maxAttempts,
|
|
126
|
+
mode: this.mode,
|
|
127
|
+
taskSlug: this.taskSlug,
|
|
128
|
+
result: current,
|
|
129
|
+
affectedChecks: this.affectedChecks(current),
|
|
130
|
+
requestPath
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
if (!remediation?.applied) {
|
|
134
|
+
const blocked = { overallStatus: "BLOCKED", reason: remediation?.reason || "Remediation was not applied", previousResult: current, requestPath };
|
|
135
|
+
history.push({ attempt, before: current.overallStatus, remediation, after: "NOT_RUN", progress: false });
|
|
136
|
+
await this.writeState({ status: "BLOCKED", reason: blocked.reason, attempts: attempt, history });
|
|
137
|
+
return { ...blocked, remediation: { status: "BLOCKED", attempts: attempt, history } };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const next = await validate({ affectedChecks: this.affectedChecks(current), attempt });
|
|
141
|
+
const progress = this.hasProgress(current, next, remediation);
|
|
142
|
+
history.push({
|
|
143
|
+
attempt,
|
|
144
|
+
before: current.overallStatus,
|
|
145
|
+
after: next.overallStatus,
|
|
146
|
+
progress,
|
|
147
|
+
changedFiles: remediation.changedFiles || [],
|
|
148
|
+
evidenceAdded: remediation.evidenceAdded || [],
|
|
149
|
+
affectedChecks: this.affectedChecks(current)
|
|
150
|
+
});
|
|
151
|
+
await this.writeState({ status: next.overallStatus, attempts: attempt, history });
|
|
152
|
+
|
|
153
|
+
if (!isRecoverableGateFailure(next.overallStatus)) {
|
|
154
|
+
await this.resetState();
|
|
155
|
+
return { ...next, remediation: { status: "RESOLVED", attempts: attempt, history } };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!progress) {
|
|
159
|
+
return {
|
|
160
|
+
overallStatus: "BLOCKED",
|
|
161
|
+
reason: "Remediation made no measurable progress",
|
|
162
|
+
previousResult: next,
|
|
163
|
+
remediation: { status: "BLOCKED", attempts: attempt, history }
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
current = next;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
overallStatus: "BLOCKED",
|
|
172
|
+
reason: `Quality remediation attempts exhausted for ${this.mode} mode`,
|
|
173
|
+
previousResult: current,
|
|
174
|
+
remediation: { status: "BLOCKED", attempts: this.maxAttempts, history }
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export { MODE_ATTEMPTS, collectBlockingFindings };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const AGENT_MAP = {
|
|
2
|
+
"atlas": "Atlas",
|
|
3
|
+
"nexus": "Nexus",
|
|
4
|
+
"orion": "Orion",
|
|
5
|
+
"astra": "Astra",
|
|
6
|
+
"sage": "Sage",
|
|
7
|
+
"phoenix": "Phoenix",
|
|
8
|
+
"architecture": "Architecture-Specialist",
|
|
9
|
+
"backend-development": "Backend-Engineer",
|
|
10
|
+
"deployment": "Deployment-Specialist",
|
|
11
|
+
"design-principles": "Design-Specialist",
|
|
12
|
+
"documentation": "Docs-Engineer",
|
|
13
|
+
"frontend-development": "Frontend-Engineer",
|
|
14
|
+
"full-stack-development": "Full-Stack-Engineer",
|
|
15
|
+
"optimize-tokens": "Token-Economist",
|
|
16
|
+
"pr-workflow": "PR-Manager",
|
|
17
|
+
"product-discovery": "Discovery-Analyst",
|
|
18
|
+
"product-planning": "Product-Planner",
|
|
19
|
+
"project-memory": "Memory-Guardian",
|
|
20
|
+
"prompt-engineer": "Prompt-Engineer",
|
|
21
|
+
"qa-workflow": "QA-Engineer",
|
|
22
|
+
"refactoring": "Refactoring-Specialist",
|
|
23
|
+
"release-workflow": "Release-Specialist",
|
|
24
|
+
"spec-driven-development": "SDD-Specialist",
|
|
25
|
+
"technical-leadership": "Technical-Leader",
|
|
26
|
+
"ui-ux-design": "UI-UX-Engineer"
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns the canonical (commercial) name for an agent given its internal slug.
|
|
31
|
+
* Falls back to PascalCase translation if no mapping exists.
|
|
32
|
+
* @param {string} slug
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
export function getCanonicalAgentName(slug) {
|
|
36
|
+
if (AGENT_MAP[slug]) return AGENT_MAP[slug];
|
|
37
|
+
|
|
38
|
+
// Fallback: convert slug-case to PascalCase
|
|
39
|
+
return slug
|
|
40
|
+
.split("-")
|
|
41
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
42
|
+
.join("");
|
|
43
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { getTemplateFiles } from "./templates.js";
|
|
3
3
|
|
|
4
|
-
export async function createInstallPlan({ cwd, exists, profile }) {
|
|
4
|
+
export async function createInstallPlan({ cwd, exists, profile = "standard" }) {
|
|
5
5
|
const actions = [];
|
|
6
6
|
const templateFiles = getTemplateFiles(profile);
|
|
7
7
|
const installRoot = ".ai-workflow";
|
|
@@ -22,10 +22,10 @@ export async function createInstallPlan({ cwd, exists, profile }) {
|
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const configPath = path.join(cwd, ".ai-workflow.json");
|
|
25
|
+
const configPath = path.join(cwd, ".ai-workflow/config.json");
|
|
26
26
|
actions.push({
|
|
27
27
|
type: (await exists(configPath)) ? "conflict" : "create",
|
|
28
|
-
relativePath: ".ai-workflow.json",
|
|
28
|
+
relativePath: ".ai-workflow/config.json",
|
|
29
29
|
absolutePath: configPath,
|
|
30
30
|
content: null
|
|
31
31
|
});
|