@entelligentsia/forgecli 1.0.25 → 1.0.36
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 +258 -0
- package/README.md +2 -0
- package/dist/CHANGELOG-forge-plugin.md +163 -0
- package/dist/bin/argv.d.ts +2 -2
- package/dist/bin/argv.js +25 -0
- package/dist/bin/argv.js.map +1 -1
- package/dist/bin/forge.js +12 -0
- package/dist/bin/forge.js.map +1 -1
- package/dist/bin/init.d.ts +23 -0
- package/dist/bin/init.js +123 -0
- package/dist/bin/init.js.map +1 -0
- package/dist/bin/uninstall.d.ts +20 -0
- package/dist/bin/uninstall.js +141 -0
- package/dist/bin/uninstall.js.map +1 -0
- package/dist/extensions/forgecli/add-pipeline.d.ts +19 -0
- package/dist/extensions/forgecli/add-pipeline.js +143 -0
- package/dist/extensions/forgecli/add-pipeline.js.map +1 -0
- package/dist/extensions/forgecli/add-task.d.ts +20 -0
- package/dist/extensions/forgecli/add-task.js +154 -0
- package/dist/extensions/forgecli/add-task.js.map +1 -0
- package/dist/extensions/forgecli/approve.d.ts +22 -0
- package/dist/extensions/forgecli/approve.js +152 -0
- package/dist/extensions/forgecli/approve.js.map +1 -0
- package/dist/extensions/forgecli/banner.d.ts +10 -0
- package/dist/extensions/forgecli/banner.js +36 -0
- package/dist/extensions/forgecli/banner.js.map +1 -0
- package/dist/extensions/forgecli/calibrate.d.ts +64 -0
- package/dist/extensions/forgecli/calibrate.js +481 -0
- package/dist/extensions/forgecli/calibrate.js.map +1 -0
- package/dist/extensions/forgecli/claude-bootstrap/bootstrap.d.ts +40 -0
- package/dist/extensions/forgecli/claude-bootstrap/bootstrap.js +593 -0
- package/dist/extensions/forgecli/claude-bootstrap/bootstrap.js.map +1 -0
- package/dist/extensions/forgecli/claude-bootstrap/settings-merge.d.ts +46 -0
- package/dist/extensions/forgecli/claude-bootstrap/settings-merge.js +245 -0
- package/dist/extensions/forgecli/claude-bootstrap/settings-merge.js.map +1 -0
- package/dist/extensions/forgecli/claude-bootstrap/uninstall.d.ts +23 -0
- package/dist/extensions/forgecli/claude-bootstrap/uninstall.js +215 -0
- package/dist/extensions/forgecli/claude-bootstrap/uninstall.js.map +1 -0
- package/dist/extensions/forgecli/collate.d.ts +22 -0
- package/dist/extensions/forgecli/collate.js +134 -0
- package/dist/extensions/forgecli/collate.js.map +1 -0
- package/dist/extensions/forgecli/commit.d.ts +22 -0
- package/dist/extensions/forgecli/commit.js +152 -0
- package/dist/extensions/forgecli/commit.js.map +1 -0
- package/dist/extensions/forgecli/config-command.d.ts +8 -0
- package/dist/extensions/forgecli/config-command.js +67 -0
- package/dist/extensions/forgecli/config-command.js.map +1 -0
- package/dist/extensions/forgecli/config-layer.d.ts +53 -0
- package/dist/extensions/forgecli/config-layer.js +72 -0
- package/dist/extensions/forgecli/config-layer.js.map +1 -0
- package/dist/extensions/forgecli/config-writer.d.ts +16 -0
- package/dist/extensions/forgecli/config-writer.js +69 -0
- package/dist/extensions/forgecli/config-writer.js.map +1 -0
- package/dist/extensions/forgecli/dashboard/component.js +10 -7
- package/dist/extensions/forgecli/dashboard/component.js.map +1 -1
- package/dist/extensions/forgecli/enhance.d.ts +27 -0
- package/dist/extensions/forgecli/enhance.js +199 -0
- package/dist/extensions/forgecli/enhance.js.map +1 -0
- package/dist/extensions/forgecli/fix-bug.d.ts +85 -0
- package/dist/extensions/forgecli/fix-bug.js +1580 -0
- package/dist/extensions/forgecli/fix-bug.js.map +1 -0
- package/dist/extensions/forgecli/forge-header.d.ts +12 -0
- package/dist/extensions/forgecli/forge-header.js +114 -0
- package/dist/extensions/forgecli/forge-header.js.map +1 -0
- package/dist/extensions/forgecli/forge-init.d.ts +26 -0
- package/dist/extensions/forgecli/forge-init.js +514 -0
- package/dist/extensions/forgecli/forge-init.js.map +1 -0
- package/dist/extensions/forgecli/forge-root.d.ts +10 -0
- package/dist/extensions/forgecli/forge-root.js +62 -0
- package/dist/extensions/forgecli/forge-root.js.map +1 -0
- package/dist/extensions/forgecli/forge-update-command.d.ts +100 -0
- package/dist/extensions/forgecli/forge-update-command.js +435 -0
- package/dist/extensions/forgecli/forge-update-command.js.map +1 -0
- package/dist/extensions/forgecli/friction-emit.d.ts +99 -0
- package/dist/extensions/forgecli/friction-emit.js +245 -0
- package/dist/extensions/forgecli/friction-emit.js.map +1 -0
- package/dist/extensions/forgecli/implement.d.ts +22 -0
- package/dist/extensions/forgecli/implement.js +170 -0
- package/dist/extensions/forgecli/implement.js.map +1 -0
- package/dist/extensions/forgecli/init-context.d.ts +99 -0
- package/dist/extensions/forgecli/init-context.js +178 -0
- package/dist/extensions/forgecli/init-context.js.map +1 -0
- package/dist/extensions/forgecli/init-progress.d.ts +39 -0
- package/dist/extensions/forgecli/init-progress.js +117 -0
- package/dist/extensions/forgecli/init-progress.js.map +1 -0
- package/dist/extensions/forgecli/input-router.d.ts +33 -0
- package/dist/extensions/forgecli/input-router.js +136 -0
- package/dist/extensions/forgecli/input-router.js.map +1 -0
- package/dist/extensions/forgecli/lib/halt-advisor.d.ts +59 -0
- package/dist/extensions/forgecli/lib/halt-advisor.js +113 -0
- package/dist/extensions/forgecli/lib/halt-advisor.js.map +1 -0
- package/dist/extensions/forgecli/lib/orchestrator-preflight.d.ts +46 -0
- package/dist/extensions/forgecli/lib/orchestrator-preflight.js +64 -0
- package/dist/extensions/forgecli/lib/orchestrator-preflight.js.map +1 -0
- package/dist/extensions/forgecli/materialize.d.ts +16 -0
- package/dist/extensions/forgecli/materialize.js +195 -0
- package/dist/extensions/forgecli/materialize.js.map +1 -0
- package/dist/extensions/forgecli/migrate.d.ts +22 -0
- package/dist/extensions/forgecli/migrate.js +260 -0
- package/dist/extensions/forgecli/migrate.js.map +1 -0
- package/dist/extensions/forgecli/migration-engine.d.ts +117 -0
- package/dist/extensions/forgecli/migration-engine.js +563 -0
- package/dist/extensions/forgecli/migration-engine.js.map +1 -0
- package/dist/extensions/forgecli/model-registry.d.ts +61 -0
- package/dist/extensions/forgecli/model-registry.js +127 -0
- package/dist/extensions/forgecli/model-registry.js.map +1 -0
- package/dist/extensions/forgecli/model-resolver.d.ts +32 -0
- package/dist/extensions/forgecli/model-resolver.js +65 -0
- package/dist/extensions/forgecli/model-resolver.js.map +1 -0
- package/dist/extensions/forgecli/model-validator.d.ts +29 -0
- package/dist/extensions/forgecli/model-validator.js +107 -0
- package/dist/extensions/forgecli/model-validator.js.map +1 -0
- package/dist/extensions/forgecli/orchestrator-status-bar.d.ts +26 -0
- package/dist/extensions/forgecli/orchestrator-status-bar.js +213 -0
- package/dist/extensions/forgecli/orchestrator-status-bar.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-body.d.ts +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-body.js +65 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-body.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-id.d.ts +23 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-id.js +140 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-id.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.d.ts +54 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.js +349 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phases.d.ts +8 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phases.js +60 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-phases.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-state.d.ts +14 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-state.js +100 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-state.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.d.ts +72 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.js +204 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.d.ts +38 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.js +166 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.d.ts +3 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.js +55 -0
- package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.d.ts +7 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.js +293 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.d.ts +2 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.js +501 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.d.ts +41 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.js +5 -0
- package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.d.ts +43 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.js +85 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.d.ts +8 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.js +37 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.d.ts +28 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.js +45 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.d.ts +26 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.js +75 -0
- package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/common/summary-recovery.d.ts +24 -0
- package/dist/extensions/forgecli/orchestrators/common/summary-recovery.js +37 -0
- package/dist/extensions/forgecli/orchestrators/common/summary-recovery.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/fix-bug.d.ts +9 -93
- package/dist/extensions/forgecli/orchestrators/fix-bug.js +23 -1721
- package/dist/extensions/forgecli/orchestrators/fix-bug.js.map +1 -1
- package/dist/extensions/forgecli/orchestrators/run-sprint.d.ts +3 -12
- package/dist/extensions/forgecli/orchestrators/run-sprint.js +48 -270
- package/dist/extensions/forgecli/orchestrators/run-sprint.js.map +1 -1
- package/dist/extensions/forgecli/orchestrators/run-task.d.ts +10 -214
- package/dist/extensions/forgecli/orchestrators/run-task.js +31 -1481
- package/dist/extensions/forgecli/orchestrators/run-task.js.map +1 -1
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.d.ts +33 -0
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.js +135 -0
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.d.ts +18 -0
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.js +55 -0
- package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-command.d.ts +9 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-command.js +174 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-command.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.d.ts +2 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.js +494 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-types.d.ts +62 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-types.js +5 -0
- package/dist/extensions/forgecli/orchestrators/task/run-task-types.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-body.d.ts +4 -0
- package/dist/extensions/forgecli/orchestrators/task/task-body.js +48 -0
- package/dist/extensions/forgecli/orchestrators/task/task-body.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-events.d.ts +63 -0
- package/dist/extensions/forgecli/orchestrators/task/task-events.js +185 -0
- package/dist/extensions/forgecli/orchestrators/task/task-events.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-gates.d.ts +34 -0
- package/dist/extensions/forgecli/orchestrators/task/task-gates.js +78 -0
- package/dist/extensions/forgecli/orchestrators/task/task-gates.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.d.ts +42 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.js +370 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phases.d.ts +14 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phases.js +26 -0
- package/dist/extensions/forgecli/orchestrators/task/task-phases.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-record.d.ts +9 -0
- package/dist/extensions/forgecli/orchestrators/task/task-record.js +58 -0
- package/dist/extensions/forgecli/orchestrators/task/task-record.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-state.d.ts +14 -0
- package/dist/extensions/forgecli/orchestrators/task/task-state.js +35 -0
- package/dist/extensions/forgecli/orchestrators/task/task-state.js.map +1 -0
- package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.d.ts +36 -0
- package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.js +152 -0
- package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.js.map +1 -0
- package/dist/extensions/forgecli/plan.d.ts +22 -0
- package/dist/extensions/forgecli/plan.js +167 -0
- package/dist/extensions/forgecli/plan.js.map +1 -0
- package/dist/extensions/forgecli/quiz-agent.d.ts +17 -0
- package/dist/extensions/forgecli/quiz-agent.js +98 -0
- package/dist/extensions/forgecli/quiz-agent.js.map +1 -0
- package/dist/extensions/forgecli/read-command.d.ts +2 -0
- package/dist/extensions/forgecli/read-command.js +100 -0
- package/dist/extensions/forgecli/read-command.js.map +1 -0
- package/dist/extensions/forgecli/regenerate.d.ts +40 -0
- package/dist/extensions/forgecli/regenerate.js +438 -0
- package/dist/extensions/forgecli/regenerate.js.map +1 -0
- package/dist/extensions/forgecli/remove-command.d.ts +17 -0
- package/dist/extensions/forgecli/remove-command.js +124 -0
- package/dist/extensions/forgecli/remove-command.js.map +1 -0
- package/dist/extensions/forgecli/report-bug.d.ts +25 -0
- package/dist/extensions/forgecli/report-bug.js +159 -0
- package/dist/extensions/forgecli/report-bug.js.map +1 -0
- package/dist/extensions/forgecli/retrospective.d.ts +20 -0
- package/dist/extensions/forgecli/retrospective.js +126 -0
- package/dist/extensions/forgecli/retrospective.js.map +1 -0
- package/dist/extensions/forgecli/review-code.d.ts +35 -0
- package/dist/extensions/forgecli/review-code.js +196 -0
- package/dist/extensions/forgecli/review-code.js.map +1 -0
- package/dist/extensions/forgecli/review-plan.d.ts +35 -0
- package/dist/extensions/forgecli/review-plan.js +200 -0
- package/dist/extensions/forgecli/review-plan.js.map +1 -0
- package/dist/extensions/forgecli/run-sprint.d.ts +27 -0
- package/dist/extensions/forgecli/run-sprint.js +716 -0
- package/dist/extensions/forgecli/run-sprint.js.map +1 -0
- package/dist/extensions/forgecli/run-task.d.ts +204 -0
- package/dist/extensions/forgecli/run-task.js +1403 -0
- package/dist/extensions/forgecli/run-task.js.map +1 -0
- package/dist/extensions/forgecli/skill-curation-flag.d.ts +21 -0
- package/dist/extensions/forgecli/skill-curation-flag.js +71 -0
- package/dist/extensions/forgecli/skill-curation-flag.js.map +1 -0
- package/dist/extensions/forgecli/skill-curator-subagent.d.ts +102 -0
- package/dist/extensions/forgecli/skill-curator-subagent.js +339 -0
- package/dist/extensions/forgecli/skill-curator-subagent.js.map +1 -0
- package/dist/extensions/forgecli/skill-retriever.d.ts +84 -0
- package/dist/extensions/forgecli/skill-retriever.js +246 -0
- package/dist/extensions/forgecli/skill-retriever.js.map +1 -0
- package/dist/extensions/forgecli/skill-usage-tracker.d.ts +91 -0
- package/dist/extensions/forgecli/skill-usage-tracker.js +224 -0
- package/dist/extensions/forgecli/skill-usage-tracker.js.map +1 -0
- package/dist/extensions/forgecli/sprint-intake.d.ts +10 -0
- package/dist/extensions/forgecli/sprint-intake.js +91 -0
- package/dist/extensions/forgecli/sprint-intake.js.map +1 -0
- package/dist/extensions/forgecli/sprint-plan.d.ts +14 -0
- package/dist/extensions/forgecli/sprint-plan.js +122 -0
- package/dist/extensions/forgecli/sprint-plan.js.map +1 -0
- package/dist/extensions/forgecli/status-command.d.ts +19 -0
- package/dist/extensions/forgecli/status-command.js +140 -0
- package/dist/extensions/forgecli/status-command.js.map +1 -0
- package/dist/extensions/forgecli/store-error-remediation.d.ts +65 -0
- package/dist/extensions/forgecli/store-error-remediation.js +307 -0
- package/dist/extensions/forgecli/store-error-remediation.js.map +1 -0
- package/dist/extensions/forgecli/store-query.d.ts +22 -0
- package/dist/extensions/forgecli/store-query.js +107 -0
- package/dist/extensions/forgecli/store-query.js.map +1 -0
- package/dist/extensions/forgecli/store-repair.d.ts +17 -0
- package/dist/extensions/forgecli/store-repair.js +123 -0
- package/dist/extensions/forgecli/store-repair.js.map +1 -0
- package/dist/extensions/forgecli/store-resolver.d.ts +56 -0
- package/dist/extensions/forgecli/store-resolver.js +263 -0
- package/dist/extensions/forgecli/store-resolver.js.map +1 -0
- package/dist/extensions/forgecli/store-validator.d.ts +16 -0
- package/dist/extensions/forgecli/store-validator.js +32 -0
- package/dist/extensions/forgecli/store-validator.js.map +1 -0
- package/dist/extensions/forgecli/test-orchestrate.d.ts +2 -0
- package/dist/extensions/forgecli/test-orchestrate.js +182 -0
- package/dist/extensions/forgecli/test-orchestrate.js.map +1 -0
- package/dist/extensions/forgecli/thread-switcher.d.ts +5 -0
- package/dist/extensions/forgecli/thread-switcher.js +189 -0
- package/dist/extensions/forgecli/thread-switcher.js.map +1 -0
- package/dist/extensions/forgecli/transition-guard.d.ts +20 -0
- package/dist/extensions/forgecli/transition-guard.js +89 -0
- package/dist/extensions/forgecli/transition-guard.js.map +1 -0
- package/dist/extensions/forgecli/update/forge-update-command.js +10 -7
- package/dist/extensions/forgecli/update/forge-update-command.js.map +1 -1
- package/dist/extensions/forgecli/update-check.d.ts +37 -0
- package/dist/extensions/forgecli/update-check.js +185 -0
- package/dist/extensions/forgecli/update-check.js.map +1 -0
- package/dist/extensions/forgecli/update-tools.d.ts +23 -0
- package/dist/extensions/forgecli/update-tools.js +135 -0
- package/dist/extensions/forgecli/update-tools.js.map +1 -0
- package/dist/extensions/forgecli/validate.d.ts +22 -0
- package/dist/extensions/forgecli/validate.js +152 -0
- package/dist/extensions/forgecli/validate.js.map +1 -0
- package/dist/extensions/forgecli/viewport-events.d.ts +78 -0
- package/dist/extensions/forgecli/viewport-events.js +243 -0
- package/dist/extensions/forgecli/viewport-events.js.map +1 -0
- package/dist/extensions/forgecli/viewport-renderer.d.ts +83 -0
- package/dist/extensions/forgecli/viewport-renderer.js +233 -0
- package/dist/extensions/forgecli/viewport-renderer.js.map +1 -0
- package/dist/extensions/forgecli/viewport-theme.d.ts +11 -0
- package/dist/extensions/forgecli/viewport-theme.js +128 -0
- package/dist/extensions/forgecli/viewport-theme.js.map +1 -0
- package/dist/extensions/forgecli/whats-new-widget.d.ts +26 -0
- package/dist/extensions/forgecli/whats-new-widget.js +376 -0
- package/dist/extensions/forgecli/whats-new-widget.js.map +1 -0
- package/dist/extensions/forgecli/whats-new.d.ts +120 -0
- package/dist/extensions/forgecli/whats-new.js +470 -0
- package/dist/extensions/forgecli/whats-new.js.map +1 -0
- package/dist/forge-payload/.base-pack/commands/approve.md +2 -2
- package/dist/forge-payload/.base-pack/commands/check-agent.md +2 -2
- package/dist/forge-payload/.base-pack/commands/collate.md +2 -2
- package/dist/forge-payload/.base-pack/commands/commit.md +2 -2
- package/dist/forge-payload/.base-pack/commands/enhance.md +2 -2
- package/dist/forge-payload/.base-pack/commands/fix-bug.md +2 -2
- package/dist/forge-payload/.base-pack/commands/implement.md +2 -2
- package/dist/forge-payload/.base-pack/commands/init.md +278 -0
- package/dist/forge-payload/.base-pack/commands/new-sprint.md +2 -2
- package/dist/forge-payload/.base-pack/commands/plan-sprint.md +2 -2
- package/dist/forge-payload/.base-pack/commands/plan.md +2 -2
- package/dist/forge-payload/.base-pack/commands/retro.md +2 -2
- package/dist/forge-payload/.base-pack/commands/review-code.md +2 -2
- package/dist/forge-payload/.base-pack/commands/review-plan.md +2 -2
- package/dist/forge-payload/.base-pack/commands/run-sprint.md +2 -2
- package/dist/forge-payload/.base-pack/commands/run-task.md +2 -2
- package/dist/forge-payload/.base-pack/commands/validate.md +2 -2
- package/dist/forge-payload/.base-pack/workflows/collator_agent.md +5 -6
- package/dist/forge-payload/.base-pack/workflows/migrate_structural.md +1 -1
- package/dist/forge-payload/.base-pack/workflows-js/wfl-init.js +449 -0
- package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
- package/dist/forge-payload/.schemas/enum-catalog.json +2 -2
- package/dist/forge-payload/.schemas/migrations.json +85 -0
- package/dist/forge-payload/commands/add-pipeline.md +1 -1
- package/dist/forge-payload/commands/add-task.md +3 -3
- package/dist/forge-payload/commands/ask.md +1 -1
- package/dist/forge-payload/commands/check-agent.md +1 -1
- package/dist/forge-payload/commands/config.md +1 -1
- package/dist/forge-payload/commands/health.md +1 -1
- package/dist/forge-payload/commands/init.md +62 -7
- package/dist/forge-payload/commands/rebuild.md +3 -3
- package/dist/forge-payload/commands/remove.md +1 -1
- package/dist/forge-payload/commands/repair.md +1 -1
- package/dist/forge-payload/commands/report-bug.md +1 -1
- package/dist/forge-payload/commands/status.md +1 -1
- package/dist/forge-payload/commands/update.md +3 -3
- package/dist/forge-payload/hooks/lib/common.cjs +228 -0
- package/dist/forge-payload/hooks/lib/plugin-detection.cjs +106 -0
- package/dist/forge-payload/hooks/lib/update-msg.cjs +23 -0
- package/dist/forge-payload/hooks/lib/update-url.cjs +46 -0
- package/dist/forge-payload/hooks/lib/write-registry.js +53 -0
- package/dist/forge-payload/init/discovery/discover-database.md +32 -0
- package/dist/forge-payload/init/discovery/discover-processes.md +31 -0
- package/dist/forge-payload/init/discovery/discover-routing.md +31 -0
- package/dist/forge-payload/init/discovery/discover-stack.md +33 -0
- package/dist/forge-payload/init/discovery/discover-testing.md +34 -0
- package/dist/forge-payload/init/generation/generate-commands.md +171 -0
- package/dist/forge-payload/init/generation/generate-kb-doc.md +60 -0
- package/dist/forge-payload/init/generation/generate-knowledge-base.md +56 -0
- package/dist/forge-payload/init/generation/generate-persona.md +73 -0
- package/dist/forge-payload/init/generation/generate-personas.md +54 -0
- package/dist/forge-payload/init/generation/generate-skill.md +66 -0
- package/dist/forge-payload/init/generation/generate-skills.md +36 -0
- package/dist/forge-payload/init/generation/generate-template.md +60 -0
- package/dist/forge-payload/init/generation/generate-templates.md +39 -0
- package/dist/forge-payload/init/generation/generate-tools.md +133 -0
- package/dist/forge-payload/init/generation/generate-workflows.md +78 -0
- package/dist/forge-payload/init/phases/phase-1-collect.md +10 -2
- package/dist/forge-payload/init/phases/phase-3-materialize.md +1 -1
- package/dist/forge-payload/init/phases/phase-4-register.md +8 -0
- package/dist/forge-payload/init/workflow-gen-plan.json +17 -0
- package/dist/forge-payload/integrity.json +16 -16
- package/dist/forge-payload/meta/workflows/meta-collate.md +5 -6
- package/dist/forge-payload/meta/workflows/meta-migrate.md +1 -1
- package/dist/forge-payload/schemas/enum-catalog.json +2 -2
- package/dist/forge-payload/schemas/structure-manifest.json +3 -12
- package/dist/forge-payload/tools/forge-preflight.cjs +268 -0
- package/dist/forge-payload/tools/lib/paths.cjs +12 -11
- package/dist/forge-payload/tools/lib/pricing.cjs +31 -11
- package/dist/forge-payload/tools/query-logger.cjs +34 -0
- package/dist/forge-payload/tools/substitute-placeholders.cjs +5 -6
- package/node_modules/@mariozechner/clipboard/package.json +1 -2
- package/package.json +2 -2
- package/node_modules/@mariozechner/clipboard-linux-x64-musl/README.md +0 -3
- package/node_modules/@mariozechner/clipboard-linux-x64-musl/package.json +0 -25
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
// run-sprint.ts — /forge:run-sprint native Orchestrator handler (FORGE-S21-T03, Plan 12).
|
|
2
|
+
//
|
|
3
|
+
// Sprint-level orchestrator that iterates over a sprint's task list,
|
|
4
|
+
// delegating per-task execution to runTaskPipeline (extracted from run-task.ts).
|
|
5
|
+
//
|
|
6
|
+
// The sprint handler does NOT contain its own phase loop; per-phase concerns
|
|
7
|
+
// are ALL delegated to runTaskPipeline. The sprint handler owns sprint
|
|
8
|
+
// coordination only: resolve sprint, confirm gates, iterate tasks, persist
|
|
9
|
+
// sprint state, dispatch architect ceremony, and emit sprint-scoped events.
|
|
10
|
+
//
|
|
11
|
+
// Plan 12 truth table (§3):
|
|
12
|
+
// Clean-complete → architect ceremony (mode=complete) → sprint-complete event
|
|
13
|
+
// User-paused → architect ceremony (mode=partial) if ≥1 task done → sprint-complete event (verdict=partial)
|
|
14
|
+
// Halted-on-failure → NO ceremony → sprint-halted event
|
|
15
|
+
//
|
|
16
|
+
// Iron Laws enforced here:
|
|
17
|
+
// IL1 — code only under forge-cli/src/extensions/forgecli/
|
|
18
|
+
// IL6 — no shell-string interpolation; all external calls via spawnSync argv arrays
|
|
19
|
+
// IL7 — every failure path emits ctx.ui.notify and returns; no silent continuation
|
|
20
|
+
// IL10 — ALL LLM dispatch goes through runForgeSubagent (NO sendKickoff)
|
|
21
|
+
//
|
|
22
|
+
// sendKickoff is NEVER called from this file.
|
|
23
|
+
// Audit-grep: grep -n "sendKickoff(" run-sprint.ts must return empty.
|
|
24
|
+
//
|
|
25
|
+
// N-H-D — Ceremony vs per-task model routing:
|
|
26
|
+
// The sprint ceremony phase (architect summary / sprint-complete event) uses
|
|
27
|
+
// loadLayeredConfig + lookupPersonaModel directly (~lines 216, 227) without
|
|
28
|
+
// calling validateModelConfig. This is intentional: the ceremony is a single
|
|
29
|
+
// LLM call with a known model and the overhead of full preflight validation
|
|
30
|
+
// is not warranted here.
|
|
31
|
+
// Per-task dispatch is entirely delegated to runTaskPipeline, which calls
|
|
32
|
+
// runOrchestratorPreflight (persona/model config validation) at entry before
|
|
33
|
+
// any LLM dispatch. The two paths are deliberately separate.
|
|
34
|
+
// Reference: lib/orchestrator-preflight.ts (N-H-D, FORGE-S25-T17).
|
|
35
|
+
import { spawnSync } from "node:child_process";
|
|
36
|
+
import * as fs from "node:fs";
|
|
37
|
+
import * as path from "node:path";
|
|
38
|
+
import { fileURLToPath } from "node:url";
|
|
39
|
+
import { assertAudience } from "./audience-gate.js";
|
|
40
|
+
import { loadLayeredConfig } from "./config-layer.js";
|
|
41
|
+
import { loadForgePersona, runForgeSubagent } from "./forge-subagent.js";
|
|
42
|
+
import { getSubagentTools } from "./forge-tools.js";
|
|
43
|
+
import { emitSyntheticEvent } from "./hook-dispatcher.js";
|
|
44
|
+
import { readPersonaDir as readPersonaDirSprint, readPipelineNames as readPipelineNamesSprint, } from "./lib/catalog-helpers.js";
|
|
45
|
+
import { discoverForgeConfigCached } from "./lib/forge-config.js";
|
|
46
|
+
import { checkMaterialization } from "./lib/manifest-checker.js";
|
|
47
|
+
import { readJsonState, sprintStateFilePath, writeJsonState } from "./lib/state-helpers.js";
|
|
48
|
+
import { lookupPersonaModel } from "./model-resolver.js";
|
|
49
|
+
import { validateModelConfig } from "./model-validator.js";
|
|
50
|
+
import { loadWorkflow } from "./parsers/workflow-loader.js";
|
|
51
|
+
import { getSessionRegistry } from "./session-registry.js";
|
|
52
|
+
import { getOrchestratorTree } from "./orchestrator-tree.js";
|
|
53
|
+
import { resolveToCanonicalId, resolveToolDir } from "./store-resolver.js";
|
|
54
|
+
import { attachViewportObserver } from "./viewport-events.js";
|
|
55
|
+
import { emitEvent, formatLocalTime, isNonInteractive, isoCompact, isStateStale, readState as readTaskState, runTaskPipeline, validateId, } from "./run-task.js";
|
|
56
|
+
// FORGE-S25-T16 (N-H-B): sprint state helpers delegate to lib/state-helpers.ts.
|
|
57
|
+
function getSprintStatePath(cwd, sprintId) {
|
|
58
|
+
if (!validateId(sprintId)) {
|
|
59
|
+
throw new Error(`Invalid sprintId for state file path: ${sprintId}`);
|
|
60
|
+
}
|
|
61
|
+
return sprintStateFilePath(cwd, sprintId);
|
|
62
|
+
}
|
|
63
|
+
function readSprintState(cwd, sprintId) {
|
|
64
|
+
return readJsonState(getSprintStatePath(cwd, sprintId));
|
|
65
|
+
}
|
|
66
|
+
function writeSprintState(cwd, state) {
|
|
67
|
+
writeJsonState(getSprintStatePath(cwd, state.sprintId), state);
|
|
68
|
+
}
|
|
69
|
+
function deleteSprintState(cwd, sprintId) {
|
|
70
|
+
const fp = getSprintStatePath(cwd, sprintId);
|
|
71
|
+
try {
|
|
72
|
+
if (fs.existsSync(fp))
|
|
73
|
+
fs.unlinkSync(fp);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// non-fatal
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function isSprintStateStale(state) {
|
|
80
|
+
const savedAt = new Date(state.savedAt).getTime();
|
|
81
|
+
const ageMs = Date.now() - savedAt;
|
|
82
|
+
const sevenDaysMs = 7 * 24 * 60 * 60 * 1000;
|
|
83
|
+
return ageMs > sevenDaysMs;
|
|
84
|
+
}
|
|
85
|
+
function readSprintRecord(sprintId, storeCli, cwd) {
|
|
86
|
+
const result = spawnSync("node", [storeCli, "read", "sprint", sprintId], { cwd, encoding: "utf8" });
|
|
87
|
+
if (result.status !== 0)
|
|
88
|
+
return null;
|
|
89
|
+
try {
|
|
90
|
+
const raw = typeof result.stdout === "string" ? result.stdout : String(result.stdout);
|
|
91
|
+
const record = JSON.parse(raw);
|
|
92
|
+
// Validate taskIds is a non-empty array of strings
|
|
93
|
+
if (!Array.isArray(record.taskIds) || record.taskIds.length === 0)
|
|
94
|
+
return null;
|
|
95
|
+
if (!record.taskIds.every((id) => typeof id === "string"))
|
|
96
|
+
return null;
|
|
97
|
+
return record;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function dispatchSprintCeremony(params) {
|
|
104
|
+
const { sprintId, mode, completedTaskIds, pausedAfterIndex, cwd, forgeRoot, ctx, registry, streamFnFactory, forgeToolDefs, } = params;
|
|
105
|
+
const startMs = Date.now();
|
|
106
|
+
// Materialized workflow path — already shipped from base pack.
|
|
107
|
+
const workflowName = "architect_review_sprint_completion";
|
|
108
|
+
const personaName = "architect";
|
|
109
|
+
let persona;
|
|
110
|
+
try {
|
|
111
|
+
persona = loadForgePersona(personaName, cwd);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return {
|
|
115
|
+
verdict: "revision-required",
|
|
116
|
+
durationMs: Date.now() - startMs,
|
|
117
|
+
errorMessage: `architect persona not found`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const taskLines = [
|
|
121
|
+
`# Sprint Completion Review — ${sprintId}`,
|
|
122
|
+
``,
|
|
123
|
+
`Mode: ${mode}`,
|
|
124
|
+
`Completed tasks: ${completedTaskIds.join(", ") || "(none)"}`,
|
|
125
|
+
];
|
|
126
|
+
if (pausedAfterIndex !== undefined) {
|
|
127
|
+
taskLines.push(`Paused after task index: ${pausedAfterIndex}`);
|
|
128
|
+
}
|
|
129
|
+
taskLines.push(``, `Execute the materialized workflow at \`.forge/workflows/${workflowName}.md\`.`, `Do not emit any phase event yourself; the orchestrator owns event emission.`);
|
|
130
|
+
const task = taskLines.join("\n");
|
|
131
|
+
// Use a dedicated session id (NOT a taskId) so the thread-switcher renders it
|
|
132
|
+
// as a sprint-scoped chip distinct from per-task sessions.
|
|
133
|
+
const sessionId = `${sprintId}:ceremony`;
|
|
134
|
+
registry.startSession(sessionId);
|
|
135
|
+
registry.startPhase(sessionId, "ceremony", 0);
|
|
136
|
+
// Bridge: register ceremony phase in OrchestratorTree so the dashboard
|
|
137
|
+
// shows the ceremony as a leaf under the sprint root.
|
|
138
|
+
const tree = getOrchestratorTree();
|
|
139
|
+
tree.startNode(sessionId, { parentId: sprintId, label: "ceremony", kind: "leaf" });
|
|
140
|
+
let model;
|
|
141
|
+
let provider;
|
|
142
|
+
let errorMessage;
|
|
143
|
+
const observer = attachViewportObserver({
|
|
144
|
+
registry,
|
|
145
|
+
sessionId,
|
|
146
|
+
phaseRole: "ceremony",
|
|
147
|
+
displayRole: "ceremony",
|
|
148
|
+
beginHeader: `─── sprint ${sprintId} ceremony begin · ${personaName} ───`,
|
|
149
|
+
});
|
|
150
|
+
// Resolve model routing for the ceremony's architect persona (Plan 16 Slice 2).
|
|
151
|
+
// N-B-E: surface schema errors to caller (Decision 9 — orchestrators fail-fast).
|
|
152
|
+
// See doc/decisions/layered-config-error-policy.md.
|
|
153
|
+
const { merged: ceremonyModelConfig, errors: ceremonyCfgErrors } = loadLayeredConfig(cwd);
|
|
154
|
+
if (ceremonyCfgErrors.length > 0) {
|
|
155
|
+
for (const e of ceremonyCfgErrors) {
|
|
156
|
+
ctx.ui.notify(`× forge:run-sprint ceremony — forge-cli config schema error: ${e}`, "error");
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
verdict: "revision-required",
|
|
160
|
+
durationMs: Date.now() - startMs,
|
|
161
|
+
errorMessage: `forge-cli config schema errors: ${ceremonyCfgErrors.join("; ")}`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const ceremonyModelLookup = lookupPersonaModel(personaName, "default", ceremonyModelConfig);
|
|
165
|
+
try {
|
|
166
|
+
const result = await runForgeSubagent({
|
|
167
|
+
persona,
|
|
168
|
+
task,
|
|
169
|
+
cwd,
|
|
170
|
+
exportTag: `${sprintId}__ceremony`,
|
|
171
|
+
forgeRoot,
|
|
172
|
+
streamFn: streamFnFactory?.({ kind: "ceremony", persona: personaName }),
|
|
173
|
+
// Sprint-scoped prompt-cache key — every subagent spawned across
|
|
174
|
+
// the sprint (ceremonies + per-task phases) shares this namespace
|
|
175
|
+
// so the system-prompt + persona prefix stays warm.
|
|
176
|
+
cacheSessionId: `forge:${sprintId}`,
|
|
177
|
+
onEvent: observer.onEvent,
|
|
178
|
+
requestedModel: ceremonyModelLookup.model,
|
|
179
|
+
modelRegistry: ctx.modelRegistry,
|
|
180
|
+
customTools: forgeToolDefs ? getSubagentTools(forgeToolDefs, persona.name) : undefined,
|
|
181
|
+
});
|
|
182
|
+
model = result.model;
|
|
183
|
+
provider = result.provider;
|
|
184
|
+
if (result.exitCode !== 0) {
|
|
185
|
+
errorMessage = result.errorMessage ?? "architect subagent exited non-zero";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
const err = e;
|
|
190
|
+
errorMessage = err?.message ?? "runForgeSubagent threw";
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
registry.completeSession(sessionId, errorMessage ? "failed" : "completed");
|
|
194
|
+
tree.completeNode(sessionId, errorMessage ? "failed" : "completed");
|
|
195
|
+
}
|
|
196
|
+
// Parse verdict from store: did the architect actually transition the sprint?
|
|
197
|
+
// The store is the source of truth — verdict text in SPRINT_COMPLETION_REVIEW.md
|
|
198
|
+
// is human-readable but the store status is authoritative.
|
|
199
|
+
let verdict = "revision-required";
|
|
200
|
+
const readResult = spawnSync("node", [`${forgeRoot}/tools/store-cli.cjs`, "read", "sprint", sprintId], {
|
|
201
|
+
cwd,
|
|
202
|
+
encoding: "utf8",
|
|
203
|
+
});
|
|
204
|
+
if (readResult.status === 0) {
|
|
205
|
+
try {
|
|
206
|
+
const sprint = JSON.parse(readResult.stdout);
|
|
207
|
+
if (sprint.status === "completed")
|
|
208
|
+
verdict = "complete";
|
|
209
|
+
else if (sprint.status === "partially-completed")
|
|
210
|
+
verdict = "partial";
|
|
211
|
+
// else: status unchanged → revision-required
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// fall through with revision-required
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
verdict,
|
|
219
|
+
model,
|
|
220
|
+
provider,
|
|
221
|
+
durationMs: Date.now() - startMs,
|
|
222
|
+
errorMessage,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
const SPRINT_STATUS_KEY = "forge:run-sprint";
|
|
226
|
+
export function registerRunSprint(pi, options = {}) {
|
|
227
|
+
pi.registerCommand("forge:run-sprint", {
|
|
228
|
+
description: "Run all tasks in a sprint sequentially. " +
|
|
229
|
+
"Usage: /forge:run-sprint <SPRINT_ID>. " +
|
|
230
|
+
"Orchestrator archetype: delegates per-task execution to runTaskPipeline.",
|
|
231
|
+
async handler(args, ctx) {
|
|
232
|
+
const cwd = options.cwd ?? process.cwd();
|
|
233
|
+
let sprintId = args.trim();
|
|
234
|
+
if (!sprintId) {
|
|
235
|
+
ctx.ui.notify("× forge:run-sprint — sprint ID required. Usage: /forge:run-sprint <SPRINT_ID>", "error");
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
// Path traversal validation (advisory #3)
|
|
239
|
+
if (!validateId(sprintId)) {
|
|
240
|
+
ctx.ui.notify(`× forge:run-sprint — invalid sprint ID format: ${sprintId}`, "error");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, `run-sprint ${sprintId}: initializing…`);
|
|
244
|
+
// ── Discover forge config ────────────────────────────────────────
|
|
245
|
+
const forgeConfig = discoverForgeConfigCached(cwd);
|
|
246
|
+
if (!forgeConfig) {
|
|
247
|
+
ctx.ui.notify("× forge:run-sprint — no Forge project found at cwd. Run /forge:init first.", "error");
|
|
248
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const forgeRoot = forgeConfig.forgeRoot;
|
|
252
|
+
// ── Resolve sprint ID (prefix-normalize, suffix-match, NLP fallback) ──
|
|
253
|
+
// Handles unprefixed IDs like "S22" → "FORGE-S22".
|
|
254
|
+
// Issue #20: unprefixed entity IDs silently poisoned substitutions.
|
|
255
|
+
const toolDir = resolveToolDir(forgeRoot);
|
|
256
|
+
const resolvedSprintId = await resolveToCanonicalId(sprintId, toolDir, cwd, "sprint", {
|
|
257
|
+
ctx,
|
|
258
|
+
commandLabel: "forge:run-sprint",
|
|
259
|
+
});
|
|
260
|
+
if (!resolvedSprintId) {
|
|
261
|
+
// Error already emitted by resolver
|
|
262
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
// Replace raw arg with canonical ID for all subsequent operations.
|
|
266
|
+
sprintId = resolvedSprintId;
|
|
267
|
+
// Update status with canonical ID so the user sees the resolved form.
|
|
268
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, `run-sprint ${sprintId}: ready`);
|
|
269
|
+
const storeCli = path.join(forgeRoot, "tools", "store-cli.cjs");
|
|
270
|
+
const preflightGate = path.join(forgeRoot, "tools", "preflight-gate.cjs");
|
|
271
|
+
// ── Sprint resolution ────────────────────────────────────────────
|
|
272
|
+
const sprintRecord = readSprintRecord(sprintId, storeCli, cwd);
|
|
273
|
+
if (!sprintRecord) {
|
|
274
|
+
ctx.ui.notify(`× forge:run-sprint — could not read sprint ${sprintId} or sprint has no task IDs.`, "error");
|
|
275
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const taskIds = sprintRecord.taskIds;
|
|
279
|
+
ctx.ui.notify(`▶ forge:run-sprint — sprint ${sprintId}: ${taskIds.length} tasks`, "info");
|
|
280
|
+
// ── Audience check (AC B-12) ──────────────────────────────────────
|
|
281
|
+
// Read the run_sprint workflow for audience check.
|
|
282
|
+
const workflowPath = path.join(cwd, ".forge", "workflows", "run_sprint.md");
|
|
283
|
+
let workflowMd;
|
|
284
|
+
let workflowAudience = "any";
|
|
285
|
+
try {
|
|
286
|
+
const loaded = loadWorkflow(workflowPath);
|
|
287
|
+
workflowMd = loaded.rawMarkdown;
|
|
288
|
+
workflowAudience = loaded.audience;
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
// Workflow file may not exist — default to orchestrator-only since
|
|
292
|
+
// /forge:run-sprint is an orchestrator archetype command.
|
|
293
|
+
workflowMd = "";
|
|
294
|
+
workflowAudience = "orchestrator-only";
|
|
295
|
+
}
|
|
296
|
+
if (!assertAudience({ workflowName: "run_sprint", audience: workflowAudience }, ctx)) {
|
|
297
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
// ── Materialization-marker check (AC B-5) ────────────────────────
|
|
301
|
+
if (workflowMd) {
|
|
302
|
+
const markerCheck = checkMaterialization(workflowPath, workflowMd);
|
|
303
|
+
if (!markerCheck.ok) {
|
|
304
|
+
for (const marker of markerCheck.missing) {
|
|
305
|
+
ctx.ui.notify(`× workflow regression: ${marker} not found in ${workflowPath}`, "error");
|
|
306
|
+
}
|
|
307
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// ── Pre-flight confirm (AC B-7) ───────────────────────────────────
|
|
312
|
+
if (!isNonInteractive()) {
|
|
313
|
+
const proceed = await ctx.ui.confirm(`Begin sprint ${sprintId}?`, `Tasks: ${taskIds.join(", ")}`);
|
|
314
|
+
if (!proceed) {
|
|
315
|
+
ctx.ui.notify("forge:run-sprint — sprint aborted.", "info");
|
|
316
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// ── Sprint-level resume detection (AC B-11) ──────────────────────
|
|
321
|
+
const existingSprintState = readSprintState(cwd, sprintId);
|
|
322
|
+
let startTaskIndex = 0;
|
|
323
|
+
let completedTaskIds = [];
|
|
324
|
+
const resumeTaskStates = new Map();
|
|
325
|
+
if (existingSprintState) {
|
|
326
|
+
if (isSprintStateStale(existingSprintState)) {
|
|
327
|
+
ctx.ui.notify(`⚠ forge:run-sprint — cached sprint state for ${sprintId} is stale (>7 days old, saved at ${formatLocalTime(existingSprintState.savedAt)}). Offering purge.`, "warning");
|
|
328
|
+
if (!isNonInteractive()) {
|
|
329
|
+
const purge = await ctx.ui.confirm(`Purge stale sprint state for ${sprintId}?`, "The cached state is older than 7 days. Purge and restart from the beginning?");
|
|
330
|
+
if (purge) {
|
|
331
|
+
deleteSprintState(cwd, sprintId);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
ctx.ui.notify("forge:run-sprint — stale state kept; aborting.", "info");
|
|
335
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
// Non-interactive: auto-abort on stale state (advisory #9)
|
|
341
|
+
ctx.ui.notify("forge:run-sprint — stale sprint state; non-interactive mode auto-aborting.", "info");
|
|
342
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
// Fresh state: offer resume
|
|
348
|
+
if (!isNonInteractive()) {
|
|
349
|
+
const resume = await ctx.ui.confirm(`Resume sprint ${sprintId}?`, `Cached state found at task ${existingSprintState.taskIndex} (${existingSprintState.completedTaskIds.length} completed). Resume from here?`);
|
|
350
|
+
if (resume) {
|
|
351
|
+
startTaskIndex = existingSprintState.taskIndex;
|
|
352
|
+
completedTaskIds = existingSprintState.completedTaskIds;
|
|
353
|
+
ctx.ui.notify(`forge:run-sprint — resuming ${sprintId} from task ${taskIds[startTaskIndex] ?? startTaskIndex}`, "info");
|
|
354
|
+
// Collect halted and cancelled task states for mid-task resume
|
|
355
|
+
// (REVIEW FIX #2, option b; ADR-S21-01: cancelled states are
|
|
356
|
+
// resumable from the beginning of the cancelled phase).
|
|
357
|
+
for (const taskId of taskIds.slice(startTaskIndex)) {
|
|
358
|
+
const taskState = readTaskState(cwd, taskId);
|
|
359
|
+
if (taskState && (taskState.halted || taskState.status === "cancelled")) {
|
|
360
|
+
resumeTaskStates.set(taskId, taskState);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
deleteSprintState(cwd, sprintId);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
// Non-interactive + existing state: auto-abort (advisory #9)
|
|
370
|
+
ctx.ui.notify(`forge:run-sprint — cached sprint state for ${sprintId} found but non-interactive mode; aborting.`, "info");
|
|
371
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// ── Model routing pre-flight (Plan 16 Slice 3) ──────────────────────
|
|
377
|
+
// Validate routing config once at sprint start (before any LLM calls).
|
|
378
|
+
// N-B-E: surface schema errors first (Decision 9 — orchestrators fail-fast).
|
|
379
|
+
// See doc/decisions/layered-config-error-policy.md.
|
|
380
|
+
{
|
|
381
|
+
const { merged: modelRoutingConfig, errors: schemaErrors } = loadLayeredConfig(cwd);
|
|
382
|
+
if (schemaErrors.length > 0) {
|
|
383
|
+
for (const e of schemaErrors) {
|
|
384
|
+
ctx.ui.notify(`× forge:run-sprint — forge-cli config schema error: ${e}`, "error");
|
|
385
|
+
}
|
|
386
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const personasDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "forge-payload", ".base-pack", "personas");
|
|
390
|
+
const personaCatalogue = readPersonaDirSprint(personasDir);
|
|
391
|
+
const forgeCfgPath = path.join(cwd, ".forge", "config.json");
|
|
392
|
+
const pipelineCatalogue = readPipelineNamesSprint(forgeCfgPath);
|
|
393
|
+
const availableModels = ctx.modelRegistry?.getAvailable?.() ?? [];
|
|
394
|
+
const strict = process.env.FORGE_STRICT_MODELS === "1";
|
|
395
|
+
const { errors, warnings } = validateModelConfig(personaCatalogue, pipelineCatalogue, modelRoutingConfig, availableModels.map((m) => ({ provider: m.provider, id: m.id })), strict);
|
|
396
|
+
for (const w of warnings) {
|
|
397
|
+
ctx.ui.notify(`⚠ forge:run-sprint — model routing: ${w.message}`, "warning");
|
|
398
|
+
}
|
|
399
|
+
if (errors.length > 0) {
|
|
400
|
+
for (const e of errors) {
|
|
401
|
+
ctx.ui.notify(`× forge:run-sprint — model routing: ${e.message}`, "error");
|
|
402
|
+
}
|
|
403
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
// ── Per-task loop (AC B-8) ────────────────────────────────────────
|
|
408
|
+
const registry = getSessionRegistry();
|
|
409
|
+
const tree = getOrchestratorTree();
|
|
410
|
+
let lastModel;
|
|
411
|
+
let lastProvider;
|
|
412
|
+
const sprintStartMs = Date.now();
|
|
413
|
+
// Bridge: register sprint root in OrchestratorTree.
|
|
414
|
+
tree.startNode(sprintId, { label: `wfl:run-sprint`, kind: "orchestrator" });
|
|
415
|
+
for (let i = startTaskIndex; i < taskIds.length; i++) {
|
|
416
|
+
const taskId = taskIds[i];
|
|
417
|
+
if (!taskId)
|
|
418
|
+
continue;
|
|
419
|
+
// ── Skip already-completed tasks ──────────────────────────────
|
|
420
|
+
// If the task is already committed/approved in the store, skip the
|
|
421
|
+
// phase pipeline and accumulate it as completed. This handles
|
|
422
|
+
// re-runs where tasks finished in a prior attempt.
|
|
423
|
+
{
|
|
424
|
+
const taskReadResult = spawnSync("node", [`${forgeRoot}/tools/store-cli.cjs`, "read", "task", taskId], {
|
|
425
|
+
cwd,
|
|
426
|
+
encoding: "utf8",
|
|
427
|
+
});
|
|
428
|
+
if (taskReadResult.status === 0) {
|
|
429
|
+
try {
|
|
430
|
+
const taskRecord = JSON.parse(taskReadResult.stdout);
|
|
431
|
+
if (taskRecord.status === "committed" || taskRecord.status === "completed") {
|
|
432
|
+
ctx.ui.notify(`▶ ${sprintId}: task ${i + 1}/${taskIds.length} — ${taskId} already ${taskRecord.status}, skipping.`, "info");
|
|
433
|
+
completedTaskIds.push(taskId);
|
|
434
|
+
lastModel = taskRecord.model ?? lastModel;
|
|
435
|
+
lastProvider = taskRecord.provider ?? lastProvider;
|
|
436
|
+
writeSprintState(cwd, {
|
|
437
|
+
sprintId,
|
|
438
|
+
taskIndex: i + 1,
|
|
439
|
+
completedTaskIds,
|
|
440
|
+
halted: false,
|
|
441
|
+
savedAt: new Date().toISOString(),
|
|
442
|
+
});
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
// Malformed task record — fall through to runTaskPipeline
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, `run-sprint ${sprintId}: task ${i + 1}/${taskIds.length} (${taskId})`);
|
|
452
|
+
ctx.ui.notify(`▶ ${sprintId}: task ${i + 1}/${taskIds.length} — ${taskId}`, "info");
|
|
453
|
+
// Determine resumeFromState for mid-task resume (REVIEW FIX #2).
|
|
454
|
+
// If a halted or cancelled task state exists for this task,
|
|
455
|
+
// pass it to runTaskPipeline so it resumes from the saved phase.
|
|
456
|
+
let resumeFromState = resumeTaskStates.get(taskId);
|
|
457
|
+
if (resumeFromState) {
|
|
458
|
+
// Validate the state is not corrupt
|
|
459
|
+
if (typeof resumeFromState.phaseIndex !== "number" ||
|
|
460
|
+
typeof resumeFromState.iterationCounts !== "object") {
|
|
461
|
+
ctx.ui.notify(`⚠ forge:run-sprint — corrupt task state for ${taskId}; starting fresh.`, "warning");
|
|
462
|
+
resumeFromState = undefined;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (resumeFromState) {
|
|
466
|
+
const resumephaseRole = resumeFromState.phaseIndex;
|
|
467
|
+
const resumeStatus = resumeFromState.status ?? (resumeFromState.halted ? "halted" : "interrupted");
|
|
468
|
+
ctx.ui.notify(`▶ forge:run-sprint — resuming ${taskId} from phase ${resumephaseRole} (${resumeStatus})`, "info");
|
|
469
|
+
}
|
|
470
|
+
// Stale task state fallback: if task state >7d, delete and start fresh
|
|
471
|
+
if (resumeFromState && isStateStale(resumeFromState)) {
|
|
472
|
+
ctx.ui.notify(`⚠ forge:run-sprint — stale task state for ${taskId} (>7d); starting fresh.`, "warning");
|
|
473
|
+
resumeFromState = undefined;
|
|
474
|
+
}
|
|
475
|
+
// ── Session lifecycle for thread-switcher (REVIEW FIX #2) ──────
|
|
476
|
+
registry.startSession(taskId);
|
|
477
|
+
// Bridge: register task in OrchestratorTree under sprint root.
|
|
478
|
+
tree.startNode(taskId, { parentId: sprintId, label: `▸ wfl:run-task`, kind: "orchestrator" });
|
|
479
|
+
const taskSignal = registry.getAbortSignal(taskId);
|
|
480
|
+
const taskResult = await runTaskPipeline({
|
|
481
|
+
taskId,
|
|
482
|
+
cwd,
|
|
483
|
+
ctx,
|
|
484
|
+
forgeRoot,
|
|
485
|
+
storeCli,
|
|
486
|
+
preflightGate,
|
|
487
|
+
registry,
|
|
488
|
+
resumeFromState,
|
|
489
|
+
signal: taskSignal,
|
|
490
|
+
forgeToolDefs: options.forgeToolDefs,
|
|
491
|
+
extensionFactories: options.extensionFactories,
|
|
492
|
+
streamFnFactory: options.streamFnFactory
|
|
493
|
+
? (c) => options.streamFnFactory?.({
|
|
494
|
+
kind: "task-phase",
|
|
495
|
+
persona: c.persona,
|
|
496
|
+
phase: c.phase,
|
|
497
|
+
taskId: c.taskId,
|
|
498
|
+
})
|
|
499
|
+
: undefined,
|
|
500
|
+
});
|
|
501
|
+
// Capture model/provider from last task result (REVIEW FIX #1)
|
|
502
|
+
if (taskResult.model)
|
|
503
|
+
lastModel = taskResult.model;
|
|
504
|
+
if (taskResult.provider)
|
|
505
|
+
lastProvider = taskResult.provider;
|
|
506
|
+
// ── Handle task result ──────────────────────────────────────
|
|
507
|
+
if (taskResult.status === "completed") {
|
|
508
|
+
completedTaskIds.push(taskId);
|
|
509
|
+
registry.completeSession(taskId, "completed");
|
|
510
|
+
tree.completeNode(taskId, "completed");
|
|
511
|
+
}
|
|
512
|
+
else if (taskResult.status === "cancelled") {
|
|
513
|
+
// Task was cancelled by user — mark session, persist sprint
|
|
514
|
+
// state, emit sprint-halted with cancellation detail, exit.
|
|
515
|
+
registry.completeSession(taskId, "cancelled");
|
|
516
|
+
tree.completeNode(taskId, "cancelled");
|
|
517
|
+
tree.completeNode(sprintId, "cancelled");
|
|
518
|
+
const cancelledEvent = {
|
|
519
|
+
eventId: `${isoCompact(sprintStartMs)}_${sprintId}_sprint_halted`,
|
|
520
|
+
sprintId,
|
|
521
|
+
role: "orchestrator",
|
|
522
|
+
action: "sprint-halted",
|
|
523
|
+
startTimestamp: new Date(sprintStartMs).toISOString(),
|
|
524
|
+
endTimestamp: new Date(Date.now()).toISOString(),
|
|
525
|
+
durationMinutes: Math.round(((Date.now() - sprintStartMs) / 60000) * 100) / 100,
|
|
526
|
+
model: lastModel ?? "orchestrator",
|
|
527
|
+
provider: lastProvider ?? "orchestrator",
|
|
528
|
+
type: "sprint-halted",
|
|
529
|
+
haltedAtTaskIndex: i,
|
|
530
|
+
haltedAtTaskId: taskId,
|
|
531
|
+
lastError: "cancelled",
|
|
532
|
+
};
|
|
533
|
+
emitEvent(storeCli, cwd, sprintId, cancelledEvent);
|
|
534
|
+
writeSprintState(cwd, {
|
|
535
|
+
sprintId,
|
|
536
|
+
taskIndex: i,
|
|
537
|
+
completedTaskIds,
|
|
538
|
+
halted: true,
|
|
539
|
+
lastError: "cancelled",
|
|
540
|
+
savedAt: new Date().toISOString(),
|
|
541
|
+
});
|
|
542
|
+
ctx.ui.notify(`⊘ forge:run-sprint — ${sprintId} halted: task ${taskId} cancelled.`, "info");
|
|
543
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
// Task halted/escalated/failed: mark session failed, persist sprint state, emit sprint-halted, exit.
|
|
548
|
+
registry.completeSession(taskId, "failed");
|
|
549
|
+
tree.completeNode(taskId, "failed");
|
|
550
|
+
tree.completeNode(sprintId, "failed");
|
|
551
|
+
const haltedEvent = {
|
|
552
|
+
eventId: `${isoCompact(sprintStartMs)}_${sprintId}_sprint_halted`,
|
|
553
|
+
sprintId,
|
|
554
|
+
role: "orchestrator",
|
|
555
|
+
action: "sprint-halted",
|
|
556
|
+
startTimestamp: new Date(sprintStartMs).toISOString(),
|
|
557
|
+
endTimestamp: new Date(Date.now()).toISOString(),
|
|
558
|
+
durationMinutes: Math.round(((Date.now() - sprintStartMs) / 60000) * 100) / 100,
|
|
559
|
+
model: lastModel ?? "orchestrator",
|
|
560
|
+
provider: lastProvider ?? "orchestrator",
|
|
561
|
+
type: "sprint-halted",
|
|
562
|
+
haltedAtTaskIndex: i,
|
|
563
|
+
haltedAtTaskId: taskId,
|
|
564
|
+
lastError: taskResult.lastError ?? "unknown",
|
|
565
|
+
};
|
|
566
|
+
emitEvent(storeCli, cwd, sprintId, haltedEvent);
|
|
567
|
+
writeSprintState(cwd, {
|
|
568
|
+
sprintId,
|
|
569
|
+
taskIndex: i,
|
|
570
|
+
completedTaskIds,
|
|
571
|
+
halted: true,
|
|
572
|
+
lastError: taskResult.lastError,
|
|
573
|
+
savedAt: new Date().toISOString(),
|
|
574
|
+
});
|
|
575
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
// ── Post-task confirm (AC B-9) ────────────────────────────
|
|
579
|
+
// Skip after final task
|
|
580
|
+
if (i < taskIds.length - 1 && !isNonInteractive()) {
|
|
581
|
+
const proceed = await ctx.ui.confirm(`Continue to next task?`, `${taskIds[i + 1]} is next. ${taskIds.length - i - 1} task(s) remaining.`);
|
|
582
|
+
if (!proceed) {
|
|
583
|
+
// Persist sprint state for resume.
|
|
584
|
+
writeSprintState(cwd, {
|
|
585
|
+
sprintId,
|
|
586
|
+
taskIndex: i + 1,
|
|
587
|
+
completedTaskIds,
|
|
588
|
+
halted: false,
|
|
589
|
+
savedAt: new Date().toISOString(),
|
|
590
|
+
});
|
|
591
|
+
// User-paused branch: dispatch ceremony if ≥1 task completed, emit partial event.
|
|
592
|
+
const pauseEndMs = Date.now();
|
|
593
|
+
let ceremonyResult;
|
|
594
|
+
if (completedTaskIds.length > 0) {
|
|
595
|
+
// Only dispatch ceremony if at least one task completed.
|
|
596
|
+
// A zero-progress pause has nothing to review.
|
|
597
|
+
ceremonyResult = await dispatchSprintCeremony({
|
|
598
|
+
sprintId,
|
|
599
|
+
mode: "partial",
|
|
600
|
+
completedTaskIds,
|
|
601
|
+
pausedAfterIndex: i,
|
|
602
|
+
cwd,
|
|
603
|
+
forgeRoot,
|
|
604
|
+
ctx,
|
|
605
|
+
registry,
|
|
606
|
+
streamFnFactory: options.streamFnFactory,
|
|
607
|
+
forgeToolDefs: options.forgeToolDefs,
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
const pausedEvent = {
|
|
611
|
+
eventId: `${isoCompact(sprintStartMs)}_${sprintId}_sprint_complete`,
|
|
612
|
+
sprintId,
|
|
613
|
+
role: "architect",
|
|
614
|
+
action: "sprint-complete",
|
|
615
|
+
startTimestamp: new Date(sprintStartMs).toISOString(),
|
|
616
|
+
endTimestamp: new Date(pauseEndMs).toISOString(),
|
|
617
|
+
durationMinutes: Math.round(((pauseEndMs - sprintStartMs) / 60000) * 100) / 100,
|
|
618
|
+
model: ceremonyResult?.model ?? lastModel ?? "orchestrator",
|
|
619
|
+
provider: ceremonyResult?.provider ?? lastProvider ?? "orchestrator",
|
|
620
|
+
type: "sprint-complete",
|
|
621
|
+
taskCount: taskIds.length,
|
|
622
|
+
completedTaskIds,
|
|
623
|
+
verdict: "partial",
|
|
624
|
+
pausedAfterTaskIndex: i,
|
|
625
|
+
waveCount: 1,
|
|
626
|
+
maxConcurrency: 1,
|
|
627
|
+
};
|
|
628
|
+
emitEvent(storeCli, cwd, sprintId, pausedEvent);
|
|
629
|
+
tree.completeNode(sprintId, "completed");
|
|
630
|
+
ctx.ui.notify("forge:run-sprint — sprint paused after task completion.", "info");
|
|
631
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
// Persist sprint state after each task transition (AC B-10)
|
|
636
|
+
writeSprintState(cwd, {
|
|
637
|
+
sprintId,
|
|
638
|
+
taskIndex: i + 1,
|
|
639
|
+
completedTaskIds,
|
|
640
|
+
halted: false,
|
|
641
|
+
savedAt: new Date().toISOString(),
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
// ── All tasks complete — clean-complete branch (Plan 12 §3) ──────
|
|
645
|
+
const sprintEndMs = Date.now();
|
|
646
|
+
// Delete sprint state on successful completion
|
|
647
|
+
deleteSprintState(cwd, sprintId);
|
|
648
|
+
const ceremony = await dispatchSprintCeremony({
|
|
649
|
+
sprintId,
|
|
650
|
+
mode: "complete",
|
|
651
|
+
completedTaskIds,
|
|
652
|
+
cwd,
|
|
653
|
+
forgeRoot,
|
|
654
|
+
ctx,
|
|
655
|
+
registry,
|
|
656
|
+
streamFnFactory: options.streamFnFactory,
|
|
657
|
+
forgeToolDefs: options.forgeToolDefs,
|
|
658
|
+
});
|
|
659
|
+
const sprintEvent = {
|
|
660
|
+
eventId: `${isoCompact(sprintStartMs)}_${sprintId}_sprint_complete`,
|
|
661
|
+
sprintId,
|
|
662
|
+
role: "architect",
|
|
663
|
+
action: "sprint-complete",
|
|
664
|
+
startTimestamp: new Date(sprintStartMs).toISOString(),
|
|
665
|
+
endTimestamp: new Date(sprintEndMs).toISOString(),
|
|
666
|
+
durationMinutes: Math.round(((sprintEndMs - sprintStartMs) / 60000) * 100) / 100,
|
|
667
|
+
model: ceremony.model ?? lastModel ?? "orchestrator",
|
|
668
|
+
provider: ceremony.provider ?? lastProvider ?? "orchestrator",
|
|
669
|
+
type: "sprint-complete",
|
|
670
|
+
taskCount: taskIds.length,
|
|
671
|
+
completedTaskIds,
|
|
672
|
+
verdict: ceremony.verdict === "revision-required" ? "partial" : ceremony.verdict,
|
|
673
|
+
waveCount: 1,
|
|
674
|
+
maxConcurrency: 1,
|
|
675
|
+
};
|
|
676
|
+
const emitResult = emitEvent(storeCli, cwd, sprintId, sprintEvent);
|
|
677
|
+
if (!emitResult.ok) {
|
|
678
|
+
ctx.ui.notify(`⚠ forge:run-sprint — sprint-complete event emit failed: ${emitResult.stderr.trim()}`, "warning");
|
|
679
|
+
}
|
|
680
|
+
// ── Emit synthetic sprint-collate-complete event (FORGE-S21-T05) ──
|
|
681
|
+
// Fires the in-process hook for post-sprint-hook.ts to consume.
|
|
682
|
+
// Best-effort: failure-to-emit notifies but does NOT halt — sprint is
|
|
683
|
+
// already complete at this point.
|
|
684
|
+
try {
|
|
685
|
+
const collateEvent = {
|
|
686
|
+
type: "sprint-collate-complete",
|
|
687
|
+
sprintId,
|
|
688
|
+
cwd,
|
|
689
|
+
};
|
|
690
|
+
await emitSyntheticEvent(collateEvent, ctx);
|
|
691
|
+
}
|
|
692
|
+
catch (err) {
|
|
693
|
+
const e = err;
|
|
694
|
+
ctx.ui.notify(`⚠ forge:run-sprint — sprint-collate-complete synthetic event emit failed: ${e.message ?? "unknown"}`, "warning");
|
|
695
|
+
}
|
|
696
|
+
ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
|
|
697
|
+
// Sprint root: mark completed in the tree. "revision-required" → failed
|
|
698
|
+
// because the architect rejected the sprint; partial → completed (the
|
|
699
|
+
// sprint itself finished, the progress indicator shows N/M tasks done).
|
|
700
|
+
const sprintTreeStatus = ceremony.verdict === "revision-required" ? "failed" : "completed";
|
|
701
|
+
tree.completeNode(sprintId, sprintTreeStatus);
|
|
702
|
+
if (ceremony.verdict === "complete") {
|
|
703
|
+
ctx.ui.notify(`〇 forge:run-sprint — sprint ${sprintId} complete (${completedTaskIds.length}/${taskIds.length} tasks).`, "info");
|
|
704
|
+
}
|
|
705
|
+
else if (ceremony.verdict === "revision-required") {
|
|
706
|
+
// Architect did not approve; surface to user.
|
|
707
|
+
ctx.ui.notify(`▲ forge:run-sprint — sprint ${sprintId} ceremony returned "Revision Required". ` +
|
|
708
|
+
`See engineering/sprints/${sprintId}/SPRINT_COMPLETION_REVIEW.md.`, "warning");
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
ctx.ui.notify(`▲ forge:run-sprint — sprint ${sprintId} marked partially-completed by architect.`, "warning");
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
//# sourceMappingURL=run-sprint.js.map
|