prizmkit 1.1.100 → 1.1.102

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) 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 +355 -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 +153 -0
  26. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +425 -0
  27. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +560 -0
  28. package/bundled/dev-pipeline/prizmkit_runtime/status.py +71 -0
  29. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +51 -36
  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 +8 -2
  34. package/bundled/dev-pipeline/templates/agent-prompts/reviewer-review.md +1 -1
  35. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +6 -2
  36. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +14 -6
  37. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +14 -4
  38. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +14 -4
  39. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +2 -0
  40. package/bundled/dev-pipeline/templates/sections/log-size-awareness.md +9 -30
  41. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +15 -7
  42. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +42 -29
  43. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +41 -39
  44. package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +8 -17
  45. package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +2 -2
  46. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +1 -3
  47. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +1 -3
  48. package/bundled/dev-pipeline/templates/sections/phase0-test-baseline.md +3 -8
  49. package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +6 -2
  50. package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +5 -1
  51. package/bundled/dev-pipeline/tests/test_auto_skip.py +436 -14
  52. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +115 -7
  53. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +40 -0
  54. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +1226 -0
  55. package/bundled/dev-pipeline/tests/test_unified_cli.py +1045 -0
  56. package/bundled/skills/_metadata.json +1 -1
  57. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +29 -34
  58. package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +6 -5
  59. package/bundled/skills/feature-pipeline-launcher/SKILL.md +29 -35
  60. package/bundled/skills/feature-pipeline-launcher/references/configuration.md +7 -6
  61. package/bundled/skills/feature-workflow/SKILL.md +3 -6
  62. package/bundled/skills/recovery-workflow/SKILL.md +23 -32
  63. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +29 -35
  64. package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +7 -6
  65. package/bundled/skills/refactor-workflow/SKILL.md +3 -6
  66. package/bundled/templates/hooks/commit-intent.json +2 -2
  67. package/bundled/templates/hooks/diff-prizm-docs.py +226 -0
  68. package/bundled/templates/hooks/prizm-pre-commit.py +45 -0
  69. package/bundled/templates/hooks/validate-prizm-docs.py +240 -0
  70. package/package.json +1 -1
  71. package/src/config.js +42 -19
  72. package/src/external-skills.js +14 -14
  73. package/src/index.js +3 -6
  74. package/src/manifest.js +11 -5
  75. package/src/metadata.js +7 -18
  76. package/src/platforms.js +25 -0
  77. package/src/prompts.js +0 -12
  78. package/src/runtimes.js +18 -11
  79. package/src/scaffold.js +144 -122
  80. package/src/upgrade.js +12 -3
  81. package/bundled/dev-pipeline/launch-bugfix-daemon.sh +0 -500
  82. package/bundled/dev-pipeline/launch-feature-daemon.sh +0 -657
  83. package/bundled/dev-pipeline/launch-refactor-daemon.sh +0 -502
  84. package/bundled/dev-pipeline/lib/branch.sh +0 -326
  85. package/bundled/dev-pipeline/lib/common.sh +0 -1476
  86. package/bundled/dev-pipeline/lib/heartbeat.sh +0 -469
  87. package/bundled/dev-pipeline/reset-bug.sh +0 -425
  88. package/bundled/dev-pipeline/reset-feature.sh +0 -436
  89. package/bundled/dev-pipeline/reset-refactor.sh +0 -423
  90. package/bundled/dev-pipeline/run-bugfix.sh +0 -1430
  91. package/bundled/dev-pipeline/run-feature.sh +0 -1718
  92. package/bundled/dev-pipeline/run-recovery.sh +0 -648
  93. package/bundled/dev-pipeline/run-refactor.sh +0 -1463
  94. package/bundled/dev-pipeline/tests/test-path-resolution.sh +0 -100
  95. package/bundled/dev-pipeline-windows/.env.example +0 -36
  96. package/bundled/dev-pipeline-windows/README.md +0 -30
  97. package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +0 -533
  98. package/bundled/dev-pipeline-windows/assets/feature-list-example.json +0 -146
  99. package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +0 -137
  100. package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +0 -9
  101. package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +0 -9
  102. package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +0 -9
  103. package/bundled/dev-pipeline-windows/lib/branch.ps1 +0 -235
  104. package/bundled/dev-pipeline-windows/lib/common.ps1 +0 -991
  105. package/bundled/dev-pipeline-windows/lib/daemon.ps1 +0 -140
  106. package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +0 -1051
  107. package/bundled/dev-pipeline-windows/lib/reset.ps1 +0 -121
  108. package/bundled/dev-pipeline-windows/reset-bug.ps1 +0 -10
  109. package/bundled/dev-pipeline-windows/reset-feature.ps1 +0 -10
  110. package/bundled/dev-pipeline-windows/reset-refactor.ps1 +0 -10
  111. package/bundled/dev-pipeline-windows/run-bugfix.ps1 +0 -9
  112. package/bundled/dev-pipeline-windows/run-feature.ps1 +0 -9
  113. package/bundled/dev-pipeline-windows/run-recovery.ps1 +0 -179
  114. package/bundled/dev-pipeline-windows/run-refactor.ps1 +0 -9
  115. package/bundled/dev-pipeline-windows/scripts/check-session-status.py +0 -301
  116. package/bundled/dev-pipeline-windows/scripts/continuation.py +0 -374
  117. package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +0 -530
  118. package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +0 -2279
  119. package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +0 -763
  120. package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +0 -805
  121. package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +0 -841
  122. package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +0 -316
  123. package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +0 -134
  124. package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +0 -382
  125. package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +0 -399
  126. package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +0 -1362
  127. package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +0 -191
  128. package/bundled/dev-pipeline-windows/scripts/prizmkit-test-gate.py +0 -446
  129. package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +0 -1121
  130. package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +0 -173
  131. package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +0 -1789
  132. package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +0 -1327
  133. package/bundled/dev-pipeline-windows/scripts/utils.py +0 -542
  134. package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +0 -7
  135. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +0 -7
  136. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +0 -65
  137. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +0 -5
  138. package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +0 -7
  139. package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +0 -73
  140. package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +0 -589
  141. package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +0 -710
  142. package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +0 -791
  143. package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +0 -263
  144. package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +0 -326
  145. package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +0 -237
  146. package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +0 -340
  147. package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +0 -270
  148. package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +0 -7
  149. package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +0 -91
  150. package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +0 -34
  151. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +0 -10
  152. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +0 -12
  153. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +0 -7
  154. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +0 -8
  155. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +0 -9
  156. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +0 -6
  157. package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +0 -21
  158. package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +0 -21
  159. package/bundled/dev-pipeline-windows/templates/sections/log-size-awareness.md +0 -41
  160. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +0 -283
  161. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +0 -112
  162. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +0 -168
  163. package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +0 -82
  164. package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +0 -75
  165. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +0 -23
  166. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +0 -14
  167. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +0 -19
  168. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +0 -63
  169. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +0 -42
  170. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +0 -44
  171. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +0 -59
  172. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +0 -39
  173. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +0 -27
  174. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +0 -27
  175. package/bundled/dev-pipeline-windows/templates/sections/phase-prizmkit-test.md +0 -39
  176. package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +0 -35
  177. package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +0 -35
  178. package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +0 -73
  179. package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +0 -13
  180. package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +0 -21
  181. package/bundled/dev-pipeline-windows/templates/sections/session-context.md +0 -5
  182. package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +0 -6
  183. package/bundled/dev-pipeline-windows/templates/sections/task-contract.md +0 -34
  184. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +0 -48
  185. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +0 -48
  186. package/bundled/dev-pipeline-windows/templates/session-status-schema.json +0 -83
  187. package/bundled/skills-windows/app-planner/SKILL.md +0 -296
  188. package/bundled/skills-windows/app-planner/assets/app-design-guide.md +0 -101
  189. package/bundled/skills-windows/app-planner/references/architecture-decisions.md +0 -52
  190. package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +0 -101
  191. package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +0 -71
  192. package/bundled/skills-windows/app-planner/references/infrastructure-convention-discovery.md +0 -108
  193. package/bundled/skills-windows/app-planner/references/project-brief-guide.md +0 -82
  194. package/bundled/skills-windows/app-planner/references/project-conventions-discovery.md +0 -59
  195. package/bundled/skills-windows/app-planner/references/project-state-detection.md +0 -90
  196. package/bundled/skills-windows/app-planner/references/red-team-checklist.md +0 -40
  197. package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +0 -619
  198. package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +0 -285
  199. package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +0 -249
  200. package/bundled/skills-windows/app-planner/references/rules/backend/question-manifest.json +0 -46
  201. package/bundled/skills-windows/app-planner/references/rules/backend/template.md +0 -173
  202. package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +0 -382
  203. package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +0 -211
  204. package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +0 -184
  205. package/bundled/skills-windows/app-planner/references/rules/database/question-manifest.json +0 -39
  206. package/bundled/skills-windows/app-planner/references/rules/database/template.md +0 -158
  207. package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +0 -820
  208. package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +0 -188
  209. package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +0 -319
  210. package/bundled/skills-windows/app-planner/references/rules/frontend/question-manifest.json +0 -51
  211. package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +0 -339
  212. package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +0 -649
  213. package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +0 -290
  214. package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +0 -232
  215. package/bundled/skills-windows/app-planner/references/rules/mobile/question-manifest.json +0 -47
  216. package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +0 -175
  217. package/bundled/skills-windows/app-planner/references/rules-configuration.md +0 -46
  218. package/bundled/skills-windows/bug-fix-workflow/SKILL.md +0 -379
  219. package/bundled/skills-windows/bug-fix-workflow/references/bug-diagnosis.md +0 -41
  220. package/bundled/skills-windows/bug-planner/SKILL.md +0 -402
  221. package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +0 -43
  222. package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +0 -44
  223. package/bundled/skills-windows/bug-planner/references/error-recovery.md +0 -73
  224. package/bundled/skills-windows/bug-planner/references/input-formats.md +0 -53
  225. package/bundled/skills-windows/bug-planner/references/schema-validation.md +0 -25
  226. package/bundled/skills-windows/bug-planner/references/severity-rules.md +0 -16
  227. package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +0 -322
  228. package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +0 -304
  229. package/bundled/skills-windows/bugfix-pipeline-launcher/references/configuration.md +0 -84
  230. package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +0 -381
  231. package/bundled/skills-windows/feature-pipeline-launcher/references/configuration.md +0 -67
  232. package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +0 -462
  233. package/bundled/skills-windows/feature-planner/SKILL.md +0 -397
  234. package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +0 -64
  235. package/bundled/skills-windows/feature-planner/assets/planning-guide.md +0 -214
  236. package/bundled/skills-windows/feature-planner/references/browser-interaction.md +0 -59
  237. package/bundled/skills-windows/feature-planner/references/completeness-review.md +0 -57
  238. package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +0 -75
  239. package/bundled/skills-windows/feature-planner/references/error-recovery.md +0 -90
  240. package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +0 -112
  241. package/bundled/skills-windows/feature-planner/references/new-project-planning.md +0 -85
  242. package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +0 -1020
  243. package/bundled/skills-windows/feature-workflow/SKILL.md +0 -374
  244. package/bundled/skills-windows/feature-workflow/references/brainstorm-guide.md +0 -137
  245. package/bundled/skills-windows/prizm-kit/SKILL.md +0 -93
  246. package/bundled/skills-windows/prizmkit-code-review/SKILL.md +0 -161
  247. package/bundled/skills-windows/prizmkit-code-review/references/dev-agent-prompt.md +0 -30
  248. package/bundled/skills-windows/prizmkit-code-review/references/review-report-template.md +0 -31
  249. package/bundled/skills-windows/prizmkit-code-review/references/reviewer-agent-prompt.md +0 -66
  250. package/bundled/skills-windows/prizmkit-code-review/scripts/check_loop.py +0 -186
  251. package/bundled/skills-windows/prizmkit-committer/SKILL.md +0 -89
  252. package/bundled/skills-windows/prizmkit-deploy/SKILL.md +0 -444
  253. package/bundled/skills-windows/prizmkit-deploy/references/ci-cd-workflows.md +0 -115
  254. package/bundled/skills-windows/prizmkit-deploy/references/cloud-platform-deploy.md +0 -93
  255. package/bundled/skills-windows/prizmkit-deploy/references/data-safety-examples.md +0 -120
  256. package/bundled/skills-windows/prizmkit-deploy/references/database-setup.md +0 -46
  257. package/bundled/skills-windows/prizmkit-deploy/references/deploy-config-schema.md +0 -148
  258. package/bundled/skills-windows/prizmkit-deploy/references/deploy-history-schema.md +0 -62
  259. package/bundled/skills-windows/prizmkit-deploy/references/deployment-modes.md +0 -50
  260. package/bundled/skills-windows/prizmkit-deploy/references/direct-upload.md +0 -26
  261. package/bundled/skills-windows/prizmkit-deploy/references/dns-setup.md +0 -42
  262. package/bundled/skills-windows/prizmkit-deploy/references/docker-deploy.md +0 -31
  263. package/bundled/skills-windows/prizmkit-deploy/references/firewall-setup.md +0 -37
  264. package/bundled/skills-windows/prizmkit-deploy/references/live-validation-notes.md +0 -21
  265. package/bundled/skills-windows/prizmkit-deploy/references/nginx-blue-green.md +0 -59
  266. package/bundled/skills-windows/prizmkit-deploy/references/ssh-bootstrap-flow.md +0 -49
  267. package/bundled/skills-windows/prizmkit-deploy/references/ssh-execution-flow.md +0 -41
  268. package/bundled/skills-windows/prizmkit-deploy/references/ssh-takeover.md +0 -20
  269. package/bundled/skills-windows/prizmkit-deploy/references/ssl-setup.md +0 -56
  270. package/bundled/skills-windows/prizmkit-implement/SKILL.md +0 -71
  271. package/bundled/skills-windows/prizmkit-init/SKILL.md +0 -356
  272. package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +0 -82
  273. package/bundled/skills-windows/prizmkit-init/references/config-schema.md +0 -70
  274. package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +0 -41
  275. package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +0 -13
  276. package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +0 -9
  277. package/bundled/skills-windows/prizmkit-plan/SKILL.md +0 -102
  278. package/bundled/skills-windows/prizmkit-plan/assets/plan-template.md +0 -115
  279. package/bundled/skills-windows/prizmkit-plan/assets/spec-template.md +0 -73
  280. package/bundled/skills-windows/prizmkit-plan/references/clarify-guide.md +0 -67
  281. package/bundled/skills-windows/prizmkit-plan/references/examples.md +0 -85
  282. package/bundled/skills-windows/prizmkit-plan/references/verification-checklist.md +0 -60
  283. package/bundled/skills-windows/prizmkit-prizm-docs/SKILL.md +0 -129
  284. package/bundled/skills-windows/prizmkit-prizm-docs/assets/prizm-docs-format.md +0 -613
  285. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-init.md +0 -45
  286. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-rebuild.md +0 -15
  287. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-status.md +0 -14
  288. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-update.md +0 -19
  289. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-validate.md +0 -17
  290. package/bundled/skills-windows/prizmkit-retrospective/SKILL.md +0 -87
  291. package/bundled/skills-windows/prizmkit-retrospective/references/knowledge-injection-steps.md +0 -50
  292. package/bundled/skills-windows/prizmkit-retrospective/references/structural-sync-steps.md +0 -43
  293. package/bundled/skills-windows/prizmkit-test/SKILL.md +0 -227
  294. package/bundled/skills-windows/prizmkit-test/references/boundary-coverage-protocol.md +0 -220
  295. package/bundled/skills-windows/prizmkit-test/references/examples.md +0 -143
  296. package/bundled/skills-windows/prizmkit-test/references/service-boundary-test-catalog.md +0 -225
  297. package/bundled/skills-windows/prizmkit-test/references/test-generation-steps.md +0 -109
  298. package/bundled/skills-windows/prizmkit-test/references/test-report-template.md +0 -111
  299. package/bundled/skills-windows/prizmkit-test/scripts/validate_boundary_report.py +0 -347
  300. package/bundled/skills-windows/recovery-workflow/SKILL.md +0 -355
  301. package/bundled/skills-windows/recovery-workflow/evals/evals.json +0 -46
  302. package/bundled/skills-windows/recovery-workflow/references/detection.md +0 -58
  303. package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +0 -544
  304. package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +0 -346
  305. package/bundled/skills-windows/refactor-pipeline-launcher/references/configuration.md +0 -72
  306. package/bundled/skills-windows/refactor-planner/SKILL.md +0 -398
  307. package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +0 -292
  308. package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +0 -301
  309. package/bundled/skills-windows/refactor-planner/references/fast-path.md +0 -59
  310. package/bundled/skills-windows/refactor-planner/references/planning-phases.md +0 -135
  311. package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +0 -221
  312. package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +0 -858
  313. package/bundled/skills-windows/refactor-workflow/SKILL.md +0 -343
  314. package/bundled/skills-windows/refactor-workflow/references/brainstorm-guide.md +0 -116
  315. package/bundled/templates/hooks/diff-prizm-docs.sh +0 -158
  316. package/bundled/templates/hooks/prizm-pre-commit.sh +0 -108
  317. package/bundled/templates/hooks/validate-prizm-docs.sh +0 -139
@@ -0,0 +1,560 @@
1
+ """AI session launch, command construction, logging, and raw signal capture."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.util
6
+ import json
7
+ import os
8
+ import queue
9
+ import shutil
10
+ import subprocess
11
+ import threading
12
+ import time
13
+ from collections import Counter
14
+ from collections.abc import Callable, Mapping, Sequence
15
+ from dataclasses import dataclass, field
16
+ from pathlib import Path
17
+
18
+ from .heartbeat import HeartbeatConfig, HeartbeatMonitor, write_stale_marker
19
+ from .processes import ProcessHandle, ProcessSpec, launch_process, terminate_process_tree
20
+ from .status import ProgressSummary
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class StreamJsonSupport:
25
+ """Detected stream-json capability for an AI CLI."""
26
+
27
+ enabled: bool
28
+ format: str = ""
29
+ source: str = ""
30
+ notes: tuple[str, ...] = ()
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class AISessionConfig:
35
+ """Inputs needed to launch one AI CLI session."""
36
+
37
+ cli_command: str
38
+ platform: str
39
+ model: str | None
40
+ prompt_path: Path
41
+ cwd: Path
42
+ log_path: Path | None = None
43
+ backup_log_path: Path | None = None
44
+ progress_path: Path | None = None
45
+ heartbeat_path: Path | None = None
46
+ effort: str | None = None
47
+ verbose: bool = False
48
+ use_stream_json: bool = False
49
+ stream_json_format: str = ""
50
+ permission_mode: str = "dangerously-skip-permissions"
51
+ total_timeout_seconds: float | None = None
52
+ stale_kill_threshold_seconds: float = 0
53
+ stale_kill_grace_seconds: float = 10
54
+ codex_subagent_timeout_seconds: int = 3300
55
+ extra_args: tuple[str, ...] = ()
56
+ environment_overrides: Mapping[str, str] = field(default_factory=dict)
57
+
58
+
59
+ @dataclass(frozen=True)
60
+ class AISessionCommand:
61
+ """Provider-specific command shape."""
62
+
63
+ argv: tuple[str, ...]
64
+ stdin_text: str | None
65
+ provider: str
66
+ prompt_mode: str
67
+
68
+
69
+ @dataclass(frozen=True)
70
+ class LogRecoveryResult:
71
+ """Result from backup session-log recovery."""
72
+
73
+ recovered: bool
74
+ cleaned_backup: bool
75
+ main_size: int
76
+ backup_size: int
77
+ session_log: Path
78
+ backup_log: Path
79
+
80
+
81
+ @dataclass(frozen=True)
82
+ class AISessionResult:
83
+ """Raw runtime signals from a session run, not final pipeline status."""
84
+
85
+ command: AISessionCommand
86
+ exit_code: int | None
87
+ timed_out: bool
88
+ termination_reason: str | None
89
+ stale_marker: Path | None
90
+ fatal_error_code: str
91
+ progress_summary: ProgressSummary
92
+ session_log: Path
93
+ backup_log: Path
94
+ log_recovery: LogRecoveryResult | None
95
+ started_epoch: float
96
+ ended_epoch: float
97
+
98
+
99
+ HelpRunner = Callable[[Sequence[str]], str]
100
+
101
+
102
+ def _normalize_platform(platform: str | None) -> str:
103
+ normalized = (platform or "").strip().lower()
104
+ return normalized if normalized in {"claude", "codex", "codebuddy"} else "codebuddy"
105
+
106
+
107
+ def _prompt_text(path: Path) -> str:
108
+ return path.read_text(encoding="utf-8")
109
+
110
+
111
+ def _default_help_runner(command: Sequence[str]) -> str:
112
+ completed = subprocess.run(
113
+ list(command),
114
+ stdout=subprocess.PIPE,
115
+ stderr=subprocess.STDOUT,
116
+ text=True,
117
+ check=False,
118
+ )
119
+ return completed.stdout or ""
120
+
121
+
122
+ def detect_stream_json_support(
123
+ cli_command: str,
124
+ platform: str | None,
125
+ help_runner: HelpRunner | None = None,
126
+ ) -> StreamJsonSupport:
127
+ """Detect stream-json support using shell-compatible provider rules."""
128
+ normalized = _normalize_platform(platform)
129
+ command_name = Path(str(cli_command).split()[0].strip("'\"")).name.lower()
130
+ if normalized == "codebuddy" or command_name == "cbc":
131
+ return StreamJsonSupport(True, "stream-json", "codebuddy-default")
132
+ runner = help_runner or _default_help_runner
133
+ if normalized == "codex":
134
+ help_output = runner((cli_command, "exec", "--help"))
135
+ if "--json" in help_output:
136
+ return StreamJsonSupport(True, "codex-json", "codex-exec-help")
137
+ return StreamJsonSupport(False, "", "codex-exec-help", ("--json not found",))
138
+ help_output = runner((cli_command, "--help"))
139
+ if "stream-json" in help_output:
140
+ return StreamJsonSupport(True, "stream-json", "cli-help")
141
+ return StreamJsonSupport(False, "", "cli-help", ("stream-json not found",))
142
+
143
+
144
+ def _codex_subagent_timeout(config: AISessionConfig) -> int:
145
+ if config.codex_subagent_timeout_seconds > 0:
146
+ return config.codex_subagent_timeout_seconds
147
+ return 3300
148
+
149
+
150
+ def build_ai_session_command(config: AISessionConfig) -> AISessionCommand:
151
+ """Build provider-specific AI CLI argv without spawning a process."""
152
+ platform = _normalize_platform(config.platform)
153
+ prompt = _prompt_text(config.prompt_path)
154
+ if platform == "claude":
155
+ argv = [
156
+ config.cli_command,
157
+ "-p",
158
+ prompt,
159
+ "--dangerously-skip-permissions",
160
+ "--disallowedTools",
161
+ "EnterWorktree",
162
+ ]
163
+ if config.verbose or config.use_stream_json:
164
+ argv.append("--verbose")
165
+ if config.use_stream_json:
166
+ argv.extend(["--output-format", "stream-json"])
167
+ if config.effort:
168
+ argv.extend(["--effort", config.effort])
169
+ if config.model:
170
+ argv.extend(["--model", config.model])
171
+ argv.extend(config.extra_args)
172
+ return AISessionCommand(tuple(argv), None, "claude", "argument")
173
+
174
+ if platform == "codex":
175
+ argv = [
176
+ config.cli_command,
177
+ "--ask-for-approval",
178
+ "never",
179
+ "--sandbox",
180
+ "danger-full-access",
181
+ ]
182
+ timeout = _codex_subagent_timeout(config)
183
+ if timeout > 0:
184
+ argv.extend(["--config", f"agents.job_max_runtime_seconds={timeout}"])
185
+ argv.extend(["--profile", "no-worktree"])
186
+ argv.extend(["exec", "--cd", str(config.cwd), "--skip-git-repo-check"])
187
+ if config.use_stream_json:
188
+ argv.append("--json")
189
+ if config.model:
190
+ argv.extend(["--model", config.model])
191
+ if config.effort:
192
+ argv.extend(["--config", f"model_reasoning_effort={config.effort}"])
193
+ argv.extend(config.extra_args)
194
+ argv.append("-")
195
+ return AISessionCommand(tuple(argv), prompt, "codex", "stdin")
196
+
197
+ argv = [config.cli_command, "--print", "-y", "--disallowedTools", "EnterWorktree"]
198
+ if config.verbose:
199
+ argv.append("--verbose")
200
+ if config.use_stream_json:
201
+ argv.extend(["--output-format", "stream-json"])
202
+ if config.effort:
203
+ argv.extend(["--effort", config.effort])
204
+ if config.model:
205
+ argv.extend(["--model", config.model])
206
+ argv.extend(config.extra_args)
207
+ return AISessionCommand(tuple(argv), prompt, "codebuddy", "stdin")
208
+
209
+
210
+ def default_backup_log_path(session_log: Path, home: Path | None = None) -> Path:
211
+ """Return the shell-compatible home backup path for a session log."""
212
+ session_dir = session_log.parent.parent
213
+ session_id = session_dir.name
214
+ root = home or Path.home()
215
+ return root / ".prizmkit" / "session-backups" / f"{session_id}.log"
216
+
217
+
218
+ def write_session_preamble(session_log: Path, backup_log: Path, prompt_path: Path, model: str | None) -> None:
219
+ """Write the session preamble to both primary and backup logs."""
220
+ session_log.parent.mkdir(parents=True, exist_ok=True)
221
+ backup_log.parent.mkdir(parents=True, exist_ok=True)
222
+ preamble = "\n".join(
223
+ [
224
+ "=== PRIZMKIT SESSION START ===",
225
+ f"Prompt: {prompt_path}",
226
+ f"Model: {model or 'default'}",
227
+ f"Started at epoch: {time.time():.6f}",
228
+ "--- OUTPUT BELOW ---",
229
+ "",
230
+ ]
231
+ )
232
+ session_log.write_text(preamble, encoding="utf-8")
233
+ backup_log.write_text(preamble, encoding="utf-8")
234
+
235
+
236
+ def recover_session_log(session_log: Path, backup_log: Path, min_size: int = 1024) -> LogRecoveryResult:
237
+ """Recover a truncated/unlinked session.log from the home backup."""
238
+ main_size = session_log.stat().st_size if session_log.is_file() else 0
239
+ backup_size = backup_log.stat().st_size if backup_log.is_file() else 0
240
+ recovered = False
241
+ if backup_log.is_file() and main_size < min_size and backup_size > main_size:
242
+ session_log.parent.mkdir(parents=True, exist_ok=True)
243
+ shutil.copyfile(backup_log, session_log)
244
+ recovered = True
245
+ cleaned = False
246
+ if backup_log.exists():
247
+ backup_log.unlink()
248
+ cleaned = True
249
+ return LogRecoveryResult(
250
+ recovered=recovered,
251
+ cleaned_backup=cleaned,
252
+ main_size=main_size,
253
+ backup_size=backup_size,
254
+ session_log=session_log,
255
+ backup_log=backup_log,
256
+ )
257
+
258
+
259
+ def _permission_denial_text(event: object) -> str:
260
+ text = json.dumps(event, ensure_ascii=False) if isinstance(event, dict) else str(event)
261
+ lower = text.lower()
262
+ markers = (
263
+ "permission denied",
264
+ "permission_denied",
265
+ "permission denial",
266
+ "denied by user",
267
+ "user denied",
268
+ "not permitted",
269
+ )
270
+ return text[:500] if any(marker in lower for marker in markers) else ""
271
+
272
+
273
+ def parse_stream_progress_lines(lines: Sequence[str]) -> ProgressSummary:
274
+ """Parse raw JSONL lines into additive provider-neutral progress metadata."""
275
+ event_type_counts: Counter[str] = Counter()
276
+ permission_denials: list[str] = []
277
+ claude_session_id = ""
278
+ cb_session_id = ""
279
+ result_subtype = ""
280
+ stop_reason = ""
281
+ has_usage = False
282
+ fatal_error_code = ""
283
+ terminal_result_text = ""
284
+ errors: list[str] = []
285
+
286
+ for raw_line in lines:
287
+ line = raw_line.strip()
288
+ if not line:
289
+ continue
290
+ try:
291
+ event = json.loads(line)
292
+ except json.JSONDecodeError:
293
+ denial = _permission_denial_text(line)
294
+ if denial:
295
+ permission_denials.append(denial)
296
+ continue
297
+ if not isinstance(event, dict):
298
+ continue
299
+ event_type = str(event.get("type") or "")
300
+ if event_type:
301
+ event_type_counts[event_type] += 1
302
+ if isinstance(event.get("sessionId"), str) and not cb_session_id:
303
+ cb_session_id = event["sessionId"].strip()
304
+ if event_type == "system" and event.get("subtype") == "init":
305
+ session_id = event.get("session_id")
306
+ if isinstance(session_id, str):
307
+ claude_session_id = session_id.strip()
308
+ if event_type == "result":
309
+ result_subtype = str(event.get("subtype") or event.get("result_subtype") or "")
310
+ stop_reason = str(event.get("stop_reason") or "")
311
+ terminal_result_text = str(event.get("result") or event.get("message") or "")[:1000]
312
+ if event_type in {"turn.failed", "error"}:
313
+ message = event.get("error") or event.get("message") or event
314
+ errors.append(str(message)[:500])
315
+ if event.get("usage") not in (None, "", {}):
316
+ has_usage = True
317
+ for key in ("fatal_error_code", "api_error_code", "error_code"):
318
+ value = event.get(key)
319
+ if value:
320
+ fatal_error_code = str(value)
321
+ break
322
+ error_obj = event.get("error")
323
+ if isinstance(error_obj, dict):
324
+ value = error_obj.get("code") or error_obj.get("type")
325
+ if value and not fatal_error_code:
326
+ fatal_error_code = str(value)
327
+ message = error_obj.get("message")
328
+ if message:
329
+ errors.append(str(message)[:500])
330
+ denial = _permission_denial_text(event)
331
+ if denial:
332
+ permission_denials.append(denial)
333
+
334
+ return ProgressSummary(
335
+ event_type_counts=dict(event_type_counts),
336
+ claude_session_id=claude_session_id,
337
+ cb_session_id=cb_session_id,
338
+ result_subtype=result_subtype,
339
+ stop_reason=stop_reason,
340
+ has_usage=has_usage,
341
+ permission_denial_count=len(permission_denials),
342
+ permission_denials=tuple(permission_denials[-10:]),
343
+ fatal_error_code=fatal_error_code,
344
+ terminal_result_text=terminal_result_text,
345
+ errors=tuple(errors[-10:]),
346
+ )
347
+
348
+
349
+ def _load_progress_tracker_class():
350
+ parser_path = Path(__file__).resolve().parents[1] / "scripts" / "parse-stream-progress.py"
351
+ spec = importlib.util.spec_from_file_location("prizmkit_parse_stream_progress", parser_path)
352
+ if spec is None or spec.loader is None:
353
+ return None
354
+ module = importlib.util.module_from_spec(spec)
355
+ spec.loader.exec_module(module)
356
+ return getattr(module, "ProgressTracker", None)
357
+
358
+
359
+ def _write_progress_summary(progress_path: Path | None, summary: ProgressSummary) -> None:
360
+ if progress_path is None:
361
+ return
362
+ progress_path.parent.mkdir(parents=True, exist_ok=True)
363
+ progress_path.write_text(json.dumps(summary.to_dict(), indent=2, sort_keys=True) + "\n", encoding="utf-8")
364
+
365
+
366
+ def _reader_thread(
367
+ pipe,
368
+ session_log: Path,
369
+ backup_log: Path,
370
+ output_queue: "queue.Queue[str]",
371
+ progress_path: Path | None = None,
372
+ ) -> None:
373
+ lines: list[str] = []
374
+ tracker = None
375
+ tracker_class = _load_progress_tracker_class() if progress_path is not None else None
376
+ if tracker_class is not None:
377
+ try:
378
+ tracker = tracker_class(session_log)
379
+ except Exception:
380
+ tracker = None
381
+ try:
382
+ for line in iter(pipe.readline, ""):
383
+ if not line:
384
+ break
385
+ with session_log.open("a", encoding="utf-8") as main_fh:
386
+ main_fh.write(line)
387
+ with backup_log.open("a", encoding="utf-8") as backup_fh:
388
+ backup_fh.write(line)
389
+ output_queue.put(line)
390
+ lines.append(line)
391
+ if progress_path is not None:
392
+ if tracker is not None:
393
+ stripped = line.strip()
394
+ if stripped:
395
+ try:
396
+ tracker.process_event(json.loads(stripped))
397
+ except json.JSONDecodeError:
398
+ tracker.last_text_snippet = stripped[:120]
399
+ tracker._detect_terminal_error(stripped, require_error_context=True)
400
+ data = tracker.to_dict()
401
+ progress_path.parent.mkdir(parents=True, exist_ok=True)
402
+ progress_path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8")
403
+ else:
404
+ _write_progress_summary(progress_path, parse_stream_progress_lines(lines))
405
+ finally:
406
+ try:
407
+ pipe.close()
408
+ except Exception:
409
+ pass
410
+
411
+
412
+ def _merged_progress(existing: Mapping[str, object], summary: ProgressSummary) -> dict[str, object]:
413
+ """Merge final summary into richer live parser output without erasing signals."""
414
+ merged = dict(existing)
415
+ for key, value in summary.to_dict().items():
416
+ current = merged.get(key)
417
+ if key == "event_type_counts" and isinstance(current, dict) and isinstance(value, dict):
418
+ combined = dict(value)
419
+ combined.update(current)
420
+ merged[key] = combined
421
+ elif current in (None, "", [], {}):
422
+ merged[key] = value
423
+ elif current == 0 and key == "permission_denial_count":
424
+ merged[key] = value
425
+ elif key not in merged:
426
+ merged[key] = value
427
+ return merged
428
+
429
+
430
+ def _summary_from_progress_dict(data: Mapping[str, object], fallback: ProgressSummary) -> ProgressSummary:
431
+ """Build ProgressSummary from merged progress data."""
432
+ event_counts = data.get("event_type_counts")
433
+ permission_denials = data.get("permission_denials")
434
+ errors = data.get("errors")
435
+ return ProgressSummary(
436
+ event_type_counts=event_counts if isinstance(event_counts, Mapping) else fallback.event_type_counts,
437
+ claude_session_id=str(data.get("claude_session_id") or fallback.claude_session_id),
438
+ cb_session_id=str(data.get("cb_session_id") or fallback.cb_session_id),
439
+ result_subtype=str(data.get("result_subtype") or fallback.result_subtype),
440
+ stop_reason=str(data.get("stop_reason") or fallback.stop_reason),
441
+ has_usage=bool(data.get("has_usage") or fallback.has_usage),
442
+ permission_denial_count=int(data.get("permission_denial_count") or fallback.permission_denial_count or 0),
443
+ permission_denials=tuple(str(item) for item in permission_denials) if isinstance(permission_denials, list) else fallback.permission_denials,
444
+ fatal_error_code=str(data.get("fatal_error_code") or fallback.fatal_error_code),
445
+ terminal_result_text=str(data.get("terminal_result_text") or fallback.terminal_result_text),
446
+ errors=tuple(str(item) for item in errors) if isinstance(errors, list) else fallback.errors,
447
+ )
448
+
449
+
450
+ class AISessionLauncher:
451
+ """AI session launcher returning raw runtime signals."""
452
+
453
+ def __init__(self, config: AISessionConfig):
454
+ self.config = config
455
+
456
+ def describe(self) -> str:
457
+ """Describe this launch contract without spawning a process."""
458
+ model_text = self.config.model or "CLI default model"
459
+ return f"AI session launch boundary: {self.config.platform} via {self.config.cli_command}, model={model_text}"
460
+
461
+ def run(self) -> AISessionResult:
462
+ """Run the AI CLI session and capture logs/progress/raw outcome signals."""
463
+ command = build_ai_session_command(self.config)
464
+ session_log = self.config.log_path or self.config.cwd / ".prizmkit" / "state" / "session.log"
465
+ backup_log = self.config.backup_log_path or default_backup_log_path(session_log)
466
+ write_session_preamble(session_log, backup_log, self.config.prompt_path, self.config.model)
467
+
468
+ spec = ProcessSpec(
469
+ command=command.argv,
470
+ cwd=self.config.cwd,
471
+ environment_overrides=self.config.environment_overrides,
472
+ environment_unsets=("CLAUDECODE",),
473
+ stdin_text=command.stdin_text,
474
+ )
475
+ handle = launch_process(spec, stderr=subprocess.STDOUT)
476
+ assert handle.popen is not None
477
+ output_queue: queue.Queue[str] = queue.Queue()
478
+ reader = threading.Thread(
479
+ target=_reader_thread,
480
+ args=(handle.popen.stdout, session_log, backup_log, output_queue, self.config.progress_path),
481
+ daemon=True,
482
+ )
483
+ reader.start()
484
+ if handle.popen.stdin is not None:
485
+ if command.stdin_text is not None:
486
+ handle.popen.stdin.write(command.stdin_text)
487
+ handle.popen.stdin.close()
488
+
489
+ heartbeat = None
490
+ heartbeat_path = self.config.heartbeat_path
491
+ if heartbeat_path is None and self.config.stale_kill_threshold_seconds > 0:
492
+ heartbeat_path = session_log.with_name("heartbeat.json")
493
+ if heartbeat_path is not None:
494
+ heartbeat = HeartbeatMonitor(
495
+ handle,
496
+ HeartbeatConfig(
497
+ heartbeat_file=heartbeat_path,
498
+ session_log=session_log,
499
+ progress_file=self.config.progress_path,
500
+ marker_dir=session_log.parent,
501
+ interval_seconds=0.1,
502
+ stale_after_seconds=self.config.stale_kill_threshold_seconds,
503
+ kill_after_seconds=self.config.stale_kill_threshold_seconds,
504
+ grace_seconds=self.config.stale_kill_grace_seconds,
505
+ ),
506
+ )
507
+ heartbeat.start()
508
+
509
+ started = time.time()
510
+ timed_out = False
511
+ termination_reason = None
512
+ stale_marker = None
513
+ try:
514
+ handle.popen.wait(timeout=self.config.total_timeout_seconds)
515
+ except subprocess.TimeoutExpired:
516
+ timed_out = True
517
+ termination_reason = "timeout"
518
+ terminate_process_tree(handle, "timeout", grace_seconds=self.config.stale_kill_grace_seconds)
519
+ reader.join(timeout=5)
520
+ heartbeat_result = heartbeat.stop() if heartbeat is not None else None
521
+ if heartbeat_result and heartbeat_result.killed:
522
+ termination_reason = heartbeat_result.reason
523
+ stale_marker = heartbeat_result.marker_path
524
+ if termination_reason is None and self.config.stale_kill_threshold_seconds > 0 and handle.popen.returncode is None:
525
+ stale_marker = write_stale_marker(
526
+ session_log.parent,
527
+ reason="stale_session",
528
+ stale_seconds=self.config.stale_kill_threshold_seconds,
529
+ threshold=self.config.stale_kill_threshold_seconds,
530
+ )
531
+ termination_reason = "stale_session"
532
+ recovery = recover_session_log(session_log, backup_log)
533
+ lines = session_log.read_text(encoding="utf-8", errors="ignore").splitlines()
534
+ summary = parse_stream_progress_lines(lines)
535
+ merged_progress: dict[str, object] = summary.to_dict()
536
+ if self.config.progress_path is not None:
537
+ existing_progress = {}
538
+ if self.config.progress_path.is_file():
539
+ try:
540
+ existing_progress = json.loads(self.config.progress_path.read_text(encoding="utf-8"))
541
+ except (OSError, json.JSONDecodeError):
542
+ existing_progress = {}
543
+ merged_progress = _merged_progress(existing_progress, summary)
544
+ self.config.progress_path.write_text(json.dumps(merged_progress, indent=2, sort_keys=True) + "\n", encoding="utf-8")
545
+ final_summary = _summary_from_progress_dict(merged_progress, summary)
546
+ ended = time.time()
547
+ return AISessionResult(
548
+ command=command,
549
+ exit_code=handle.popen.returncode,
550
+ timed_out=timed_out,
551
+ termination_reason=termination_reason,
552
+ stale_marker=stale_marker,
553
+ fatal_error_code=final_summary.fatal_error_code,
554
+ progress_summary=final_summary,
555
+ session_log=session_log,
556
+ backup_log=backup_log,
557
+ log_recovery=recovery,
558
+ started_epoch=started,
559
+ ended_epoch=ended,
560
+ )
@@ -0,0 +1,71 @@
1
+ """Status updater and progress summary contracts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ from dataclasses import dataclass, field
7
+ from pathlib import Path
8
+
9
+ from .paths import RuntimePaths
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class StatusTarget:
14
+ """Plan and state files touched by a status updater."""
15
+
16
+ kind: str
17
+ plan_path: Path
18
+ state_dir: Path
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class StatusUpdateRequest:
23
+ """A requested status updater action for behavior porting."""
24
+
25
+ target: StatusTarget
26
+ action: str
27
+ item_id: str | None = None
28
+ session_status: str | None = None
29
+ metadata: Mapping[str, str] = field(default_factory=dict)
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class ProgressSummary:
34
+ """Provider-neutral stream progress metadata used by session primitives."""
35
+
36
+ event_type_counts: Mapping[str, int] = field(default_factory=dict)
37
+ claude_session_id: str = ""
38
+ cb_session_id: str = ""
39
+ result_subtype: str = ""
40
+ stop_reason: str = ""
41
+ has_usage: bool = False
42
+ permission_denial_count: int = 0
43
+ permission_denials: tuple[str, ...] = ()
44
+ fatal_error_code: str = ""
45
+ terminal_result_text: str = ""
46
+ errors: tuple[str, ...] = ()
47
+
48
+ def to_dict(self) -> dict[str, object]:
49
+ """Return JSON-serializable progress metadata."""
50
+ return {
51
+ "event_type_counts": dict(self.event_type_counts),
52
+ "claude_session_id": self.claude_session_id,
53
+ "cb_session_id": self.cb_session_id,
54
+ "result_subtype": self.result_subtype,
55
+ "stop_reason": self.stop_reason,
56
+ "has_usage": self.has_usage,
57
+ "permission_denial_count": self.permission_denial_count,
58
+ "permission_denials": list(self.permission_denials),
59
+ "fatal_error_code": self.fatal_error_code,
60
+ "terminal_result_text": self.terminal_result_text,
61
+ "errors": list(self.errors),
62
+ }
63
+
64
+
65
+ def default_status_targets(paths: RuntimePaths) -> dict[str, StatusTarget]:
66
+ """Return the canonical status targets for all pipeline families."""
67
+ return {
68
+ "feature": StatusTarget("feature", paths.feature_plan, paths.feature_state_dir),
69
+ "bugfix": StatusTarget("bugfix", paths.bugfix_plan, paths.bugfix_state_dir),
70
+ "refactor": StatusTarget("refactor", paths.refactor_plan, paths.refactor_state_dir),
71
+ }