claude-dev-env 4.3.0 → 7.1.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 (480) 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 +5 -4
  12. package/.agents/skills/pull-request/SKILL.md +11 -13
  13. package/.agents/skills/pull-request/reference/publication-tasks.md +3 -3
  14. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +9 -1
  15. package/.agents/skills/pull-request/scripts/pull_request.py +35 -44
  16. package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +9 -0
  17. package/.agents/skills/pull-request/scripts/test_pull_request.py +35 -3
  18. package/.agents/skills/session-continuity/README.md +189 -0
  19. package/.agents/skills/session-continuity/SKILL.md +114 -0
  20. package/.agents/skills/session-continuity/continuity.mjs +375 -0
  21. package/.agents/skills/session-continuity/continuity.test.mjs +303 -0
  22. package/.agents/skills/skill-builder/SKILL.md +3 -196
  23. package/.agents/skills/syncing-submodules/scripts/test_sync_parent_pointer.py +0 -7
  24. package/.agents/skills/windows-scheduled-task/SKILL.md +153 -0
  25. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
  26. package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
  27. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
  28. package/bin/cde.mjs +36 -8
  29. package/bin/cde.test.mjs +48 -0
  30. package/bin/ever-shipped-skills.mjs +2 -20
  31. package/bin/ever-shipped-skills.test.mjs +8 -0
  32. package/bin/expand_home_directory_tokens.mjs +4 -5
  33. package/bin/expand_home_directory_tokens.test.mjs +128 -0
  34. package/bin/git_hooks_installer.mjs +22 -3
  35. package/bin/git_hooks_installer.test.mjs +159 -5
  36. package/bin/install-session-continuity.mjs +78 -0
  37. package/bin/install-session-continuity.test.mjs +42 -0
  38. package/bin/install.agents-home.test.mjs +15 -13
  39. package/bin/install.codex-rules.test.mjs +5 -2
  40. package/bin/install.cursor-rules.test.mjs +5 -2
  41. package/bin/install.mjs +506 -63
  42. package/bin/install.policy-lint.test.mjs +130 -0
  43. package/bin/install.profiles.test.mjs +10 -0
  44. package/bin/install.prune.test.mjs +286 -37
  45. package/bin/install.pstack-plugin.test.mjs +114 -0
  46. package/bin/install.test.mjs +537 -120
  47. package/bin/install.transaction.test.mjs +3 -3
  48. package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
  49. package/docs/CODE_RULES.md +1 -1
  50. package/hooks/advisory/pr_done_reminder.py +342 -0
  51. package/hooks/advisory/test_pr_done_reminder.py +249 -0
  52. package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
  53. package/hooks/blocking/code_rules_banned_identifiers.py +3 -0
  54. package/hooks/blocking/code_rules_paired_test.py +22 -15
  55. package/hooks/blocking/precommit_code_rules_gate.py +5 -8
  56. package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +7 -52
  57. package/hooks/blocking/test_bash_post_call_dispatcher.py +65 -4
  58. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -69
  59. package/hooks/blocking/test_code_rules_banned_identifiers.py +64 -0
  60. package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
  61. package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
  62. package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
  63. package/hooks/blocking/test_pre_tool_use_dispatcher.py +43 -342
  64. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
  65. package/hooks/blocking/test_shared_stdin_adoption.py +1 -47
  66. package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
  67. package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +116 -0
  68. package/hooks/git-hooks/post_commit.py +33 -0
  69. package/hooks/git-hooks/pre_commit.py +18 -49
  70. package/hooks/git-hooks/pre_push.py +45 -541
  71. package/hooks/git-hooks/test_installed_policy_lint.py +208 -0
  72. package/hooks/git-hooks/test_native_hook_support.py +129 -0
  73. package/hooks/git-hooks/test_native_policy_commit.py +120 -0
  74. package/hooks/git-hooks/test_post_commit.py +43 -0
  75. package/hooks/git-hooks/test_pre_commit.py +38 -148
  76. package/hooks/git-hooks/test_pre_push.py +48 -1631
  77. package/hooks/git-hooks/test_verification_notice.py +60 -0
  78. package/hooks/git-hooks/test_verification_start.py +173 -0
  79. package/hooks/git-hooks/verification_notice.py +192 -0
  80. package/hooks/git-hooks/verification_notice_context.py +253 -0
  81. package/hooks/git-hooks/verification_notice_state.py +237 -0
  82. package/hooks/git-hooks/verification_start.py +100 -0
  83. package/hooks/hooks.json +1 -120
  84. package/hooks/hooks_constants/banned_identifiers_constants.py +4 -0
  85. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -1
  86. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -15
  87. package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -4
  88. package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
  89. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -93
  90. package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -4
  91. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +19 -2
  92. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +3 -66
  93. package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
  94. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +46 -201
  95. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +3 -25
  96. package/hooks/validation/test_post_tool_use_dispatcher.py +35 -298
  97. package/hooks/validators/run_all_validators.py +5 -1
  98. package/hooks/validators/security_checks.py +57 -2
  99. package/hooks/validators/test_hook_timing_harness.py +2 -5
  100. package/hooks/validators/test_run_all_validators_mutation_tool_parity.py +3 -11
  101. package/hooks/validators/test_run_all_validators_policy_lint.py +36 -0
  102. package/hooks/validators/test_security_checks.py +57 -0
  103. package/package.json +2 -2
  104. package/rules/confirm-implementation-forks.md +2 -0
  105. package/rules/durable-post-artifacts.md +2 -1
  106. package/rules/git-workflow.md +18 -6
  107. package/rules/pstack-host-mapping.md +48 -0
  108. package/rules/vault-context.md +3 -5
  109. package/rules/verify-runtime-state.md +7 -0
  110. package/scripts/audit-hooks-cli.mjs +5 -0
  111. package/scripts/audit-hooks.mjs +72 -16
  112. package/scripts/automatic_advisory/__init__.py +1 -0
  113. package/scripts/automatic_advisory/checkout.py +51 -0
  114. package/scripts/automatic_advisory/cli.py +334 -0
  115. package/scripts/automatic_advisory/config/__init__.py +1 -0
  116. package/scripts/automatic_advisory/config/constants.py +93 -0
  117. package/scripts/automatic_advisory/config/timing.py +4 -0
  118. package/scripts/automatic_advisory/configuration.py +197 -0
  119. package/scripts/automatic_advisory/execution.py +350 -0
  120. package/scripts/automatic_advisory/git.py +69 -0
  121. package/scripts/automatic_advisory/model.py +107 -0
  122. package/scripts/automatic_advisory/process_host.py +32 -0
  123. package/scripts/automatic_advisory/publisher.py +84 -0
  124. package/scripts/automatic_advisory/runner.py +399 -0
  125. package/scripts/automatic_advisory/state.py +116 -0
  126. package/scripts/automatic_advisory/window_flags.py +41 -0
  127. package/scripts/automatic_advisory/windows_job.py +122 -0
  128. package/scripts/check.ps1 +0 -1
  129. package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
  130. package/scripts/ci/installer-lifecycle.test.mjs +26 -0
  131. package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
  132. package/scripts/codex_compat_materializer.py +99 -13
  133. package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
  134. package/{.agents/skills/_shared/pr-loop/scripts → scripts}/durable_post_lint.py +35 -43
  135. package/{.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/durable_post_lint_constants.py → scripts/durable_post_lint_config/config/constants.py} +10 -10
  136. package/{.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint_constants.py → scripts/durable_post_lint_config/config/test_constants.py} +14 -1
  137. package/scripts/local_report_cli.py +184 -0
  138. package/scripts/local_report_core.py +247 -0
  139. package/scripts/local_report_publisher.py +12 -0
  140. package/scripts/local_report_validation.py +357 -0
  141. package/scripts/local_verification/__init__.py +23 -0
  142. package/scripts/local_verification/check_runner.py +364 -0
  143. package/scripts/local_verification/check_support.py +99 -0
  144. package/scripts/local_verification/cli.py +158 -0
  145. package/scripts/local_verification/command_runner.py +84 -0
  146. package/scripts/local_verification/config/__init__.py +1 -0
  147. package/scripts/local_verification/config/constants.py +89 -0
  148. package/scripts/local_verification/config/timing.py +1 -0
  149. package/scripts/local_verification/git_state.py +146 -0
  150. package/scripts/local_verification/manifest.py +224 -0
  151. package/scripts/local_verification/model.py +174 -0
  152. package/scripts/local_verification/runner.py +234 -0
  153. package/scripts/policy_lint/adapter_detectors.py +13 -6
  154. package/scripts/policy_lint/adapter_pairing.py +58 -3
  155. package/scripts/policy_lint/adapters.py +7 -4
  156. package/scripts/policy_lint/config/approved_test_pairs.py +323 -0
  157. package/scripts/policy_lint/config/constants.py +5 -0
  158. package/scripts/policy_lint/engine.py +48 -4
  159. package/scripts/policy_lint/model.py +1 -0
  160. package/scripts/policy_lint/registry.py +1 -1
  161. package/scripts/policy_lint/selection.py +2 -1
  162. package/scripts/policy_lint/selection_git.py +2 -0
  163. package/scripts/pr_verification/__init__.py +1 -0
  164. package/scripts/pr_verification/branch_refs.py +114 -0
  165. package/scripts/pr_verification/config/__init__.py +1 -0
  166. package/scripts/pr_verification/config/constants.py +84 -0
  167. package/scripts/pr_verification/config/timing.py +5 -0
  168. package/scripts/pr_verification/github.py +137 -0
  169. package/scripts/pr_verification/github_api.py +376 -0
  170. package/scripts/pr_verification/github_labels.py +88 -0
  171. package/scripts/pr_verification/github_parsing.py +182 -0
  172. package/scripts/pr_verification/github_transport.py +91 -0
  173. package/scripts/pr_verification/lock.py +122 -0
  174. package/scripts/pr_verification/model.py +43 -0
  175. package/scripts/pr_verification/requirements.txt +1 -0
  176. package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
  177. package/scripts/shared_tree_paths.py +32 -3
  178. package/{.agents/skills/_shared/pr-loop/scripts → scripts}/test_durable_post_lint.py +97 -0
  179. package/scripts/test_shared_tree_paths.py +33 -0
  180. package/scripts/tests/test-audit-hooks-cli.mjs +150 -0
  181. package/scripts/tests/test-audit-hooks.mjs +94 -2
  182. package/scripts/tests/test_automatic_advisory.py +376 -0
  183. package/scripts/tests/test_automatic_advisory_job_cleanup.py +71 -0
  184. package/scripts/tests/test_automatic_advisory_polling.py +216 -0
  185. package/scripts/tests/test_automatic_advisory_recovery.py +320 -0
  186. package/scripts/tests/test_automatic_advisory_repairs.py +400 -0
  187. package/scripts/tests/test_automatic_advisory_settings.py +48 -0
  188. package/scripts/tests/test_automatic_advisory_window_flags.py +163 -0
  189. package/scripts/tests/test_closed_pr_label.py +157 -0
  190. package/scripts/tests/test_codex_compat_materializer.py +189 -6
  191. package/scripts/tests/test_github_labels.py +101 -0
  192. package/scripts/tests/test_github_transport.py +125 -0
  193. package/scripts/tests/test_local_report_publisher.py +396 -0
  194. package/scripts/tests/test_local_report_publisher_cli.py +85 -0
  195. package/scripts/tests/test_local_verification.py +348 -0
  196. package/scripts/tests/test_local_verification_cli.py +98 -0
  197. package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
  198. package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
  199. package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
  200. package/scripts/tests/test_policy_lint_rules_pairing.py +163 -0
  201. package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
  202. package/scripts/tests/test_policy_lint_selection.py +25 -2
  203. package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
  204. package/scripts/tests/test_pr_verification_github.py +392 -0
  205. package/scripts/tests/test_pr_verification_lock.py +168 -0
  206. package/scripts/tests/test_rules.py +3 -7
  207. package/scripts/tests/test_verification_notice.py +347 -0
  208. package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
  209. package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
  210. package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
  211. package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
  212. package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
  213. package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
  214. package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
  215. package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
  216. package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
  217. package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
  218. package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
  219. package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
  220. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
  221. package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
  222. package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
  223. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
  224. package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
  225. package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
  226. package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
  227. package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
  228. package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
  229. package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
  230. package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
  231. package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
  232. package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
  233. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
  234. package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
  235. package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
  236. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
  237. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
  238. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
  239. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
  240. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
  241. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
  242. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
  243. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
  244. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
  245. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
  246. package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
  247. package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
  248. package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
  249. package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
  250. package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
  251. package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
  252. package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
  253. package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
  254. package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
  255. package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
  256. package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
  257. package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
  258. package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
  259. package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
  260. package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
  261. package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
  262. package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
  263. package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
  264. package/.agents/skills/autoconverge/AGENTS.md +0 -1
  265. package/.agents/skills/autoconverge/SKILL.md +0 -431
  266. package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
  267. package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
  268. package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
  269. package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
  270. package/.agents/skills/autoconverge/reference/convergence.md +0 -219
  271. package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
  272. package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
  273. package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
  274. package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
  275. package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
  276. package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
  277. package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
  278. package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
  279. package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
  280. package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
  281. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
  282. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
  283. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
  284. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
  285. package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
  286. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
  287. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
  288. package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
  289. package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
  290. package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
  291. package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
  292. package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
  293. package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
  294. package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
  295. package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
  296. package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
  297. package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
  298. package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
  299. package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
  300. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
  301. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
  302. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
  303. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
  304. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
  305. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
  306. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
  307. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
  308. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
  309. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
  310. package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
  311. package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
  312. package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
  313. package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
  314. package/.agents/skills/hitl/SKILL.md +0 -45
  315. package/.agents/skills/pr-cleanup/SKILL.md +0 -140
  316. package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
  317. package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
  318. package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
  319. package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
  320. package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
  321. package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
  322. package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
  323. package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
  324. package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
  325. package/.agents/skills/pr-refinement/SKILL.md +0 -31
  326. package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
  327. package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
  328. package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
  329. package/.agents/skills/pr-small-cl/SKILL.md +0 -52
  330. package/.agents/skills/pr-title-description/SKILL.md +0 -96
  331. package/.agents/skills/prototype/SKILL.md +0 -85
  332. package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
  333. package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
  334. package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
  335. package/.agents/skills/prototype/scripts/conftest.py +0 -15
  336. package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
  337. package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
  338. package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
  339. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  340. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
  341. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
  342. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
  343. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
  344. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
  345. package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
  346. package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
  347. package/.agents/skills/prototype/workflows/promotion.md +0 -25
  348. package/.agents/skills/prototype/workflows/sandbox.md +0 -35
  349. package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
  350. package/.agents/skills/rebase/AGENTS.md +0 -1
  351. package/.agents/skills/rebase/SKILL.md +0 -178
  352. package/.agents/skills/review-router/SKILL.md +0 -51
  353. package/.agents/skills/review-router/reference/route-policy.json +0 -7
  354. package/.agents/skills/review-router/reference/task-seeds.md +0 -1
  355. package/.agents/skills/review-router/scripts/conftest.py +0 -4
  356. package/.agents/skills/review-router/scripts/review_router.py +0 -50
  357. package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
  358. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
  359. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  360. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
  361. package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
  362. package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
  363. package/.agents/skills/review-tier/SKILL.md +0 -56
  364. package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
  365. package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
  366. package/.agents/skills/review-tier/scripts/conftest.py +0 -4
  367. package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
  368. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
  369. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  370. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
  371. package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
  372. package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
  373. package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
  374. package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
  375. package/.agents/skills/session-log/AGENTS.md +0 -1
  376. package/.agents/skills/session-log/SKILL.md +0 -170
  377. package/.agents/skills/session-log/templates/frontmatter.md +0 -40
  378. package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
  379. package/.agents/skills/session-tidy/AGENTS.md +0 -1
  380. package/.agents/skills/session-tidy/SKILL.md +0 -191
  381. package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
  382. package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
  383. package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
  384. package/.agents/skills/skill-builder/references/description-field.md +0 -111
  385. package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
  386. package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
  387. package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
  388. package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
  389. package/.agents/skills/skill-builder/references/skill-types.md +0 -232
  390. package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
  391. package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
  392. package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
  393. package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
  394. package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
  395. package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
  396. package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
  397. package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
  398. package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
  399. package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
  400. package/.agents/skills/update/.claude/CLAUDE.md +0 -1
  401. package/.agents/skills/update/AGENTS.md +0 -1
  402. package/.agents/skills/update/SKILL.md +0 -190
  403. package/_shared/pr-loop/AGENTS.md +0 -1
  404. package/_shared/pr-loop/audit-contract.md +0 -183
  405. package/_shared/pr-loop/audit-reply-template.md +0 -151
  406. package/_shared/pr-loop/code-rules-gate.md +0 -65
  407. package/_shared/pr-loop/fix-protocol.md +0 -60
  408. package/_shared/pr-loop/gh-payloads.md +0 -75
  409. package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
  410. package/_shared/pr-loop/precatch-rubric.md +0 -66
  411. package/_shared/pr-loop/preflight-proposal.md +0 -56
  412. package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
  413. package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
  414. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
  415. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
  416. package/_shared/pr-loop/state-schema.md +0 -45
  417. package/_shared/pr-loop/worker-spawn.md +0 -189
  418. package/hooks/blocking/ask_user_question_shape_blocker.py +0 -83
  419. package/hooks/blocking/bot_mention_comment_blocker.py +0 -76
  420. package/hooks/blocking/claude_md_orphan_file_blocker.py +0 -129
  421. package/hooks/blocking/cursor_cli_python_misfire_blocker.py +0 -108
  422. package/hooks/blocking/destructive_command_blocker.py +0 -2073
  423. package/hooks/blocking/fable_spawn_gate.py +0 -187
  424. package/hooks/blocking/luna_fast_mode_gate.py +0 -160
  425. package/hooks/blocking/nas_ssh_binary_enforcer.py +0 -163
  426. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +0 -260
  427. package/hooks/blocking/package_inventory_stale_blocker.py +0 -107
  428. package/hooks/blocking/pii_prevention_blocker.py +0 -365
  429. package/hooks/blocking/piped_pytest_blocker.py +0 -1223
  430. package/hooks/blocking/question_to_user_enforcer.py +0 -130
  431. package/hooks/blocking/send_user_file_open_locally_blocker.py +0 -71
  432. package/hooks/blocking/sensitive_file_protector.py +0 -167
  433. package/hooks/blocking/session_edit_stage_gate.py +0 -684
  434. package/hooks/blocking/session_handoff_blocker.py +0 -182
  435. package/hooks/blocking/shell_substitution_blocker.py +0 -350
  436. package/hooks/blocking/test_ask_user_question_shape.py +0 -69
  437. package/hooks/blocking/test_bot_mention_comment_blocker.py +0 -131
  438. package/hooks/blocking/test_claude_md_orphan_file_blocker.py +0 -1277
  439. package/hooks/blocking/test_cursor_cli_python_misfire_blocker.py +0 -208
  440. package/hooks/blocking/test_destructive_command_blocker.py +0 -2024
  441. package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +0 -105
  442. package/hooks/blocking/test_destructive_command_blocker_no_verify.py +0 -122
  443. package/hooks/blocking/test_destructive_command_blocker_patterns.py +0 -133
  444. package/hooks/blocking/test_fable_spawn_gate.py +0 -381
  445. package/hooks/blocking/test_inventory_deadlock_resolution.py +0 -154
  446. package/hooks/blocking/test_luna_fast_mode_gate.py +0 -211
  447. package/hooks/blocking/test_nas_ssh_binary_enforcer.py +0 -225
  448. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +0 -231
  449. package/hooks/blocking/test_package_inventory_stale_blocker.py +0 -382
  450. package/hooks/blocking/test_pii_prevention_blocker.py +0 -1222
  451. package/hooks/blocking/test_pii_prevention_windows_git_path.py +0 -41
  452. package/hooks/blocking/test_pii_write_surface_ephemeral.py +0 -221
  453. package/hooks/blocking/test_piped_pytest_blocker.py +0 -587
  454. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -233
  455. package/hooks/blocking/test_question_to_user_enforcer.py +0 -169
  456. package/hooks/blocking/test_send_user_file_open_locally_blocker.py +0 -118
  457. package/hooks/blocking/test_sensitive_file_protector.py +0 -365
  458. package/hooks/blocking/test_session_edit_stage_gate.py +0 -537
  459. package/hooks/blocking/test_session_edit_stage_gate_staging.py +0 -64
  460. package/hooks/blocking/test_session_edit_stage_gate_support.py +0 -204
  461. package/hooks/blocking/test_session_edit_stage_gate_tracker.py +0 -116
  462. package/hooks/blocking/test_session_handoff_blocker.py +0 -318
  463. package/hooks/blocking/test_shell_substitution_blocker.py +0 -298
  464. package/hooks/blocking/test_unscoped_search_blocker.py +0 -102
  465. package/hooks/blocking/test_write_existing_file_blocker.py +0 -122
  466. package/hooks/blocking/tests/test_pii_prevention_blocker.py +0 -259
  467. package/hooks/blocking/unscoped_search_blocker.py +0 -391
  468. package/hooks/blocking/write_existing_file_blocker.py +0 -124
  469. package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +0 -19
  470. package/hooks/hooks_constants/cursor_cli_python_misfire_blocker_constants.py +0 -79
  471. package/hooks/hooks_constants/fable_spawn_gate_constants.py +0 -61
  472. package/hooks/hooks_constants/luna_fast_mode_gate_constants.py +0 -45
  473. package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +0 -20
  474. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +0 -57
  475. package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +0 -17
  476. package/hooks/hooks_constants/sensitive_file_protector_constants.py +0 -40
  477. package/hooks/hooks_constants/session_handoff_blocker_constants.py +0 -10
  478. package/hooks/hooks_constants/shell_substitution_blocker_constants.py +0 -80
  479. /package/.agents/skills/{_shared/pr-loop → session-continuity}/AGENTS.md +0 -0
  480. /package/{_shared/pr-loop → .agents/skills/session-continuity}/CLAUDE.md +0 -0
package/bin/install.mjs CHANGED
@@ -11,7 +11,10 @@ import {
11
11
  KNOWN_GIT_HOOK_NAMES,
12
12
  } from './git_hooks_installer.mjs';
13
13
  import { installMypyIniForClaudeHooks } from './install_mypy_ini.mjs';
14
- import { expandHomeDirectoryTokensInSettings } from './expand_home_directory_tokens.mjs';
14
+ import {
15
+ expandHomeDirectoryTokens,
16
+ expandHomeDirectoryTokensInSettings,
17
+ } from './expand_home_directory_tokens.mjs';
15
18
  import { EVER_SHIPPED_SKILL_NAMES } from './ever-shipped-skills.mjs';
16
19
  import {
17
20
  managedDenyEntriesFromPackageSettings,
@@ -35,6 +38,11 @@ import {
35
38
  WINDOWS_PYTHON_LAUNCHER_COMMAND,
36
39
  PYTHON_PROBE_TIMEOUT_MILLISECONDS,
37
40
  } from './install-constants.mjs';
41
+ import {
42
+ PSTACK_PLUGIN_DIRECTORY_NAME,
43
+ PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH,
44
+ refreshPstackPluginManifest,
45
+ } from '../scripts/refresh_pstack_plugin_skills.mjs';
38
46
  import {
39
47
  resolveInstallRoot,
40
48
  parseExplicitTargetFromArgv,
@@ -170,7 +178,6 @@ export const CORE_INCLUDE_DIRECTORIES = [
170
178
  export const CORE_SKILLS = [
171
179
  'orchestrator', 'orchestrator-refresh', 'team-advisor',
172
180
  'grok-spawn',
173
- 'pr-small-cl',
174
181
  'everything-search',
175
182
  'test-runner',
176
183
  'privacy-hygiene',
@@ -316,10 +323,6 @@ export const INSTALL_GROUPS = {
316
323
  includeAllHooks: true,
317
324
  includeCodexRules: true,
318
325
  },
319
- journal: {
320
- description: 'Session logging and memory',
321
- skills: ['session-log', 'session-tidy'],
322
- },
323
326
  ...dependencyDiscovery.groups,
324
327
  };
325
328
 
@@ -429,6 +432,26 @@ export function runCursorRuleSync(pythonCommand, scriptPath, claudeRoot, cursorR
429
432
  );
430
433
  }
431
434
 
435
+ /**
436
+ * Refresh the pstack plugin manifest Claude Code reads, when this home has one.
437
+ *
438
+ * Claude Code loads a skills-directory folder as a plugin when it holds
439
+ * `.claude-plugin/plugin.json`, and namespaces its skills as `pstack:how`.
440
+ * Without the manifest, a folder of skill folders loads nothing.
441
+ *
442
+ * @param {string} [skillsRoot] The skills root to read. Defaults to the root
443
+ * this run installs into.
444
+ * @returns {string|null} The manifest path when this run wrote it, else null.
445
+ */
446
+ export function refreshInstalledPstackPluginManifest(
447
+ skillsRoot = join(CLAUDE_HOME, MANAGED_SKILLS_DIRECTORY_NAME),
448
+ ) {
449
+ const pluginRoot = join(skillsRoot, PSTACK_PLUGIN_DIRECTORY_NAME);
450
+ const outcome = refreshPstackPluginManifest(pluginRoot);
451
+ if (!outcome.didWrite) return null;
452
+ return join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
453
+ }
454
+
432
455
  function seedCodexPstackPreferences() {
433
456
  const preferenceDirectory = join(AGENTS_HOME, CURSOR_RULES_DIRECTORY_NAME);
434
457
  const preferencePath = join(
@@ -1211,6 +1234,35 @@ export const RETIRED_HOOK_REGISTRATION_RELATIVE_PATHS = new Set([
1211
1234
  'session/untracked_repo_detector.py',
1212
1235
  'session/gh_pr_author_session_cleanup.py',
1213
1236
  'observability/pr_description_writer_spawn_tracker.py',
1237
+ 'blocking/plain_language_blocker.py',
1238
+ 'lifecycle/config_change_guard.py',
1239
+ 'blocking/docstring_rule_gate_count_blocker.py',
1240
+ 'blocking/write_existing_file_blocker.py',
1241
+ 'blocking/sensitive_file_protector.py',
1242
+ 'blocking/pii_prevention_blocker.py',
1243
+ 'blocking/duplicate_rmtree_helper_blocker.py',
1244
+ 'blocking/claude_md_orphan_file_blocker.py',
1245
+ 'blocking/package_inventory_stale_blocker.py',
1246
+ 'blocking/env_var_table_code_drift_blocker.py',
1247
+ 'blocking/pytest_testpaths_orphan_blocker.py',
1248
+ 'blocking/destructive_command_blocker.py',
1249
+ 'blocking/shell_substitution_blocker.py',
1250
+ 'blocking/piped_pytest_blocker.py',
1251
+ 'blocking/cursor_cli_python_misfire_blocker.py',
1252
+ 'blocking/unscoped_search_blocker.py',
1253
+ 'blocking/nas_ssh_binary_enforcer.py',
1254
+ 'blocking/block_main_commit.py',
1255
+ 'blocking/session_edit_stage_gate.py',
1256
+ 'blocking/bash_pre_tool_use_dispatcher.py',
1257
+ 'blocking/stop_dispatcher.py',
1258
+ 'blocking/bot_mention_comment_blocker.py',
1259
+ 'blocking/fable_spawn_gate.py',
1260
+ 'blocking/luna_fast_mode_gate.py',
1261
+ 'blocking/orchestrator_refresh_reschedule_gate.py',
1262
+ 'blocking/ask_user_question_shape_blocker.py',
1263
+ 'blocking/send_user_file_open_locally_blocker.py',
1264
+ 'blocking/question_to_user_enforcer.py',
1265
+ 'blocking/session_handoff_blocker.py',
1214
1266
  ]);
1215
1267
 
1216
1268
  /**
@@ -1324,15 +1376,26 @@ export function commandReferencesManagedHook(commandString, managedHookRelativeP
1324
1376
  * @returns {boolean} True when the managed tail ends at a path boundary.
1325
1377
  */
1326
1378
  function commandTailEndsAtManagedHook(normalizedCommand, relativePath) {
1379
+ return commandHoldsPathAtBoundary(normalizedCommand, `/.claude/hooks/${relativePath}`);
1380
+ }
1381
+
1382
+ /**
1383
+ * Report whether a command holds one path spelling that ends at a path boundary:
1384
+ * end of string, or an argument separator (whitespace, quote, or semicolon).
1385
+ *
1386
+ * @param {string} normalizedCommand Forward-slash-normalized hook command.
1387
+ * @param {string} pathText The path spelling to look for.
1388
+ * @returns {boolean} True when the command holds that path at a boundary.
1389
+ */
1390
+ function commandHoldsPathAtBoundary(normalizedCommand, pathText) {
1327
1391
  const commandArgumentBoundary = /[\s'";]/;
1328
- const managedTail = `/.claude/hooks/${relativePath}`;
1329
- let searchStart = normalizedCommand.indexOf(managedTail);
1392
+ let searchStart = normalizedCommand.indexOf(pathText);
1330
1393
  while (searchStart !== -1) {
1331
- const characterAfterTail = normalizedCommand[searchStart + managedTail.length];
1332
- if (characterAfterTail === undefined || commandArgumentBoundary.test(characterAfterTail)) {
1394
+ const characterAfterPath = normalizedCommand[searchStart + pathText.length];
1395
+ if (characterAfterPath === undefined || commandArgumentBoundary.test(characterAfterPath)) {
1333
1396
  return true;
1334
1397
  }
1335
- searchStart = normalizedCommand.indexOf(managedTail, searchStart + 1);
1398
+ searchStart = normalizedCommand.indexOf(pathText, searchStart + 1);
1336
1399
  }
1337
1400
  return false;
1338
1401
  }
@@ -1539,29 +1602,139 @@ export function retiredManagedHookRelativePaths(
1539
1602
  return retiredRelativePaths;
1540
1603
  }
1541
1604
 
1605
+ const MANAGED_HOOK_ROOT_PATHS = [
1606
+ join(CLAUDE_HOME, MANAGED_HOOKS_DIRECTORY_NAME),
1607
+ join(AGENTS_HOME, MANAGED_HOOKS_DIRECTORY_NAME),
1608
+ INSTALL_ROOT_RESOLUTION.codexHooksInstallDirectory,
1609
+ ];
1610
+
1611
+ const CODEX_HOOKS_CONFIGURATION_FILE_NAME = 'hooks.json';
1612
+
1613
+ const CODEX_HOOKS_CONFIGURATION_PATH = join(
1614
+ dirname(INSTALL_ROOT_RESOLUTION.codexHooksInstallDirectory),
1615
+ CODEX_HOOKS_CONFIGURATION_FILE_NAME,
1616
+ );
1617
+
1542
1618
  /**
1543
- * Report whether a settings.json hook command runs one of the retired managed
1544
- * hook scripts.
1619
+ * Resolve a path through every link on it, or return null when it is missing.
1545
1620
  *
1546
- * The anchored `/.claude/hooks/<relative>` tail is the same test the merge uses to
1547
- * tell this installer's entries from a user's, so a command whose path is a
1548
- * retired tail plus a suffix stays outside the set. The inline validators-runner
1549
- * shape sits outside this test on purpose: it names no script, so no manifest
1550
- * record can retire it, and the merge writes it fresh on every run.
1621
+ * @param {string} filesystemPath The absolute path to resolve.
1622
+ * @returns {string|null} The real path, or null when nothing stands there.
1623
+ */
1624
+ function realPathOrNull(filesystemPath) {
1625
+ try {
1626
+ return realpathSync(filesystemPath);
1627
+ } catch {
1628
+ return null;
1629
+ }
1630
+ }
1631
+
1632
+ /**
1633
+ * Return comparison keys for each owned hooks directory, including its real path.
1551
1634
  *
1552
- * A command that is not a string belongs to an entry this installer never wrote —
1553
- * a hand-edited settings.json, or a third-party entry carrying another shape — so
1554
- * it names no retired script and its entry stays.
1635
+ * ~/.claude/hooks, the agents-home hooks directory, and ~/.codex/hooks can
1636
+ * point at one directory. Both the given path and its resolved path are keys.
1555
1637
  *
1556
- * @param {unknown} commandString The hook command from settings.json.
1638
+ * @param {string[]} allHookRootPaths The hooks directories this installer writes.
1639
+ * @returns {Set<string>} The comparison key of every owned spelling.
1640
+ */
1641
+ export function ownedHookRootComparisonKeys(allHookRootPaths) {
1642
+ const allRootKeys = new Set();
1643
+ for (const hookRootPath of allHookRootPaths) {
1644
+ if (!hookRootPath) continue;
1645
+ allRootKeys.add(comparisonKeyForPath(hookRootPath));
1646
+ const realHookRootPath = realPathOrNull(hookRootPath);
1647
+ if (realHookRootPath) allRootKeys.add(comparisonKeyForPath(realHookRootPath));
1648
+ }
1649
+ return allRootKeys;
1650
+ }
1651
+
1652
+ /**
1653
+ * Expand `$HOME`, `${HOME}`, `~/`, and `%USERPROFILE%` in a hook command.
1654
+ *
1655
+ * `$HOME`, `${HOME}`, and `~/` go through expandHomeDirectoryTokens, the helper
1656
+ * the settings rewrite already uses, so a tilde is not left in front of home.
1657
+ * `%USERPROFILE%` stays here because this comparison only reads a spelling.
1658
+ *
1659
+ * @param {string} normalizedCommand Forward-slash-normalized hook command.
1660
+ * @param {string} homeDirectory The absolute home directory to substitute.
1661
+ * @returns {string} The command with each home reference expanded.
1662
+ */
1663
+ function expandHomeReferencesInCommand(normalizedCommand, homeDirectory) {
1664
+ const normalizedHome = homeDirectory.replace(/\\/g, '/').replace(/\/+$/, '');
1665
+ const userProfileExpanded = normalizedCommand.replace(
1666
+ /%USERPROFILE%/gi, () => normalizedHome,
1667
+ );
1668
+ return expandHomeDirectoryTokens(userProfileExpanded, homeDirectory);
1669
+ }
1670
+
1671
+ /**
1672
+ * Build the spelling a hook command is compared through: forward-slashed, home
1673
+ * references expanded, and case-folded wherever the filesystem ignores case.
1674
+ *
1675
+ * @param {string} commandString The hook command from a host configuration file.
1676
+ * @param {string} homeDirectory The absolute home directory to substitute.
1677
+ * @returns {string} The command spelling the owned-root test reads.
1678
+ */
1679
+ export function hookCommandComparisonSpelling(commandString, homeDirectory) {
1680
+ const forwardSlashCommand = commandString.replace(/\\/g, '/');
1681
+ const expandedCommand = expandHomeReferencesInCommand(forwardSlashCommand, homeDirectory);
1682
+ return isCaseInsensitiveFilesystem() ? expandedCommand.toLowerCase() : expandedCommand;
1683
+ }
1684
+
1685
+ /**
1686
+ * Report whether a hook command runs one script under a hooks directory this
1687
+ * installer owns.
1688
+ *
1689
+ * @param {unknown} commandString The hook command from a host configuration file.
1690
+ * @param {string} relativePath The script path under a hooks directory.
1691
+ * @param {{allOwnedHookRootKeys: Set<string>, homeDirectory: string}} ownership The owned roots.
1692
+ * @returns {boolean} True when the command runs that script under an owned root.
1693
+ */
1694
+ export function commandNamesOwnedHookScript(commandString, relativePath, ownership) {
1695
+ if (typeof commandString !== 'string') return false;
1696
+ const shouldFoldCase = isCaseInsensitiveFilesystem();
1697
+ const comparisonCommand = hookCommandComparisonSpelling(
1698
+ commandString, ownership.homeDirectory,
1699
+ );
1700
+ const comparisonRelativePath = shouldFoldCase ? relativePath.toLowerCase() : relativePath;
1701
+ for (const ownedRootKey of ownership.allOwnedHookRootKeys) {
1702
+ const ownedScriptPath = `${ownedRootKey}/${comparisonRelativePath}`;
1703
+ if (commandHoldsPathAtBoundary(comparisonCommand, ownedScriptPath)) return true;
1704
+ }
1705
+ return false;
1706
+ }
1707
+
1708
+ /**
1709
+ * Build the ownership one retirement walk reads, filling in this run's own roots.
1710
+ *
1711
+ * @param {{allOwnedHookRootKeys?: Set<string>, homeDirectory?: string}} [options] Test overrides.
1712
+ * @returns {{allOwnedHookRootKeys: Set<string>, homeDirectory: string}} The ownership.
1713
+ */
1714
+ function retiredHookOwnership(options = {}) {
1715
+ return {
1716
+ allOwnedHookRootKeys: options.allOwnedHookRootKeys
1717
+ ?? ownedHookRootComparisonKeys(MANAGED_HOOK_ROOT_PATHS),
1718
+ homeDirectory: options.homeDirectory ?? homedir(),
1719
+ };
1720
+ }
1721
+
1722
+ /**
1723
+ * Report whether a host hook command runs a retired managed script under an
1724
+ * owned hooks directory.
1725
+ *
1726
+ * A path-plus-suffix, a command under another tool's tree, a non-string
1727
+ * command, and the inline validators-runner shape (no script name) are left
1728
+ * in place. The merge rewrites the validators-runner entry on every run.
1729
+ *
1730
+ * @param {unknown} commandString The hook command from a host configuration file.
1557
1731
  * @param {Set<string>} retiredHookRelativePaths Retired script paths under hooks/.
1732
+ * @param {{allOwnedHookRootKeys: Set<string>, homeDirectory: string}} ownership The owned roots.
1558
1733
  * @returns {boolean} True when the command runs a retired managed script.
1559
1734
  */
1560
- function commandReferencesRetiredHook(commandString, retiredHookRelativePaths) {
1561
- if (typeof commandString !== 'string') return false;
1562
- const normalizedCommand = commandString.replace(/\\/g, '/');
1735
+ function commandReferencesRetiredHook(commandString, retiredHookRelativePaths, ownership) {
1563
1736
  for (const relativePath of retiredHookRelativePaths) {
1564
- if (commandTailEndsAtManagedHook(normalizedCommand, relativePath)) return true;
1737
+ if (commandNamesOwnedHookScript(commandString, relativePath, ownership)) return true;
1565
1738
  }
1566
1739
  return false;
1567
1740
  }
@@ -1621,15 +1794,18 @@ function groupHookEntries(group) {
1621
1794
  *
1622
1795
  * @param {object} settings The parsed settings.json object (mutated in place).
1623
1796
  * @param {Set<string>} retiredHookRelativePaths Retired script paths under hooks/.
1797
+ * @param {{allOwnedHookRootKeys: Set<string>, homeDirectory: string}} ownership The owned roots.
1624
1798
  * @returns {number} How many hook entries were removed.
1625
1799
  */
1626
- function stripRetiredHookEntries(settings, retiredHookRelativePaths) {
1800
+ function stripRetiredHookEntries(settings, retiredHookRelativePaths, ownership) {
1627
1801
  let removedCount = 0;
1628
1802
  for (const [eventType, matcherGroups] of Object.entries(settings.hooks)) {
1629
1803
  if (!Array.isArray(matcherGroups)) continue;
1630
1804
  const eventOutcome = retainedMatcherGroups(
1631
1805
  matcherGroups,
1632
- commandString => commandReferencesRetiredHook(commandString, retiredHookRelativePaths),
1806
+ commandString => commandReferencesRetiredHook(
1807
+ commandString, retiredHookRelativePaths, ownership,
1808
+ ),
1633
1809
  );
1634
1810
  removedCount += eventOutcome.removedCount;
1635
1811
  if (eventOutcome.keptGroups.length === 0) {
@@ -1641,35 +1817,256 @@ function stripRetiredHookEntries(settings, retiredHookRelativePaths) {
1641
1817
  return removedCount;
1642
1818
  }
1643
1819
 
1820
+ const DEFAULT_HOST_CONFIGURATION_INDENT = ' ';
1821
+
1644
1822
  /**
1645
- * Remove every settings.json entry that runs a retired managed hook script,
1646
- * writing the file only when an entry left it.
1823
+ * Read the indent the host configuration file already uses.
1647
1824
  *
1648
- * A run that retires no hook leaves settings.json byte-identical, so an install
1649
- * touches the user's settings for a reason a reader can name. A settings file the
1650
- * installer cannot parse is left alone with a warning.
1825
+ * ~/.claude/settings.json uses four spaces and ~/.codex/hooks.json uses two.
1826
+ * Forcing four spaces would rewrite every line of one of those files. The first
1827
+ * indented line supplies the indent, tabs included.
1651
1828
  *
1652
- * @param {string} settingsPath The absolute settings.json path.
1829
+ * @param {string} settingsText The host configuration file as it stands on disk.
1830
+ * @returns {string} The indent one nesting level uses.
1831
+ */
1832
+ function hostConfigurationIndent(settingsText) {
1833
+ const firstIndentedLine = /\n([ \t]+)\S/.exec(settingsText);
1834
+ return firstIndentedLine ? firstIndentedLine[1] : DEFAULT_HOST_CONFIGURATION_INDENT;
1835
+ }
1836
+
1837
+ /**
1838
+ * Remove every entry of one host hook configuration file that runs a retired
1839
+ * managed hook script, writing the file only when an entry left it.
1840
+ *
1841
+ * Claude settings.json and Codex hooks.json share this hook shape. A run that
1842
+ * retires no hook leaves the file byte-identical. A file the installer cannot
1843
+ * read or cannot parse is left alone with a warning, and the prune continues
1844
+ * with the other host configuration file.
1845
+ *
1846
+ * @param {string} settingsPath The absolute host configuration path.
1653
1847
  * @param {Set<string>} retiredHookRelativePaths Retired script paths under hooks/.
1848
+ * @param {{allOwnedHookRootKeys?: Set<string>, homeDirectory?: string}} [options] Test overrides.
1654
1849
  * @returns {number} How many hook entries were removed.
1655
1850
  */
1656
- export function pruneRetiredHookEntriesFromSettings(settingsPath, retiredHookRelativePaths) {
1851
+ export function pruneRetiredHookEntriesFromSettings(
1852
+ settingsPath, retiredHookRelativePaths, options = {},
1853
+ ) {
1657
1854
  if (retiredHookRelativePaths.size === 0) return 0;
1658
1855
  if (!existsSync(settingsPath)) return 0;
1856
+ let settingsText;
1659
1857
  let settings;
1660
1858
  try {
1661
- settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
1662
- } catch (parseError) {
1663
- console.warn(` Warning: leaving settings.json as it stands — the file holds JSON the installer cannot read (${parseError.message})`);
1859
+ settingsText = readFileSync(settingsPath, 'utf8');
1860
+ settings = JSON.parse(settingsText);
1861
+ } catch (readError) {
1862
+ console.warn(` Warning: leaving ${basename(settingsPath)} as it stands — the installer cannot read the file as JSON (${readError.message})`);
1664
1863
  return 0;
1665
1864
  }
1666
1865
  if (!settings.hooks || typeof settings.hooks !== 'object') return 0;
1667
- const removedCount = stripRetiredHookEntries(settings, retiredHookRelativePaths);
1866
+ const removedCount = stripRetiredHookEntries(
1867
+ settings, retiredHookRelativePaths, retiredHookOwnership(options),
1868
+ );
1668
1869
  if (removedCount === 0) return 0;
1669
- writeFileSync(settingsPath, JSON.stringify(settings, null, 4) + '\n');
1870
+ const settingsIndent = hostConfigurationIndent(settingsText);
1871
+ writeFileSync(settingsPath, JSON.stringify(settings, null, settingsIndent) + '\n');
1670
1872
  return removedCount;
1671
1873
  }
1672
1874
 
1875
+ const RETIRED_HOOK_STAND_IN_MARKER = 'claude-dev-env retired hook stand-in';
1876
+
1877
+ const RETIRED_HOOK_STAND_IN_SOURCE = `"""${RETIRED_HOOK_STAND_IN_MARKER}.
1878
+
1879
+ This install retired the hook that stood here and removed its settings.json
1880
+ registration. A session that started before the install still holds the old
1881
+ registration and runs this path on every matching tool call, so the path keeps an
1882
+ inert module: it does nothing and exits 0, which the harness reads as "allow".
1883
+ Restart the session to pick up the new registration. A later install removes this
1884
+ file once no registration names it.
1885
+ """
1886
+ `;
1887
+
1888
+ const PYTHON_HOOK_SUFFIX = '.py';
1889
+
1890
+
1891
+ /**
1892
+ * List every hook command a settings object holds, across every event type.
1893
+ *
1894
+ * A group with no hooks array contributes nothing. An entry whose command is
1895
+ * absent or is not a string still reaches the caller.
1896
+ *
1897
+ * @param {object} settings The parsed settings.json object.
1898
+ * @returns {unknown[]} Every hook entry's command, in the order settings holds them.
1899
+ */
1900
+ function settingsHookCommands(settings) {
1901
+ const allCommands = [];
1902
+ for (const matcherGroups of Object.values(settings.hooks ?? {})) {
1903
+ if (!Array.isArray(matcherGroups)) continue;
1904
+ for (const group of matcherGroups) {
1905
+ for (const hook of groupHookEntries(group) ?? []) allCommands.push(hook?.command);
1906
+ }
1907
+ }
1908
+ return allCommands;
1909
+ }
1910
+
1911
+
1912
+ /**
1913
+ * Read the hook commands one host configuration file holds, or none when it
1914
+ * cannot be read. Claude settings.json and Codex hooks.json share this shape.
1915
+ *
1916
+ * Call this before the run mutates settings. The hook merge rewrites
1917
+ * settings.json early, and by prune time the file no longer names what a
1918
+ * session started before this install still invokes.
1919
+ *
1920
+ * @param {string} settingsPath The absolute host configuration path.
1921
+ * @returns {unknown[]} Every hook command the file holds.
1922
+ */
1923
+ export function settingsHookCommandsAtPath(settingsPath) {
1924
+ if (!existsSync(settingsPath)) return [];
1925
+ let settings;
1926
+ try {
1927
+ settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
1928
+ } catch {
1929
+ return [];
1930
+ }
1931
+ if (!settings?.hooks || typeof settings.hooks !== 'object') return [];
1932
+ return settingsHookCommands(settings);
1933
+ }
1934
+
1935
+
1936
+ /**
1937
+ * Name the retired hook scripts a stale registration still runs.
1938
+ *
1939
+ * The commands come from the reading taken before the run mutated settings.json.
1940
+ * Those paths are where a stand-in belongs.
1941
+ *
1942
+ * @param {unknown[]} allCommands Hook commands read before the run mutated settings.
1943
+ * @param {Set<string>} retiredHookRelativePaths Retired script paths under hooks/.
1944
+ * @param {{allOwnedHookRootKeys?: Set<string>, homeDirectory?: string}} [options] Test overrides.
1945
+ * @returns {Set<string>} The retired paths a command names.
1946
+ */
1947
+ export function retiredHookPathsNamedByCommands(
1948
+ allCommands, retiredHookRelativePaths, options = {},
1949
+ ) {
1950
+ const ownership = retiredHookOwnership(options);
1951
+ const namedPaths = new Set();
1952
+ for (const relativePath of retiredHookRelativePaths) {
1953
+ const namesThisPath = allCommands.some(
1954
+ command => commandNamesOwnedHookScript(command, relativePath, ownership),
1955
+ );
1956
+ if (namesThisPath) namedPaths.add(relativePath);
1957
+ }
1958
+ return namedPaths;
1959
+ }
1960
+
1961
+
1962
+ /**
1963
+ * Write an inert Python module at each retired hook path a stale registration
1964
+ * still runs.
1965
+ *
1966
+ * Deleting a registered script makes python3 exit 2. The harness treats a
1967
+ * PreToolUse exit 2 as a block, so every matching tool call is denied until
1968
+ * the session restarts. An inert module with one docstring exits 0, which the
1969
+ * harness treats as allow.
1970
+ *
1971
+ * An existing file is left in place. Each written path is recorded in the
1972
+ * manifest so a later install removes the stand-in once no registration names
1973
+ * it.
1974
+ *
1975
+ * @param {string} hooksRoot The absolute installed hooks directory.
1976
+ * @param {Set<string>} registeredRetiredRelativePaths Retired paths a registration names.
1977
+ * @returns {string[]} The absolute paths this run wrote a stand-in to.
1978
+ */
1979
+ export function writeRetiredHookStandIns(hooksRoot, registeredRetiredRelativePaths) {
1980
+ const writtenPaths = [];
1981
+ for (const relativePath of registeredRetiredRelativePaths) {
1982
+ if (!relativePath.endsWith(PYTHON_HOOK_SUFFIX)) continue;
1983
+ const standInPath = join(hooksRoot, ...relativePath.split('/'));
1984
+ if (existsSync(standInPath)) continue;
1985
+ mkdirSync(dirname(standInPath), { recursive: true });
1986
+ writeFileSync(standInPath, RETIRED_HOOK_STAND_IN_SOURCE);
1987
+ writtenPaths.push(standInPath);
1988
+ }
1989
+ return writtenPaths;
1990
+ }
1991
+
1992
+
1993
+ /**
1994
+ * List owned hooks directories that exist, one path per distinct real directory.
1995
+ *
1996
+ * On a healthy install the three roots are pointers to one directory. Keying
1997
+ * on the real path writes the stand-in once. A missing root is skipped.
1998
+ *
1999
+ * @param {string[]} allHookRootPaths The hooks directories this installer writes.
2000
+ * @returns {string[]} One path per distinct hooks directory that exists.
2001
+ */
2002
+ function distinctExistingHookRoots(allHookRootPaths) {
2003
+ const allRootKeys = new Set();
2004
+ const allDistinctRoots = [];
2005
+ for (const hookRootPath of allHookRootPaths) {
2006
+ const realHookRootPath = realPathOrNull(hookRootPath);
2007
+ if (!realHookRootPath) continue;
2008
+ const rootKey = comparisonKeyForPath(realHookRootPath);
2009
+ if (allRootKeys.has(rootKey)) continue;
2010
+ allRootKeys.add(rootKey);
2011
+ allDistinctRoots.push(hookRootPath);
2012
+ }
2013
+ return allDistinctRoots;
2014
+ }
2015
+
2016
+ /**
2017
+ * Leave an inert stand-in at each retired path under every owned hooks directory.
2018
+ *
2019
+ * @param {string[]} allHookRootPaths The hooks directories this installer writes.
2020
+ * @param {Set<string>} registeredRetiredRelativePaths Retired paths a registration names.
2021
+ * @returns {string[]} The absolute paths this run wrote a stand-in to.
2022
+ */
2023
+ function writeRetiredHookStandInsAcrossOwnedRoots(
2024
+ allHookRootPaths, registeredRetiredRelativePaths,
2025
+ ) {
2026
+ const allWrittenPaths = [];
2027
+ for (const hooksRoot of distinctExistingHookRoots(allHookRootPaths)) {
2028
+ allWrittenPaths.push(
2029
+ ...writeRetiredHookStandIns(hooksRoot, registeredRetiredRelativePaths),
2030
+ );
2031
+ }
2032
+ return allWrittenPaths;
2033
+ }
2034
+
2035
+ /**
2036
+ * Name each retired registration a stand-in now fills, by its path under hooks/.
2037
+ *
2038
+ * @param {string[]} allHookRootPaths The hooks directories this installer writes.
2039
+ * @param {string[]} standInPaths The stand-ins this run wrote.
2040
+ * @returns {string[]} The sorted relative names, one per registration.
2041
+ */
2042
+ function standInRegistrationNames(allHookRootPaths, standInPaths) {
2043
+ const allNames = new Set();
2044
+ for (const standInPath of standInPaths) {
2045
+ const owningRoot = allHookRootPaths.find(
2046
+ hookRootPath => isInsideDirectory(resolve(standInPath), resolve(hookRootPath)),
2047
+ );
2048
+ if (!owningRoot) continue;
2049
+ allNames.add(relative(owningRoot, standInPath).replace(/\\/g, '/'));
2050
+ }
2051
+ return [...allNames].sort();
2052
+ }
2053
+
2054
+ /**
2055
+ * Print the one-line restart notice naming the registrations this run retired.
2056
+ *
2057
+ * @param {string[]} allHookRootPaths The hooks directories this installer writes.
2058
+ * @param {string[]} standInPaths The stand-ins this run wrote.
2059
+ * @returns {void}
2060
+ */
2061
+ function reportRetiredHookStandIns(allHookRootPaths, standInPaths) {
2062
+ if (standInPaths.length === 0) return;
2063
+ const allNames = standInRegistrationNames(allHookRootPaths, standInPaths);
2064
+ console.log(
2065
+ ` Restart open sessions: this install retired ${allNames.join(', ')} and left an inert stand-in at each path, so a session started before it keeps working until you restart it.`,
2066
+ );
2067
+ }
2068
+
2069
+
1673
2070
  /**
1674
2071
  * Load ~/.claude/settings.json for an in-place merge, or `{}` when absent/empty.
1675
2072
  *
@@ -1975,46 +2372,78 @@ function pruneStaleFilesAcrossManagedRoots(priorInstalledFiles, currentInstalled
1975
2372
  return { prunedCount, skillsPrunedCount, failedPaths };
1976
2373
  }
1977
2374
 
2375
+ /**
2376
+ * Remove every retired registration from each host hook configuration file, and
2377
+ * report each file that lost one.
2378
+ *
2379
+ * Claude Code reads ~/.claude/settings.json and Codex reads ~/.codex/hooks.json.
2380
+ * A session of either host holds the registrations its own file named at start,
2381
+ * so a retirement that reaches only one file leaves the other host's open session
2382
+ * running a script this install moved.
2383
+ *
2384
+ * @param {string} settingsPath The absolute settings.json path.
2385
+ * @param {Set<string>} retiredHookPaths Retired script paths under hooks/.
2386
+ * @returns {void}
2387
+ */
2388
+ function pruneRetiredHookRegistrations(settingsPath, retiredHookPaths) {
2389
+ const allConfigurationPaths = [settingsPath, CODEX_HOOKS_CONFIGURATION_PATH];
2390
+ for (const configurationPath of allConfigurationPaths) {
2391
+ const removedCount = pruneRetiredHookEntriesFromSettings(
2392
+ configurationPath, retiredHookPaths,
2393
+ );
2394
+ if (removedCount === 0) continue;
2395
+ console.log(
2396
+ ` Hook entries: ${removedCount} retired entry(s) removed from ${basename(configurationPath)}`,
2397
+ );
2398
+ }
2399
+ }
2400
+
1978
2401
  /**
1979
2402
  * Run every prune a full install performs, in the order that keeps ~/.claude
1980
2403
  * consistent at each step.
1981
2404
  *
1982
- * The settings entries of retired hooks go before the file move: a settings.json
1983
- * naming a hook script that has already left ~/.claude/hooks makes every session
1984
- * start invoke a missing script, so the reference leaves first and the script
1985
- * follows.
1986
- *
1987
- * Both prunes report how much content reached the run's backup root, and their sum
1988
- * is what backup retention answers to: the sweep of older recovery points runs
1989
- * only once this run holds one of its own.
2405
+ * Retired hook entries leave settings before the script is moved, so a live
2406
+ * session does not invoke a missing file. Stand-ins are written last, after
2407
+ * the move has emptied those paths. Paths come from `priorSettingsHookCommands`,
2408
+ * read before the run mutated settings. The caller records each stand-in in
2409
+ * the manifest so a later install removes it once no registration names it.
1990
2410
  *
1991
2411
  * @param {Set<string>} copiedSkillNames Skill directory names this run wrote.
1992
2412
  * @param {string[]|null} priorManifestSkills The prior manifest's skill names, or null.
1993
2413
  * @param {string[]|null} priorManifestFiles The prior manifest's file list, or null.
1994
2414
  * @param {string[]} installedFiles Every file this run copied.
1995
- * @returns {{prunedCount: number, skillsPrunedCount: number, failedPaths: string[]}}
1996
- * The stale files moved across all roots, the skills root's share, and every
1997
- * path whose move failed.
2415
+ * @param {unknown[]} priorSettingsHookCommands Hook commands read before this run mutated settings.
2416
+ * @returns {{prunedCount: number, skillsPrunedCount: number, failedPaths: string[],
2417
+ * standInPaths: string[]}} The stale files moved across all roots, the skills
2418
+ * root's share, every path whose move failed, and every stand-in written.
1998
2419
  */
1999
2420
  function runFullInstallPrunes(
2000
- copiedSkillNames, priorManifestSkills, priorManifestFiles, installedFiles,
2421
+ copiedSkillNames,
2422
+ priorManifestSkills,
2423
+ priorManifestFiles,
2424
+ installedFiles,
2425
+ priorSettingsHookCommands,
2001
2426
  ) {
2427
+ const hooksRoot = join(CLAUDE_HOME, MANAGED_HOOKS_DIRECTORY_NAME);
2428
+ const settingsPath = join(CLAUDE_HOME, SETTINGS_FILE_NAME);
2002
2429
  const retiredSkillMovedCount = pruneRetiredSkills(copiedSkillNames, priorManifestSkills);
2003
- const removedHookEntryCount = pruneRetiredHookEntriesFromSettings(
2004
- join(CLAUDE_HOME, SETTINGS_FILE_NAME),
2005
- retiredManagedHookRelativePaths(
2006
- priorManifestFiles, installedFiles, join(CLAUDE_HOME, MANAGED_HOOKS_DIRECTORY_NAME),
2007
- ),
2430
+ const retiredHookPaths = retiredManagedHookRelativePaths(
2431
+ priorManifestFiles, installedFiles, hooksRoot,
2008
2432
  );
2009
- if (removedHookEntryCount > 0) {
2010
- console.log(` Hook entries: ${removedHookEntryCount} retired entry(s) removed from settings.json`);
2011
- }
2433
+ const registeredRetiredPaths = retiredHookPathsNamedByCommands(
2434
+ priorSettingsHookCommands, retiredHookPaths,
2435
+ );
2436
+ pruneRetiredHookRegistrations(settingsPath, retiredHookPaths);
2012
2437
  const staleOutcome = pruneStaleFilesAcrossManagedRoots(
2013
2438
  priorManifestFiles, installedFiles, currentRunBackupRoot(),
2014
2439
  );
2440
+ const standInPaths = writeRetiredHookStandInsAcrossOwnedRoots(
2441
+ MANAGED_HOOK_ROOT_PATHS, registeredRetiredPaths,
2442
+ );
2443
+ reportRetiredHookStandIns(MANAGED_HOOK_ROOT_PATHS, standInPaths);
2015
2444
  const didRunMoveContent = retiredSkillMovedCount + staleOutcome.prunedCount > 0;
2016
2445
  retainNewestRunBackupOnly(currentRunBackupRoot(), didRunMoveContent);
2017
- return staleOutcome;
2446
+ return { ...staleOutcome, standInPaths };
2018
2447
  }
2019
2448
 
2020
2449
  /**
@@ -2115,6 +2544,10 @@ function executeInstallPlan(plan) {
2115
2544
  */
2116
2545
  function executeInstallPlanMutations(plan, transactionHelpers) {
2117
2546
  const { throwIfFault, syncWrittenPaths } = transactionHelpers;
2547
+ const priorSettingsHookCommands = [
2548
+ ...settingsHookCommandsAtPath(join(CLAUDE_HOME, SETTINGS_FILE_NAME)),
2549
+ ...settingsHookCommandsAtPath(CODEX_HOOKS_CONFIGURATION_PATH),
2550
+ ];
2118
2551
  const selectedGroups = plan.selectedGroups;
2119
2552
  const priorManifestFiles = plan.priorManifest.files;
2120
2553
  const priorManifestSkills = plan.priorManifest.skills;
@@ -2320,6 +2753,9 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
2320
2753
  }
2321
2754
  summary.skills = { created: skillsCreated, updated: skillsUpdated, pruned: 0, paths: skillPaths };
2322
2755
  allInstalledFiles.push(...skillPaths);
2756
+ const pstackManifestPath = refreshInstalledPstackPluginManifest();
2757
+ if (pstackManifestPath) allInstalledFiles.push(pstackManifestPath);
2758
+ summary.pstackPlugin = { manifestPath: pstackManifestPath };
2323
2759
  syncWrittenPaths([...allInstalledFiles, ...publishedPointerPaths]);
2324
2760
  throwIfFault(FAULT_PHASES.AFTER_FILE_STAGING);
2325
2761
  throwIfFault(FAULT_PHASES.BEFORE_DURABLE_PROMOTION);
@@ -2459,11 +2895,16 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
2459
2895
  if (didPruneRun) {
2460
2896
  try {
2461
2897
  const prunes = runFullInstallPrunes(
2462
- copiedSkillNames, priorManifestSkills, priorManifestFiles, allInstalledFiles,
2898
+ copiedSkillNames,
2899
+ priorManifestSkills,
2900
+ priorManifestFiles,
2901
+ allInstalledFiles,
2902
+ priorSettingsHookCommands,
2463
2903
  );
2464
2904
  summary.skills.pruned = prunes.skillsPrunedCount;
2465
2905
  stalePrunedTotal = prunes.prunedCount;
2466
2906
  failedPrunePaths = prunes.failedPaths;
2907
+ allInstalledFiles.push(...prunes.standInPaths);
2467
2908
  didPruneFinish = true;
2468
2909
  } catch (pruneError) {
2469
2910
  console.warn(
@@ -2807,7 +3248,6 @@ ${groupLines}
2807
3248
 
2808
3249
  Examples:
2809
3250
  npx ${PACKAGE_NAME} --only core
2810
- npx ${PACKAGE_NAME} --only core,journal
2811
3251
  npx ${PACKAGE_NAME} --profile editor --only core
2812
3252
  npx ${PACKAGE_NAME} --profiles editor,mel --only core
2813
3253
 
@@ -2817,6 +3257,9 @@ Named profiles resolve under LLM_SETTINGS_PROFILES_ROOT or ~/.claude-profiles/<d
2817
3257
  Codex exec-policy files copy into ~/.codex/rules, or CODEX_HOME/rules when CODEX_HOME is set.
2818
3258
  Cursor rule files generate into ~/.cursor/rules as stem-named mdc files, one per Claude rule.
2819
3259
 
3260
+ The installer reads only flags. A bare path argument carries no meaning, so
3261
+ npx ${PACKAGE_NAME} . lands in the same root as a run with no argument.
3262
+
2820
3263
  Root precedence: --target > CLAUDE_CONFIG_DIR > ~/.claude
2821
3264
  Profile selection (--profile/--profiles) is mutually exclusive with --target.
2822
3265
  Target selection finishes before any mutation. Each target writes its own manifest