prizmkit 1.1.100 → 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.
Files changed (309) hide show
  1. package/bin/create-prizmkit.js +4 -4
  2. package/bundled/VERSION.json +3 -3
  3. package/bundled/dev-pipeline/.env.example +3 -3
  4. package/bundled/dev-pipeline/README.md +110 -90
  5. package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +1 -1
  6. package/bundled/dev-pipeline/cli.py +27 -0
  7. package/bundled/dev-pipeline/prizmkit_runtime/__init__.py +13 -0
  8. package/bundled/dev-pipeline/prizmkit_runtime/__main__.py +9 -0
  9. package/bundled/dev-pipeline/prizmkit_runtime/cli.py +193 -0
  10. package/bundled/dev-pipeline/prizmkit_runtime/commands.py +313 -0
  11. package/bundled/dev-pipeline/prizmkit_runtime/compat.py +32 -0
  12. package/bundled/dev-pipeline/prizmkit_runtime/config.py +410 -0
  13. package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +478 -0
  14. package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +734 -0
  15. package/bundled/dev-pipeline/prizmkit_runtime/heartbeat.py +256 -0
  16. package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +109 -0
  17. package/bundled/dev-pipeline/prizmkit_runtime/paths.py +110 -0
  18. package/bundled/dev-pipeline/prizmkit_runtime/processes.py +373 -0
  19. package/bundled/dev-pipeline/prizmkit_runtime/reset.py +389 -0
  20. package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +51 -0
  21. package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +179 -0
  22. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +345 -0
  23. package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +118 -0
  24. package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +187 -0
  25. package/bundled/dev-pipeline/prizmkit_runtime/runner_status.py +147 -0
  26. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +422 -0
  27. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +552 -0
  28. package/bundled/dev-pipeline/prizmkit_runtime/status.py +71 -0
  29. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +38 -23
  30. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +20 -5
  31. package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +2 -2
  32. package/bundled/dev-pipeline/scripts/parse-stream-progress.py +39 -0
  33. package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +7 -1
  34. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +4 -0
  35. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +12 -3
  36. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +13 -3
  37. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +13 -3
  38. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +2 -0
  39. package/bundled/dev-pipeline/templates/sections/log-size-awareness.md +9 -30
  40. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +15 -7
  41. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +42 -29
  42. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +41 -39
  43. package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +4 -0
  44. package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +4 -0
  45. package/bundled/dev-pipeline/tests/test_auto_skip.py +436 -14
  46. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +104 -0
  47. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +40 -0
  48. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +1138 -0
  49. package/bundled/dev-pipeline/tests/test_unified_cli.py +1045 -0
  50. package/bundled/skills/_metadata.json +1 -1
  51. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +29 -34
  52. package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +6 -5
  53. package/bundled/skills/feature-pipeline-launcher/SKILL.md +29 -35
  54. package/bundled/skills/feature-pipeline-launcher/references/configuration.md +7 -6
  55. package/bundled/skills/feature-workflow/SKILL.md +3 -6
  56. package/bundled/skills/recovery-workflow/SKILL.md +23 -32
  57. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +29 -35
  58. package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +7 -6
  59. package/bundled/skills/refactor-workflow/SKILL.md +3 -6
  60. package/bundled/templates/hooks/commit-intent.json +2 -2
  61. package/bundled/templates/hooks/diff-prizm-docs.py +226 -0
  62. package/bundled/templates/hooks/prizm-pre-commit.py +45 -0
  63. package/bundled/templates/hooks/validate-prizm-docs.py +240 -0
  64. package/package.json +1 -1
  65. package/src/config.js +33 -12
  66. package/src/index.js +3 -6
  67. package/src/manifest.js +3 -2
  68. package/src/metadata.js +7 -18
  69. package/src/prompts.js +0 -12
  70. package/src/runtimes.js +18 -11
  71. package/src/scaffold.js +133 -116
  72. package/src/upgrade.js +1 -1
  73. package/bundled/dev-pipeline/launch-bugfix-daemon.sh +0 -500
  74. package/bundled/dev-pipeline/launch-feature-daemon.sh +0 -657
  75. package/bundled/dev-pipeline/launch-refactor-daemon.sh +0 -502
  76. package/bundled/dev-pipeline/lib/branch.sh +0 -326
  77. package/bundled/dev-pipeline/lib/common.sh +0 -1476
  78. package/bundled/dev-pipeline/lib/heartbeat.sh +0 -469
  79. package/bundled/dev-pipeline/reset-bug.sh +0 -425
  80. package/bundled/dev-pipeline/reset-feature.sh +0 -436
  81. package/bundled/dev-pipeline/reset-refactor.sh +0 -423
  82. package/bundled/dev-pipeline/run-bugfix.sh +0 -1430
  83. package/bundled/dev-pipeline/run-feature.sh +0 -1718
  84. package/bundled/dev-pipeline/run-recovery.sh +0 -648
  85. package/bundled/dev-pipeline/run-refactor.sh +0 -1463
  86. package/bundled/dev-pipeline/tests/test-path-resolution.sh +0 -100
  87. package/bundled/dev-pipeline-windows/.env.example +0 -36
  88. package/bundled/dev-pipeline-windows/README.md +0 -30
  89. package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +0 -533
  90. package/bundled/dev-pipeline-windows/assets/feature-list-example.json +0 -146
  91. package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +0 -137
  92. package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +0 -9
  93. package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +0 -9
  94. package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +0 -9
  95. package/bundled/dev-pipeline-windows/lib/branch.ps1 +0 -235
  96. package/bundled/dev-pipeline-windows/lib/common.ps1 +0 -991
  97. package/bundled/dev-pipeline-windows/lib/daemon.ps1 +0 -140
  98. package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +0 -1051
  99. package/bundled/dev-pipeline-windows/lib/reset.ps1 +0 -121
  100. package/bundled/dev-pipeline-windows/reset-bug.ps1 +0 -10
  101. package/bundled/dev-pipeline-windows/reset-feature.ps1 +0 -10
  102. package/bundled/dev-pipeline-windows/reset-refactor.ps1 +0 -10
  103. package/bundled/dev-pipeline-windows/run-bugfix.ps1 +0 -9
  104. package/bundled/dev-pipeline-windows/run-feature.ps1 +0 -9
  105. package/bundled/dev-pipeline-windows/run-recovery.ps1 +0 -179
  106. package/bundled/dev-pipeline-windows/run-refactor.ps1 +0 -9
  107. package/bundled/dev-pipeline-windows/scripts/check-session-status.py +0 -301
  108. package/bundled/dev-pipeline-windows/scripts/continuation.py +0 -374
  109. package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +0 -530
  110. package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +0 -2279
  111. package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +0 -763
  112. package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +0 -805
  113. package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +0 -841
  114. package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +0 -316
  115. package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +0 -134
  116. package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +0 -382
  117. package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +0 -399
  118. package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +0 -1362
  119. package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +0 -191
  120. package/bundled/dev-pipeline-windows/scripts/prizmkit-test-gate.py +0 -446
  121. package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +0 -1121
  122. package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +0 -173
  123. package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +0 -1789
  124. package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +0 -1327
  125. package/bundled/dev-pipeline-windows/scripts/utils.py +0 -542
  126. package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +0 -7
  127. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +0 -7
  128. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +0 -65
  129. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +0 -5
  130. package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +0 -7
  131. package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +0 -73
  132. package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +0 -589
  133. package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +0 -710
  134. package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +0 -791
  135. package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +0 -263
  136. package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +0 -326
  137. package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +0 -237
  138. package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +0 -340
  139. package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +0 -270
  140. package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +0 -7
  141. package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +0 -91
  142. package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +0 -34
  143. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +0 -10
  144. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +0 -12
  145. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +0 -7
  146. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +0 -8
  147. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +0 -9
  148. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +0 -6
  149. package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +0 -21
  150. package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +0 -21
  151. package/bundled/dev-pipeline-windows/templates/sections/log-size-awareness.md +0 -41
  152. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +0 -283
  153. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +0 -112
  154. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +0 -168
  155. package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +0 -82
  156. package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +0 -75
  157. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +0 -23
  158. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +0 -14
  159. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +0 -19
  160. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +0 -63
  161. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +0 -42
  162. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +0 -44
  163. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +0 -59
  164. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +0 -39
  165. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +0 -27
  166. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +0 -27
  167. package/bundled/dev-pipeline-windows/templates/sections/phase-prizmkit-test.md +0 -39
  168. package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +0 -35
  169. package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +0 -35
  170. package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +0 -73
  171. package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +0 -13
  172. package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +0 -21
  173. package/bundled/dev-pipeline-windows/templates/sections/session-context.md +0 -5
  174. package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +0 -6
  175. package/bundled/dev-pipeline-windows/templates/sections/task-contract.md +0 -34
  176. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +0 -48
  177. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +0 -48
  178. package/bundled/dev-pipeline-windows/templates/session-status-schema.json +0 -83
  179. package/bundled/skills-windows/app-planner/SKILL.md +0 -296
  180. package/bundled/skills-windows/app-planner/assets/app-design-guide.md +0 -101
  181. package/bundled/skills-windows/app-planner/references/architecture-decisions.md +0 -52
  182. package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +0 -101
  183. package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +0 -71
  184. package/bundled/skills-windows/app-planner/references/infrastructure-convention-discovery.md +0 -108
  185. package/bundled/skills-windows/app-planner/references/project-brief-guide.md +0 -82
  186. package/bundled/skills-windows/app-planner/references/project-conventions-discovery.md +0 -59
  187. package/bundled/skills-windows/app-planner/references/project-state-detection.md +0 -90
  188. package/bundled/skills-windows/app-planner/references/red-team-checklist.md +0 -40
  189. package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +0 -619
  190. package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +0 -285
  191. package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +0 -249
  192. package/bundled/skills-windows/app-planner/references/rules/backend/question-manifest.json +0 -46
  193. package/bundled/skills-windows/app-planner/references/rules/backend/template.md +0 -173
  194. package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +0 -382
  195. package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +0 -211
  196. package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +0 -184
  197. package/bundled/skills-windows/app-planner/references/rules/database/question-manifest.json +0 -39
  198. package/bundled/skills-windows/app-planner/references/rules/database/template.md +0 -158
  199. package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +0 -820
  200. package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +0 -188
  201. package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +0 -319
  202. package/bundled/skills-windows/app-planner/references/rules/frontend/question-manifest.json +0 -51
  203. package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +0 -339
  204. package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +0 -649
  205. package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +0 -290
  206. package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +0 -232
  207. package/bundled/skills-windows/app-planner/references/rules/mobile/question-manifest.json +0 -47
  208. package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +0 -175
  209. package/bundled/skills-windows/app-planner/references/rules-configuration.md +0 -46
  210. package/bundled/skills-windows/bug-fix-workflow/SKILL.md +0 -379
  211. package/bundled/skills-windows/bug-fix-workflow/references/bug-diagnosis.md +0 -41
  212. package/bundled/skills-windows/bug-planner/SKILL.md +0 -402
  213. package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +0 -43
  214. package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +0 -44
  215. package/bundled/skills-windows/bug-planner/references/error-recovery.md +0 -73
  216. package/bundled/skills-windows/bug-planner/references/input-formats.md +0 -53
  217. package/bundled/skills-windows/bug-planner/references/schema-validation.md +0 -25
  218. package/bundled/skills-windows/bug-planner/references/severity-rules.md +0 -16
  219. package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +0 -322
  220. package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +0 -304
  221. package/bundled/skills-windows/bugfix-pipeline-launcher/references/configuration.md +0 -84
  222. package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +0 -381
  223. package/bundled/skills-windows/feature-pipeline-launcher/references/configuration.md +0 -67
  224. package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +0 -462
  225. package/bundled/skills-windows/feature-planner/SKILL.md +0 -397
  226. package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +0 -64
  227. package/bundled/skills-windows/feature-planner/assets/planning-guide.md +0 -214
  228. package/bundled/skills-windows/feature-planner/references/browser-interaction.md +0 -59
  229. package/bundled/skills-windows/feature-planner/references/completeness-review.md +0 -57
  230. package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +0 -75
  231. package/bundled/skills-windows/feature-planner/references/error-recovery.md +0 -90
  232. package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +0 -112
  233. package/bundled/skills-windows/feature-planner/references/new-project-planning.md +0 -85
  234. package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +0 -1020
  235. package/bundled/skills-windows/feature-workflow/SKILL.md +0 -374
  236. package/bundled/skills-windows/feature-workflow/references/brainstorm-guide.md +0 -137
  237. package/bundled/skills-windows/prizm-kit/SKILL.md +0 -93
  238. package/bundled/skills-windows/prizmkit-code-review/SKILL.md +0 -161
  239. package/bundled/skills-windows/prizmkit-code-review/references/dev-agent-prompt.md +0 -30
  240. package/bundled/skills-windows/prizmkit-code-review/references/review-report-template.md +0 -31
  241. package/bundled/skills-windows/prizmkit-code-review/references/reviewer-agent-prompt.md +0 -66
  242. package/bundled/skills-windows/prizmkit-code-review/scripts/check_loop.py +0 -186
  243. package/bundled/skills-windows/prizmkit-committer/SKILL.md +0 -89
  244. package/bundled/skills-windows/prizmkit-deploy/SKILL.md +0 -444
  245. package/bundled/skills-windows/prizmkit-deploy/references/ci-cd-workflows.md +0 -115
  246. package/bundled/skills-windows/prizmkit-deploy/references/cloud-platform-deploy.md +0 -93
  247. package/bundled/skills-windows/prizmkit-deploy/references/data-safety-examples.md +0 -120
  248. package/bundled/skills-windows/prizmkit-deploy/references/database-setup.md +0 -46
  249. package/bundled/skills-windows/prizmkit-deploy/references/deploy-config-schema.md +0 -148
  250. package/bundled/skills-windows/prizmkit-deploy/references/deploy-history-schema.md +0 -62
  251. package/bundled/skills-windows/prizmkit-deploy/references/deployment-modes.md +0 -50
  252. package/bundled/skills-windows/prizmkit-deploy/references/direct-upload.md +0 -26
  253. package/bundled/skills-windows/prizmkit-deploy/references/dns-setup.md +0 -42
  254. package/bundled/skills-windows/prizmkit-deploy/references/docker-deploy.md +0 -31
  255. package/bundled/skills-windows/prizmkit-deploy/references/firewall-setup.md +0 -37
  256. package/bundled/skills-windows/prizmkit-deploy/references/live-validation-notes.md +0 -21
  257. package/bundled/skills-windows/prizmkit-deploy/references/nginx-blue-green.md +0 -59
  258. package/bundled/skills-windows/prizmkit-deploy/references/ssh-bootstrap-flow.md +0 -49
  259. package/bundled/skills-windows/prizmkit-deploy/references/ssh-execution-flow.md +0 -41
  260. package/bundled/skills-windows/prizmkit-deploy/references/ssh-takeover.md +0 -20
  261. package/bundled/skills-windows/prizmkit-deploy/references/ssl-setup.md +0 -56
  262. package/bundled/skills-windows/prizmkit-implement/SKILL.md +0 -71
  263. package/bundled/skills-windows/prizmkit-init/SKILL.md +0 -356
  264. package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +0 -82
  265. package/bundled/skills-windows/prizmkit-init/references/config-schema.md +0 -70
  266. package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +0 -41
  267. package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +0 -13
  268. package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +0 -9
  269. package/bundled/skills-windows/prizmkit-plan/SKILL.md +0 -102
  270. package/bundled/skills-windows/prizmkit-plan/assets/plan-template.md +0 -115
  271. package/bundled/skills-windows/prizmkit-plan/assets/spec-template.md +0 -73
  272. package/bundled/skills-windows/prizmkit-plan/references/clarify-guide.md +0 -67
  273. package/bundled/skills-windows/prizmkit-plan/references/examples.md +0 -85
  274. package/bundled/skills-windows/prizmkit-plan/references/verification-checklist.md +0 -60
  275. package/bundled/skills-windows/prizmkit-prizm-docs/SKILL.md +0 -129
  276. package/bundled/skills-windows/prizmkit-prizm-docs/assets/prizm-docs-format.md +0 -613
  277. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-init.md +0 -45
  278. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-rebuild.md +0 -15
  279. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-status.md +0 -14
  280. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-update.md +0 -19
  281. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-validate.md +0 -17
  282. package/bundled/skills-windows/prizmkit-retrospective/SKILL.md +0 -87
  283. package/bundled/skills-windows/prizmkit-retrospective/references/knowledge-injection-steps.md +0 -50
  284. package/bundled/skills-windows/prizmkit-retrospective/references/structural-sync-steps.md +0 -43
  285. package/bundled/skills-windows/prizmkit-test/SKILL.md +0 -227
  286. package/bundled/skills-windows/prizmkit-test/references/boundary-coverage-protocol.md +0 -220
  287. package/bundled/skills-windows/prizmkit-test/references/examples.md +0 -143
  288. package/bundled/skills-windows/prizmkit-test/references/service-boundary-test-catalog.md +0 -225
  289. package/bundled/skills-windows/prizmkit-test/references/test-generation-steps.md +0 -109
  290. package/bundled/skills-windows/prizmkit-test/references/test-report-template.md +0 -111
  291. package/bundled/skills-windows/prizmkit-test/scripts/validate_boundary_report.py +0 -347
  292. package/bundled/skills-windows/recovery-workflow/SKILL.md +0 -355
  293. package/bundled/skills-windows/recovery-workflow/evals/evals.json +0 -46
  294. package/bundled/skills-windows/recovery-workflow/references/detection.md +0 -58
  295. package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +0 -544
  296. package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +0 -346
  297. package/bundled/skills-windows/refactor-pipeline-launcher/references/configuration.md +0 -72
  298. package/bundled/skills-windows/refactor-planner/SKILL.md +0 -398
  299. package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +0 -292
  300. package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +0 -301
  301. package/bundled/skills-windows/refactor-planner/references/fast-path.md +0 -59
  302. package/bundled/skills-windows/refactor-planner/references/planning-phases.md +0 -135
  303. package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +0 -221
  304. package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +0 -858
  305. package/bundled/skills-windows/refactor-workflow/SKILL.md +0 -343
  306. package/bundled/skills-windows/refactor-workflow/references/brainstorm-guide.md +0 -116
  307. package/bundled/templates/hooks/diff-prizm-docs.sh +0 -158
  308. package/bundled/templates/hooks/prizm-pre-commit.sh +0 -108
  309. 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))