claude-dev-env 4.3.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 (293) 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 +33 -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 +1 -3
  18. package/.agents/skills/skill-builder/SKILL.md +3 -196
  19. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
  20. package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
  21. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
  22. package/bin/install.agents-home.test.mjs +15 -13
  23. package/bin/install.mjs +28 -0
  24. package/bin/install.policy-lint.test.mjs +78 -0
  25. package/bin/install.prune.test.mjs +59 -20
  26. package/bin/install.pstack-plugin.test.mjs +114 -0
  27. package/bin/install.test.mjs +37 -0
  28. package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
  29. package/hooks/advisory/pr_done_reminder.py +342 -0
  30. package/hooks/advisory/test_pr_done_reminder.py +249 -0
  31. package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
  32. package/hooks/blocking/code_rules_paired_test.py +22 -15
  33. package/hooks/blocking/test_bash_post_call_dispatcher.py +65 -4
  34. package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
  35. package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
  36. package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
  37. package/hooks/blocking/test_pre_tool_use_dispatcher.py +68 -133
  38. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
  39. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +18 -18
  40. package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
  41. package/hooks/git-hooks/pre_commit.py +52 -11
  42. package/hooks/git-hooks/test_installed_policy_lint.py +159 -0
  43. package/hooks/git-hooks/test_native_policy_commit.py +87 -0
  44. package/hooks/git-hooks/test_pre_commit.py +246 -0
  45. package/hooks/hooks.json +1 -1
  46. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -1
  47. package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
  48. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -43
  49. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +19 -2
  50. package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
  51. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +20 -32
  52. package/package.json +2 -2
  53. package/rules/durable-post-artifacts.md +2 -1
  54. package/rules/git-workflow.md +6 -6
  55. package/scripts/check.ps1 +0 -1
  56. package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
  57. package/scripts/ci/installer-lifecycle.test.mjs +26 -0
  58. package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
  59. package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
  60. package/{.agents/skills/_shared/pr-loop/scripts → scripts}/durable_post_lint.py +5 -41
  61. package/{.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/durable_post_lint_constants.py → scripts/durable_post_lint_config/config/constants.py} +0 -10
  62. package/{.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint_constants.py → scripts/durable_post_lint_config/config/test_constants.py} +1 -1
  63. package/scripts/policy_lint/adapter_detectors.py +13 -6
  64. package/scripts/policy_lint/adapters.py +7 -4
  65. package/scripts/policy_lint/config/constants.py +5 -0
  66. package/scripts/policy_lint/engine.py +48 -4
  67. package/scripts/policy_lint/model.py +1 -0
  68. package/scripts/policy_lint/registry.py +1 -1
  69. package/scripts/policy_lint/selection.py +2 -1
  70. package/scripts/policy_lint/selection_git.py +2 -0
  71. package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
  72. package/scripts/shared_tree_paths.py +32 -3
  73. package/scripts/test_shared_tree_paths.py +33 -0
  74. package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
  75. package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
  76. package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
  77. package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
  78. package/scripts/tests/test_policy_lint_selection.py +25 -2
  79. package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
  80. package/scripts/tests/test_rules.py +3 -7
  81. package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
  82. package/.agents/skills/_shared/pr-loop/AGENTS.md +0 -1
  83. package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
  84. package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
  85. package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
  86. package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
  87. package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
  88. package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
  89. package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
  90. package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
  91. package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
  92. package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
  93. package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
  94. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
  95. package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
  96. package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
  97. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
  98. package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
  99. package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
  100. package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
  101. package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
  102. package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
  103. package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
  104. package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
  105. package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
  106. package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
  107. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
  108. package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
  109. package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
  110. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
  111. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
  112. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
  113. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
  114. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
  115. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
  116. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
  117. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
  118. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
  119. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
  120. package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
  121. package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
  122. package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
  123. package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
  124. package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
  125. package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
  126. package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
  127. package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
  128. package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
  129. package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
  130. package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
  131. package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
  132. package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
  133. package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
  134. package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
  135. package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
  136. package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
  137. package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
  138. package/.agents/skills/autoconverge/AGENTS.md +0 -1
  139. package/.agents/skills/autoconverge/SKILL.md +0 -431
  140. package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
  141. package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
  142. package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
  143. package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
  144. package/.agents/skills/autoconverge/reference/convergence.md +0 -219
  145. package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
  146. package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
  147. package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
  148. package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
  149. package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
  150. package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
  151. package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
  152. package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
  153. package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
  154. package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
  155. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
  156. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
  157. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
  158. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
  159. package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
  160. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
  161. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
  162. package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
  163. package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
  164. package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
  165. package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
  166. package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
  167. package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
  168. package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
  169. package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
  170. package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
  171. package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
  172. package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
  173. package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
  174. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
  175. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
  176. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
  177. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
  178. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
  179. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
  180. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
  181. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
  182. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
  183. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
  184. package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
  185. package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
  186. package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
  187. package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
  188. package/.agents/skills/hitl/SKILL.md +0 -45
  189. package/.agents/skills/pr-cleanup/SKILL.md +0 -140
  190. package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
  191. package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
  192. package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
  193. package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
  194. package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
  195. package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
  196. package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
  197. package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
  198. package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
  199. package/.agents/skills/pr-refinement/SKILL.md +0 -31
  200. package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
  201. package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
  202. package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
  203. package/.agents/skills/pr-small-cl/SKILL.md +0 -52
  204. package/.agents/skills/pr-title-description/SKILL.md +0 -96
  205. package/.agents/skills/prototype/SKILL.md +0 -85
  206. package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
  207. package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
  208. package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
  209. package/.agents/skills/prototype/scripts/conftest.py +0 -15
  210. package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
  211. package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
  212. package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
  213. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  214. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
  215. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
  216. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
  217. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
  218. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
  219. package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
  220. package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
  221. package/.agents/skills/prototype/workflows/promotion.md +0 -25
  222. package/.agents/skills/prototype/workflows/sandbox.md +0 -35
  223. package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
  224. package/.agents/skills/rebase/AGENTS.md +0 -1
  225. package/.agents/skills/rebase/SKILL.md +0 -178
  226. package/.agents/skills/review-router/SKILL.md +0 -51
  227. package/.agents/skills/review-router/reference/route-policy.json +0 -7
  228. package/.agents/skills/review-router/reference/task-seeds.md +0 -1
  229. package/.agents/skills/review-router/scripts/conftest.py +0 -4
  230. package/.agents/skills/review-router/scripts/review_router.py +0 -50
  231. package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
  232. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
  233. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  234. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
  235. package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
  236. package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
  237. package/.agents/skills/review-tier/SKILL.md +0 -56
  238. package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
  239. package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
  240. package/.agents/skills/review-tier/scripts/conftest.py +0 -4
  241. package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
  242. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
  243. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  244. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
  245. package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
  246. package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
  247. package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
  248. package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
  249. package/.agents/skills/session-log/AGENTS.md +0 -1
  250. package/.agents/skills/session-log/SKILL.md +0 -170
  251. package/.agents/skills/session-log/templates/frontmatter.md +0 -40
  252. package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
  253. package/.agents/skills/session-tidy/AGENTS.md +0 -1
  254. package/.agents/skills/session-tidy/SKILL.md +0 -191
  255. package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
  256. package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
  257. package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
  258. package/.agents/skills/skill-builder/references/description-field.md +0 -111
  259. package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
  260. package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
  261. package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
  262. package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
  263. package/.agents/skills/skill-builder/references/skill-types.md +0 -232
  264. package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
  265. package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
  266. package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
  267. package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
  268. package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
  269. package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
  270. package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
  271. package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
  272. package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
  273. package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
  274. package/.agents/skills/update/.claude/CLAUDE.md +0 -1
  275. package/.agents/skills/update/AGENTS.md +0 -1
  276. package/.agents/skills/update/SKILL.md +0 -190
  277. package/_shared/pr-loop/AGENTS.md +0 -1
  278. package/_shared/pr-loop/CLAUDE.md +0 -1
  279. package/_shared/pr-loop/audit-contract.md +0 -183
  280. package/_shared/pr-loop/audit-reply-template.md +0 -151
  281. package/_shared/pr-loop/code-rules-gate.md +0 -65
  282. package/_shared/pr-loop/fix-protocol.md +0 -60
  283. package/_shared/pr-loop/gh-payloads.md +0 -75
  284. package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
  285. package/_shared/pr-loop/precatch-rubric.md +0 -66
  286. package/_shared/pr-loop/preflight-proposal.md +0 -56
  287. package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
  288. package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
  289. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
  290. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
  291. package/_shared/pr-loop/state-schema.md +0 -45
  292. package/_shared/pr-loop/worker-spawn.md +0 -189
  293. /package/{.agents/skills/_shared/pr-loop/scripts → scripts}/test_durable_post_lint.py +0 -0
@@ -1,8 +1,9 @@
1
1
  """Constants for the PreToolUse dispatcher that hosts Write/Edit/MultiEdit hooks.
2
2
 
3
- Holds the ordered hosted-hook list with per-hook applicable-tool sets, the
4
- special exit codes, the deny decision string, and the hook-event name. The
5
- dispatcher imports each of these by name.
3
+ The roster keeps action-boundary checks, repository checks, and checks without
4
+ a proven local replacement. File-policy rules with a staged replacement run
5
+ through cde lint in native pre-commit. Their detectors stay importable for that
6
+ linter. Bash cleanup protection uses its own roster.
6
7
  """
7
8
 
8
9
  from __future__ import annotations
@@ -55,14 +56,10 @@ ALL_WRITE_EDIT_MULTI_EDIT_APPLY_PATCH_TOOL_NAMES: frozenset[str] = frozenset(
55
56
  ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES | {APPLY_PATCH_TOOL_NAME}
56
57
  )
57
58
 
58
- # apply_patch reaches this narrower roster: the gates whose miss causes immediate
59
- # harm, plus the TDD gate and the edit tracker. It stays off the full lint surface.
60
59
  ALL_IMMEDIATE_HARM_SCRIPT_PATHS: tuple[str, ...] = (
61
60
  "blocking/pii_prevention_blocker.py",
62
61
  "blocking/sensitive_file_protector.py",
63
62
  "blocking/write_existing_file_blocker.py",
64
- "blocking/code_rules_enforcer.py",
65
- "blocking/tdd_enforcer.py",
66
63
  )
67
64
 
68
65
 
@@ -71,19 +68,18 @@ STATE_DESCRIPTION_BLOCKER_MODULE_NAME = "state_description_blocker"
71
68
 
72
69
  @dataclass(frozen=True)
73
70
  class HostedHookEntry:
74
- """A single hosted hook with its applicable-tools constraint and blocking flag.
71
+ """One hosted hook, the tools it applies to, and whether a crash blocks.
75
72
 
76
73
  Attributes:
77
74
  script_relative_path: Hook path relative to the hooks/ directory.
78
75
  applicable_tool_names: Tool names this hook applies to. The dispatcher
79
76
  skips the hook when the payload's tool is not in this set.
80
- is_blocking: True when a crash surfaces a blocking signal; False when the
77
+ is_blocking: True when a crash is a blocking signal. False when the
81
78
  hook is advisory and a crash stays silent.
82
- native_module_name: The importable module name whose evaluate function
83
- the dispatcher calls in-process for this hook, or None when the hook
84
- runs via runpy under __main__. The named module exposes a function
85
- named `evaluate` taking the payload dict and returning a deny-reason
86
- string or None.
79
+ native_module_name: Importable module whose evaluate function the
80
+ dispatcher calls in-process, or None when the hook runs via runpy
81
+ under __main__. evaluate takes the payload dict and returns a
82
+ deny-reason string or None.
87
83
  """
88
84
 
89
85
  script_relative_path: str
@@ -109,39 +105,10 @@ ALL_HOSTED_HOOK_ENTRIES: tuple[HostedHookEntry, ...] = (
109
105
  script_relative_path="validation/hook_format_validator.py",
110
106
  applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
111
107
  ),
112
- HostedHookEntry(
113
- script_relative_path="blocking/code_rules_enforcer.py",
114
- applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_APPLY_PATCH_TOOL_NAMES,
115
- ),
116
- HostedHookEntry(
117
- script_relative_path="blocking/tdd_enforcer.py",
118
- applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_APPLY_PATCH_TOOL_NAMES,
119
- ),
120
- HostedHookEntry(
121
- script_relative_path="blocking/windows_rmtree_blocker.py",
122
- applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
123
- ),
124
108
  HostedHookEntry(
125
109
  script_relative_path="blocking/duplicate_rmtree_helper_blocker.py",
126
110
  applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
127
111
  ),
128
- HostedHookEntry(
129
- script_relative_path="blocking/state_description_blocker.py",
130
- applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
131
- native_module_name=STATE_DESCRIPTION_BLOCKER_MODULE_NAME,
132
- ),
133
- HostedHookEntry(
134
- script_relative_path="blocking/subprocess_budget_completeness.py",
135
- applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
136
- ),
137
- HostedHookEntry(
138
- script_relative_path="blocking/hook_prose_detector_consistency.py",
139
- applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
140
- ),
141
- HostedHookEntry(
142
- script_relative_path="blocking/workflow_substitution_slot_blocker.py",
143
- applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
144
- ),
145
112
  HostedHookEntry(
146
113
  script_relative_path="blocking/claude_md_orphan_file_blocker.py",
147
114
  applicable_tool_names=ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES,
@@ -1,16 +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_test_failure_recorder_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 "observability/test_failure_recorder.py" in all_script_paths
14
+ assert all_script_paths == [
15
+ "observability/test_failure_recorder.py",
16
+ "advisory/pr_done_reminder.py",
17
+ ]
18
+
19
+
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"
14
31
 
15
32
 
16
33
  def test_every_roster_entry_applies_to_bash() -> None:
@@ -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"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "4.3.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/",
@@ -40,7 +40,8 @@ name does not form a path.
40
40
 
41
41
  ## Validation
42
42
 
43
- Run `.agents/skills/_shared/pr-loop/scripts/durable_post_lint.py` before the
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
44
45
  server write. Pass the matching action and body file. Use `pr-create`,
45
46
  `pr-edit`, `pr-comment`, `pr-review`, `issue-create`, `issue-edit`,
46
47
  `issue-comment`, or `github-mcp-post`.
@@ -19,13 +19,13 @@ User-level rule: applies to **every** git repo that uses GitHub with `gh`. Small
19
19
 
20
20
  Use the `pr-description-writer` agent before creating a pull request or
21
21
  rewriting its full description. Publish its title and body file through
22
- `.agents/skills/pull-request/scripts/pull_request.py`. The
23
- `pr-title-description` skill may review the writer's output but cannot replace
24
- the required writer.
22
+ `.agents/skills/pull-request/scripts/pull_request.py`.
25
23
 
26
- Run `_shared/pr-loop/scripts/durable_post_lint.py` before any pull request,
27
- issue, or GitHub MCP post. The linter checks the action-specific title, body,
28
- and volatile-path rules before credential lookup or network access.
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
29
 
30
30
  Use `.agents/skills/pull-request/scripts/recover_legacy_author.py
31
31
  <exact-state-file> --confirm-inactive` only for one explicitly selected legacy
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() {
@@ -0,0 +1,26 @@
1
+ import assert from 'node:assert/strict';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { readFileSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+ import test from 'node:test';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const CI_DIRECTORY = dirname(fileURLToPath(import.meta.url));
9
+ const DRIVER_PATH = join(CI_DIRECTORY, 'installer-lifecycle.mjs');
10
+ const DRIVER_TIMEOUT_MILLISECONDS = 60_000;
11
+
12
+ test('the installer lifecycle driver completes in an isolated home', () => {
13
+ const lifecycleProcess = spawnSync(process.execPath, [DRIVER_PATH], {
14
+ encoding: 'utf8',
15
+ timeout: DRIVER_TIMEOUT_MILLISECONDS,
16
+ });
17
+ assert.equal(lifecycleProcess.status, 0, `${lifecycleProcess.stdout}\n${lifecycleProcess.stderr}`);
18
+ assert.match(lifecycleProcess.stdout, /ALL CHECKS PASSED/);
19
+ assert.match(lifecycleProcess.stdout, /Sandbox removed:/);
20
+ });
21
+
22
+ test('the Windows adapter invokes the CI-owned lifecycle driver', () => {
23
+ const adapterSource = readFileSync(join(CI_DIRECTORY, 'windows-installer-lifecycle.ps1'), 'utf8');
24
+ assert.match(adapterSource, /\$driverPath = Join-Path \$PSScriptRoot 'installer-lifecycle\.mjs'/);
25
+ assert.doesNotMatch(adapterSource, /skills\\run-claude-dev-env/);
26
+ });
@@ -1,13 +1,3 @@
1
- # Windows CI adapter for the Node installer lifecycle driver.
2
- #
3
- # The driver already redirects HOME, USERPROFILE, and GIT_CONFIG_GLOBAL into a
4
- # throwaway sandbox. This adapter records that isolation contract, runs the
5
- # 16-check driver, and writes bounded evidence under -EvidenceRoot so a failed
6
- # Windows runner leaves an actionable artifact.
7
- #
8
- # Usage (from packages/claude-dev-env):
9
- # pwsh -NoProfile -File scripts/ci/windows-installer-lifecycle.ps1 -EvidenceRoot <dir>
10
-
11
1
  [CmdletBinding()]
12
2
  param(
13
3
  [Parameter(Mandatory = $true)]
@@ -17,7 +7,7 @@ param(
17
7
  $ErrorActionPreference = 'Stop'
18
8
 
19
9
  $packageRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
20
- $driverPath = Join-Path $packageRoot '.claude\skills\run-claude-dev-env\driver.mjs'
10
+ $driverPath = Join-Path $PSScriptRoot 'installer-lifecycle.mjs'
21
11
  $allIsolationEnvNames = @(
22
12
  'HOME',
23
13
  'USERPROFILE',
@@ -1,8 +1,4 @@
1
- """Constants for resolving directories inside the installed shared tree.
2
-
3
- These name the shape of the installed tree itself, not any one worker or
4
- skill that reads it, so every caller of shared_tree_paths shares them.
5
- """
1
+ """Directory names for the installed shared tree."""
6
2
 
7
3
  from __future__ import annotations
8
4
 
@@ -15,3 +11,6 @@ SCRIPTS_DIRECTORY_NAME: str = "scripts"
15
11
  PROCESS_TREE_DIRECTORY_NAME: str = "process-tree"
16
12
 
17
13
  PROCESS_TREE_KILL_MODULE_FILENAME: str = "process_tree_kill.py"
14
+
15
+ AGENTS_DIRECTORY_SUFFIX: str = ".agents"
16
+ DEFAULT_MANAGED_ROOT_NAME: str = ".claude"
@@ -8,7 +8,7 @@ from collections.abc import Sequence
8
8
  from dataclasses import dataclass
9
9
  from pathlib import Path
10
10
 
11
- from skills_pr_loop_constants.durable_post_lint_constants import (
11
+ from durable_post_lint_config.config.constants import (
12
12
  ACTION_PR_CREATE,
13
13
  ACTION_PR_EDIT,
14
14
  ALL_BARE_VOLATILE_PATH_MARKERS,
@@ -82,14 +82,7 @@ def _text_has_anchored_marker(normalized_text: str, marker: str) -> bool:
82
82
 
83
83
 
84
84
  def find_volatile_path_marker(body_text: str) -> str | None:
85
- """Return the first volatile path marker in a GitHub post body.
86
-
87
- Args:
88
- body_text: Post body text to scan.
89
-
90
- Returns:
91
- The matched marker, or None when the body names no volatile path.
92
- """
85
+ """Return the first volatile path marker in a GitHub post body."""
93
86
  normalized_text = body_text.replace("\\", PATH_ANCHOR_CHARACTER).lower()
94
87
  for each_marker in ALL_PATH_ANCHORED_VOLATILE_PATH_MARKERS:
95
88
  if _text_has_anchored_marker(normalized_text, each_marker):
@@ -101,17 +94,7 @@ def find_volatile_path_marker(body_text: str) -> str | None:
101
94
 
102
95
 
103
96
  def read_body_file(body_file: Path) -> str:
104
- """Read one UTF-8 body file without exposing its contents on failure.
105
-
106
- Args:
107
- body_file: Local post body file.
108
-
109
- Returns:
110
- Decoded body text.
111
-
112
- Raises:
113
- DurablePostInputError: The file is missing, unreadable, or invalid UTF-8.
114
- """
97
+ """Read one UTF-8 body file without exposing its contents on failure."""
115
98
  try:
116
99
  return body_file.read_text(encoding=BODY_FILE_ENCODING)
117
100
  except (OSError, UnicodeError) as error:
@@ -184,19 +167,7 @@ def lint_durable_post(
184
167
  title: str | None,
185
168
  body_text: str | None,
186
169
  ) -> tuple[DurablePostFinding, ...]:
187
- """Return content findings for one locally valid GitHub post request.
188
-
189
- Args:
190
- action: GitHub post action name.
191
- title: Pull request title when the action accepts one.
192
- body_text: Decoded body text when the action accepts a body.
193
-
194
- Returns:
195
- Content findings in stable rule order.
196
-
197
- Raises:
198
- DurablePostUsageError: The action and supplied fields are not a valid request.
199
- """
170
+ """Return content findings for one locally valid GitHub post request."""
200
171
  _validate_request_shape(action, title, body_text)
201
172
  return (*_title_findings(title), *_body_findings(action, body_text))
202
173
 
@@ -210,14 +181,7 @@ def _parse_arguments(all_arguments: Sequence[str]) -> argparse.Namespace:
210
181
 
211
182
 
212
183
  def main(all_arguments: Sequence[str]) -> int:
213
- """Validate one GitHub post and return its result status.
214
-
215
- Args:
216
- all_arguments: Command arguments after the script name.
217
-
218
- Returns:
219
- Zero when clean, one for content findings, or two for invalid input.
220
- """
184
+ """Validate one GitHub post and return its result status."""
221
185
  arguments = _parse_arguments(all_arguments)
222
186
  try:
223
187
  body_text = (
@@ -23,7 +23,6 @@ ALL_POST_ACTIONS: frozenset[str] = frozenset(
23
23
  ACTION_PR_REVIEW,
24
24
  }
25
25
  )
26
-
27
26
  ALL_BODY_REQUIRED_ACTIONS: frozenset[str] = frozenset(
28
27
  {
29
28
  ACTION_GITHUB_MCP_POST,
@@ -35,13 +34,10 @@ ALL_BODY_REQUIRED_ACTIONS: frozenset[str] = frozenset(
35
34
  ACTION_PR_REVIEW,
36
35
  }
37
36
  )
38
-
39
37
  ALL_PR_DESCRIPTION_ACTIONS: frozenset[str] = frozenset(
40
38
  {ACTION_PR_CREATE, ACTION_PR_EDIT}
41
39
  )
42
-
43
40
  ALL_TITLE_ACTIONS: frozenset[str] = frozenset({ACTION_PR_CREATE, ACTION_PR_EDIT})
44
-
45
41
  ALL_CONVENTIONAL_TITLE_TYPES: tuple[str, ...] = (
46
42
  "build",
47
43
  "chore",
@@ -55,11 +51,9 @@ ALL_CONVENTIONAL_TITLE_TYPES: tuple[str, ...] = (
55
51
  "style",
56
52
  "test",
57
53
  )
58
-
59
54
  CONVENTIONAL_TITLE_PATTERN: re.Pattern[str] = re.compile(
60
55
  rf"^(?:{'|'.join(ALL_CONVENTIONAL_TITLE_TYPES)})(?:\([^)\r\n]+\))?!?: [^\s].*$"
61
56
  )
62
-
63
57
  ALL_REQUIRED_PR_DESCRIPTION_HEADINGS: tuple[str, ...] = (
64
58
  "Summary",
65
59
  "Description",
@@ -67,12 +61,10 @@ ALL_REQUIRED_PR_DESCRIPTION_HEADINGS: tuple[str, ...] = (
67
61
  "How",
68
62
  "Verification",
69
63
  )
70
-
71
64
  ALL_PATH_ANCHORED_VOLATILE_PATH_MARKERS: tuple[str, ...] = (
72
65
  ".claude-profile-a/jobs/",
73
66
  ".claude/worktrees/",
74
67
  )
75
-
76
68
  ALL_BARE_VOLATILE_PATH_MARKERS: tuple[str, ...] = (
77
69
  "appdata/local/temp",
78
70
  "/tmp/",
@@ -80,7 +72,6 @@ ALL_BARE_VOLATILE_PATH_MARKERS: tuple[str, ...] = (
80
72
  "$env:temp",
81
73
  "$claude_job_dir",
82
74
  )
83
-
84
75
  BODY_FILE_ENCODING = "utf-8"
85
76
  CLEAN_EXIT_CODE = 0
86
77
  CONTENT_FINDING_EXIT_CODE = 1
@@ -88,7 +79,6 @@ EMPTY_BODY_CODE = "empty-body"
88
79
  INPUT_ERROR_EXIT_CODE = 2
89
80
  PATH_ANCHOR_CHARACTER = "/"
90
81
  PATH_SEGMENT_START_CHARACTERS = "_-"
91
-
92
82
  BODY_FILE_UNREADABLE_MESSAGE = "body file is not readable UTF-8 text"
93
83
  BODY_REQUIRED_MESSAGE = "this action requires a body file"
94
84
  BODY_MUST_NOT_BE_EMPTY_MESSAGE = "body file must contain text"
@@ -1,6 +1,6 @@
1
1
  """Tests for durable GitHub post linter constants."""
2
2
 
3
- from skills_pr_loop_constants.durable_post_lint_constants import (
3
+ from durable_post_lint_config.config.constants import (
4
4
  ALL_CONVENTIONAL_TITLE_TYPES,
5
5
  ALL_POST_ACTIONS,
6
6
  CONVENTIONAL_TITLE_PATTERN,
@@ -29,24 +29,31 @@ def terminology_diagnostics(
29
29
  document_set: DocumentSet,
30
30
  load_module: adapter_support.HookModuleLoader,
31
31
  ) -> tuple[Diagnostic, ...]:
32
- """Report staged prose that near-misses a newly introduced identifier.
32
+ """Report prose that near-misses a newly introduced identifier.
33
33
 
34
34
  Args:
35
- document_set: Candidate staged documents and repository root.
35
+ document_set: Staged or base change selection and repository root.
36
36
  load_module: Shared script module loader.
37
37
 
38
38
  Returns:
39
39
  Diagnostics that name the prose file and line.
40
40
  """
41
- if document_set.selection is not SelectionKind.STAGED:
41
+ all_arguments: tuple[Path | str, ...]
42
+ if document_set.selection is SelectionKind.BASE:
43
+ if document_set.base_revision is None:
44
+ raise ValueError("Base selection is missing its comparison revision")
45
+ checker_name = "strict_base_terminology_findings"
46
+ all_arguments = (document_set.repository_root, document_set.base_revision)
47
+ elif document_set.selection is SelectionKind.STAGED:
48
+ checker_name = "strict_staged_terminology_findings"
49
+ all_arguments = (document_set.repository_root,)
50
+ else:
42
51
  return ()
43
52
  terminology_module = load_module(constants.TERMINOLOGY_SWEEP_MODULE_NAME)
44
53
  helper_stdout = StringIO()
45
54
  helper_stderr = StringIO()
46
55
  with redirect_stdout(helper_stdout), redirect_stderr(helper_stderr):
47
- all_findings = terminology_module.strict_staged_terminology_findings(
48
- document_set.repository_root
49
- )
56
+ all_findings = getattr(terminology_module, checker_name)(*all_arguments)
50
57
  return tuple(_terminology_diagnostic(each_finding) for each_finding in all_findings)
51
58
 
52
59