prizmkit 1.1.99 → 1.1.101

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