prizmkit 1.1.99 → 1.1.101
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 +345 -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 +147 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +422 -0
- package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +552 -0
- package/bundled/dev-pipeline/prizmkit_runtime/status.py +71 -0
- package/bundled/dev-pipeline/scripts/check-session-status.py +74 -1
- package/bundled/dev-pipeline/scripts/continuation.py +374 -0
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +64 -52
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +35 -25
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +2 -2
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +15 -20
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +185 -16
- package/bundled/dev-pipeline/scripts/update-bug-status.py +214 -6
- package/bundled/dev-pipeline/scripts/update-feature-status.py +237 -6
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +214 -6
- package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +8 -2
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +4 -0
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +14 -5
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +16 -6
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +16 -6
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +1 -1
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +3 -3
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +2 -0
- package/bundled/dev-pipeline/templates/sections/log-size-awareness.md +13 -69
- 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/test-failure-recovery-agent.md +4 -0
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +4 -0
- package/bundled/dev-pipeline/templates/session-status-schema.json +1 -1
- package/bundled/dev-pipeline/tests/conftest.py +1 -0
- package/bundled/dev-pipeline/tests/test_auto_skip.py +932 -0
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +207 -0
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +100 -0
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +43 -0
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +1138 -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 +33 -12
- package/src/index.js +3 -6
- package/src/manifest.js +3 -2
- package/src/metadata.js +7 -18
- package/src/prompts.js +0 -12
- package/src/runtimes.js +18 -11
- package/src/scaffold.js +133 -116
- package/src/upgrade.js +1 -1
- 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 -1257
- package/bundled/dev-pipeline/lib/heartbeat.sh +0 -469
- package/bundled/dev-pipeline/reset-bug.sh +0 -395
- package/bundled/dev-pipeline/reset-feature.sh +0 -406
- package/bundled/dev-pipeline/reset-refactor.sh +0 -393
- package/bundled/dev-pipeline/run-bugfix.sh +0 -1315
- package/bundled/dev-pipeline/run-feature.sh +0 -1602
- package/bundled/dev-pipeline/run-recovery.sh +0 -648
- package/bundled/dev-pipeline/run-refactor.sh +0 -1348
- package/bundled/dev-pipeline/scripts/monitor-log.sh +0 -104
- 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 -829
- package/bundled/dev-pipeline-windows/lib/daemon.ps1 +0 -140
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +0 -967
- package/bundled/dev-pipeline-windows/lib/reset.ps1 +0 -87
- package/bundled/dev-pipeline-windows/reset-bug.ps1 +0 -9
- package/bundled/dev-pipeline-windows/reset-feature.ps1 +0 -9
- package/bundled/dev-pipeline-windows/reset-refactor.ps1 +0 -9
- 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 -228
- package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +0 -530
- package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +0 -2282
- package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +0 -767
- package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +0 -805
- package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +0 -845
- 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/monitor-log.ps1 +0 -102
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +0 -1232
- 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 -913
- package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +0 -173
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +0 -1558
- package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +0 -1119
- 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 -78
- 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,1257 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ============================================================
|
|
3
|
-
# dev-pipeline/lib/common.sh - Shared shell helpers
|
|
4
|
-
#
|
|
5
|
-
# Shared by feature and bugfix pipeline runners.
|
|
6
|
-
# Provides:
|
|
7
|
-
# - CLI/platform detection
|
|
8
|
-
# - Common color + log helpers
|
|
9
|
-
# - Common dependency checks
|
|
10
|
-
# ============================================================
|
|
11
|
-
|
|
12
|
-
# Colors
|
|
13
|
-
RED='\033[0;31m'
|
|
14
|
-
GREEN='\033[0;32m'
|
|
15
|
-
YELLOW='\033[1;33m'
|
|
16
|
-
BLUE='\033[0;34m'
|
|
17
|
-
MAGENTA='\033[0;35m'
|
|
18
|
-
BOLD='\033[1m'
|
|
19
|
-
NC='\033[0m'
|
|
20
|
-
|
|
21
|
-
log_info() { echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
22
|
-
log_warn() { echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
23
|
-
log_error() { echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
24
|
-
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
25
|
-
|
|
26
|
-
prizm_require_positive_int() {
|
|
27
|
-
local option_name="$1"
|
|
28
|
-
local option_value="$2"
|
|
29
|
-
if [[ ! "$option_value" =~ ^[1-9][0-9]*$ ]]; then
|
|
30
|
-
log_error "$option_name must be a positive integer: $option_value"
|
|
31
|
-
exit 1
|
|
32
|
-
fi
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
# Ignore temporary git worktrees created under root-level hidden AI tool dirs
|
|
36
|
-
# matching .<tool>/worktree(s), without hiding normal source directories.
|
|
37
|
-
PRIZM_GIT_HIDDEN_TOOL_WORKTREE_EXCLUDES=(
|
|
38
|
-
':(top,exclude,glob).*/worktree'
|
|
39
|
-
':(top,exclude,glob).*/worktree/**'
|
|
40
|
-
':(top,exclude,glob).*/worktrees'
|
|
41
|
-
':(top,exclude,glob).*/worktrees/**'
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
prizm_git_status_safe() {
|
|
45
|
-
local project_root="$1"
|
|
46
|
-
git -C "$project_root" status --porcelain -- . \
|
|
47
|
-
"${PRIZM_GIT_HIDDEN_TOOL_WORKTREE_EXCLUDES[@]}" 2>/dev/null
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
prizm_git_add_all_safe() {
|
|
51
|
-
local project_root="$1"
|
|
52
|
-
git -C "$project_root" add -A -- . \
|
|
53
|
-
"${PRIZM_GIT_HIDDEN_TOOL_WORKTREE_EXCLUDES[@]}" 2>/dev/null
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
prizm_git_relative_path() {
|
|
57
|
-
local project_root="$1"
|
|
58
|
-
local target_path="$2"
|
|
59
|
-
python3 - "$project_root" "$target_path" <<'PY' 2>/dev/null || true
|
|
60
|
-
import os
|
|
61
|
-
import sys
|
|
62
|
-
root = os.path.abspath(sys.argv[1])
|
|
63
|
-
target = os.path.abspath(sys.argv[2])
|
|
64
|
-
print(os.path.relpath(target, root).replace(os.sep, "/"))
|
|
65
|
-
PY
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
prizm_git_status_non_bookkeeping() {
|
|
69
|
-
local project_root="$1"
|
|
70
|
-
local state_dir="$2"
|
|
71
|
-
local list_path="$3"
|
|
72
|
-
|
|
73
|
-
local state_rel list_rel
|
|
74
|
-
state_rel="$(prizm_git_relative_path "$project_root" "$state_dir")"
|
|
75
|
-
list_rel="$(prizm_git_relative_path "$project_root" "$list_path")"
|
|
76
|
-
|
|
77
|
-
local line path
|
|
78
|
-
while IFS= read -r line; do
|
|
79
|
-
[[ -n "$line" ]] || continue
|
|
80
|
-
path="${line:3}"
|
|
81
|
-
if [[ "$path" == *" -> "* ]]; then
|
|
82
|
-
path="${path##* -> }"
|
|
83
|
-
fi
|
|
84
|
-
path="${path%\"}"
|
|
85
|
-
path="${path#\"}"
|
|
86
|
-
path="${path#./}"
|
|
87
|
-
if [[ -n "$list_rel" && "$path" == "$list_rel" ]]; then
|
|
88
|
-
continue
|
|
89
|
-
fi
|
|
90
|
-
if [[ -n "$state_rel" && ( "$path" == "$state_rel" || "$path" == "$state_rel/"* ) ]]; then
|
|
91
|
-
continue
|
|
92
|
-
fi
|
|
93
|
-
printf '%s\n' "$line"
|
|
94
|
-
done < <(prizm_git_status_safe "$project_root")
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
prizm_git_status_bookkeeping() {
|
|
98
|
-
local project_root="$1"
|
|
99
|
-
local state_dir="$2"
|
|
100
|
-
local list_path="$3"
|
|
101
|
-
|
|
102
|
-
local state_rel list_rel
|
|
103
|
-
state_rel="$(prizm_git_relative_path "$project_root" "$state_dir")"
|
|
104
|
-
list_rel="$(prizm_git_relative_path "$project_root" "$list_path")"
|
|
105
|
-
|
|
106
|
-
local line path
|
|
107
|
-
while IFS= read -r line; do
|
|
108
|
-
[[ -n "$line" ]] || continue
|
|
109
|
-
path="${line:3}"
|
|
110
|
-
if [[ "$path" == *" -> "* ]]; then
|
|
111
|
-
path="${path##* -> }"
|
|
112
|
-
fi
|
|
113
|
-
path="${path%\"}"
|
|
114
|
-
path="${path#\"}"
|
|
115
|
-
path="${path#./}"
|
|
116
|
-
if [[ -n "$list_rel" && "$path" == "$list_rel" ]]; then
|
|
117
|
-
printf '%s\n' "$line"
|
|
118
|
-
continue
|
|
119
|
-
fi
|
|
120
|
-
if [[ -n "$state_rel" && ( "$path" == "$state_rel" || "$path" == "$state_rel/"* ) ]]; then
|
|
121
|
-
printf '%s\n' "$line"
|
|
122
|
-
fi
|
|
123
|
-
done < <(prizm_git_status_safe "$project_root")
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
prizm_git_commit_paths() {
|
|
127
|
-
local project_root="$1"
|
|
128
|
-
local message="$2"
|
|
129
|
-
shift 2
|
|
130
|
-
|
|
131
|
-
local paths=()
|
|
132
|
-
local path rel_path
|
|
133
|
-
for path in "$@"; do
|
|
134
|
-
[[ -n "$path" && -e "$path" ]] || continue
|
|
135
|
-
rel_path="$(prizm_git_relative_path "$project_root" "$path")"
|
|
136
|
-
[[ -n "$rel_path" ]] || continue
|
|
137
|
-
paths+=("$rel_path")
|
|
138
|
-
done
|
|
139
|
-
[[ ${#paths[@]} -gt 0 ]] || return 0
|
|
140
|
-
|
|
141
|
-
git -C "$project_root" add -- "${paths[@]}" 2>/dev/null || true
|
|
142
|
-
if ! git -C "$project_root" diff --cached --quiet -- "${paths[@]}" 2>/dev/null; then
|
|
143
|
-
git -C "$project_root" commit --no-verify -m "$message" -- "${paths[@]}" 2>/dev/null || true
|
|
144
|
-
fi
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
# ============================================================
|
|
149
|
-
# Path resolution (single source of truth)
|
|
150
|
-
# ============================================================
|
|
151
|
-
# Exports three directories used throughout dev-pipeline scripts:
|
|
152
|
-
# PIPELINE_DIR — directory containing the dev-pipeline scripts
|
|
153
|
-
# PRIZMKIT_DIR — the .prizmkit/ runtime directory
|
|
154
|
-
# PROJECT_ROOT — the user's project root
|
|
155
|
-
#
|
|
156
|
-
# Layout detection (no override case):
|
|
157
|
-
# - If PIPELINE_DIR's parent is named ".prizmkit" → user-install layout
|
|
158
|
-
# (.prizmkit/dev-pipeline/). PRIZMKIT_DIR = parent.
|
|
159
|
-
# - Otherwise → framework repo source (<repo>/dev-pipeline/).
|
|
160
|
-
# PROJECT_ROOT = parent, PRIZMKIT_DIR = <repo>/.prizmkit.
|
|
161
|
-
#
|
|
162
|
-
# Callers (tests) may pre-set any of these env vars to override detection.
|
|
163
|
-
if [[ -z "${PIPELINE_DIR:-}" ]]; then
|
|
164
|
-
PIPELINE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
165
|
-
fi
|
|
166
|
-
if [[ -z "${PRIZMKIT_DIR:-}" || -z "${PROJECT_ROOT:-}" ]]; then
|
|
167
|
-
_pipeline_parent="$(dirname "$PIPELINE_DIR")"
|
|
168
|
-
if [[ "$(basename "$_pipeline_parent")" == ".prizmkit" ]]; then
|
|
169
|
-
: "${PRIZMKIT_DIR:=$_pipeline_parent}"
|
|
170
|
-
: "${PROJECT_ROOT:=$(dirname "$_pipeline_parent")}"
|
|
171
|
-
else
|
|
172
|
-
: "${PROJECT_ROOT:=$_pipeline_parent}"
|
|
173
|
-
: "${PRIZMKIT_DIR:=$PROJECT_ROOT/.prizmkit}"
|
|
174
|
-
fi
|
|
175
|
-
unset _pipeline_parent
|
|
176
|
-
fi
|
|
177
|
-
export PIPELINE_DIR PRIZMKIT_DIR PROJECT_ROOT
|
|
178
|
-
|
|
179
|
-
# ============================================================
|
|
180
|
-
# .env file loading
|
|
181
|
-
# ============================================================
|
|
182
|
-
|
|
183
|
-
# Load .env file if it exists. Does NOT override already-set env vars.
|
|
184
|
-
# Supports: KEY=VALUE, KEY="VALUE", KEY='VALUE', comments (#), empty lines.
|
|
185
|
-
# If the file does not exist, silently continues (no error).
|
|
186
|
-
prizm_load_env() {
|
|
187
|
-
local env_file="${1:-.env}"
|
|
188
|
-
[[ -f "$env_file" ]] || return 0
|
|
189
|
-
|
|
190
|
-
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
191
|
-
# Skip empty lines and comments
|
|
192
|
-
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
|
|
193
|
-
# Remove inline comments (not inside quotes)
|
|
194
|
-
line="${line%%#*}"
|
|
195
|
-
# Trim whitespace
|
|
196
|
-
line="$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
|
197
|
-
[[ -z "$line" ]] && continue
|
|
198
|
-
# Parse KEY=VALUE
|
|
199
|
-
if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
|
|
200
|
-
local key="${BASH_REMATCH[1]}"
|
|
201
|
-
local val="${BASH_REMATCH[2]}"
|
|
202
|
-
# Strip surrounding quotes
|
|
203
|
-
val="${val#\"}" ; val="${val%\"}"
|
|
204
|
-
val="${val#\'}" ; val="${val%\'}"
|
|
205
|
-
# Only set if not already defined in environment
|
|
206
|
-
if [[ -z "${!key+x}" ]]; then
|
|
207
|
-
export "$key=$val"
|
|
208
|
-
fi
|
|
209
|
-
fi
|
|
210
|
-
done < "$env_file"
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
# ============================================================
|
|
214
|
-
# Test mode: bootstrap prompt logging
|
|
215
|
-
# ============================================================
|
|
216
|
-
|
|
217
|
-
# Log bootstrap prompt content when PRIZMKIT_ENV=test.
|
|
218
|
-
# Called after prompt generation, before AI CLI session spawn.
|
|
219
|
-
# Usage: prizm_log_bootstrap_prompt <prompt_path> <item_id>
|
|
220
|
-
prizm_log_bootstrap_prompt() {
|
|
221
|
-
local prompt_path="$1"
|
|
222
|
-
local item_id="$2"
|
|
223
|
-
|
|
224
|
-
[[ "${PRIZMKIT_ENV:-}" == "test" ]] || return 0
|
|
225
|
-
[[ -f "$prompt_path" ]] || return 0
|
|
226
|
-
|
|
227
|
-
local lines size
|
|
228
|
-
lines=$(wc -l < "$prompt_path" 2>/dev/null | tr -d ' ')
|
|
229
|
-
size=$(wc -c < "$prompt_path" 2>/dev/null | tr -d ' ')
|
|
230
|
-
|
|
231
|
-
echo ""
|
|
232
|
-
echo -e "${MAGENTA}[TEST]${NC} ════════════════════════════════════════════════════"
|
|
233
|
-
echo -e "${MAGENTA}[TEST]${NC} Bootstrap Prompt for $item_id"
|
|
234
|
-
echo -e "${MAGENTA}[TEST]${NC} Lines: $lines | Size: $((size / 1024))KB"
|
|
235
|
-
echo -e "${MAGENTA}[TEST]${NC} Path: $prompt_path"
|
|
236
|
-
echo -e "${MAGENTA}[TEST]${NC} ════════════════════════════════════════════════════"
|
|
237
|
-
cat "$prompt_path"
|
|
238
|
-
echo ""
|
|
239
|
-
echo -e "${MAGENTA}[TEST]${NC} ════════════════════════ END ═══════════════════════"
|
|
240
|
-
echo ""
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
# Detect AI CLI + platform.
|
|
244
|
-
# Priority:
|
|
245
|
-
# AI_CLI env > .prizmkit/config.json ai_cli > manifest/config platform > CODEBUDDY_CLI
|
|
246
|
-
# > installed PrizmKit platform artifacts > PATH auto-detect > error
|
|
247
|
-
#
|
|
248
|
-
# Exports:
|
|
249
|
-
# CLI_CMD
|
|
250
|
-
# PLATFORM
|
|
251
|
-
# PRIZMKIT_PLATFORM
|
|
252
|
-
_prizm_json_value() {
|
|
253
|
-
local json_file="$1"
|
|
254
|
-
local key="$2"
|
|
255
|
-
[[ -f "$json_file" ]] || return 0
|
|
256
|
-
JSON_FILE="$json_file" JSON_KEY="$key" python3 - <<'PY' 2>/dev/null || true
|
|
257
|
-
import json
|
|
258
|
-
import os
|
|
259
|
-
|
|
260
|
-
try:
|
|
261
|
-
with open(os.environ["JSON_FILE"], encoding="utf-8") as fh:
|
|
262
|
-
data = json.load(fh)
|
|
263
|
-
value = data.get(os.environ["JSON_KEY"], "")
|
|
264
|
-
if value is not None and value != "":
|
|
265
|
-
print(value)
|
|
266
|
-
except Exception:
|
|
267
|
-
pass
|
|
268
|
-
PY
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
_prizm_normalize_platform() {
|
|
272
|
-
printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
_prizm_concrete_platform() {
|
|
276
|
-
case "$(_prizm_normalize_platform "$1")" in
|
|
277
|
-
codex|claude|codebuddy) printf '%s\n' "$(_prizm_normalize_platform "$1")" ;;
|
|
278
|
-
esac
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
_prizm_cli_names_for_platform() {
|
|
282
|
-
case "$(_prizm_normalize_platform "$1")" in
|
|
283
|
-
codex) printf '%s\n' codex ;;
|
|
284
|
-
claude) printf '%s\n' claude ;;
|
|
285
|
-
codebuddy) printf '%s\n' cbc ;;
|
|
286
|
-
both) printf '%s\n' claude cbc ;;
|
|
287
|
-
all) printf '%s\n' codex claude cbc ;;
|
|
288
|
-
esac
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
_prizm_expected_cli_text() {
|
|
292
|
-
_prizm_cli_names_for_platform "$1" | awk 'BEGIN { sep="" } { printf "%s%s", sep, $0; sep=", " } END { if (sep != "") print "" }'
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
_prizm_resolve_cli_name() {
|
|
296
|
-
local cli_name="$1"
|
|
297
|
-
if [[ "$cli_name" == "cbc" && -n "${CODEBUDDY_CLI:-}" ]]; then
|
|
298
|
-
printf '%s\n' "$CODEBUDDY_CLI"
|
|
299
|
-
return 0
|
|
300
|
-
fi
|
|
301
|
-
if command -v "$cli_name" &>/dev/null; then
|
|
302
|
-
printf '%s\n' "$cli_name"
|
|
303
|
-
return 0
|
|
304
|
-
fi
|
|
305
|
-
return 1
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
_prizm_cli_for_platform() {
|
|
309
|
-
local platform="$1"
|
|
310
|
-
local cli_name cli_cmd
|
|
311
|
-
while IFS= read -r cli_name; do
|
|
312
|
-
[[ -n "$cli_name" ]] || continue
|
|
313
|
-
cli_cmd="$(_prizm_resolve_cli_name "$cli_name" || true)"
|
|
314
|
-
if [[ -n "$cli_cmd" ]]; then
|
|
315
|
-
printf '%s\n' "$cli_cmd"
|
|
316
|
-
return 0
|
|
317
|
-
fi
|
|
318
|
-
done < <(_prizm_cli_names_for_platform "$platform")
|
|
319
|
-
return 1
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
_prizm_known_platform_from_cli() {
|
|
323
|
-
local cli_cmd="$1"
|
|
324
|
-
local cli_token="${cli_cmd%%[[:space:]]*}"
|
|
325
|
-
cli_token="${cli_token#\"}" ; cli_token="${cli_token%\"}"
|
|
326
|
-
cli_token="${cli_token#\'}" ; cli_token="${cli_token%\'}"
|
|
327
|
-
local cli_name="${cli_token##*/}"
|
|
328
|
-
if [[ "$cli_name" == *"claude"* ]]; then
|
|
329
|
-
printf '%s\n' claude
|
|
330
|
-
elif [[ "$cli_name" == *"codex"* ]]; then
|
|
331
|
-
printf '%s\n' codex
|
|
332
|
-
elif [[ "$cli_name" == *"cbc"* || "$cli_name" == *"codebuddy"* ]]; then
|
|
333
|
-
printf '%s\n' codebuddy
|
|
334
|
-
else
|
|
335
|
-
return 1
|
|
336
|
-
fi
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
_prizm_platform_from_cli() {
|
|
340
|
-
local cli_cmd="$1"
|
|
341
|
-
if ! _prizm_known_platform_from_cli "$cli_cmd"; then
|
|
342
|
-
printf '%s\n' codebuddy
|
|
343
|
-
fi
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
_prizm_session_platform() {
|
|
347
|
-
local cli_platform
|
|
348
|
-
cli_platform="$(_prizm_known_platform_from_cli "$CLI_CMD" || true)"
|
|
349
|
-
if [[ -n "$cli_platform" ]]; then
|
|
350
|
-
printf '%s\n' "$cli_platform"
|
|
351
|
-
return 0
|
|
352
|
-
fi
|
|
353
|
-
|
|
354
|
-
local platform="${PRIZMKIT_PLATFORM:-${PLATFORM:-}}"
|
|
355
|
-
case "$(_prizm_normalize_platform "$platform")" in
|
|
356
|
-
codex|claude|codebuddy) printf '%s\n' "$(_prizm_normalize_platform "$platform")" ;;
|
|
357
|
-
*) _prizm_platform_from_cli "$CLI_CMD" ;;
|
|
358
|
-
esac
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
_prizm_project_platform_for_cli() {
|
|
362
|
-
local cli_cmd="$1"
|
|
363
|
-
local manifest_platform="$2"
|
|
364
|
-
local config_platform="$3"
|
|
365
|
-
local cli_platform
|
|
366
|
-
cli_platform="$(_prizm_known_platform_from_cli "$cli_cmd" || true)"
|
|
367
|
-
if [[ -n "$cli_platform" ]]; then
|
|
368
|
-
printf '%s\n' "$cli_platform"
|
|
369
|
-
elif [[ -n "${PRIZMKIT_PLATFORM:-}" ]]; then
|
|
370
|
-
printf '%s\n' "$PRIZMKIT_PLATFORM"
|
|
371
|
-
elif [[ -n "$(_prizm_concrete_platform "$manifest_platform")" ]]; then
|
|
372
|
-
_prizm_concrete_platform "$manifest_platform"
|
|
373
|
-
elif [[ -n "$(_prizm_concrete_platform "$config_platform")" ]]; then
|
|
374
|
-
_prizm_concrete_platform "$config_platform"
|
|
375
|
-
else
|
|
376
|
-
printf '%s\n' codebuddy
|
|
377
|
-
fi
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
prizm_detect_cli_and_platform() {
|
|
381
|
-
# Load .env from .prizmkit/.env if it exists (does not override existing env vars)
|
|
382
|
-
if [[ -n "${PRIZMKIT_DIR:-}" ]]; then
|
|
383
|
-
prizm_load_env "$PRIZMKIT_DIR/.env"
|
|
384
|
-
fi
|
|
385
|
-
|
|
386
|
-
local _raw_cli=""
|
|
387
|
-
local _config_ai_cli=""
|
|
388
|
-
local _config_platform=""
|
|
389
|
-
local _manifest_platform=""
|
|
390
|
-
local _installed_cli=""
|
|
391
|
-
local _platform_cli=""
|
|
392
|
-
local _platform_cli_names=""
|
|
393
|
-
|
|
394
|
-
if [[ -n "${PRIZMKIT_DIR:-}" ]]; then
|
|
395
|
-
_config_ai_cli="$(_prizm_json_value "$PRIZMKIT_DIR/config.json" ai_cli)"
|
|
396
|
-
_config_platform="$(_prizm_json_value "$PRIZMKIT_DIR/config.json" platform)"
|
|
397
|
-
_manifest_platform="$(_prizm_json_value "$PRIZMKIT_DIR/manifest.json" platform)"
|
|
398
|
-
fi
|
|
399
|
-
|
|
400
|
-
_detect_installed_platform_cli() {
|
|
401
|
-
if [[ -f "$PROJECT_ROOT/.codex/agents/prizm-dev-team-dev.toml" || -f "$PROJECT_ROOT/.agents/skills/prizm-kit/SKILL.md" ]] && command -v codex &>/dev/null; then
|
|
402
|
-
echo "codex"
|
|
403
|
-
elif [[ -f "$PROJECT_ROOT/.claude/commands/prizm-kit.md" ]] && command -v claude &>/dev/null; then
|
|
404
|
-
echo "claude"
|
|
405
|
-
elif [[ -f "$PROJECT_ROOT/.codebuddy/agents/prizm-dev-team-dev.md" || -f "$PROJECT_ROOT/.codebuddy/skills/prizm-kit/SKILL.md" ]]; then
|
|
406
|
-
_prizm_resolve_cli_name cbc || true
|
|
407
|
-
fi
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if [[ -n "${AI_CLI:-}" ]]; then
|
|
411
|
-
_raw_cli="$AI_CLI"
|
|
412
|
-
elif [[ -n "$_config_ai_cli" ]]; then
|
|
413
|
-
_raw_cli="$_config_ai_cli"
|
|
414
|
-
elif [[ -n "$_manifest_platform" ]]; then
|
|
415
|
-
_platform_cli="$(_prizm_cli_for_platform "$_manifest_platform" || true)"
|
|
416
|
-
_platform_cli_names="$(_prizm_cli_names_for_platform "$_manifest_platform")"
|
|
417
|
-
if [[ -n "$_platform_cli" ]]; then
|
|
418
|
-
_raw_cli="$_platform_cli"
|
|
419
|
-
elif [[ -n "$_platform_cli_names" ]]; then
|
|
420
|
-
echo "ERROR: Installed PrizmKit platform '$_manifest_platform' requires one of: $(_prizm_expected_cli_text "$_manifest_platform"). Install it or set AI_CLI." >&2
|
|
421
|
-
exit 1
|
|
422
|
-
fi
|
|
423
|
-
elif [[ -n "$_config_platform" ]]; then
|
|
424
|
-
_platform_cli="$(_prizm_cli_for_platform "$_config_platform" || true)"
|
|
425
|
-
_platform_cli_names="$(_prizm_cli_names_for_platform "$_config_platform")"
|
|
426
|
-
if [[ -n "$_platform_cli" ]]; then
|
|
427
|
-
_raw_cli="$_platform_cli"
|
|
428
|
-
elif [[ -n "$_platform_cli_names" ]]; then
|
|
429
|
-
echo "ERROR: Configured PrizmKit platform '$_config_platform' requires one of: $(_prizm_expected_cli_text "$_config_platform"). Install it or set AI_CLI." >&2
|
|
430
|
-
exit 1
|
|
431
|
-
fi
|
|
432
|
-
fi
|
|
433
|
-
|
|
434
|
-
if [[ -z "$_raw_cli" && -n "${CODEBUDDY_CLI:-}" ]]; then
|
|
435
|
-
_raw_cli="$CODEBUDDY_CLI"
|
|
436
|
-
fi
|
|
437
|
-
if [[ -z "$_raw_cli" ]]; then
|
|
438
|
-
_installed_cli="$(_detect_installed_platform_cli || true)"
|
|
439
|
-
if [[ -n "$_installed_cli" ]]; then
|
|
440
|
-
_raw_cli="$_installed_cli"
|
|
441
|
-
fi
|
|
442
|
-
fi
|
|
443
|
-
if [[ -z "$_raw_cli" ]] && command -v claude &>/dev/null; then
|
|
444
|
-
_raw_cli="claude"
|
|
445
|
-
elif [[ -z "$_raw_cli" ]] && command -v cbc &>/dev/null; then
|
|
446
|
-
_raw_cli="cbc"
|
|
447
|
-
elif [[ -z "$_raw_cli" ]] && command -v codex &>/dev/null; then
|
|
448
|
-
_raw_cli="codex"
|
|
449
|
-
elif [[ -z "$_raw_cli" ]]; then
|
|
450
|
-
echo "ERROR: No AI CLI found. Install CodeBuddy (cbc), Claude Code (claude), or Codex (codex)." >&2
|
|
451
|
-
exit 1
|
|
452
|
-
fi
|
|
453
|
-
|
|
454
|
-
CLI_CMD="$_raw_cli"
|
|
455
|
-
|
|
456
|
-
PLATFORM="$(_prizm_project_platform_for_cli "$_raw_cli" "$_manifest_platform" "$_config_platform")"
|
|
457
|
-
|
|
458
|
-
export CLI_CMD
|
|
459
|
-
export PLATFORM
|
|
460
|
-
export PRIZMKIT_PLATFORM="$PLATFORM"
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
# Validate PRIZMKIT_EFFORT against the detected platform's supported values.
|
|
464
|
-
# Exits with a friendly error if the value is unsupported.
|
|
465
|
-
prizm_validate_effort() {
|
|
466
|
-
[[ -n "${PRIZMKIT_EFFORT:-}" ]] || return 0
|
|
467
|
-
|
|
468
|
-
local session_platform
|
|
469
|
-
session_platform="$(_prizm_session_platform)"
|
|
470
|
-
|
|
471
|
-
local allowed_values
|
|
472
|
-
case "$session_platform" in
|
|
473
|
-
claude) allowed_values="low, medium, high, xhigh, max" ;;
|
|
474
|
-
codex) allowed_values="low, medium, high, xhigh" ;;
|
|
475
|
-
*) allowed_values="low, medium, high, xhigh" ;;
|
|
476
|
-
esac
|
|
477
|
-
|
|
478
|
-
local valid=0
|
|
479
|
-
local val
|
|
480
|
-
for val in $allowed_values; do
|
|
481
|
-
val="${val%,}"
|
|
482
|
-
[[ "$val" == "$PRIZMKIT_EFFORT" ]] && valid=1 && break
|
|
483
|
-
done
|
|
484
|
-
|
|
485
|
-
if [[ "$valid" -ne 1 ]]; then
|
|
486
|
-
local platform_label="$session_platform"
|
|
487
|
-
[[ "$session_platform" == "claude" ]] && platform_label="Claude Code"
|
|
488
|
-
[[ "$session_platform" == "codebuddy" ]] && platform_label="CodeBuddy"
|
|
489
|
-
[[ "$session_platform" == "codex" ]] && platform_label="Codex"
|
|
490
|
-
|
|
491
|
-
log_error "PRIZMKIT_EFFORT='$PRIZMKIT_EFFORT' is not supported by the detected CLI ($platform_label)."
|
|
492
|
-
log_error " Supported values for $platform_label: $allowed_values"
|
|
493
|
-
log_error " Set PRIZMKIT_EFFORT to one of the above, or unset it to use the CLI default."
|
|
494
|
-
exit 1
|
|
495
|
-
fi
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
# Start an AI CLI session in the background.
|
|
499
|
-
# Usage: prizm_start_ai_session <prompt_path> <log_path> <model>
|
|
500
|
-
# Sets PRIZM_AI_PID to the spawned process PID. Do not call this function via
|
|
501
|
-
# command substitution; the background process must remain a child of the
|
|
502
|
-
# runner shell so wait/heartbeat/trap handling works correctly.
|
|
503
|
-
PRIZM_AI_PID=""
|
|
504
|
-
|
|
505
|
-
_prizm_codex_subagent_timeout_seconds() {
|
|
506
|
-
local configured="${CODEX_SUBAGENT_TIMEOUT_SECONDS:-}"
|
|
507
|
-
if [[ "$configured" =~ ^[0-9]+$ && "$configured" -gt 0 ]]; then
|
|
508
|
-
printf '%s\n' "$configured"
|
|
509
|
-
return 0
|
|
510
|
-
fi
|
|
511
|
-
|
|
512
|
-
local wait_threshold="${CODEX_WAIT_STALE_KILL_THRESHOLD:-3600}"
|
|
513
|
-
if [[ "$wait_threshold" =~ ^[0-9]+$ && "$wait_threshold" -gt 600 ]]; then
|
|
514
|
-
printf '%s\n' "$((wait_threshold - 300))"
|
|
515
|
-
return 0
|
|
516
|
-
fi
|
|
517
|
-
|
|
518
|
-
printf '%s\n' 3300
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
prizm_start_ai_session() {
|
|
522
|
-
local prompt_path="$1"
|
|
523
|
-
local log_path="$2"
|
|
524
|
-
local model="${3:-}"
|
|
525
|
-
|
|
526
|
-
unset CLAUDECODE 2>/dev/null || true
|
|
527
|
-
|
|
528
|
-
# Create backup outside project tree. Claude Code subagents can unlink
|
|
529
|
-
# session.log inside the project, but cannot reach $HOME/.prizmkit/.
|
|
530
|
-
# tee writes to the backup file while >> redirects to the main log.
|
|
531
|
-
# If the main log gets severed (unlinked+recreated by subagent),
|
|
532
|
-
# prizm_recover_session_log copies the backup back when the session ends.
|
|
533
|
-
local backup_dir="$HOME/.prizmkit/session-backups"
|
|
534
|
-
mkdir -p "$backup_dir"
|
|
535
|
-
local session_dir
|
|
536
|
-
session_dir="$(dirname "$(dirname "$log_path")")"
|
|
537
|
-
local session_id
|
|
538
|
-
session_id="$(basename "$session_dir")"
|
|
539
|
-
local backup_log="$backup_dir/${session_id}.log"
|
|
540
|
-
PRIZM_BACKUP_LOG="$backup_log"
|
|
541
|
-
|
|
542
|
-
# Write preamble to both files
|
|
543
|
-
{
|
|
544
|
-
echo "=== PRIZMKIT SESSION START ==="
|
|
545
|
-
echo "Prompt: $prompt_path"
|
|
546
|
-
echo "Model: ${model:-default}"
|
|
547
|
-
echo "Started at: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
548
|
-
echo "--- OUTPUT BELOW ---"
|
|
549
|
-
} > "$log_path"
|
|
550
|
-
cp "$log_path" "$backup_log"
|
|
551
|
-
|
|
552
|
-
local session_platform
|
|
553
|
-
session_platform="$(_prizm_session_platform)"
|
|
554
|
-
|
|
555
|
-
case "$session_platform" in
|
|
556
|
-
claude)
|
|
557
|
-
local claude_args=(-p "$(cat "$prompt_path")" --dangerously-skip-permissions)
|
|
558
|
-
if [[ "$VERBOSE" == "1" || "$USE_STREAM_JSON" == "true" ]]; then
|
|
559
|
-
claude_args+=(--verbose)
|
|
560
|
-
fi
|
|
561
|
-
if [[ "$USE_STREAM_JSON" == "true" ]]; then
|
|
562
|
-
claude_args+=(--output-format stream-json)
|
|
563
|
-
fi
|
|
564
|
-
if [[ -n "${PRIZMKIT_EFFORT:-}" ]]; then
|
|
565
|
-
claude_args+=(--effort "$PRIZMKIT_EFFORT")
|
|
566
|
-
fi
|
|
567
|
-
if [[ -n "$model" ]]; then
|
|
568
|
-
claude_args+=(--model "$model")
|
|
569
|
-
fi
|
|
570
|
-
"$CLI_CMD" "${claude_args[@]}" 2>&1 | tee -a "$backup_log" >> "$log_path" &
|
|
571
|
-
;;
|
|
572
|
-
codex)
|
|
573
|
-
local codex_args=(--ask-for-approval never --sandbox danger-full-access)
|
|
574
|
-
local codex_subagent_timeout
|
|
575
|
-
codex_subagent_timeout="$(_prizm_codex_subagent_timeout_seconds)"
|
|
576
|
-
if [[ "$codex_subagent_timeout" =~ ^[0-9]+$ && "$codex_subagent_timeout" -gt 0 ]]; then
|
|
577
|
-
codex_args+=(--config "agents.job_max_runtime_seconds=$codex_subagent_timeout")
|
|
578
|
-
fi
|
|
579
|
-
codex_args+=(exec --cd "$PROJECT_ROOT" --skip-git-repo-check)
|
|
580
|
-
if [[ "$USE_STREAM_JSON" == "true" ]]; then
|
|
581
|
-
codex_args+=(--json)
|
|
582
|
-
fi
|
|
583
|
-
if [[ -n "$model" ]]; then
|
|
584
|
-
codex_args+=(--model "$model")
|
|
585
|
-
fi
|
|
586
|
-
if [[ -n "${PRIZMKIT_EFFORT:-}" ]]; then
|
|
587
|
-
codex_args+=(--config "model_reasoning_effort=$PRIZMKIT_EFFORT")
|
|
588
|
-
fi
|
|
589
|
-
"$CLI_CMD" "${codex_args[@]}" - < "$prompt_path" 2>&1 | tee -a "$backup_log" >> "$log_path" &
|
|
590
|
-
;;
|
|
591
|
-
*)
|
|
592
|
-
local cb_args=(--print -y)
|
|
593
|
-
if [[ "$VERBOSE" == "1" ]]; then
|
|
594
|
-
cb_args+=(--verbose)
|
|
595
|
-
fi
|
|
596
|
-
if [[ "$USE_STREAM_JSON" == "true" ]]; then
|
|
597
|
-
cb_args+=(--output-format stream-json)
|
|
598
|
-
fi
|
|
599
|
-
if [[ -n "${PRIZMKIT_EFFORT:-}" ]]; then
|
|
600
|
-
cb_args+=(--effort "$PRIZMKIT_EFFORT")
|
|
601
|
-
fi
|
|
602
|
-
if [[ -n "$model" ]]; then
|
|
603
|
-
cb_args+=(--model "$model")
|
|
604
|
-
fi
|
|
605
|
-
"$CLI_CMD" "${cb_args[@]}" < "$prompt_path" 2>&1 | tee -a "$backup_log" >> "$log_path" &
|
|
606
|
-
;;
|
|
607
|
-
esac
|
|
608
|
-
PRIZM_AI_PID=$!
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
# Detect AI CLI/provider infrastructure failures that are outside the
|
|
612
|
-
# generated code's control. These should be retried without consuming the
|
|
613
|
-
# item's code retry budget.
|
|
614
|
-
prizm_detect_infra_error() {
|
|
615
|
-
local session_log="${1:-}"
|
|
616
|
-
local progress_json="${2:-}"
|
|
617
|
-
|
|
618
|
-
local haystack=""
|
|
619
|
-
if [[ -n "$session_log" && -f "$session_log" ]]; then
|
|
620
|
-
haystack="$(tail -c 65536 "$session_log" 2>/dev/null || true)"
|
|
621
|
-
fi
|
|
622
|
-
if [[ -n "$progress_json" && -f "$progress_json" ]]; then
|
|
623
|
-
haystack+=$'\n'
|
|
624
|
-
haystack+="$(cat "$progress_json" 2>/dev/null || true)"
|
|
625
|
-
fi
|
|
626
|
-
|
|
627
|
-
[[ -n "$haystack" ]] || return 1
|
|
628
|
-
|
|
629
|
-
if printf '%s' "$haystack" | grep -Eiq \
|
|
630
|
-
'auth_unavailable|no auth available|502 Bad Gateway|503 Service Unavailable|504 Gateway Timeout|gateway timeout|upstream (connect )?error|connection reset|ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|rate limit|rate_limit|temporarily unavailable|overloaded'; then
|
|
631
|
-
return 0
|
|
632
|
-
fi
|
|
633
|
-
|
|
634
|
-
return 1
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
# Detect AI runtime/provider request failures that are not caused by generated
|
|
638
|
-
# project code. Unlike generic infra errors, these can be deterministic for the
|
|
639
|
-
# current transcript (for example context_too_large), so the runner must first
|
|
640
|
-
# check semantic completion before deciding whether to retry.
|
|
641
|
-
prizm_detect_ai_runtime_error() {
|
|
642
|
-
local session_log="${1:-}"
|
|
643
|
-
local progress_json="${2:-}"
|
|
644
|
-
|
|
645
|
-
if [[ -n "$progress_json" && -f "$progress_json" ]]; then
|
|
646
|
-
local fatal_error_code
|
|
647
|
-
fatal_error_code=$(python3 - "$progress_json" <<'PY' 2>/dev/null || true
|
|
648
|
-
import json
|
|
649
|
-
import sys
|
|
650
|
-
|
|
651
|
-
try:
|
|
652
|
-
with open(sys.argv[1], encoding="utf-8") as fh:
|
|
653
|
-
progress = json.load(fh)
|
|
654
|
-
except Exception:
|
|
655
|
-
raise SystemExit(0)
|
|
656
|
-
|
|
657
|
-
code = progress.get("fatal_error_code")
|
|
658
|
-
if code:
|
|
659
|
-
print(str(code))
|
|
660
|
-
PY
|
|
661
|
-
)
|
|
662
|
-
if [[ -n "$fatal_error_code" ]]; then
|
|
663
|
-
return 0
|
|
664
|
-
fi
|
|
665
|
-
fi
|
|
666
|
-
|
|
667
|
-
local haystack=""
|
|
668
|
-
if [[ -n "$session_log" && -f "$session_log" ]]; then
|
|
669
|
-
haystack="$(tail -c 65536 "$session_log" 2>/dev/null || true)"
|
|
670
|
-
fi
|
|
671
|
-
if [[ -n "$progress_json" && -f "$progress_json" ]]; then
|
|
672
|
-
haystack+=$'\n'
|
|
673
|
-
haystack+="$(cat "$progress_json" 2>/dev/null || true)"
|
|
674
|
-
fi
|
|
675
|
-
|
|
676
|
-
[[ -n "$haystack" ]] || return 1
|
|
677
|
-
|
|
678
|
-
if printf '%s' "$haystack" | grep -Eiq \
|
|
679
|
-
'context_too_large|model_context_window_exceeded|input exceeds the context window|context window of this model|context window (was )?exceeded|exceeded (the )?context window|invalid_request_error.*context window|context window.*invalid_request_error'; then
|
|
680
|
-
if printf '%s' "$haystack" | grep -Eiq \
|
|
681
|
-
'api error|invalid_request_error|api_error_status|api_error_code|status[[:space:]]*[:=]?[[:space:]]*(400|413)|last_result_is_error[[:space:]"'\'':=]+true|is_error[[:space:]"'\'':=]+true'; then
|
|
682
|
-
return 0
|
|
683
|
-
fi
|
|
684
|
-
fi
|
|
685
|
-
|
|
686
|
-
return 1
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
prizm_feature_slug_from_list() {
|
|
690
|
-
local feature_list="$1"
|
|
691
|
-
local feature_id="$2"
|
|
692
|
-
python3 - "$feature_list" "$feature_id" <<'PY'
|
|
693
|
-
import json
|
|
694
|
-
import re
|
|
695
|
-
import sys
|
|
696
|
-
|
|
697
|
-
feature_list, feature_id = sys.argv[1], sys.argv[2]
|
|
698
|
-
with open(feature_list, encoding="utf-8") as fh:
|
|
699
|
-
data = json.load(fh)
|
|
700
|
-
for feature in data.get("features", []):
|
|
701
|
-
if feature.get("id") == feature_id:
|
|
702
|
-
number = feature.get("id", "").replace("F-", "").replace("f-", "").zfill(3)
|
|
703
|
-
title = str(feature.get("title", "")).lower()
|
|
704
|
-
title = re.sub(r"[^a-z0-9\s-]", "", title)
|
|
705
|
-
title = re.sub(r"[\s]+", "-", title.strip())
|
|
706
|
-
title = re.sub(r"-+", "-", title).strip("-")
|
|
707
|
-
print(f"{number}-{title}" if title else number)
|
|
708
|
-
raise SystemExit(0)
|
|
709
|
-
raise SystemExit(1)
|
|
710
|
-
PY
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
prizm_checkpoint_all_complete() {
|
|
714
|
-
local checkpoint_file="$1"
|
|
715
|
-
[[ -f "$checkpoint_file" ]] || return 1
|
|
716
|
-
python3 - "$checkpoint_file" <<'PY'
|
|
717
|
-
import json
|
|
718
|
-
import sys
|
|
719
|
-
|
|
720
|
-
try:
|
|
721
|
-
with open(sys.argv[1], encoding="utf-8") as fh:
|
|
722
|
-
data = json.load(fh)
|
|
723
|
-
except Exception:
|
|
724
|
-
raise SystemExit(2)
|
|
725
|
-
steps = data.get("steps")
|
|
726
|
-
if not isinstance(steps, list) or not steps:
|
|
727
|
-
raise SystemExit(1)
|
|
728
|
-
for step in steps:
|
|
729
|
-
if not isinstance(step, dict) or step.get("status") not in ("completed", "skipped"):
|
|
730
|
-
raise SystemExit(1)
|
|
731
|
-
raise SystemExit(0)
|
|
732
|
-
PY
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
# Return the successful feature commit SHA if one exists in base_ref..HEAD.
|
|
736
|
-
# Prefer messages containing the feature ID. If checkpoint is complete, allow an
|
|
737
|
-
# older prompt variant only when the non-WIP commit identifies the feature title.
|
|
738
|
-
prizm_find_feature_commit() {
|
|
739
|
-
local project_root="$1"
|
|
740
|
-
local base_ref="$2"
|
|
741
|
-
local feature_id="$3"
|
|
742
|
-
local allow_title_fallback="${4:-false}"
|
|
743
|
-
local feature_title="${5:-}"
|
|
744
|
-
|
|
745
|
-
git -C "$project_root" rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 1
|
|
746
|
-
|
|
747
|
-
local range="${base_ref}..HEAD"
|
|
748
|
-
if [[ -z "$base_ref" ]] || ! git -C "$project_root" rev-parse --verify "$base_ref" >/dev/null 2>&1; then
|
|
749
|
-
range="HEAD"
|
|
750
|
-
fi
|
|
751
|
-
|
|
752
|
-
local feature_commit
|
|
753
|
-
feature_commit=$(git -C "$project_root" log "$range" --format='%H%x09%s' 2>/dev/null \
|
|
754
|
-
| awk -F '\t' -v fid="$feature_id" 'index($2, fid) > 0 && $2 !~ /^wip(\(|:)/ { print $1; exit }')
|
|
755
|
-
if [[ -n "$feature_commit" ]]; then
|
|
756
|
-
printf '%s\n' "$feature_commit"
|
|
757
|
-
return 0
|
|
758
|
-
fi
|
|
759
|
-
|
|
760
|
-
if [[ "$allow_title_fallback" == "true" && -n "$feature_title" ]]; then
|
|
761
|
-
feature_commit=$(git -C "$project_root" log "$range" --format='%H%x09%s' 2>/dev/null \
|
|
762
|
-
| python3 -c '
|
|
763
|
-
import re
|
|
764
|
-
import sys
|
|
765
|
-
|
|
766
|
-
title = sys.argv[1]
|
|
767
|
-
|
|
768
|
-
def words(text):
|
|
769
|
-
return [w for w in re.split(r"[^a-z0-9]+", text.lower()) if len(w) >= 3]
|
|
770
|
-
|
|
771
|
-
title_words = words(title)
|
|
772
|
-
if not title_words:
|
|
773
|
-
raise SystemExit(1)
|
|
774
|
-
# Require all title words for short titles, or a strong majority for longer
|
|
775
|
-
# titles so punctuation/articles do not make older commits unmatchable.
|
|
776
|
-
required = len(title_words) if len(title_words) <= 3 else max(3, int(len(title_words) * 0.75 + 0.999))
|
|
777
|
-
for line in sys.stdin:
|
|
778
|
-
commit, sep, subject = line.rstrip("\n").partition("\t")
|
|
779
|
-
if not sep or re.match(r"^wip(\(|:)", subject):
|
|
780
|
-
continue
|
|
781
|
-
subject_words = set(words(subject))
|
|
782
|
-
if sum(1 for word in title_words if word in subject_words) >= required:
|
|
783
|
-
print(commit)
|
|
784
|
-
raise SystemExit(0)
|
|
785
|
-
raise SystemExit(1)
|
|
786
|
-
' "$feature_title")
|
|
787
|
-
if [[ -n "$feature_commit" ]]; then
|
|
788
|
-
printf '%s\n' "$feature_commit"
|
|
789
|
-
return 0
|
|
790
|
-
fi
|
|
791
|
-
fi
|
|
792
|
-
|
|
793
|
-
return 1
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
# Semantic completion means the durable workflow checkpoint is complete and a
|
|
797
|
-
# non-WIP feature commit exists. This intentionally runs before exit-code based
|
|
798
|
-
# failure classification so delayed post-commit model errors cannot strand a
|
|
799
|
-
# completed feature on its dev branch.
|
|
800
|
-
PRIZM_SEMANTIC_FEATURE_SLUG=""
|
|
801
|
-
PRIZM_SEMANTIC_COMMIT_SHA=""
|
|
802
|
-
prizm_feature_semantically_complete() {
|
|
803
|
-
local feature_list="$1"
|
|
804
|
-
local feature_id="$2"
|
|
805
|
-
local project_root="$3"
|
|
806
|
-
local base_ref="$4"
|
|
807
|
-
local prizmkit_dir="$5"
|
|
808
|
-
|
|
809
|
-
PRIZM_SEMANTIC_FEATURE_SLUG=""
|
|
810
|
-
PRIZM_SEMANTIC_COMMIT_SHA=""
|
|
811
|
-
|
|
812
|
-
local feature_slug
|
|
813
|
-
feature_slug=$(prizm_feature_slug_from_list "$feature_list" "$feature_id" 2>/dev/null) || return 1
|
|
814
|
-
local checkpoint_file="$prizmkit_dir/specs/${feature_slug}/workflow-checkpoint.json"
|
|
815
|
-
prizm_checkpoint_all_complete "$checkpoint_file" || return 1
|
|
816
|
-
|
|
817
|
-
local feature_title
|
|
818
|
-
feature_title=$(python3 - "$feature_list" "$feature_id" <<'PY' 2>/dev/null || true
|
|
819
|
-
import json
|
|
820
|
-
import sys
|
|
821
|
-
with open(sys.argv[1], encoding="utf-8") as fh:
|
|
822
|
-
data = json.load(fh)
|
|
823
|
-
for feature in data.get("features", []):
|
|
824
|
-
if feature.get("id") == sys.argv[2]:
|
|
825
|
-
print(feature.get("title", ""))
|
|
826
|
-
break
|
|
827
|
-
PY
|
|
828
|
-
)
|
|
829
|
-
|
|
830
|
-
local commit_sha
|
|
831
|
-
commit_sha=$(prizm_find_feature_commit "$project_root" "$base_ref" "$feature_id" true "$feature_title" 2>/dev/null) || return 1
|
|
832
|
-
|
|
833
|
-
PRIZM_SEMANTIC_FEATURE_SLUG="$feature_slug"
|
|
834
|
-
PRIZM_SEMANTIC_COMMIT_SHA="$commit_sha"
|
|
835
|
-
return 0
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
prizm_synthesize_failure_log() {
|
|
839
|
-
local failure_log="$1"
|
|
840
|
-
local feature_id="$2"
|
|
841
|
-
local session_id="$3"
|
|
842
|
-
local session_status="$4"
|
|
843
|
-
local exit_code="$5"
|
|
844
|
-
local stale_kill_marker="$6"
|
|
845
|
-
local progress_json="$7"
|
|
846
|
-
local checkpoint_file="$8"
|
|
847
|
-
local project_root="$9"
|
|
848
|
-
local base_ref="${10:-}"
|
|
849
|
-
|
|
850
|
-
[[ -n "$failure_log" ]] || return 0
|
|
851
|
-
[[ -f "$failure_log" ]] && return 0
|
|
852
|
-
mkdir -p "$(dirname "$failure_log")" || return 0
|
|
853
|
-
|
|
854
|
-
local progress_summary="Progress data unavailable."
|
|
855
|
-
if [[ -f "$progress_json" ]]; then
|
|
856
|
-
progress_summary=$(python3 - "$progress_json" <<'PY' 2>/dev/null || true
|
|
857
|
-
import json
|
|
858
|
-
import sys
|
|
859
|
-
try:
|
|
860
|
-
with open(sys.argv[1], encoding="utf-8") as fh:
|
|
861
|
-
data = json.load(fh)
|
|
862
|
-
except Exception as exc:
|
|
863
|
-
print(f"Progress parse error: {exc}")
|
|
864
|
-
raise SystemExit(0)
|
|
865
|
-
fields = [
|
|
866
|
-
("fatal_error_code", data.get("fatal_error_code")),
|
|
867
|
-
("api_error_status", data.get("api_error_status")),
|
|
868
|
-
("api_error_code", data.get("api_error_code")),
|
|
869
|
-
("current_phase", data.get("current_phase")),
|
|
870
|
-
("current_tool", data.get("current_tool")),
|
|
871
|
-
("last_text_snippet", data.get("last_text_snippet")),
|
|
872
|
-
("terminal_result_text", data.get("terminal_result_text")),
|
|
873
|
-
]
|
|
874
|
-
for key, value in fields:
|
|
875
|
-
if value not in (None, "", []):
|
|
876
|
-
text = str(value).replace("\n", " ")
|
|
877
|
-
print(f"- {key}: {text[:500]}")
|
|
878
|
-
PY
|
|
879
|
-
)
|
|
880
|
-
[[ -n "$progress_summary" ]] || progress_summary="Progress data contained no terminal fields."
|
|
881
|
-
fi
|
|
882
|
-
|
|
883
|
-
local stale_summary="No stale-kill marker."
|
|
884
|
-
if [[ -f "$stale_kill_marker" ]]; then
|
|
885
|
-
stale_summary="$(cat "$stale_kill_marker" 2>/dev/null || true)"
|
|
886
|
-
fi
|
|
887
|
-
|
|
888
|
-
local checkpoint_summary="No checkpoint file found."
|
|
889
|
-
if [[ -f "$checkpoint_file" ]]; then
|
|
890
|
-
checkpoint_summary=$(python3 - "$checkpoint_file" <<'PY' 2>/dev/null || true
|
|
891
|
-
import json
|
|
892
|
-
import sys
|
|
893
|
-
try:
|
|
894
|
-
with open(sys.argv[1], encoding="utf-8") as fh:
|
|
895
|
-
data = json.load(fh)
|
|
896
|
-
except Exception as exc:
|
|
897
|
-
print(f"Checkpoint parse error: {exc}")
|
|
898
|
-
raise SystemExit(0)
|
|
899
|
-
steps = data.get("steps") or []
|
|
900
|
-
complete = sum(1 for step in steps if isinstance(step, dict) and step.get("status") in ("completed", "skipped"))
|
|
901
|
-
print(f"{complete}/{len(steps)} steps completed_or_skipped")
|
|
902
|
-
for step in steps:
|
|
903
|
-
if isinstance(step, dict) and step.get("status") not in ("completed", "skipped"):
|
|
904
|
-
print(f"- incomplete: {step.get('id')} {step.get('skill')} = {step.get('status')}")
|
|
905
|
-
PY
|
|
906
|
-
)
|
|
907
|
-
fi
|
|
908
|
-
|
|
909
|
-
local latest_commit="unavailable"
|
|
910
|
-
local feature_commit="no"
|
|
911
|
-
if git -C "$project_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
912
|
-
latest_commit=$(git -C "$project_root" rev-parse --short HEAD 2>/dev/null || echo unavailable)
|
|
913
|
-
if prizm_find_feature_commit "$project_root" "$base_ref" "$feature_id" false >/dev/null 2>&1; then
|
|
914
|
-
feature_commit="yes"
|
|
915
|
-
fi
|
|
916
|
-
fi
|
|
917
|
-
|
|
918
|
-
local dirty_summary="unavailable"
|
|
919
|
-
dirty_summary=$(git -C "$project_root" status --short 2>/dev/null || true)
|
|
920
|
-
[[ -n "$dirty_summary" ]] || dirty_summary="clean"
|
|
921
|
-
|
|
922
|
-
cat > "$failure_log" <<EOF
|
|
923
|
-
# Runtime-synthesized failure log
|
|
924
|
-
|
|
925
|
-
## Session
|
|
926
|
-
|
|
927
|
-
- feature_id: $feature_id
|
|
928
|
-
- session_id: ${session_id:-unknown}
|
|
929
|
-
- session_status: $session_status
|
|
930
|
-
- exit_code: $exit_code
|
|
931
|
-
|
|
932
|
-
## Stale kill marker
|
|
933
|
-
|
|
934
|
-
\`\`\`json
|
|
935
|
-
$stale_summary
|
|
936
|
-
\`\`\`
|
|
937
|
-
|
|
938
|
-
## Progress
|
|
939
|
-
|
|
940
|
-
$progress_summary
|
|
941
|
-
|
|
942
|
-
## Checkpoint
|
|
943
|
-
|
|
944
|
-
$checkpoint_summary
|
|
945
|
-
|
|
946
|
-
## Git state
|
|
947
|
-
|
|
948
|
-
- feature_commit_exists: $feature_commit
|
|
949
|
-
- latest_commit: $latest_commit
|
|
950
|
-
|
|
951
|
-
\`\`\`text
|
|
952
|
-
$dirty_summary
|
|
953
|
-
\`\`\`
|
|
954
|
-
|
|
955
|
-
## Recommended recovery action
|
|
956
|
-
|
|
957
|
-
- If this is an AI runtime/provider error before checkpoint completion, retry the session with a fresh context.
|
|
958
|
-
- If the working tree is dirty, preserve or review those changes before any reset or merge.
|
|
959
|
-
EOF
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
prizm_extract_update_new_status() {
|
|
963
|
-
python3 -c "
|
|
964
|
-
import json, sys
|
|
965
|
-
raw = sys.stdin.read()
|
|
966
|
-
try:
|
|
967
|
-
data = json.loads(raw)
|
|
968
|
-
except Exception:
|
|
969
|
-
sys.exit(0)
|
|
970
|
-
value = data.get('new_status')
|
|
971
|
-
if value:
|
|
972
|
-
print(value)
|
|
973
|
-
"
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
# Run an AI CLI session synchronously.
|
|
977
|
-
# Usage: prizm_run_ai_session <prompt_path> <log_path> <model>
|
|
978
|
-
prizm_run_ai_session() {
|
|
979
|
-
local prompt_path="$1"
|
|
980
|
-
local log_path="$2"
|
|
981
|
-
local model="${3:-}"
|
|
982
|
-
|
|
983
|
-
unset CLAUDECODE 2>/dev/null || true
|
|
984
|
-
|
|
985
|
-
# Create backup outside project tree (same rationale as prizm_start_ai_session)
|
|
986
|
-
local backup_dir="$HOME/.prizmkit/session-backups"
|
|
987
|
-
mkdir -p "$backup_dir"
|
|
988
|
-
local session_dir
|
|
989
|
-
session_dir="$(dirname "$(dirname "$log_path")")"
|
|
990
|
-
local session_id
|
|
991
|
-
session_id="$(basename "$session_dir")"
|
|
992
|
-
local backup_log="$backup_dir/${session_id}.log"
|
|
993
|
-
PRIZM_BACKUP_LOG="$backup_log"
|
|
994
|
-
|
|
995
|
-
# Write preamble to both files
|
|
996
|
-
{
|
|
997
|
-
echo "=== PRIZMKIT SESSION START ==="
|
|
998
|
-
echo "Prompt: $prompt_path"
|
|
999
|
-
echo "Model: ${model:-default}"
|
|
1000
|
-
echo "Started at: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
1001
|
-
echo "--- OUTPUT BELOW ---"
|
|
1002
|
-
} > "$log_path"
|
|
1003
|
-
cp "$log_path" "$backup_log"
|
|
1004
|
-
|
|
1005
|
-
local session_platform
|
|
1006
|
-
session_platform="$(_prizm_session_platform)"
|
|
1007
|
-
|
|
1008
|
-
case "$session_platform" in
|
|
1009
|
-
claude)
|
|
1010
|
-
local claude_args=(-p "$(cat "$prompt_path")" --dangerously-skip-permissions)
|
|
1011
|
-
if [[ -n "${PRIZMKIT_EFFORT:-}" ]]; then
|
|
1012
|
-
claude_args+=(--effort "$PRIZMKIT_EFFORT")
|
|
1013
|
-
fi
|
|
1014
|
-
if [[ -n "$model" ]]; then
|
|
1015
|
-
claude_args+=(--model "$model")
|
|
1016
|
-
fi
|
|
1017
|
-
"$CLI_CMD" "${claude_args[@]}" 2>&1 | tee -a "$backup_log" >> "$log_path"
|
|
1018
|
-
;;
|
|
1019
|
-
codex)
|
|
1020
|
-
local codex_args=(--ask-for-approval never --sandbox danger-full-access)
|
|
1021
|
-
local codex_subagent_timeout
|
|
1022
|
-
codex_subagent_timeout="$(_prizm_codex_subagent_timeout_seconds)"
|
|
1023
|
-
if [[ "$codex_subagent_timeout" =~ ^[0-9]+$ && "$codex_subagent_timeout" -gt 0 ]]; then
|
|
1024
|
-
codex_args+=(--config "agents.job_max_runtime_seconds=$codex_subagent_timeout")
|
|
1025
|
-
fi
|
|
1026
|
-
codex_args+=(exec --cd "$PROJECT_ROOT" --skip-git-repo-check)
|
|
1027
|
-
if [[ "$USE_STREAM_JSON" == "true" ]]; then
|
|
1028
|
-
codex_args+=(--json)
|
|
1029
|
-
fi
|
|
1030
|
-
if [[ -n "$model" ]]; then
|
|
1031
|
-
codex_args+=(--model "$model")
|
|
1032
|
-
fi
|
|
1033
|
-
if [[ -n "${PRIZMKIT_EFFORT:-}" ]]; then
|
|
1034
|
-
codex_args+=(--config "model_reasoning_effort=$PRIZMKIT_EFFORT")
|
|
1035
|
-
fi
|
|
1036
|
-
"$CLI_CMD" "${codex_args[@]}" - < "$prompt_path" 2>&1 | tee -a "$backup_log" >> "$log_path"
|
|
1037
|
-
;;
|
|
1038
|
-
*)
|
|
1039
|
-
local cb_args=(--print -y)
|
|
1040
|
-
if [[ -n "${PRIZMKIT_EFFORT:-}" ]]; then
|
|
1041
|
-
cb_args+=(--effort "$PRIZMKIT_EFFORT")
|
|
1042
|
-
fi
|
|
1043
|
-
if [[ -n "$model" ]]; then
|
|
1044
|
-
cb_args+=(--model "$model")
|
|
1045
|
-
fi
|
|
1046
|
-
"$CLI_CMD" "${cb_args[@]}" < "$prompt_path" 2>&1 | tee -a "$backup_log" >> "$log_path"
|
|
1047
|
-
;;
|
|
1048
|
-
esac
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
# Recover session log from backup if the main log was truncated during execution.
|
|
1052
|
-
# Claude Code subagents can unlink session.log at the filesystem level. The backup
|
|
1053
|
-
# lives in $HOME/.prizmkit/ — outside the project tree — so it survives.
|
|
1054
|
-
#
|
|
1055
|
-
# Must be called AFTER the AI CLI process exits and BEFORE the session summary
|
|
1056
|
-
# reads session.log. Uses PRIZM_BACKUP_LOG set by prizm_start_ai_session /
|
|
1057
|
-
# prizm_run_ai_session.
|
|
1058
|
-
prizm_recover_session_log() {
|
|
1059
|
-
local session_log="${1:-}"
|
|
1060
|
-
local backup_log="${PRIZM_BACKUP_LOG:-}"
|
|
1061
|
-
|
|
1062
|
-
if [[ -z "$backup_log" || ! -f "$backup_log" ]]; then
|
|
1063
|
-
return 0
|
|
1064
|
-
fi
|
|
1065
|
-
|
|
1066
|
-
local main_size=0
|
|
1067
|
-
if [[ -f "$session_log" ]]; then
|
|
1068
|
-
main_size=$(wc -c < "$session_log" 2>/dev/null | tr -d ' ') || true
|
|
1069
|
-
fi
|
|
1070
|
-
|
|
1071
|
-
local backup_size=0
|
|
1072
|
-
backup_size=$(wc -c < "$backup_log" 2>/dev/null | tr -d ' ') || true
|
|
1073
|
-
|
|
1074
|
-
if [[ "$main_size" -lt 1024 && "$backup_size" -gt "$main_size" ]]; then
|
|
1075
|
-
# Ensure parent directory exists before copying (subagent may have
|
|
1076
|
-
# deleted it along with session.log).
|
|
1077
|
-
local log_dir
|
|
1078
|
-
log_dir="$(dirname "$session_log")"
|
|
1079
|
-
mkdir -p "$log_dir" 2>/dev/null || true
|
|
1080
|
-
if cp "$backup_log" "$session_log" 2>/dev/null; then
|
|
1081
|
-
log_info "Session log recovered from backup (${backup_size}B → $session_log)"
|
|
1082
|
-
else
|
|
1083
|
-
log_warn "Session log recovery failed: could not copy backup to $session_log"
|
|
1084
|
-
fi
|
|
1085
|
-
fi
|
|
1086
|
-
|
|
1087
|
-
# Clean up backup either way (rm is safe even with set -e)
|
|
1088
|
-
rm -f "$backup_log" 2>/dev/null || true
|
|
1089
|
-
return 0
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
# prizm_detect_subagents <session_log>
|
|
1093
|
-
#
|
|
1094
|
-
# Scan session log for subagent spawns, count them, and log the result.
|
|
1095
|
-
# Sets _SUBAGENT_COUNT to the number of Agent tool calls detected.
|
|
1096
|
-
# Requires: USE_STREAM_JSON (from detect_stream_json_support)
|
|
1097
|
-
_SUBAGENT_COUNT=0
|
|
1098
|
-
prizm_detect_subagents() {
|
|
1099
|
-
local session_log="$1"
|
|
1100
|
-
_SUBAGENT_COUNT=0
|
|
1101
|
-
[[ -f "$session_log" ]] || return 0
|
|
1102
|
-
|
|
1103
|
-
local count=0
|
|
1104
|
-
if [[ "$USE_STREAM_JSON" == "true" && "${STREAM_JSON_FORMAT:-}" == "codex-json" ]]; then
|
|
1105
|
-
count=$(grep -c '"tool"[[:space:]]*:[[:space:]]*"spawn_agent"' "$session_log" 2>/dev/null) || true
|
|
1106
|
-
elif [[ "$USE_STREAM_JSON" == "true" ]]; then
|
|
1107
|
-
count=$(grep -c '"name"[[:space:]]*:[[:space:]]*"Agent"' "$session_log" 2>/dev/null) || true
|
|
1108
|
-
else
|
|
1109
|
-
count=$(grep -cE '(Tool: Agent|"tool":\s*"Agent"|"tool":\s*"spawn_agent"|tool_use.*Agent|subagent_type|collab: SpawnAgent)' "$session_log" 2>/dev/null) || true
|
|
1110
|
-
fi
|
|
1111
|
-
|
|
1112
|
-
count=${count:-0}
|
|
1113
|
-
_SUBAGENT_COUNT=$count
|
|
1114
|
-
if [[ "$count" -gt 0 ]]; then
|
|
1115
|
-
log_info "Subagent calls detected in session: $count"
|
|
1116
|
-
else
|
|
1117
|
-
log_info "Subagent calls detected in session: 0 (single-agent mode)"
|
|
1118
|
-
fi
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
# Common dependency check (jq + python3 + optional CLI in PATH)
|
|
1122
|
-
# Args:
|
|
1123
|
-
# $1 - cli command (optional)
|
|
1124
|
-
prizm_check_common_dependencies() {
|
|
1125
|
-
local cli_cmd="${1:-}"
|
|
1126
|
-
|
|
1127
|
-
if ! command -v jq &>/dev/null; then
|
|
1128
|
-
log_error "jq is required but not installed. Install with: brew install jq"
|
|
1129
|
-
exit 1
|
|
1130
|
-
fi
|
|
1131
|
-
|
|
1132
|
-
if ! command -v python3 &>/dev/null; then
|
|
1133
|
-
log_error "python3 is required but not installed."
|
|
1134
|
-
exit 1
|
|
1135
|
-
fi
|
|
1136
|
-
|
|
1137
|
-
if [[ -n "$cli_cmd" ]] && ! command -v "$cli_cmd" &>/dev/null; then
|
|
1138
|
-
log_warn "AI CLI '$cli_cmd' not found in PATH."
|
|
1139
|
-
log_warn "Set AI_CLI environment variable to the correct command."
|
|
1140
|
-
log_warn "Continuing anyway (will fail when spawning sessions)..."
|
|
1141
|
-
fi
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
# Ensure git is installed and a given directory is a git repository.
|
|
1145
|
-
# If git is missing, attempt auto-install; on failure print instructions and exit.
|
|
1146
|
-
# If not inside a git repo, run git init + initial commit automatically.
|
|
1147
|
-
#
|
|
1148
|
-
# Usage: prizm_ensure_git_repo [directory]
|
|
1149
|
-
# directory — path to check/initialize (defaults to current working directory)
|
|
1150
|
-
prizm_ensure_git_repo() {
|
|
1151
|
-
local target_dir="${1:-.}"
|
|
1152
|
-
|
|
1153
|
-
# --- helper: run a command with a timeout (macOS-compatible) ---
|
|
1154
|
-
# Uses GNU timeout / gtimeout if available, otherwise a background+kill fallback.
|
|
1155
|
-
_run_with_timeout() {
|
|
1156
|
-
local secs="$1"; shift
|
|
1157
|
-
if command -v timeout &>/dev/null; then
|
|
1158
|
-
timeout "$secs" "$@"
|
|
1159
|
-
elif command -v gtimeout &>/dev/null; then
|
|
1160
|
-
gtimeout "$secs" "$@"
|
|
1161
|
-
else
|
|
1162
|
-
# background + kill fallback
|
|
1163
|
-
"$@" &
|
|
1164
|
-
local pid=$!
|
|
1165
|
-
( sleep "$secs" && kill "$pid" 2>/dev/null ) &
|
|
1166
|
-
local watchdog=$!
|
|
1167
|
-
if wait "$pid" 2>/dev/null; then
|
|
1168
|
-
kill "$watchdog" 2>/dev/null; wait "$watchdog" 2>/dev/null
|
|
1169
|
-
return 0
|
|
1170
|
-
else
|
|
1171
|
-
kill "$watchdog" 2>/dev/null; wait "$watchdog" 2>/dev/null
|
|
1172
|
-
return 1
|
|
1173
|
-
fi
|
|
1174
|
-
fi
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
if ! command -v git &>/dev/null; then
|
|
1178
|
-
log_warn "git is not installed. Attempting automatic installation..."
|
|
1179
|
-
local _install_ok=false
|
|
1180
|
-
case "$(uname -s)" in
|
|
1181
|
-
Darwin)
|
|
1182
|
-
if command -v brew &>/dev/null; then
|
|
1183
|
-
log_info "Installing git via Homebrew..."
|
|
1184
|
-
if _run_with_timeout 120 brew install git &>/dev/null; then
|
|
1185
|
-
_install_ok=true
|
|
1186
|
-
fi
|
|
1187
|
-
else
|
|
1188
|
-
# xcode-select --install opens a GUI dialog that cannot be automated;
|
|
1189
|
-
# skip auto-install and fall through to manual instructions.
|
|
1190
|
-
log_info "Homebrew not found; cannot auto-install git on macOS."
|
|
1191
|
-
fi
|
|
1192
|
-
;;
|
|
1193
|
-
Linux)
|
|
1194
|
-
# Verify passwordless sudo is available (daemon/CI safety)
|
|
1195
|
-
if ! sudo -n true 2>/dev/null; then
|
|
1196
|
-
log_info "sudo requires a password; cannot auto-install git."
|
|
1197
|
-
elif command -v apt-get &>/dev/null; then
|
|
1198
|
-
log_info "Installing git via apt-get..."
|
|
1199
|
-
_run_with_timeout 120 sudo apt-get update -y &>/dev/null
|
|
1200
|
-
if _run_with_timeout 120 sudo apt-get install -y git &>/dev/null; then
|
|
1201
|
-
_install_ok=true
|
|
1202
|
-
fi
|
|
1203
|
-
elif command -v yum &>/dev/null; then
|
|
1204
|
-
log_info "Installing git via yum..."
|
|
1205
|
-
if _run_with_timeout 120 sudo yum install -y git &>/dev/null; then
|
|
1206
|
-
_install_ok=true
|
|
1207
|
-
fi
|
|
1208
|
-
elif command -v dnf &>/dev/null; then
|
|
1209
|
-
log_info "Installing git via dnf..."
|
|
1210
|
-
if _run_with_timeout 120 sudo dnf install -y git &>/dev/null; then
|
|
1211
|
-
_install_ok=true
|
|
1212
|
-
fi
|
|
1213
|
-
elif command -v pacman &>/dev/null; then
|
|
1214
|
-
log_info "Installing git via pacman..."
|
|
1215
|
-
if _run_with_timeout 120 sudo pacman -S --noconfirm git &>/dev/null; then
|
|
1216
|
-
_install_ok=true
|
|
1217
|
-
fi
|
|
1218
|
-
fi
|
|
1219
|
-
;;
|
|
1220
|
-
esac
|
|
1221
|
-
|
|
1222
|
-
if $_install_ok && command -v git &>/dev/null; then
|
|
1223
|
-
log_success "git installed successfully ($(git --version))."
|
|
1224
|
-
else
|
|
1225
|
-
log_error "Automatic git installation failed or timed out."
|
|
1226
|
-
log_error "Please install git manually:"
|
|
1227
|
-
log_error " macOS: brew install git or xcode-select --install"
|
|
1228
|
-
log_error " Ubuntu: sudo apt-get install git"
|
|
1229
|
-
log_error " CentOS: sudo yum install git"
|
|
1230
|
-
log_error " Windows: https://git-scm.com/download/win"
|
|
1231
|
-
exit 1
|
|
1232
|
-
fi
|
|
1233
|
-
fi
|
|
1234
|
-
|
|
1235
|
-
# Validate target directory exists
|
|
1236
|
-
if [[ ! -d "$target_dir" ]]; then
|
|
1237
|
-
log_error "Target directory does not exist: $target_dir"
|
|
1238
|
-
exit 1
|
|
1239
|
-
fi
|
|
1240
|
-
|
|
1241
|
-
if ! git -C "$target_dir" rev-parse --is-inside-work-tree &>/dev/null 2>&1; then
|
|
1242
|
-
log_warn "Directory is not a git repository: $target_dir"
|
|
1243
|
-
log_info "Initializing git repository..."
|
|
1244
|
-
git -C "$target_dir" init -b main
|
|
1245
|
-
log_info "Git repository initialized at: $target_dir"
|
|
1246
|
-
# Create initial commit so branches and diffs work
|
|
1247
|
-
if ! git -C "$target_dir" add -A; then
|
|
1248
|
-
log_error "Failed to stage files in: $target_dir"
|
|
1249
|
-
exit 1
|
|
1250
|
-
fi
|
|
1251
|
-
git -C "$target_dir" commit --no-verify -m "chore: initial commit (auto-created by dev-pipeline)" || {
|
|
1252
|
-
# If nothing to commit (empty project), create an empty initial commit
|
|
1253
|
-
git -C "$target_dir" commit --no-verify --allow-empty -m "chore: initial commit (auto-created by dev-pipeline)"
|
|
1254
|
-
}
|
|
1255
|
-
log_success "Initial commit created — workspace content committed."
|
|
1256
|
-
fi
|
|
1257
|
-
}
|