claude-dev-env 4.2.0 → 7.0.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 (350) hide show
  1. package/.agents/agents/pr-description-writer.md +2 -2
  2. package/.agents/agents/test_agent_frontmatter.py +2 -2
  3. package/.agents/agents/test_pr_description_writer_style.py +1 -3
  4. package/.agents/skills/e-code-review/SKILL.md +12 -12
  5. package/.agents/skills/e-code-review/preflight-proposal.contract.test.mjs +20 -0
  6. package/.agents/skills/e-code-review/reference/fix.md +15 -17
  7. package/.agents/skills/e-code-review/reference/loop.md +35 -39
  8. package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +2 -1
  9. package/.agents/skills/e-code-review/reference/preflight-proposal.md +44 -9
  10. package/.agents/skills/e-code-review/reference/runner-selection.md +13 -14
  11. package/.agents/skills/issue-tracker/SKILL.md +7 -0
  12. package/.agents/skills/pull-request/SKILL.md +216 -0
  13. package/.agents/skills/pull-request/reference/publication-tasks.md +18 -0
  14. package/.agents/skills/pull-request/scripts/github_pr_command_constants/__init__.py +1 -0
  15. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/__init__.py +1 -0
  16. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +69 -0
  17. package/.agents/skills/pull-request/scripts/pull_request.py +265 -0
  18. package/.agents/skills/pull-request/scripts/recover_legacy_author.py +251 -0
  19. package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +58 -0
  20. package/.agents/skills/pull-request/scripts/test_pull_request.py +396 -0
  21. package/.agents/skills/pull-request/scripts/test_recover_legacy_author.py +221 -0
  22. package/.agents/skills/skill-builder/SKILL.md +3 -196
  23. package/.agents/skills/test-runner/SKILL.md +69 -0
  24. package/.agents/skills/test-runner/scripts/preflight_checks.py +365 -0
  25. package/.agents/skills/test-runner/scripts/run_tests.py +316 -0
  26. package/.agents/skills/test-runner/scripts/test_preflight_checks.py +185 -0
  27. package/.agents/skills/test-runner/scripts/test_run_tests.py +281 -0
  28. package/.agents/skills/test-runner/scripts/test_runner_constants/__init__.py +1 -0
  29. package/.agents/skills/test-runner/scripts/test_runner_constants/config/__init__.py +1 -0
  30. package/.agents/skills/test-runner/scripts/test_runner_constants/config/constants.py +96 -0
  31. package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
  32. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
  33. package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
  34. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
  35. package/bin/ever-shipped-skills.mjs +2 -0
  36. package/bin/ever-shipped-skills.test.mjs +8 -0
  37. package/bin/install.agents-home.test.mjs +15 -13
  38. package/bin/install.mjs +37 -1
  39. package/bin/install.policy-lint.test.mjs +78 -0
  40. package/bin/install.prune.test.mjs +149 -20
  41. package/bin/install.pstack-plugin.test.mjs +114 -0
  42. package/bin/install.test.mjs +61 -7
  43. package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
  44. package/hooks/advisory/pr_done_reminder.py +342 -0
  45. package/hooks/advisory/test_pr_done_reminder.py +249 -0
  46. package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
  47. package/hooks/blocking/code_rules_paired_test.py +22 -15
  48. package/hooks/blocking/pii_prevention_blocker.py +32 -51
  49. package/hooks/blocking/test_bash_post_call_dispatcher.py +55 -17
  50. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +1 -4
  51. package/hooks/blocking/test_code_rules_command_dispatch.py +2 -2
  52. package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
  53. package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
  54. package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
  55. package/hooks/blocking/test_pii_prevention_blocker.py +37 -89
  56. package/hooks/blocking/test_pre_tool_use_dispatcher.py +68 -133
  57. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
  58. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +18 -18
  59. package/hooks/gh_post_body_texts.py +195 -0
  60. package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
  61. package/hooks/git-hooks/pre_commit.py +52 -11
  62. package/hooks/git-hooks/test_installed_policy_lint.py +159 -0
  63. package/hooks/git-hooks/test_native_policy_commit.py +87 -0
  64. package/hooks/git-hooks/test_pre_commit.py +246 -0
  65. package/hooks/hooks.json +1 -16
  66. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -2
  67. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -8
  68. package/hooks/hooks_constants/gh_post_body_texts_constants.py +12 -0
  69. package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
  70. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -43
  71. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +17 -7
  72. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -7
  73. package/hooks/hooks_constants/test_failure_recorder_constants.py +3 -3
  74. package/hooks/hooks_constants/test_gh_post_body_texts_constants.py +19 -0
  75. package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
  76. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +20 -32
  77. package/hooks/observability/test_failure_recorder.py +1 -1
  78. package/hooks/pyproject.toml +1 -1
  79. package/hooks/test_gh_post_body_texts.py +79 -0
  80. package/package.json +2 -2
  81. package/rules/durable-post-artifacts.md +32 -20
  82. package/rules/gh-cli-conventions.md +23 -13
  83. package/rules/git-workflow.md +21 -8
  84. package/scripts/check.ps1 +0 -1
  85. package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
  86. package/scripts/ci/installer-lifecycle.test.mjs +26 -0
  87. package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
  88. package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
  89. package/scripts/durable_post_lint.py +206 -0
  90. package/scripts/durable_post_lint_config/config/constants.py +96 -0
  91. package/scripts/durable_post_lint_config/config/test_constants.py +25 -0
  92. package/scripts/policy_lint/adapter_configuration.py +53 -12
  93. package/scripts/policy_lint/adapter_detectors.py +13 -6
  94. package/scripts/policy_lint/adapters.py +7 -4
  95. package/scripts/policy_lint/config/constants.py +5 -0
  96. package/scripts/policy_lint/engine.py +48 -4
  97. package/scripts/policy_lint/model.py +1 -0
  98. package/scripts/policy_lint/registry.py +1 -1
  99. package/scripts/policy_lint/selection.py +2 -1
  100. package/scripts/policy_lint/selection_git.py +2 -0
  101. package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
  102. package/scripts/repository_checks/config/constants.py +0 -5
  103. package/scripts/shared_tree_paths.py +32 -3
  104. package/scripts/test_durable_post_lint.py +332 -0
  105. package/scripts/test_shared_tree_paths.py +33 -0
  106. package/scripts/tests/test_adapter_configuration.py +54 -0
  107. package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
  108. package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
  109. package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
  110. package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
  111. package/scripts/tests/test_policy_lint_selection.py +25 -2
  112. package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
  113. package/scripts/tests/test_rules.py +3 -7
  114. package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
  115. package/.agents/skills/_shared/pr-loop/AGENTS.md +0 -1
  116. package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
  117. package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
  118. package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
  119. package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
  120. package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
  121. package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
  122. package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
  123. package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
  124. package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
  125. package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
  126. package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
  127. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
  128. package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
  129. package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
  130. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
  131. package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
  132. package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
  133. package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
  134. package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
  135. package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
  136. package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
  137. package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
  138. package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
  139. package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
  140. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
  141. package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
  142. package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
  143. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
  144. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
  145. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
  146. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
  147. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
  148. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
  149. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
  150. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
  151. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
  152. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
  153. package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
  154. package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
  155. package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
  156. package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
  157. package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
  158. package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
  159. package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
  160. package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
  161. package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
  162. package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
  163. package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
  164. package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
  165. package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
  166. package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
  167. package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
  168. package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
  169. package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
  170. package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
  171. package/.agents/skills/autoconverge/AGENTS.md +0 -1
  172. package/.agents/skills/autoconverge/SKILL.md +0 -431
  173. package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
  174. package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
  175. package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
  176. package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
  177. package/.agents/skills/autoconverge/reference/convergence.md +0 -219
  178. package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
  179. package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
  180. package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
  181. package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
  182. package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
  183. package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
  184. package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
  185. package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
  186. package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
  187. package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
  188. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
  189. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
  190. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
  191. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
  192. package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
  193. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
  194. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
  195. package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
  196. package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
  197. package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
  198. package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
  199. package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
  200. package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
  201. package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
  202. package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
  203. package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
  204. package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
  205. package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
  206. package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
  207. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
  208. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
  209. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
  210. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
  211. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
  212. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
  213. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
  214. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
  215. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
  216. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
  217. package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
  218. package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
  219. package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
  220. package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
  221. package/.agents/skills/hitl/SKILL.md +0 -45
  222. package/.agents/skills/pr-cleanup/SKILL.md +0 -140
  223. package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
  224. package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
  225. package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
  226. package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
  227. package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
  228. package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
  229. package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
  230. package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
  231. package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
  232. package/.agents/skills/pr-refinement/SKILL.md +0 -31
  233. package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
  234. package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
  235. package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
  236. package/.agents/skills/pr-small-cl/SKILL.md +0 -52
  237. package/.agents/skills/pr-title-description/SKILL.md +0 -96
  238. package/.agents/skills/prototype/SKILL.md +0 -85
  239. package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
  240. package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
  241. package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
  242. package/.agents/skills/prototype/scripts/conftest.py +0 -15
  243. package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
  244. package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
  245. package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
  246. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  247. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
  248. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
  249. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
  250. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
  251. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
  252. package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
  253. package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
  254. package/.agents/skills/prototype/workflows/promotion.md +0 -25
  255. package/.agents/skills/prototype/workflows/sandbox.md +0 -35
  256. package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
  257. package/.agents/skills/rebase/AGENTS.md +0 -1
  258. package/.agents/skills/rebase/SKILL.md +0 -178
  259. package/.agents/skills/review-router/SKILL.md +0 -51
  260. package/.agents/skills/review-router/reference/route-policy.json +0 -7
  261. package/.agents/skills/review-router/reference/task-seeds.md +0 -1
  262. package/.agents/skills/review-router/scripts/conftest.py +0 -4
  263. package/.agents/skills/review-router/scripts/review_router.py +0 -50
  264. package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
  265. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
  266. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  267. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
  268. package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
  269. package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
  270. package/.agents/skills/review-tier/SKILL.md +0 -56
  271. package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
  272. package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
  273. package/.agents/skills/review-tier/scripts/conftest.py +0 -4
  274. package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
  275. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
  276. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  277. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
  278. package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
  279. package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
  280. package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
  281. package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
  282. package/.agents/skills/session-log/AGENTS.md +0 -1
  283. package/.agents/skills/session-log/SKILL.md +0 -170
  284. package/.agents/skills/session-log/templates/frontmatter.md +0 -40
  285. package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
  286. package/.agents/skills/session-tidy/AGENTS.md +0 -1
  287. package/.agents/skills/session-tidy/SKILL.md +0 -191
  288. package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
  289. package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
  290. package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
  291. package/.agents/skills/skill-builder/references/description-field.md +0 -111
  292. package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
  293. package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
  294. package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
  295. package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
  296. package/.agents/skills/skill-builder/references/skill-types.md +0 -232
  297. package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
  298. package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
  299. package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
  300. package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
  301. package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
  302. package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
  303. package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
  304. package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
  305. package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
  306. package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
  307. package/.agents/skills/update/.claude/CLAUDE.md +0 -1
  308. package/.agents/skills/update/AGENTS.md +0 -1
  309. package/.agents/skills/update/SKILL.md +0 -190
  310. package/_shared/pr-loop/AGENTS.md +0 -1
  311. package/_shared/pr-loop/CLAUDE.md +0 -1
  312. package/_shared/pr-loop/audit-contract.md +0 -183
  313. package/_shared/pr-loop/audit-reply-template.md +0 -151
  314. package/_shared/pr-loop/code-rules-gate.md +0 -65
  315. package/_shared/pr-loop/fix-protocol.md +0 -60
  316. package/_shared/pr-loop/gh-payloads.md +0 -75
  317. package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
  318. package/_shared/pr-loop/precatch-rubric.md +0 -66
  319. package/_shared/pr-loop/preflight-proposal.md +0 -56
  320. package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
  321. package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
  322. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
  323. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
  324. package/_shared/pr-loop/state-schema.md +0 -45
  325. package/_shared/pr-loop/worker-spawn.md +0 -189
  326. package/hooks/_gh_pr_author_swap_utils.py +0 -1225
  327. package/hooks/blocking/conventional_pr_title_gate.py +0 -443
  328. package/hooks/blocking/gh_body_arg_blocker.py +0 -176
  329. package/hooks/blocking/gh_pr_author_enforcer.py +0 -487
  330. package/hooks/blocking/gh_pr_author_restore.py +0 -105
  331. package/hooks/blocking/pr_description_writer_gate.py +0 -256
  332. package/hooks/blocking/test_conventional_pr_title_gate.py +0 -640
  333. package/hooks/blocking/test_gh_body_arg_blocker.py +0 -441
  334. package/hooks/blocking/test_gh_pr_author_enforcer.py +0 -1166
  335. package/hooks/blocking/test_gh_pr_author_restore.py +0 -513
  336. package/hooks/blocking/test_gh_pr_author_swap_utils.py +0 -910
  337. package/hooks/blocking/test_pr_description_writer_gate.py +0 -234
  338. package/hooks/blocking/test_preflight_check.py +0 -343
  339. package/hooks/blocking/test_test_preflight_check.py +0 -80
  340. package/hooks/blocking/test_volatile_path_in_post_blocker.py +0 -322
  341. package/hooks/blocking/volatile_path_in_post_blocker.py +0 -461
  342. package/hooks/hooks_constants/conventional_pr_title_gate_constants.py +0 -58
  343. package/hooks/hooks_constants/gh_pr_author_swap_constants.py +0 -76
  344. package/hooks/hooks_constants/pr_description_writer_gate_constants.py +0 -73
  345. package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +0 -55
  346. package/hooks/observability/pr_description_writer_spawn_tracker.py +0 -113
  347. package/hooks/observability/test_pr_description_writer_spawn_tracker.py +0 -150
  348. package/hooks/session/gh_pr_author_session_cleanup.py +0 -175
  349. package/hooks/session/test_gh_pr_author_session_cleanup.py +0 -576
  350. package/hooks/test__gh_pr_author_swap_utils.py +0 -333
@@ -32,6 +32,7 @@ from pr_loop_shared_constants.terminology_sweep_constants import (
32
32
  ALL_COMMON_ENGLISH_COMPOUND_TAIL_WORDS,
33
33
  ALL_DIFF_FILE_PATH_STRIP_PREFIXES,
34
34
  ALL_GIT_DIFF_CACHED_UNIFIED_ZERO_COMMAND,
35
+ ALL_GIT_DIFF_UNIFIED_ZERO_COMMAND,
35
36
  ALL_GIT_GREP_BASE_TREE_COMMAND_PREFIX,
36
37
  ALL_PROSE_STOPWORD_TOKENS,
37
38
  ALL_STRING_ESCAPE_SEQUENCES,
@@ -622,21 +623,23 @@ def _identifier_names_on_added_code_lines(diff_text: str) -> frozenset[str]:
622
623
 
623
624
 
624
625
  def _base_tree_names(
625
- repository_root: Path, all_names: frozenset[str]
626
+ repository_root: Path,
627
+ all_names: frozenset[str],
628
+ tree_revision: str,
626
629
  ) -> frozenset[str]:
627
- """Return the names the repository's base tree already contains.
630
+ """Return the names the comparison tree already contains.
628
631
 
629
- Each name is looked up as a whole word in the base revision's tree. A
630
- repository with no commits yet, or a lookup that git cannot run, reports
631
- the name as absent, so the sweep then treats it as newly introduced.
632
- That is the behaviour the sweep has with no base-tree check at all.
632
+ Each name is looked up as a whole word in ``tree_revision``. A repository
633
+ with no commits yet, or a lookup that git cannot run, reports the name as
634
+ absent, so the sweep treats it as newly introduced.
633
635
 
634
636
  Args:
635
637
  repository_root: The repository root the lookups run in.
636
638
  all_names: The identifier names to look up.
639
+ tree_revision: The revision whose tree is searched.
637
640
 
638
641
  Returns:
639
- The subset of names present in the base tree.
642
+ The subset of names present in that tree.
640
643
  """
641
644
  all_present_names: set[str] = set()
642
645
  for each_name in sorted(all_names):
@@ -645,7 +648,7 @@ def _base_tree_names(
645
648
  [
646
649
  *ALL_GIT_GREP_BASE_TREE_COMMAND_PREFIX,
647
650
  each_name,
648
- GIT_BASE_TREE_REVISION,
651
+ tree_revision,
649
652
  ],
650
653
  cwd=str(repository_root),
651
654
  capture_output=True,
@@ -695,14 +698,16 @@ def _raise_for_git_failure(
695
698
 
696
699
 
697
700
  def _strict_base_tree_names(
698
- repository_root: Path, all_names: frozenset[str]
701
+ repository_root: Path,
702
+ all_names: frozenset[str],
703
+ tree_revision: str,
699
704
  ) -> frozenset[str]:
700
705
  all_present_names: set[str] = set()
701
706
  for each_name in sorted(all_names):
702
707
  all_arguments = (
703
708
  *ALL_GIT_GREP_BASE_TREE_COMMAND_PREFIX,
704
709
  each_name,
705
- GIT_BASE_TREE_REVISION,
710
+ tree_revision,
706
711
  )
707
712
  grep_process = _run_strict_git(repository_root, all_arguments)
708
713
  if grep_process.returncode == 0:
@@ -728,14 +733,53 @@ def strict_staged_terminology_findings(repository_root: Path) -> list[str]:
728
733
  RuntimeError: Git could not read the staged diff or look up an
729
734
  identifier in HEAD.
730
735
  """
731
- diff_process = _run_strict_git(
732
- repository_root, ALL_GIT_DIFF_CACHED_UNIFIED_ZERO_COMMAND
736
+ return _strict_terminology_findings(
737
+ repository_root,
738
+ ALL_GIT_DIFF_CACHED_UNIFIED_ZERO_COMMAND,
739
+ GIT_BASE_TREE_REVISION,
733
740
  )
741
+
742
+
743
+ def strict_base_terminology_findings(
744
+ repository_root: Path, base_revision: str
745
+ ) -> list[str]:
746
+ """Return near-miss findings for worktree changes against a base revision.
747
+
748
+ Args:
749
+ repository_root: The repository root the worktree diff is read from.
750
+ base_revision: The tree that worktree changes are compared against.
751
+
752
+ Returns:
753
+ One finding string per near-miss term on an added prose line.
754
+
755
+ Raises:
756
+ RuntimeError: Git could not read the committed diff or look up an
757
+ identifier in the comparison tree.
758
+ """
759
+ all_arguments = (
760
+ *ALL_GIT_DIFF_UNIFIED_ZERO_COMMAND,
761
+ base_revision,
762
+ )
763
+ return _strict_terminology_findings(
764
+ repository_root,
765
+ all_arguments,
766
+ base_revision,
767
+ )
768
+
769
+
770
+ def _strict_terminology_findings(
771
+ repository_root: Path,
772
+ all_diff_arguments: Sequence[str],
773
+ tree_revision: str,
774
+ ) -> list[str]:
775
+ diff_process = _run_strict_git(repository_root, all_diff_arguments)
734
776
  _raise_for_git_failure(diff_process, GIT_DIFF_OPERATION)
735
777
  all_added_names = _identifier_names_on_added_code_lines(diff_process.stdout)
736
778
  preexisting_tuples = frozenset(
737
779
  _identifier_token_tuple(each_name)
738
- for each_name in _strict_base_tree_names(repository_root, all_added_names)
780
+ for each_name in _strict_base_tree_names(
781
+ repository_root, all_added_names, tree_revision
782
+ )
739
783
  )
740
784
  return _find_terminology_near_misses(diff_process.stdout, preexisting_tuples)
741
785
 
@@ -743,16 +787,15 @@ def strict_staged_terminology_findings(repository_root: Path) -> list[str]:
743
787
  def staged_terminology_findings(repository_root: Path) -> list[str]:
744
788
  """Return terminology near-miss findings for a repository's staged diff.
745
789
 
746
- An identifier the base tree already names is not one the staged diff
747
- introduces, so no prose is flagged against it. Only genuinely new
748
- identifiers are swept.
790
+ An identifier already present in HEAD is not treated as introduced, so
791
+ prose is not flagged against it.
749
792
 
750
793
  Args:
751
794
  repository_root: The repository root the staged diff is read from.
752
795
 
753
796
  Returns:
754
- One finding string per near-miss term on a staged added prose line, or
755
- an empty list when the staged diff is empty or git cannot be run.
797
+ One finding string per near-miss term on a staged prose line, or an
798
+ empty list when the staged diff is empty or git cannot be run.
756
799
  """
757
800
  diff_process = subprocess.run(
758
801
  list(ALL_GIT_DIFF_CACHED_UNIFIED_ZERO_COMMAND),
@@ -770,7 +813,9 @@ def staged_terminology_findings(repository_root: Path) -> list[str]:
770
813
  all_added_names = _identifier_names_on_added_code_lines(diff_process.stdout)
771
814
  preexisting_tuples = frozenset(
772
815
  _identifier_token_tuple(each_name)
773
- for each_name in _base_tree_names(repository_root, all_added_names)
816
+ for each_name in _base_tree_names(
817
+ repository_root, all_added_names, GIT_BASE_TREE_REVISION
818
+ )
774
819
  )
775
820
  return _find_terminology_near_misses(diff_process.stdout, preexisting_tuples)
776
821
 
@@ -31,6 +31,7 @@ sweep_module = _load_sweep_module()
31
31
  sweep_diff = sweep_module.sweep_diff
32
32
  staged_terminology_findings = sweep_module.staged_terminology_findings
33
33
  strict_staged_terminology_findings = sweep_module.strict_staged_terminology_findings
34
+ strict_base_terminology_findings = sweep_module.strict_base_terminology_findings
34
35
  main = sweep_module.main
35
36
  parse_added_lines = sweep_module._parse_added_lines
36
37
 
@@ -53,6 +54,54 @@ def _init_git_repository(repository_path: Path) -> None:
53
54
  )
54
55
 
55
56
 
57
+ def _run_repository_git(repository_path: Path, all_arguments: list[str]) -> str:
58
+ completed = subprocess.run(
59
+ all_arguments,
60
+ cwd=repository_path,
61
+ check=True,
62
+ capture_output=True,
63
+ text=True,
64
+ env=_hermetic_git_environment(),
65
+ )
66
+ return completed.stdout.strip()
67
+
68
+
69
+ def _write_repository_text(
70
+ repository_path: Path, relative_path: str, text: str
71
+ ) -> None:
72
+ (repository_path / relative_path).write_text(text, encoding="utf-8")
73
+
74
+
75
+ def _commit_all(repository_path: Path, message: str) -> None:
76
+ _run_repository_git(repository_path, ["git", "add", "-A"])
77
+ _run_repository_git(repository_path, ["git", "commit", "-m", message])
78
+
79
+
80
+ def _head_revision(repository_path: Path) -> str:
81
+ return _run_repository_git(repository_path, ["git", "rev-parse", "HEAD"])
82
+
83
+
84
+ def _committed_near_miss_repository(repository_path: Path) -> str:
85
+ _init_git_repository(repository_path)
86
+ _write_repository_text(repository_path, "quota.py", "baseline = 1\n")
87
+ _write_repository_text(repository_path, "README.md", "Baseline quota text.\n")
88
+ _commit_all(repository_path, "base")
89
+ comparison_revision = _head_revision(repository_path)
90
+ _write_repository_text(
91
+ repository_path,
92
+ "quota.py",
93
+ "baseline = 1\npremium_request_interactions = 5\n",
94
+ )
95
+ _commit_all(repository_path, "identifier")
96
+ _write_repository_text(
97
+ repository_path,
98
+ "README.md",
99
+ "The premium-request-budget field gates the run.\n",
100
+ )
101
+ _commit_all(repository_path, "documentation")
102
+ return comparison_revision
103
+
104
+
56
105
  CODE_AND_PROSE_DIFF = (
57
106
  "diff --git a/api/quota.py b/api/quota.py\n"
58
107
  "--- a/api/quota.py\n"
@@ -310,6 +359,16 @@ def test_staged_terminology_findings_empty_when_clean(tmp_path: Path) -> None:
310
359
  assert staged_terminology_findings(tmp_path) == []
311
360
 
312
361
 
362
+ def test_strict_base_terminology_findings_reports_committed_near_miss(
363
+ tmp_path: Path,
364
+ ) -> None:
365
+ comparison_revision = _committed_near_miss_repository(tmp_path)
366
+ all_findings = strict_base_terminology_findings(tmp_path, comparison_revision)
367
+ assert len(all_findings) == 1
368
+ assert "README.md:1" in all_findings[0]
369
+ assert "premium-request-budget" in all_findings[0]
370
+
371
+
313
372
  def test_parse_added_lines_counts_pre_increment_content_as_added_line() -> None:
314
373
  diff_with_pre_increment = (
315
374
  "diff --git a/app.mjs b/app.mjs\n"
@@ -88,6 +88,7 @@ export const EVER_SHIPPED_SKILL_NAMES = new Set([
88
88
  'pr-loop-lifecycle',
89
89
  'pr-review-responder',
90
90
  'pr-scope-resolve',
91
+ 'pull-request',
91
92
  'pre-compact',
92
93
  'privacy-hygiene',
93
94
  'prototype',
@@ -122,6 +123,7 @@ export const EVER_SHIPPED_SKILL_NAMES = new Set([
122
123
  'syncing-submodules',
123
124
  'task-build',
124
125
  'team-advisor',
126
+ 'test-runner',
125
127
  'update',
126
128
  'usage-pause',
127
129
  'verified-build',
@@ -28,3 +28,11 @@ test('EVER_SHIPPED_SKILL_NAMES includes every current shipped skill', () => {
28
28
 
29
29
  assert.deepEqual(allUncoveredSkillNames, [], 'every current skill must enter the fallback set');
30
30
  });
31
+
32
+ test('EVER_SHIPPED_SKILL_NAMES retains the explicit test runner', () => {
33
+ assert.ok(EVER_SHIPPED_SKILL_NAMES.has('test-runner'));
34
+ });
35
+
36
+ test('EVER_SHIPPED_SKILL_NAMES includes the pull request skill', () => {
37
+ assert.equal(EVER_SHIPPED_SKILL_NAMES.has('pull-request'), true);
38
+ });
@@ -32,6 +32,8 @@ const INSTALLER_PATH = join(THIS_DIRECTORY, 'install.mjs');
32
32
  const PACKAGE_DIRECTORY = dirname(THIS_DIRECTORY);
33
33
  const SHIPPED_SKILL_NAME = 'privacy-hygiene';
34
34
  const ELI5_SKILL_NAME = 'eli5';
35
+ const E_CODE_REVIEW_SKILL_NAME = 'e-code-review';
36
+ const REFERENCE_DIRECTORY_NAME = 'reference';
35
37
  const SHIPPED_AGENT_FILE_NAME = 'clean-coder.md';
36
38
  const SHIPPED_AGENT_FILE_NAMES = [
37
39
  SHIPPED_AGENT_FILE_NAME,
@@ -69,10 +71,7 @@ const CLEAN_CODER_POLICY_REFERENCES = [
69
71
  ],
70
72
  ];
71
73
  const PERSONAL_SKILL_NAME = 'my-notes';
72
- const SHARED_DIRECTORY_NAME = '_shared';
73
- const PR_LOOP_DIRECTORY_NAME = 'pr-loop';
74
74
  const PREFLIGHT_PROPOSAL_FILE_NAME = 'preflight-proposal.md';
75
- const PREFLIGHT_PROPOSAL_POINTER = '@~/.claude/_shared/pr-loop/preflight-proposal.md';
76
75
 
77
76
  /**
78
77
  * @param {string} homeDirectory
@@ -100,20 +99,23 @@ function runInstaller(homeDirectory, extraArguments, environmentOverrides = {})
100
99
  }
101
100
 
102
101
  /**
103
- * @param {{ homeDirectory: string, skillsInstallDirectory: string }} installationPaths
102
+ * @param {{ skillsInstallDirectory: string }} installationPaths
104
103
  */
105
104
  function assertProposalContractInstallation(installationPaths) {
106
- const { homeDirectory, skillsInstallDirectory } = installationPaths;
105
+ const { skillsInstallDirectory } = installationPaths;
107
106
  const allContractPathSegments = [
108
- SHARED_DIRECTORY_NAME, PR_LOOP_DIRECTORY_NAME, PREFLIGHT_PROPOSAL_FILE_NAME,
107
+ E_CODE_REVIEW_SKILL_NAME,
108
+ REFERENCE_DIRECTORY_NAME,
109
+ PREFLIGHT_PROPOSAL_FILE_NAME,
109
110
  ];
110
- const installedPointerPath = join(skillsInstallDirectory, ...allContractPathSegments);
111
- const pointerLine = readFileSync(installedPointerPath, 'utf8').trimEnd().split('\n').at(-1);
112
- const allTargetSegments = pointerLine.slice('@~/'.length).split('/');
113
- const installedContractPath = join(homeDirectory, ...allTargetSegments);
114
- const sourceContractPath = join(PACKAGE_DIRECTORY, ...allContractPathSegments);
111
+ const installedContractPath = join(skillsInstallDirectory, ...allContractPathSegments);
112
+ const sourceSkillsDirectory = resolvePackageManagedDirectory(
113
+ PACKAGE_DIRECTORY,
114
+ MANAGED_SKILLS_DIRECTORY_NAME,
115
+ );
116
+ const sourceContractPath = join(sourceSkillsDirectory, ...allContractPathSegments);
115
117
 
116
- assert.equal(pointerLine, PREFLIGHT_PROPOSAL_POINTER);
118
+ assert.equal(existsSync(installedContractPath), true);
117
119
  assert.equal(readFileSync(installedContractPath, 'utf8'), readFileSync(sourceContractPath, 'utf8'));
118
120
  }
119
121
 
@@ -197,7 +199,7 @@ test('a full install writes skills and agents under .agents and points .claude a
197
199
  );
198
200
  assert.equal(realpathSync(lookupSkillFile), realpathSync(canonicalSkillFile));
199
201
  assert.equal(realpathSync(lookupEli5SkillFile), realpathSync(canonicalEli5SkillFile));
200
- assertProposalContractInstallation({ homeDirectory, skillsInstallDirectory });
202
+ assertProposalContractInstallation({ skillsInstallDirectory });
201
203
  assert.equal(
202
204
  readFileSync(lookupAgentFile, 'utf8'),
203
205
  readFileSync(canonicalAgentFile, 'utf8'),
package/bin/install.mjs CHANGED
@@ -35,6 +35,11 @@ import {
35
35
  WINDOWS_PYTHON_LAUNCHER_COMMAND,
36
36
  PYTHON_PROBE_TIMEOUT_MILLISECONDS,
37
37
  } from './install-constants.mjs';
38
+ import {
39
+ PSTACK_PLUGIN_DIRECTORY_NAME,
40
+ PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH,
41
+ refreshPstackPluginManifest,
42
+ } from '../scripts/refresh_pstack_plugin_skills.mjs';
38
43
  import {
39
44
  resolveInstallRoot,
40
45
  parseExplicitTargetFromArgv,
@@ -172,6 +177,7 @@ export const CORE_SKILLS = [
172
177
  'grok-spawn',
173
178
  'pr-small-cl',
174
179
  'everything-search',
180
+ 'test-runner',
175
181
  'privacy-hygiene',
176
182
  'issue-tracker',
177
183
  'task-build',
@@ -428,6 +434,26 @@ export function runCursorRuleSync(pythonCommand, scriptPath, claudeRoot, cursorR
428
434
  );
429
435
  }
430
436
 
437
+ /**
438
+ * Refresh the pstack plugin manifest Claude Code reads, when this home has one.
439
+ *
440
+ * Claude Code loads a skills-directory folder as a plugin when it holds
441
+ * `.claude-plugin/plugin.json`, and namespaces its skills as `pstack:how`.
442
+ * Without the manifest, a folder of skill folders loads nothing.
443
+ *
444
+ * @param {string} [skillsRoot] The skills root to read. Defaults to the root
445
+ * this run installs into.
446
+ * @returns {string|null} The manifest path when this run wrote it, else null.
447
+ */
448
+ export function refreshInstalledPstackPluginManifest(
449
+ skillsRoot = join(CLAUDE_HOME, MANAGED_SKILLS_DIRECTORY_NAME),
450
+ ) {
451
+ const pluginRoot = join(skillsRoot, PSTACK_PLUGIN_DIRECTORY_NAME);
452
+ const outcome = refreshPstackPluginManifest(pluginRoot);
453
+ if (!outcome.didWrite) return null;
454
+ return join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
455
+ }
456
+
431
457
  function seedCodexPstackPreferences() {
432
458
  const preferenceDirectory = join(AGENTS_HOME, CURSOR_RULES_DIRECTORY_NAME);
433
459
  const preferencePath = join(
@@ -1181,6 +1207,11 @@ export const FOLDED_HOOK_RELATIVE_PATHS = new Set([
1181
1207
  'blocking/pytest_testpaths_orphan_blocker.py',
1182
1208
  'blocking/open_questions_in_plans_blocker.py',
1183
1209
  'blocking/md_to_html_blocker.py',
1210
+ 'blocking/gh_body_arg_blocker.py',
1211
+ 'blocking/volatile_path_in_post_blocker.py',
1212
+ 'blocking/conventional_pr_title_gate.py',
1213
+ 'blocking/gh_pr_author_enforcer.py',
1214
+ 'blocking/pr_description_writer_gate.py',
1184
1215
  'session/untracked_repo_detector.py',
1185
1216
  ]);
1186
1217
 
@@ -1198,11 +1229,13 @@ export const POST_FOLDED_HOOK_RELATIVE_PATHS = new Set([
1198
1229
  'workflow/auto_formatter.py',
1199
1230
  'workflow/doc_gist_auto_publish.py',
1200
1231
  'workflow/md_to_html_companion.py',
1232
+ 'blocking/gh_pr_author_restore.py',
1201
1233
  ]);
1202
1234
 
1203
- /** Hook scripts that still ship but no longer run as registered hooks. */
1204
1235
  export const RETIRED_HOOK_REGISTRATION_RELATIVE_PATHS = new Set([
1205
1236
  'session/untracked_repo_detector.py',
1237
+ 'session/gh_pr_author_session_cleanup.py',
1238
+ 'observability/pr_description_writer_spawn_tracker.py',
1206
1239
  ]);
1207
1240
 
1208
1241
  /**
@@ -2312,6 +2345,9 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
2312
2345
  }
2313
2346
  summary.skills = { created: skillsCreated, updated: skillsUpdated, pruned: 0, paths: skillPaths };
2314
2347
  allInstalledFiles.push(...skillPaths);
2348
+ const pstackManifestPath = refreshInstalledPstackPluginManifest();
2349
+ if (pstackManifestPath) allInstalledFiles.push(pstackManifestPath);
2350
+ summary.pstackPlugin = { manifestPath: pstackManifestPath };
2315
2351
  syncWrittenPaths([...allInstalledFiles, ...publishedPointerPaths]);
2316
2352
  throwIfFault(FAULT_PHASES.AFTER_FILE_STAGING);
2317
2353
  throwIfFault(FAULT_PHASES.BEFORE_DURABLE_PROMOTION);
@@ -0,0 +1,78 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFileSync } from 'node:fs';
4
+ import {
5
+ FOLDED_HOOK_RELATIVE_PATHS,
6
+ mergeHooksIntoSettings,
7
+ } from './install.mjs';
8
+
9
+ const allRetiredWriteHooks = [
10
+ 'blocking/code_rules_enforcer.py',
11
+ 'blocking/tdd_enforcer.py',
12
+ 'blocking/windows_rmtree_blocker.py',
13
+ 'blocking/state_description_blocker.py',
14
+ 'blocking/subprocess_budget_completeness.py',
15
+ 'blocking/hook_prose_detector_consistency.py',
16
+ 'blocking/workflow_substitution_slot_blocker.py',
17
+ ];
18
+ const sourceHooks = JSON.parse(readFileSync(new URL('../hooks/hooks.json', import.meta.url), 'utf8'));
19
+ const installedRoot = '/fixture-home/.claude';
20
+ const foreignCommand = 'python /unmanaged/keep_this_hook.py';
21
+ const foreignSessionCommand = 'node /unmanaged/session.js';
22
+
23
+ function legacySettings() {
24
+ return {
25
+ permissions: { allow: ['Read'] },
26
+ hooks: {
27
+ PreToolUse: [{
28
+ matcher: 'Write|Edit|MultiEdit|apply_patch',
29
+ hooks: [
30
+ { type: 'command', command: foreignCommand, timeout: 17 },
31
+ ...allRetiredWriteHooks.map(relativePath => ({
32
+ type: 'command',
33
+ command: `python ${installedRoot}/hooks/${relativePath}`,
34
+ timeout: 10,
35
+ })),
36
+ ],
37
+ }],
38
+ SessionStart: [{
39
+ matcher: '',
40
+ hooks: [{ type: 'command', command: foreignSessionCommand, timeout: 9 }],
41
+ }],
42
+ },
43
+ };
44
+ }
45
+
46
+ function allCommands(settings) {
47
+ return Object.values(settings.hooks).flatMap(groups =>
48
+ groups.flatMap(group => group.hooks.map(hook => hook.command)));
49
+ }
50
+
51
+ test('every retired write registration already has an installer-owned prune path', () => {
52
+ for (const relativePath of allRetiredWriteHooks) {
53
+ assert.ok(FOLDED_HOOK_RELATIVE_PATHS.has(relativePath), relativePath);
54
+ }
55
+ });
56
+
57
+ test('upgrade prunes legacy direct file checks while retaining foreign hooks', () => {
58
+ const settings = legacySettings();
59
+ mergeHooksIntoSettings(settings, sourceHooks, installedRoot, 'python3');
60
+ const commands = allCommands(settings);
61
+ for (const relativePath of allRetiredWriteHooks) {
62
+ assert.ok(!commands.some(command => command.includes(relativePath)), relativePath);
63
+ }
64
+ assert.ok(commands.includes(foreignCommand));
65
+ assert.ok(commands.includes(foreignSessionCommand));
66
+ assert.deepEqual(settings.permissions, { allow: ['Read'] });
67
+ assert.ok(commands.some(command => command.includes('/blocking/pre_tool_use_dispatcher.py')));
68
+ assert.ok(commands.some(command => command.includes('/blocking/bash_pre_tool_use_dispatcher.py')));
69
+ });
70
+
71
+ test('reinstall does not restore retired direct checks or duplicate foreign hooks', () => {
72
+ const settings = legacySettings();
73
+ mergeHooksIntoSettings(settings, sourceHooks, installedRoot, 'python3');
74
+ const firstInstall = structuredClone(settings);
75
+ mergeHooksIntoSettings(settings, sourceHooks, installedRoot, 'python3');
76
+ assert.deepEqual(settings, firstInstall);
77
+ assert.equal(allCommands(settings).filter(command => command === foreignCommand).length, 1);
78
+ });