create-quiver 0.10.0 → 0.12.1
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/BACKLOG.md +16 -17
- package/CHANGELOG.md +78 -0
- package/README.md +208 -41
- package/README_FOR_AI.md +50 -24
- package/ROADMAP.md +34 -11
- package/docs/AI_CONTEXT.md.template +2 -0
- package/docs/AI_ONBOARDING_PROMPT.md.template +31 -18
- package/docs/COMMANDS.md.template +90 -16
- package/docs/CONTEXTO.md.template +2 -0
- package/docs/DECISIONS.md.template +1 -0
- package/docs/INDEX.md.template +20 -18
- package/docs/STATUS.md.template +6 -1
- package/docs/SUPPORT_MATRIX.md.template +2 -2
- package/docs/TROUBLESHOOTING.md.template +50 -0
- package/docs/WORKFLOW.md.template +27 -17
- package/package.json +27 -4
- package/package.template.json +13 -1
- package/scripts/init-docs.sh +11 -4
- package/scripts/package-quiver.sh +18 -2
- package/specs/quiver-v22-guided-ai-workflow/EVIDENCE_REPORT.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/EXECUTION_PLAN.md +88 -0
- package/specs/quiver-v22-guided-ai-workflow/SPEC.md +228 -0
- package/specs/quiver-v22-guided-ai-workflow/STATUS.md +42 -0
- package/specs/quiver-v22-guided-ai-workflow/pr.md +104 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/slice.json +51 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/slice.json +55 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/slice.json +57 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/slice.json +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/slice.json +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/slice.json +54 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/slice.json +57 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/slice.json +55 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/slice.json +53 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/EXECUTION_BRIEF.md +59 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/slice.json +59 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/slice.json +60 -0
- package/specs/quiver-v23-guided-flow-productization/EVIDENCE_REPORT.md +80 -0
- package/specs/quiver-v23-guided-flow-productization/EXECUTION_PLAN.md +80 -0
- package/specs/quiver-v23-guided-flow-productization/SPEC.md +203 -0
- package/specs/quiver-v23-guided-flow-productization/STATUS.md +39 -0
- package/specs/quiver-v23-guided-flow-productization/pr.md +119 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/slice.json +51 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/EXECUTION_BRIEF.md +35 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/slice.json +56 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/EXECUTION_BRIEF.md +29 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/slice.json +55 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/EXECUTION_BRIEF.md +29 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/slice.json +54 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/slice.json +59 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/EXECUTION_BRIEF.md +29 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/slice.json +53 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/slice.json +54 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/slice.json +55 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/slice.json +55 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/EXECUTION_BRIEF.md +34 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/slice.json +57 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +32 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/slice.json +63 -0
- package/specs/quiver-v24-dx-onboarding-hardening/EVIDENCE_REPORT.md +55 -0
- package/specs/quiver-v24-dx-onboarding-hardening/EXECUTION_PLAN.md +43 -0
- package/specs/quiver-v24-dx-onboarding-hardening/SPEC.md +149 -0
- package/specs/quiver-v24-dx-onboarding-hardening/STATUS.md +31 -0
- package/specs/quiver-v24-dx-onboarding-hardening/pr.md +76 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/slice.json +51 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/slice.json +55 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/slice.json +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/slice.json +53 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/slice.json +70 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/EXECUTION_BRIEF.md +49 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/slice.json +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/CLOSURE_BRIEF.md +43 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/slice.json +60 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/slice.json +51 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/slice.json +54 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/EXECUTION_BRIEF.md +51 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/slice.json +59 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/slice.json +76 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EVIDENCE_REPORT.md +293 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EXECUTION_PLAN.md +58 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/SPEC.md +242 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/STATUS.md +35 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/pr.md +77 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/slice.json +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/CLOSURE_BRIEF.md +43 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/slice.json +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/slice.json +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/slice.json +55 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/slice.json +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/slice.json +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/slice.json +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/EVIDENCE_REPORT.md +208 -0
- package/specs/quiver-v26-0121-smoke-hardening/EXECUTION_PLAN.md +57 -0
- package/specs/quiver-v26-0121-smoke-hardening/SPEC.md +137 -0
- package/specs/quiver-v26-0121-smoke-hardening/STATUS.md +32 -0
- package/specs/quiver-v26-0121-smoke-hardening/pr.md +96 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/slice.json +73 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/EXECUTION_BRIEF.md +51 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/slice.json +76 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/slice.json +75 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/slice.json +77 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/slice.json +77 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/slice.json +84 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/slice.json +82 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/slice.json +92 -0
- package/src/create-quiver/commands/ai.js +1060 -37
- package/src/create-quiver/commands/demo.js +22 -0
- package/src/create-quiver/commands/evidence.js +37 -0
- package/src/create-quiver/commands/flow.js +562 -0
- package/src/create-quiver/commands/graph.js +19 -4
- package/src/create-quiver/commands/next.js +28 -0
- package/src/create-quiver/commands/plan.js +9 -6
- package/src/create-quiver/commands/prepare.js +236 -0
- package/src/create-quiver/commands/spec.js +133 -0
- package/src/create-quiver/index.js +1010 -31
- package/src/create-quiver/lib/actionable-error.js +27 -0
- package/src/create-quiver/lib/agent-profiles.js +148 -0
- package/src/create-quiver/lib/ai/context-packs.js +16 -0
- package/src/create-quiver/lib/ai/execution-plan.js +377 -11
- package/src/create-quiver/lib/ai/executor.js +633 -24
- package/src/create-quiver/lib/ai/export-state.js +534 -0
- package/src/create-quiver/lib/ai/github.js +279 -0
- package/src/create-quiver/lib/ai/onboarding-template.js +578 -0
- package/src/create-quiver/lib/ai/plan-review.js +286 -0
- package/src/create-quiver/lib/ai/providers.js +5 -3
- package/src/create-quiver/lib/ai/run-state.js +414 -0
- package/src/create-quiver/lib/ai/safety.js +5 -0
- package/src/create-quiver/lib/ai/spec-generator.js +12 -0
- package/src/create-quiver/lib/ai/spec-templates.js +80 -11
- package/src/create-quiver/lib/approvals.js +369 -0
- package/src/create-quiver/lib/demo.js +832 -0
- package/src/create-quiver/lib/doctor.js +309 -0
- package/src/create-quiver/lib/evidence.js +115 -0
- package/src/create-quiver/lib/handoff.js +81 -12
- package/src/create-quiver/lib/init-docs.js +302 -17
- package/src/create-quiver/lib/init-layout.js +34 -1
- package/src/create-quiver/lib/json.js +53 -3
- package/src/create-quiver/lib/lifecycle.js +6 -0
- package/src/create-quiver/lib/package-safety.js +117 -0
- package/src/create-quiver/lib/readiness.js +103 -21
- package/src/create-quiver/lib/scope.js +50 -7
- package/src/create-quiver/lib/slice-graph.js +138 -37
- package/src/create-quiver/lib/slice.js +14 -9
- package/src/create-quiver/lib/spec-worktrees.js +363 -0
|
@@ -1,11 +1,56 @@
|
|
|
1
1
|
const fs = require('node:fs');
|
|
2
2
|
const path = require('node:path');
|
|
3
3
|
|
|
4
|
+
const { redactSecrets } = require('../lib/evidence');
|
|
5
|
+
const { formatActionableError } = require('../lib/actionable-error');
|
|
4
6
|
const { buildContextPackMetadata, normalizeRole } = require('../lib/ai/context-packs');
|
|
5
|
-
const { runExecuteSlice } = require('../lib/ai/executor');
|
|
6
|
-
const {
|
|
7
|
+
const { runExecuteSlice, runPromptSlice } = require('../lib/ai/executor');
|
|
8
|
+
const { runExecutePlan } = require('../lib/ai/execution-plan');
|
|
9
|
+
const { buildPrCreatePlan, formatPreflightReport, formatPrCreateReport, preflightGitHubPr, runGhPrCreate } = require('../lib/ai/github');
|
|
10
|
+
const { buildContextPreparationDrafts, buildPlannerOnboardingPrompt } = require('../lib/ai/onboarding-template');
|
|
11
|
+
const {
|
|
12
|
+
collectLifecycleExport,
|
|
13
|
+
formatLifecycleExportMarkdown,
|
|
14
|
+
formatLifecycleInspect,
|
|
15
|
+
formatSlicesList,
|
|
16
|
+
formatSpecsList,
|
|
17
|
+
formatTraceReport,
|
|
18
|
+
} = require('../lib/ai/export-state');
|
|
19
|
+
const {
|
|
20
|
+
PLAN_REVIEW_PROMPT_SOURCE,
|
|
21
|
+
buildPlanReviewPrompt,
|
|
22
|
+
readPlanReview,
|
|
23
|
+
resolveReviewedTechnicalPlanInput,
|
|
24
|
+
resolveTechnicalPlanReviewInput,
|
|
25
|
+
savePlanReview,
|
|
26
|
+
summarizePlanReview,
|
|
27
|
+
} = require('../lib/ai/plan-review');
|
|
7
28
|
const { buildSpecGenerationManifest, describeSpecGeneration, generateSpecArtifacts } = require('../lib/ai/spec-generator');
|
|
8
29
|
const { buildProviderInvocation, runProvider } = require('../lib/ai/providers');
|
|
30
|
+
const {
|
|
31
|
+
createAiRun,
|
|
32
|
+
ensureAiRun,
|
|
33
|
+
formatAiRunResume,
|
|
34
|
+
formatAiRunStatus,
|
|
35
|
+
recordAiRunApproval,
|
|
36
|
+
resolveAiRun,
|
|
37
|
+
updateAiRunPhase,
|
|
38
|
+
} = require('../lib/ai/run-state');
|
|
39
|
+
const {
|
|
40
|
+
agentProfilesPath,
|
|
41
|
+
getAgentProfile,
|
|
42
|
+
listAgentProfiles,
|
|
43
|
+
resolveProfileProvider,
|
|
44
|
+
setAgentProfile,
|
|
45
|
+
} = require('../lib/agent-profiles');
|
|
46
|
+
const {
|
|
47
|
+
PLANNER_APPROVAL_PHASES,
|
|
48
|
+
approvePlannerPhase,
|
|
49
|
+
readPhaseApproval,
|
|
50
|
+
resolveApprovedPlannerInput,
|
|
51
|
+
savePlannerDraft,
|
|
52
|
+
summarizePlannerApproval,
|
|
53
|
+
} = require('../lib/approvals');
|
|
9
54
|
const { assertPlannerPhaseReady, getPlannerPhaseDetails, normalizePlannerPhase, PlannerPhaseError } = require('../lib/ai/phase-gates');
|
|
10
55
|
|
|
11
56
|
const DEFAULT_ONBOARD_PROVIDER = 'codex';
|
|
@@ -15,6 +60,8 @@ const DEFAULT_PLAN_PROVIDER = 'codex';
|
|
|
15
60
|
const DEFAULT_PLAN_ROLE = 'planner';
|
|
16
61
|
const DEFAULT_PLAN_CONTEXT = 'planning';
|
|
17
62
|
const DEFAULT_PLAN_PHASE = 'acceptance';
|
|
63
|
+
const CONTEXT_PREP_START = '<!-- quiver:context-prep:start -->';
|
|
64
|
+
const CONTEXT_PREP_END = '<!-- quiver:context-prep:end -->';
|
|
18
65
|
|
|
19
66
|
function formatError(message) {
|
|
20
67
|
return `create-quiver: ${message}`;
|
|
@@ -33,6 +80,14 @@ function readTextFile(filePath, repoRoot) {
|
|
|
33
80
|
return fs.readFileSync(resolved, 'utf8');
|
|
34
81
|
}
|
|
35
82
|
|
|
83
|
+
function readTextFileOrEmpty(filePath, repoRoot) {
|
|
84
|
+
if (!filePath) {
|
|
85
|
+
return '';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return readTextFile(filePath, repoRoot);
|
|
89
|
+
}
|
|
90
|
+
|
|
36
91
|
function normalizeTimeout(timeoutMs) {
|
|
37
92
|
if (timeoutMs === undefined || timeoutMs === null || timeoutMs === '') {
|
|
38
93
|
return undefined;
|
|
@@ -46,7 +101,14 @@ function normalizeTimeout(timeoutMs) {
|
|
|
46
101
|
return parsed;
|
|
47
102
|
}
|
|
48
103
|
|
|
49
|
-
function
|
|
104
|
+
function resolveProviderForProfile(repoRoot, role, provider, providerExplicit, fallbackProvider) {
|
|
105
|
+
if (providerExplicit === true || (provider && providerExplicit !== false)) {
|
|
106
|
+
return String(provider || fallbackProvider).trim().toLowerCase();
|
|
107
|
+
}
|
|
108
|
+
return resolveProfileProvider(repoRoot, role, fallbackProvider);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildPlanContext({ role, context, phase, inputText, inputPath, repoRoot, revise = false }) {
|
|
50
112
|
const phaseDetails = getPlannerPhaseDetails(phase);
|
|
51
113
|
const pack = buildContextPackMetadata({
|
|
52
114
|
role,
|
|
@@ -57,7 +119,9 @@ function buildPlanContext({ role, context, phase, inputText, inputPath, repoRoot
|
|
|
57
119
|
const sections = [
|
|
58
120
|
pack.prompt,
|
|
59
121
|
`Phase: ${phaseDetails.phase}`,
|
|
60
|
-
|
|
122
|
+
revise
|
|
123
|
+
? 'Task: revise the current draft and produce a new version only. Do not advance phase, approve, create specs, or modify product code.'
|
|
124
|
+
: phaseDetails.phase === 'acceptance'
|
|
61
125
|
? 'Task: produce acceptance criteria only. Do not create files or modify product code.'
|
|
62
126
|
: 'Task: produce a technical plan only. Do not create files or modify product code.',
|
|
63
127
|
];
|
|
@@ -88,34 +152,49 @@ function buildOnboardContext({ role, context, inputText, inputPath, repoRoot })
|
|
|
88
152
|
repoRoot,
|
|
89
153
|
});
|
|
90
154
|
const relativeInputPath = inputPath ? path.relative(repoRoot, path.resolve(repoRoot, inputPath)).split(path.sep).join('/') : '';
|
|
91
|
-
const
|
|
92
|
-
pack
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
155
|
+
const built = buildPlannerOnboardingPrompt({
|
|
156
|
+
pack,
|
|
157
|
+
inputText,
|
|
158
|
+
inputPath: relativeInputPath,
|
|
159
|
+
repoRoot,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
pack,
|
|
164
|
+
plan: built.plan,
|
|
165
|
+
prompt: built.prompt,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function formatDryRunReport({ task, provider, role, contextPack, phase, invocation, onboardingPlan }) {
|
|
170
|
+
const lines = [
|
|
171
|
+
`AI ${task} dry-run`,
|
|
172
|
+
`Provider: ${provider}`,
|
|
173
|
+
`Role: ${role}`,
|
|
174
|
+
`Context pack: ${contextPack}`,
|
|
96
175
|
];
|
|
97
176
|
|
|
98
|
-
if (
|
|
99
|
-
|
|
177
|
+
if (phase) {
|
|
178
|
+
lines.push(`Phase: ${phase}`);
|
|
100
179
|
}
|
|
101
180
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
181
|
+
lines.push(`Command: ${invocation.command} ${invocation.args.join(' ')}`);
|
|
182
|
+
lines.push(`Timeout: ${invocation.timeoutMs}ms`);
|
|
183
|
+
lines.push(`Prompt transport: ${invocation.promptTransport.mode}`);
|
|
184
|
+
lines.push(`Prompt length: ${invocation.promptLength} bytes`);
|
|
105
185
|
|
|
106
|
-
if (
|
|
107
|
-
|
|
186
|
+
if (onboardingPlan) {
|
|
187
|
+
lines.push(`Prompt source: ${onboardingPlan.promptSource}`);
|
|
188
|
+
lines.push(`Selected docs: ${onboardingPlan.selectedDocs.length}`);
|
|
189
|
+
lines.push(`Documentation debt: ${onboardingPlan.missingDocs.length}`);
|
|
108
190
|
}
|
|
109
191
|
|
|
110
|
-
return {
|
|
111
|
-
pack,
|
|
112
|
-
prompt: sections.join('\n\n'),
|
|
113
|
-
};
|
|
192
|
+
return `${lines.join('\n')}\n`;
|
|
114
193
|
}
|
|
115
194
|
|
|
116
|
-
function
|
|
195
|
+
function formatPromptOnlyReport({ task, provider, role, contextPack, phase, invocation, prompt, onboardingPlan, promptSource, inputPath, inputKind, inputVersion }) {
|
|
117
196
|
const lines = [
|
|
118
|
-
`AI ${task}
|
|
197
|
+
`AI ${task} prompt-only`,
|
|
119
198
|
`Provider: ${provider}`,
|
|
120
199
|
`Role: ${role}`,
|
|
121
200
|
`Context pack: ${contextPack}`,
|
|
@@ -130,16 +209,276 @@ function formatDryRunReport({ task, provider, role, contextPack, phase, invocati
|
|
|
130
209
|
lines.push(`Prompt transport: ${invocation.promptTransport.mode}`);
|
|
131
210
|
lines.push(`Prompt length: ${invocation.promptLength} bytes`);
|
|
132
211
|
|
|
212
|
+
if (onboardingPlan) {
|
|
213
|
+
lines.push(`Prompt source: ${onboardingPlan.promptSource}`);
|
|
214
|
+
lines.push(`Selected docs: ${onboardingPlan.selectedDocs.length}`);
|
|
215
|
+
lines.push(`Documentation debt: ${onboardingPlan.missingDocs.length}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (promptSource) {
|
|
219
|
+
lines.push(`Prompt source: ${promptSource}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (inputPath) {
|
|
223
|
+
lines.push(`Input file: ${inputPath}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (inputKind) {
|
|
227
|
+
lines.push(`Input kind: ${inputKind}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (inputVersion) {
|
|
231
|
+
lines.push(`Input version: v${inputVersion}`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
lines.push('--- PROMPT START ---');
|
|
235
|
+
lines.push(String(prompt || '').trimEnd());
|
|
236
|
+
lines.push('--- PROMPT END ---');
|
|
237
|
+
|
|
238
|
+
return `${lines.join('\n')}\n`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function formatPathList(items, emptyLabel = 'none') {
|
|
242
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
243
|
+
return [`- ${emptyLabel}`];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return items.map((item) => `- ${item}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function formatContextPreparationReport({ dryRun, plan, writePlan, writtenDocs, snapshot, completed = false }) {
|
|
250
|
+
const lines = [
|
|
251
|
+
dryRun ? 'AI prepare-context dry-run' : completed ? 'AI prepare-context completed' : 'AI prepare-context write plan',
|
|
252
|
+
`Mode: ${dryRun ? 'dry-run' : 'live'}`,
|
|
253
|
+
`Project: ${plan.projectName}`,
|
|
254
|
+
`Project slug: ${plan.projectSlug}`,
|
|
255
|
+
'Writes: docs-only',
|
|
256
|
+
'Product code: untouched',
|
|
257
|
+
`Proposed docs: ${writePlan.length > 0 ? writePlan.map((item) => item.path).join(', ') : 'none'}`,
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
if (!dryRun) {
|
|
261
|
+
lines.push(`${completed ? 'Written docs' : 'Planned writes'}: ${writtenDocs.length > 0 ? writtenDocs.join(', ') : 'none'}`);
|
|
262
|
+
if (snapshot) {
|
|
263
|
+
lines.push(`Snapshot: ${snapshot.root}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (completed) {
|
|
268
|
+
return `${lines.join('\n')}\n`;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
lines.push(
|
|
272
|
+
'Proposed changes:',
|
|
273
|
+
...writePlan.map((item) => `- ${item.path}: ${item.action}${item.reason ? ` (${item.reason})` : ''}`),
|
|
274
|
+
'Diff preview:',
|
|
275
|
+
...formatDiffPreview(writePlan),
|
|
276
|
+
'Files considered:',
|
|
277
|
+
...plan.filesConsidered.map((item) => `- ${item.path}: ${item.present ? 'present' : 'absent'}${item.reason ? ` (${item.reason})` : ''}`),
|
|
278
|
+
'Assumptions:',
|
|
279
|
+
...formatPathList(plan.assumptions),
|
|
280
|
+
'Risks:',
|
|
281
|
+
...formatPathList(plan.risks),
|
|
282
|
+
'Contradictions:',
|
|
283
|
+
...formatPathList(plan.contradictions),
|
|
284
|
+
'Omitted paths:',
|
|
285
|
+
...formatPathList(plan.omittedPaths),
|
|
286
|
+
'Uncertainty markers: TODO | Assumption | Pending confirmation',
|
|
287
|
+
);
|
|
288
|
+
|
|
133
289
|
return `${lines.join('\n')}\n`;
|
|
134
290
|
}
|
|
135
291
|
|
|
136
292
|
function writeProviderOutput(result) {
|
|
137
293
|
if (result.stdout) {
|
|
138
|
-
process.stdout.write(result.stdout);
|
|
294
|
+
process.stdout.write(redactSecrets(result.stdout));
|
|
139
295
|
}
|
|
140
296
|
if (result.stderr) {
|
|
141
|
-
process.stderr.write(result.stderr);
|
|
297
|
+
process.stderr.write(redactSecrets(result.stderr));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function getRedactedProviderText(result) {
|
|
302
|
+
return redactSecrets([result.stdout, result.stderr].filter(Boolean).join(''));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function normalizeText(value) {
|
|
306
|
+
return String(value || '').replace(/\r\n/g, '\n');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function buildRevisionInput({ phase, feedbackPath, feedbackText, repoRoot }) {
|
|
310
|
+
const current = readPhaseApproval(repoRoot, phase);
|
|
311
|
+
if (!current.draft) {
|
|
312
|
+
throw new Error(formatError(`ai revise --phase ${phase} requires an existing draft; current status is ${current.status}. Run \`npx create-quiver ai plan --phase ${phase} --input <file>\` first.`));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const sections = [];
|
|
316
|
+
|
|
317
|
+
if (phase === 'technical-plan') {
|
|
318
|
+
const acceptance = resolveApprovedPlannerInput(repoRoot, phase, undefined);
|
|
319
|
+
const acceptanceText = readTextFile(acceptance.inputPath, repoRoot);
|
|
320
|
+
sections.push(`Approved acceptance input (${acceptance.inputPath}):`, acceptanceText.trimEnd());
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
sections.push(
|
|
324
|
+
`Current ${phase} draft (${current.draft.path}):`,
|
|
325
|
+
current.draft.contents.trimEnd(),
|
|
326
|
+
`Human feedback (${feedbackPath}):`,
|
|
327
|
+
feedbackText.trimEnd(),
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
return sections.join('\n\n');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function buildManagedContextBlock(content) {
|
|
334
|
+
return `${CONTEXT_PREP_START}\n${String(content || '').trimEnd()}\n${CONTEXT_PREP_END}\n`;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function mergeContextDraft(existingContent, draftContent) {
|
|
338
|
+
const existing = normalizeText(existingContent);
|
|
339
|
+
const block = buildManagedContextBlock(draftContent);
|
|
340
|
+
const startIndex = existing.indexOf(CONTEXT_PREP_START);
|
|
341
|
+
const endIndex = existing.indexOf(CONTEXT_PREP_END);
|
|
342
|
+
|
|
343
|
+
if (startIndex >= 0 && endIndex > startIndex) {
|
|
344
|
+
const before = existing.slice(0, startIndex).trimEnd();
|
|
345
|
+
const after = existing.slice(endIndex + CONTEXT_PREP_END.length).trimStart();
|
|
346
|
+
return `${before}\n\n${block}${after ? `\n${after}` : ''}`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return `${existing.trimEnd()}\n\n${block}`;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function firstChangedLineIndex(beforeLines, afterLines) {
|
|
353
|
+
const max = Math.max(beforeLines.length, afterLines.length);
|
|
354
|
+
for (let index = 0; index < max; index += 1) {
|
|
355
|
+
if (beforeLines[index] !== afterLines[index]) {
|
|
356
|
+
return index;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return -1;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function buildDiffSnippet(pathLabel, beforeContent, afterContent, maxLines = 10) {
|
|
363
|
+
const beforeLines = normalizeText(beforeContent).split('\n');
|
|
364
|
+
const afterLines = normalizeText(afterContent).split('\n');
|
|
365
|
+
const changedAt = firstChangedLineIndex(beforeLines, afterLines);
|
|
366
|
+
|
|
367
|
+
if (changedAt === -1) {
|
|
368
|
+
return [`diff -- ${pathLabel}`, ' no changes'];
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const start = Math.max(0, changedAt - 2);
|
|
372
|
+
const beforeSnippet = beforeLines.slice(start, start + maxLines);
|
|
373
|
+
const afterSnippet = afterLines.slice(start, start + maxLines);
|
|
374
|
+
const lines = [
|
|
375
|
+
`--- ${pathLabel} (current)`,
|
|
376
|
+
`+++ ${pathLabel} (proposed)`,
|
|
377
|
+
];
|
|
378
|
+
|
|
379
|
+
for (const line of beforeSnippet) {
|
|
380
|
+
if (line) {
|
|
381
|
+
lines.push(`- ${line}`);
|
|
382
|
+
}
|
|
142
383
|
}
|
|
384
|
+
|
|
385
|
+
for (const line of afterSnippet) {
|
|
386
|
+
if (line) {
|
|
387
|
+
lines.push(`+ ${line}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return lines;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function buildContextWritePlan(repoRoot, drafts) {
|
|
395
|
+
return drafts.map((draft) => {
|
|
396
|
+
const destinationPath = path.join(repoRoot, draft.path);
|
|
397
|
+
const exists = fs.existsSync(destinationPath);
|
|
398
|
+
const currentContent = exists ? fs.readFileSync(destinationPath, 'utf8') : '';
|
|
399
|
+
const proposedContent = exists
|
|
400
|
+
? mergeContextDraft(currentContent, draft.content)
|
|
401
|
+
: `${String(draft.content || '').replace(/\s+$/g, '')}\n`;
|
|
402
|
+
const changed = normalizeText(currentContent) !== normalizeText(proposedContent);
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
path: draft.path,
|
|
406
|
+
destinationPath,
|
|
407
|
+
action: changed ? (exists ? 'update' : 'create') : 'skip',
|
|
408
|
+
reason: changed ? (exists ? 'human content preserved; Quiver block appended or refreshed' : 'missing approved context doc') : 'already up to date',
|
|
409
|
+
exists,
|
|
410
|
+
currentContent,
|
|
411
|
+
proposedContent,
|
|
412
|
+
diff: buildDiffSnippet(draft.path, currentContent, proposedContent),
|
|
413
|
+
};
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function formatDiffPreview(writePlan) {
|
|
418
|
+
const lines = [];
|
|
419
|
+
for (const item of writePlan) {
|
|
420
|
+
if (item.action === 'skip') {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
lines.push(...item.diff);
|
|
424
|
+
}
|
|
425
|
+
return lines.length > 0 ? lines : ['- no changes'];
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function createContextSnapshots(repoRoot, run, writePlan, now = new Date()) {
|
|
429
|
+
const stamp = now.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z');
|
|
430
|
+
const snapshotRoot = path.join(repoRoot, '.quiver', 'runs', run.run_id, 'snapshots', stamp);
|
|
431
|
+
const manifest = {
|
|
432
|
+
schema_version: 1,
|
|
433
|
+
run_id: run.run_id,
|
|
434
|
+
created_at: now.toISOString(),
|
|
435
|
+
entries: [],
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
fs.mkdirSync(snapshotRoot, { recursive: true });
|
|
439
|
+
|
|
440
|
+
for (const item of writePlan) {
|
|
441
|
+
if (item.action === 'skip') {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
const entry = {
|
|
445
|
+
path: item.path,
|
|
446
|
+
action: item.action,
|
|
447
|
+
existed: item.exists,
|
|
448
|
+
snapshot_path: null,
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
if (item.exists) {
|
|
452
|
+
const snapshotPath = path.join(snapshotRoot, item.path);
|
|
453
|
+
fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });
|
|
454
|
+
fs.copyFileSync(item.destinationPath, snapshotPath);
|
|
455
|
+
entry.snapshot_path = path.relative(repoRoot, snapshotPath).split(path.sep).join('/');
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
manifest.entries.push(entry);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const manifestPath = path.join(snapshotRoot, 'manifest.json');
|
|
462
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
463
|
+
|
|
464
|
+
return {
|
|
465
|
+
root: path.relative(repoRoot, snapshotRoot).split(path.sep).join('/'),
|
|
466
|
+
manifestPath: path.relative(repoRoot, manifestPath).split(path.sep).join('/'),
|
|
467
|
+
entries: manifest.entries,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function writeDraftDocs(writePlan) {
|
|
472
|
+
const writtenDocs = [];
|
|
473
|
+
for (const item of writePlan) {
|
|
474
|
+
if (item.action === 'skip') {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
fs.mkdirSync(path.dirname(item.destinationPath), { recursive: true });
|
|
478
|
+
fs.writeFileSync(item.destinationPath, item.proposedContent);
|
|
479
|
+
writtenDocs.push(item.path);
|
|
480
|
+
}
|
|
481
|
+
return writtenDocs;
|
|
143
482
|
}
|
|
144
483
|
|
|
145
484
|
function formatSpecDryRunReport({ manifest, repoRoot }) {
|
|
@@ -178,6 +517,43 @@ function formatSpecGenerationResult(result, repoRoot) {
|
|
|
178
517
|
return `${lines.join('\n')}\n`;
|
|
179
518
|
}
|
|
180
519
|
|
|
520
|
+
function formatApprovalResult(result, repoRoot) {
|
|
521
|
+
const relativePath = path.relative(repoRoot, result.filePath).split(path.sep).join('/');
|
|
522
|
+
const lines = [
|
|
523
|
+
'AI approval saved',
|
|
524
|
+
`Phase: ${result.phase}`,
|
|
525
|
+
`Status: approved`,
|
|
526
|
+
`Artifact: ${relativePath}`,
|
|
527
|
+
`Source file: ${result.sourceFile}`,
|
|
528
|
+
`Timestamp: ${result.createdAt}`,
|
|
529
|
+
];
|
|
530
|
+
if (result.version) {
|
|
531
|
+
lines.push(`Version: v${result.version}`);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return `${lines.join('\n')}\n`;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function formatApprovalDryRunResult({ phase, input, version }) {
|
|
538
|
+
const lines = ['AI approval dry-run', `Phase: ${phase}`];
|
|
539
|
+
if (version) {
|
|
540
|
+
lines.push(`Version: v${version}`);
|
|
541
|
+
}
|
|
542
|
+
if (input) {
|
|
543
|
+
lines.push(`Input file: ${input}`);
|
|
544
|
+
}
|
|
545
|
+
return `${lines.join('\n')}\n`;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function formatApprovalStatusReport(repoRoot) {
|
|
549
|
+
const sections = ['AI approvals status'];
|
|
550
|
+
for (const phase of PLANNER_APPROVAL_PHASES) {
|
|
551
|
+
sections.push(summarizePlannerApproval(repoRoot, phase).trimEnd());
|
|
552
|
+
}
|
|
553
|
+
sections.push(summarizePlanReview(repoRoot).trimEnd());
|
|
554
|
+
return `${sections.join('\n\n')}\n`;
|
|
555
|
+
}
|
|
556
|
+
|
|
181
557
|
function annotateProviderError(error, scope, phase) {
|
|
182
558
|
const phaseLabel = phase ? ` phase '${phase}'` : '';
|
|
183
559
|
const message = error && error.message ? error.message : String(error);
|
|
@@ -198,12 +574,13 @@ function annotateGitHubError(error, scope) {
|
|
|
198
574
|
}
|
|
199
575
|
|
|
200
576
|
async function runOnboard(repoRoot, options = {}) {
|
|
201
|
-
const provider = String(options.provider || DEFAULT_ONBOARD_PROVIDER).trim().toLowerCase();
|
|
202
577
|
const role = normalizeRole(options.role || DEFAULT_ONBOARD_ROLE);
|
|
578
|
+
const provider = resolveProviderForProfile(repoRoot, role, options.provider, options.providerExplicit, DEFAULT_ONBOARD_PROVIDER);
|
|
203
579
|
const context = options.context || DEFAULT_ONBOARD_CONTEXT;
|
|
204
580
|
const timeoutMs = normalizeTimeout(options.timeout);
|
|
205
581
|
const inputText = readTextFile(options.input, repoRoot);
|
|
206
|
-
const
|
|
582
|
+
const contextInfo = buildOnboardContext({ role, context, inputText, inputPath: options.input, repoRoot });
|
|
583
|
+
const prompt = contextInfo.prompt;
|
|
207
584
|
let invocation;
|
|
208
585
|
|
|
209
586
|
try {
|
|
@@ -223,11 +600,26 @@ async function runOnboard(repoRoot, options = {}) {
|
|
|
223
600
|
role,
|
|
224
601
|
contextPack: context,
|
|
225
602
|
invocation,
|
|
603
|
+
onboardingPlan: contextInfo.plan,
|
|
226
604
|
};
|
|
227
605
|
process.stdout.write(formatDryRunReport(report));
|
|
228
606
|
return report;
|
|
229
607
|
}
|
|
230
608
|
|
|
609
|
+
if (options.printPrompt) {
|
|
610
|
+
const report = {
|
|
611
|
+
task: 'onboard',
|
|
612
|
+
provider,
|
|
613
|
+
role,
|
|
614
|
+
contextPack: context,
|
|
615
|
+
invocation,
|
|
616
|
+
onboardingPlan: contextInfo.plan,
|
|
617
|
+
prompt,
|
|
618
|
+
};
|
|
619
|
+
process.stdout.write(formatPromptOnlyReport(report));
|
|
620
|
+
return report;
|
|
621
|
+
}
|
|
622
|
+
|
|
231
623
|
let result;
|
|
232
624
|
try {
|
|
233
625
|
result = await (options.runProviderFn || runProvider)(provider, {
|
|
@@ -257,30 +649,103 @@ async function runOnboard(repoRoot, options = {}) {
|
|
|
257
649
|
role,
|
|
258
650
|
contextPack: context,
|
|
259
651
|
invocation,
|
|
652
|
+
onboardingPlan: contextInfo.plan,
|
|
260
653
|
result,
|
|
261
654
|
};
|
|
262
655
|
}
|
|
263
656
|
|
|
657
|
+
async function runPrepareContext(repoRoot, options = {}) {
|
|
658
|
+
const draftPack = buildContextPreparationDrafts(repoRoot);
|
|
659
|
+
const writePlan = buildContextWritePlan(repoRoot, draftPack.docs);
|
|
660
|
+
const report = {
|
|
661
|
+
task: 'prepare-context',
|
|
662
|
+
dryRun: options.dryRun === true,
|
|
663
|
+
docs: draftPack.docs.map((doc) => doc.path),
|
|
664
|
+
plan: draftPack.plan,
|
|
665
|
+
writePlan: writePlan.map((item) => ({
|
|
666
|
+
path: item.path,
|
|
667
|
+
action: item.action,
|
|
668
|
+
reason: item.reason,
|
|
669
|
+
})),
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
if (options.dryRun) {
|
|
673
|
+
process.stdout.write(formatContextPreparationReport({
|
|
674
|
+
dryRun: true,
|
|
675
|
+
plan: draftPack.plan,
|
|
676
|
+
writePlan,
|
|
677
|
+
writtenDocs: [],
|
|
678
|
+
}));
|
|
679
|
+
return report;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const lifecycleRun = ensureAiRun(repoRoot, {
|
|
683
|
+
command: 'ai prepare-context',
|
|
684
|
+
input: options.input || '',
|
|
685
|
+
runId: options.runId,
|
|
686
|
+
phase: 'created',
|
|
687
|
+
});
|
|
688
|
+
const snapshot = createContextSnapshots(repoRoot, lifecycleRun, writePlan, options.now || new Date());
|
|
689
|
+
const plannedDocs = writePlan.filter((item) => item.action !== 'skip').map((item) => item.path);
|
|
690
|
+
process.stdout.write(formatContextPreparationReport({
|
|
691
|
+
dryRun: false,
|
|
692
|
+
plan: draftPack.plan,
|
|
693
|
+
writePlan,
|
|
694
|
+
writtenDocs: plannedDocs,
|
|
695
|
+
snapshot,
|
|
696
|
+
}));
|
|
697
|
+
const writtenDocs = writeDraftDocs(writePlan);
|
|
698
|
+
updateAiRunPhase(repoRoot, lifecycleRun.run_id, 'onboarding-ready', {
|
|
699
|
+
artifact: snapshot.manifestPath,
|
|
700
|
+
command: 'ai prepare-context',
|
|
701
|
+
});
|
|
702
|
+
process.stdout.write(formatContextPreparationReport({
|
|
703
|
+
dryRun: false,
|
|
704
|
+
plan: draftPack.plan,
|
|
705
|
+
writePlan,
|
|
706
|
+
writtenDocs,
|
|
707
|
+
snapshot,
|
|
708
|
+
completed: true,
|
|
709
|
+
}));
|
|
710
|
+
|
|
711
|
+
return {
|
|
712
|
+
...report,
|
|
713
|
+
runId: lifecycleRun.run_id,
|
|
714
|
+
snapshot,
|
|
715
|
+
writtenDocs,
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
|
|
264
719
|
async function runPlan(repoRoot, options = {}) {
|
|
265
720
|
const phase = normalizePlannerPhase(options.phase || DEFAULT_PLAN_PHASE);
|
|
266
721
|
const role = normalizeRole(options.role || DEFAULT_PLAN_ROLE);
|
|
267
|
-
const provider =
|
|
722
|
+
const provider = resolveProviderForProfile(repoRoot, role, options.provider, options.providerExplicit, DEFAULT_PLAN_PROVIDER);
|
|
268
723
|
const context = options.context || DEFAULT_PLAN_CONTEXT;
|
|
269
724
|
const timeoutMs = normalizeTimeout(options.timeout);
|
|
270
|
-
|
|
271
|
-
if (!options.input) {
|
|
272
|
-
throw new Error(formatError(`missing input file for ai plan phase '${phase}'`));
|
|
273
|
-
}
|
|
725
|
+
let inputPath = options.input || '';
|
|
274
726
|
|
|
275
727
|
if (phase === 'spec') {
|
|
276
|
-
const
|
|
728
|
+
const resolved = resolveReviewedTechnicalPlanInput(repoRoot, inputPath || undefined);
|
|
729
|
+
inputPath = resolved.inputPath;
|
|
730
|
+
const inputText = readTextFileOrEmpty(inputPath, repoRoot);
|
|
277
731
|
const manifest = buildSpecGenerationManifest({
|
|
278
|
-
inputPath
|
|
732
|
+
inputPath,
|
|
279
733
|
inputText,
|
|
280
734
|
repoRoot,
|
|
281
735
|
specSlug: options.specSlug,
|
|
282
736
|
});
|
|
283
737
|
|
|
738
|
+
if (options.printPrompt) {
|
|
739
|
+
const report = {
|
|
740
|
+
task: 'plan',
|
|
741
|
+
phase,
|
|
742
|
+
manifest,
|
|
743
|
+
};
|
|
744
|
+
process.stdout.write('AI plan prompt-only\nPhase: spec\nNo provider prompt is used for spec generation; showing the local generation plan instead.\n');
|
|
745
|
+
process.stdout.write(formatSpecDryRunReport({ manifest, repoRoot }));
|
|
746
|
+
return report;
|
|
747
|
+
}
|
|
748
|
+
|
|
284
749
|
if (options.dryRun) {
|
|
285
750
|
const report = {
|
|
286
751
|
task: 'plan',
|
|
@@ -292,7 +757,7 @@ async function runPlan(repoRoot, options = {}) {
|
|
|
292
757
|
}
|
|
293
758
|
|
|
294
759
|
const result = generateSpecArtifacts(repoRoot, {
|
|
295
|
-
input:
|
|
760
|
+
input: inputPath,
|
|
296
761
|
specSlug: options.specSlug,
|
|
297
762
|
});
|
|
298
763
|
process.stdout.write(formatSpecGenerationResult(result, repoRoot));
|
|
@@ -309,14 +774,39 @@ async function runPlan(repoRoot, options = {}) {
|
|
|
309
774
|
|
|
310
775
|
assertPlannerPhaseReady(phase);
|
|
311
776
|
|
|
312
|
-
|
|
777
|
+
let inputText = '';
|
|
778
|
+
|
|
779
|
+
if (options.revise === true) {
|
|
780
|
+
if (!inputPath) {
|
|
781
|
+
throw new Error(formatError(`missing feedback input file for ai revise phase '${phase}'`));
|
|
782
|
+
}
|
|
783
|
+
const feedbackText = readTextFile(inputPath, repoRoot);
|
|
784
|
+
inputText = buildRevisionInput({
|
|
785
|
+
phase,
|
|
786
|
+
feedbackPath: inputPath,
|
|
787
|
+
feedbackText,
|
|
788
|
+
repoRoot,
|
|
789
|
+
});
|
|
790
|
+
} else if (phase === 'technical-plan') {
|
|
791
|
+
const resolved = resolveApprovedPlannerInput(repoRoot, phase, inputPath || undefined);
|
|
792
|
+
inputPath = resolved.inputPath;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
if (!inputPath) {
|
|
796
|
+
throw new Error(formatError(`missing input file for ai plan phase '${phase}'`));
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
if (!inputText) {
|
|
800
|
+
inputText = readTextFile(inputPath, repoRoot);
|
|
801
|
+
}
|
|
313
802
|
const contextInfo = buildPlanContext({
|
|
314
803
|
role,
|
|
315
804
|
context,
|
|
316
805
|
phase,
|
|
317
806
|
inputText,
|
|
318
|
-
inputPath
|
|
807
|
+
inputPath,
|
|
319
808
|
repoRoot,
|
|
809
|
+
revise: options.revise === true,
|
|
320
810
|
});
|
|
321
811
|
const prompt = contextInfo.prompt;
|
|
322
812
|
let invocation;
|
|
@@ -344,6 +834,20 @@ async function runPlan(repoRoot, options = {}) {
|
|
|
344
834
|
return report;
|
|
345
835
|
}
|
|
346
836
|
|
|
837
|
+
if (options.printPrompt) {
|
|
838
|
+
const report = {
|
|
839
|
+
task: 'plan',
|
|
840
|
+
provider,
|
|
841
|
+
role,
|
|
842
|
+
contextPack: contextInfo.pack.packName,
|
|
843
|
+
phase,
|
|
844
|
+
invocation,
|
|
845
|
+
prompt,
|
|
846
|
+
};
|
|
847
|
+
process.stdout.write(formatPromptOnlyReport(report));
|
|
848
|
+
return report;
|
|
849
|
+
}
|
|
850
|
+
|
|
347
851
|
let result;
|
|
348
852
|
try {
|
|
349
853
|
result = await (options.runProviderFn || runProvider)(provider, {
|
|
@@ -367,6 +871,17 @@ async function runPlan(repoRoot, options = {}) {
|
|
|
367
871
|
throw annotateProviderError(result.error || new Error('provider run failed'), 'plan', phase);
|
|
368
872
|
}
|
|
369
873
|
|
|
874
|
+
const draft = savePlannerDraft(repoRoot, phase, inputPath, getRedactedProviderText(result));
|
|
875
|
+
const lifecycleRun = ensureAiRun(repoRoot, {
|
|
876
|
+
command: `ai plan --phase ${phase}`,
|
|
877
|
+
input: inputPath,
|
|
878
|
+
runId: options.runId,
|
|
879
|
+
});
|
|
880
|
+
updateAiRunPhase(repoRoot, lifecycleRun.run_id, phase === 'acceptance' ? 'acceptance-draft' : 'technical-plan-draft', {
|
|
881
|
+
artifact: path.relative(repoRoot, draft.filePath).split(path.sep).join('/'),
|
|
882
|
+
command: `ai plan --phase ${phase}`,
|
|
883
|
+
});
|
|
884
|
+
|
|
370
885
|
return {
|
|
371
886
|
task: 'plan',
|
|
372
887
|
provider,
|
|
@@ -378,6 +893,436 @@ async function runPlan(repoRoot, options = {}) {
|
|
|
378
893
|
};
|
|
379
894
|
}
|
|
380
895
|
|
|
896
|
+
async function runReviewPlan(repoRoot, options = {}) {
|
|
897
|
+
const role = 'planner';
|
|
898
|
+
const provider = resolveProviderForProfile(repoRoot, 'reviewer', options.provider, options.providerExplicit, DEFAULT_PLAN_PROVIDER);
|
|
899
|
+
const context = options.context || DEFAULT_PLAN_CONTEXT;
|
|
900
|
+
const timeoutMs = normalizeTimeout(options.timeout);
|
|
901
|
+
const resolved = resolveTechnicalPlanReviewInput(repoRoot, options.input || undefined);
|
|
902
|
+
const inputPath = resolved.inputPath;
|
|
903
|
+
const inputText = readTextFile(inputPath, repoRoot);
|
|
904
|
+
const pack = buildContextPackMetadata({
|
|
905
|
+
role,
|
|
906
|
+
packName: context,
|
|
907
|
+
repoRoot,
|
|
908
|
+
});
|
|
909
|
+
const built = buildPlanReviewPrompt({
|
|
910
|
+
pack,
|
|
911
|
+
inputText,
|
|
912
|
+
inputPath,
|
|
913
|
+
});
|
|
914
|
+
let invocation;
|
|
915
|
+
|
|
916
|
+
try {
|
|
917
|
+
invocation = buildProviderInvocation(provider, {
|
|
918
|
+
prompt: built.prompt,
|
|
919
|
+
cwd: repoRoot,
|
|
920
|
+
timeoutMs,
|
|
921
|
+
});
|
|
922
|
+
} catch (error) {
|
|
923
|
+
throw annotateProviderError(error, 'review-plan');
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
if (options.dryRun) {
|
|
927
|
+
const report = {
|
|
928
|
+
task: 'review-plan',
|
|
929
|
+
provider,
|
|
930
|
+
role: 'reviewer',
|
|
931
|
+
contextPack: pack.packName,
|
|
932
|
+
invocation,
|
|
933
|
+
promptSource: built.promptSource,
|
|
934
|
+
inputPath,
|
|
935
|
+
inputKind: resolved.kind,
|
|
936
|
+
inputVersion: resolved.version,
|
|
937
|
+
};
|
|
938
|
+
process.stdout.write(formatDryRunReport({
|
|
939
|
+
task: 'review-plan',
|
|
940
|
+
provider,
|
|
941
|
+
role: 'reviewer',
|
|
942
|
+
contextPack: pack.packName,
|
|
943
|
+
phase: 'plan-review',
|
|
944
|
+
invocation,
|
|
945
|
+
}));
|
|
946
|
+
process.stdout.write(`Prompt source: ${built.promptSource}\n`);
|
|
947
|
+
process.stdout.write(`Input file: ${inputPath}\n`);
|
|
948
|
+
process.stdout.write(`Input kind: ${resolved.kind}\n`);
|
|
949
|
+
if (resolved.version) {
|
|
950
|
+
process.stdout.write(`Input version: v${resolved.version}\n`);
|
|
951
|
+
}
|
|
952
|
+
return report;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
if (options.printPrompt) {
|
|
956
|
+
const report = {
|
|
957
|
+
task: 'review-plan',
|
|
958
|
+
provider,
|
|
959
|
+
role: 'reviewer',
|
|
960
|
+
contextPack: pack.packName,
|
|
961
|
+
phase: 'plan-review',
|
|
962
|
+
invocation,
|
|
963
|
+
prompt: built.prompt,
|
|
964
|
+
promptSource: built.promptSource,
|
|
965
|
+
inputPath,
|
|
966
|
+
inputKind: resolved.kind,
|
|
967
|
+
inputVersion: resolved.version,
|
|
968
|
+
};
|
|
969
|
+
process.stdout.write(formatPromptOnlyReport(report));
|
|
970
|
+
return report;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
let result;
|
|
974
|
+
try {
|
|
975
|
+
result = await (options.runProviderFn || runProvider)(provider, {
|
|
976
|
+
prompt: built.prompt,
|
|
977
|
+
cwd: repoRoot,
|
|
978
|
+
timeoutMs,
|
|
979
|
+
dryRun: false,
|
|
980
|
+
probe: options.probe,
|
|
981
|
+
spawn: options.spawn,
|
|
982
|
+
tempRoot: options.tempRoot,
|
|
983
|
+
tempFileName: options.tempFileName,
|
|
984
|
+
tempFilePrefix: options.tempFilePrefix,
|
|
985
|
+
});
|
|
986
|
+
} catch (error) {
|
|
987
|
+
throw annotateProviderError(error, 'review-plan');
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
writeProviderOutput(result);
|
|
991
|
+
|
|
992
|
+
if (!result.ok) {
|
|
993
|
+
throw annotateProviderError(result.error || new Error('provider run failed'), 'review-plan');
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
const saved = savePlanReview(repoRoot, {
|
|
997
|
+
contents: getRedactedProviderText(result),
|
|
998
|
+
inputPath,
|
|
999
|
+
inputKind: resolved.kind,
|
|
1000
|
+
inputVersion: resolved.version,
|
|
1001
|
+
});
|
|
1002
|
+
const relativePath = path.relative(repoRoot, saved.filePath).split(path.sep).join('/');
|
|
1003
|
+
process.stdout.write(`AI plan review saved\nArtifact: ${relativePath}\nPrompt source: ${PLAN_REVIEW_PROMPT_SOURCE}\n`);
|
|
1004
|
+
|
|
1005
|
+
return {
|
|
1006
|
+
task: 'review-plan',
|
|
1007
|
+
provider,
|
|
1008
|
+
role: 'reviewer',
|
|
1009
|
+
contextPack: pack.packName,
|
|
1010
|
+
inputPath,
|
|
1011
|
+
inputKind: resolved.kind,
|
|
1012
|
+
inputVersion: resolved.version,
|
|
1013
|
+
filePath: relativePath,
|
|
1014
|
+
invocation,
|
|
1015
|
+
result,
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
async function runRevise(repoRoot, options = {}) {
|
|
1020
|
+
const phase = normalizePlannerPhase(options.phase || DEFAULT_PLAN_PHASE);
|
|
1021
|
+
if (phase === 'spec') {
|
|
1022
|
+
throw new Error(formatError(`ai revise does not support phase '${phase}'`));
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
const approval = readPhaseApproval(repoRoot, phase);
|
|
1026
|
+
if (approval.status !== 'draft' && approval.status !== 'stale') {
|
|
1027
|
+
throw new Error(formatError(`ai revise --phase ${phase} requires an existing draft; current status is ${approval.status}. Run \`npx create-quiver ai plan --phase ${phase} --input <file>\` first.`));
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
return runPlan(repoRoot, {
|
|
1031
|
+
...options,
|
|
1032
|
+
phase,
|
|
1033
|
+
revise: true,
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
async function runApprove(repoRoot, options = {}) {
|
|
1038
|
+
const phase = normalizePlannerPhase(options.phase || DEFAULT_PLAN_PHASE);
|
|
1039
|
+
if (phase === 'spec') {
|
|
1040
|
+
throw new Error(formatError(`ai approve does not support phase '${phase}'`));
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
if (!options.version) {
|
|
1044
|
+
throw new Error(formatError(`ai approve --phase ${phase} requires --version <n>. Review drafts with \`npx create-quiver ai approvals\`.`));
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
if (options.input) {
|
|
1048
|
+
throw new Error(formatError(`ai approve --phase ${phase} approves saved draft versions only. Use \`npx create-quiver ai revise --phase ${phase} --input ${options.input}\` to create a new draft first.`));
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
if (phase === 'technical-plan') {
|
|
1052
|
+
const review = readPlanReview(repoRoot);
|
|
1053
|
+
if (review.status !== 'unapproved' && review.status !== 'reviewed') {
|
|
1054
|
+
throw new Error(formatError(`ai approve --phase technical-plan requires a production review for the current draft; current review status is ${review.status}. Run \`npx create-quiver ai review-plan\`.`));
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const inputText = '';
|
|
1059
|
+
|
|
1060
|
+
if (options.dryRun) {
|
|
1061
|
+
process.stdout.write(formatApprovalDryRunResult({ phase, input: options.input, version: options.version }));
|
|
1062
|
+
return {
|
|
1063
|
+
task: 'approve',
|
|
1064
|
+
phase,
|
|
1065
|
+
input: options.input,
|
|
1066
|
+
version: options.version || null,
|
|
1067
|
+
dryRun: true,
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
const result = approvePlannerPhase(repoRoot, phase, options.input || '', inputText, {
|
|
1072
|
+
version: options.version || undefined,
|
|
1073
|
+
});
|
|
1074
|
+
const lifecycleRun = ensureAiRun(repoRoot, {
|
|
1075
|
+
command: `ai approve --phase ${phase}`,
|
|
1076
|
+
input: options.input || result.filePath,
|
|
1077
|
+
runId: options.runId,
|
|
1078
|
+
});
|
|
1079
|
+
recordAiRunApproval(repoRoot, lifecycleRun.run_id, {
|
|
1080
|
+
artifact: path.relative(repoRoot, result.filePath).split(path.sep).join('/'),
|
|
1081
|
+
phase,
|
|
1082
|
+
source_file: options.input || `draft version ${options.version}`,
|
|
1083
|
+
version: result.version || null,
|
|
1084
|
+
});
|
|
1085
|
+
updateAiRunPhase(repoRoot, lifecycleRun.run_id, phase === 'acceptance' ? 'acceptance-approved' : 'technical-plan-approved', {
|
|
1086
|
+
artifact: path.relative(repoRoot, result.filePath).split(path.sep).join('/'),
|
|
1087
|
+
command: `ai approve --phase ${phase}`,
|
|
1088
|
+
});
|
|
1089
|
+
process.stdout.write(formatApprovalResult({
|
|
1090
|
+
...result,
|
|
1091
|
+
sourceFile: options.input || `draft version ${options.version}`,
|
|
1092
|
+
}, repoRoot));
|
|
1093
|
+
|
|
1094
|
+
return {
|
|
1095
|
+
task: 'approve',
|
|
1096
|
+
phase,
|
|
1097
|
+
input: options.input,
|
|
1098
|
+
filePath: path.relative(repoRoot, result.filePath).split(path.sep).join('/'),
|
|
1099
|
+
createdAt: result.createdAt,
|
|
1100
|
+
version: result.version || null,
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
async function runApprovalStatus(repoRoot) {
|
|
1105
|
+
const report = formatApprovalStatusReport(repoRoot);
|
|
1106
|
+
process.stdout.write(report);
|
|
1107
|
+
return {
|
|
1108
|
+
task: 'approval-status',
|
|
1109
|
+
report,
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
function runLifecycleStatus(repoRoot, options = {}) {
|
|
1114
|
+
const run = resolveAiRun(repoRoot, options.runId || '');
|
|
1115
|
+
const report = formatAiRunStatus(repoRoot, run);
|
|
1116
|
+
process.stdout.write(report);
|
|
1117
|
+
return {
|
|
1118
|
+
task: 'status',
|
|
1119
|
+
run,
|
|
1120
|
+
report,
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
function runLifecycleResume(repoRoot, options = {}) {
|
|
1125
|
+
const run = resolveAiRun(repoRoot, options.runId || '');
|
|
1126
|
+
const report = formatAiRunResume(repoRoot, run);
|
|
1127
|
+
process.stdout.write(report);
|
|
1128
|
+
return {
|
|
1129
|
+
task: 'resume',
|
|
1130
|
+
run,
|
|
1131
|
+
report,
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
function runInspect(repoRoot, options = {}) {
|
|
1136
|
+
const report = collectLifecycleExport(repoRoot, {
|
|
1137
|
+
includeCompleted: options.includeCompleted === true,
|
|
1138
|
+
});
|
|
1139
|
+
process.stdout.write(formatLifecycleInspect(report));
|
|
1140
|
+
return {
|
|
1141
|
+
task: 'inspect',
|
|
1142
|
+
report,
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
function runExport(repoRoot, options = {}) {
|
|
1147
|
+
const report = collectLifecycleExport(repoRoot, {
|
|
1148
|
+
includeCompleted: options.includeCompleted === true,
|
|
1149
|
+
});
|
|
1150
|
+
const format = String(options.format || 'json').trim().toLowerCase();
|
|
1151
|
+
|
|
1152
|
+
if (format === 'json') {
|
|
1153
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
1154
|
+
return {
|
|
1155
|
+
task: 'export',
|
|
1156
|
+
format,
|
|
1157
|
+
report,
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
if (format === 'markdown' || format === 'md') {
|
|
1162
|
+
process.stdout.write(formatLifecycleExportMarkdown(report));
|
|
1163
|
+
return {
|
|
1164
|
+
task: 'export',
|
|
1165
|
+
format: 'markdown',
|
|
1166
|
+
report,
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
throw new Error(formatError(`unsupported ai export format: ${format}. Supported formats: json, markdown`));
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
function runSpecsList(repoRoot, options = {}) {
|
|
1174
|
+
const report = collectLifecycleExport(repoRoot, {
|
|
1175
|
+
includeCompleted: options.includeCompleted === true,
|
|
1176
|
+
});
|
|
1177
|
+
if (options.json === true) {
|
|
1178
|
+
process.stdout.write(`${JSON.stringify({ specs: report.specs }, null, 2)}\n`);
|
|
1179
|
+
} else {
|
|
1180
|
+
process.stdout.write(formatSpecsList(report));
|
|
1181
|
+
}
|
|
1182
|
+
return {
|
|
1183
|
+
task: 'specs',
|
|
1184
|
+
specs: report.specs,
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
function runSlicesList(repoRoot, options = {}) {
|
|
1189
|
+
const report = collectLifecycleExport(repoRoot, {
|
|
1190
|
+
includeCompleted: options.includeCompleted === true,
|
|
1191
|
+
});
|
|
1192
|
+
if (options.json === true) {
|
|
1193
|
+
process.stdout.write(`${JSON.stringify({ slices: report.slices }, null, 2)}\n`);
|
|
1194
|
+
} else {
|
|
1195
|
+
process.stdout.write(formatSlicesList(report));
|
|
1196
|
+
}
|
|
1197
|
+
return {
|
|
1198
|
+
task: 'slices',
|
|
1199
|
+
slices: report.slices,
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
function runTraceReport(repoRoot, options = {}) {
|
|
1204
|
+
const report = collectLifecycleExport(repoRoot, {
|
|
1205
|
+
includeCompleted: options.includeCompleted === true,
|
|
1206
|
+
});
|
|
1207
|
+
process.stdout.write(formatTraceReport(report));
|
|
1208
|
+
return {
|
|
1209
|
+
task: 'trace',
|
|
1210
|
+
report,
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
function runLifecycleRun(repoRoot, options = {}) {
|
|
1215
|
+
const command = String(options.command || '').trim().toLowerCase();
|
|
1216
|
+
if (command !== 'create') {
|
|
1217
|
+
throw new Error(formatError(`unsupported ai run subcommand: ${command}. Supported tasks: create`));
|
|
1218
|
+
}
|
|
1219
|
+
if (!options.input) {
|
|
1220
|
+
throw new Error(formatError('ai run create requires --input <requirements.md>'));
|
|
1221
|
+
}
|
|
1222
|
+
const run = createAiRun(repoRoot, {
|
|
1223
|
+
command: 'ai run create',
|
|
1224
|
+
input: options.input,
|
|
1225
|
+
runId: options.runId,
|
|
1226
|
+
specSlug: options.specSlug,
|
|
1227
|
+
});
|
|
1228
|
+
const report = formatAiRunStatus(repoRoot, run);
|
|
1229
|
+
process.stdout.write(report);
|
|
1230
|
+
return {
|
|
1231
|
+
task: 'run',
|
|
1232
|
+
command,
|
|
1233
|
+
run,
|
|
1234
|
+
report,
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
function formatAgentProfile(profile) {
|
|
1239
|
+
const lines = [
|
|
1240
|
+
`Role: ${profile.role}`,
|
|
1241
|
+
`Provider: ${profile.provider}`,
|
|
1242
|
+
`Model: ${profile.model || '(not set)'}`,
|
|
1243
|
+
`Label: ${profile.label || '(not set)'}`,
|
|
1244
|
+
`Context: ${profile.context || '(not set)'}`,
|
|
1245
|
+
`Updated: ${profile.updated_at}`,
|
|
1246
|
+
];
|
|
1247
|
+
return `${lines.join('\n')}\n`;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
function formatAgentProfileList(profiles) {
|
|
1251
|
+
const lines = ['AI agent profiles'];
|
|
1252
|
+
for (const item of profiles) {
|
|
1253
|
+
if (!item.configured) {
|
|
1254
|
+
lines.push(`- ${item.role}: not configured`);
|
|
1255
|
+
continue;
|
|
1256
|
+
}
|
|
1257
|
+
const model = item.profile.model ? ` model=${item.profile.model}` : '';
|
|
1258
|
+
const label = item.profile.label ? ` label=${item.profile.label}` : '';
|
|
1259
|
+
lines.push(`- ${item.role}: provider=${item.profile.provider}${model}${label}`);
|
|
1260
|
+
}
|
|
1261
|
+
return `${lines.join('\n')}\n`;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
function runAgent(repoRoot, options = {}) {
|
|
1265
|
+
const command = String(options.command || '').trim().toLowerCase();
|
|
1266
|
+
|
|
1267
|
+
if (command === 'set') {
|
|
1268
|
+
if (!options.role) {
|
|
1269
|
+
throw new Error(formatError('missing agent role. Use: npx create-quiver ai agent set <planner|executor|reviewer|doctor> --provider <provider>'));
|
|
1270
|
+
}
|
|
1271
|
+
if (!options.provider) {
|
|
1272
|
+
throw new Error(formatError('ai agent set requires --provider. Supported providers: codex, claude, gemini.'));
|
|
1273
|
+
}
|
|
1274
|
+
const result = setAgentProfile(repoRoot, options.role, {
|
|
1275
|
+
context: options.context,
|
|
1276
|
+
label: options.label,
|
|
1277
|
+
model: options.model,
|
|
1278
|
+
provider: options.provider,
|
|
1279
|
+
});
|
|
1280
|
+
process.stdout.write('AI agent profile saved\n');
|
|
1281
|
+
process.stdout.write(formatAgentProfile(result.profile));
|
|
1282
|
+
process.stdout.write(`State: ${path.relative(repoRoot, result.filePath).split(path.sep).join('/')}\n`);
|
|
1283
|
+
return {
|
|
1284
|
+
task: 'agent',
|
|
1285
|
+
command,
|
|
1286
|
+
profile: result.profile,
|
|
1287
|
+
filePath: path.relative(repoRoot, result.filePath).split(path.sep).join('/'),
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
if (command === 'show') {
|
|
1292
|
+
if (!options.role) {
|
|
1293
|
+
throw new Error(formatError('missing agent role. Use: npx create-quiver ai agent show <planner|executor|reviewer|doctor>'));
|
|
1294
|
+
}
|
|
1295
|
+
const profile = getAgentProfile(repoRoot, options.role);
|
|
1296
|
+
if (!profile) {
|
|
1297
|
+
throw new Error(formatActionableError({
|
|
1298
|
+
failure: `agent profile '${options.role}' is not configured.`,
|
|
1299
|
+
impact: 'Quiver will fall back to default provider behavior and may use the wrong model/cost profile.',
|
|
1300
|
+
fix: `Configure the ${options.role} profile with a supported provider and optional model label.`,
|
|
1301
|
+
nextCommand: `npx create-quiver ai agent set ${options.role} --provider <provider> --model <label>`,
|
|
1302
|
+
}));
|
|
1303
|
+
}
|
|
1304
|
+
process.stdout.write(formatAgentProfile(profile));
|
|
1305
|
+
return {
|
|
1306
|
+
task: 'agent',
|
|
1307
|
+
command,
|
|
1308
|
+
profile,
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
if (command === 'list' || command === 'ls' || command === '') {
|
|
1313
|
+
const profiles = listAgentProfiles(repoRoot);
|
|
1314
|
+
process.stdout.write(formatAgentProfileList(profiles));
|
|
1315
|
+
process.stdout.write(`State: ${path.relative(repoRoot, agentProfilesPath(repoRoot)).split(path.sep).join('/')}\n`);
|
|
1316
|
+
return {
|
|
1317
|
+
task: 'agent',
|
|
1318
|
+
command: 'list',
|
|
1319
|
+
profiles,
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
throw new Error(formatError(`unsupported ai agent subcommand: ${command}. Supported tasks: set, list, show`));
|
|
1324
|
+
}
|
|
1325
|
+
|
|
381
1326
|
async function runGitHubTask(repoRoot, options = {}, mode = 'pr') {
|
|
382
1327
|
const dryRun = options.dryRun === true;
|
|
383
1328
|
let report;
|
|
@@ -409,7 +1354,69 @@ async function runGitHubTask(repoRoot, options = {}, mode = 'pr') {
|
|
|
409
1354
|
}
|
|
410
1355
|
|
|
411
1356
|
async function runPr(repoRoot, options = {}) {
|
|
412
|
-
|
|
1357
|
+
const dryRun = options.dryRun === true;
|
|
1358
|
+
const create = options.create === true;
|
|
1359
|
+
let preflight;
|
|
1360
|
+
|
|
1361
|
+
try {
|
|
1362
|
+
preflight = await (options.preflightFn || preflightGitHubPr)(repoRoot, {
|
|
1363
|
+
remote: options.remote,
|
|
1364
|
+
sshHostAlias: options.sshHostAlias,
|
|
1365
|
+
identityFile: options.identityFile,
|
|
1366
|
+
gitFlowGuidePath: options.gitFlowGuidePath,
|
|
1367
|
+
ghCommand: options.ghCommand,
|
|
1368
|
+
ghProbe: options.ghProbe,
|
|
1369
|
+
ghAuthProbe: options.ghAuthProbe,
|
|
1370
|
+
ghProbeArgs: options.ghProbeArgs,
|
|
1371
|
+
ghAuthArgs: options.ghAuthArgs,
|
|
1372
|
+
blockedBranches: options.blockedBranches,
|
|
1373
|
+
});
|
|
1374
|
+
} catch (error) {
|
|
1375
|
+
throw annotateGitHubError(error, 'pr');
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
let plan;
|
|
1379
|
+
try {
|
|
1380
|
+
plan = buildPrCreatePlan(repoRoot, preflight, {
|
|
1381
|
+
baseBranch: options.baseBranch,
|
|
1382
|
+
ghCommand: options.ghCommand,
|
|
1383
|
+
input: options.input,
|
|
1384
|
+
prBodyPath: options.prBodyPath,
|
|
1385
|
+
title: options.title,
|
|
1386
|
+
});
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
throw annotateGitHubError(error, 'pr');
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
if (dryRun || !create) {
|
|
1392
|
+
process.stdout.write(formatPrCreateReport({ preflight, plan }, { dryRun, create }));
|
|
1393
|
+
return {
|
|
1394
|
+
task: 'pr',
|
|
1395
|
+
dryRun,
|
|
1396
|
+
create,
|
|
1397
|
+
preflight,
|
|
1398
|
+
plan,
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
let result;
|
|
1403
|
+
try {
|
|
1404
|
+
result = runGhPrCreate(plan, {
|
|
1405
|
+
ghCreateRunner: options.ghCreateRunner,
|
|
1406
|
+
});
|
|
1407
|
+
} catch (error) {
|
|
1408
|
+
throw annotateGitHubError(error, 'pr');
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
process.stdout.write(formatPrCreateReport({ preflight, plan, result }, { dryRun: false, create: true }));
|
|
1412
|
+
return {
|
|
1413
|
+
task: 'pr',
|
|
1414
|
+
dryRun: false,
|
|
1415
|
+
create: true,
|
|
1416
|
+
preflight,
|
|
1417
|
+
plan,
|
|
1418
|
+
result,
|
|
1419
|
+
};
|
|
413
1420
|
}
|
|
414
1421
|
|
|
415
1422
|
async function runDoctor(repoRoot, options = {}) {
|
|
@@ -433,9 +1440,25 @@ module.exports = {
|
|
|
433
1440
|
formatSpecDryRunReport,
|
|
434
1441
|
normalizeTimeout,
|
|
435
1442
|
readTextFile,
|
|
1443
|
+
runAgent,
|
|
436
1444
|
runDoctor,
|
|
1445
|
+
runExecutePlan,
|
|
437
1446
|
runExecuteSlice,
|
|
1447
|
+
runLifecycleResume,
|
|
1448
|
+
runLifecycleRun,
|
|
1449
|
+
runLifecycleStatus,
|
|
1450
|
+
runExport,
|
|
1451
|
+
runInspect,
|
|
1452
|
+
runPromptSlice,
|
|
1453
|
+
runApprove,
|
|
1454
|
+
runApprovalStatus,
|
|
1455
|
+
runPrepareContext,
|
|
1456
|
+
runReviewPlan,
|
|
1457
|
+
runRevise,
|
|
438
1458
|
runPr,
|
|
1459
|
+
runSlicesList,
|
|
1460
|
+
runSpecsList,
|
|
1461
|
+
runTraceReport,
|
|
439
1462
|
runOnboard,
|
|
440
1463
|
runPlan,
|
|
441
1464
|
writeProviderOutput,
|