prizmkit 1.1.100 → 1.1.102
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/bin/create-prizmkit.js +4 -4
- package/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/.env.example +3 -3
- package/bundled/dev-pipeline/README.md +110 -90
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +1 -1
- package/bundled/dev-pipeline/cli.py +27 -0
- package/bundled/dev-pipeline/prizmkit_runtime/__init__.py +13 -0
- package/bundled/dev-pipeline/prizmkit_runtime/__main__.py +9 -0
- package/bundled/dev-pipeline/prizmkit_runtime/cli.py +193 -0
- package/bundled/dev-pipeline/prizmkit_runtime/commands.py +313 -0
- package/bundled/dev-pipeline/prizmkit_runtime/compat.py +32 -0
- package/bundled/dev-pipeline/prizmkit_runtime/config.py +410 -0
- package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +478 -0
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +734 -0
- package/bundled/dev-pipeline/prizmkit_runtime/heartbeat.py +256 -0
- package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +109 -0
- package/bundled/dev-pipeline/prizmkit_runtime/paths.py +110 -0
- package/bundled/dev-pipeline/prizmkit_runtime/processes.py +373 -0
- package/bundled/dev-pipeline/prizmkit_runtime/reset.py +389 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +51 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +179 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +355 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +118 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +187 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_status.py +153 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +425 -0
- package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +560 -0
- package/bundled/dev-pipeline/prizmkit_runtime/status.py +71 -0
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +51 -36
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +20 -5
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +2 -2
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +39 -0
- package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +8 -2
- package/bundled/dev-pipeline/templates/agent-prompts/reviewer-review.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +6 -2
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +14 -6
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +14 -4
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +14 -4
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +2 -0
- package/bundled/dev-pipeline/templates/sections/log-size-awareness.md +9 -30
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +15 -7
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +42 -29
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +41 -39
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +8 -17
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +1 -3
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +1 -3
- package/bundled/dev-pipeline/templates/sections/phase0-test-baseline.md +3 -8
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +6 -2
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +5 -1
- package/bundled/dev-pipeline/tests/test_auto_skip.py +436 -14
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +115 -7
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +40 -0
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +1226 -0
- package/bundled/dev-pipeline/tests/test_unified_cli.py +1045 -0
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +29 -34
- package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +6 -5
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +29 -35
- package/bundled/skills/feature-pipeline-launcher/references/configuration.md +7 -6
- package/bundled/skills/feature-workflow/SKILL.md +3 -6
- package/bundled/skills/recovery-workflow/SKILL.md +23 -32
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +29 -35
- package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +7 -6
- package/bundled/skills/refactor-workflow/SKILL.md +3 -6
- package/bundled/templates/hooks/commit-intent.json +2 -2
- package/bundled/templates/hooks/diff-prizm-docs.py +226 -0
- package/bundled/templates/hooks/prizm-pre-commit.py +45 -0
- package/bundled/templates/hooks/validate-prizm-docs.py +240 -0
- package/package.json +1 -1
- package/src/config.js +42 -19
- package/src/external-skills.js +14 -14
- package/src/index.js +3 -6
- package/src/manifest.js +11 -5
- package/src/metadata.js +7 -18
- package/src/platforms.js +25 -0
- package/src/prompts.js +0 -12
- package/src/runtimes.js +18 -11
- package/src/scaffold.js +144 -122
- package/src/upgrade.js +12 -3
- package/bundled/dev-pipeline/launch-bugfix-daemon.sh +0 -500
- package/bundled/dev-pipeline/launch-feature-daemon.sh +0 -657
- package/bundled/dev-pipeline/launch-refactor-daemon.sh +0 -502
- package/bundled/dev-pipeline/lib/branch.sh +0 -326
- package/bundled/dev-pipeline/lib/common.sh +0 -1476
- package/bundled/dev-pipeline/lib/heartbeat.sh +0 -469
- package/bundled/dev-pipeline/reset-bug.sh +0 -425
- package/bundled/dev-pipeline/reset-feature.sh +0 -436
- package/bundled/dev-pipeline/reset-refactor.sh +0 -423
- package/bundled/dev-pipeline/run-bugfix.sh +0 -1430
- package/bundled/dev-pipeline/run-feature.sh +0 -1718
- package/bundled/dev-pipeline/run-recovery.sh +0 -648
- package/bundled/dev-pipeline/run-refactor.sh +0 -1463
- package/bundled/dev-pipeline/tests/test-path-resolution.sh +0 -100
- package/bundled/dev-pipeline-windows/.env.example +0 -36
- package/bundled/dev-pipeline-windows/README.md +0 -30
- package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +0 -533
- package/bundled/dev-pipeline-windows/assets/feature-list-example.json +0 -146
- package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +0 -137
- package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +0 -9
- package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +0 -9
- package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +0 -9
- package/bundled/dev-pipeline-windows/lib/branch.ps1 +0 -235
- package/bundled/dev-pipeline-windows/lib/common.ps1 +0 -991
- package/bundled/dev-pipeline-windows/lib/daemon.ps1 +0 -140
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +0 -1051
- package/bundled/dev-pipeline-windows/lib/reset.ps1 +0 -121
- package/bundled/dev-pipeline-windows/reset-bug.ps1 +0 -10
- package/bundled/dev-pipeline-windows/reset-feature.ps1 +0 -10
- package/bundled/dev-pipeline-windows/reset-refactor.ps1 +0 -10
- package/bundled/dev-pipeline-windows/run-bugfix.ps1 +0 -9
- package/bundled/dev-pipeline-windows/run-feature.ps1 +0 -9
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +0 -179
- package/bundled/dev-pipeline-windows/run-refactor.ps1 +0 -9
- package/bundled/dev-pipeline-windows/scripts/check-session-status.py +0 -301
- package/bundled/dev-pipeline-windows/scripts/continuation.py +0 -374
- package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +0 -530
- package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +0 -2279
- package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +0 -763
- package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +0 -805
- package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +0 -841
- package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +0 -316
- package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +0 -134
- package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +0 -382
- package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +0 -399
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +0 -1362
- package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +0 -191
- package/bundled/dev-pipeline-windows/scripts/prizmkit-test-gate.py +0 -446
- package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +0 -1121
- package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +0 -173
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +0 -1789
- package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +0 -1327
- package/bundled/dev-pipeline-windows/scripts/utils.py +0 -542
- package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +0 -7
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +0 -7
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +0 -65
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +0 -5
- package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +0 -7
- package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +0 -73
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +0 -589
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +0 -710
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +0 -791
- package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +0 -263
- package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +0 -326
- package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +0 -237
- package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +0 -340
- package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +0 -270
- package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +0 -7
- package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +0 -91
- package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +0 -34
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +0 -10
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +0 -12
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +0 -7
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +0 -8
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +0 -9
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +0 -6
- package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +0 -21
- package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +0 -21
- package/bundled/dev-pipeline-windows/templates/sections/log-size-awareness.md +0 -41
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +0 -283
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +0 -112
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +0 -168
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +0 -82
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +0 -75
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +0 -23
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +0 -14
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +0 -19
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +0 -63
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +0 -42
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +0 -44
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +0 -59
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +0 -39
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +0 -27
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +0 -27
- package/bundled/dev-pipeline-windows/templates/sections/phase-prizmkit-test.md +0 -39
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +0 -35
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +0 -35
- package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +0 -73
- package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +0 -13
- package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +0 -21
- package/bundled/dev-pipeline-windows/templates/sections/session-context.md +0 -5
- package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +0 -6
- package/bundled/dev-pipeline-windows/templates/sections/task-contract.md +0 -34
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +0 -48
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +0 -48
- package/bundled/dev-pipeline-windows/templates/session-status-schema.json +0 -83
- package/bundled/skills-windows/app-planner/SKILL.md +0 -296
- package/bundled/skills-windows/app-planner/assets/app-design-guide.md +0 -101
- package/bundled/skills-windows/app-planner/references/architecture-decisions.md +0 -52
- package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +0 -101
- package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +0 -71
- package/bundled/skills-windows/app-planner/references/infrastructure-convention-discovery.md +0 -108
- package/bundled/skills-windows/app-planner/references/project-brief-guide.md +0 -82
- package/bundled/skills-windows/app-planner/references/project-conventions-discovery.md +0 -59
- package/bundled/skills-windows/app-planner/references/project-state-detection.md +0 -90
- package/bundled/skills-windows/app-planner/references/red-team-checklist.md +0 -40
- package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +0 -619
- package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +0 -285
- package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +0 -249
- package/bundled/skills-windows/app-planner/references/rules/backend/question-manifest.json +0 -46
- package/bundled/skills-windows/app-planner/references/rules/backend/template.md +0 -173
- package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +0 -382
- package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +0 -211
- package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +0 -184
- package/bundled/skills-windows/app-planner/references/rules/database/question-manifest.json +0 -39
- package/bundled/skills-windows/app-planner/references/rules/database/template.md +0 -158
- package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +0 -820
- package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +0 -188
- package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +0 -319
- package/bundled/skills-windows/app-planner/references/rules/frontend/question-manifest.json +0 -51
- package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +0 -339
- package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +0 -649
- package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +0 -290
- package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +0 -232
- package/bundled/skills-windows/app-planner/references/rules/mobile/question-manifest.json +0 -47
- package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +0 -175
- package/bundled/skills-windows/app-planner/references/rules-configuration.md +0 -46
- package/bundled/skills-windows/bug-fix-workflow/SKILL.md +0 -379
- package/bundled/skills-windows/bug-fix-workflow/references/bug-diagnosis.md +0 -41
- package/bundled/skills-windows/bug-planner/SKILL.md +0 -402
- package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +0 -43
- package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +0 -44
- package/bundled/skills-windows/bug-planner/references/error-recovery.md +0 -73
- package/bundled/skills-windows/bug-planner/references/input-formats.md +0 -53
- package/bundled/skills-windows/bug-planner/references/schema-validation.md +0 -25
- package/bundled/skills-windows/bug-planner/references/severity-rules.md +0 -16
- package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +0 -322
- package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +0 -304
- package/bundled/skills-windows/bugfix-pipeline-launcher/references/configuration.md +0 -84
- package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +0 -381
- package/bundled/skills-windows/feature-pipeline-launcher/references/configuration.md +0 -67
- package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +0 -462
- package/bundled/skills-windows/feature-planner/SKILL.md +0 -397
- package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +0 -64
- package/bundled/skills-windows/feature-planner/assets/planning-guide.md +0 -214
- package/bundled/skills-windows/feature-planner/references/browser-interaction.md +0 -59
- package/bundled/skills-windows/feature-planner/references/completeness-review.md +0 -57
- package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +0 -75
- package/bundled/skills-windows/feature-planner/references/error-recovery.md +0 -90
- package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +0 -112
- package/bundled/skills-windows/feature-planner/references/new-project-planning.md +0 -85
- package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +0 -1020
- package/bundled/skills-windows/feature-workflow/SKILL.md +0 -374
- package/bundled/skills-windows/feature-workflow/references/brainstorm-guide.md +0 -137
- package/bundled/skills-windows/prizm-kit/SKILL.md +0 -93
- package/bundled/skills-windows/prizmkit-code-review/SKILL.md +0 -161
- package/bundled/skills-windows/prizmkit-code-review/references/dev-agent-prompt.md +0 -30
- package/bundled/skills-windows/prizmkit-code-review/references/review-report-template.md +0 -31
- package/bundled/skills-windows/prizmkit-code-review/references/reviewer-agent-prompt.md +0 -66
- package/bundled/skills-windows/prizmkit-code-review/scripts/check_loop.py +0 -186
- package/bundled/skills-windows/prizmkit-committer/SKILL.md +0 -89
- package/bundled/skills-windows/prizmkit-deploy/SKILL.md +0 -444
- package/bundled/skills-windows/prizmkit-deploy/references/ci-cd-workflows.md +0 -115
- package/bundled/skills-windows/prizmkit-deploy/references/cloud-platform-deploy.md +0 -93
- package/bundled/skills-windows/prizmkit-deploy/references/data-safety-examples.md +0 -120
- package/bundled/skills-windows/prizmkit-deploy/references/database-setup.md +0 -46
- package/bundled/skills-windows/prizmkit-deploy/references/deploy-config-schema.md +0 -148
- package/bundled/skills-windows/prizmkit-deploy/references/deploy-history-schema.md +0 -62
- package/bundled/skills-windows/prizmkit-deploy/references/deployment-modes.md +0 -50
- package/bundled/skills-windows/prizmkit-deploy/references/direct-upload.md +0 -26
- package/bundled/skills-windows/prizmkit-deploy/references/dns-setup.md +0 -42
- package/bundled/skills-windows/prizmkit-deploy/references/docker-deploy.md +0 -31
- package/bundled/skills-windows/prizmkit-deploy/references/firewall-setup.md +0 -37
- package/bundled/skills-windows/prizmkit-deploy/references/live-validation-notes.md +0 -21
- package/bundled/skills-windows/prizmkit-deploy/references/nginx-blue-green.md +0 -59
- package/bundled/skills-windows/prizmkit-deploy/references/ssh-bootstrap-flow.md +0 -49
- package/bundled/skills-windows/prizmkit-deploy/references/ssh-execution-flow.md +0 -41
- package/bundled/skills-windows/prizmkit-deploy/references/ssh-takeover.md +0 -20
- package/bundled/skills-windows/prizmkit-deploy/references/ssl-setup.md +0 -56
- package/bundled/skills-windows/prizmkit-implement/SKILL.md +0 -71
- package/bundled/skills-windows/prizmkit-init/SKILL.md +0 -356
- package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +0 -82
- package/bundled/skills-windows/prizmkit-init/references/config-schema.md +0 -70
- package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +0 -41
- package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +0 -13
- package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +0 -9
- package/bundled/skills-windows/prizmkit-plan/SKILL.md +0 -102
- package/bundled/skills-windows/prizmkit-plan/assets/plan-template.md +0 -115
- package/bundled/skills-windows/prizmkit-plan/assets/spec-template.md +0 -73
- package/bundled/skills-windows/prizmkit-plan/references/clarify-guide.md +0 -67
- package/bundled/skills-windows/prizmkit-plan/references/examples.md +0 -85
- package/bundled/skills-windows/prizmkit-plan/references/verification-checklist.md +0 -60
- package/bundled/skills-windows/prizmkit-prizm-docs/SKILL.md +0 -129
- package/bundled/skills-windows/prizmkit-prizm-docs/assets/prizm-docs-format.md +0 -613
- package/bundled/skills-windows/prizmkit-prizm-docs/references/op-init.md +0 -45
- package/bundled/skills-windows/prizmkit-prizm-docs/references/op-rebuild.md +0 -15
- package/bundled/skills-windows/prizmkit-prizm-docs/references/op-status.md +0 -14
- package/bundled/skills-windows/prizmkit-prizm-docs/references/op-update.md +0 -19
- package/bundled/skills-windows/prizmkit-prizm-docs/references/op-validate.md +0 -17
- package/bundled/skills-windows/prizmkit-retrospective/SKILL.md +0 -87
- package/bundled/skills-windows/prizmkit-retrospective/references/knowledge-injection-steps.md +0 -50
- package/bundled/skills-windows/prizmkit-retrospective/references/structural-sync-steps.md +0 -43
- package/bundled/skills-windows/prizmkit-test/SKILL.md +0 -227
- package/bundled/skills-windows/prizmkit-test/references/boundary-coverage-protocol.md +0 -220
- package/bundled/skills-windows/prizmkit-test/references/examples.md +0 -143
- package/bundled/skills-windows/prizmkit-test/references/service-boundary-test-catalog.md +0 -225
- package/bundled/skills-windows/prizmkit-test/references/test-generation-steps.md +0 -109
- package/bundled/skills-windows/prizmkit-test/references/test-report-template.md +0 -111
- package/bundled/skills-windows/prizmkit-test/scripts/validate_boundary_report.py +0 -347
- package/bundled/skills-windows/recovery-workflow/SKILL.md +0 -355
- package/bundled/skills-windows/recovery-workflow/evals/evals.json +0 -46
- package/bundled/skills-windows/recovery-workflow/references/detection.md +0 -58
- package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +0 -544
- package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +0 -346
- package/bundled/skills-windows/refactor-pipeline-launcher/references/configuration.md +0 -72
- package/bundled/skills-windows/refactor-planner/SKILL.md +0 -398
- package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +0 -292
- package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +0 -301
- package/bundled/skills-windows/refactor-planner/references/fast-path.md +0 -59
- package/bundled/skills-windows/refactor-planner/references/planning-phases.md +0 -135
- package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +0 -221
- package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +0 -858
- package/bundled/skills-windows/refactor-workflow/SKILL.md +0 -343
- package/bundled/skills-windows/refactor-workflow/references/brainstorm-guide.md +0 -116
- package/bundled/templates/hooks/diff-prizm-docs.sh +0 -158
- package/bundled/templates/hooks/prizm-pre-commit.sh +0 -108
- package/bundled/templates/hooks/validate-prizm-docs.sh +0 -139
|
@@ -1,1718 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
# ============================================================
|
|
5
|
-
# dev-pipeline/run-feature.sh - Autonomous Dev Pipeline Runner
|
|
6
|
-
#
|
|
7
|
-
# Drives the prizm-dev-team multi-agent team through iterative
|
|
8
|
-
# AI CLI sessions (CodeBuddy or Claude Code) to build a complete app
|
|
9
|
-
# from a feature list.
|
|
10
|
-
#
|
|
11
|
-
# Usage:
|
|
12
|
-
# ./run-feature.sh run [.prizmkit/plans/feature-list.json] [options] Run all features
|
|
13
|
-
# ./run-feature.sh run <feature-id> [options] Run a single feature
|
|
14
|
-
# ./run-feature.sh status [.prizmkit/plans/feature-list.json] Show pipeline status
|
|
15
|
-
# ./run-feature.sh reset Clear all state
|
|
16
|
-
#
|
|
17
|
-
# Environment Variables:
|
|
18
|
-
# MAX_RETRIES Max code retries per feature (default: 3)
|
|
19
|
-
# MAX_INFRA_RETRIES Max infrastructure/provider retries per feature (default: 3)
|
|
20
|
-
# AI_CLI AI CLI command name (override; also readable from .prizmkit/config.json)
|
|
21
|
-
# CODEBUDDY_CLI Legacy alias for AI_CLI (deprecated, use AI_CLI instead)
|
|
22
|
-
# PRIZMKIT_PLATFORM Platform hint for custom AI_CLI wrappers: codebuddy, claude, or codex
|
|
23
|
-
# MODEL AI model to use (e.g. claude-opus-4.6, claude-sonnet-4.6, claude-haiku-4.5)
|
|
24
|
-
# VERBOSE Set to 1 to enable --verbose on AI CLI (shows subagent output)
|
|
25
|
-
# HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)
|
|
26
|
-
# STALE_KILL_THRESHOLD Auto-kill session after N seconds of no progress (default: 900)
|
|
27
|
-
# HEARTBEAT_STALE_THRESHOLD Heartbeat stale threshold in seconds (default: 600)
|
|
28
|
-
# MAX_LOG_SIZE Include passive log-size checkpoint guidance above this threshold (default: 2097152 = 2MB, set 0 to disable)
|
|
29
|
-
# PIPELINE_MODE Override mode for all features: lite|standard|full (used by daemon)
|
|
30
|
-
# DEV_BRANCH Custom dev branch name (default: auto-generated dev/{feature_id}-YYYYMMDDHHmm)
|
|
31
|
-
# AUTO_PUSH Auto-push to remote after successful feature (default: 0). Set to 1 to enable.
|
|
32
|
-
# STOP_ON_FAILURE Stop pipeline after a task exhausts all retries (default: 0). Set to 1 to stop.
|
|
33
|
-
# ============================================================
|
|
34
|
-
|
|
35
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
36
|
-
|
|
37
|
-
# Source shared common helpers (exports PIPELINE_DIR/PRIZMKIT_DIR/PROJECT_ROOT,
|
|
38
|
-
# CLI/platform detection, logs, deps)
|
|
39
|
-
source "$SCRIPT_DIR/lib/common.sh"
|
|
40
|
-
|
|
41
|
-
STATE_DIR="${PRIZMKIT_DIR}/state/features"
|
|
42
|
-
SCRIPTS_DIR="$SCRIPT_DIR/scripts"
|
|
43
|
-
|
|
44
|
-
# Configuration (override via environment variables)
|
|
45
|
-
MAX_RETRIES=${MAX_RETRIES:-3}
|
|
46
|
-
MAX_INFRA_RETRIES=${MAX_INFRA_RETRIES:-3}
|
|
47
|
-
HEARTBEAT_STALE_THRESHOLD=${HEARTBEAT_STALE_THRESHOLD:-600}
|
|
48
|
-
HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-30}
|
|
49
|
-
STALE_KILL_THRESHOLD=${STALE_KILL_THRESHOLD:-900}
|
|
50
|
-
VERBOSE=${VERBOSE:-0}
|
|
51
|
-
MODEL=${MODEL:-""}
|
|
52
|
-
PRIZMKIT_EFFORT=${PRIZMKIT_EFFORT:-""}
|
|
53
|
-
DEV_BRANCH=${DEV_BRANCH:-""}
|
|
54
|
-
AUTO_PUSH=${AUTO_PUSH:-0}
|
|
55
|
-
STOP_ON_FAILURE=${STOP_ON_FAILURE:-0}
|
|
56
|
-
ENABLE_DEPLOY=${ENABLE_DEPLOY:-0}
|
|
57
|
-
|
|
58
|
-
# Source shared common helpers (CLI/platform detection + logs + deps)
|
|
59
|
-
prizm_detect_cli_and_platform
|
|
60
|
-
|
|
61
|
-
# Validate PRIZMKIT_EFFORT early (fail-fast before any sessions are spawned)
|
|
62
|
-
prizm_validate_effort
|
|
63
|
-
|
|
64
|
-
# Source shared heartbeat library
|
|
65
|
-
source "$SCRIPT_DIR/lib/heartbeat.sh"
|
|
66
|
-
|
|
67
|
-
# Source shared branch library
|
|
68
|
-
source "$SCRIPT_DIR/lib/branch.sh"
|
|
69
|
-
|
|
70
|
-
# Detect stream-json support
|
|
71
|
-
detect_stream_json_support "$CLI_CMD"
|
|
72
|
-
|
|
73
|
-
# Feature list path (set in main, used by cleanup trap)
|
|
74
|
-
FEATURE_LIST=""
|
|
75
|
-
|
|
76
|
-
# Branch tracking (for cleanup on interrupt)
|
|
77
|
-
_ORIGINAL_BRANCH=""
|
|
78
|
-
_DEV_BRANCH_NAME=""
|
|
79
|
-
_SPAWN_FEATURE_SLUG=""
|
|
80
|
-
_SPAWN_EXIT_CODE=0
|
|
81
|
-
|
|
82
|
-
# ============================================================
|
|
83
|
-
# Shared: Spawn an AI CLI session and wait for result
|
|
84
|
-
# ============================================================
|
|
85
|
-
|
|
86
|
-
# Spawns an AI CLI session with heartbeat + timeout, waits for completion,
|
|
87
|
-
# and checks session status. Canonical status updates happen after the caller
|
|
88
|
-
# returns to the original branch.
|
|
89
|
-
#
|
|
90
|
-
# Arguments:
|
|
91
|
-
# $1 - feature_id
|
|
92
|
-
# $2 - feature_list (absolute path)
|
|
93
|
-
# $3 - session_id
|
|
94
|
-
# $4 - bootstrap_prompt (path)
|
|
95
|
-
# $5 - session_dir
|
|
96
|
-
# $6 - max_retries (for status update)
|
|
97
|
-
spawn_and_wait_session() {
|
|
98
|
-
local feature_id="$1"
|
|
99
|
-
local feature_list="$2"
|
|
100
|
-
local session_id="$3"
|
|
101
|
-
local bootstrap_prompt="$4"
|
|
102
|
-
local session_dir="$5"
|
|
103
|
-
local max_retries="$6"
|
|
104
|
-
local feature_model="${7:-}"
|
|
105
|
-
local base_branch="${8:-main}"
|
|
106
|
-
|
|
107
|
-
local session_log="$session_dir/logs/session.log"
|
|
108
|
-
local progress_json="$session_dir/logs/progress.json"
|
|
109
|
-
|
|
110
|
-
_SPAWN_FEATURE_SLUG=""
|
|
111
|
-
_SPAWN_EXIT_CODE=0
|
|
112
|
-
|
|
113
|
-
local effective_model="${feature_model:-$MODEL}"
|
|
114
|
-
local cbc_pid
|
|
115
|
-
prizm_start_ai_session "$bootstrap_prompt" "$session_log" "$effective_model"
|
|
116
|
-
cbc_pid="$PRIZM_AI_PID"
|
|
117
|
-
|
|
118
|
-
# Start progress parser (no-op if stream-json not supported)
|
|
119
|
-
start_progress_parser "$session_log" "$progress_json" "$SCRIPTS_DIR"
|
|
120
|
-
local parser_pid="${_PARSER_PID:-}"
|
|
121
|
-
|
|
122
|
-
# Heartbeat monitor (reads progress.json when available, falls back to tail)
|
|
123
|
-
# Also monitors for stale sessions and auto-kills if no progress for STALE_KILL_THRESHOLD seconds
|
|
124
|
-
start_heartbeat "$cbc_pid" "$session_log" "$progress_json" "$HEARTBEAT_INTERVAL" "$STALE_KILL_THRESHOLD"
|
|
125
|
-
local heartbeat_pid="${_HEARTBEAT_PID:-}"
|
|
126
|
-
|
|
127
|
-
# Wait for AI CLI to finish
|
|
128
|
-
local exit_code=0
|
|
129
|
-
if wait "$cbc_pid" 2>/dev/null; then
|
|
130
|
-
exit_code=0
|
|
131
|
-
else
|
|
132
|
-
exit_code=$?
|
|
133
|
-
fi
|
|
134
|
-
|
|
135
|
-
# Clean up heartbeat and parser
|
|
136
|
-
stop_heartbeat "$heartbeat_pid"
|
|
137
|
-
stop_progress_parser "$parser_pid"
|
|
138
|
-
|
|
139
|
-
# Recover session log from backup if truncated by subagent stdout redirection
|
|
140
|
-
prizm_recover_session_log "$session_log"
|
|
141
|
-
|
|
142
|
-
_SPAWN_EXIT_CODE="$exit_code"
|
|
143
|
-
|
|
144
|
-
# Check for stale-kill marker (heartbeat killed the process due to no progress)
|
|
145
|
-
local stale_kill_marker="$session_dir/logs/stale-kill.json"
|
|
146
|
-
local was_stale_killed=false
|
|
147
|
-
if [[ -f "$stale_kill_marker" ]]; then
|
|
148
|
-
was_stale_killed=true
|
|
149
|
-
log_warn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
150
|
-
fi
|
|
151
|
-
|
|
152
|
-
local was_context_overflow=false
|
|
153
|
-
if prizm_detect_context_overflow_error "$session_log" "$progress_json"; then
|
|
154
|
-
was_context_overflow=true
|
|
155
|
-
fi
|
|
156
|
-
|
|
157
|
-
local was_infra_error=false
|
|
158
|
-
if [[ $exit_code -ne 0 ]] && prizm_detect_infra_error "$session_log" "$progress_json"; then
|
|
159
|
-
was_infra_error=true
|
|
160
|
-
fi
|
|
161
|
-
|
|
162
|
-
# Show final session summary
|
|
163
|
-
if [[ -f "$session_log" ]]; then
|
|
164
|
-
local final_size=$(wc -c < "$session_log" 2>/dev/null | tr -d ' ')
|
|
165
|
-
local final_lines=$(wc -l < "$session_log" 2>/dev/null | tr -d ' ')
|
|
166
|
-
log_info "Session log: $final_lines lines, $((final_size / 1024))KB"
|
|
167
|
-
if [[ "$final_size" -lt 1024 && "$exit_code" -eq 0 ]]; then
|
|
168
|
-
log_warn "Session log was truncated during execution (subagent stdout redirection may have severed it)"
|
|
169
|
-
log_warn "Proceeding with exit-code-only outcome detection — child transcripts and git state are still valid"
|
|
170
|
-
fi
|
|
171
|
-
fi
|
|
172
|
-
log_info "exit_code=$exit_code"
|
|
173
|
-
|
|
174
|
-
# ── Determine session outcome from observable signals ──────────────
|
|
175
|
-
# No dependency on session-status.json — uses exit code, git commits,
|
|
176
|
-
# and working tree cleanliness as the single source of truth.
|
|
177
|
-
local session_status
|
|
178
|
-
local project_root
|
|
179
|
-
project_root="$PROJECT_ROOT"
|
|
180
|
-
local default_branch="$base_branch"
|
|
181
|
-
|
|
182
|
-
local semantic_finalized=false
|
|
183
|
-
local semantic_feature_slug=""
|
|
184
|
-
local semantic_commit_sha=""
|
|
185
|
-
local was_ai_runtime_error=false
|
|
186
|
-
if prizm_detect_ai_runtime_error "$session_log" "$progress_json"; then
|
|
187
|
-
was_ai_runtime_error=true
|
|
188
|
-
fi
|
|
189
|
-
|
|
190
|
-
if prizm_feature_semantically_complete "$feature_list" "$feature_id" "$project_root" "$default_branch" "$PRIZMKIT_DIR"; then
|
|
191
|
-
semantic_finalized=true
|
|
192
|
-
semantic_feature_slug="$PRIZM_SEMANTIC_FEATURE_SLUG"
|
|
193
|
-
semantic_commit_sha="$PRIZM_SEMANTIC_COMMIT_SHA"
|
|
194
|
-
if [[ $exit_code -ne 0 || "$was_stale_killed" == true || "$was_context_overflow" == true || "$was_ai_runtime_error" == true ]]; then
|
|
195
|
-
log_warn "Session ended with a failure signal after semantic completion; treating as finalized success"
|
|
196
|
-
log_warn "Semantic completion commit: ${semantic_commit_sha:-unknown}"
|
|
197
|
-
fi
|
|
198
|
-
session_status="success"
|
|
199
|
-
elif [[ "$was_context_overflow" == true ]]; then
|
|
200
|
-
log_warn "Session failed because the AI CLI exceeded the model context window"
|
|
201
|
-
log_warn "Context overflow is retried without consuming code retry budget"
|
|
202
|
-
session_status="context_overflow"
|
|
203
|
-
elif [[ "$was_ai_runtime_error" == true ]]; then
|
|
204
|
-
log_warn "Session failed due to structured AI runtime error"
|
|
205
|
-
log_warn "AI runtime errors are retried without consuming code retry budget"
|
|
206
|
-
session_status="infra_error"
|
|
207
|
-
elif [[ "$was_infra_error" == true ]]; then
|
|
208
|
-
log_warn "Session failed due to AI CLI/provider infrastructure error"
|
|
209
|
-
log_warn "Infrastructure errors are retried without consuming code retry budget"
|
|
210
|
-
session_status="infra_error"
|
|
211
|
-
elif [[ "$was_stale_killed" == true ]]; then
|
|
212
|
-
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
213
|
-
log_warn "Heartbeat-killed sessions are treated as failed; dev branch is preserved for inspection"
|
|
214
|
-
session_status="crashed"
|
|
215
|
-
elif [[ $exit_code -ne 0 ]]; then
|
|
216
|
-
log_warn "Session exited with code $exit_code"
|
|
217
|
-
session_status="crashed"
|
|
218
|
-
else
|
|
219
|
-
# Exit code 0 — check if the session actually produced commits
|
|
220
|
-
local has_commits=""
|
|
221
|
-
if git -C "$project_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
222
|
-
has_commits=$(git -C "$project_root" log "${default_branch}..HEAD" --oneline 2>/dev/null | head -1)
|
|
223
|
-
fi
|
|
224
|
-
|
|
225
|
-
if [[ -n "$has_commits" ]]; then
|
|
226
|
-
session_status="success"
|
|
227
|
-
else
|
|
228
|
-
# No commits found — check if there are uncommitted changes (session
|
|
229
|
-
# did work but didn't commit, e.g. context window exhausted)
|
|
230
|
-
local uncommitted=""
|
|
231
|
-
uncommitted=$(prizm_git_status_safe "$project_root" | head -1 || true)
|
|
232
|
-
if [[ -n "$uncommitted" ]]; then
|
|
233
|
-
log_warn "Session exited cleanly but produced no commits (uncommitted changes found) — auto-committing..."
|
|
234
|
-
prizm_git_add_all_safe "$project_root" || true
|
|
235
|
-
if git -C "$project_root" commit --no-verify -m "chore($feature_id): auto-commit session work" 2>/dev/null; then
|
|
236
|
-
log_info "Auto-commit succeeded"
|
|
237
|
-
session_status="success"
|
|
238
|
-
else
|
|
239
|
-
log_warn "Auto-commit failed — no changes to commit"
|
|
240
|
-
session_status="crashed"
|
|
241
|
-
fi
|
|
242
|
-
else
|
|
243
|
-
log_warn "Session exited cleanly but produced no commits and no changes"
|
|
244
|
-
session_status="crashed"
|
|
245
|
-
fi
|
|
246
|
-
fi
|
|
247
|
-
fi
|
|
248
|
-
|
|
249
|
-
# ── Post-success validation ──────────────────────────────────────────
|
|
250
|
-
if [[ "$session_status" == "success" ]]; then
|
|
251
|
-
if git -C "$project_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
252
|
-
local dirty_files=""
|
|
253
|
-
dirty_files=$(prizm_git_status_safe "$project_root" || true)
|
|
254
|
-
if [[ -n "$dirty_files" ]]; then
|
|
255
|
-
log_info "Auto-committing remaining session artifacts..."
|
|
256
|
-
prizm_git_add_all_safe "$project_root" || true
|
|
257
|
-
git -C "$project_root" commit --no-verify --amend --no-edit 2>/dev/null \
|
|
258
|
-
|| git -C "$project_root" commit --no-verify -m "chore($feature_id): include remaining session artifacts" 2>/dev/null \
|
|
259
|
-
|| true
|
|
260
|
-
fi
|
|
261
|
-
fi
|
|
262
|
-
fi
|
|
263
|
-
|
|
264
|
-
log_info "Session result: $session_status"
|
|
265
|
-
|
|
266
|
-
# Subagent detection
|
|
267
|
-
prizm_detect_subagents "$session_log"
|
|
268
|
-
|
|
269
|
-
# Write lightweight session summary for post-session inspection
|
|
270
|
-
local feature_slug
|
|
271
|
-
if [[ -n "$semantic_feature_slug" ]]; then
|
|
272
|
-
feature_slug="$semantic_feature_slug"
|
|
273
|
-
else
|
|
274
|
-
feature_slug=$(python3 -c "
|
|
275
|
-
import json, re, sys
|
|
276
|
-
flist, fid = sys.argv[1], sys.argv[2]
|
|
277
|
-
with open(flist) as f:
|
|
278
|
-
data = json.load(f)
|
|
279
|
-
for feat in data.get('features', []):
|
|
280
|
-
if feat.get('id') == fid:
|
|
281
|
-
fnum = feat['id'].replace('F-', '').replace('f-', '').zfill(3)
|
|
282
|
-
title = feat.get('title', '').lower()
|
|
283
|
-
title = re.sub(r'[^a-z0-9\s-]', '', title)
|
|
284
|
-
title = re.sub(r'[\s]+', '-', title.strip())
|
|
285
|
-
title = re.sub(r'-+', '-', title).strip('-')
|
|
286
|
-
print(f'{fnum}-{title}')
|
|
287
|
-
sys.exit(0)
|
|
288
|
-
sys.exit(1)
|
|
289
|
-
" "$feature_list" "$feature_id" 2>/dev/null) || {
|
|
290
|
-
log_warn "Could not resolve feature slug for $feature_id — session summary and artifact validation will be skipped"
|
|
291
|
-
feature_slug=""
|
|
292
|
-
}
|
|
293
|
-
fi
|
|
294
|
-
_SPAWN_FEATURE_SLUG="$feature_slug"
|
|
295
|
-
|
|
296
|
-
# Validate key artifacts exist after successful session
|
|
297
|
-
if [[ "$session_status" == "success" && -n "$feature_slug" ]]; then
|
|
298
|
-
local context_snapshot="$PRIZMKIT_DIR/specs/${feature_slug}/context-snapshot.md"
|
|
299
|
-
local plan_file="$PRIZMKIT_DIR/specs/${feature_slug}/plan.md"
|
|
300
|
-
|
|
301
|
-
if [[ ! -f "$context_snapshot" ]]; then
|
|
302
|
-
log_warn "ARTIFACT_MISSING: context-snapshot.md not found at $context_snapshot"
|
|
303
|
-
fi
|
|
304
|
-
if [[ ! -f "$plan_file" ]]; then
|
|
305
|
-
log_warn "ARTIFACT_MISSING: plan.md not found at $plan_file"
|
|
306
|
-
fi
|
|
307
|
-
|
|
308
|
-
# Validate checkpoint completeness
|
|
309
|
-
local checkpoint_file="$PRIZMKIT_DIR/specs/${feature_slug}/workflow-checkpoint.json"
|
|
310
|
-
if [[ -f "$checkpoint_file" ]]; then
|
|
311
|
-
local checkpoint_result
|
|
312
|
-
checkpoint_result=$(python3 -c "
|
|
313
|
-
import json, sys
|
|
314
|
-
try:
|
|
315
|
-
with open(sys.argv[1]) as f:
|
|
316
|
-
data = json.load(f)
|
|
317
|
-
except json.JSONDecodeError as e:
|
|
318
|
-
print('CORRUPTED: {}'.format(e))
|
|
319
|
-
sys.exit(2)
|
|
320
|
-
incomplete = [s for s in data['steps'] if s['status'] not in ('completed', 'skipped')]
|
|
321
|
-
if incomplete:
|
|
322
|
-
for s in incomplete:
|
|
323
|
-
print('INCOMPLETE: {} {} = {}'.format(s['id'], s['skill'], s['status']))
|
|
324
|
-
sys.exit(1)
|
|
325
|
-
print('ALL_COMPLETE')
|
|
326
|
-
sys.exit(0)
|
|
327
|
-
" "$checkpoint_file" 2>&1) || checkpoint_result="CHECK_FAILED"
|
|
328
|
-
local check_exit=$?
|
|
329
|
-
if [[ "$checkpoint_result" == "CHECK_FAILED" ]]; then
|
|
330
|
-
check_exit=2
|
|
331
|
-
elif [[ "$checkpoint_result" == *"INCOMPLETE"* ]]; then
|
|
332
|
-
check_exit=1
|
|
333
|
-
else
|
|
334
|
-
check_exit=0
|
|
335
|
-
fi
|
|
336
|
-
if [[ $check_exit -eq 2 ]]; then
|
|
337
|
-
log_warn "CHECKPOINT_CORRUPTED: workflow-checkpoint.json is not valid JSON"
|
|
338
|
-
elif [[ $check_exit -eq 1 ]]; then
|
|
339
|
-
log_warn "CHECKPOINT_INCOMPLETE: Not all workflow steps completed:"
|
|
340
|
-
echo "$checkpoint_result" | while read -r line; do log_warn " $line"; done
|
|
341
|
-
else
|
|
342
|
-
log_info "CHECKPOINT: All workflow steps completed"
|
|
343
|
-
fi
|
|
344
|
-
else
|
|
345
|
-
log_info "CHECKPOINT: No workflow-checkpoint.json found (checkpoint system not active)"
|
|
346
|
-
fi
|
|
347
|
-
fi
|
|
348
|
-
|
|
349
|
-
# Propagate completion notes for dependency context (only on success)
|
|
350
|
-
if [[ "$session_status" == "success" && -n "$feature_slug" ]]; then
|
|
351
|
-
local summary_path="$PRIZMKIT_DIR/specs/$feature_slug/completion-summary.json"
|
|
352
|
-
if [[ -f "$summary_path" ]]; then
|
|
353
|
-
python3 "$SCRIPTS_DIR/patch-completion-notes.py" \
|
|
354
|
-
--feature-list "$feature_list" \
|
|
355
|
-
--feature-id "$feature_id" \
|
|
356
|
-
--summary "$summary_path" >/dev/null 2>&1 && {
|
|
357
|
-
log_info "Propagated completion notes for $feature_id to feature-list.json"
|
|
358
|
-
} || {
|
|
359
|
-
log_warn "Failed to propagate completion notes for $feature_id"
|
|
360
|
-
}
|
|
361
|
-
else
|
|
362
|
-
log_info "No completion-summary.json for $feature_id — dependency context will be limited"
|
|
363
|
-
fi
|
|
364
|
-
fi
|
|
365
|
-
|
|
366
|
-
# Return status via global variable (avoids $() swallowing stdout)
|
|
367
|
-
_SPAWN_RESULT="$session_status"
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
finalize_feature_status_after_branch_return() {
|
|
371
|
-
local feature_id="$1"
|
|
372
|
-
local feature_list="$2"
|
|
373
|
-
local session_id="$3"
|
|
374
|
-
local session_status="$4"
|
|
375
|
-
local max_retries="$5"
|
|
376
|
-
local max_infra_retries="$6"
|
|
377
|
-
local session_dir="$7"
|
|
378
|
-
local base_branch="${8:-main}"
|
|
379
|
-
local active_dev_branch="${9:-}"
|
|
380
|
-
local fatal_error_code="${10:-}"
|
|
381
|
-
local continuation_summary_path="${11:-}"
|
|
382
|
-
local no_progress_count="${12:-0}"
|
|
383
|
-
local progress_fingerprint="${13:-}"
|
|
384
|
-
|
|
385
|
-
local feature_slug="${_SPAWN_FEATURE_SLUG:-}"
|
|
386
|
-
local progress_json="$session_dir/logs/progress.json"
|
|
387
|
-
local stale_kill_marker="$session_dir/logs/stale-kill.json"
|
|
388
|
-
local exit_code="${_SPAWN_EXIT_CODE:-0}"
|
|
389
|
-
|
|
390
|
-
# Check if session produced a failure-log for future retries; synthesize one
|
|
391
|
-
# after branch return so canonical diagnostics live on the original branch.
|
|
392
|
-
if [[ "$session_status" != "success" && -n "$feature_slug" ]]; then
|
|
393
|
-
local failure_log="$PRIZMKIT_DIR/specs/${feature_slug}/failure-log.md"
|
|
394
|
-
local checkpoint_file_for_failure="$PRIZMKIT_DIR/specs/${feature_slug}/workflow-checkpoint.json"
|
|
395
|
-
if [[ -f "$failure_log" ]]; then
|
|
396
|
-
log_info "FAILURE_LOG: Session wrote failure-log.md — will be available to next retry"
|
|
397
|
-
else
|
|
398
|
-
prizm_synthesize_failure_log \
|
|
399
|
-
"$failure_log" "$feature_id" "$session_id" "$session_status" "$exit_code" \
|
|
400
|
-
"$stale_kill_marker" "$progress_json" "$checkpoint_file_for_failure" "$PROJECT_ROOT" "$base_branch"
|
|
401
|
-
if [[ -f "$failure_log" ]]; then
|
|
402
|
-
log_info "FAILURE_LOG: Runtime synthesized failure-log.md — will be available to next retry"
|
|
403
|
-
else
|
|
404
|
-
log_info "FAILURE_LOG: No failure-log.md written by session"
|
|
405
|
-
fi
|
|
406
|
-
fi
|
|
407
|
-
fi
|
|
408
|
-
|
|
409
|
-
# Update feature status on the original branch. The caller commits the
|
|
410
|
-
# resulting feature-list diff immediately after this helper returns.
|
|
411
|
-
local update_output
|
|
412
|
-
update_output=$(python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
413
|
-
--feature-list "$feature_list" \
|
|
414
|
-
--state-dir "$STATE_DIR" \
|
|
415
|
-
--feature-id "$feature_id" \
|
|
416
|
-
--session-status "$session_status" \
|
|
417
|
-
--session-id "$session_id" \
|
|
418
|
-
--max-retries "$max_retries" \
|
|
419
|
-
--max-infra-retries "$max_infra_retries" \
|
|
420
|
-
--active-dev-branch "$active_dev_branch" \
|
|
421
|
-
--base-branch "$base_branch" \
|
|
422
|
-
--last-fatal-error-code "$fatal_error_code" \
|
|
423
|
-
--continuation-summary-path "$continuation_summary_path" \
|
|
424
|
-
--no-progress-count "$no_progress_count" \
|
|
425
|
-
--progress-fingerprint "$progress_fingerprint" \
|
|
426
|
-
--action update) || {
|
|
427
|
-
log_error "Failed to update feature status: $update_output"
|
|
428
|
-
log_error ".prizmkit/plans/feature-list.json may be out of sync. Manual intervention needed."
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
_SPAWN_ITEM_STATUS="$(printf '%s' "$update_output" | prizm_extract_update_new_status)"
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
# ============================================================
|
|
435
|
-
# Graceful Shutdown
|
|
436
|
-
# ============================================================
|
|
437
|
-
|
|
438
|
-
cleanup() {
|
|
439
|
-
echo ""
|
|
440
|
-
log_warn "Received interrupt signal. Saving state..."
|
|
441
|
-
|
|
442
|
-
# Kill all child processes (claude-internal, heartbeat, progress parser, etc.)
|
|
443
|
-
kill 0 2>/dev/null || true
|
|
444
|
-
|
|
445
|
-
# Log current branch info for recovery
|
|
446
|
-
if [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
447
|
-
log_info "Development was on branch: $_DEV_BRANCH_NAME"
|
|
448
|
-
log_info "Original branch was: $_ORIGINAL_BRANCH"
|
|
449
|
-
fi
|
|
450
|
-
|
|
451
|
-
# Update status of currently in-progress feature to interrupted
|
|
452
|
-
if [[ -n "$FEATURE_LIST" && -f "$FEATURE_LIST" ]]; then
|
|
453
|
-
# Find any in-progress feature and mark it as interrupted
|
|
454
|
-
local _interrupted_id
|
|
455
|
-
_interrupted_id=$(python3 -c "
|
|
456
|
-
import json, sys
|
|
457
|
-
with open(sys.argv[1]) as f:
|
|
458
|
-
data = json.load(f)
|
|
459
|
-
for feat in data.get('features', []):
|
|
460
|
-
if feat.get('status') == 'in_progress':
|
|
461
|
-
print(feat['id'])
|
|
462
|
-
break
|
|
463
|
-
" "$FEATURE_LIST" 2>/dev/null || echo "")
|
|
464
|
-
|
|
465
|
-
if [[ -n "$_interrupted_id" ]]; then
|
|
466
|
-
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
467
|
-
--feature-list "$FEATURE_LIST" \
|
|
468
|
-
--state-dir "$STATE_DIR" \
|
|
469
|
-
--feature-id "$_interrupted_id" \
|
|
470
|
-
--session-status "failed" \
|
|
471
|
-
--max-retries "$MAX_RETRIES" \
|
|
472
|
-
--max-infra-retries "$MAX_INFRA_RETRIES" \
|
|
473
|
-
--action update 2>/dev/null || true
|
|
474
|
-
log_info "Feature $_interrupted_id marked as failed due to interrupt"
|
|
475
|
-
fi
|
|
476
|
-
|
|
477
|
-
# Pause the pipeline (mark remaining pending items)
|
|
478
|
-
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
479
|
-
--feature-list "$FEATURE_LIST" \
|
|
480
|
-
--state-dir "$STATE_DIR" \
|
|
481
|
-
--action pause 2>/dev/null || true
|
|
482
|
-
fi
|
|
483
|
-
|
|
484
|
-
# GUARANTEED: always return to original branch (save WIP on dev branch first)
|
|
485
|
-
branch_ensure_return "$PROJECT_ROOT" "$_ORIGINAL_BRANCH" "$_DEV_BRANCH_NAME"
|
|
486
|
-
|
|
487
|
-
log_info "Pipeline paused. Run './run-feature.sh run' to resume."
|
|
488
|
-
exit 130
|
|
489
|
-
}
|
|
490
|
-
trap cleanup SIGINT SIGTERM
|
|
491
|
-
|
|
492
|
-
# ============================================================
|
|
493
|
-
# Dependency Check
|
|
494
|
-
# ============================================================
|
|
495
|
-
|
|
496
|
-
check_dependencies() {
|
|
497
|
-
prizm_check_common_dependencies "$CLI_CMD"
|
|
498
|
-
local _project_root
|
|
499
|
-
_project_root="$PROJECT_ROOT"
|
|
500
|
-
prizm_ensure_git_repo "$_project_root"
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
# ============================================================
|
|
504
|
-
# run-one: Run a single feature with full control
|
|
505
|
-
# ============================================================
|
|
506
|
-
|
|
507
|
-
run_one() {
|
|
508
|
-
local feature_id=""
|
|
509
|
-
local feature_list=""
|
|
510
|
-
local dry_run=false
|
|
511
|
-
local resume_phase=""
|
|
512
|
-
local mode_override=""
|
|
513
|
-
local critic_override=""
|
|
514
|
-
local do_clean=false
|
|
515
|
-
local no_reset=false
|
|
516
|
-
|
|
517
|
-
# Parse arguments
|
|
518
|
-
while [[ $# -gt 0 ]]; do
|
|
519
|
-
case "$1" in
|
|
520
|
-
--dry-run)
|
|
521
|
-
dry_run=true
|
|
522
|
-
shift
|
|
523
|
-
;;
|
|
524
|
-
--resume-phase)
|
|
525
|
-
shift
|
|
526
|
-
if [[ $# -eq 0 ]]; then
|
|
527
|
-
log_error "--resume-phase requires a value"
|
|
528
|
-
exit 1
|
|
529
|
-
fi
|
|
530
|
-
resume_phase="$1"
|
|
531
|
-
shift
|
|
532
|
-
;;
|
|
533
|
-
--mode)
|
|
534
|
-
shift
|
|
535
|
-
if [[ $# -eq 0 ]]; then
|
|
536
|
-
log_error "--mode requires a value (lite|standard|full)"
|
|
537
|
-
exit 1
|
|
538
|
-
fi
|
|
539
|
-
case "$1" in
|
|
540
|
-
lite|standard|full)
|
|
541
|
-
mode_override="$1"
|
|
542
|
-
;;
|
|
543
|
-
*)
|
|
544
|
-
log_error "Invalid mode: $1 (must be lite, standard, or full)"
|
|
545
|
-
exit 1
|
|
546
|
-
;;
|
|
547
|
-
esac
|
|
548
|
-
shift
|
|
549
|
-
;;
|
|
550
|
-
--clean)
|
|
551
|
-
do_clean=true
|
|
552
|
-
shift
|
|
553
|
-
;;
|
|
554
|
-
--no-reset)
|
|
555
|
-
no_reset=true
|
|
556
|
-
shift
|
|
557
|
-
;;
|
|
558
|
-
--critic)
|
|
559
|
-
critic_override="true"
|
|
560
|
-
shift
|
|
561
|
-
;;
|
|
562
|
-
--no-critic)
|
|
563
|
-
critic_override="false"
|
|
564
|
-
shift
|
|
565
|
-
;;
|
|
566
|
-
--max-infra-retries)
|
|
567
|
-
shift
|
|
568
|
-
if [[ $# -eq 0 ]]; then
|
|
569
|
-
log_error "--max-infra-retries requires a value"
|
|
570
|
-
exit 1
|
|
571
|
-
fi
|
|
572
|
-
prizm_require_positive_int "--max-infra-retries" "$1"
|
|
573
|
-
MAX_INFRA_RETRIES="$1"
|
|
574
|
-
shift
|
|
575
|
-
;;
|
|
576
|
-
F-*|f-*)
|
|
577
|
-
feature_id="$1"
|
|
578
|
-
shift
|
|
579
|
-
;;
|
|
580
|
-
*)
|
|
581
|
-
feature_list="$1"
|
|
582
|
-
shift
|
|
583
|
-
;;
|
|
584
|
-
esac
|
|
585
|
-
done
|
|
586
|
-
|
|
587
|
-
# Validate required args
|
|
588
|
-
if [[ -z "$feature_id" ]]; then
|
|
589
|
-
log_error "Feature ID is required (e.g. F-007)"
|
|
590
|
-
echo ""
|
|
591
|
-
show_help
|
|
592
|
-
exit 1
|
|
593
|
-
fi
|
|
594
|
-
|
|
595
|
-
# Default feature list
|
|
596
|
-
if [[ -z "$feature_list" ]]; then
|
|
597
|
-
feature_list="$PRIZMKIT_DIR/plans/feature-list.json"
|
|
598
|
-
fi
|
|
599
|
-
|
|
600
|
-
# Resolve to absolute path
|
|
601
|
-
if [[ ! "$feature_list" = /* ]]; then
|
|
602
|
-
feature_list="$(pwd)/$feature_list"
|
|
603
|
-
fi
|
|
604
|
-
|
|
605
|
-
FEATURE_LIST="$feature_list"
|
|
606
|
-
|
|
607
|
-
# Default resume phase
|
|
608
|
-
if [[ -z "$resume_phase" ]]; then
|
|
609
|
-
resume_phase="null"
|
|
610
|
-
fi
|
|
611
|
-
|
|
612
|
-
# Validation
|
|
613
|
-
if [[ ! -f "$feature_list" ]]; then
|
|
614
|
-
log_error "Feature list not found: $feature_list"
|
|
615
|
-
exit 1
|
|
616
|
-
fi
|
|
617
|
-
|
|
618
|
-
check_dependencies
|
|
619
|
-
|
|
620
|
-
# Initialize pipeline state if needed (same logic as run_all)
|
|
621
|
-
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
622
|
-
log_info "Initializing pipeline state for single-feature run..."
|
|
623
|
-
local init_result init_stderr init_tmpstderr
|
|
624
|
-
init_tmpstderr=$(mktemp)
|
|
625
|
-
if ! init_result=$(python3 "$SCRIPTS_DIR/init-pipeline.py" \
|
|
626
|
-
--feature-list "$feature_list" \
|
|
627
|
-
--state-dir "$STATE_DIR" 2>"$init_tmpstderr"); then
|
|
628
|
-
init_stderr=$(cat "$init_tmpstderr" 2>/dev/null || true)
|
|
629
|
-
rm -f "$init_tmpstderr"
|
|
630
|
-
if [[ -n "$init_stderr" ]]; then
|
|
631
|
-
log_warn "$init_stderr"
|
|
632
|
-
fi
|
|
633
|
-
log_error "Pipeline initialization failed (script error)"
|
|
634
|
-
exit 1
|
|
635
|
-
fi
|
|
636
|
-
init_stderr=$(cat "$init_tmpstderr" 2>/dev/null || true)
|
|
637
|
-
rm -f "$init_tmpstderr"
|
|
638
|
-
|
|
639
|
-
# Show any stderr warnings without corrupting JSON
|
|
640
|
-
if [[ -n "$init_stderr" ]]; then
|
|
641
|
-
log_warn "$init_stderr"
|
|
642
|
-
fi
|
|
643
|
-
|
|
644
|
-
local init_valid
|
|
645
|
-
init_valid=$(echo "$init_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('valid', False))" 2>/dev/null || echo "False")
|
|
646
|
-
|
|
647
|
-
if [[ "$init_valid" != "True" ]]; then
|
|
648
|
-
log_error "Pipeline initialization failed:"
|
|
649
|
-
echo "$init_result"
|
|
650
|
-
exit 1
|
|
651
|
-
fi
|
|
652
|
-
|
|
653
|
-
local features_count
|
|
654
|
-
features_count=$(echo "$init_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('features_count', 0))" 2>/dev/null || echo "0")
|
|
655
|
-
log_success "Pipeline initialized with $features_count features"
|
|
656
|
-
fi
|
|
657
|
-
|
|
658
|
-
# Verify feature exists
|
|
659
|
-
local feature_title
|
|
660
|
-
feature_title=$(python3 -c "
|
|
661
|
-
import json, sys
|
|
662
|
-
feature_list_path, fid = sys.argv[1], sys.argv[2]
|
|
663
|
-
with open(feature_list_path) as f:
|
|
664
|
-
data = json.load(f)
|
|
665
|
-
for feat in data.get('features', []):
|
|
666
|
-
if feat.get('id') == fid:
|
|
667
|
-
print(feat.get('title', ''))
|
|
668
|
-
sys.exit(0)
|
|
669
|
-
sys.exit(1)
|
|
670
|
-
" "$feature_list" "$feature_id" 2>/dev/null) || {
|
|
671
|
-
log_error "Feature $feature_id not found in $feature_list"
|
|
672
|
-
exit 1
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
# Optional Clean
|
|
676
|
-
if [[ "$do_clean" == true ]]; then
|
|
677
|
-
if [[ "$dry_run" == true ]]; then
|
|
678
|
-
log_warn "Dry-run mode: --clean ignored (no artifacts will be deleted)"
|
|
679
|
-
else
|
|
680
|
-
log_info "Cleaning artifacts for $feature_id..."
|
|
681
|
-
|
|
682
|
-
local feature_slug
|
|
683
|
-
feature_slug=$(python3 -c "
|
|
684
|
-
import json, re, sys
|
|
685
|
-
feature_list_path, fid = sys.argv[1], sys.argv[2]
|
|
686
|
-
with open(feature_list_path) as f:
|
|
687
|
-
data = json.load(f)
|
|
688
|
-
for feat in data.get('features', []):
|
|
689
|
-
if feat.get('id') == fid:
|
|
690
|
-
fnum = feat['id'].replace('F-', '').replace('f-', '').zfill(3)
|
|
691
|
-
title = feat.get('title', '').lower()
|
|
692
|
-
title = re.sub(r'[^a-z0-9\s-]', '', title)
|
|
693
|
-
title = re.sub(r'[\s]+', '-', title.strip())
|
|
694
|
-
title = re.sub(r'-+', '-', title).strip('-')
|
|
695
|
-
print(f'{fnum}-{title}')
|
|
696
|
-
sys.exit(0)
|
|
697
|
-
sys.exit(1)
|
|
698
|
-
" "$feature_list" "$feature_id" 2>/dev/null) || {
|
|
699
|
-
log_warn "Could not determine feature slug for cleanup"
|
|
700
|
-
feature_slug=""
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
local project_root
|
|
704
|
-
project_root="$PROJECT_ROOT"
|
|
705
|
-
|
|
706
|
-
if [[ -n "$feature_slug" ]]; then
|
|
707
|
-
local specs_dir="$PRIZMKIT_DIR/specs/$feature_slug"
|
|
708
|
-
if [[ -d "$specs_dir" ]]; then
|
|
709
|
-
rm -rf "$specs_dir"
|
|
710
|
-
log_info "Removed $specs_dir"
|
|
711
|
-
fi
|
|
712
|
-
fi
|
|
713
|
-
|
|
714
|
-
local dev_team_dir="$project_root/.dev-team"
|
|
715
|
-
if [[ -d "$dev_team_dir" ]]; then
|
|
716
|
-
rm -rf "$dev_team_dir"
|
|
717
|
-
log_info "Removed $dev_team_dir"
|
|
718
|
-
fi
|
|
719
|
-
|
|
720
|
-
local feature_state_dir="$STATE_DIR/$feature_id"
|
|
721
|
-
if [[ -d "$feature_state_dir" ]]; then
|
|
722
|
-
rm -rf "$feature_state_dir"
|
|
723
|
-
log_info "Removed $feature_state_dir"
|
|
724
|
-
fi
|
|
725
|
-
fi
|
|
726
|
-
fi
|
|
727
|
-
|
|
728
|
-
# Reset Status
|
|
729
|
-
if [[ "$no_reset" == false && "$dry_run" == false ]]; then
|
|
730
|
-
log_info "Resetting $feature_id status..."
|
|
731
|
-
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
732
|
-
--feature-list "$feature_list" \
|
|
733
|
-
--state-dir "$STATE_DIR" \
|
|
734
|
-
--feature-id "$feature_id" \
|
|
735
|
-
--action reset >/dev/null 2>&1 || {
|
|
736
|
-
log_warn "Failed to reset feature status (may already be pending)"
|
|
737
|
-
}
|
|
738
|
-
elif [[ "$dry_run" == true && "$no_reset" == false ]]; then
|
|
739
|
-
log_info "Dry-run mode: skipping status reset"
|
|
740
|
-
fi
|
|
741
|
-
|
|
742
|
-
# Generate Bootstrap Prompt
|
|
743
|
-
local run_id session_id session_dir bootstrap_prompt
|
|
744
|
-
run_id=$(jq -r '.run_id' "$STATE_DIR/pipeline.json")
|
|
745
|
-
session_id="${feature_id}-$(date +%Y%m%d%H%M%S)"
|
|
746
|
-
session_dir="$STATE_DIR/$feature_id/sessions/$session_id"
|
|
747
|
-
mkdir -p "$session_dir/logs"
|
|
748
|
-
|
|
749
|
-
bootstrap_prompt="$session_dir/bootstrap-prompt.md"
|
|
750
|
-
|
|
751
|
-
# Read retry count from status.json
|
|
752
|
-
local retry_count
|
|
753
|
-
retry_count=$(python3 -c "
|
|
754
|
-
import json, os
|
|
755
|
-
status_path = os.path.join('$STATE_DIR', '$feature_id', 'status.json')
|
|
756
|
-
if os.path.isfile(status_path):
|
|
757
|
-
with open(status_path) as f:
|
|
758
|
-
d = json.load(f)
|
|
759
|
-
print(d.get('retry_count', 0))
|
|
760
|
-
else:
|
|
761
|
-
print(0)
|
|
762
|
-
" 2>/dev/null || echo "0")
|
|
763
|
-
|
|
764
|
-
local prompt_args=(
|
|
765
|
-
--feature-list "$feature_list"
|
|
766
|
-
--feature-id "$feature_id"
|
|
767
|
-
--session-id "$session_id"
|
|
768
|
-
--run-id "$run_id"
|
|
769
|
-
--retry-count "$retry_count"
|
|
770
|
-
--resume-phase "$resume_phase"
|
|
771
|
-
--state-dir "$STATE_DIR"
|
|
772
|
-
--output "$bootstrap_prompt"
|
|
773
|
-
)
|
|
774
|
-
|
|
775
|
-
if [[ -n "$mode_override" ]]; then
|
|
776
|
-
prompt_args+=(--mode "$mode_override")
|
|
777
|
-
fi
|
|
778
|
-
|
|
779
|
-
if [[ -n "${critic_override:-}" ]]; then
|
|
780
|
-
prompt_args+=(--critic "$critic_override")
|
|
781
|
-
elif [[ "${ENABLE_CRITIC:-}" == "true" || "${ENABLE_CRITIC:-}" == "1" ]]; then
|
|
782
|
-
prompt_args+=(--critic "true")
|
|
783
|
-
elif [[ "${ENABLE_CRITIC:-}" == "false" || "${ENABLE_CRITIC:-}" == "0" ]]; then
|
|
784
|
-
prompt_args+=(--critic "false")
|
|
785
|
-
fi
|
|
786
|
-
|
|
787
|
-
log_info "Generating bootstrap prompt..."
|
|
788
|
-
local gen_output
|
|
789
|
-
gen_output=$(python3 "$SCRIPTS_DIR/generate-bootstrap-prompt.py" "${prompt_args[@]}" 2>/dev/null) || {
|
|
790
|
-
log_error "Failed to generate bootstrap prompt for $feature_id"
|
|
791
|
-
return 1
|
|
792
|
-
}
|
|
793
|
-
local feature_model pipeline_mode agent_count critic_enabled
|
|
794
|
-
feature_model=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('model',''))" 2>/dev/null || echo "")
|
|
795
|
-
pipeline_mode=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('pipeline_mode','lite'))" 2>/dev/null || echo "lite")
|
|
796
|
-
agent_count=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('agent_count',1))" 2>/dev/null || echo "1")
|
|
797
|
-
critic_enabled=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('critic_enabled','false'))" 2>/dev/null || echo "false")
|
|
798
|
-
|
|
799
|
-
# Dry-Run: Print info and exit
|
|
800
|
-
if [[ "$dry_run" == true ]]; then
|
|
801
|
-
echo ""
|
|
802
|
-
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
803
|
-
echo -e "${BOLD} Dry Run: $feature_id — $feature_title${NC}"
|
|
804
|
-
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
805
|
-
echo ""
|
|
806
|
-
log_info "Session ID: $session_id"
|
|
807
|
-
log_info "Resume Phase: $resume_phase"
|
|
808
|
-
if [[ -n "$mode_override" ]]; then
|
|
809
|
-
log_info "Mode Override: $mode_override"
|
|
810
|
-
else
|
|
811
|
-
log_info "Mode: auto-detect (from complexity)"
|
|
812
|
-
fi
|
|
813
|
-
log_info "Pipeline mode: $pipeline_mode"
|
|
814
|
-
log_info "Agents: $agent_count (critic: $([ "$critic_enabled" = "true" ] && echo "enabled" || echo "disabled"))"
|
|
815
|
-
if [[ -n "$feature_model" ]]; then
|
|
816
|
-
log_info "Feature Model: $feature_model"
|
|
817
|
-
elif [[ -n "${MODEL:-}" ]]; then
|
|
818
|
-
log_info "Model (env): $MODEL"
|
|
819
|
-
else
|
|
820
|
-
log_info "Model: (CLI default)"
|
|
821
|
-
fi
|
|
822
|
-
echo ""
|
|
823
|
-
log_info "Bootstrap prompt written to:"
|
|
824
|
-
echo " $bootstrap_prompt"
|
|
825
|
-
echo ""
|
|
826
|
-
|
|
827
|
-
local prompt_lines
|
|
828
|
-
prompt_lines=$(wc -l < "$bootstrap_prompt" | tr -d ' ')
|
|
829
|
-
log_info "Prompt: $prompt_lines lines"
|
|
830
|
-
echo ""
|
|
831
|
-
echo -e "${BOLD}--- Session Context (from prompt) ---${NC}"
|
|
832
|
-
sed -n '/^## Session Context/,/^##[^#]/p' "$bootstrap_prompt" | head -20
|
|
833
|
-
echo -e "${BOLD}--- end ---${NC}"
|
|
834
|
-
echo ""
|
|
835
|
-
|
|
836
|
-
log_success "Dry run complete. Inspect full prompt with:"
|
|
837
|
-
echo " cat $bootstrap_prompt"
|
|
838
|
-
return 0
|
|
839
|
-
fi
|
|
840
|
-
|
|
841
|
-
# Log bootstrap prompt in test mode
|
|
842
|
-
prizm_log_bootstrap_prompt "$bootstrap_prompt" "$feature_id"
|
|
843
|
-
|
|
844
|
-
# Spawn AI CLI Session
|
|
845
|
-
echo ""
|
|
846
|
-
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
847
|
-
echo -e "${BOLD} Run: $feature_id — $feature_title${NC}"
|
|
848
|
-
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
849
|
-
log_info "AI CLI: $CLI_CMD (platform: $PLATFORM)"
|
|
850
|
-
log_info "Session ID: $session_id"
|
|
851
|
-
log_info "Resume Phase: $resume_phase"
|
|
852
|
-
local effective_model="${feature_model:-$MODEL}"
|
|
853
|
-
if [[ -n "$effective_model" ]]; then
|
|
854
|
-
log_info "Model: $effective_model"
|
|
855
|
-
else
|
|
856
|
-
log_info "Model: (CLI default)"
|
|
857
|
-
fi
|
|
858
|
-
if [[ -n "$mode_override" ]]; then
|
|
859
|
-
log_info "Mode Override: $mode_override"
|
|
860
|
-
fi
|
|
861
|
-
local _run_one_mode_desc
|
|
862
|
-
case "$pipeline_mode" in
|
|
863
|
-
lite) _run_one_mode_desc="Tier 1 — Single Agent" ;;
|
|
864
|
-
standard) _run_one_mode_desc="Tier 2 — Orchestrator + Dev + Reviewer" ;;
|
|
865
|
-
full) _run_one_mode_desc="Tier 3 — Full Team (+ Multi-Critic)" ;;
|
|
866
|
-
*) _run_one_mode_desc="$pipeline_mode" ;;
|
|
867
|
-
esac
|
|
868
|
-
log_info "Pipeline mode: ${BOLD}$pipeline_mode${NC} ($_run_one_mode_desc)"
|
|
869
|
-
log_info "Agents: $agent_count (critic: $([ "$critic_enabled" = "true" ] && echo "enabled" || echo "disabled"))"
|
|
870
|
-
log_info "Prompt: $bootstrap_prompt"
|
|
871
|
-
log_info "Log: $session_dir/logs/session.log"
|
|
872
|
-
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
873
|
-
echo ""
|
|
874
|
-
|
|
875
|
-
# Override cleanup trap for single-feature mode (use distinct name to avoid overwriting global cleanup)
|
|
876
|
-
cleanup_single_feature() {
|
|
877
|
-
echo ""
|
|
878
|
-
log_warn "Interrupted. Killing session..."
|
|
879
|
-
# Kill all child processes
|
|
880
|
-
kill 0 2>/dev/null || true
|
|
881
|
-
# Log current branch info
|
|
882
|
-
if [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
883
|
-
log_info "Development was on branch: $_DEV_BRANCH_NAME"
|
|
884
|
-
fi
|
|
885
|
-
log_info "Session log: $session_dir/logs/session.log"
|
|
886
|
-
|
|
887
|
-
# Update feature status to failed on interrupt
|
|
888
|
-
if [[ -n "$feature_list" && -f "$feature_list" ]]; then
|
|
889
|
-
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
890
|
-
--feature-list "$feature_list" \
|
|
891
|
-
--state-dir "$STATE_DIR" \
|
|
892
|
-
--feature-id "$feature_id" \
|
|
893
|
-
--session-status "failed" \
|
|
894
|
-
--max-retries "$MAX_RETRIES" \
|
|
895
|
-
--max-infra-retries "$MAX_INFRA_RETRIES" \
|
|
896
|
-
--action update 2>/dev/null || true
|
|
897
|
-
log_info "Feature $feature_id marked as failed due to interrupt"
|
|
898
|
-
fi
|
|
899
|
-
|
|
900
|
-
# GUARANTEED: always return to original branch (save WIP on dev branch first)
|
|
901
|
-
branch_ensure_return "$PROJECT_ROOT" "$_ORIGINAL_BRANCH" "$_DEV_BRANCH_NAME"
|
|
902
|
-
exit 130
|
|
903
|
-
}
|
|
904
|
-
trap cleanup_single_feature SIGINT SIGTERM
|
|
905
|
-
|
|
906
|
-
_SPAWN_RESULT=""
|
|
907
|
-
_SPAWN_ITEM_STATUS=""
|
|
908
|
-
|
|
909
|
-
# Branch lifecycle: create and checkout feature branch
|
|
910
|
-
local _proj_root
|
|
911
|
-
_proj_root="$PROJECT_ROOT"
|
|
912
|
-
local _source_branch
|
|
913
|
-
_source_branch=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")
|
|
914
|
-
_ORIGINAL_BRANCH="$_source_branch"
|
|
915
|
-
|
|
916
|
-
# Mark feature as in-progress (update JSON for runtime monitoring, no commit)
|
|
917
|
-
# The status change will be committed together with the final status update
|
|
918
|
-
# after the session completes, avoiding an extra noise commit per feature.
|
|
919
|
-
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
920
|
-
--feature-list "$feature_list" \
|
|
921
|
-
--state-dir "$STATE_DIR" \
|
|
922
|
-
--feature-id "$feature_id" \
|
|
923
|
-
--action start >/dev/null 2>&1 || true
|
|
924
|
-
|
|
925
|
-
local _branch_name="${DEV_BRANCH:-dev/${feature_id}-$(date +%Y%m%d%H%M)}"
|
|
926
|
-
if branch_create "$_proj_root" "$_branch_name" "$_source_branch"; then
|
|
927
|
-
_DEV_BRANCH_NAME="$_branch_name"
|
|
928
|
-
else
|
|
929
|
-
log_error "Failed to create branch: $_branch_name from $_source_branch — aborting to avoid running on $_source_branch"
|
|
930
|
-
return 1
|
|
931
|
-
fi
|
|
932
|
-
|
|
933
|
-
spawn_and_wait_session \
|
|
934
|
-
"$feature_id" "$feature_list" "$session_id" \
|
|
935
|
-
"$bootstrap_prompt" "$session_dir" "$MAX_RETRIES" "$feature_model" "$_ORIGINAL_BRANCH"
|
|
936
|
-
local session_status="$_SPAWN_RESULT"
|
|
937
|
-
|
|
938
|
-
local active_dev_branch_for_status="$_DEV_BRANCH_NAME"
|
|
939
|
-
local continuation_summary_path=""
|
|
940
|
-
local no_progress_count=0
|
|
941
|
-
local progress_after=""
|
|
942
|
-
|
|
943
|
-
# Merge dev branch back to original on success
|
|
944
|
-
if [[ "$session_status" == "success" && -n "$_DEV_BRANCH_NAME" ]]; then
|
|
945
|
-
if branch_merge "$_proj_root" "$_DEV_BRANCH_NAME" "$_ORIGINAL_BRANCH" "$AUTO_PUSH"; then
|
|
946
|
-
_DEV_BRANCH_NAME=""
|
|
947
|
-
else
|
|
948
|
-
log_warn "Auto-merge failed — dev branch preserved: $_DEV_BRANCH_NAME"
|
|
949
|
-
log_warn "Merge manually: git checkout $_ORIGINAL_BRANCH && git rebase $_DEV_BRANCH_NAME"
|
|
950
|
-
session_status="merge_conflict"
|
|
951
|
-
fi
|
|
952
|
-
elif [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
953
|
-
# Session failed — preserve dev branch for inspection
|
|
954
|
-
log_warn "Session failed — dev branch preserved for inspection: $_DEV_BRANCH_NAME"
|
|
955
|
-
_DEV_BRANCH_NAME=""
|
|
956
|
-
fi
|
|
957
|
-
|
|
958
|
-
# GUARANTEED: always return to original branch regardless of success/failure/merge outcome
|
|
959
|
-
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
960
|
-
|
|
961
|
-
finalize_feature_status_after_branch_return \
|
|
962
|
-
"$feature_id" "$feature_list" "$session_id" "$session_status" "$MAX_RETRIES" "$MAX_INFRA_RETRIES" "$session_dir" "$_ORIGINAL_BRANCH" "$active_dev_branch_for_status" "context_overflow" "$continuation_summary_path" "$no_progress_count" "${progress_after:-}"
|
|
963
|
-
|
|
964
|
-
# Commit feature status update on the original branch (after guaranteed return)
|
|
965
|
-
if ! git -C "$_proj_root" diff --quiet "$feature_list" 2>/dev/null; then
|
|
966
|
-
prizm_git_commit_paths "$_proj_root" "chore($feature_id): update feature status" "$feature_list"
|
|
967
|
-
fi
|
|
968
|
-
|
|
969
|
-
echo ""
|
|
970
|
-
if [[ "$session_status" == "success" ]]; then
|
|
971
|
-
log_success "════════════════════════════════════════════════════"
|
|
972
|
-
log_success " $feature_id completed successfully!"
|
|
973
|
-
log_success "════════════════════════════════════════════════════"
|
|
974
|
-
else
|
|
975
|
-
log_error "════════════════════════════════════════════════════"
|
|
976
|
-
log_error " $feature_id result: $session_status"
|
|
977
|
-
log_error " Review log: $session_dir/logs/session.log"
|
|
978
|
-
log_error "════════════════════════════════════════════════════"
|
|
979
|
-
fi
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
# ============================================================
|
|
983
|
-
# Main Loop: Run all features
|
|
984
|
-
# ============================================================
|
|
985
|
-
|
|
986
|
-
main() {
|
|
987
|
-
local feature_list="${1:-$PRIZMKIT_DIR/plans/feature-list.json}"
|
|
988
|
-
local features_filter="${2:-}"
|
|
989
|
-
|
|
990
|
-
# Resolve to absolute path
|
|
991
|
-
if [[ ! "$feature_list" = /* ]]; then
|
|
992
|
-
feature_list="$(pwd)/$feature_list"
|
|
993
|
-
fi
|
|
994
|
-
|
|
995
|
-
FEATURE_LIST="$feature_list"
|
|
996
|
-
|
|
997
|
-
# Validate feature list exists
|
|
998
|
-
if [[ ! -f "$feature_list" ]]; then
|
|
999
|
-
log_error "Feature list not found: $feature_list"
|
|
1000
|
-
log_info "Create a feature list first using the feature-planner skill,"
|
|
1001
|
-
log_info "or provide a path: ./run-feature.sh run <path-to-.prizmkit/plans/feature-list.json>"
|
|
1002
|
-
exit 1
|
|
1003
|
-
fi
|
|
1004
|
-
|
|
1005
|
-
# Validate .prizmkit/plans/feature-list.json is under project root
|
|
1006
|
-
local fl_dir
|
|
1007
|
-
fl_dir="$(cd "$(dirname "$feature_list")" && pwd)"
|
|
1008
|
-
if [[ "$fl_dir" != "$PRIZMKIT_DIR/plans" && "$fl_dir" != "$PROJECT_ROOT" ]]; then
|
|
1009
|
-
log_warn "feature-list.json is not under project root ($PROJECT_ROOT), found at $fl_dir"
|
|
1010
|
-
log_warn "Pipeline expects feature-list.json at ${PRIZMKIT_DIR}/plans/feature-list.json. Proceeding but results may be unstable."
|
|
1011
|
-
fi
|
|
1012
|
-
|
|
1013
|
-
check_dependencies
|
|
1014
|
-
|
|
1015
|
-
# Initialize pipeline state if needed
|
|
1016
|
-
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
1017
|
-
log_info "Initializing pipeline state..."
|
|
1018
|
-
local init_result init_stderr init_tmpstderr
|
|
1019
|
-
init_tmpstderr=$(mktemp)
|
|
1020
|
-
if ! init_result=$(python3 "$SCRIPTS_DIR/init-pipeline.py" \
|
|
1021
|
-
--feature-list "$feature_list" \
|
|
1022
|
-
--state-dir "$STATE_DIR" 2>"$init_tmpstderr"); then
|
|
1023
|
-
init_stderr=$(cat "$init_tmpstderr" 2>/dev/null || true)
|
|
1024
|
-
rm -f "$init_tmpstderr"
|
|
1025
|
-
if [[ -n "$init_stderr" ]]; then
|
|
1026
|
-
log_warn "$init_stderr"
|
|
1027
|
-
fi
|
|
1028
|
-
log_error "Pipeline initialization failed (script error)"
|
|
1029
|
-
exit 1
|
|
1030
|
-
fi
|
|
1031
|
-
init_stderr=$(cat "$init_tmpstderr" 2>/dev/null || true)
|
|
1032
|
-
rm -f "$init_tmpstderr"
|
|
1033
|
-
|
|
1034
|
-
# Show any stderr warnings (e.g. project root detection) without corrupting JSON
|
|
1035
|
-
if [[ -n "$init_stderr" ]]; then
|
|
1036
|
-
log_warn "$init_stderr"
|
|
1037
|
-
fi
|
|
1038
|
-
|
|
1039
|
-
local init_valid
|
|
1040
|
-
init_valid=$(echo "$init_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('valid', False))" 2>/dev/null || echo "False")
|
|
1041
|
-
|
|
1042
|
-
if [[ "$init_valid" != "True" ]]; then
|
|
1043
|
-
log_error "Pipeline initialization failed:"
|
|
1044
|
-
echo "$init_result"
|
|
1045
|
-
exit 1
|
|
1046
|
-
fi
|
|
1047
|
-
|
|
1048
|
-
local features_count
|
|
1049
|
-
features_count=$(echo "$init_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('features_count', 0))" 2>/dev/null || echo "0")
|
|
1050
|
-
log_success "Pipeline initialized with $features_count features"
|
|
1051
|
-
else
|
|
1052
|
-
log_info "Resuming existing pipeline..."
|
|
1053
|
-
fi
|
|
1054
|
-
|
|
1055
|
-
# Print header
|
|
1056
|
-
echo ""
|
|
1057
|
-
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
1058
|
-
echo -e "${BOLD} Dev-Pipeline Runner Started${NC}"
|
|
1059
|
-
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
1060
|
-
log_info "Feature list: $feature_list"
|
|
1061
|
-
if [[ -n "$features_filter" ]]; then
|
|
1062
|
-
log_info "Features filter: $features_filter"
|
|
1063
|
-
fi
|
|
1064
|
-
log_info "Max code retries per feature: $MAX_RETRIES"
|
|
1065
|
-
log_info "Max infrastructure retries per feature: $MAX_INFRA_RETRIES"
|
|
1066
|
-
log_info "AI CLI: $CLI_CMD (platform: $PLATFORM)"
|
|
1067
|
-
if [[ -n "${MODEL:-}" ]]; then
|
|
1068
|
-
log_info "Default Model: $MODEL"
|
|
1069
|
-
fi
|
|
1070
|
-
if [[ "$STOP_ON_FAILURE" == "1" ]]; then
|
|
1071
|
-
log_info "Stop on failure: enabled"
|
|
1072
|
-
fi
|
|
1073
|
-
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
1074
|
-
echo ""
|
|
1075
|
-
|
|
1076
|
-
# Branch lifecycle: each feature gets its own dev branch (created per-iteration below)
|
|
1077
|
-
local _proj_root
|
|
1078
|
-
_proj_root="$PROJECT_ROOT"
|
|
1079
|
-
local _source_branch
|
|
1080
|
-
_source_branch=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")
|
|
1081
|
-
_ORIGINAL_BRANCH="$_source_branch"
|
|
1082
|
-
|
|
1083
|
-
# Main processing loop
|
|
1084
|
-
local session_count=0
|
|
1085
|
-
local total_subagent_calls=0
|
|
1086
|
-
|
|
1087
|
-
while true; do
|
|
1088
|
-
# Safety net: ensure we're on the original branch at the start of each iteration.
|
|
1089
|
-
# If a previous iteration's `continue` skipped branch_ensure_return, we could
|
|
1090
|
-
# still be on a dev branch. This prevents cascading branch confusion.
|
|
1091
|
-
if [[ -n "$_ORIGINAL_BRANCH" ]]; then
|
|
1092
|
-
local _cur_branch
|
|
1093
|
-
_cur_branch=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
|
|
1094
|
-
if [[ -n "$_cur_branch" && "$_cur_branch" != "$_ORIGINAL_BRANCH" ]]; then
|
|
1095
|
-
log_warn "Still on branch $_cur_branch at loop start — returning to $_ORIGINAL_BRANCH"
|
|
1096
|
-
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
1097
|
-
fi
|
|
1098
|
-
fi
|
|
1099
|
-
|
|
1100
|
-
# Check for stuck features
|
|
1101
|
-
local stuck_result
|
|
1102
|
-
stuck_result=$(python3 "$SCRIPTS_DIR/detect-stuck.py" \
|
|
1103
|
-
--state-dir "$STATE_DIR" \
|
|
1104
|
-
--feature-list "$FEATURE_LIST" \
|
|
1105
|
-
--max-retries "$MAX_RETRIES" \
|
|
1106
|
-
--stale-threshold "$HEARTBEAT_STALE_THRESHOLD" 2>/dev/null || echo '{"stuck_count": 0}')
|
|
1107
|
-
|
|
1108
|
-
local stuck_count
|
|
1109
|
-
stuck_count=$(echo "$stuck_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('stuck_count', 0))" 2>/dev/null || echo "0")
|
|
1110
|
-
|
|
1111
|
-
if [[ "$stuck_count" -gt 0 ]]; then
|
|
1112
|
-
log_warn "Detected $stuck_count stuck feature(s):"
|
|
1113
|
-
echo "$stuck_result" | python3 -c "
|
|
1114
|
-
import sys, json
|
|
1115
|
-
data = json.load(sys.stdin)
|
|
1116
|
-
for f in data.get('stuck_features', []):
|
|
1117
|
-
print(f' - {f[\"feature_id\"]}: {f[\"reason\"]} — {f[\"suggestion\"]}')
|
|
1118
|
-
" 2>/dev/null || true
|
|
1119
|
-
fi
|
|
1120
|
-
|
|
1121
|
-
# Find next feature to process
|
|
1122
|
-
local next_feature
|
|
1123
|
-
local _get_next_args=(
|
|
1124
|
-
--feature-list "$feature_list"
|
|
1125
|
-
--state-dir "$STATE_DIR"
|
|
1126
|
-
--max-retries "$MAX_RETRIES"
|
|
1127
|
-
--action get_next
|
|
1128
|
-
)
|
|
1129
|
-
if [[ -n "$features_filter" ]]; then
|
|
1130
|
-
_get_next_args+=(--features "$features_filter")
|
|
1131
|
-
fi
|
|
1132
|
-
if ! next_feature=$(python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
1133
|
-
"${_get_next_args[@]}" 2>/dev/null); then
|
|
1134
|
-
|
|
1135
|
-
log_error "Failed to get next feature"
|
|
1136
|
-
break
|
|
1137
|
-
fi
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
if [[ "$next_feature" == "PIPELINE_COMPLETE" ]]; then
|
|
1141
|
-
echo ""
|
|
1142
|
-
log_success "════════════════════════════════════════════════════"
|
|
1143
|
-
log_success " Pipeline finished."
|
|
1144
|
-
log_success " Total sessions: $session_count"
|
|
1145
|
-
log_success " Total subagent calls: $total_subagent_calls"
|
|
1146
|
-
log_success "════════════════════════════════════════════════════"
|
|
1147
|
-
|
|
1148
|
-
# Check for auto-skipped features
|
|
1149
|
-
local auto_skipped_count
|
|
1150
|
-
auto_skipped_count=$(python3 -c "
|
|
1151
|
-
import json, sys
|
|
1152
|
-
with open(sys.argv[1]) as f:
|
|
1153
|
-
data = json.load(f)
|
|
1154
|
-
count = sum(1 for f in data.get('features', []) if f.get('status') == 'auto_skipped')
|
|
1155
|
-
print(count)
|
|
1156
|
-
" "$feature_list" 2>/dev/null || echo "0")
|
|
1157
|
-
|
|
1158
|
-
if [[ "$auto_skipped_count" -gt 0 ]]; then
|
|
1159
|
-
echo ""
|
|
1160
|
-
log_warn "$auto_skipped_count feature(s) were auto-skipped due to failed dependencies."
|
|
1161
|
-
log_warn "Run './run-feature.sh status' to see details."
|
|
1162
|
-
log_warn "Run './run-feature.sh unskip' to reset and retry them."
|
|
1163
|
-
fi
|
|
1164
|
-
|
|
1165
|
-
# ── Deploy session (only if ENABLE_DEPLOY=1 and all features completed) ──
|
|
1166
|
-
if [[ "$ENABLE_DEPLOY" == "1" ]]; then
|
|
1167
|
-
local incomplete_count
|
|
1168
|
-
incomplete_count=$({ python3 -c "
|
|
1169
|
-
import json, sys
|
|
1170
|
-
with open(sys.argv[1]) as f:
|
|
1171
|
-
data = json.load(f)
|
|
1172
|
-
bad = [f for f in data.get('features', [])
|
|
1173
|
-
if f.get('status') not in ('completed', 'skipped')]
|
|
1174
|
-
for f in bad:
|
|
1175
|
-
print(f\" {f['id']}: {f.get('status', 'unknown')} — {f.get('title', '')}\")
|
|
1176
|
-
print(len(bad))
|
|
1177
|
-
" "$feature_list" 2>/dev/null || echo "0"; } | tee /dev/stderr | tail -1)
|
|
1178
|
-
|
|
1179
|
-
if [[ "$incomplete_count" -gt 0 ]]; then
|
|
1180
|
-
echo ""
|
|
1181
|
-
log_warn "DEPLOY BLOCKED: $incomplete_count task(s) not completed successfully."
|
|
1182
|
-
log_warn "Fix failed tasks and re-run, or manually run /prizmkit-deploy."
|
|
1183
|
-
else
|
|
1184
|
-
echo ""
|
|
1185
|
-
log_info "All tasks completed — starting deploy session..."
|
|
1186
|
-
log_info "ENABLE_DEPLOY=1"
|
|
1187
|
-
|
|
1188
|
-
local deploy_session_id="deploy-$(date +%Y%m%d%H%M%S)"
|
|
1189
|
-
local deploy_session_dir="$STATE_DIR/deploy/$deploy_session_id"
|
|
1190
|
-
mkdir -p "$deploy_session_dir/logs"
|
|
1191
|
-
|
|
1192
|
-
local deploy_prompt="$deploy_session_dir/bootstrap-prompt.md"
|
|
1193
|
-
local _deploy_branch _deploy_commit
|
|
1194
|
-
_deploy_branch=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
1195
|
-
_deploy_commit=$(git -C "$_proj_root" rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
|
1196
|
-
cat > "$deploy_prompt" << DEPLOY_PROMPT_EOF
|
|
1197
|
-
## Deploy
|
|
1198
|
-
|
|
1199
|
-
All features in the pipeline completed successfully.
|
|
1200
|
-
|
|
1201
|
-
- Branch: $_deploy_branch
|
|
1202
|
-
- Commit: $_deploy_commit
|
|
1203
|
-
|
|
1204
|
-
Run /prizmkit-deploy to deploy the project. Read .prizmkit/deploy/deploy.config.json
|
|
1205
|
-
for deployment configuration. If no deploy config exists, guide the user through
|
|
1206
|
-
setting one up before deploying.
|
|
1207
|
-
DEPLOY_PROMPT_EOF
|
|
1208
|
-
|
|
1209
|
-
log_info "Deploy prompt: $deploy_prompt"
|
|
1210
|
-
log_info "Deploy log: $deploy_session_dir/logs/session.log"
|
|
1211
|
-
|
|
1212
|
-
prizm_run_ai_session "$deploy_prompt" "$deploy_session_dir/logs/session.log"
|
|
1213
|
-
local deploy_exit=$?
|
|
1214
|
-
|
|
1215
|
-
# Recover log from backup if truncated
|
|
1216
|
-
prizm_recover_session_log "$deploy_session_dir/logs/session.log"
|
|
1217
|
-
|
|
1218
|
-
if [[ $deploy_exit -eq 0 ]]; then
|
|
1219
|
-
log_success "Deploy session completed (exit 0)"
|
|
1220
|
-
else
|
|
1221
|
-
log_warn "Deploy session exited with code $deploy_exit"
|
|
1222
|
-
log_warn "Review log: $deploy_session_dir/logs/session.log"
|
|
1223
|
-
fi
|
|
1224
|
-
fi
|
|
1225
|
-
fi
|
|
1226
|
-
|
|
1227
|
-
break
|
|
1228
|
-
fi
|
|
1229
|
-
|
|
1230
|
-
if [[ "$next_feature" == "PIPELINE_BLOCKED" ]]; then
|
|
1231
|
-
log_warn "All remaining features are blocked by dependencies or failed."
|
|
1232
|
-
log_warn "Run './run-feature.sh status' to see details."
|
|
1233
|
-
log_warn "Waiting 60s before re-checking... (Ctrl+C to stop)"
|
|
1234
|
-
sleep 60
|
|
1235
|
-
continue
|
|
1236
|
-
fi
|
|
1237
|
-
|
|
1238
|
-
# Parse feature info
|
|
1239
|
-
local feature_id feature_title retry_count infra_error_count resume_phase active_dev_branch base_branch continuation_pending continuation_reason context_overflow_count continuation_count previous_session_id no_progress_count
|
|
1240
|
-
feature_id=$(echo "$next_feature" | jq -r '.feature_id')
|
|
1241
|
-
feature_title=$(echo "$next_feature" | jq -r '.title')
|
|
1242
|
-
retry_count=$(echo "$next_feature" | jq -r '.retry_count // 0')
|
|
1243
|
-
infra_error_count=$(echo "$next_feature" | jq -r '.infra_error_count // 0')
|
|
1244
|
-
resume_phase=$(echo "$next_feature" | jq -r '.resume_from_phase // "null"')
|
|
1245
|
-
active_dev_branch=$(echo "$next_feature" | jq -r '.active_dev_branch // ""')
|
|
1246
|
-
base_branch=$(echo "$next_feature" | jq -r '.base_branch // ""')
|
|
1247
|
-
continuation_pending=$(echo "$next_feature" | jq -r '.continuation_pending // false')
|
|
1248
|
-
continuation_reason=$(echo "$next_feature" | jq -r '.continuation_reason // ""')
|
|
1249
|
-
context_overflow_count=$(echo "$next_feature" | jq -r '.context_overflow_count // 0')
|
|
1250
|
-
continuation_count=$(echo "$next_feature" | jq -r '.continuation_count // 0')
|
|
1251
|
-
previous_session_id=$(echo "$next_feature" | jq -r '.last_context_overflow_session_id // ""')
|
|
1252
|
-
no_progress_count=$(echo "$next_feature" | jq -r '.no_progress_count // 0')
|
|
1253
|
-
|
|
1254
|
-
echo ""
|
|
1255
|
-
echo -e "${BOLD}────────────────────────────────────────────────────${NC}"
|
|
1256
|
-
log_info "Feature: ${BOLD}$feature_id${NC} — $feature_title"
|
|
1257
|
-
log_info "Code retry: $retry_count / $MAX_RETRIES"
|
|
1258
|
-
log_info "Infrastructure retry: $infra_error_count / $MAX_INFRA_RETRIES"
|
|
1259
|
-
if [[ "$resume_phase" != "null" ]]; then
|
|
1260
|
-
log_info "Resuming from Phase $resume_phase"
|
|
1261
|
-
fi
|
|
1262
|
-
if [[ "$continuation_pending" == "true" && -n "$active_dev_branch" ]]; then
|
|
1263
|
-
log_info "Continuation pending (${continuation_reason:-context_overflow}); reusing dev branch: $active_dev_branch"
|
|
1264
|
-
log_info "Continuation count: $continuation_count (context overflows: $context_overflow_count)"
|
|
1265
|
-
fi
|
|
1266
|
-
echo -e "${BOLD}────────────────────────────────────────────────────${NC}"
|
|
1267
|
-
|
|
1268
|
-
# Auto-commit any uncommitted workspace changes before starting the pipeline.
|
|
1269
|
-
# This ensures a clean baseline without blocking execution.
|
|
1270
|
-
local _dirty_files=""
|
|
1271
|
-
_dirty_files=$(prizm_git_status_non_bookkeeping "$_proj_root" "$STATE_DIR" "$feature_list" || true)
|
|
1272
|
-
if [[ -n "$_dirty_files" ]]; then
|
|
1273
|
-
log_info "Auto-committing workspace changes before $feature_id..."
|
|
1274
|
-
prizm_git_add_all_safe "$_proj_root"
|
|
1275
|
-
git -C "$_proj_root" commit --no-verify -m "chore: workspace snapshot ready for run $feature_id" 2>/dev/null || true
|
|
1276
|
-
fi
|
|
1277
|
-
|
|
1278
|
-
local _bookkeeping_dirty=""
|
|
1279
|
-
_bookkeeping_dirty=$(prizm_git_status_bookkeeping "$_proj_root" "$STATE_DIR" "$feature_list" || true)
|
|
1280
|
-
if [[ -n "$_bookkeeping_dirty" ]]; then
|
|
1281
|
-
log_info "Committing pending pipeline bookkeeping before $feature_id..."
|
|
1282
|
-
prizm_git_commit_paths "$_proj_root" "chore($feature_id): include pipeline bookkeeping" "$feature_list" "$STATE_DIR"
|
|
1283
|
-
fi
|
|
1284
|
-
|
|
1285
|
-
# Mark feature as in-progress BEFORE creating dev branch
|
|
1286
|
-
# This ensures the in_progress status commit lands on the original branch,
|
|
1287
|
-
# not the dev branch — preventing rebase conflicts in branch_merge later.
|
|
1288
|
-
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
1289
|
-
--feature-list "$feature_list" \
|
|
1290
|
-
--state-dir "$STATE_DIR" \
|
|
1291
|
-
--feature-id "$feature_id" \
|
|
1292
|
-
--action start >/dev/null 2>&1 || true
|
|
1293
|
-
# Commit the in_progress status on the original branch
|
|
1294
|
-
if ! git -C "$_proj_root" diff --quiet "$feature_list" 2>/dev/null; then
|
|
1295
|
-
prizm_git_commit_paths "$_proj_root" "chore($feature_id): mark in_progress" "$feature_list"
|
|
1296
|
-
fi
|
|
1297
|
-
|
|
1298
|
-
# Continuation base branch from runtime state is authoritative for branch reuse.
|
|
1299
|
-
if [[ "$continuation_pending" == "true" && -n "$base_branch" ]]; then
|
|
1300
|
-
_ORIGINAL_BRANCH="$base_branch"
|
|
1301
|
-
fi
|
|
1302
|
-
|
|
1303
|
-
# Create or reuse per-feature dev branch (from the now-updated original branch)
|
|
1304
|
-
local _feature_branch
|
|
1305
|
-
if [[ -n "$DEV_BRANCH" ]]; then
|
|
1306
|
-
_feature_branch="$DEV_BRANCH"
|
|
1307
|
-
elif [[ "$continuation_pending" == "true" && -n "$active_dev_branch" ]]; then
|
|
1308
|
-
_feature_branch="$active_dev_branch"
|
|
1309
|
-
else
|
|
1310
|
-
_feature_branch="dev/${feature_id}-$(date +%Y%m%d%H%M)"
|
|
1311
|
-
fi
|
|
1312
|
-
if branch_create "$_proj_root" "$_feature_branch" "$_ORIGINAL_BRANCH"; then
|
|
1313
|
-
_DEV_BRANCH_NAME="$_feature_branch"
|
|
1314
|
-
log_info "Dev branch: $_feature_branch"
|
|
1315
|
-
else
|
|
1316
|
-
log_error "Failed to create dev branch: $_feature_branch from $_ORIGINAL_BRANCH — aborting to avoid running on $_ORIGINAL_BRANCH"
|
|
1317
|
-
_DEV_BRANCH_NAME=""
|
|
1318
|
-
break
|
|
1319
|
-
fi
|
|
1320
|
-
|
|
1321
|
-
while true; do
|
|
1322
|
-
# Generate session ID and bootstrap prompt
|
|
1323
|
-
local session_id run_id
|
|
1324
|
-
run_id=$(jq -r '.run_id' "$STATE_DIR/pipeline.json")
|
|
1325
|
-
session_id="${feature_id}-$(date +%Y%m%d%H%M%S)"
|
|
1326
|
-
|
|
1327
|
-
local session_dir="$STATE_DIR/$feature_id/sessions/$session_id"
|
|
1328
|
-
mkdir -p "$session_dir/logs"
|
|
1329
|
-
|
|
1330
|
-
local bootstrap_prompt="$session_dir/bootstrap-prompt.md"
|
|
1331
|
-
local continuation_summary_path
|
|
1332
|
-
continuation_summary_path=$(python3 -c "
|
|
1333
|
-
import json, re, sys
|
|
1334
|
-
feature_list, feature_id = sys.argv[1], sys.argv[2]
|
|
1335
|
-
with open(feature_list) as f:
|
|
1336
|
-
data = json.load(f)
|
|
1337
|
-
for feature in data.get('features', []):
|
|
1338
|
-
if feature.get('id') == feature_id:
|
|
1339
|
-
numeric = feature_id.replace('F-', '').replace('f-', '').zfill(3)
|
|
1340
|
-
title = feature.get('title', '').lower()
|
|
1341
|
-
title = re.sub(r'[^a-z0-9\s-]', '', title)
|
|
1342
|
-
title = re.sub(r'[\s]+', '-', title.strip())
|
|
1343
|
-
title = re.sub(r'-+', '-', title).strip('-')
|
|
1344
|
-
print(f'.prizmkit/specs/{numeric}-{title}/continuation-summary.md')
|
|
1345
|
-
sys.exit(0)
|
|
1346
|
-
sys.exit(1)
|
|
1347
|
-
" "$feature_list" "$feature_id" 2>/dev/null || echo "")
|
|
1348
|
-
|
|
1349
|
-
local main_prompt_args=(
|
|
1350
|
-
--feature-list "$feature_list"
|
|
1351
|
-
--feature-id "$feature_id"
|
|
1352
|
-
--session-id "$session_id"
|
|
1353
|
-
--run-id "$run_id"
|
|
1354
|
-
--retry-count "$retry_count"
|
|
1355
|
-
--resume-phase "$resume_phase"
|
|
1356
|
-
--state-dir "$STATE_DIR"
|
|
1357
|
-
--output "$bootstrap_prompt"
|
|
1358
|
-
)
|
|
1359
|
-
|
|
1360
|
-
if [[ "$continuation_pending" == "true" ]]; then
|
|
1361
|
-
main_prompt_args+=(
|
|
1362
|
-
--continuation-mode "${continuation_reason:-context_overflow}"
|
|
1363
|
-
--previous-session-id "$previous_session_id"
|
|
1364
|
-
--continuation-count "$continuation_count"
|
|
1365
|
-
--context-overflow-count "$context_overflow_count"
|
|
1366
|
-
--task-type "feature"
|
|
1367
|
-
--active-dev-branch "$active_dev_branch"
|
|
1368
|
-
--base-branch "$base_branch"
|
|
1369
|
-
)
|
|
1370
|
-
if [[ -n "$continuation_summary_path" ]]; then
|
|
1371
|
-
main_prompt_args+=(--continuation-summary-path "$continuation_summary_path")
|
|
1372
|
-
fi
|
|
1373
|
-
fi
|
|
1374
|
-
|
|
1375
|
-
# Support PIPELINE_MODE env var (set by launch-feature-daemon.sh --mode)
|
|
1376
|
-
if [[ -n "${PIPELINE_MODE:-}" ]]; then
|
|
1377
|
-
main_prompt_args+=(--mode "$PIPELINE_MODE")
|
|
1378
|
-
fi
|
|
1379
|
-
|
|
1380
|
-
# Support ENABLE_CRITIC env var (set by launch-feature-daemon.sh --critic)
|
|
1381
|
-
if [[ "${ENABLE_CRITIC:-}" == "true" || "${ENABLE_CRITIC:-}" == "1" ]]; then
|
|
1382
|
-
main_prompt_args+=(--critic "true")
|
|
1383
|
-
elif [[ "${ENABLE_CRITIC:-}" == "false" || "${ENABLE_CRITIC:-}" == "0" ]]; then
|
|
1384
|
-
main_prompt_args+=(--critic "false")
|
|
1385
|
-
fi
|
|
1386
|
-
|
|
1387
|
-
local gen_output
|
|
1388
|
-
gen_output=$(python3 "$SCRIPTS_DIR/generate-bootstrap-prompt.py" "${main_prompt_args[@]}" 2>/dev/null) || {
|
|
1389
|
-
log_error "Failed to generate bootstrap prompt for $feature_id"
|
|
1390
|
-
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
1391
|
-
_DEV_BRANCH_NAME=""
|
|
1392
|
-
continue 2
|
|
1393
|
-
}
|
|
1394
|
-
local feature_model pipeline_mode agent_count critic_enabled
|
|
1395
|
-
feature_model=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('model',''))" 2>/dev/null || echo "")
|
|
1396
|
-
pipeline_mode=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('pipeline_mode','lite'))" 2>/dev/null || echo "lite")
|
|
1397
|
-
agent_count=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('agent_count',1))" 2>/dev/null || echo "1")
|
|
1398
|
-
critic_enabled=$(echo "$gen_output" | python3 -c "import json,sys; print(json.load(sys.stdin).get('critic_enabled','false'))" 2>/dev/null || echo "false")
|
|
1399
|
-
|
|
1400
|
-
# Log pipeline mode and agent configuration
|
|
1401
|
-
local _mode_desc
|
|
1402
|
-
case "$pipeline_mode" in
|
|
1403
|
-
lite) _mode_desc="Tier 1 — Single Agent" ;;
|
|
1404
|
-
standard) _mode_desc="Tier 2 — Orchestrator + Dev + Reviewer" ;;
|
|
1405
|
-
full) _mode_desc="Tier 3 — Full Team (+ Multi-Critic)" ;;
|
|
1406
|
-
*) _mode_desc="$pipeline_mode" ;;
|
|
1407
|
-
esac
|
|
1408
|
-
log_info "Pipeline mode: ${BOLD}$pipeline_mode${NC} ($_mode_desc)"
|
|
1409
|
-
log_info "Agents: $agent_count (critic: $([ "$critic_enabled" = "true" ] && echo "enabled" || echo "disabled"))"
|
|
1410
|
-
|
|
1411
|
-
local continuation_artifact_dir="${continuation_summary_path%/continuation-summary.md}"
|
|
1412
|
-
local continuation_checkpoint_path="$continuation_artifact_dir/workflow-checkpoint.json"
|
|
1413
|
-
local progress_before progress_after
|
|
1414
|
-
progress_before=$(prizm_context_progress_fingerprint "$_proj_root" "feature" "$feature_id" "$continuation_artifact_dir" "$continuation_checkpoint_path")
|
|
1415
|
-
|
|
1416
|
-
# Spawn session and wait
|
|
1417
|
-
prizm_log_bootstrap_prompt "$bootstrap_prompt" "$feature_id"
|
|
1418
|
-
log_info "Spawning AI CLI session: $session_id"
|
|
1419
|
-
if [[ -n "$feature_model" ]]; then
|
|
1420
|
-
log_info "Feature model: $feature_model"
|
|
1421
|
-
fi
|
|
1422
|
-
_SPAWN_RESULT=""
|
|
1423
|
-
_SPAWN_ITEM_STATUS=""
|
|
1424
|
-
|
|
1425
|
-
spawn_and_wait_session \
|
|
1426
|
-
"$feature_id" "$feature_list" "$session_id" \
|
|
1427
|
-
"$bootstrap_prompt" "$session_dir" "$MAX_RETRIES" "$feature_model" "$_ORIGINAL_BRANCH"
|
|
1428
|
-
local session_status="$_SPAWN_RESULT"
|
|
1429
|
-
|
|
1430
|
-
# Merge per-feature dev branch back to original on success
|
|
1431
|
-
local active_dev_branch_for_status="$_DEV_BRANCH_NAME"
|
|
1432
|
-
if [[ "$session_status" == "context_overflow" ]]; then
|
|
1433
|
-
progress_after=$(prizm_context_progress_fingerprint "$_proj_root" "feature" "$feature_id" "$continuation_artifact_dir" "$continuation_checkpoint_path")
|
|
1434
|
-
if prizm_context_progress_changed "$progress_before" "$progress_after"; then
|
|
1435
|
-
no_progress_count=0
|
|
1436
|
-
log_info "Context-overflow session made observable progress; continuing on $_DEV_BRANCH_NAME"
|
|
1437
|
-
else
|
|
1438
|
-
no_progress_count=$((no_progress_count + 1))
|
|
1439
|
-
log_warn "Context-overflow session made no observable progress ($no_progress_count consecutive)"
|
|
1440
|
-
fi
|
|
1441
|
-
if [[ "$no_progress_count" -ge 2 ]]; then
|
|
1442
|
-
log_warn "Pausing automatic continuation: context_overflow_without_git_checkpoint_or_artifact_progress"
|
|
1443
|
-
session_status="stalled_context_continuation"
|
|
1444
|
-
else
|
|
1445
|
-
finalize_feature_status_after_branch_return \
|
|
1446
|
-
"$feature_id" "$feature_list" "$session_id" "$session_status" "$MAX_RETRIES" "$MAX_INFRA_RETRIES" "$session_dir" "$_ORIGINAL_BRANCH" "$active_dev_branch_for_status" "context_overflow" "$continuation_summary_path" "$no_progress_count" "$progress_after"
|
|
1447
|
-
session_count=$((session_count + 1))
|
|
1448
|
-
total_subagent_calls=$((total_subagent_calls + _SUBAGENT_COUNT))
|
|
1449
|
-
continuation_pending="true"
|
|
1450
|
-
continuation_reason="context_overflow"
|
|
1451
|
-
previous_session_id="$session_id"
|
|
1452
|
-
active_dev_branch="$_DEV_BRANCH_NAME"
|
|
1453
|
-
base_branch="$_ORIGINAL_BRANCH"
|
|
1454
|
-
context_overflow_count=$((context_overflow_count + 1))
|
|
1455
|
-
continuation_count=$((continuation_count + 1))
|
|
1456
|
-
log_info "Launching automatic continuation for $feature_id on same branch/worktree"
|
|
1457
|
-
continue
|
|
1458
|
-
fi
|
|
1459
|
-
fi
|
|
1460
|
-
|
|
1461
|
-
if [[ "$session_status" == "success" && -n "$_DEV_BRANCH_NAME" ]]; then
|
|
1462
|
-
if branch_merge "$_proj_root" "$_DEV_BRANCH_NAME" "$_ORIGINAL_BRANCH" "$AUTO_PUSH"; then
|
|
1463
|
-
_DEV_BRANCH_NAME=""
|
|
1464
|
-
else
|
|
1465
|
-
log_warn "Auto-merge failed — dev branch preserved: $_DEV_BRANCH_NAME"
|
|
1466
|
-
log_warn "Merge manually: git checkout $_ORIGINAL_BRANCH && git rebase $_DEV_BRANCH_NAME"
|
|
1467
|
-
session_status="merge_conflict"
|
|
1468
|
-
fi
|
|
1469
|
-
elif [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
1470
|
-
# Session failed — preserve dev branch for inspection
|
|
1471
|
-
log_warn "Session failed — dev branch preserved for inspection: $_DEV_BRANCH_NAME"
|
|
1472
|
-
fi
|
|
1473
|
-
|
|
1474
|
-
# GUARANTEED: always return to original branch regardless of success/failure/merge outcome
|
|
1475
|
-
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
1476
|
-
|
|
1477
|
-
finalize_feature_status_after_branch_return \
|
|
1478
|
-
"$feature_id" "$feature_list" "$session_id" "$session_status" "$MAX_RETRIES" "$MAX_INFRA_RETRIES" "$session_dir" "$_ORIGINAL_BRANCH" "$active_dev_branch_for_status" "context_overflow" "$continuation_summary_path" "$no_progress_count" "${progress_after:-}"
|
|
1479
|
-
local item_status_after_session="${_SPAWN_ITEM_STATUS:-}"
|
|
1480
|
-
|
|
1481
|
-
# Commit feature status update on the original branch (after guaranteed return)
|
|
1482
|
-
if ! git -C "$_proj_root" diff --quiet "$feature_list" 2>/dev/null; then
|
|
1483
|
-
prizm_git_commit_paths "$_proj_root" "chore($feature_id): update feature status" "$feature_list"
|
|
1484
|
-
fi
|
|
1485
|
-
|
|
1486
|
-
session_count=$((session_count + 1))
|
|
1487
|
-
total_subagent_calls=$((total_subagent_calls + _SUBAGENT_COUNT))
|
|
1488
|
-
break
|
|
1489
|
-
done
|
|
1490
|
-
|
|
1491
|
-
# Stop-on-failure: abort only after the task is actually marked failed.
|
|
1492
|
-
# Pending retry outcomes keep running; both code and infra failures have
|
|
1493
|
-
# bounded budgets in update-feature-status.py.
|
|
1494
|
-
if [[ "$session_status" != "success" && "$STOP_ON_FAILURE" == "1" && "$item_status_after_session" == "failed" ]]; then
|
|
1495
|
-
echo ""
|
|
1496
|
-
log_error "════════════════════════════════════════════════════"
|
|
1497
|
-
log_error " STOP_ON_FAILURE: Pipeline halted after $feature_id exhausted retries."
|
|
1498
|
-
log_error " Total sessions completed: $session_count"
|
|
1499
|
-
log_error " Set STOP_ON_FAILURE=0 to continue past failures."
|
|
1500
|
-
log_error "════════════════════════════════════════════════════"
|
|
1501
|
-
break
|
|
1502
|
-
elif [[ "$session_status" != "success" && "$STOP_ON_FAILURE" == "1" ]]; then
|
|
1503
|
-
log_info "STOP_ON_FAILURE: $feature_id is ${item_status_after_session:-unknown}; code/infra retry budget not exhausted, continuing."
|
|
1504
|
-
fi
|
|
1505
|
-
|
|
1506
|
-
# Brief pause before next iteration
|
|
1507
|
-
log_info "Pausing 5s before next feature..."
|
|
1508
|
-
sleep 5
|
|
1509
|
-
done
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
# ============================================================
|
|
1513
|
-
# Entry Point
|
|
1514
|
-
# ============================================================
|
|
1515
|
-
|
|
1516
|
-
show_help() {
|
|
1517
|
-
echo "Usage: $0 <command> [options]"
|
|
1518
|
-
echo ""
|
|
1519
|
-
echo "Commands:"
|
|
1520
|
-
echo " run [.prizmkit/plans/feature-list.json] [--features <filter>] Run features (all or filtered subset)"
|
|
1521
|
-
echo " run <feature-id> [options] Run a single feature"
|
|
1522
|
-
echo " status [.prizmkit/plans/feature-list.json] Show pipeline status"
|
|
1523
|
-
echo " unskip [feature-id] [.prizmkit/plans/feature-list.json] Reset auto-skipped/failed features"
|
|
1524
|
-
echo " test-cli Test AI CLI: show detected CLI, version, and model"
|
|
1525
|
-
echo " reset Clear all state and start fresh"
|
|
1526
|
-
echo " help Show this help message"
|
|
1527
|
-
echo ""
|
|
1528
|
-
echo "Feature Filter (--features):"
|
|
1529
|
-
echo " --features F-001,F-003,F-005 Run only specified features"
|
|
1530
|
-
echo " --features F-001:F-010 Run a range of features (F-001 through F-010)"
|
|
1531
|
-
echo " --features F-001,F-005:F-010 Mixed: individual IDs + ranges"
|
|
1532
|
-
echo ""
|
|
1533
|
-
echo "Single Feature Options (run <feature-id>):"
|
|
1534
|
-
echo " --dry-run Generate bootstrap prompt only, don't spawn session"
|
|
1535
|
-
echo " --resume-phase N Override resume phase (default: auto-detect)"
|
|
1536
|
-
echo " --mode <lite|standard|full> Override pipeline mode (bypasses estimated_complexity)"
|
|
1537
|
-
echo " --critic Enable adversarial critic review for this feature"
|
|
1538
|
-
echo " --no-critic Disable critic review (overrides feature-list setting)"
|
|
1539
|
-
echo " --clean Delete artifacts and reset before running"
|
|
1540
|
-
echo " --no-reset Skip feature status reset step"
|
|
1541
|
-
echo " --max-infra-retries N Max infrastructure/provider retries (default: 3)"
|
|
1542
|
-
echo ""
|
|
1543
|
-
echo "Pipeline Options (run all):"
|
|
1544
|
-
echo " --max-infra-retries N Max infrastructure/provider retries (default: 3)"
|
|
1545
|
-
echo ""
|
|
1546
|
-
echo "Environment Variables:"
|
|
1547
|
-
echo " MAX_RETRIES Max code retries per feature (default: 3)"
|
|
1548
|
-
echo " MAX_INFRA_RETRIES Max infrastructure/provider retries per feature (default: 3)"
|
|
1549
|
-
echo " AI_CLI AI CLI command name (auto-detected: cbc, claude, or codex)"
|
|
1550
|
-
echo " MODEL AI model ID (e.g. claude-opus-4.6, claude-sonnet-4.6, claude-haiku-4.5)"
|
|
1551
|
-
echo " PRIZMKIT_EFFORT AI reasoning effort (low|medium|high|xhigh|max; max is Claude Code only)"
|
|
1552
|
-
echo " HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)"
|
|
1553
|
-
echo " STALE_KILL_THRESHOLD Auto-kill session after N seconds of no progress (default: 900)"
|
|
1554
|
-
echo " HEARTBEAT_STALE_THRESHOLD Heartbeat stale threshold in seconds (default: 600)"
|
|
1555
|
-
echo " MAX_LOG_SIZE Include passive log-size checkpoint guidance above this threshold (default: 2097152 = 2MB, set 0 to disable)"
|
|
1556
|
-
echo " PIPELINE_MODE Override mode for all features: lite|standard|full"
|
|
1557
|
-
echo " ENABLE_CRITIC Enable critic review for all features: true|false"
|
|
1558
|
-
echo " STOP_ON_FAILURE Stop pipeline when a task exhausts retries: 0|1 (default: 0)"
|
|
1559
|
-
echo ""
|
|
1560
|
-
echo "Examples:"
|
|
1561
|
-
echo " ./run-feature.sh run # Run all features"
|
|
1562
|
-
echo " ./run-feature.sh run --features F-001,F-003,F-005 # Run specific features"
|
|
1563
|
-
echo " ./run-feature.sh run --features F-001:F-010 # Run features F-001 through F-010"
|
|
1564
|
-
echo " ./run-feature.sh run F-007 --dry-run # Inspect generated prompt"
|
|
1565
|
-
echo " ./run-feature.sh run F-007 --dry-run --mode lite # Test lite mode"
|
|
1566
|
-
echo " ./run-feature.sh run F-007 --resume-phase 6 # Skip to implementation"
|
|
1567
|
-
echo " ./run-feature.sh run F-007 --clean --mode standard # Clean + run standard"
|
|
1568
|
-
echo " ./run-feature.sh status # Show pipeline status"
|
|
1569
|
-
echo " MODEL=claude-sonnet-4.6 ./run-feature.sh run # Use Sonnet model"
|
|
1570
|
-
echo " MODEL=claude-haiku-4.5 ./run-feature.sh test-cli # Test with Haiku"
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
case "${1:-run}" in
|
|
1574
|
-
run|resume)
|
|
1575
|
-
shift || true
|
|
1576
|
-
# Check if first arg is a feature ID (F-xxx pattern)
|
|
1577
|
-
if [[ "${1:-}" =~ ^[Ff]-[0-9]+ ]]; then
|
|
1578
|
-
run_one "$@"
|
|
1579
|
-
else
|
|
1580
|
-
# Parse positional and --features flag
|
|
1581
|
-
_run_feature_list="$PRIZMKIT_DIR/plans/feature-list.json"
|
|
1582
|
-
_run_features_filter=""
|
|
1583
|
-
while [[ $# -gt 0 ]]; do
|
|
1584
|
-
case "$1" in
|
|
1585
|
-
--features)
|
|
1586
|
-
shift
|
|
1587
|
-
if [[ $# -eq 0 ]]; then
|
|
1588
|
-
log_error "--features requires a value (e.g. --features F-001,F-003 or --features F-001:F-010)"
|
|
1589
|
-
exit 1
|
|
1590
|
-
fi
|
|
1591
|
-
_run_features_filter="$1"
|
|
1592
|
-
shift
|
|
1593
|
-
;;
|
|
1594
|
-
--max-infra-retries)
|
|
1595
|
-
shift
|
|
1596
|
-
if [[ $# -eq 0 ]]; then
|
|
1597
|
-
log_error "--max-infra-retries requires a value"
|
|
1598
|
-
exit 1
|
|
1599
|
-
fi
|
|
1600
|
-
prizm_require_positive_int "--max-infra-retries" "$1"
|
|
1601
|
-
MAX_INFRA_RETRIES="$1"
|
|
1602
|
-
shift
|
|
1603
|
-
;;
|
|
1604
|
-
*)
|
|
1605
|
-
_run_feature_list="$1"
|
|
1606
|
-
shift
|
|
1607
|
-
;;
|
|
1608
|
-
esac
|
|
1609
|
-
done
|
|
1610
|
-
main "$_run_feature_list" "$_run_features_filter"
|
|
1611
|
-
fi
|
|
1612
|
-
;;
|
|
1613
|
-
status)
|
|
1614
|
-
check_dependencies
|
|
1615
|
-
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
1616
|
-
log_error "No pipeline state found. Run './run-feature.sh run' first."
|
|
1617
|
-
exit 1
|
|
1618
|
-
fi
|
|
1619
|
-
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
1620
|
-
--feature-list "${2:-$PRIZMKIT_DIR/plans/feature-list.json}" \
|
|
1621
|
-
--state-dir "$STATE_DIR" \
|
|
1622
|
-
--action status
|
|
1623
|
-
;;
|
|
1624
|
-
test-cli)
|
|
1625
|
-
echo ""
|
|
1626
|
-
echo "============================================"
|
|
1627
|
-
echo " Dev-Pipeline AI CLI Test"
|
|
1628
|
-
echo "============================================"
|
|
1629
|
-
echo ""
|
|
1630
|
-
echo " Detected CLI: $CLI_CMD"
|
|
1631
|
-
echo " Platform: $PLATFORM"
|
|
1632
|
-
if [[ -n "$MODEL" ]]; then
|
|
1633
|
-
echo " Requested Model: $MODEL"
|
|
1634
|
-
fi
|
|
1635
|
-
|
|
1636
|
-
# Get CLI version (first line only)
|
|
1637
|
-
cli_version=$("$CLI_CMD" -v 2>&1 | head -1 || echo "unknown")
|
|
1638
|
-
echo " CLI Version: $cli_version"
|
|
1639
|
-
echo ""
|
|
1640
|
-
echo " Querying AI model (headless mode)..."
|
|
1641
|
-
|
|
1642
|
-
test_prompt="What AI assistant/platform are you and what model are you running? Reply in one line, e.g. \"I'm Claude Code Claude Opnus x.x\".No extra text."
|
|
1643
|
-
|
|
1644
|
-
# Run headless query with 30s timeout (background + kill pattern for macOS)
|
|
1645
|
-
tmpfile=$(mktemp)
|
|
1646
|
-
prompt_file=$(mktemp)
|
|
1647
|
-
printf '%s\n' "$test_prompt" > "$prompt_file"
|
|
1648
|
-
(
|
|
1649
|
-
prizm_run_ai_session "$prompt_file" "$tmpfile" "$MODEL"
|
|
1650
|
-
) &
|
|
1651
|
-
query_pid=$!
|
|
1652
|
-
( sleep 30 && kill "$query_pid" 2>/dev/null ) &
|
|
1653
|
-
timer_pid=$!
|
|
1654
|
-
wait "$query_pid" 2>/dev/null
|
|
1655
|
-
kill "$timer_pid" 2>/dev/null
|
|
1656
|
-
wait "$timer_pid" 2>/dev/null || true
|
|
1657
|
-
|
|
1658
|
-
model_reply=$(cat "$tmpfile" 2>/dev/null | head -3)
|
|
1659
|
-
rm -f "$tmpfile" "$prompt_file"
|
|
1660
|
-
|
|
1661
|
-
if [[ -z "$model_reply" ]]; then
|
|
1662
|
-
model_reply="(no response — CLI may require auth or is unavailable)"
|
|
1663
|
-
fi
|
|
1664
|
-
|
|
1665
|
-
echo ""
|
|
1666
|
-
echo " AI Response: $model_reply"
|
|
1667
|
-
echo ""
|
|
1668
|
-
echo "============================================"
|
|
1669
|
-
echo ""
|
|
1670
|
-
;;
|
|
1671
|
-
reset)
|
|
1672
|
-
log_warn "Resetting pipeline state..."
|
|
1673
|
-
rm -rf "$STATE_DIR"
|
|
1674
|
-
log_success "State cleared. Run './run-feature.sh run' to start fresh."
|
|
1675
|
-
;;
|
|
1676
|
-
unskip)
|
|
1677
|
-
check_dependencies
|
|
1678
|
-
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
1679
|
-
log_error "No pipeline state found. Run './run-feature.sh run' first."
|
|
1680
|
-
exit 1
|
|
1681
|
-
fi
|
|
1682
|
-
_unskip_feature_list="$PRIZMKIT_DIR/plans/feature-list.json"
|
|
1683
|
-
_unskip_feature_id=""
|
|
1684
|
-
shift || true
|
|
1685
|
-
# Parse arguments: optional feature-id and feature-list path
|
|
1686
|
-
while [[ $# -gt 0 ]]; do
|
|
1687
|
-
if [[ "$1" =~ ^[Ff]-[0-9]+ ]]; then
|
|
1688
|
-
_unskip_feature_id="$1"
|
|
1689
|
-
else
|
|
1690
|
-
_unskip_feature_list="$1"
|
|
1691
|
-
fi
|
|
1692
|
-
shift
|
|
1693
|
-
done
|
|
1694
|
-
_unskip_args=(
|
|
1695
|
-
--feature-list "$_unskip_feature_list"
|
|
1696
|
-
--state-dir "$STATE_DIR"
|
|
1697
|
-
--action unskip
|
|
1698
|
-
)
|
|
1699
|
-
if [[ -n "$_unskip_feature_id" ]]; then
|
|
1700
|
-
_unskip_args+=(--feature-id "$_unskip_feature_id")
|
|
1701
|
-
fi
|
|
1702
|
-
python3 "$SCRIPTS_DIR/update-feature-status.py" "${_unskip_args[@]}"
|
|
1703
|
-
|
|
1704
|
-
# Commit the status change
|
|
1705
|
-
if ! git diff --quiet "$_unskip_feature_list" 2>/dev/null; then
|
|
1706
|
-
git add "$_unskip_feature_list" 2>/dev/null || true
|
|
1707
|
-
git commit -m "chore: unskip auto-skipped features" 2>/dev/null || true
|
|
1708
|
-
fi
|
|
1709
|
-
;;
|
|
1710
|
-
help|--help|-h)
|
|
1711
|
-
show_help
|
|
1712
|
-
;;
|
|
1713
|
-
*)
|
|
1714
|
-
log_error "Unknown command: $1"
|
|
1715
|
-
show_help
|
|
1716
|
-
exit 1
|
|
1717
|
-
;;
|
|
1718
|
-
esac
|