@opengsd/gsd-path 1.0.0 → 1.2.0

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 (498) hide show
  1. package/AGENTS.md +33 -13
  2. package/CHANGELOG.md +68 -0
  3. package/DOCS.md +75 -6
  4. package/FULL.md +12 -15
  5. package/HOOKS.md +18 -9
  6. package/QUICK.md +8 -6
  7. package/README.md +218 -14
  8. package/RUNTIME.md +16 -5
  9. package/UPDATE.md +30 -11
  10. package/WORKFLOW.md +35 -16
  11. package/package.json +23 -1
  12. package/platforms/claude/dispatch.md +4 -4
  13. package/platforms/copilot/dispatch.md +4 -4
  14. package/platforms/cursor/dispatch.md +5 -10
  15. package/platforms/grok/dispatch.md +4 -6
  16. package/platforms/kimi/dispatch.md +4 -4
  17. package/platforms/kiro/dispatch.md +4 -6
  18. package/platforms/opencode/dispatch.md +4 -4
  19. package/platforms/qwen/dispatch.md +4 -4
  20. package/platforms/shared-agents/dispatch.md +12 -19
  21. package/scripts/_common.py +5 -0
  22. package/scripts/archive_milestone.py +30 -124
  23. package/scripts/bootstrap_repository.py +33 -4
  24. package/scripts/build_recovery.py +292 -0
  25. package/scripts/build_state.py +2 -0
  26. package/scripts/check_docs_audit.py +7 -3
  27. package/scripts/check_handoffs.py +7 -1
  28. package/scripts/check_task_briefs.py +3 -0
  29. package/scripts/detect_project.py +22 -3
  30. package/scripts/discussion_records.py +3 -0
  31. package/scripts/discussion_validate.py +8 -3
  32. package/scripts/dispatch_driver.py +233 -22
  33. package/scripts/git_guard.py +14 -5
  34. package/scripts/guard_hook.py +11 -1
  35. package/scripts/install.mjs +54 -1448
  36. package/scripts/install.py +155 -188
  37. package/scripts/integration.py +54 -28
  38. package/scripts/isolation.py +24 -6
  39. package/scripts/jev_review.py +138 -0
  40. package/scripts/lean_verification.py +3 -1
  41. package/scripts/loop_run.py +2 -0
  42. package/scripts/model_policy.py +279 -0
  43. package/scripts/path_config.py +248 -0
  44. package/scripts/pipeline_diagnose.py +15 -3
  45. package/scripts/pipeline_git.py +3 -0
  46. package/scripts/pipeline_state.py +117 -186
  47. package/scripts/pipeline_undo.py +14 -7
  48. package/scripts/promote_lookahead.py +33 -162
  49. package/scripts/review_panel.py +11 -0
  50. package/scripts/roadmap.py +326 -0
  51. package/scripts/runtime_store.py +278 -0
  52. package/scripts/skill-resources.json +182 -4
  53. package/scripts/state_checkpoint.py +114 -72
  54. package/scripts/state_promote.py +47 -44
  55. package/scripts/status_runtime.py +92 -6
  56. package/scripts/task_context.py +73 -0
  57. package/scripts/token_budget.py +40 -1
  58. package/scripts/workflow_run.py +128 -8
  59. package/scripts/worktree_paths.py +94 -0
  60. package/skills/gsd-path/BUILD.md +54 -575
  61. package/skills/gsd-path/DECIDE.md +4 -6
  62. package/skills/gsd-path/DEFINE.md +23 -21
  63. package/skills/gsd-path/DOCS-AUDIT.md +2 -0
  64. package/skills/gsd-path/INSPECT.md +32 -8
  65. package/skills/gsd-path/PLAN.md +29 -19
  66. package/skills/gsd-path/RESEARCH.md +5 -6
  67. package/skills/gsd-path/ROADMAP.md +24 -58
  68. package/skills/gsd-path/SHIP.md +29 -17
  69. package/skills/gsd-path/SKILL.md +46 -22
  70. package/skills/gsd-path/references/build-abandon.md +33 -0
  71. package/skills/gsd-path/references/build-native.md +502 -0
  72. package/skills/gsd-path/references/build-recovery.md +78 -0
  73. package/skills/gsd-path/references/coder.md +8 -3
  74. package/skills/gsd-path/references/config.md +57 -0
  75. package/skills/gsd-path/references/dispatch.md +12 -19
  76. package/skills/gsd-path/references/docs-auditor.md +14 -0
  77. package/skills/gsd-path/references/jev-review.md +82 -0
  78. package/skills/gsd-path/references/model-policy.md +139 -0
  79. package/skills/gsd-path/references/reviewer-final.md +21 -0
  80. package/skills/gsd-path/references/reviewer-panel.md +17 -0
  81. package/skills/gsd-path/references/reviewer-skeptic.md +20 -0
  82. package/skills/gsd-path/references/reviewer-wave.md +89 -0
  83. package/skills/gsd-path/references/reviewer.md +12 -148
  84. package/skills/gsd-path/references/runtime-selection.md +25 -0
  85. package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  86. package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  87. package/skills/gsd-path/scripts/_common.py +5 -0
  88. package/skills/gsd-path/scripts/archive_milestone.py +30 -124
  89. package/skills/gsd-path/scripts/bootstrap_repository.py +33 -4
  90. package/skills/gsd-path/scripts/build_recovery.py +292 -0
  91. package/skills/gsd-path/scripts/build_state.py +2 -0
  92. package/skills/gsd-path/scripts/check_docs_audit.py +7 -3
  93. package/skills/gsd-path/scripts/check_handoffs.py +7 -1
  94. package/skills/gsd-path/scripts/check_task_briefs.py +3 -0
  95. package/skills/gsd-path/scripts/detect_project.py +22 -3
  96. package/skills/gsd-path/scripts/discussion_records.py +3 -0
  97. package/skills/gsd-path/scripts/discussion_validate.py +8 -3
  98. package/skills/gsd-path/scripts/dispatch_driver.py +233 -22
  99. package/skills/gsd-path/scripts/git_guard.py +768 -0
  100. package/skills/gsd-path/scripts/integration.py +54 -28
  101. package/skills/gsd-path/scripts/isolation.py +24 -6
  102. package/skills/gsd-path/scripts/jev_review.py +138 -0
  103. package/skills/gsd-path/scripts/lean_verification.py +3 -1
  104. package/skills/gsd-path/scripts/loop_run.py +2 -0
  105. package/skills/gsd-path/scripts/model_policy.py +279 -0
  106. package/skills/gsd-path/scripts/path_config.py +248 -0
  107. package/skills/gsd-path/scripts/pipeline_git.py +3 -0
  108. package/skills/gsd-path/scripts/pipeline_state.py +117 -186
  109. package/skills/gsd-path/scripts/promote_lookahead.py +33 -162
  110. package/skills/gsd-path/scripts/review_panel.py +11 -0
  111. package/skills/gsd-path/scripts/roadmap.py +326 -0
  112. package/skills/gsd-path/scripts/state_checkpoint.py +114 -72
  113. package/skills/gsd-path/scripts/state_promote.py +47 -44
  114. package/skills/gsd-path/scripts/status_runtime.py +208 -0
  115. package/skills/gsd-path/scripts/task_context.py +73 -0
  116. package/skills/gsd-path/scripts/token_budget.py +40 -1
  117. package/skills/gsd-path/scripts/workflow_run.py +128 -8
  118. package/skills/gsd-path/scripts/worktree_paths.py +94 -0
  119. package/skills/gsd-path/templates/skeptic.md +1 -1
  120. package/skills/gsd-path/templates/task.md +2 -0
  121. package/skills/gsd-path/templates/wave-panel.md +1 -1
  122. package/skills/gsd-path/templates/wave-review.md +1 -1
  123. package/skills/gsd-path-build/SKILL.md +54 -575
  124. package/skills/gsd-path-build/references/build-abandon.md +33 -0
  125. package/skills/gsd-path-build/references/build-native.md +502 -0
  126. package/skills/gsd-path-build/references/build-recovery.md +78 -0
  127. package/skills/gsd-path-build/references/coder.md +8 -3
  128. package/skills/gsd-path-build/references/dispatch.md +12 -19
  129. package/skills/gsd-path-build/references/jev-review.md +82 -0
  130. package/skills/gsd-path-build/references/model-policy.md +139 -0
  131. package/skills/gsd-path-build/references/reviewer-final.md +21 -0
  132. package/skills/gsd-path-build/references/reviewer-panel.md +17 -0
  133. package/skills/gsd-path-build/references/reviewer-skeptic.md +20 -0
  134. package/skills/gsd-path-build/references/reviewer-wave.md +89 -0
  135. package/skills/gsd-path-build/references/reviewer.md +12 -148
  136. package/skills/gsd-path-build/references/runtime-selection.md +25 -0
  137. package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  138. package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  139. package/skills/gsd-path-build/scripts/_common.py +5 -0
  140. package/skills/gsd-path-build/scripts/archive_milestone.py +30 -124
  141. package/skills/gsd-path-build/scripts/build_recovery.py +292 -0
  142. package/skills/gsd-path-build/scripts/build_state.py +2 -0
  143. package/skills/gsd-path-build/scripts/check_handoffs.py +7 -1
  144. package/skills/gsd-path-build/scripts/check_task_briefs.py +3 -0
  145. package/skills/gsd-path-build/scripts/detect_project.py +1536 -0
  146. package/skills/gsd-path-build/scripts/discussion_records.py +3 -0
  147. package/skills/gsd-path-build/scripts/discussion_validate.py +8 -3
  148. package/skills/gsd-path-build/scripts/dispatch_driver.py +233 -22
  149. package/skills/gsd-path-build/scripts/integration.py +54 -28
  150. package/skills/gsd-path-build/scripts/isolation.py +24 -6
  151. package/skills/gsd-path-build/scripts/jev_review.py +138 -0
  152. package/skills/gsd-path-build/scripts/loop_run.py +2 -0
  153. package/skills/gsd-path-build/scripts/model_policy.py +279 -0
  154. package/skills/gsd-path-build/scripts/path_config.py +248 -0
  155. package/skills/gsd-path-build/scripts/pipeline_git.py +3 -0
  156. package/skills/gsd-path-build/scripts/pipeline_state.py +117 -186
  157. package/skills/gsd-path-build/scripts/promote_lookahead.py +286 -0
  158. package/skills/gsd-path-build/scripts/review_panel.py +11 -0
  159. package/skills/gsd-path-build/scripts/roadmap.py +326 -0
  160. package/skills/gsd-path-build/scripts/state_checkpoint.py +114 -72
  161. package/skills/gsd-path-build/scripts/state_promote.py +47 -44
  162. package/skills/gsd-path-build/scripts/task_context.py +73 -0
  163. package/skills/gsd-path-build/scripts/token_budget.py +40 -1
  164. package/skills/gsd-path-build/scripts/workflow_run.py +128 -8
  165. package/skills/gsd-path-build/scripts/worktree_paths.py +94 -0
  166. package/skills/gsd-path-build/templates/skeptic.md +1 -1
  167. package/skills/gsd-path-build/templates/task.md +2 -0
  168. package/skills/gsd-path-build/templates/wave-panel.md +1 -1
  169. package/skills/gsd-path-build/templates/wave-review.md +1 -1
  170. package/skills/gsd-path-decide/SKILL.md +4 -6
  171. package/skills/gsd-path-decide/references/dispatch.md +12 -19
  172. package/skills/gsd-path-decide/references/model-policy.md +139 -0
  173. package/skills/gsd-path-decide/references/runtime-selection.md +25 -0
  174. package/skills/gsd-path-decide/scripts/_common.py +5 -0
  175. package/skills/gsd-path-decide/scripts/archive_milestone.py +2171 -0
  176. package/skills/gsd-path-decide/scripts/build_recovery.py +292 -0
  177. package/skills/gsd-path-decide/scripts/check_handoffs.py +7 -1
  178. package/skills/gsd-path-decide/scripts/check_task_briefs.py +3 -0
  179. package/skills/gsd-path-decide/scripts/detect_project.py +1536 -0
  180. package/skills/gsd-path-decide/scripts/discussion_records.py +3 -0
  181. package/skills/gsd-path-decide/scripts/discussion_validate.py +1326 -0
  182. package/skills/gsd-path-decide/scripts/integration.py +1459 -0
  183. package/skills/gsd-path-decide/scripts/isolation.py +24 -6
  184. package/skills/gsd-path-decide/scripts/loop_run.py +839 -0
  185. package/skills/gsd-path-decide/scripts/model_policy.py +279 -0
  186. package/skills/gsd-path-decide/scripts/path_config.py +248 -0
  187. package/skills/gsd-path-decide/scripts/pipeline_git.py +3 -0
  188. package/skills/gsd-path-decide/scripts/pipeline_state.py +117 -186
  189. package/skills/gsd-path-decide/scripts/promote_lookahead.py +286 -0
  190. package/skills/gsd-path-decide/scripts/review_panel.py +564 -0
  191. package/skills/gsd-path-decide/scripts/roadmap.py +326 -0
  192. package/skills/gsd-path-decide/scripts/state_checkpoint.py +114 -72
  193. package/skills/gsd-path-decide/scripts/state_promote.py +47 -44
  194. package/skills/gsd-path-decide/scripts/worktree_paths.py +94 -0
  195. package/skills/gsd-path-define/SKILL.md +23 -21
  196. package/skills/gsd-path-define/references/build-recovery.md +78 -0
  197. package/skills/gsd-path-define/references/runtime-selection.md +25 -0
  198. package/skills/gsd-path-define/scripts/_common.py +5 -0
  199. package/skills/gsd-path-define/scripts/archive_milestone.py +2171 -0
  200. package/skills/gsd-path-define/scripts/build_recovery.py +292 -0
  201. package/skills/gsd-path-define/scripts/check_handoffs.py +7 -1
  202. package/skills/gsd-path-define/scripts/check_task_briefs.py +3 -0
  203. package/skills/gsd-path-define/scripts/detect_project.py +22 -3
  204. package/skills/gsd-path-define/scripts/discussion_records.py +3 -0
  205. package/skills/gsd-path-define/scripts/discussion_validate.py +1326 -0
  206. package/skills/gsd-path-define/scripts/integration.py +1459 -0
  207. package/skills/gsd-path-define/scripts/isolation.py +24 -6
  208. package/skills/gsd-path-define/scripts/loop_run.py +839 -0
  209. package/skills/gsd-path-define/scripts/model_policy.py +279 -0
  210. package/skills/gsd-path-define/scripts/path_config.py +248 -0
  211. package/skills/gsd-path-define/scripts/pipeline_git.py +3 -0
  212. package/skills/gsd-path-define/scripts/pipeline_state.py +117 -186
  213. package/skills/gsd-path-define/scripts/promote_lookahead.py +286 -0
  214. package/skills/gsd-path-define/scripts/review_panel.py +564 -0
  215. package/skills/gsd-path-define/scripts/roadmap.py +326 -0
  216. package/skills/gsd-path-define/scripts/state_checkpoint.py +114 -72
  217. package/skills/gsd-path-define/scripts/state_promote.py +47 -44
  218. package/skills/gsd-path-define/scripts/worktree_paths.py +94 -0
  219. package/skills/gsd-path-discuss/SKILL.md +2 -0
  220. package/skills/gsd-path-discuss/references/runtime-selection.md +25 -0
  221. package/skills/gsd-path-discuss/scripts/_common.py +5 -0
  222. package/skills/gsd-path-discuss/scripts/discussion_records.py +3 -0
  223. package/skills/gsd-path-discuss/scripts/loop_run.py +839 -0
  224. package/skills/gsd-path-discuss/scripts/model_policy.py +279 -0
  225. package/skills/gsd-path-discuss/scripts/path_config.py +248 -0
  226. package/skills/gsd-path-discuss/scripts/review_panel.py +564 -0
  227. package/skills/gsd-path-discuss/scripts/worktree_paths.py +94 -0
  228. package/skills/gsd-path-docs-audit/SKILL.md +2 -0
  229. package/skills/gsd-path-docs-audit/references/dispatch.md +12 -19
  230. package/skills/gsd-path-docs-audit/references/docs-auditor.md +14 -0
  231. package/skills/gsd-path-docs-audit/references/model-policy.md +139 -0
  232. package/skills/gsd-path-docs-audit/references/runtime-selection.md +25 -0
  233. package/skills/gsd-path-docs-audit/scripts/_common.py +5 -0
  234. package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +2171 -0
  235. package/skills/gsd-path-docs-audit/scripts/build_recovery.py +292 -0
  236. package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +7 -3
  237. package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +7 -1
  238. package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +3 -0
  239. package/skills/gsd-path-docs-audit/scripts/detect_project.py +1536 -0
  240. package/skills/gsd-path-docs-audit/scripts/discussion_records.py +3 -0
  241. package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +1326 -0
  242. package/skills/gsd-path-docs-audit/scripts/integration.py +1459 -0
  243. package/skills/gsd-path-docs-audit/scripts/isolation.py +24 -6
  244. package/skills/gsd-path-docs-audit/scripts/loop_run.py +839 -0
  245. package/skills/gsd-path-docs-audit/scripts/model_policy.py +279 -0
  246. package/skills/gsd-path-docs-audit/scripts/path_config.py +248 -0
  247. package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +3 -0
  248. package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +117 -186
  249. package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +286 -0
  250. package/skills/gsd-path-docs-audit/scripts/review_panel.py +564 -0
  251. package/skills/gsd-path-docs-audit/scripts/roadmap.py +326 -0
  252. package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +114 -72
  253. package/skills/gsd-path-docs-audit/scripts/state_promote.py +47 -44
  254. package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +94 -0
  255. package/skills/gsd-path-forensics/SKILL.md +9 -5
  256. package/skills/gsd-path-forensics/references/runtime-selection.md +25 -0
  257. package/skills/gsd-path-forensics/scripts/_common.py +5 -0
  258. package/skills/gsd-path-forensics/scripts/discussion_records.py +3 -0
  259. package/skills/gsd-path-forensics/scripts/loop_run.py +839 -0
  260. package/skills/gsd-path-forensics/scripts/model_policy.py +279 -0
  261. package/skills/gsd-path-forensics/scripts/path_config.py +248 -0
  262. package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +15 -3
  263. package/skills/gsd-path-forensics/scripts/pipeline_undo.py +14 -7
  264. package/skills/gsd-path-forensics/scripts/review_panel.py +564 -0
  265. package/skills/gsd-path-forensics/scripts/worktree_paths.py +94 -0
  266. package/skills/gsd-path-inspect/SKILL.md +32 -8
  267. package/skills/gsd-path-inspect/references/dispatch.md +12 -19
  268. package/skills/gsd-path-inspect/references/docs-auditor.md +14 -0
  269. package/skills/gsd-path-inspect/references/model-policy.md +139 -0
  270. package/skills/gsd-path-inspect/references/runtime-selection.md +25 -0
  271. package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  272. package/skills/gsd-path-inspect/scripts/_common.py +5 -0
  273. package/skills/gsd-path-inspect/scripts/archive_milestone.py +2171 -0
  274. package/skills/gsd-path-inspect/scripts/build_recovery.py +292 -0
  275. package/skills/gsd-path-inspect/scripts/check_docs_audit.py +7 -3
  276. package/skills/gsd-path-inspect/scripts/check_handoffs.py +7 -1
  277. package/skills/gsd-path-inspect/scripts/check_task_briefs.py +3 -0
  278. package/skills/gsd-path-inspect/scripts/detect_project.py +22 -3
  279. package/skills/gsd-path-inspect/scripts/discussion_records.py +3 -0
  280. package/skills/gsd-path-inspect/scripts/discussion_validate.py +1326 -0
  281. package/skills/gsd-path-inspect/scripts/integration.py +1459 -0
  282. package/skills/gsd-path-inspect/scripts/isolation.py +24 -6
  283. package/skills/gsd-path-inspect/scripts/loop_run.py +839 -0
  284. package/skills/gsd-path-inspect/scripts/model_policy.py +279 -0
  285. package/skills/gsd-path-inspect/scripts/path_config.py +248 -0
  286. package/skills/gsd-path-inspect/scripts/pipeline_git.py +3 -0
  287. package/skills/gsd-path-inspect/scripts/pipeline_state.py +117 -186
  288. package/skills/gsd-path-inspect/scripts/promote_lookahead.py +286 -0
  289. package/skills/gsd-path-inspect/scripts/review_panel.py +564 -0
  290. package/skills/gsd-path-inspect/scripts/roadmap.py +326 -0
  291. package/skills/gsd-path-inspect/scripts/state_checkpoint.py +114 -72
  292. package/skills/gsd-path-inspect/scripts/state_promote.py +47 -44
  293. package/skills/gsd-path-inspect/scripts/workflow_run.py +237 -0
  294. package/skills/gsd-path-inspect/scripts/worktree_paths.py +94 -0
  295. package/skills/gsd-path-loop/SKILL.md +2 -0
  296. package/skills/gsd-path-loop/references/runtime-selection.md +25 -0
  297. package/skills/gsd-path-loop/scripts/_common.py +5 -0
  298. package/skills/gsd-path-loop/scripts/discussion_records.py +3 -0
  299. package/skills/gsd-path-loop/scripts/loop_run.py +2 -0
  300. package/skills/gsd-path-loop/scripts/model_policy.py +279 -0
  301. package/skills/gsd-path-loop/scripts/path_config.py +248 -0
  302. package/skills/gsd-path-loop/scripts/review_panel.py +564 -0
  303. package/skills/gsd-path-loop/scripts/worktree_paths.py +94 -0
  304. package/skills/gsd-path-migrate/SKILL.md +2 -0
  305. package/skills/gsd-path-migrate/references/runtime-selection.md +25 -0
  306. package/skills/gsd-path-migrate/scripts/_common.py +185 -0
  307. package/skills/gsd-path-migrate/scripts/discussion_records.py +3 -0
  308. package/skills/gsd-path-migrate/scripts/loop_run.py +839 -0
  309. package/skills/gsd-path-migrate/scripts/model_policy.py +279 -0
  310. package/skills/gsd-path-migrate/scripts/path_config.py +248 -0
  311. package/skills/gsd-path-migrate/scripts/review_panel.py +564 -0
  312. package/skills/gsd-path-plan/SKILL.md +29 -19
  313. package/skills/gsd-path-plan/references/build-recovery.md +78 -0
  314. package/skills/gsd-path-plan/references/dispatch.md +12 -19
  315. package/skills/gsd-path-plan/references/model-policy.md +139 -0
  316. package/skills/gsd-path-plan/references/runtime-selection.md +25 -0
  317. package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  318. package/skills/gsd-path-plan/scripts/_common.py +5 -0
  319. package/skills/gsd-path-plan/scripts/archive_milestone.py +2171 -0
  320. package/skills/gsd-path-plan/scripts/build_recovery.py +292 -0
  321. package/skills/gsd-path-plan/scripts/check_handoffs.py +7 -1
  322. package/skills/gsd-path-plan/scripts/check_task_briefs.py +3 -0
  323. package/skills/gsd-path-plan/scripts/detect_project.py +1536 -0
  324. package/skills/gsd-path-plan/scripts/discussion_records.py +3 -0
  325. package/skills/gsd-path-plan/scripts/discussion_validate.py +1326 -0
  326. package/skills/gsd-path-plan/scripts/integration.py +1459 -0
  327. package/skills/gsd-path-plan/scripts/isolation.py +24 -6
  328. package/skills/gsd-path-plan/scripts/loop_run.py +839 -0
  329. package/skills/gsd-path-plan/scripts/model_policy.py +279 -0
  330. package/skills/gsd-path-plan/scripts/path_config.py +248 -0
  331. package/skills/gsd-path-plan/scripts/pipeline_git.py +3 -0
  332. package/skills/gsd-path-plan/scripts/pipeline_state.py +117 -186
  333. package/skills/gsd-path-plan/scripts/promote_lookahead.py +286 -0
  334. package/skills/gsd-path-plan/scripts/review_panel.py +11 -0
  335. package/skills/gsd-path-plan/scripts/roadmap.py +326 -0
  336. package/skills/gsd-path-plan/scripts/state_checkpoint.py +114 -72
  337. package/skills/gsd-path-plan/scripts/state_promote.py +47 -44
  338. package/skills/gsd-path-plan/scripts/workflow_run.py +128 -8
  339. package/skills/gsd-path-plan/scripts/worktree_paths.py +94 -0
  340. package/skills/gsd-path-plan/templates/task.md +2 -0
  341. package/skills/gsd-path-research/SKILL.md +5 -6
  342. package/skills/gsd-path-research/references/dispatch.md +12 -19
  343. package/skills/gsd-path-research/references/model-policy.md +139 -0
  344. package/skills/gsd-path-research/references/runtime-selection.md +25 -0
  345. package/skills/gsd-path-research/scripts/_common.py +5 -0
  346. package/skills/gsd-path-research/scripts/archive_milestone.py +2171 -0
  347. package/skills/gsd-path-research/scripts/build_recovery.py +292 -0
  348. package/skills/gsd-path-research/scripts/check_handoffs.py +7 -1
  349. package/skills/gsd-path-research/scripts/check_task_briefs.py +3 -0
  350. package/skills/gsd-path-research/scripts/detect_project.py +1536 -0
  351. package/skills/gsd-path-research/scripts/discussion_records.py +3 -0
  352. package/skills/gsd-path-research/scripts/discussion_validate.py +1326 -0
  353. package/skills/gsd-path-research/scripts/integration.py +1459 -0
  354. package/skills/gsd-path-research/scripts/isolation.py +24 -6
  355. package/skills/gsd-path-research/scripts/loop_run.py +839 -0
  356. package/skills/gsd-path-research/scripts/model_policy.py +279 -0
  357. package/skills/gsd-path-research/scripts/path_config.py +248 -0
  358. package/skills/gsd-path-research/scripts/pipeline_git.py +3 -0
  359. package/skills/gsd-path-research/scripts/pipeline_state.py +117 -186
  360. package/skills/gsd-path-research/scripts/promote_lookahead.py +286 -0
  361. package/skills/gsd-path-research/scripts/review_panel.py +564 -0
  362. package/skills/gsd-path-research/scripts/roadmap.py +326 -0
  363. package/skills/gsd-path-research/scripts/state_checkpoint.py +114 -72
  364. package/skills/gsd-path-research/scripts/state_promote.py +47 -44
  365. package/skills/gsd-path-research/scripts/worktree_paths.py +94 -0
  366. package/skills/gsd-path-roadmap/SKILL.md +24 -58
  367. package/skills/gsd-path-roadmap/references/dispatch.md +12 -19
  368. package/skills/gsd-path-roadmap/references/model-policy.md +139 -0
  369. package/skills/gsd-path-roadmap/references/runtime-selection.md +25 -0
  370. package/skills/gsd-path-roadmap/scripts/_common.py +5 -0
  371. package/skills/gsd-path-roadmap/scripts/archive_milestone.py +30 -124
  372. package/skills/gsd-path-roadmap/scripts/build_recovery.py +292 -0
  373. package/skills/gsd-path-roadmap/scripts/check_handoffs.py +7 -1
  374. package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +3 -0
  375. package/skills/gsd-path-roadmap/scripts/detect_project.py +22 -3
  376. package/skills/gsd-path-roadmap/scripts/discussion_records.py +3 -0
  377. package/skills/gsd-path-roadmap/scripts/discussion_validate.py +8 -3
  378. package/skills/gsd-path-roadmap/scripts/integration.py +54 -28
  379. package/skills/gsd-path-roadmap/scripts/isolation.py +24 -6
  380. package/skills/gsd-path-roadmap/scripts/loop_run.py +839 -0
  381. package/skills/gsd-path-roadmap/scripts/model_policy.py +279 -0
  382. package/skills/gsd-path-roadmap/scripts/path_config.py +248 -0
  383. package/skills/gsd-path-roadmap/scripts/pipeline_git.py +3 -0
  384. package/skills/gsd-path-roadmap/scripts/pipeline_state.py +117 -186
  385. package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +33 -162
  386. package/skills/gsd-path-roadmap/scripts/review_panel.py +11 -0
  387. package/skills/gsd-path-roadmap/scripts/roadmap.py +326 -0
  388. package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +114 -72
  389. package/skills/gsd-path-roadmap/scripts/state_promote.py +47 -44
  390. package/skills/gsd-path-roadmap/scripts/worktree_paths.py +94 -0
  391. package/skills/gsd-path-ship/SKILL.md +29 -17
  392. package/skills/gsd-path-ship/references/dispatch.md +12 -19
  393. package/skills/gsd-path-ship/references/jev-review.md +82 -0
  394. package/skills/gsd-path-ship/references/model-policy.md +139 -0
  395. package/skills/gsd-path-ship/references/reviewer-final.md +21 -0
  396. package/skills/gsd-path-ship/references/reviewer-panel.md +17 -0
  397. package/skills/gsd-path-ship/references/reviewer-skeptic.md +20 -0
  398. package/skills/gsd-path-ship/references/reviewer-wave.md +89 -0
  399. package/skills/gsd-path-ship/references/reviewer.md +12 -148
  400. package/skills/gsd-path-ship/references/runtime-selection.md +25 -0
  401. package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  402. package/skills/gsd-path-ship/scripts/_common.py +5 -0
  403. package/skills/gsd-path-ship/scripts/archive_milestone.py +30 -124
  404. package/skills/gsd-path-ship/scripts/build_recovery.py +292 -0
  405. package/skills/gsd-path-ship/scripts/build_state.py +2 -0
  406. package/skills/gsd-path-ship/scripts/check_handoffs.py +7 -1
  407. package/skills/gsd-path-ship/scripts/check_task_briefs.py +3 -0
  408. package/skills/gsd-path-ship/scripts/detect_project.py +1536 -0
  409. package/skills/gsd-path-ship/scripts/discussion_records.py +3 -0
  410. package/skills/gsd-path-ship/scripts/discussion_validate.py +8 -3
  411. package/skills/gsd-path-ship/scripts/integration.py +54 -28
  412. package/skills/gsd-path-ship/scripts/isolation.py +24 -6
  413. package/skills/gsd-path-ship/scripts/jev_review.py +138 -0
  414. package/skills/gsd-path-ship/scripts/lean_verification.py +3 -1
  415. package/skills/gsd-path-ship/scripts/loop_run.py +839 -0
  416. package/skills/gsd-path-ship/scripts/model_policy.py +279 -0
  417. package/skills/gsd-path-ship/scripts/path_config.py +248 -0
  418. package/skills/gsd-path-ship/scripts/pipeline_git.py +3 -0
  419. package/skills/gsd-path-ship/scripts/pipeline_state.py +117 -186
  420. package/skills/gsd-path-ship/scripts/promote_lookahead.py +286 -0
  421. package/skills/gsd-path-ship/scripts/review_panel.py +11 -0
  422. package/skills/gsd-path-ship/scripts/roadmap.py +326 -0
  423. package/skills/gsd-path-ship/scripts/state_checkpoint.py +114 -72
  424. package/skills/gsd-path-ship/scripts/state_promote.py +47 -44
  425. package/skills/gsd-path-ship/scripts/workflow_run.py +128 -8
  426. package/skills/gsd-path-ship/scripts/worktree_paths.py +94 -0
  427. package/skills/gsd-path-ship/templates/wave-review.md +1 -1
  428. package/skills/gsd-path-undo/SKILL.md +2 -0
  429. package/skills/gsd-path-undo/references/runtime-selection.md +25 -0
  430. package/skills/gsd-path-undo/scripts/_common.py +5 -0
  431. package/skills/gsd-path-undo/scripts/discussion_records.py +3 -0
  432. package/skills/gsd-path-undo/scripts/loop_run.py +839 -0
  433. package/skills/gsd-path-undo/scripts/model_policy.py +279 -0
  434. package/skills/gsd-path-undo/scripts/path_config.py +248 -0
  435. package/skills/gsd-path-undo/scripts/pipeline_undo.py +14 -7
  436. package/skills/gsd-path-undo/scripts/review_panel.py +564 -0
  437. package/skills/gsd-path-undo/scripts/worktree_paths.py +94 -0
  438. package/skills/path/BUILD.md +54 -575
  439. package/skills/path/DECIDE.md +4 -6
  440. package/skills/path/DEFINE.md +23 -21
  441. package/skills/path/DOCS-AUDIT.md +2 -0
  442. package/skills/path/INSPECT.md +32 -8
  443. package/skills/path/PLAN.md +29 -19
  444. package/skills/path/RESEARCH.md +5 -6
  445. package/skills/path/ROADMAP.md +24 -58
  446. package/skills/path/SHIP.md +29 -17
  447. package/skills/path/SKILL.md +46 -22
  448. package/skills/path/references/build-abandon.md +33 -0
  449. package/skills/path/references/build-native.md +502 -0
  450. package/skills/path/references/build-recovery.md +78 -0
  451. package/skills/path/references/coder.md +8 -3
  452. package/skills/path/references/config.md +57 -0
  453. package/skills/path/references/dispatch.md +12 -19
  454. package/skills/path/references/docs-auditor.md +14 -0
  455. package/skills/path/references/jev-review.md +82 -0
  456. package/skills/path/references/model-policy.md +139 -0
  457. package/skills/path/references/reviewer-final.md +21 -0
  458. package/skills/path/references/reviewer-panel.md +17 -0
  459. package/skills/path/references/reviewer-skeptic.md +20 -0
  460. package/skills/path/references/reviewer-wave.md +89 -0
  461. package/skills/path/references/reviewer.md +12 -148
  462. package/skills/path/references/runtime-selection.md +25 -0
  463. package/skills/path/scripts/_common.py +5 -0
  464. package/skills/path/scripts/archive_milestone.py +30 -124
  465. package/skills/path/scripts/bootstrap_repository.py +33 -4
  466. package/skills/path/scripts/build_recovery.py +292 -0
  467. package/skills/path/scripts/build_state.py +2 -0
  468. package/skills/path/scripts/check_docs_audit.py +7 -3
  469. package/skills/path/scripts/check_handoffs.py +7 -1
  470. package/skills/path/scripts/check_task_briefs.py +3 -0
  471. package/skills/path/scripts/detect_project.py +22 -3
  472. package/skills/path/scripts/discussion_records.py +3 -0
  473. package/skills/path/scripts/discussion_validate.py +8 -3
  474. package/skills/path/scripts/dispatch_driver.py +233 -22
  475. package/skills/path/scripts/git_guard.py +768 -0
  476. package/skills/path/scripts/integration.py +54 -28
  477. package/skills/path/scripts/isolation.py +24 -6
  478. package/skills/path/scripts/jev_review.py +138 -0
  479. package/skills/path/scripts/lean_verification.py +3 -1
  480. package/skills/path/scripts/loop_run.py +2 -0
  481. package/skills/path/scripts/model_policy.py +279 -0
  482. package/skills/path/scripts/path_config.py +248 -0
  483. package/skills/path/scripts/pipeline_git.py +3 -0
  484. package/skills/path/scripts/pipeline_state.py +117 -186
  485. package/skills/path/scripts/promote_lookahead.py +33 -162
  486. package/skills/path/scripts/review_panel.py +11 -0
  487. package/skills/path/scripts/roadmap.py +326 -0
  488. package/skills/path/scripts/state_checkpoint.py +114 -72
  489. package/skills/path/scripts/state_promote.py +47 -44
  490. package/skills/path/scripts/status_runtime.py +208 -0
  491. package/skills/path/scripts/task_context.py +73 -0
  492. package/skills/path/scripts/token_budget.py +40 -1
  493. package/skills/path/scripts/workflow_run.py +128 -8
  494. package/skills/path/scripts/worktree_paths.py +94 -0
  495. package/skills/path/templates/skeptic.md +1 -1
  496. package/skills/path/templates/task.md +2 -0
  497. package/skills/path/templates/wave-panel.md +1 -1
  498. package/skills/path/templates/wave-review.md +1 -1
@@ -3,6 +3,7 @@
3
3
 
4
4
  import argparse
5
5
  import errno
6
+ import hashlib
6
7
  import json
7
8
  import os
8
9
  import re
@@ -17,9 +18,11 @@ from typing import Callable, Iterable, List, Mapping, Optional, Sequence, Tuple,
17
18
  sys.dont_write_bytecode = True
18
19
 
19
20
  try:
20
- from . import sync_skill_resources
21
+ from . import sync_skill_resources, runtime_store, status_runtime
21
22
  except ImportError: # Direct execution from scripts/.
22
23
  import sync_skill_resources # type: ignore
24
+ import runtime_store
25
+ import status_runtime
23
26
 
24
27
 
25
28
  def skill_names_for_manifest(manifest: Mapping) -> Tuple[str, ...]:
@@ -46,25 +49,7 @@ CLAUDE_BRIDGE = "@../AGENTS.md\n@../WORKFLOW.md\n"
46
49
  HOOKS_DIRECTORY = ".gsd-path"
47
50
  GUARD_SCRIPTS = ("guard_hook.py", "git_guard.py")
48
51
  GUARD_MARKER = "gsd-path guard"
49
- PROJECT_RUNTIME_SCRIPTS = (
50
- "pipeline_state.py",
51
- "check_handoffs.py",
52
- "check_task_briefs.py",
53
- "isolation.py",
54
- "discussion_records.py",
55
- "pipeline_git.py",
56
- "promote_lookahead.py",
57
- "detect_project.py",
58
- "pipeline_diagnose.py",
59
- "pipeline_undo.py",
60
- "archive_milestone.py",
61
- "review_panel.py",
62
- "_common.py",
63
- "state_checkpoint.py",
64
- "state_promote.py",
65
- "discussion_validate.py",
66
- "integration.py",
67
- )
52
+ PROJECT_RUNTIME_SCRIPTS = runtime_store.RUNTIME_FILES
68
53
  PROJECT_RUNTIME_MARKER = "gsd-path project runtime"
69
54
  PROJECT_STATUS_LAUNCHER = "status_runtime.py"
70
55
  PROJECT_STATUS_MARKER = "gsd-path project status launcher"
@@ -886,10 +871,15 @@ def _create_install_lock(lock: Path) -> None:
886
871
  raise
887
872
 
888
873
 
874
+ def _install_lock_path(root: Path) -> Path:
875
+ identity = hashlib.sha256(os.fsencode(root.resolve())).hexdigest()
876
+ return Path.home() / ".gsd-path" / "install-locks" / identity / INSTALL_LOCK_NAME
877
+
878
+
889
879
  def _acquire_install_locks(roots: Iterable[Path]) -> Tuple[List[Path], List[Path]]:
890
880
  locks: List[Path] = []
891
881
  for root in roots:
892
- candidate = root.parent / INSTALL_LOCK_NAME
882
+ candidate = _install_lock_path(root)
893
883
  if not any(_same_path(candidate, existing) for existing in locks):
894
884
  locks.append(candidate)
895
885
  locks.sort(key=lambda candidate: os.path.normcase(os.fspath(candidate)))
@@ -1028,15 +1018,7 @@ def _project_destinations(
1028
1018
  False,
1029
1019
  ),
1030
1020
  ]
1031
- destinations.extend(
1032
- (
1033
- project / HOOKS_DIRECTORY / "runtime" / name,
1034
- f"scripts/{name}",
1035
- None,
1036
- False,
1037
- )
1038
- for name in PROJECT_RUNTIME_SCRIPTS
1039
- )
1021
+ destinations.append((project / HOOKS_DIRECTORY / "runtime.json", None, "", False))
1040
1022
  if "claude" in selected:
1041
1023
  destinations.append(
1042
1024
  (project / ".claude" / "CLAUDE.md", None, CLAUDE_BRIDGE, False)
@@ -1044,7 +1026,7 @@ def _project_destinations(
1044
1026
  if hooks:
1045
1027
  for name in GUARD_SCRIPTS:
1046
1028
  destinations.append(
1047
- (project / HOOKS_DIRECTORY / name, f"scripts/{name}", None, False)
1029
+ (project / HOOKS_DIRECTORY / name, None, runtime_store.guard_launcher(name), False)
1048
1030
  )
1049
1031
  if "claude" in selected:
1050
1032
  destinations.append(
@@ -1104,6 +1086,8 @@ def _update_replacement(
1104
1086
  the managed-file check for a replaceable destination, or None for a
1105
1087
  kept one."""
1106
1088
  parent = project / HOOKS_DIRECTORY
1089
+ if (parent / "runtime.json").is_file() and destination.parent == parent:
1090
+ return None # Stable wiring and selection change only through explicit operations.
1107
1091
  directory = destination.parent
1108
1092
  if _same_path(directory, parent / "runtime"):
1109
1093
  return _is_managed_project_runtime
@@ -1244,6 +1228,10 @@ def _validate_project(
1244
1228
  source = source_root / source_name
1245
1229
  if source.is_symlink() or not source.is_file():
1246
1230
  raise InstallerError(f"missing project contract: {source}")
1231
+ try:
1232
+ runtime_store.prepare(source_root, project, dry_run=True)
1233
+ except (OSError, ValueError) as error:
1234
+ raise InstallerError(str(error)) from error
1247
1235
  mergers = _native_settings_mergers(project, selected, hooks)
1248
1236
  for destination, _, _, _ in _project_destinations(
1249
1237
  project, selected, hooks, interpreter, hooks_dir
@@ -1278,11 +1266,17 @@ def _apply_project(
1278
1266
  hooks_dir: Optional[Path],
1279
1267
  update: bool = False,
1280
1268
  ) -> None:
1269
+ try:
1270
+ pin = runtime_store.prepare(source_root, project)
1271
+ except (OSError, ValueError) as error:
1272
+ raise InstallerError(str(error)) from error
1281
1273
  _create_directory(project, transaction.created_directories)
1282
1274
  mergers = _native_settings_mergers(project, selected, hooks)
1283
1275
  for destination, source_name, content, executable in _project_destinations(
1284
1276
  project, selected, hooks, interpreter, hooks_dir
1285
1277
  ):
1278
+ if destination.name == "runtime.json" and destination.parent == project / HOOKS_DIRECTORY:
1279
+ content = runtime_store.pin_text(pin)
1286
1280
  _create_directory(destination.parent, transaction.created_directories)
1287
1281
  merge = mergers.get(destination)
1288
1282
  if _lexists(destination) and not destination.is_symlink():
@@ -1298,7 +1292,7 @@ def _apply_project(
1298
1292
  elif source_name:
1299
1293
  replacement = (source_root / source_name).read_bytes()
1300
1294
  else:
1301
- replacement = content or ""
1295
+ replacement = (content or "").encode("utf-8")
1302
1296
  _atomic_write(
1303
1297
  destination, replacement, 0o755 if executable else mode
1304
1298
  )
@@ -1314,9 +1308,9 @@ def _apply_project(
1314
1308
  created = True
1315
1309
  shutil.copyfileobj(source, output)
1316
1310
  else:
1317
- with destination.open("x", encoding="utf-8") as output:
1311
+ with destination.open("xb") as output:
1318
1312
  created = True
1319
- output.write(content)
1313
+ output.write(content.encode("utf-8"))
1320
1314
  if executable:
1321
1315
  destination.chmod(0o755)
1322
1316
  transaction.copied.append(destination)
@@ -1640,7 +1634,7 @@ def _validate_hooks_refresh(
1640
1634
  project / HOOKS_DIRECTORY / "runtime", "project runtime directory"
1641
1635
  )
1642
1636
  refresh_guards = _refreshes_guards(project, full, initialize)
1643
- runtime_exists = any(
1637
+ runtime_exists = (project / HOOKS_DIRECTORY / "runtime.json").is_file() or any(
1644
1638
  _lexists(project / HOOKS_DIRECTORY / "runtime" / name)
1645
1639
  for name in PROJECT_RUNTIME_SCRIPTS
1646
1640
  )
@@ -1731,162 +1725,63 @@ def _validate_hooks_refresh(
1731
1725
  )
1732
1726
 
1733
1727
 
1734
- def _refresh_hooks_unlocked(
1735
- source_root: Path,
1736
- project: Path,
1737
- full: bool,
1738
- dry_run: bool = False,
1739
- selected: Sequence[str] = (),
1740
- initialize: bool = False,
1741
- ) -> List[str]:
1742
- """Refreshed project-relative paths; "note:"-prefixed entries are
1743
- user-facing notes rather than refreshed files."""
1744
- hooks_dir = _git_hooks_directory(project) if full else None
1745
- interpreter: Optional[str] = None
1746
- if not dry_run:
1747
- if full:
1748
- interpreter, hooks_dir = _required_hook_runtime(
1749
- project,
1750
- "--hooks-init" if initialize else "--hooks-refresh-full",
1751
- selected,
1752
- )
1753
- else:
1754
- interpreter = _required_python_runtime("--hooks-refresh", selected)
1755
- _validate_hooks_refresh(
1756
- source_root, project, full, hooks_dir, selected, initialize
1757
- )
1758
- refreshed: List[str] = []
1759
- refresh_guards = _refreshes_guards(project, full, initialize)
1760
- runtime = project / HOOKS_DIRECTORY / "runtime"
1761
- if not dry_run:
1762
- runtime.parent.mkdir(parents=True, exist_ok=True)
1763
- staging = Path(tempfile.mkdtemp(prefix=".runtime-stage-", dir=runtime.parent))
1764
- previous = staging.with_name(staging.name + "-previous")
1765
- moved_previous = False
1766
- published_runtime = False
1767
- launcher = runtime.parent / PROJECT_STATUS_LAUNCHER
1768
- launcher_original = launcher.read_bytes() if _lexists(launcher) else None
1769
- launcher_mode = (
1770
- launcher.stat().st_mode & 0o777 if launcher_original is not None else None
1771
- )
1772
- guard_originals: List[Tuple[Path, Optional[bytes], Optional[int]]] = []
1773
- try:
1774
- for name in PROJECT_RUNTIME_SCRIPTS:
1775
- shutil.copy2(source_root / "scripts" / name, staging / name)
1776
- _atomic_copy(source_root / "scripts" / PROJECT_STATUS_LAUNCHER, launcher)
1777
- if _lexists(runtime):
1778
- os.replace(runtime, previous)
1779
- moved_previous = True
1780
- os.replace(staging, runtime)
1781
- published_runtime = True
1782
- if refresh_guards:
1783
- for name in GUARD_SCRIPTS:
1784
- destination = project / HOOKS_DIRECTORY / name
1785
- original = destination.read_bytes() if _lexists(destination) else None
1786
- mode = (
1787
- destination.stat().st_mode & 0o777
1788
- if original is not None
1789
- else None
1790
- )
1791
- guard_originals.append((destination, original, mode))
1792
- _atomic_copy(source_root / "scripts" / name, destination)
1793
- if moved_previous:
1794
- _remove_path(previous)
1795
- except BaseException as error:
1796
- for destination, original, mode in reversed(guard_originals):
1797
- if original is None:
1798
- _remove_path(destination)
1799
- else:
1800
- _atomic_write(destination, original, mode)
1801
- if launcher_original is None:
1802
- _remove_path(launcher)
1803
- else:
1804
- _atomic_write(launcher, launcher_original, launcher_mode)
1805
- if published_runtime and _lexists(runtime):
1806
- _remove_path(runtime)
1807
- if not _lexists(runtime) and moved_previous and _lexists(previous):
1808
- os.replace(previous, runtime)
1809
- if isinstance(error, Exception):
1810
- raise InstallerError(
1811
- f"project runtime refresh failed: {error}"
1812
- ) from error
1813
- raise
1814
- finally:
1815
- _remove_path(staging)
1816
- for name in PROJECT_RUNTIME_SCRIPTS:
1817
- refreshed.append(_describe_project_path(project, runtime / name))
1818
- refreshed.append(
1819
- _describe_project_path(
1820
- project, project / HOOKS_DIRECTORY / PROJECT_STATUS_LAUNCHER
1821
- )
1822
- )
1823
- if refresh_guards:
1824
- for name in GUARD_SCRIPTS:
1825
- destination = project / HOOKS_DIRECTORY / name
1826
- refreshed.append(_describe_project_path(project, destination))
1827
- if full:
1828
- for target, settings, merge, generated in (
1829
- (
1830
- "claude",
1831
- project / ".claude" / "settings.json",
1832
- _merged_claude_settings,
1833
- claude_hooks_settings,
1834
- ),
1835
- (
1836
- "codex",
1837
- project / ".codex" / "hooks.json",
1838
- _merged_codex_settings,
1839
- codex_hooks_settings,
1840
- ),
1841
- (
1842
- "cursor",
1843
- project / ".cursor" / "hooks.json",
1844
- _merged_cursor_settings,
1845
- cursor_hooks_settings,
1846
- ),
1847
- ):
1848
- if initialize and target not in selected:
1849
- continue
1850
- exists = _lexists(settings)
1851
- if exists or target in selected:
1852
- if not dry_run:
1853
- settings.parent.mkdir(parents=True, exist_ok=True)
1854
- content = (
1855
- merge(settings, interpreter)
1856
- if exists
1857
- else generated(interpreter)
1858
- )
1859
- _atomic_write(settings, content)
1860
- refreshed.append(_describe_project_path(project, settings))
1861
- if hooks_dir is not None:
1862
- for hook_name, generator in GIT_HOOKS:
1863
- hook_path = hooks_dir / hook_name
1864
- if not dry_run:
1865
- content = generator(interpreter)
1866
- hook_path.parent.mkdir(parents=True, exist_ok=True)
1867
- _atomic_write(hook_path, content, mode=0o755)
1868
- refreshed.append(_describe_project_path(project, hook_path))
1869
- return refreshed
1870
-
1871
-
1872
1728
  def refresh_hooks(
1873
- source_root: Path,
1874
- project: Path,
1875
- full: bool,
1876
- dry_run: bool = False,
1877
- selected: Sequence[str] = (),
1878
- initialize: bool = False,
1729
+ source_root: Path, project: Path, full: bool, dry_run: bool = False,
1730
+ selected: Sequence[str] = (), initialize: bool = False,
1879
1731
  ) -> List[str]:
1880
- if dry_run:
1881
- return _refresh_hooks_unlocked(
1882
- source_root, project, full, True, selected, initialize
1883
- )
1884
1732
  _validate_directory_destination(project, "project path")
1733
+ if full and not dry_run:
1734
+ _required_hook_runtime(project, "--hooks-init" if initialize else "--hooks-refresh-full", selected)
1735
+ pinned = os.path.lexists(project / HOOKS_DIRECTORY / "runtime.json")
1736
+ if os.path.lexists(project / HOOKS_DIRECTORY / "runtime"):
1737
+ raise InstallerError("legacy runtime requires --runtime-migrate before refresh")
1738
+ if pinned:
1739
+ try:
1740
+ status_runtime.resolve_runtime(project)
1741
+ except (OSError, ValueError) as error:
1742
+ raise InstallerError(str(error)) from error
1743
+ _validate_hooks_refresh(source_root, project, full, _git_hooks_directory(project) if full else None, selected, initialize)
1744
+ if not dry_run:
1745
+ _required_python_runtime("--hooks-refresh", selected)
1746
+ elif not initialize and not _has_legacy_project_contracts(source_root, project):
1747
+ _validate_hooks_refresh(source_root, project, full, _git_hooks_directory(project) if full else None, selected, initialize)
1748
+ raise InstallerError(f"no managed GSD Path hooks or runtime found in project: {project}")
1749
+ if dry_run:
1750
+ if pinned:
1751
+ return ["note: selected runtime retained; use --runtime-upgrade for an explicit upgrade"]
1752
+ _validate_hooks_refresh(source_root, project, full,
1753
+ _git_hooks_directory(project) if full else None, selected, True)
1754
+ runtime_store.prepare(source_root, project, dry_run=True)
1755
+ return [".gsd-path/runtime.json", ".gsd-path/status_runtime.py"]
1885
1756
  locks, created = _acquire_install_locks([project / HOOKS_DIRECTORY])
1886
1757
  try:
1887
- return _refresh_hooks_unlocked(
1888
- source_root, project, full, False, selected, initialize
1889
- )
1758
+ if not pinned:
1759
+ interpreter = _required_python_runtime("--hooks-init", selected)
1760
+ hooks_dir = _git_hooks_directory(project) if full else None
1761
+ _validate_hooks_refresh(source_root, project, full, hooks_dir, selected, True)
1762
+ transaction = ProjectTransaction()
1763
+ try:
1764
+ _apply_project(source_root, project, selected, full, transaction, interpreter, hooks_dir, update=True)
1765
+ except BaseException:
1766
+ _rollback_project(transaction)
1767
+ raise
1768
+ return [".gsd-path/runtime.json", ".gsd-path/status_runtime.py"]
1769
+ if not full and not _has_managed_guard_wiring(project):
1770
+ return ["note: selected runtime retained; use --runtime-upgrade for an explicit upgrade"]
1771
+ interpreter = _required_python_runtime("--hooks-refresh", selected)
1772
+ hooks_dir = _git_hooks_directory(project) if full else None
1773
+ refresh_targets = list(selected)
1774
+ if full and not initialize:
1775
+ for target, relative in (("claude", ".claude/settings.json"), ("codex", ".codex/hooks.json"), ("cursor", ".cursor/hooks.json")):
1776
+ if (project / relative).exists() and target not in refresh_targets:
1777
+ refresh_targets.append(target)
1778
+ transaction = ProjectTransaction()
1779
+ try:
1780
+ _apply_project(source_root, project, refresh_targets, True, transaction, interpreter, hooks_dir, update=True)
1781
+ except BaseException:
1782
+ _rollback_project(transaction)
1783
+ raise
1784
+ return ["note: selected runtime retained; stable hooks initialized/refreshed"]
1890
1785
  finally:
1891
1786
  _release_install_locks(locks, created)
1892
1787
 
@@ -2035,6 +1930,11 @@ def _read_package_version(manifest: Path) -> Optional[str]:
2035
1930
 
2036
1931
  def _validated_project_state(source_root: Path, project: Path) -> dict:
2037
1932
  validator = source_root / "scripts" / "pipeline_state.py"
1933
+ if (project / HOOKS_DIRECTORY / "runtime.json").exists():
1934
+ try:
1935
+ validator = status_runtime.resolve_runtime(project) / "pipeline_state.py"
1936
+ except (OSError, ValueError) as error:
1937
+ raise InstallerError(str(error)) from error
2038
1938
  if validator.is_symlink() or not validator.is_file():
2039
1939
  raise InstallerError(f"canonical state validator is unavailable: {validator}")
2040
1940
  interpreter = _required_python_runtime("--doctor")
@@ -2072,6 +1972,11 @@ def _validated_project_state(source_root: Path, project: Path) -> dict:
2072
1972
  def _validate_project_runtime_status(source_root: Path, project: Path) -> None:
2073
1973
  interpreter = _required_python_runtime("--doctor")
2074
1974
  runtime = source_root / "scripts" / "pipeline_state.py"
1975
+ if (project / HOOKS_DIRECTORY / "runtime.json").exists():
1976
+ try:
1977
+ runtime = status_runtime.resolve_runtime(project) / "pipeline_state.py"
1978
+ except (OSError, ValueError) as error:
1979
+ raise InstallerError(f"runtime changed during doctor validation: {error}") from error
2075
1980
  environment = os.environ.copy()
2076
1981
  environment["GIT_OPTIONAL_LOCKS"] = "0"
2077
1982
  try:
@@ -2097,6 +2002,13 @@ def _validate_project_runtime_status(source_root: Path, project: Path) -> None:
2097
2002
 
2098
2003
 
2099
2004
  def _project_runtime_matches(source_root: Path, project: Path) -> bool:
2005
+ if (project / HOOKS_DIRECTORY / "runtime.json").exists():
2006
+ try:
2007
+ root = status_runtime.resolve_runtime(project)
2008
+ launcher = project / HOOKS_DIRECTORY / PROJECT_STATUS_LAUNCHER
2009
+ return not launcher.is_symlink() and launcher.read_bytes() == (root / PROJECT_STATUS_LAUNCHER).read_bytes()
2010
+ except (OSError, ValueError):
2011
+ return False
2100
2012
  pairs = [
2101
2013
  (
2102
2014
  project / HOOKS_DIRECTORY / PROJECT_STATUS_LAUNCHER,
@@ -2368,7 +2280,15 @@ def doctor(
2368
2280
  "project: .claude/CLAUDE.md exists but is not the managed bridge",
2369
2281
  )
2370
2282
 
2283
+ pinned = os.path.lexists(project / HOOKS_DIRECTORY / "runtime.json")
2371
2284
  runtime = project / HOOKS_DIRECTORY / "runtime"
2285
+ if pinned:
2286
+ try:
2287
+ runtime = status_runtime.resolve_runtime(project)
2288
+ push("ok", f"project: selected runtime {status_runtime.declaration(project)['version']}")
2289
+ except (OSError, ValueError) as error:
2290
+ push("fail", str(error))
2291
+ return findings
2372
2292
  launcher = project / HOOKS_DIRECTORY / PROJECT_STATUS_LAUNCHER
2373
2293
  runtime_current = True
2374
2294
  if launcher.is_symlink():
@@ -2380,7 +2300,7 @@ def doctor(
2380
2300
  else:
2381
2301
  launcher_content = read_project_file(launcher, "project: status launcher")
2382
2302
  launcher_source = read_project_file(
2383
- source_root / "scripts" / PROJECT_STATUS_LAUNCHER,
2303
+ (runtime if pinned else source_root / "scripts") / PROJECT_STATUS_LAUNCHER,
2384
2304
  "package: status launcher",
2385
2305
  )
2386
2306
  if launcher_content is None or launcher_source is None:
@@ -2421,7 +2341,7 @@ def doctor(
2421
2341
  runtime_current = False
2422
2342
  continue
2423
2343
  source = read_project_file(
2424
- source_root / "scripts" / name, f"package: runtime {name}"
2344
+ (runtime if pinned else source_root / "scripts") / name, f"package: runtime {name}"
2425
2345
  )
2426
2346
  if source is None:
2427
2347
  runtime_current = False
@@ -2492,6 +2412,8 @@ def doctor(
2492
2412
  source = read_project_file(
2493
2413
  source_root / "scripts" / name, f"package: guard {name}"
2494
2414
  )
2415
+ if pinned:
2416
+ source = runtime_store.guard_launcher(name).encode()
2495
2417
  if source is None:
2496
2418
  continue
2497
2419
  if GUARD_MARKER.encode() not in content:
@@ -2851,12 +2773,16 @@ def parser() -> argparse.ArgumentParser:
2851
2773
  for target in TARGETS:
2852
2774
  argument_parser.add_argument(f"--{target}", action="store_true")
2853
2775
  argument_parser.add_argument(f"--{target}-root", type=Path)
2776
+ argument_parser.add_argument("--adapter-request", action="store_true", help=argparse.SUPPRESS)
2854
2777
  argument_parser.add_argument("--all", action="store_true", dest="all_targets")
2855
2778
  argument_parser.add_argument("--update", action="store_true")
2856
2779
  argument_parser.add_argument("--local", action="store_true")
2857
2780
  argument_parser.add_argument("--dry-run", action="store_true")
2858
2781
  argument_parser.add_argument("--project", type=Path)
2859
2782
  argument_parser.add_argument("--doctor", action="store_true")
2783
+ runtime_actions = argument_parser.add_mutually_exclusive_group()
2784
+ for action in ("restore", "upgrade", "migrate"):
2785
+ runtime_actions.add_argument(f"--runtime-{action}", action="store_true")
2860
2786
  argument_parser.add_argument("--hooks", action="store_true")
2861
2787
  argument_parser.add_argument("--hooks-init", action="store_true")
2862
2788
  argument_parser.add_argument("--hooks-refresh", action="store_true")
@@ -2873,10 +2799,51 @@ def parser() -> argparse.ArgumentParser:
2873
2799
  def main(argv: Optional[Sequence[str]] = None) -> int:
2874
2800
  argument_parser = parser()
2875
2801
  arguments = argument_parser.parse_args(argv)
2802
+ if arguments.adapter_request:
2803
+ try:
2804
+ request = json.load(sys.stdin)
2805
+ source = Path(request["source"])
2806
+ project = Path(request["project"])
2807
+ action = request["action"]
2808
+ if action == "install":
2809
+ plans = [TargetPlan(p["name"], Path(p["root"])) for p in request["plans"]]
2810
+ result = install(source, plans, project=project, dry_run=request["dryRun"],
2811
+ hooks=request["hooks"], migrate_legacy=request["migrateLegacy"],
2812
+ update=request["update"])
2813
+ elif action == "refresh":
2814
+ result = refresh_hooks(source, project, request["full"], request["dryRun"],
2815
+ request["selected"], request["initialize"])
2816
+ elif action == "doctor":
2817
+ result = doctor(source, request["targets"], lambda target: Path(request["roots"][target]), project)
2818
+ else:
2819
+ raise InstallerError("unknown installer adapter action")
2820
+ print(json.dumps(result))
2821
+ return 0
2822
+ except (InstallerError, OSError, ValueError, KeyError) as error:
2823
+ print(str(error), file=sys.stderr)
2824
+ return 1
2876
2825
  source_root = absolute_path(arguments.source_root)
2877
2826
  project = (
2878
2827
  absolute_path(arguments.project) if arguments.project is not None else None
2879
2828
  )
2829
+ runtime_action = next((action for action in ("restore", "upgrade", "migrate")
2830
+ if getattr(arguments, "runtime_" + action)), None)
2831
+ if runtime_action:
2832
+ if project is None:
2833
+ argument_parser.error(f"--runtime-{runtime_action} requires --project")
2834
+ try:
2835
+ _validate_project_git_root(project)
2836
+ locks, created = ([], []) if arguments.dry_run else _acquire_install_locks([project / HOOKS_DIRECTORY])
2837
+ try:
2838
+ pin = runtime_store.operate(source_root, project, runtime_action, dry_run=arguments.dry_run)
2839
+ finally:
2840
+ _release_install_locks(locks, created)
2841
+ print(f"runtime: {runtime_action} {pin['version']} ({pin['digest']})"
2842
+ + (" (dry run)" if arguments.dry_run else ""))
2843
+ return 0
2844
+ except (InstallerError, OSError, ValueError) as error:
2845
+ print(f"error: {error}", file=sys.stderr)
2846
+ return 1
2880
2847
  if arguments.doctor:
2881
2848
  named = [target for target in TARGETS if getattr(arguments, target)]
2882
2849
  selected = named if named and not arguments.all_targets else list(TARGETS)