claude-dev-env 4.2.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/.agents/agents/pr-description-writer.md +2 -2
  2. package/.agents/agents/test_agent_frontmatter.py +2 -2
  3. package/.agents/agents/test_pr_description_writer_style.py +1 -3
  4. package/.agents/skills/e-code-review/SKILL.md +12 -12
  5. package/.agents/skills/e-code-review/preflight-proposal.contract.test.mjs +20 -0
  6. package/.agents/skills/e-code-review/reference/fix.md +15 -17
  7. package/.agents/skills/e-code-review/reference/loop.md +35 -39
  8. package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +2 -1
  9. package/.agents/skills/e-code-review/reference/preflight-proposal.md +44 -9
  10. package/.agents/skills/e-code-review/reference/runner-selection.md +13 -14
  11. package/.agents/skills/issue-tracker/SKILL.md +7 -0
  12. package/.agents/skills/pull-request/SKILL.md +216 -0
  13. package/.agents/skills/pull-request/reference/publication-tasks.md +18 -0
  14. package/.agents/skills/pull-request/scripts/github_pr_command_constants/__init__.py +1 -0
  15. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/__init__.py +1 -0
  16. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +69 -0
  17. package/.agents/skills/pull-request/scripts/pull_request.py +265 -0
  18. package/.agents/skills/pull-request/scripts/recover_legacy_author.py +251 -0
  19. package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +58 -0
  20. package/.agents/skills/pull-request/scripts/test_pull_request.py +396 -0
  21. package/.agents/skills/pull-request/scripts/test_recover_legacy_author.py +221 -0
  22. package/.agents/skills/skill-builder/SKILL.md +3 -196
  23. package/.agents/skills/test-runner/SKILL.md +69 -0
  24. package/.agents/skills/test-runner/scripts/preflight_checks.py +365 -0
  25. package/.agents/skills/test-runner/scripts/run_tests.py +316 -0
  26. package/.agents/skills/test-runner/scripts/test_preflight_checks.py +185 -0
  27. package/.agents/skills/test-runner/scripts/test_run_tests.py +281 -0
  28. package/.agents/skills/test-runner/scripts/test_runner_constants/__init__.py +1 -0
  29. package/.agents/skills/test-runner/scripts/test_runner_constants/config/__init__.py +1 -0
  30. package/.agents/skills/test-runner/scripts/test_runner_constants/config/constants.py +96 -0
  31. package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
  32. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
  33. package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
  34. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
  35. package/bin/ever-shipped-skills.mjs +2 -0
  36. package/bin/ever-shipped-skills.test.mjs +8 -0
  37. package/bin/install.agents-home.test.mjs +15 -13
  38. package/bin/install.mjs +37 -1
  39. package/bin/install.policy-lint.test.mjs +78 -0
  40. package/bin/install.prune.test.mjs +149 -20
  41. package/bin/install.pstack-plugin.test.mjs +114 -0
  42. package/bin/install.test.mjs +61 -7
  43. package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
  44. package/hooks/advisory/pr_done_reminder.py +342 -0
  45. package/hooks/advisory/test_pr_done_reminder.py +249 -0
  46. package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
  47. package/hooks/blocking/code_rules_paired_test.py +22 -15
  48. package/hooks/blocking/pii_prevention_blocker.py +32 -51
  49. package/hooks/blocking/test_bash_post_call_dispatcher.py +55 -17
  50. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +1 -4
  51. package/hooks/blocking/test_code_rules_command_dispatch.py +2 -2
  52. package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
  53. package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
  54. package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
  55. package/hooks/blocking/test_pii_prevention_blocker.py +37 -89
  56. package/hooks/blocking/test_pre_tool_use_dispatcher.py +68 -133
  57. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
  58. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +18 -18
  59. package/hooks/gh_post_body_texts.py +195 -0
  60. package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
  61. package/hooks/git-hooks/pre_commit.py +52 -11
  62. package/hooks/git-hooks/test_installed_policy_lint.py +159 -0
  63. package/hooks/git-hooks/test_native_policy_commit.py +87 -0
  64. package/hooks/git-hooks/test_pre_commit.py +246 -0
  65. package/hooks/hooks.json +1 -16
  66. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -2
  67. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -8
  68. package/hooks/hooks_constants/gh_post_body_texts_constants.py +12 -0
  69. package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
  70. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -43
  71. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +17 -7
  72. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -7
  73. package/hooks/hooks_constants/test_failure_recorder_constants.py +3 -3
  74. package/hooks/hooks_constants/test_gh_post_body_texts_constants.py +19 -0
  75. package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
  76. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +20 -32
  77. package/hooks/observability/test_failure_recorder.py +1 -1
  78. package/hooks/pyproject.toml +1 -1
  79. package/hooks/test_gh_post_body_texts.py +79 -0
  80. package/package.json +2 -2
  81. package/rules/durable-post-artifacts.md +32 -20
  82. package/rules/gh-cli-conventions.md +23 -13
  83. package/rules/git-workflow.md +21 -8
  84. package/scripts/check.ps1 +0 -1
  85. package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
  86. package/scripts/ci/installer-lifecycle.test.mjs +26 -0
  87. package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
  88. package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
  89. package/scripts/durable_post_lint.py +206 -0
  90. package/scripts/durable_post_lint_config/config/constants.py +96 -0
  91. package/scripts/durable_post_lint_config/config/test_constants.py +25 -0
  92. package/scripts/policy_lint/adapter_configuration.py +53 -12
  93. package/scripts/policy_lint/adapter_detectors.py +13 -6
  94. package/scripts/policy_lint/adapters.py +7 -4
  95. package/scripts/policy_lint/config/constants.py +5 -0
  96. package/scripts/policy_lint/engine.py +48 -4
  97. package/scripts/policy_lint/model.py +1 -0
  98. package/scripts/policy_lint/registry.py +1 -1
  99. package/scripts/policy_lint/selection.py +2 -1
  100. package/scripts/policy_lint/selection_git.py +2 -0
  101. package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
  102. package/scripts/repository_checks/config/constants.py +0 -5
  103. package/scripts/shared_tree_paths.py +32 -3
  104. package/scripts/test_durable_post_lint.py +332 -0
  105. package/scripts/test_shared_tree_paths.py +33 -0
  106. package/scripts/tests/test_adapter_configuration.py +54 -0
  107. package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
  108. package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
  109. package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
  110. package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
  111. package/scripts/tests/test_policy_lint_selection.py +25 -2
  112. package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
  113. package/scripts/tests/test_rules.py +3 -7
  114. package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
  115. package/.agents/skills/_shared/pr-loop/AGENTS.md +0 -1
  116. package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
  117. package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
  118. package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
  119. package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
  120. package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
  121. package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
  122. package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
  123. package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
  124. package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
  125. package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
  126. package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
  127. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
  128. package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
  129. package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
  130. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
  131. package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
  132. package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
  133. package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
  134. package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
  135. package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
  136. package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
  137. package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
  138. package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
  139. package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
  140. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
  141. package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
  142. package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
  143. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
  144. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
  145. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
  146. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
  147. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
  148. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
  149. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
  150. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
  151. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
  152. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
  153. package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
  154. package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
  155. package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
  156. package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
  157. package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
  158. package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
  159. package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
  160. package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
  161. package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
  162. package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
  163. package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
  164. package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
  165. package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
  166. package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
  167. package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
  168. package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
  169. package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
  170. package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
  171. package/.agents/skills/autoconverge/AGENTS.md +0 -1
  172. package/.agents/skills/autoconverge/SKILL.md +0 -431
  173. package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
  174. package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
  175. package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
  176. package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
  177. package/.agents/skills/autoconverge/reference/convergence.md +0 -219
  178. package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
  179. package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
  180. package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
  181. package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
  182. package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
  183. package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
  184. package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
  185. package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
  186. package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
  187. package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
  188. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
  189. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
  190. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
  191. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
  192. package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
  193. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
  194. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
  195. package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
  196. package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
  197. package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
  198. package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
  199. package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
  200. package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
  201. package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
  202. package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
  203. package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
  204. package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
  205. package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
  206. package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
  207. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
  208. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
  209. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
  210. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
  211. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
  212. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
  213. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
  214. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
  215. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
  216. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
  217. package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
  218. package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
  219. package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
  220. package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
  221. package/.agents/skills/hitl/SKILL.md +0 -45
  222. package/.agents/skills/pr-cleanup/SKILL.md +0 -140
  223. package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
  224. package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
  225. package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
  226. package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
  227. package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
  228. package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
  229. package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
  230. package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
  231. package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
  232. package/.agents/skills/pr-refinement/SKILL.md +0 -31
  233. package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
  234. package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
  235. package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
  236. package/.agents/skills/pr-small-cl/SKILL.md +0 -52
  237. package/.agents/skills/pr-title-description/SKILL.md +0 -96
  238. package/.agents/skills/prototype/SKILL.md +0 -85
  239. package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
  240. package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
  241. package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
  242. package/.agents/skills/prototype/scripts/conftest.py +0 -15
  243. package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
  244. package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
  245. package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
  246. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  247. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
  248. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
  249. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
  250. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
  251. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
  252. package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
  253. package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
  254. package/.agents/skills/prototype/workflows/promotion.md +0 -25
  255. package/.agents/skills/prototype/workflows/sandbox.md +0 -35
  256. package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
  257. package/.agents/skills/rebase/AGENTS.md +0 -1
  258. package/.agents/skills/rebase/SKILL.md +0 -178
  259. package/.agents/skills/review-router/SKILL.md +0 -51
  260. package/.agents/skills/review-router/reference/route-policy.json +0 -7
  261. package/.agents/skills/review-router/reference/task-seeds.md +0 -1
  262. package/.agents/skills/review-router/scripts/conftest.py +0 -4
  263. package/.agents/skills/review-router/scripts/review_router.py +0 -50
  264. package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
  265. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
  266. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  267. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
  268. package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
  269. package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
  270. package/.agents/skills/review-tier/SKILL.md +0 -56
  271. package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
  272. package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
  273. package/.agents/skills/review-tier/scripts/conftest.py +0 -4
  274. package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
  275. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
  276. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  277. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
  278. package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
  279. package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
  280. package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
  281. package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
  282. package/.agents/skills/session-log/AGENTS.md +0 -1
  283. package/.agents/skills/session-log/SKILL.md +0 -170
  284. package/.agents/skills/session-log/templates/frontmatter.md +0 -40
  285. package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
  286. package/.agents/skills/session-tidy/AGENTS.md +0 -1
  287. package/.agents/skills/session-tidy/SKILL.md +0 -191
  288. package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
  289. package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
  290. package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
  291. package/.agents/skills/skill-builder/references/description-field.md +0 -111
  292. package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
  293. package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
  294. package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
  295. package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
  296. package/.agents/skills/skill-builder/references/skill-types.md +0 -232
  297. package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
  298. package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
  299. package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
  300. package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
  301. package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
  302. package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
  303. package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
  304. package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
  305. package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
  306. package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
  307. package/.agents/skills/update/.claude/CLAUDE.md +0 -1
  308. package/.agents/skills/update/AGENTS.md +0 -1
  309. package/.agents/skills/update/SKILL.md +0 -190
  310. package/_shared/pr-loop/AGENTS.md +0 -1
  311. package/_shared/pr-loop/CLAUDE.md +0 -1
  312. package/_shared/pr-loop/audit-contract.md +0 -183
  313. package/_shared/pr-loop/audit-reply-template.md +0 -151
  314. package/_shared/pr-loop/code-rules-gate.md +0 -65
  315. package/_shared/pr-loop/fix-protocol.md +0 -60
  316. package/_shared/pr-loop/gh-payloads.md +0 -75
  317. package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
  318. package/_shared/pr-loop/precatch-rubric.md +0 -66
  319. package/_shared/pr-loop/preflight-proposal.md +0 -56
  320. package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
  321. package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
  322. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
  323. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
  324. package/_shared/pr-loop/state-schema.md +0 -45
  325. package/_shared/pr-loop/worker-spawn.md +0 -189
  326. package/hooks/_gh_pr_author_swap_utils.py +0 -1225
  327. package/hooks/blocking/conventional_pr_title_gate.py +0 -443
  328. package/hooks/blocking/gh_body_arg_blocker.py +0 -176
  329. package/hooks/blocking/gh_pr_author_enforcer.py +0 -487
  330. package/hooks/blocking/gh_pr_author_restore.py +0 -105
  331. package/hooks/blocking/pr_description_writer_gate.py +0 -256
  332. package/hooks/blocking/test_conventional_pr_title_gate.py +0 -640
  333. package/hooks/blocking/test_gh_body_arg_blocker.py +0 -441
  334. package/hooks/blocking/test_gh_pr_author_enforcer.py +0 -1166
  335. package/hooks/blocking/test_gh_pr_author_restore.py +0 -513
  336. package/hooks/blocking/test_gh_pr_author_swap_utils.py +0 -910
  337. package/hooks/blocking/test_pr_description_writer_gate.py +0 -234
  338. package/hooks/blocking/test_preflight_check.py +0 -343
  339. package/hooks/blocking/test_test_preflight_check.py +0 -80
  340. package/hooks/blocking/test_volatile_path_in_post_blocker.py +0 -322
  341. package/hooks/blocking/volatile_path_in_post_blocker.py +0 -461
  342. package/hooks/hooks_constants/conventional_pr_title_gate_constants.py +0 -58
  343. package/hooks/hooks_constants/gh_pr_author_swap_constants.py +0 -76
  344. package/hooks/hooks_constants/pr_description_writer_gate_constants.py +0 -73
  345. package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +0 -55
  346. package/hooks/observability/pr_description_writer_spawn_tracker.py +0 -113
  347. package/hooks/observability/test_pr_description_writer_spawn_tracker.py +0 -150
  348. package/hooks/session/gh_pr_author_session_cleanup.py +0 -175
  349. package/hooks/session/test_gh_pr_author_session_cleanup.py +0 -576
  350. package/hooks/test__gh_pr_author_swap_utils.py +0 -333
@@ -1,23 +1,33 @@
1
1
  """Behavioral tests for the Bash PostToolUse dispatcher's hosted-hook roster."""
2
2
 
3
+ from hooks_constants import bash_post_call_dispatcher_constants as constants
3
4
  from hooks_constants.bash_post_call_dispatcher_constants import (
4
5
  ALL_BASH_POST_TOOL_USE_HOSTED_HOOK_ENTRIES,
5
6
  )
6
7
  from hooks_constants.bash_pre_tool_use_dispatcher_constants import BASH_TOOL_NAME
7
8
 
8
9
 
9
- def test_roster_names_the_gh_pr_author_restore_hook() -> None:
10
+ def test_roster_runs_the_recorder_then_the_pr_done_reminder() -> None:
10
11
  all_script_paths = [
11
12
  each_entry.script_relative_path for each_entry in ALL_BASH_POST_TOOL_USE_HOSTED_HOOK_ENTRIES
12
13
  ]
13
- assert "blocking/gh_pr_author_restore.py" in all_script_paths
14
+ assert all_script_paths == [
15
+ "observability/test_failure_recorder.py",
16
+ "advisory/pr_done_reminder.py",
17
+ ]
14
18
 
15
19
 
16
- def test_roster_names_the_test_failure_recorder_hook() -> None:
17
- all_script_paths = [
18
- each_entry.script_relative_path for each_entry in ALL_BASH_POST_TOOL_USE_HOSTED_HOOK_ENTRIES
19
- ]
20
- assert "observability/test_failure_recorder.py" in all_script_paths
20
+ def test_pr_done_reminder_also_serves_the_powershell_tool() -> None:
21
+ reminder_entry = ALL_BASH_POST_TOOL_USE_HOSTED_HOOK_ENTRIES[-1]
22
+ assert reminder_entry.script_relative_path == "advisory/pr_done_reminder.py"
23
+ assert "PowerShell" in reminder_entry.applicable_tool_names
24
+
25
+
26
+ def test_context_forwarding_keys_match_the_hook_output_contract() -> None:
27
+ assert constants.POST_TOOL_USE_HOOK_EVENT_NAME == "PostToolUse"
28
+ assert constants.HOOK_SPECIFIC_OUTPUT_KEY == "hookSpecificOutput"
29
+ assert constants.ADDITIONAL_CONTEXT_KEY == "additionalContext"
30
+ assert constants.ADDITIONAL_CONTEXT_JOIN_SEPARATOR == "\n\n"
21
31
 
22
32
 
23
33
  def test_every_roster_entry_applies_to_bash() -> None:
@@ -20,28 +20,21 @@ POWERSHELL_TOOL_NAME = ALL_CONSTANT_BINDINGS["POWERSHELL_TOOL_NAME"]
20
20
 
21
21
  _EXPECTED_BASH_ORDER = (
22
22
  "blocking/destructive_command_blocker.py",
23
- "blocking/gh_body_arg_blocker.py",
24
23
  "blocking/shell_substitution_blocker.py",
25
24
  "blocking/piped_pytest_blocker.py",
26
25
  "blocking/cursor_cli_python_misfire_blocker.py",
27
26
  "blocking/unscoped_search_blocker.py",
28
27
  "blocking/nas_ssh_binary_enforcer.py",
29
- "blocking/volatile_path_in_post_blocker.py",
30
28
  "blocking/pii_prevention_blocker.py",
31
- "blocking/conventional_pr_title_gate.py",
32
29
  "blocking/block_main_commit.py",
33
30
  "blocking/session_edit_stage_gate.py",
34
- "blocking/test_preflight_check.py",
35
31
  "blocking/windows_rmtree_blocker.py",
36
- "blocking/gh_pr_author_enforcer.py",
37
- "blocking/pr_description_writer_gate.py",
38
32
  )
39
33
 
40
34
  _POWERSHELL_APPLICABLE = (
41
35
  "blocking/cursor_cli_python_misfire_blocker.py",
42
36
  "blocking/unscoped_search_blocker.py",
43
37
  "blocking/pii_prevention_blocker.py",
44
- "blocking/pr_description_writer_gate.py",
45
38
  )
46
39
 
47
40
 
@@ -6,9 +6,7 @@ tokens that keep the recognizer to one unchained command, the pytest program
6
6
  and Python interpreter basenames, and the flag-token marker the path
7
7
  extractor uses to skip an option rather than an existing test file argument.
8
8
 
9
- ``BASH_TOOL_NAME`` lives in ``gh_pr_author_swap_constants`` already; this
10
- module reuses that name rather than a second literal. The pytest-program and
11
- interpreter recognition here is deliberately its own small, stdlib-only
9
+ The pytest-program and interpreter recognition here is deliberately its own small, stdlib-only
12
10
  check rather than a reuse of ``piped_pytest_blocker_constants`` /
13
11
  ``pytest_invocation``: those solve the harder pipe-and-wrapper problem and
14
12
  pull in a much larger transitive import for every Bash call regardless of
@@ -19,6 +17,8 @@ from __future__ import annotations
19
17
 
20
18
  import re
21
19
 
20
+ BASH_TOOL_NAME = "Bash"
21
+
22
22
  ALL_PYTEST_MENTION_SUBSTRINGS: tuple[str, ...] = ("pytest", "py.test")
23
23
  EXIT_CODE_ERROR_PREFIX: str = "Error: Exit code "
24
24
  OPTION_TOKEN_PREFIX: str = "-"
@@ -0,0 +1,19 @@
1
+ """Tests for GitHub post body extraction constants."""
2
+
3
+ from hooks_constants import gh_post_body_texts_constants as constants
4
+
5
+
6
+ def test_post_actions_cover_pull_requests_and_issues() -> None:
7
+ assert constants.ALL_GH_POST_SUBCOMMANDS == {
8
+ "issue": frozenset({"comment", "create", "edit"}),
9
+ "pr": frozenset({"comment", "create", "edit", "review"}),
10
+ }
11
+ assert constants.ALL_MCP_BODY_PARAM_NAMES == ("body", "comment")
12
+
13
+
14
+ def test_parser_constants_are_stable() -> None:
15
+ assert constants.BODY_FILE_ENCODING == "utf-8"
16
+ assert constants.BODY_FLAG_WITH_VALUE_STEP == 2
17
+ assert constants.GH_COMMAND_NAME == "gh"
18
+ assert constants.MINIMUM_POST_SUBCOMMAND_TOKEN_COUNT == 2
19
+ assert constants.TOKEN_JOIN_SEPARATOR == " "
@@ -0,0 +1,40 @@
1
+ """Pin the PR done reminder's command shape and checklist wording."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from hooks_constants import pr_done_reminder_constants as constants
6
+
7
+
8
+ def test_gh_probe_asks_for_every_field_the_checklist_reads() -> None:
9
+ assert constants.ALL_GH_PR_VIEW_ARGUMENTS[:3] == ("gh", "pr", "view")
10
+ all_requested_fields = constants.ALL_GH_PR_VIEW_ARGUMENTS[-1].split(",")
11
+ for each_field in ("number", "url", "isDraft", "mergeable", "statusCheckRollup", "labels"):
12
+ assert each_field in all_requested_fields
13
+
14
+
15
+ def test_git_global_options_that_take_a_value_are_named() -> None:
16
+ assert constants.ALL_GIT_OPTIONS_WITH_VALUE == frozenset({"-C", "-c"})
17
+
18
+
19
+ def test_powershell_wrappers_and_their_command_flags_are_named() -> None:
20
+ assert "pwsh" in constants.ALL_POWERSHELL_PROGRAM_NAMES
21
+ assert "powershell" in constants.ALL_POWERSHELL_PROGRAM_NAMES
22
+ assert "-command" in constants.ALL_POWERSHELL_COMMAND_FLAGS
23
+ assert "-c" in constants.ALL_POWERSHELL_COMMAND_FLAGS
24
+
25
+
26
+ def test_checklist_lines_join_on_a_single_newline() -> None:
27
+ assert constants.REMINDER_LINE_SEPARATOR == "\n"
28
+
29
+
30
+ def test_every_mergeable_value_has_a_hint() -> None:
31
+ assert set(constants.ALL_REMINDER_HINTS_BY_MERGEABLE) == {
32
+ constants.MERGEABLE_CLEAN_VALUE,
33
+ constants.MERGEABLE_CONFLICTING_VALUE,
34
+ constants.MERGEABLE_UNKNOWN_VALUE,
35
+ }
36
+
37
+
38
+ def test_header_says_it_never_blocks() -> None:
39
+ assert "never a block" in constants.REMINDER_HEADER
40
+ assert "never a block" in constants.NO_PULL_REQUEST_REMINDER
@@ -13,6 +13,9 @@ try:
13
13
  if str(_BLOCKING_DIR) not in sys.path:
14
14
  sys.path.insert(0, str(_BLOCKING_DIR))
15
15
 
16
+ from hooks_constants.bash_pre_tool_use_dispatcher_constants import (
17
+ ALL_BASH_HOSTED_HOOK_ENTRIES,
18
+ )
16
19
  from hooks_constants.pre_tool_use_dispatcher_constants import (
17
20
  ALL_HOSTED_HOOK_ENTRIES,
18
21
  ALL_IMMEDIATE_HARM_SCRIPT_PATHS,
@@ -74,10 +77,12 @@ def test_duplicate_rmtree_helper_blocker_runs_via_runpy() -> None:
74
77
  assert entry.native_module_name is None
75
78
 
76
79
 
77
- def test_windows_rmtree_blocker_still_registered() -> None:
78
- entry = _entry_for("blocking/windows_rmtree_blocker.py")
79
- assert entry is not None
80
- assert entry.applicable_tool_names == ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES
80
+ def test_windows_rmtree_write_check_is_retired_but_bash_check_remains() -> None:
81
+ assert _entry_for("blocking/windows_rmtree_blocker.py") is None
82
+ all_bash_paths = {
83
+ each_entry.script_relative_path for each_entry in ALL_BASH_HOSTED_HOOK_ENTRIES
84
+ }
85
+ assert "blocking/windows_rmtree_blocker.py" in all_bash_paths
81
86
 
82
87
 
83
88
  def test_blocking_hook_crash_deny_reason_surfaces_the_constant() -> None:
@@ -119,22 +124,11 @@ def test_every_hosted_script_path_exists_under_the_hooks_root() -> None:
119
124
  )
120
125
 
121
126
 
122
- def test_every_native_module_exposes_a_callable_evaluate() -> None:
123
- nativized_entries = [
124
- each_entry
125
- for each_entry in ALL_HOSTED_HOOK_ENTRIES
126
- if each_entry.native_module_name is not None
127
- ]
128
- assert nativized_entries, (
129
- "the roster must carry at least one nativized hook for this test to lock the contract"
130
- )
131
- for each_entry in nativized_entries:
132
- native_module = importlib.import_module(each_entry.native_module_name)
133
- evaluate_function = getattr(native_module, "evaluate", None)
134
- assert callable(evaluate_function), (
135
- f"{each_entry.native_module_name} must expose a callable named evaluate, "
136
- "matching the native_module_name docstring contract"
137
- )
127
+ def test_retired_native_detector_keeps_its_callable_linter_interface() -> None:
128
+ native_module = importlib.import_module("state_description_blocker")
129
+ assert native_module.find_violations("The API uses port 8080.", "guide.md") == []
130
+ assert native_module.find_violations("Previously set via env var.", "guide.md")
131
+ assert all(each_entry.native_module_name is None for each_entry in ALL_HOSTED_HOOK_ENTRIES)
138
132
 
139
133
 
140
134
  def test_four_way_set_covers_every_mutation_tool_name() -> None:
@@ -144,7 +138,7 @@ def test_four_way_set_covers_every_mutation_tool_name() -> None:
144
138
 
145
139
 
146
140
  def test_immediate_harm_hooks_reach_apply_patch() -> None:
147
- """PII, sensitive-path, existing-file, CODE_RULES, and TDD gates all see apply_patch."""
141
+ """PII, sensitive-path, and existing-file checks retain apply_patch coverage."""
148
142
  for each_script_path in ALL_IMMEDIATE_HARM_SCRIPT_PATHS:
149
143
  entry = _entry_for(each_script_path)
150
144
  assert entry is not None, f"{each_script_path} must stay on the hosted roster"
@@ -154,8 +148,6 @@ def test_immediate_harm_hooks_reach_apply_patch() -> None:
154
148
 
155
149
 
156
150
  _ALL_DEFERRED_LINT_SCRIPT_PATHS = (
157
- "blocking/state_description_blocker.py",
158
- "blocking/workflow_substitution_slot_blocker.py",
159
151
  "blocking/claude_md_orphan_file_blocker.py",
160
152
  "blocking/package_inventory_stale_blocker.py",
161
153
  "blocking/env_var_table_code_drift_blocker.py",
@@ -164,9 +156,6 @@ _ALL_DEFERRED_LINT_SCRIPT_PATHS = (
164
156
  "blocking/docstring_rule_gate_count_blocker.py",
165
157
  "blocking/duplicate_rmtree_helper_blocker.py",
166
158
  "validation/hook_format_validator.py",
167
- "blocking/hook_prose_detector_consistency.py",
168
- "blocking/subprocess_budget_completeness.py",
169
- "blocking/windows_rmtree_blocker.py",
170
159
  "advisory/refactor_guard.py",
171
160
  "advisory/migration_safety_advisor.py",
172
161
  )
@@ -177,7 +166,7 @@ def test_lint_and_advisory_hooks_stay_off_the_apply_patch_roster() -> None:
177
166
 
178
167
  apply_patch reaches only a gate whose violation is already real and
179
168
  unrecoverable the moment the call returns: a leaked secret, an exposed
180
- sensitive path, a blind overwrite, or an untested change landing. Every
169
+ sensitive path, or a blind overwrite. Every
181
170
  hook named here instead judges the quality of the authored content for a
182
171
  consequence the write itself does not cause right now: doc-inventory
183
172
  drift, prose or naming style, a future-runtime correctness pattern (an
@@ -225,13 +214,12 @@ def test_edit_and_multi_edit_applicable_sets_are_equal() -> None:
225
214
  )
226
215
 
227
216
 
228
- def test_multi_edit_widened_hooks_reach_multi_edit() -> None:
217
+ def test_retained_boundary_hooks_reach_multi_edit() -> None:
229
218
  all_multi_edit_widened_script_paths = (
230
219
  "blocking/write_existing_file_blocker.py",
231
- "blocking/code_rules_enforcer.py",
232
- "blocking/tdd_enforcer.py",
233
- "blocking/state_description_blocker.py",
234
- )
220
+ "blocking/sensitive_file_protector.py",
221
+ "blocking/pii_prevention_blocker.py",
222
+ )
235
223
  for each_script_path in all_multi_edit_widened_script_paths:
236
224
  entry = _entry_for(each_script_path)
237
225
  assert entry is not None, f"{each_script_path} must stay on the hosted roster"
@@ -101,13 +101,13 @@ try:
101
101
  for each_bootstrap_directory in (_hooks_root_directory, _blocking_directory):
102
102
  if each_bootstrap_directory not in sys.path:
103
103
  sys.path.insert(0, each_bootstrap_directory)
104
- from hooks_constants.gh_pr_author_swap_constants import BASH_TOOL_NAME
105
104
  from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin
106
105
  from hooks_constants.test_failure_recorder_constants import (
107
106
  ALL_CHAINING_OPERATOR_SUBSTRINGS,
108
107
  ALL_PYTEST_MENTION_SUBSTRINGS,
109
108
  ALL_PYTEST_PROGRAM_BASENAMES,
110
109
  ALL_REDIRECTION_TOKENS,
110
+ BASH_TOOL_NAME,
111
111
  EXIT_CODE_ERROR_PREFIX,
112
112
  MODULE_RUN_FLAG,
113
113
  NODE_ID_FILE_PATH_SEPARATOR,
@@ -24,7 +24,7 @@ exclude = [
24
24
 
25
25
 
26
26
  [[tool.mypy.overrides]]
27
- module = ["exempt_paths", "code_rules_path_utils", "gh_pr_author_enforcer"]
27
+ module = ["exempt_paths", "code_rules_path_utils"]
28
28
  warn_unused_ignores = false
29
29
 
30
30
  [[tool.mypy.overrides]]
@@ -0,0 +1,79 @@
1
+ """Tests for durable GitHub post body extraction."""
2
+
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+
7
+ from gh_post_body_texts import (
8
+ extract_gh_post_body_texts_for_privacy_gate,
9
+ extract_mcp_body_texts,
10
+ )
11
+
12
+
13
+ @pytest.mark.parametrize(
14
+ ("command", "expected_text"),
15
+ [
16
+ ('gh pr create --body "pull request text"', "pull request text"),
17
+ ('gh pr comment 12 --body="comment text"', "comment text"),
18
+ ('gh issue edit 7 -b "issue text"', "issue text"),
19
+ ('GH_HOST=github.example gh pr review 8 --body "review text"', "review text"),
20
+ ],
21
+ )
22
+ def test_inline_post_bodies_are_extracted(command: str, expected_text: str) -> None:
23
+ all_body_texts, deny_reason = extract_gh_post_body_texts_for_privacy_gate(command)
24
+ assert all_body_texts == [expected_text]
25
+ assert deny_reason is None
26
+
27
+
28
+ def test_relative_body_file_uses_supplied_working_directory(tmp_path: Path) -> None:
29
+ body_file = tmp_path / "body.md"
30
+ body_file.write_text("body file text", encoding="utf-8")
31
+ all_body_texts, deny_reason = extract_gh_post_body_texts_for_privacy_gate(
32
+ "gh pr comment 12 --body-file body.md",
33
+ str(tmp_path),
34
+ )
35
+ assert all_body_texts == ["body file text"]
36
+ assert deny_reason is None
37
+
38
+
39
+ @pytest.mark.parametrize("body_file_text", ["missing.md", "$BODY_FILE", "-"])
40
+ def test_unreadable_body_file_fails_closed(body_file_text: str) -> None:
41
+ all_body_texts, deny_reason = extract_gh_post_body_texts_for_privacy_gate(
42
+ f"gh pr comment 12 --body-file {body_file_text}"
43
+ )
44
+ assert all_body_texts == []
45
+ assert deny_reason is not None
46
+ assert "could not be read" in deny_reason
47
+
48
+
49
+ def test_invalid_utf8_body_file_fails_closed(tmp_path: Path) -> None:
50
+ body_file = tmp_path / "body.md"
51
+ body_file.write_bytes(b"\xff")
52
+ all_body_texts, deny_reason = extract_gh_post_body_texts_for_privacy_gate(
53
+ f'gh issue create --body-file "{body_file}"'
54
+ )
55
+ assert all_body_texts == []
56
+ assert deny_reason is not None
57
+
58
+
59
+ @pytest.mark.parametrize(
60
+ "command",
61
+ [
62
+ "gh pr view 12",
63
+ 'echo "gh pr comment 12 --body text"',
64
+ "git status",
65
+ "",
66
+ ],
67
+ )
68
+ def test_non_post_commands_have_no_body(command: str) -> None:
69
+ assert extract_gh_post_body_texts_for_privacy_gate(command) == ([], None)
70
+
71
+
72
+ def test_mcp_body_and_comment_values_are_extracted() -> None:
73
+ assert extract_mcp_body_texts(
74
+ {"body": "body text", "comment": "comment text", "title": "ignored"}
75
+ ) == ["body text", "comment text"]
76
+
77
+
78
+ def test_mcp_non_string_and_empty_values_are_ignored() -> None:
79
+ assert extract_mcp_body_texts({"body": "", "comment": 7}) == []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "4.2.0",
3
+ "version": "7.0.0",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,7 +9,7 @@
9
9
  "cde": "bin/cde.mjs"
10
10
  },
11
11
  "scripts": {
12
- "test": "node --test \"bin/*.test.mjs\" \".agents/skills/**/*.test.mjs\" \"tests/fresh-session/**/*.test.mjs\""
12
+ "test": "node --test \"bin/*.test.mjs\" \".agents/skills/**/*.test.mjs\" \"tests/fresh-session/**/*.test.mjs\" \"scripts/ci/*.test.mjs\""
13
13
  },
14
14
  "files": [
15
15
  "bin/",
@@ -1,38 +1,50 @@
1
- # Durable Post Artifacts
1
+ # GitHub post input rules
2
2
 
3
- **When this applies:** Any GitHub post that lives on the server — an issue, a pull request, a comment, or a review — created through `gh` (`gh pr create/comment/edit/review`, `gh issue create/comment/edit`) or a GitHub MCP post tool.
3
+ ## When this applies
4
+
5
+ Use this rule for a GitHub issue, pull request, comment, or review created
6
+ through `gh` or a GitHub MCP post tool.
4
7
 
5
8
  ## Rule
6
9
 
7
- A post lives forever. Job scratch directories, worktrees, and system temp folders do not — they are cleaned soon after the run that made them. So a post must never point at a path in that scratch. The moment the directory is cleaned, the reference breaks and the reader is left with a dead path.
10
+ A post remains after the job ends. Job scratch directories, worktrees, and
11
+ system temp folders do not. Do not put a path from one of those directories in
12
+ a post.
8
13
 
9
- Handle the two kinds of content differently:
14
+ Handle text and binary content differently:
10
15
 
11
- - **Text data** (logs, tables, diffs, stack traces): paste the actual text inline in the post body. Do not link a scratch file that holds it.
12
- - **Binary artifacts** (images, screenshots, archives): upload the file to the repository's durable `artifacts` release and link the permanent URL. Use the helper:
16
+ - **Text data** such as logs, tables, diffs, and stack traces belongs inline in
17
+ the post body. Do not link a scratch file that holds text data.
18
+ - **Binary artifacts** such as images, screenshots, and archives belong in the
19
+ repository's durable `artifacts` release. Use the helper:
13
20
 
14
21
  ```
15
22
  python3 ~/.claude/scripts/gh_artifact_upload.py <file-path> <owner/repo>
16
23
  ```
17
24
 
18
- It ensures the repository has a prerelease tagged `artifacts`, uploads the file under a `YYYYMMDD_HHMMSS_<name>` asset name, and prints the permanent download URL. Put that URL in the post.
25
+ The helper creates the `artifacts` prerelease when needed, uploads the file
26
+ under a `YYYYMMDD_HHMMSS_<name>` asset name, and prints a permanent download
27
+ URL. Put that URL in the post.
19
28
 
20
29
  ## Volatile paths that must not appear in a post body
21
30
 
22
- - A job scratch directory (`.claude-profile-a/jobs/`)
23
- - A worktree (`.claude/worktrees/`)
24
- - A system temp location (`AppData\Local\Temp`, `%TEMP%`, `$env:TEMP`, `/tmp/`)
25
- - The job scratch environment variable (`$CLAUDE_JOB_DIR`)
26
-
27
- Both slash directions count.
28
-
29
- The worktree and job-scratch entries count as a path when either:
31
+ - A job scratch directory: `.claude-profile-a/jobs/`
32
+ - A worktree: `.claude/worktrees/`
33
+ - A system temp location: `AppData\\Local\\Temp`, `%TEMP%`, `$env:TEMP`, or
34
+ `/tmp/`
35
+ - The job scratch environment variable: `$CLAUDE_JOB_DIR`
30
36
 
31
- - A `/` or `\` sits right before them — a drive-letter path (`C:\Users\me\.claude\worktrees\wt\f.py`), a home path (`~/.claude/worktrees/wt`), or a POSIX absolute path (`/home/me/.claude-profile-a/jobs/j/log.txt`).
32
- - A path segment follows them — a relative path that names a child under that directory (`see .claude/worktrees/wt-1/notes.md`, a markdown link target, or `cd .claude/worktrees/wt-199`).
37
+ Both slash directions count. The path rule applies when a slash or backslash
38
+ precedes a marker, or when a path segment follows it. A standalone directory
39
+ name does not form a path.
33
40
 
34
- With neither anchor the text names the directory rather than something inside it, and it posts — a quoted config constant, a backticked directory name, or a placeholder form such as `.claude/worktrees/<name>`. Un-backticked prose that puts a word immediately after the marker reads the same as a relative path and is blocked; the placeholder form is the documented escape.
41
+ ## Validation
35
42
 
36
- ## Enforcement
43
+ Resolve the active managed root (`CLAUDE_CONFIG_DIR` when set, `~/.claude`
44
+ otherwise), then run `<managed-root>/scripts/durable_post_lint.py` before the
45
+ server write. Pass the matching action and body file. Use `pr-create`,
46
+ `pr-edit`, `pr-comment`, `pr-review`, `issue-create`, `issue-edit`,
47
+ `issue-comment`, or `github-mcp-post`.
37
48
 
38
- The `volatile_path_in_post_blocker` PreToolUse hook reads the body of each `gh` post command and each GitHub MCP post call, scans it for these markers, and blocks the post when it finds one. For a `--body-file`, the hook reads the file and scans its contents, so writing the body to a temp file and passing it with `--body-file` stays allowed — what the hook rejects is a volatile path inside the text that gets posted.
49
+ The linter reads the body file and reports a volatile local path without
50
+ printing the body. Fix the body and rerun the linter before posting.
@@ -1,26 +1,36 @@
1
- # gh CLI Conventions
1
+ # gh CLI conventions
2
2
 
3
- Two call shapes the `gh` CLI gets wrong by default.
3
+ Two `gh` call shapes need explicit handling.
4
4
 
5
- ## Body content goes in a file
5
+ ## Put body content in a file
6
6
 
7
- Every `gh` command carrying markdown body content (`gh pr create/edit/comment/review`, `gh issue create/edit/comment`) uses `--body-file <path>` with a temp file — never a `--body` / `-b` string, where backticks land on GitHub as a literal `` \` ``. Write the temp file BOM-free:
7
+ Every `gh` command that carries markdown body content uses
8
+ `--body-file <path>`. This applies to `gh pr create`, `gh pr edit`,
9
+ `gh pr comment`, `gh pr review`, `gh issue create`, `gh issue edit`, and
10
+ `gh issue comment`. Never pass a `--body` or `-b` string. Write the file as
11
+ BOM-free UTF-8:
8
12
 
9
13
  ```powershell
10
14
  [IO.File]::WriteAllText($bodyPath, $body, [Text.UTF8Encoding]::new($false))
11
15
  ```
12
16
 
13
- MCP GitHub tools take `body` as a structured parameter and are unaffected.
17
+ MCP GitHub tools take `body` as a structured parameter. Write the same body to
18
+ a UTF-8 file and run the shared linter before sending that parameter.
14
19
 
15
- `gh_body_arg_blocker.py` (PreToolUse on Bash, hosted by `bash_pre_tool_use_dispatcher`) blocks `--body <arg>` and returns the corrective message.
20
+ For pull requests, use
21
+ `.agents/skills/pull-request/scripts/pull_request.py`. It passes
22
+ `--body-file` to `gh` after the action-aware linter succeeds. For issues and
23
+ GitHub MCP posts, run the linter directly with `issue-create`, `issue-edit`,
24
+ `issue-comment`, or `github-mcp-post` as the action.
16
25
 
17
26
  ## Paginated reads slurp before they filter
18
27
 
19
- Every `gh api` read of a paginated GitHub list endpoint (PR `reviews` / `comments` / `files`, issue `comments`, `pulls`, `issues`) uses `--paginate --slurp` piped to **external** `jq`. The built-in `--jq` runs per page, so a cross-page operation like `sort_by | last` gives a wrong-but-confident result.
28
+ Every `gh api` read of a paginated GitHub list endpoint uses
29
+ `--paginate --slurp` and pipes the result to external `jq`. This applies to PR
30
+ reviews, comments, and files, plus issue comments, pulls, and issues. The
31
+ built-in `--jq` runs once per page and can produce a wrong cross-page result.
20
32
 
21
- Single-object endpoints (`pulls/<n>`, `issues/<n>`) skip pagination and may use `--jq` directly. For a newest-first walk, sort the slurped array and take the last element; for single-page bounds, cap with a `per_page` query parameter.
22
-
23
- ## Sibling rules
24
-
25
- - [`destructive-commands.md`](destructive-commands.md) — why a body describing `rm -rf` must travel by file path.
26
- - [`durable-post-artifacts.md`](durable-post-artifacts.md) — what a post body may reference.
33
+ Single-object endpoints such as `pulls/<n>` and `issues/<n>` do not need
34
+ pagination and may use `--jq` directly. For a newest-first walk, sort the
35
+ slurped array and take the last element. For one page, cap the request with a
36
+ `per_page` query parameter.
@@ -1,8 +1,8 @@
1
- # Git Workflow
1
+ # Git workflow
2
2
 
3
- User-level rule: applies to **every** git repo that uses GitHub with `gh` (no exceptions for “small” or non-primary repos unless the user says otherwise in the session).
3
+ User-level rule: applies to **every** git repo that uses GitHub with `gh`. Small or non-primary repos follow the same rule unless the user says otherwise in the session.
4
4
 
5
- ## Workflow Decision Tree
5
+ ## Workflow decision tree
6
6
 
7
7
  **When to use stacked PRs:** Feature B depends on Feature A's implementation
8
8
 
@@ -13,19 +13,32 @@ User-level rule: applies to **every** git repo that uses GitHub with `gh` (no ex
13
13
  2. Get reviewed and MERGE infrastructure first
14
14
  3. Launch parallel feature PRs that use merged infrastructure
15
15
 
16
- ## PR Submission Rules
16
+ ## Pull request submission rules
17
17
 
18
18
  **ALWAYS create PRs as DRAFT:** Use `gh pr create --draft` for ALL PRs
19
19
 
20
- **The pr-description-writer agent writes the body:** Spawn the `pr-description-writer` agent (Agent tool, `subagent_type` `pr-description-writer`), then publish what it returns with `--body-file`. `pr_description_writer_gate.py` (PreToolUse on Bash and PowerShell, hosted by `bash_pre_tool_use_dispatcher`) denies a `gh pr create` when the session records no such spawn. A trailing `# pr-description-skip` comment on the command opts out for a body you write yourself.
20
+ Use the `pr-description-writer` agent before creating a pull request or
21
+ rewriting its full description. Publish its title and body file through
22
+ `.agents/skills/pull-request/scripts/pull_request.py`.
21
23
 
22
- ## Git Golden Rules (NON-NEGOTIABLE)
24
+ Resolve the active managed root (`CLAUDE_CONFIG_DIR` when set, `~/.claude`
25
+ otherwise), then run `<managed-root>/scripts/durable_post_lint.py` before any
26
+ pull request, issue, or GitHub MCP post. The linter checks the action-specific
27
+ title, body, and volatile-path rules before credential lookup or network
28
+ access.
29
+
30
+ Use `.agents/skills/pull-request/scripts/recover_legacy_author.py
31
+ <exact-state-file> --confirm-inactive` only for one explicitly selected legacy
32
+ author record. Do not infer a record from age alone. Keep every other record
33
+ untouched.
34
+
35
+ ## Git golden rules
23
36
 
24
37
  1. **DRAFT BEFORE PUSH**: When pushing ANYTHING to a PR, it MUST be in draft state first
25
38
  - Before push: `gh pr ready --undo`
26
39
  - After review approved: `gh pr ready`
27
40
 
28
- ## Never Commit Working Documents or Images
41
+ ## Never commit working documents or images
29
42
 
30
43
  **NEVER commit these files to the repo:**
31
44
 
@@ -49,4 +62,4 @@ An image a PR needs as visual evidence is not an exception to that row. Upload i
49
62
 
50
63
  Repair only reported findings, then re-verify after every repair.
51
64
 
52
- Every `gh` post in this workflow uses `--body-file` per `gh-cli-conventions.md` and keeps volatile scratch paths out per `durable-post-artifacts.md`; stage session edits per `re-stage-before-commit.md` before each commit.
65
+ Every `gh` post in this workflow uses `--body-file` per `gh-cli-conventions.md` and keeps volatile scratch paths out per `durable-post-artifacts.md`. Stage session edits per `re-stage-before-commit.md` before each commit.
package/scripts/check.ps1 CHANGED
@@ -152,7 +152,6 @@ if (-not $SkipMypy) {
152
152
  copilot_quota.py `
153
153
  fix_hookspath.py `
154
154
  grant_project_claude_permissions.py `
155
- post_audit_thread.py `
156
155
  preflight_self_heal.py `
157
156
  preflight.py `
158
157
  reviewer_availability.py `
@@ -1,29 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * Smoke driver for the claude-dev-env installer.
4
- *
5
- * The installer (bin/install.mjs) writes into os.homedir()/.claude and
6
- * os.homedir()/.agents and has
7
- * three global side effects: it copies files under ~/.claude/ and ~/.agents/, sets
8
- * `git config --global core.hooksPath`, and writes ~/.mypy.ini. There is no
9
- * flag to redirect the target, so this driver isolates every side effect by
10
- * spawning the installer with HOME, USERPROFILE, and GIT_CONFIG_GLOBAL all
11
- * pointed at a throwaway sandbox directory. Node's os.homedir() follows
12
- * USERPROFILE on Windows and HOME on POSIX, so setting both redirects the
13
- * install target on either platform without touching the real ~/.claude/.
14
- *
15
- * The isolated GIT_CONFIG_GLOBAL is seeded with `safe.directory = *` so the
16
- * installer's `git status` source-conflict guard runs even when the checkout
17
- * lives somewhere git flags as dubious ownership (for example a UNC network
18
- * share), while the installer's `git config --global core.hooksPath` write
19
- * still lands only in the sandbox config.
20
- *
21
- * It runs the three lifecycle commands an agent cares about — `--help`, a full
22
- * install, and `--uninstall` — and asserts the observable result of each
23
- * against the sandbox tree. Exit code 0 means every assertion held.
24
- *
25
- * Usage: node .claude/skills/run-claude-dev-env/driver.mjs
26
- */
27
2
 
28
3
  import { spawnSync } from 'node:child_process';
29
4
  import {
@@ -41,7 +16,7 @@ import { tmpdir } from 'node:os';
41
16
  import { fileURLToPath } from 'node:url';
42
17
 
43
18
  const DRIVER_DIRECTORY = dirname(fileURLToPath(import.meta.url));
44
- const PACKAGE_ROOT = resolve(DRIVER_DIRECTORY, '..', '..', '..');
19
+ const PACKAGE_ROOT = resolve(DRIVER_DIRECTORY, '..', '..');
45
20
  const INSTALL_ENTRY = join(PACKAGE_ROOT, 'bin', 'install.mjs');
46
21
 
47
22
  const checks = [];
@@ -68,7 +43,7 @@ function collectFilesRecursive(directory) {
68
43
 
69
44
  function runInstaller(sandboxHome, installerArguments) {
70
45
  const gitConfigGlobal = join(sandboxHome, '.gitconfig-sandbox');
71
- const result = spawnSync(process.execPath, [INSTALL_ENTRY, ...installerArguments], {
46
+ const installerProcess = spawnSync(process.execPath, [INSTALL_ENTRY, ...installerArguments], {
72
47
  cwd: PACKAGE_ROOT,
73
48
  encoding: 'utf8',
74
49
  env: {
@@ -78,7 +53,7 @@ function runInstaller(sandboxHome, installerArguments) {
78
53
  GIT_CONFIG_GLOBAL: gitConfigGlobal,
79
54
  },
80
55
  });
81
- return result;
56
+ return installerProcess;
82
57
  }
83
58
 
84
59
  function main() {