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
@@ -0,0 +1,128 @@
1
+ import { test } from 'node:test';
2
+ import { strict as assert } from 'node:assert';
3
+
4
+ import {
5
+ expandHomeDirectoryTokens,
6
+ expandHomeDirectoryTokensInSettings,
7
+ } from './expand_home_directory_tokens.mjs';
8
+
9
+
10
+ test('expandHomeDirectoryTokens expands $HOME, ${HOME}, and ~/', () => {
11
+ assert.equal(
12
+ expandHomeDirectoryTokens(
13
+ 'python $HOME/.claude/hooks/session/fix_worktree_hookspath.py',
14
+ 'C:\\Users\\x',
15
+ ),
16
+ 'python C:/Users/x/.claude/hooks/session/fix_worktree_hookspath.py',
17
+ );
18
+ assert.equal(
19
+ expandHomeDirectoryTokens('python ${HOME}/.claude/hooks/a.py', '/home/x'),
20
+ 'python /home/x/.claude/hooks/a.py',
21
+ );
22
+ assert.equal(
23
+ expandHomeDirectoryTokens('python ~/.claude/hooks/a.py', '/home/x'),
24
+ 'python /home/x/.claude/hooks/a.py',
25
+ );
26
+ assert.equal(
27
+ expandHomeDirectoryTokens('echo $HOMEPATH', 'C:/Users/x'),
28
+ 'echo $HOMEPATH',
29
+ );
30
+ });
31
+
32
+
33
+ test('expandHomeDirectoryTokens inserts dollar characters in home paths literally', () => {
34
+ const homeWithReplaceMetacharacters = 'C:/Users/$&evil$1';
35
+ assert.equal(
36
+ expandHomeDirectoryTokens('python $HOME/.claude/a.py', homeWithReplaceMetacharacters),
37
+ 'python C:/Users/$&evil$1/.claude/a.py',
38
+ );
39
+ assert.equal(
40
+ expandHomeDirectoryTokens('python ${HOME}/.claude/a.py', homeWithReplaceMetacharacters),
41
+ 'python C:/Users/$&evil$1/.claude/a.py',
42
+ );
43
+ assert.equal(
44
+ expandHomeDirectoryTokens('python ~/.claude/a.py', homeWithReplaceMetacharacters),
45
+ 'python C:/Users/$&evil$1/.claude/a.py',
46
+ );
47
+ });
48
+
49
+
50
+ test('expandHomeDirectoryTokens expands ${HOME} before $HOME so braces stay intact', () => {
51
+ assert.equal(
52
+ expandHomeDirectoryTokens('python ${HOME}/.claude/a.py', '/home/x'),
53
+ 'python /home/x/.claude/a.py',
54
+ );
55
+ });
56
+
57
+
58
+ test('expandHomeDirectoryTokens strips trailing slashes from the home directory', () => {
59
+ assert.equal(
60
+ expandHomeDirectoryTokens('python $HOME/.claude/a.py', 'C:/Users/x/'),
61
+ 'python C:/Users/x/.claude/a.py',
62
+ );
63
+ });
64
+
65
+
66
+ test('expandHomeDirectoryTokensInSettings skips non-array hook event values', () => {
67
+ const settings = {
68
+ hooks: {
69
+ SessionStart: 'not-an-array',
70
+ PreToolUse: [
71
+ {
72
+ matcher: 'Write',
73
+ hooks: [{ type: 'command', command: 'python $HOME/.claude/hooks/a.py' }],
74
+ },
75
+ ],
76
+ },
77
+ };
78
+ expandHomeDirectoryTokensInSettings(settings, 'C:/Users/x');
79
+ assert.equal(settings.hooks.SessionStart, 'not-an-array');
80
+ assert.equal(
81
+ settings.hooks.PreToolUse[0].hooks[0].command,
82
+ 'python C:/Users/x/.claude/hooks/a.py',
83
+ );
84
+ });
85
+
86
+
87
+ test('expandHomeDirectoryTokensInSettings rewrites hooks and statusLine', () => {
88
+ const settings = {
89
+ hooks: {
90
+ SessionStart: [
91
+ {
92
+ matcher: '',
93
+ hooks: [
94
+ {
95
+ type: 'command',
96
+ command: 'python $HOME/.claude/hooks/session/fix_worktree_hookspath.py',
97
+ },
98
+ ],
99
+ },
100
+ ],
101
+ },
102
+ statusLine: {
103
+ type: 'command',
104
+ command: 'python "$HOME/.claude/statusline-command.py"',
105
+ },
106
+ };
107
+ expandHomeDirectoryTokensInSettings(settings, 'C:/Users/x');
108
+ assert.equal(
109
+ settings.hooks.SessionStart[0].hooks[0].command,
110
+ 'python C:/Users/x/.claude/hooks/session/fix_worktree_hookspath.py',
111
+ );
112
+ assert.equal(
113
+ settings.statusLine.command,
114
+ 'python "C:/Users/x/.claude/statusline-command.py"',
115
+ );
116
+ });
117
+
118
+
119
+ test('expandHomeDirectoryTokens expands a tilde path that follows a semicolon or an opening parenthesis', () => {
120
+ assert.equal(
121
+ expandHomeDirectoryTokens('cd /repo;~/.claude/hooks/x.py', '/home/x'),
122
+ 'cd /repo;/home/x/.claude/hooks/x.py',
123
+ );
124
+ assert.equal(
125
+ expandHomeDirectoryTokens('(~/.claude/hooks/x.py)', '/home/x'),
126
+ '(/home/x/.claude/hooks/x.py)',
127
+ );
128
+ });
@@ -6,6 +6,7 @@ import { join } from 'node:path';
6
6
  export const KNOWN_GIT_HOOK_NAMES = Object.freeze([
7
7
  'pre-commit',
8
8
  'pre-push',
9
+ 'post-commit',
9
10
  ]);
10
11
 
11
12
  export const SHIM_DOCSTRING = (
@@ -72,6 +73,12 @@ function deriveModuleNameFromGitNativeHookName(gitNativeHookName) {
72
73
 
73
74
  function buildShimContent(pythonModuleName) {
74
75
  const shimContentHeader = '#!/usr/bin/env python3\n';
76
+ const failureMessage = JSON.stringify(
77
+ `claude-dev-env: advisory hook ${pythonModuleName} failed: `,
78
+ );
79
+ const nonzeroMessage = JSON.stringify(
80
+ `claude-dev-env: advisory hook ${pythonModuleName} returned non-zero: `,
81
+ );
75
82
  return (
76
83
  shimContentHeader
77
84
  + `"""${SHIM_DOCSTRING}"""\n`
@@ -82,9 +89,21 @@ function buildShimContent(pythonModuleName) {
82
89
  + 'if str(shim_directory) not in sys.path:\n'
83
90
  + ' sys.path.insert(0, str(shim_directory))\n'
84
91
  + '\n'
85
- + `import ${pythonModuleName}\n`
86
- + '\n'
87
- + `sys.exit(${pythonModuleName}.main())\n`
92
+ + 'try:\n'
93
+ + ` import ${pythonModuleName}\n`
94
+ + ` advisory_exit_code = ${pythonModuleName}.main()\n`
95
+ + 'except SystemExit as advisory_system_exit:\n'
96
+ + ' if advisory_system_exit.code == 130:\n'
97
+ + ' raise\n'
98
+ + ' if advisory_system_exit.code not in (None, 0):\n'
99
+ + ` print(${failureMessage} + str(advisory_system_exit), file=sys.stderr)\n`
100
+ + ' sys.exit(0)\n'
101
+ + 'except Exception as advisory_error:\n'
102
+ + ` print(${failureMessage} + str(advisory_error), file=sys.stderr)\n`
103
+ + ' sys.exit(0)\n'
104
+ + 'if advisory_exit_code:\n'
105
+ + ` print(${nonzeroMessage} + str(advisory_exit_code), file=sys.stderr)\n`
106
+ + 'sys.exit(0)\n'
88
107
  );
89
108
  }
90
109
 
@@ -1,6 +1,7 @@
1
1
  import { test } from 'node:test';
2
2
  import { strict as assert } from 'node:assert';
3
- import { mkdtempSync, rmSync, existsSync, readFileSync, mkdirSync, statSync, symlinkSync } from 'node:fs';
3
+ import { mkdtempSync, rmSync, existsSync, readFileSync, mkdirSync, statSync, symlinkSync, writeFileSync } from 'node:fs';
4
+ import { execFileSync, spawnSync } from 'node:child_process';
4
5
  import { tmpdir } from 'node:os';
5
6
  import { join } from 'node:path';
6
7
 
@@ -58,16 +59,16 @@ test('writeAllGitHookShims creates one shim per known hook name', () => {
58
59
  });
59
60
 
60
61
 
61
- test('known git hooks exclude post-commit', () => {
62
- assert.equal(KNOWN_GIT_HOOK_NAMES.includes('post-commit'), false);
62
+ test('known git hooks include post-commit for current-head notices', () => {
63
+ assert.deepEqual(KNOWN_GIT_HOOK_NAMES, ['pre-commit', 'pre-push', 'post-commit']);
63
64
  });
64
65
 
65
66
 
66
- test('writeAllGitHookShims does not create a post-commit shim', () => {
67
+ test('writeAllGitHookShims creates the post-commit shim', () => {
67
68
  const { temporaryRoot, gitHooksDirectory } = makeTemporaryGitHooksDirectory();
68
69
  try {
69
70
  writeAllGitHookShims({ gitHooksDirectory });
70
- assert.equal(existsSync(join(gitHooksDirectory, 'post-commit')), false);
71
+ assert.equal(existsSync(join(gitHooksDirectory, 'post-commit')), true);
71
72
  } finally {
72
73
  rmSync(temporaryRoot, { recursive: true, force: true });
73
74
  }
@@ -221,3 +222,156 @@ test('writeGitHookShim rejects hooks directory that is a symlink (loopP5c-5)', (
221
222
  }
222
223
  });
223
224
 
225
+
226
+ test('generated advisory shims keep temporary Git commit and push available after module failure', () => {
227
+ const { temporaryRoot, gitHooksDirectory } = makeTemporaryGitHooksDirectory();
228
+ const repositoryRoot = join(temporaryRoot, 'repository');
229
+ const bareRepositoryRoot = join(temporaryRoot, 'remote.git');
230
+ const targetRemote = 'https://github.com/JonEcho/python-automation.git';
231
+ try {
232
+ writeFileSync(
233
+ join(gitHooksDirectory, 'broken_advisory.py'),
234
+ 'def main():\n raise RuntimeError("advisory failure")\n',
235
+ );
236
+ writeGitHookShim({
237
+ gitHooksDirectory,
238
+ gitNativeHookName: 'pre-commit',
239
+ pythonModuleName: 'broken_advisory',
240
+ });
241
+ writeGitHookShim({
242
+ gitHooksDirectory,
243
+ gitNativeHookName: 'pre-push',
244
+ pythonModuleName: 'broken_advisory',
245
+ });
246
+ mkdirSync(repositoryRoot);
247
+ execFileSync('git', ['init', '--initial-branch=main', '--quiet'], { cwd: repositoryRoot });
248
+ execFileSync('git', ['config', 'user.email', 'test@example.invalid'], { cwd: repositoryRoot });
249
+ execFileSync('git', ['config', 'user.name', 'Verification Test'], { cwd: repositoryRoot });
250
+ execFileSync('git', ['config', 'core.hooksPath', gitHooksDirectory], { cwd: repositoryRoot });
251
+ writeFileSync(join(repositoryRoot, 'README.md'), 'check\n');
252
+ execFileSync('git', ['add', 'README.md'], { cwd: repositoryRoot });
253
+
254
+ const commitResult = spawnSync('git', ['commit', '-m', 'initial'], {
255
+ cwd: repositoryRoot,
256
+ encoding: 'utf8',
257
+ });
258
+ assert.equal(commitResult.status, 0);
259
+ assert.match(commitResult.stderr, /advisory hook broken_advisory failed: advisory failure/);
260
+
261
+ execFileSync('git', ['init', '--bare', '--quiet', bareRepositoryRoot], { cwd: temporaryRoot });
262
+ execFileSync('git', ['remote', 'add', 'origin', targetRemote], { cwd: repositoryRoot });
263
+ execFileSync('git', ['config', `url.${bareRepositoryRoot}.insteadOf`, targetRemote], {
264
+ cwd: repositoryRoot,
265
+ });
266
+ const pushResult = spawnSync('git', ['push', 'origin', 'main'], {
267
+ cwd: repositoryRoot,
268
+ encoding: 'utf8',
269
+ });
270
+ assert.equal(pushResult.status, 0);
271
+ assert.match(pushResult.stderr, /advisory hook broken_advisory failed: advisory failure/);
272
+ } finally {
273
+ rmSync(temporaryRoot, { recursive: true, force: true });
274
+ }
275
+ });
276
+
277
+
278
+ test('generated advisory shim turns a nonzero module return into a success with a warning', () => {
279
+ const { temporaryRoot, gitHooksDirectory } = makeTemporaryGitHooksDirectory();
280
+ try {
281
+ writeFileSync(
282
+ join(gitHooksDirectory, 'nonzero_advisory.py'),
283
+ 'def main():\n return 1\n',
284
+ );
285
+ const shimPath = writeGitHookShim({
286
+ gitHooksDirectory,
287
+ gitNativeHookName: 'pre-commit',
288
+ pythonModuleName: 'nonzero_advisory',
289
+ });
290
+ const shimResult = spawnSync('python', [shimPath], {
291
+ encoding: 'utf8',
292
+ });
293
+ assert.equal(shimResult.status, 0);
294
+ assert.match(shimResult.stderr, /advisory hook nonzero_advisory returned non-zero: 1/);
295
+ } finally {
296
+ rmSync(temporaryRoot, { recursive: true, force: true });
297
+ }
298
+ });
299
+
300
+
301
+ function runGeneratedAdvisoryModule(pythonModuleName, moduleSource) {
302
+ const { temporaryRoot, gitHooksDirectory } = makeTemporaryGitHooksDirectory();
303
+ try {
304
+ if (moduleSource !== null) {
305
+ writeFileSync(join(gitHooksDirectory, pythonModuleName + '.py'), moduleSource);
306
+ }
307
+ const shimPath = writeGitHookShim({
308
+ gitHooksDirectory,
309
+ gitNativeHookName: 'pre-commit',
310
+ pythonModuleName,
311
+ });
312
+ return spawnSync('python', [shimPath], {
313
+ encoding: 'utf8',
314
+ });
315
+ } finally {
316
+ rmSync(temporaryRoot, { recursive: true, force: true });
317
+ }
318
+ }
319
+
320
+
321
+ test('generated advisory shim reports a missing module and succeeds', () => {
322
+ const shimResult = runGeneratedAdvisoryModule('missing_advisory', null);
323
+ assert.equal(shimResult.status, 0);
324
+ assert.match(shimResult.stderr, /advisory hook missing_advisory failed: No module named/);
325
+ });
326
+
327
+
328
+ test('generated advisory shim reports a syntax error and succeeds', () => {
329
+ const shimResult = runGeneratedAdvisoryModule(
330
+ 'broken_syntax',
331
+ 'def main(:\n return 1\n',
332
+ );
333
+ assert.equal(shimResult.status, 0);
334
+ assert.match(shimResult.stderr, /advisory hook broken_syntax failed:/);
335
+ });
336
+
337
+
338
+ test('generated advisory shim reports ordinary SystemExit and succeeds', () => {
339
+ const shimResult = runGeneratedAdvisoryModule(
340
+ 'ordinary_exit',
341
+ 'def main():\n raise SystemExit(1)\n',
342
+ );
343
+ assert.equal(shimResult.status, 0);
344
+ assert.match(shimResult.stderr, /advisory hook ordinary_exit failed: 1/);
345
+ });
346
+
347
+
348
+ test('generated advisory shim treats successful SystemExit as silent success', () => {
349
+ for (const [pythonModuleName, moduleSource] of [
350
+ ['zero_exit', 'def main():\n raise SystemExit(0)\n'],
351
+ ['empty_exit', 'def main():\n raise SystemExit()\n'],
352
+ ]) {
353
+ const shimResult = runGeneratedAdvisoryModule(pythonModuleName, moduleSource);
354
+ assert.equal(shimResult.status, 0);
355
+ assert.equal(shimResult.stderr, '');
356
+ }
357
+ });
358
+
359
+
360
+ test('generated advisory shim preserves cancellation SystemExit 130', () => {
361
+ const shimResult = runGeneratedAdvisoryModule(
362
+ 'cancelled_exit',
363
+ 'def main():\n raise SystemExit(130)\n',
364
+ );
365
+ assert.equal(shimResult.status, 130);
366
+ assert.doesNotMatch(shimResult.stderr, /advisory hook cancelled_exit/);
367
+ });
368
+
369
+
370
+ test('generated advisory shim preserves KeyboardInterrupt cancellation', () => {
371
+ const shimResult = runGeneratedAdvisoryModule(
372
+ 'keyboard_cancel',
373
+ 'def main():\n raise KeyboardInterrupt()\n',
374
+ );
375
+ assert.notEqual(shimResult.status, 0);
376
+ assert.match(shimResult.stderr, /KeyboardInterrupt/);
377
+ });
@@ -0,0 +1,78 @@
1
+ import { existsSync, readFileSync, realpathSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ export function continuityHookConfiguration(host, script) {
6
+ if (host === 'cursor') {
7
+ throw new Error('Cursor automatic activation is unsupported: beforeSubmitPrompt has no agent-context output, sessionStart is fire-and-forget, and preCompact is observational. No Cursor settings changed.');
8
+ }
9
+ if (!['claude', 'codex'].includes(host)) throw new Error(`Unsupported host ${host}`);
10
+ if (/[\r\n"`$%]/.test(script)) throw new Error('Hook script path contains shell expansion characters');
11
+ const command = `node "${script.replace(/\\/g, '/')}" hook ${host}`;
12
+ const events = host === 'claude' ? ['UserPromptSubmit', 'UserPromptExpansion', 'SessionStart'] : ['UserPromptSubmit', 'SessionStart'];
13
+ return Object.fromEntries(events.map(event => [event, [{
14
+ ...(event === 'SessionStart' ? { matcher: host === 'claude' ? 'startup|resume|compact|clear|fork' : 'startup|resume|compact|clear' } : {}),
15
+ ...(event === 'UserPromptExpansion' ? { matcher: '^(pstack:)?poteto-mode$|^(claude-dev-env:)?session-continuity$' } : {}),
16
+ hooks: [{ type: 'command', command, timeout: 10 }],
17
+ }]]));
18
+ }
19
+
20
+ export function mergeContinuityHooks(existing, host, script) {
21
+ const additions = continuityHookConfiguration(host, script);
22
+ const mergedConfiguration = structuredClone(existing);
23
+ mergedConfiguration.hooks ||= {};
24
+ for (const [event, groups] of Object.entries(additions)) {
25
+ const current = mergedConfiguration.hooks[event] || [];
26
+ for (const group of current) {
27
+ for (const hook of group.hooks || []) {
28
+ if (hook.command?.includes('/session-continuity/continuity.mjs') && hook.command !== groups[0].hooks[0].command) {
29
+ throw new Error('Another continuity installation owns this host config. Select its profile or remove that installation explicitly.');
30
+ }
31
+ }
32
+ }
33
+ const kept = current.flatMap(group => {
34
+ const hooks = (group.hooks || []).filter(hook => hook.command !== groups[0].hooks[0].command);
35
+ return hooks.length ? [{ ...group, hooks }] : [];
36
+ });
37
+ mergedConfiguration.hooks[event] = kept.concat(groups);
38
+ }
39
+ return mergedConfiguration;
40
+ }
41
+
42
+ async function main() {
43
+ const requested = process.argv.slice(2);
44
+ const selected = requested.length ? requested : ['claude', 'codex'];
45
+ if (selected.some(host => !['claude', 'codex'].includes(host))) continuityHookConfiguration(selected.find(host => !['claude', 'codex'].includes(host)), '');
46
+ const { resolveInstallRoot } = await import('./resolve-install-root.mjs');
47
+ const roots = resolveInstallRoot();
48
+ const script = join(roots.skillsInstallDirectory, 'session-continuity', 'continuity.mjs');
49
+ if (!existsSync(script) || !existsSync(join(dirname(script), 'SKILL.md'))) {
50
+ throw new Error('Run the full claude-dev-env installer from this checkout first so the companion is in the canonical agents home.');
51
+ }
52
+ const paths = { claude: join(roots.managedRoot, 'settings.json'), codex: join(dirname(roots.codexRulesInstallDirectory), 'hooks.json') };
53
+ const plans = selected.map(host => {
54
+ const path = paths[host];
55
+ if (!existsSync(dirname(path))) throw new Error(`Host config directory is absent: ${dirname(path)}`);
56
+ const existing = existsSync(path) ? readFileSync(path, 'utf8') : null;
57
+ return { path, existing, content: JSON.stringify(mergeContinuityHooks(existing ? JSON.parse(existing) : {}, host, script), null, 2) + '\n' };
58
+ });
59
+ for (const plan of plans) {
60
+ if (plan.existing === plan.content) { console.log(`Already configured: ${plan.path}`); continue; }
61
+ if (plan.existing !== null) {
62
+ const backup = `${plan.path}.before-session-continuity`;
63
+ if (!existsSync(backup)) writeFileSync(backup, plan.existing, { flag: 'wx', mode: 0o600 });
64
+ }
65
+ const current = existsSync(plan.path) ? readFileSync(plan.path, 'utf8') : null;
66
+ if (current !== plan.existing) throw new Error(`Config changed during setup: ${plan.path}. Rerun setup.`);
67
+ const temporary = `${plan.path}.continuity-${process.pid}.tmp`;
68
+ writeFileSync(temporary, plan.content, { flag: 'wx', mode: 0o600 });
69
+ renameSync(temporary, plan.path);
70
+ if (readFileSync(plan.path, 'utf8') !== plan.content) throw new Error(`Config read-back mismatch: ${plan.path}`);
71
+ console.log(`Configured and read back: ${plan.path}`);
72
+ }
73
+ console.log('Review and trust the new hooks in each host. Cursor remains unsupported; no Cursor settings changed.');
74
+ }
75
+
76
+ if (process.argv[1] && resolve(realpathSync(process.argv[1])) === fileURLToPath(import.meta.url)) {
77
+ main().catch(error => { console.error(error.message); process.exitCode = 1; });
78
+ }
@@ -0,0 +1,42 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import { continuityHookConfiguration, mergeContinuityHooks } from './install-session-continuity.mjs';
4
+
5
+ test('setup preserves other hooks and settings, is idempotent, and rejects competing profiles', () => {
6
+ const existing = { permissions: { deny: ['secret'] }, hooks: { SessionStart: [{ hooks: [{ type: 'command', command: 'existing-hook' }] }] } };
7
+ const script = '/home/jon/.agents/skills/session-continuity/continuity.mjs';
8
+ const first = mergeContinuityHooks(existing, 'claude', script);
9
+ assert.deepEqual(mergeContinuityHooks(first, 'claude', script), first);
10
+ assert.deepEqual(first.permissions, existing.permissions);
11
+ assert.equal(first.hooks.SessionStart[0].hooks[0].command, 'existing-hook');
12
+ assert.equal(existing.hooks.SessionStart.length, 1);
13
+ assert.throws(() => mergeContinuityHooks(first, 'claude', '/other/session-continuity/continuity.mjs'), /Another continuity installation/);
14
+ assert.throws(() => continuityHookConfiguration('claude', '/path/with$expansion/continuity.mjs'), /expansion/);
15
+ });
16
+
17
+ test('Cursor setup refuses an unsupported automatic activation claim without changing config', () => {
18
+ const existing = { version: 1, hooks: { beforeSubmitPrompt: [{ command: 'existing' }] } };
19
+ const before = JSON.stringify(existing);
20
+ assert.throws(() => mergeContinuityHooks(existing, 'cursor', '/skill/continuity.mjs'), /beforeSubmitPrompt has no agent-context output/);
21
+ assert.equal(JSON.stringify(existing), before);
22
+ });
23
+
24
+ for (const host of ['claude', 'codex']) {
25
+ test(`${host}: setup registers only documented context-loading events`, () => {
26
+ const configuration = continuityHookConfiguration(host, '/home/jon/.agents/skills/session-continuity/continuity.mjs');
27
+ const expected = host === 'claude'
28
+ ? ['UserPromptSubmit', 'UserPromptExpansion', 'SessionStart']
29
+ : ['UserPromptSubmit', 'SessionStart'];
30
+ assert.deepEqual(Object.keys(configuration), expected);
31
+ for (const groups of Object.values(configuration)) {
32
+ assert.equal(groups.length, 1);
33
+ assert.equal(groups[0].hooks[0].type, 'command');
34
+ assert.equal(groups[0].hooks[0].command, `node "/home/jon/.agents/skills/session-continuity/continuity.mjs" hook ${host}`);
35
+ }
36
+ assert.equal(new RegExp(configuration.SessionStart[0].matcher).test('compact'), true);
37
+ if (host === 'claude') {
38
+ assert.equal(new RegExp(configuration.UserPromptExpansion[0].matcher).test('pstack:poteto-mode'), true);
39
+ assert.equal(new RegExp(configuration.UserPromptExpansion[0].matcher).test('explain-poteto-mode'), false);
40
+ }
41
+ });
42
+ }
@@ -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'),
@@ -185,7 +185,7 @@ test('the shipped Codex rules file has no production-scanner home-path findings'
185
185
  );
186
186
  });
187
187
 
188
- test('--only journal skips Codex rules; --only core copies them', () => {
188
+ test('--only journal is rejected before Codex rules change; --only core copies them', () => {
189
189
  const homeDirectory = mkdtempSync(join(tmpdir(), 'cdev-codex-groups-'));
190
190
  try {
191
191
  const installedRulesPath = join(
@@ -194,7 +194,10 @@ test('--only journal skips Codex rules; --only core copies them', () => {
194
194
  CODEX_RULES_DIRECTORY_NAME,
195
195
  CODEX_RULES_SHIPPED_FILE_NAME,
196
196
  );
197
- runInstaller(homeDirectory, ['--only', 'journal']);
197
+ assert.throws(
198
+ () => runInstaller(homeDirectory, ['--only', 'journal']),
199
+ error => error.status === 1 && /Unknown group\(s\): journal/.test(error.stderr),
200
+ );
198
201
  assert.equal(existsSync(installedRulesPath), false);
199
202
 
200
203
  runInstaller(homeDirectory, ['--only', 'core']);
@@ -100,7 +100,7 @@ test('a full install writes stem-named Cursor rules and leaves a local extra mdc
100
100
  }
101
101
  });
102
102
 
103
- test('--only journal skips Cursor rule generation; --only core writes them', () => {
103
+ test('--only journal is rejected before Cursor rules change; --only core writes them', () => {
104
104
  const homeDirectory = mkdtempSync(join(tmpdir(), 'cdev-cursor-groups-'));
105
105
  try {
106
106
  const generatedPath = join(
@@ -115,7 +115,10 @@ test('--only journal skips Cursor rule generation; --only core writes them', ()
115
115
  'rules',
116
116
  'pstack-model-preferences.codex.json',
117
117
  );
118
- runInstaller(homeDirectory, ['--only', 'journal']);
118
+ assert.throws(
119
+ () => runInstaller(homeDirectory, ['--only', 'journal']),
120
+ error => error.status === 1 && /Unknown group\(s\): journal/.test(error.stderr),
121
+ );
119
122
  assert.equal(existsSync(generatedPath), false);
120
123
  assert.equal(existsSync(preferencePath), false);
121
124