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
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
"""Branch and worktree helper primitives."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
HIDDEN_TOOL_WORKTREE_EXCLUDES = (
|
|
15
|
+
":(top,exclude).prizmkit-worktree",
|
|
16
|
+
":(top,exclude).skills-lock.json",
|
|
17
|
+
":(top,exclude).AGENTS.md",
|
|
18
|
+
":(top,exclude,glob).*/worktree",
|
|
19
|
+
":(top,exclude,glob).*/worktree/**",
|
|
20
|
+
":(top,exclude,glob).*/worktrees",
|
|
21
|
+
":(top,exclude,glob).*/worktrees/**",
|
|
22
|
+
":(top,exclude).claude",
|
|
23
|
+
":(top,exclude,glob).claude/**",
|
|
24
|
+
":(top,exclude).codebuddy",
|
|
25
|
+
":(top,exclude,glob).codebuddy/**",
|
|
26
|
+
":(top,exclude).codex",
|
|
27
|
+
":(top,exclude,glob).codex/**",
|
|
28
|
+
":(top,exclude).agents",
|
|
29
|
+
":(top,exclude,glob).agents/**",
|
|
30
|
+
":(top,exclude).prizmkit/prizm-docs",
|
|
31
|
+
":(top,exclude,glob).prizmkit/prizm-docs/**",
|
|
32
|
+
":(top,exclude).prizmkit/config.json",
|
|
33
|
+
":(top,exclude).prizmkit/manifest.json",
|
|
34
|
+
":(top,exclude).prizmkit/dev-pipeline/scripts",
|
|
35
|
+
":(top,exclude,glob).prizmkit/dev-pipeline/scripts/**",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
WORKTREE_PLATFORM_SUPPORT_DIRS = (".claude", ".codebuddy", ".codex", ".agents")
|
|
39
|
+
WORKTREE_ROOT_SUPPORT_FILES = ("skills-lock.json", "AGENTS.md")
|
|
40
|
+
WORKTREE_PRIZMKIT_SUPPORT_ENTRIES = ("prizm-docs", "config.json", "manifest.json")
|
|
41
|
+
WORKTREE_SUPPORT_IGNORE_NAMES = {"__pycache__", ".DS_Store", "settings.local.json"}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class BranchContext:
|
|
46
|
+
"""Branch state required by git/worktree helpers."""
|
|
47
|
+
|
|
48
|
+
base_branch: str
|
|
49
|
+
working_branch: str
|
|
50
|
+
project_root: Path
|
|
51
|
+
remote_name: str = "origin"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True)
|
|
55
|
+
class WorktreePolicy:
|
|
56
|
+
"""Worktree creation and cleanup policy for later porting."""
|
|
57
|
+
|
|
58
|
+
use_worktree: bool
|
|
59
|
+
cleanup_on_success: bool
|
|
60
|
+
preserve_on_failure: bool
|
|
61
|
+
worktree_root: Path | None = None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class WorktreeRuntimeContext:
|
|
68
|
+
"""Resolved linked-worktree paths and safety metadata."""
|
|
69
|
+
|
|
70
|
+
context: BranchContext
|
|
71
|
+
policy: WorktreePolicy
|
|
72
|
+
worktree_root: Path
|
|
73
|
+
worktree_path: Path
|
|
74
|
+
sentinel_path: Path
|
|
75
|
+
|
|
76
|
+
def is_cleanup_path_safe(self) -> bool:
|
|
77
|
+
root = self.worktree_root.resolve()
|
|
78
|
+
target = self.worktree_path.resolve()
|
|
79
|
+
try:
|
|
80
|
+
return os.path.commonpath([str(root), str(target)]) == str(root) and target != root
|
|
81
|
+
except ValueError:
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True)
|
|
88
|
+
class WorktreeOperationResult:
|
|
89
|
+
"""Executable linked-worktree operation result."""
|
|
90
|
+
|
|
91
|
+
ok: bool
|
|
92
|
+
reason: str = ""
|
|
93
|
+
runtime: WorktreeRuntimeContext | None = None
|
|
94
|
+
results: tuple[GitCommandResult, ...] = ()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass(frozen=True)
|
|
98
|
+
class WorktreeRemovalResult:
|
|
99
|
+
"""Guarded worktree removal result."""
|
|
100
|
+
|
|
101
|
+
ok: bool
|
|
102
|
+
reason: str = ""
|
|
103
|
+
results: tuple[GitCommandResult, ...] = ()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass(frozen=True)
|
|
107
|
+
class WorktreeSupportAssetsResult:
|
|
108
|
+
"""Support assets materialized into an AI-visible linked worktree."""
|
|
109
|
+
|
|
110
|
+
ok: bool
|
|
111
|
+
copied: tuple[Path, ...] = ()
|
|
112
|
+
existing: tuple[Path, ...] = ()
|
|
113
|
+
missing: tuple[str, ...] = ()
|
|
114
|
+
reason: str = ""
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _safe_worktree_name(branch_name: str) -> str:
|
|
118
|
+
safe = re.sub(r"[^A-Za-z0-9._-]+", "-", branch_name.strip())
|
|
119
|
+
safe = safe.strip(".-")
|
|
120
|
+
return safe or "worktree"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def worktree_runtime_context(context: BranchContext, policy: WorktreePolicy) -> WorktreeRuntimeContext:
|
|
124
|
+
"""Resolve deterministic per-branch worktree paths under a runner-owned root."""
|
|
125
|
+
root = (policy.worktree_root or context.project_root / ".prizmkit" / "state" / "worktrees").resolve()
|
|
126
|
+
worktree_path = root / _safe_worktree_name(context.working_branch)
|
|
127
|
+
return WorktreeRuntimeContext(
|
|
128
|
+
context=context,
|
|
129
|
+
policy=policy,
|
|
130
|
+
worktree_root=root,
|
|
131
|
+
worktree_path=worktree_path,
|
|
132
|
+
sentinel_path=worktree_path / ".prizmkit-worktree",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def linked_worktree_setup_plan(runtime: WorktreeRuntimeContext) -> GitCommandPlan:
|
|
137
|
+
"""Plan linked-worktree branch creation without main-worktree checkout."""
|
|
138
|
+
context = runtime.context
|
|
139
|
+
return GitCommandPlan(
|
|
140
|
+
name="linked_worktree_setup",
|
|
141
|
+
commands=(
|
|
142
|
+
_git("rev-parse", "--verify", context.working_branch, allow_failure=True, description="check existing branch"),
|
|
143
|
+
_git("branch", context.working_branch, context.base_branch, allow_failure=True, description="create branch without checkout"),
|
|
144
|
+
_git("worktree", "list", "--porcelain", allow_failure=True, description="inspect registered worktrees"),
|
|
145
|
+
_git("worktree", "add", "--checkout", str(runtime.worktree_path), context.working_branch, allow_failure=True, description="create or reuse linked worktree"),
|
|
146
|
+
),
|
|
147
|
+
notes=(
|
|
148
|
+
"Creates the task branch without checking it out in the main working tree.",
|
|
149
|
+
"A deterministic path lets context-overflow continuations reuse the same worktree.",
|
|
150
|
+
),
|
|
151
|
+
require_one_success=((0, 1),),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def worktree_merge_plan(runtime: WorktreeRuntimeContext, *, auto_push: bool = False) -> GitCommandPlan:
|
|
156
|
+
"""Plan rebase-in-worktree then fast-forward merge in the main repository."""
|
|
157
|
+
context = runtime.context
|
|
158
|
+
commands = [
|
|
159
|
+
_git("rebase", context.base_branch, description="rebase worktree branch onto base"),
|
|
160
|
+
_git("merge", "--ff-only", context.working_branch, description="fast-forward base from worktree branch"),
|
|
161
|
+
]
|
|
162
|
+
if auto_push:
|
|
163
|
+
commands.append(_git("push", allow_failure=True, description="optional auto-push after merge"))
|
|
164
|
+
return GitCommandPlan(
|
|
165
|
+
name="worktree_merge",
|
|
166
|
+
commands=tuple(commands),
|
|
167
|
+
notes=("Run rebase in the linked worktree and merge from the main checkout after checkout-free setup.",),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def worktree_remove_plan(runtime: WorktreeRuntimeContext, *, delete_branch: bool = True) -> GitCommandPlan:
|
|
172
|
+
"""Plan guarded linked-worktree removal and optional branch deletion."""
|
|
173
|
+
commands = [
|
|
174
|
+
_git("worktree", "remove", "--force", str(runtime.worktree_path), description="remove linked worktree"),
|
|
175
|
+
]
|
|
176
|
+
if delete_branch:
|
|
177
|
+
commands.append(_git("branch", "-d", runtime.context.working_branch, allow_failure=True, description="delete merged task branch"))
|
|
178
|
+
return GitCommandPlan(
|
|
179
|
+
name="worktree_remove",
|
|
180
|
+
commands=tuple(commands),
|
|
181
|
+
notes=("Caller must verify cleanup safety and registration before executing.",),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def worktree_registered(runtime: WorktreeRuntimeContext) -> bool:
|
|
186
|
+
"""Return whether git worktree metadata contains the resolved path."""
|
|
187
|
+
result = run_git_command(runtime.context.project_root, ("worktree", "list", "--porcelain"))
|
|
188
|
+
if result.return_code != 0:
|
|
189
|
+
return False
|
|
190
|
+
target = str(runtime.worktree_path.resolve())
|
|
191
|
+
candidates: list[str] = []
|
|
192
|
+
for line in result.stdout.splitlines():
|
|
193
|
+
stripped = line.strip()
|
|
194
|
+
if stripped.startswith("worktree "):
|
|
195
|
+
candidates.append(stripped.removeprefix("worktree ").strip())
|
|
196
|
+
elif stripped:
|
|
197
|
+
candidates.append(stripped.split()[0])
|
|
198
|
+
return any(Path(candidate).resolve() == Path(target) for candidate in candidates if candidate)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def mark_worktree_sentinel(runtime: WorktreeRuntimeContext) -> None:
|
|
202
|
+
"""Write a small runner-owned sentinel inside a linked worktree."""
|
|
203
|
+
runtime.sentinel_path.parent.mkdir(parents=True, exist_ok=True)
|
|
204
|
+
runtime.sentinel_path.write_text(runtime.context.working_branch + "\n", encoding="utf-8")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def materialize_worktree_support_assets(runtime: WorktreeRuntimeContext) -> WorktreeSupportAssetsResult:
|
|
209
|
+
"""Copy ignored/untracked PrizmKit support assets into the linked worktree.
|
|
210
|
+
|
|
211
|
+
Git worktree checkout materializes only tracked files. Platform agent
|
|
212
|
+
definitions, Prizm docs/config, and installed runtime utility scripts are
|
|
213
|
+
commonly ignored support assets, yet generated prompts reference them from
|
|
214
|
+
the AI-visible project root. Copy only missing support files so continuation
|
|
215
|
+
worktrees keep any task-local artifact changes.
|
|
216
|
+
"""
|
|
217
|
+
source_root = runtime.context.project_root
|
|
218
|
+
target_root = runtime.worktree_path
|
|
219
|
+
copied: list[Path] = []
|
|
220
|
+
existing: list[Path] = []
|
|
221
|
+
missing: list[str] = []
|
|
222
|
+
|
|
223
|
+
try:
|
|
224
|
+
for relative in WORKTREE_PLATFORM_SUPPORT_DIRS:
|
|
225
|
+
_materialize_optional_support_entry(source_root, target_root, relative, copied, existing, missing)
|
|
226
|
+
for relative in WORKTREE_ROOT_SUPPORT_FILES:
|
|
227
|
+
_materialize_optional_support_entry(source_root, target_root, relative, copied, existing, missing)
|
|
228
|
+
for relative in WORKTREE_PRIZMKIT_SUPPORT_ENTRIES:
|
|
229
|
+
_materialize_optional_support_entry(
|
|
230
|
+
source_root / ".prizmkit",
|
|
231
|
+
target_root / ".prizmkit",
|
|
232
|
+
relative,
|
|
233
|
+
copied,
|
|
234
|
+
existing,
|
|
235
|
+
missing,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
runtime_scripts = _runtime_support_scripts_source(source_root)
|
|
239
|
+
if runtime_scripts is None:
|
|
240
|
+
missing.append(".prizmkit/dev-pipeline/scripts")
|
|
241
|
+
else:
|
|
242
|
+
target = target_root / ".prizmkit" / "dev-pipeline" / "scripts"
|
|
243
|
+
changed = _copy_support_entry(runtime_scripts, target)
|
|
244
|
+
(copied if changed else existing).append(target)
|
|
245
|
+
except OSError as exc:
|
|
246
|
+
return WorktreeSupportAssetsResult(False, tuple(copied), tuple(existing), tuple(missing), str(exc))
|
|
247
|
+
|
|
248
|
+
return WorktreeSupportAssetsResult(True, tuple(copied), tuple(existing), tuple(missing))
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _materialize_optional_support_entry(
|
|
252
|
+
source_root: Path,
|
|
253
|
+
target_root: Path,
|
|
254
|
+
relative: str,
|
|
255
|
+
copied: list[Path],
|
|
256
|
+
existing: list[Path],
|
|
257
|
+
missing: list[str],
|
|
258
|
+
) -> None:
|
|
259
|
+
source = source_root / relative
|
|
260
|
+
target = target_root / relative
|
|
261
|
+
if not source.exists() and not source.is_symlink():
|
|
262
|
+
missing.append(relative)
|
|
263
|
+
return
|
|
264
|
+
changed = _copy_support_entry(source, target)
|
|
265
|
+
(copied if changed else existing).append(target)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _runtime_support_scripts_source(project_root: Path) -> Path | None:
|
|
269
|
+
candidates = (
|
|
270
|
+
project_root / ".prizmkit" / "dev-pipeline" / "scripts",
|
|
271
|
+
project_root / "dev-pipeline" / "scripts",
|
|
272
|
+
)
|
|
273
|
+
for candidate in candidates:
|
|
274
|
+
if candidate.is_dir():
|
|
275
|
+
return candidate
|
|
276
|
+
return None
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _copy_support_entry(source: Path, target: Path) -> bool:
|
|
280
|
+
if source.is_symlink():
|
|
281
|
+
if target.exists() or target.is_symlink():
|
|
282
|
+
return False
|
|
283
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
284
|
+
os.symlink(os.readlink(source), target)
|
|
285
|
+
return True
|
|
286
|
+
if source.is_file():
|
|
287
|
+
if target.exists():
|
|
288
|
+
return False
|
|
289
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
290
|
+
shutil.copy2(source, target)
|
|
291
|
+
return True
|
|
292
|
+
|
|
293
|
+
changed = False
|
|
294
|
+
if not target.exists():
|
|
295
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
296
|
+
changed = True
|
|
297
|
+
for current_root, dir_names, file_names in os.walk(source):
|
|
298
|
+
dir_names[:] = [name for name in dir_names if not _skip_support_asset_name(name)]
|
|
299
|
+
relative_root = Path(current_root).relative_to(source)
|
|
300
|
+
target_root = target / relative_root
|
|
301
|
+
if not target_root.exists():
|
|
302
|
+
target_root.mkdir(parents=True, exist_ok=True)
|
|
303
|
+
changed = True
|
|
304
|
+
for file_name in file_names:
|
|
305
|
+
if _skip_support_asset_name(file_name):
|
|
306
|
+
continue
|
|
307
|
+
source_file = Path(current_root) / file_name
|
|
308
|
+
target_file = target_root / file_name
|
|
309
|
+
if target_file.exists() or target_file.is_symlink():
|
|
310
|
+
continue
|
|
311
|
+
if source_file.is_symlink():
|
|
312
|
+
os.symlink(os.readlink(source_file), target_file)
|
|
313
|
+
else:
|
|
314
|
+
shutil.copy2(source_file, target_file)
|
|
315
|
+
changed = True
|
|
316
|
+
return changed
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _skip_support_asset_name(name: str) -> bool:
|
|
320
|
+
return name in WORKTREE_SUPPORT_IGNORE_NAMES or name.endswith(".pyc")
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def ensure_linked_worktree(runtime: WorktreeRuntimeContext) -> WorktreeOperationResult:
|
|
324
|
+
"""Create or reuse a linked worktree for the runtime context."""
|
|
325
|
+
if not runtime.policy.use_worktree:
|
|
326
|
+
return WorktreeOperationResult(False, "worktree_disabled", runtime)
|
|
327
|
+
if not runtime.is_cleanup_path_safe():
|
|
328
|
+
return WorktreeOperationResult(False, "unsafe_worktree_path", runtime)
|
|
329
|
+
|
|
330
|
+
runtime.worktree_root.mkdir(parents=True, exist_ok=True)
|
|
331
|
+
results: list[GitCommandResult] = []
|
|
332
|
+
|
|
333
|
+
branch_check = run_git_command(runtime.context.project_root, ("rev-parse", "--verify", runtime.context.working_branch))
|
|
334
|
+
results.append(branch_check)
|
|
335
|
+
if branch_check.return_code != 0:
|
|
336
|
+
branch_create = run_git_command(
|
|
337
|
+
runtime.context.project_root,
|
|
338
|
+
("branch", runtime.context.working_branch, runtime.context.base_branch),
|
|
339
|
+
)
|
|
340
|
+
results.append(branch_create)
|
|
341
|
+
if branch_create.return_code != 0:
|
|
342
|
+
return WorktreeOperationResult(False, "branch_create_failed", runtime, tuple(results))
|
|
343
|
+
|
|
344
|
+
if worktree_registered(runtime):
|
|
345
|
+
mark_worktree_sentinel(runtime)
|
|
346
|
+
return WorktreeOperationResult(True, runtime=runtime, results=tuple(results))
|
|
347
|
+
|
|
348
|
+
if runtime.worktree_path.exists() and any(runtime.worktree_path.iterdir()):
|
|
349
|
+
return WorktreeOperationResult(False, "worktree_path_exists_unregistered", runtime, tuple(results))
|
|
350
|
+
|
|
351
|
+
add = run_git_command(
|
|
352
|
+
runtime.context.project_root,
|
|
353
|
+
("worktree", "add", "--checkout", str(runtime.worktree_path), runtime.context.working_branch),
|
|
354
|
+
)
|
|
355
|
+
results.append(add)
|
|
356
|
+
if add.return_code != 0:
|
|
357
|
+
if worktree_registered(runtime):
|
|
358
|
+
mark_worktree_sentinel(runtime)
|
|
359
|
+
return WorktreeOperationResult(True, runtime=runtime, results=tuple(results))
|
|
360
|
+
return WorktreeOperationResult(False, "worktree_add_failed", runtime, tuple(results))
|
|
361
|
+
|
|
362
|
+
mark_worktree_sentinel(runtime)
|
|
363
|
+
return WorktreeOperationResult(True, runtime=runtime, results=tuple(results))
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def merge_linked_worktree(runtime: WorktreeRuntimeContext, *, auto_push: bool = False) -> WorktreeOperationResult:
|
|
367
|
+
"""Rebase the linked worktree branch and fast-forward merge it into base."""
|
|
368
|
+
results: list[GitCommandResult] = []
|
|
369
|
+
rebase = run_git_command(runtime.worktree_path, ("rebase", runtime.context.base_branch))
|
|
370
|
+
results.append(rebase)
|
|
371
|
+
if rebase.return_code != 0:
|
|
372
|
+
results.append(run_git_command(runtime.worktree_path, ("rebase", "--abort")))
|
|
373
|
+
return WorktreeOperationResult(False, "merge_conflict", runtime, tuple(results))
|
|
374
|
+
|
|
375
|
+
checkout = run_git_command(runtime.context.project_root, ("checkout", runtime.context.base_branch))
|
|
376
|
+
results.append(checkout)
|
|
377
|
+
if checkout.return_code != 0:
|
|
378
|
+
return WorktreeOperationResult(False, "base_checkout_failed", runtime, tuple(results))
|
|
379
|
+
|
|
380
|
+
merge = run_git_command(runtime.context.project_root, ("merge", "--ff-only", runtime.context.working_branch))
|
|
381
|
+
results.append(merge)
|
|
382
|
+
if merge.return_code != 0:
|
|
383
|
+
results.append(run_git_command(runtime.context.project_root, ("merge", "--abort")))
|
|
384
|
+
return WorktreeOperationResult(False, "merge_conflict", runtime, tuple(results))
|
|
385
|
+
|
|
386
|
+
if auto_push:
|
|
387
|
+
results.append(run_git_command(runtime.context.project_root, ("push",)))
|
|
388
|
+
|
|
389
|
+
return WorktreeOperationResult(True, runtime=runtime, results=tuple(results))
|
|
390
|
+
|
|
391
|
+
def guarded_worktree_remove(runtime: WorktreeRuntimeContext, *, delete_branch: bool = True) -> WorktreeRemovalResult:
|
|
392
|
+
"""Remove a worktree only when path, sentinel, and git metadata are safe."""
|
|
393
|
+
if not runtime.is_cleanup_path_safe():
|
|
394
|
+
return WorktreeRemovalResult(False, "unsafe_worktree_path")
|
|
395
|
+
if not runtime.sentinel_path.is_file():
|
|
396
|
+
return WorktreeRemovalResult(False, "missing_worktree_sentinel")
|
|
397
|
+
if not worktree_registered(runtime):
|
|
398
|
+
return WorktreeRemovalResult(False, "worktree_not_registered")
|
|
399
|
+
results: list[GitCommandResult] = []
|
|
400
|
+
for command in worktree_remove_plan(runtime, delete_branch=delete_branch).commands:
|
|
401
|
+
result = run_git_command(runtime.context.project_root, command.args)
|
|
402
|
+
results.append(result)
|
|
403
|
+
if result.return_code != 0 and not command.allow_failure:
|
|
404
|
+
return WorktreeRemovalResult(False, command.description or "worktree_remove_failed", tuple(results))
|
|
405
|
+
return WorktreeRemovalResult(True, results=tuple(results))
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
@dataclass(frozen=True)
|
|
409
|
+
class GitCommand:
|
|
410
|
+
"""One planned git command."""
|
|
411
|
+
|
|
412
|
+
args: tuple[str, ...]
|
|
413
|
+
allow_failure: bool = False
|
|
414
|
+
description: str = ""
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
@dataclass(frozen=True)
|
|
418
|
+
class GitCommandPlan:
|
|
419
|
+
"""A deterministic branch/worktree command plan."""
|
|
420
|
+
|
|
421
|
+
name: str
|
|
422
|
+
commands: tuple[GitCommand, ...]
|
|
423
|
+
never_fails: bool = False
|
|
424
|
+
notes: tuple[str, ...] = ()
|
|
425
|
+
require_one_success: tuple[tuple[int, ...], ...] = ()
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
@dataclass(frozen=True)
|
|
429
|
+
class GitCommandResult:
|
|
430
|
+
"""Result from running one git command."""
|
|
431
|
+
|
|
432
|
+
args: tuple[str, ...]
|
|
433
|
+
return_code: int
|
|
434
|
+
stdout: str
|
|
435
|
+
stderr: str
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
@dataclass(frozen=True)
|
|
439
|
+
class GitPlanResult:
|
|
440
|
+
"""Result from running a command plan."""
|
|
441
|
+
|
|
442
|
+
plan: GitCommandPlan
|
|
443
|
+
results: tuple[GitCommandResult, ...]
|
|
444
|
+
|
|
445
|
+
@property
|
|
446
|
+
def ok(self) -> bool:
|
|
447
|
+
if self.plan.never_fails:
|
|
448
|
+
return True
|
|
449
|
+
for command, result in zip(self.plan.commands, self.results):
|
|
450
|
+
if result.return_code != 0 and not command.allow_failure:
|
|
451
|
+
return False
|
|
452
|
+
for success_group in self.plan.require_one_success:
|
|
453
|
+
if not any(
|
|
454
|
+
index < len(self.results) and self.results[index].return_code == 0
|
|
455
|
+
for index in success_group
|
|
456
|
+
):
|
|
457
|
+
return False
|
|
458
|
+
return True
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def _git(*args: str, allow_failure: bool = False, description: str = "") -> GitCommand:
|
|
462
|
+
return GitCommand(args=args, allow_failure=allow_failure, description=description)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def branch_create_plan(context: BranchContext) -> GitCommandPlan:
|
|
466
|
+
"""Plan shell-equivalent branch_create intent."""
|
|
467
|
+
return GitCommandPlan(
|
|
468
|
+
name="branch_create",
|
|
469
|
+
commands=(
|
|
470
|
+
_git("rev-parse", "--verify", context.working_branch, allow_failure=True, description="check existing branch"),
|
|
471
|
+
_git("checkout", "-b", context.working_branch, context.base_branch, allow_failure=True, description="create branch if absent"),
|
|
472
|
+
_git("checkout", context.working_branch, allow_failure=True, description="checkout existing branch fallback"),
|
|
473
|
+
),
|
|
474
|
+
notes=("Create and checkout branch from source, or checkout if it already exists.",),
|
|
475
|
+
require_one_success=((1, 2),),
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def branch_return_plan(context: BranchContext) -> GitCommandPlan:
|
|
480
|
+
"""Plan shell-equivalent branch_return intent."""
|
|
481
|
+
return GitCommandPlan(
|
|
482
|
+
name="branch_return",
|
|
483
|
+
commands=(
|
|
484
|
+
_git("rev-parse", "--abbrev-ref", "HEAD", description="detect current branch"),
|
|
485
|
+
_git("checkout", context.base_branch, allow_failure=True, description="checkout original branch if needed"),
|
|
486
|
+
),
|
|
487
|
+
notes=("Safe when already on the original branch.",),
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def branch_merge_plan(context: BranchContext, *, auto_push: bool = False) -> GitCommandPlan:
|
|
492
|
+
"""Plan shell-equivalent branch_merge safety invariants."""
|
|
493
|
+
commands = [
|
|
494
|
+
_git("diff", "--name-only", allow_failure=True, description="detect tracked dirty files"),
|
|
495
|
+
_git("diff", "--cached", "--name-only", allow_failure=True, description="detect staged dirty files"),
|
|
496
|
+
_git("stash", "push", "-m", "pipeline-merge-stash", allow_failure=True, description="stash tracked dirty files only"),
|
|
497
|
+
_git("rebase", context.base_branch, context.working_branch, description="rebase dev onto original"),
|
|
498
|
+
_git("checkout", context.base_branch, description="checkout original for fast-forward"),
|
|
499
|
+
_git("merge", "--ff-only", context.working_branch, description="fast-forward original"),
|
|
500
|
+
]
|
|
501
|
+
if auto_push:
|
|
502
|
+
commands.append(_git("push", allow_failure=True, description="optional auto-push"))
|
|
503
|
+
commands.extend(
|
|
504
|
+
[
|
|
505
|
+
_git("branch", "-d", context.working_branch, allow_failure=True, description="delete merged dev branch"),
|
|
506
|
+
_git("stash", "pop", allow_failure=True, description="restore stashed tracked files"),
|
|
507
|
+
]
|
|
508
|
+
)
|
|
509
|
+
return GitCommandPlan(
|
|
510
|
+
name="branch_merge",
|
|
511
|
+
commands=tuple(commands),
|
|
512
|
+
notes=(
|
|
513
|
+
"Tracked-only stash avoids capturing ignored state artifacts.",
|
|
514
|
+
"Rebase then fast-forward preserves merge invariant.",
|
|
515
|
+
),
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def branch_save_wip_plan(context: BranchContext) -> GitCommandPlan:
|
|
520
|
+
"""Plan shell-equivalent never-fail WIP preservation."""
|
|
521
|
+
return GitCommandPlan(
|
|
522
|
+
name="branch_save_wip",
|
|
523
|
+
commands=(
|
|
524
|
+
_git("rev-parse", "--abbrev-ref", "HEAD", allow_failure=True, description="verify current branch"),
|
|
525
|
+
_git("status", "--porcelain", allow_failure=True, description="detect uncommitted work"),
|
|
526
|
+
_git("add", "-A", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES, allow_failure=True, description="stage non-ignored work excluding hidden tool worktrees"),
|
|
527
|
+
_git(
|
|
528
|
+
"commit",
|
|
529
|
+
"--no-verify",
|
|
530
|
+
"-m",
|
|
531
|
+
f"wip({context.working_branch}): interrupted — uncommitted work saved",
|
|
532
|
+
"-m",
|
|
533
|
+
"Pipeline was interrupted by signal. This commit preserves work-in-progress.",
|
|
534
|
+
"-m",
|
|
535
|
+
f"To resume: git checkout {context.working_branch}",
|
|
536
|
+
allow_failure=True,
|
|
537
|
+
description="create WIP commit",
|
|
538
|
+
),
|
|
539
|
+
),
|
|
540
|
+
never_fails=True,
|
|
541
|
+
notes=("Never-fail cleanup helper; errors are diagnostics only.",),
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def branch_ensure_return_plan(context: BranchContext) -> GitCommandPlan:
|
|
546
|
+
"""Plan shell-equivalent guaranteed return cleanup."""
|
|
547
|
+
return GitCommandPlan(
|
|
548
|
+
name="branch_ensure_return",
|
|
549
|
+
commands=(
|
|
550
|
+
_git("rebase", "--abort", allow_failure=True, description="abort in-progress rebase"),
|
|
551
|
+
_git("rev-parse", "--abbrev-ref", "HEAD", allow_failure=True, description="detect current branch"),
|
|
552
|
+
_git("status", "--porcelain", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES, allow_failure=True, description="detect WIP before checkout"),
|
|
553
|
+
_git("add", "-A", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES, allow_failure=True, description="stage WIP before checkout"),
|
|
554
|
+
_git(
|
|
555
|
+
"commit",
|
|
556
|
+
"--no-verify",
|
|
557
|
+
"-m",
|
|
558
|
+
f"wip({context.working_branch}): interrupted — uncommitted work saved",
|
|
559
|
+
"-m",
|
|
560
|
+
"Pipeline was interrupted by signal. This commit preserves work-in-progress.",
|
|
561
|
+
"-m",
|
|
562
|
+
f"To resume: git checkout {context.working_branch}",
|
|
563
|
+
allow_failure=True,
|
|
564
|
+
description="save WIP before return when on dev branch",
|
|
565
|
+
),
|
|
566
|
+
_git("stash", "push", "-m", "pipeline-ensure-return-stash", allow_failure=True, description="stash checkout blockers"),
|
|
567
|
+
_git("checkout", context.base_branch, allow_failure=True, description="return to original branch"),
|
|
568
|
+
_git("stash", "pop", allow_failure=True, description="restore stashed checkout blockers"),
|
|
569
|
+
),
|
|
570
|
+
never_fails=True,
|
|
571
|
+
notes=("Always returns success to support trap/finally cleanup paths.",),
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def worktree_add_plan(context: BranchContext, policy: WorktreePolicy) -> GitCommandPlan:
|
|
576
|
+
"""Plan worktree creation without recreating retired Windows runtime sources."""
|
|
577
|
+
root = policy.worktree_root or context.project_root.parent / f"{context.working_branch.replace('/', '-')}-worktree"
|
|
578
|
+
return GitCommandPlan(
|
|
579
|
+
name="worktree_add",
|
|
580
|
+
commands=(
|
|
581
|
+
_git("worktree", "add", str(root), context.working_branch, description="create git worktree"),
|
|
582
|
+
),
|
|
583
|
+
notes=("Generic git worktree helper; no retired platform source trees are created.",),
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def current_branch(project_root: Path) -> str:
|
|
588
|
+
"""Return current git branch name, or an empty string outside git."""
|
|
589
|
+
result = run_git_command(project_root, ("rev-parse", "--abbrev-ref", "HEAD"))
|
|
590
|
+
return result.stdout.strip() if result.return_code == 0 else ""
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def git_status_safe(project_root: Path) -> str:
|
|
594
|
+
"""Return status while excluding hidden AI tool worktree directories."""
|
|
595
|
+
result = run_git_command(project_root, ("status", "--porcelain", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES))
|
|
596
|
+
return result.stdout if result.return_code == 0 else ""
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def branch_save_wip(project_root: Path, working_branch: str) -> GitPlanResult:
|
|
600
|
+
"""Execute shell-compatible WIP save only when currently on working_branch."""
|
|
601
|
+
context = BranchContext(base_branch="", working_branch=working_branch, project_root=project_root)
|
|
602
|
+
plan = branch_save_wip_plan(context)
|
|
603
|
+
if not working_branch or current_branch(project_root) != working_branch or not git_status_safe(project_root).strip():
|
|
604
|
+
return GitPlanResult(plan=plan, results=())
|
|
605
|
+
return run_git_plan(project_root, plan)
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def worktree_save_wip(runtime: WorktreeRuntimeContext) -> GitPlanResult:
|
|
609
|
+
"""Preserve dirty linked-worktree changes on the task branch before removal."""
|
|
610
|
+
return branch_save_wip(runtime.worktree_path, runtime.context.working_branch)
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
def branch_ensure_return(project_root: Path, base_branch: str, working_branch: str = "") -> GitPlanResult:
|
|
614
|
+
"""Best-effort shell-compatible ensure-return helper that never reports failure."""
|
|
615
|
+
active = current_branch(project_root)
|
|
616
|
+
if working_branch and active == working_branch:
|
|
617
|
+
branch_save_wip(project_root, working_branch)
|
|
618
|
+
context = BranchContext(base_branch=base_branch, working_branch=working_branch or active, project_root=project_root)
|
|
619
|
+
return run_git_plan(project_root, branch_ensure_return_plan(context))
|
|
620
|
+
|
|
621
|
+
def run_git_command(project_root: Path, args: Sequence[str]) -> GitCommandResult:
|
|
622
|
+
"""Run one git command in a project root and capture output."""
|
|
623
|
+
completed = subprocess.run(
|
|
624
|
+
["git", "-C", str(project_root), *map(str, args)],
|
|
625
|
+
stdout=subprocess.PIPE,
|
|
626
|
+
stderr=subprocess.PIPE,
|
|
627
|
+
text=True,
|
|
628
|
+
check=False,
|
|
629
|
+
)
|
|
630
|
+
return GitCommandResult(
|
|
631
|
+
args=tuple(map(str, args)),
|
|
632
|
+
return_code=completed.returncode,
|
|
633
|
+
stdout=completed.stdout,
|
|
634
|
+
stderr=completed.stderr,
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
def run_git_plan(project_root: Path, plan: GitCommandPlan) -> GitPlanResult:
|
|
639
|
+
"""Run a plan sequentially, stopping on non-allowed failures."""
|
|
640
|
+
results: list[GitCommandResult] = []
|
|
641
|
+
for command in plan.commands:
|
|
642
|
+
result = run_git_command(project_root, command.args)
|
|
643
|
+
results.append(result)
|
|
644
|
+
if result.return_code != 0 and not (command.allow_failure or plan.never_fails):
|
|
645
|
+
break
|
|
646
|
+
return GitPlanResult(plan=plan, results=tuple(results))
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
@dataclass(frozen=True)
|
|
650
|
+
class ResetBranchCleanupResult:
|
|
651
|
+
"""Observed result for reset-time branch cleanup."""
|
|
652
|
+
|
|
653
|
+
branch: str
|
|
654
|
+
cleaned: bool
|
|
655
|
+
deleted: bool
|
|
656
|
+
returned_to: str
|
|
657
|
+
warnings: tuple[str, ...] = ()
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def default_branch(project_root: Path) -> str:
|
|
661
|
+
"""Resolve the same main/master/default-branch fallback used by reset scripts."""
|
|
662
|
+
origin = run_git_command(project_root, ("symbolic-ref", "refs/remotes/origin/HEAD"))
|
|
663
|
+
if origin.return_code == 0 and origin.stdout.strip():
|
|
664
|
+
return origin.stdout.strip().removeprefix("refs/remotes/origin/")
|
|
665
|
+
if run_git_command(project_root, ("rev-parse", "--verify", "main")).return_code == 0:
|
|
666
|
+
return "main"
|
|
667
|
+
if run_git_command(project_root, ("rev-parse", "--verify", "master")).return_code == 0:
|
|
668
|
+
return "master"
|
|
669
|
+
return current_branch(project_root) or "main"
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
def reset_branch_pattern(prefix: str, item_id: str) -> str:
|
|
673
|
+
"""Return the reset helper branch glob for one item."""
|
|
674
|
+
return f"{prefix}/{item_id}-*"
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def list_reset_branches(project_root: Path, prefix: str, item_id: str) -> tuple[str, ...]:
|
|
678
|
+
"""List dev branches that shell reset helpers would clean for an item."""
|
|
679
|
+
result = run_git_command(project_root, ("branch", "--list", reset_branch_pattern(prefix, item_id)))
|
|
680
|
+
if result.return_code != 0:
|
|
681
|
+
return ()
|
|
682
|
+
branches = []
|
|
683
|
+
for line in result.stdout.splitlines():
|
|
684
|
+
branch = line.strip().lstrip("* ").strip()
|
|
685
|
+
if branch:
|
|
686
|
+
branches.append(branch)
|
|
687
|
+
return tuple(branches)
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
def reset_cleanup_branch(project_root: Path, branch: str, *, return_branch: str, delete: bool) -> ResetBranchCleanupResult:
|
|
691
|
+
"""Discard uncommitted branch changes and optionally delete the branch.
|
|
692
|
+
|
|
693
|
+
This is intentionally reset-specific: unlike interrupted runner cleanup, manual
|
|
694
|
+
reset abandons uncommitted work on matching dev branches to recreate the
|
|
695
|
+
shell helper contract.
|
|
696
|
+
"""
|
|
697
|
+
warnings: list[str] = []
|
|
698
|
+
active = current_branch(project_root)
|
|
699
|
+
restored = active
|
|
700
|
+
stashed = False
|
|
701
|
+
|
|
702
|
+
if active == branch:
|
|
703
|
+
run_git_command(project_root, ("reset", "HEAD"))
|
|
704
|
+
run_git_command(project_root, ("checkout", "--", "."))
|
|
705
|
+
run_git_command(project_root, ("clean", "-fd"))
|
|
706
|
+
checkout = run_git_command(project_root, ("checkout", return_branch))
|
|
707
|
+
if checkout.return_code != 0:
|
|
708
|
+
warnings.append(f"failed to checkout {return_branch}")
|
|
709
|
+
restored = branch
|
|
710
|
+
else:
|
|
711
|
+
restored = return_branch
|
|
712
|
+
else:
|
|
713
|
+
dirty = run_git_command(project_root, ("status", "--porcelain"))
|
|
714
|
+
if dirty.stdout.strip():
|
|
715
|
+
stash = run_git_command(project_root, ("stash", "push", "--include-untracked", "-m", "reset-pipeline-temp"))
|
|
716
|
+
stashed = stash.return_code == 0
|
|
717
|
+
checkout = run_git_command(project_root, ("checkout", branch))
|
|
718
|
+
if checkout.return_code == 0:
|
|
719
|
+
run_git_command(project_root, ("reset", "HEAD"))
|
|
720
|
+
run_git_command(project_root, ("checkout", "--", "."))
|
|
721
|
+
run_git_command(project_root, ("clean", "-fd"))
|
|
722
|
+
run_git_command(project_root, ("checkout", active or return_branch))
|
|
723
|
+
restored = active or return_branch
|
|
724
|
+
else:
|
|
725
|
+
warnings.append(f"could not checkout {branch} to discard changes")
|
|
726
|
+
if stashed:
|
|
727
|
+
run_git_command(project_root, ("stash", "pop"))
|
|
728
|
+
|
|
729
|
+
deleted = False
|
|
730
|
+
if delete:
|
|
731
|
+
deleted = run_git_command(project_root, ("branch", "-D", branch)).return_code == 0
|
|
732
|
+
if not deleted:
|
|
733
|
+
warnings.append(f"failed to delete branch {branch}")
|
|
734
|
+
return ResetBranchCleanupResult(branch=branch, cleaned=True, deleted=deleted, returned_to=restored, warnings=tuple(warnings))
|