claude-dev-env 1.94.0 → 2.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 (280) hide show
  1. package/_shared/advisor/CLAUDE.md +2 -2
  2. package/_shared/advisor/advisor-protocol.md +41 -33
  3. package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +17 -13
  4. package/_shared/advisor/scripts/model_tier_run_validator.py +27 -18
  5. package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +95 -25
  6. package/_shared/advisor/scripts/tests/test_tier_model_ids.py +17 -17
  7. package/_shared/advisor/scripts/tier_model_ids.py +18 -18
  8. package/_shared/pr-loop/CLAUDE.md +1 -0
  9. package/_shared/pr-loop/scripts/CLAUDE.md +2 -1
  10. package/_shared/pr-loop/scripts/README.md +1 -0
  11. package/_shared/pr-loop/scripts/code_rules_gate.py +253 -1980
  12. package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +32 -0
  13. package/_shared/pr-loop/scripts/code_rules_gate_parts/__init__.py +7 -0
  14. package/_shared/pr-loop/scripts/code_rules_gate_parts/added_line_maps.py +268 -0
  15. package/_shared/pr-loop/scripts/code_rules_gate_parts/enforcer_loading.py +172 -0
  16. package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_arguments.py +70 -0
  17. package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_running.py +326 -0
  18. package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +85 -0
  19. package/_shared/pr-loop/scripts/code_rules_gate_parts/git_file_sets.py +331 -0
  20. package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +369 -0
  21. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/conftest.py +14 -0
  22. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_added_line_maps.py +118 -0
  23. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_enforcer_loading.py +17 -0
  24. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_arguments.py +29 -0
  25. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_running.py +99 -0
  26. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_blob_readers.py +69 -0
  27. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_file_sets.py +137 -0
  28. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +116 -0
  29. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_violation_scoping.py +75 -0
  30. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_wrapper_plumb_check.py +49 -0
  31. package/_shared/pr-loop/scripts/code_rules_gate_parts/violation_scoping.py +328 -0
  32. package/_shared/pr-loop/scripts/code_rules_gate_parts/wrapper_plumb_check.py +206 -0
  33. package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +24 -17
  34. package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviews_disabled_constants.py +1 -0
  35. package/_shared/pr-loop/scripts/reviews_disabled.py +19 -2
  36. package/_shared/pr-loop/scripts/test_code_rules_gate.py +278 -0
  37. package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +6 -39
  38. package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +43 -0
  39. package/_shared/pr-loop/worker-spawn.md +186 -0
  40. package/agents/code-verifier.md +1 -1
  41. package/bin/CLAUDE.md +10 -1
  42. package/bin/ever-shipped-skills.mjs +73 -0
  43. package/bin/expand_home_directory_tokens.mjs +1 -1
  44. package/bin/install.mjs +140 -7
  45. package/bin/install.prune.test.mjs +457 -0
  46. package/docs/CODE_RULES.md +1 -1
  47. package/hooks/advisory/refactor_guard.py +3 -4
  48. package/hooks/blocking/CLAUDE.md +7 -1
  49. package/hooks/blocking/block_main_commit.py +2 -2
  50. package/hooks/blocking/claude_md_orphan_file_blocker.py +75 -699
  51. package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +28 -0
  52. package/hooks/blocking/claude_md_orphan_file_blocker_parts/__init__.py +1 -0
  53. package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/__init__.py +1 -0
  54. package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/orphan_blocker_constants.py +18 -0
  55. package/hooks/blocking/claude_md_orphan_file_blocker_parts/decision.py +81 -0
  56. package/hooks/blocking/claude_md_orphan_file_blocker_parts/references.py +307 -0
  57. package/hooks/blocking/claude_md_orphan_file_blocker_parts/scan_plan.py +124 -0
  58. package/hooks/blocking/claude_md_orphan_file_blocker_parts/subtree_scan.py +179 -0
  59. package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/conftest.py +10 -0
  60. package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_decision.py +34 -0
  61. package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_references.py +42 -0
  62. package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_scan_plan.py +27 -0
  63. package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_subtree_scan.py +30 -0
  64. package/hooks/blocking/code_rules_boolean_mustcheck.py +1 -1
  65. package/hooks/blocking/code_rules_enforcer.py +4 -0
  66. package/hooks/blocking/code_rules_mock_completeness.py +1 -1
  67. package/hooks/blocking/code_rules_optional_params.py +2 -2
  68. package/hooks/blocking/code_rules_shared.py +83 -1
  69. package/hooks/blocking/code_rules_test_assertions.py +1 -1
  70. package/hooks/blocking/code_rules_test_layout.py +9 -3
  71. package/hooks/blocking/code_rules_typeddict_stub.py +1 -1
  72. package/hooks/blocking/gh_pr_author_enforcer.py +1 -1
  73. package/hooks/blocking/inventory_intent_records/CLAUDE.md +26 -0
  74. package/hooks/blocking/inventory_intent_records/__init__.py +1 -0
  75. package/hooks/blocking/inventory_intent_records/config/__init__.py +1 -0
  76. package/hooks/blocking/inventory_intent_records/config/intent_records_constants.py +20 -0
  77. package/hooks/blocking/inventory_intent_records/records.py +271 -0
  78. package/hooks/blocking/inventory_intent_records/tests/conftest.py +10 -0
  79. package/hooks/blocking/inventory_intent_records/tests/test_records.py +80 -0
  80. package/hooks/blocking/package_inventory_stale_blocker.py +54 -384
  81. package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +26 -0
  82. package/hooks/blocking/package_inventory_stale_blocker_parts/__init__.py +1 -0
  83. package/hooks/blocking/package_inventory_stale_blocker_parts/config/__init__.py +1 -0
  84. package/hooks/blocking/package_inventory_stale_blocker_parts/config/inventory_blocker_constants.py +16 -0
  85. package/hooks/blocking/package_inventory_stale_blocker_parts/decision.py +84 -0
  86. package/hooks/blocking/package_inventory_stale_blocker_parts/inventory_detection.py +307 -0
  87. package/hooks/blocking/package_inventory_stale_blocker_parts/tests/conftest.py +10 -0
  88. package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_decision.py +38 -0
  89. package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_inventory_detection.py +61 -0
  90. package/hooks/blocking/pii_payload_scan.py +138 -42
  91. package/hooks/blocking/pii_prevention_blocker.py +185 -291
  92. package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +24 -0
  93. package/hooks/blocking/pii_prevention_blocker_parts/__init__.py +1 -0
  94. package/hooks/blocking/pii_prevention_blocker_parts/config/__init__.py +1 -0
  95. package/hooks/blocking/pii_prevention_blocker_parts/config/repository_resolution_constants.py +28 -0
  96. package/hooks/blocking/pii_prevention_blocker_parts/repository_exemption.py +214 -0
  97. package/hooks/blocking/pii_prevention_blocker_parts/repository_resolution.py +208 -0
  98. package/hooks/blocking/plain_language_blocker.py +138 -4
  99. package/hooks/blocking/pr_description_command_parser.py +8 -4
  100. package/hooks/blocking/precommit_code_rules_gate.py +3 -3
  101. package/hooks/blocking/sensitive_file_protector.py +114 -48
  102. package/hooks/blocking/tdd_enforcer.py +97 -601
  103. package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +30 -0
  104. package/hooks/blocking/tdd_enforcer_parts/__init__.py +1 -0
  105. package/hooks/blocking/tdd_enforcer_parts/candidate_paths.py +142 -0
  106. package/hooks/blocking/tdd_enforcer_parts/config/__init__.py +1 -0
  107. package/hooks/blocking/tdd_enforcer_parts/config/tdd_enforcer_constants.py +32 -0
  108. package/hooks/blocking/tdd_enforcer_parts/content_analysis.py +268 -0
  109. package/hooks/blocking/tdd_enforcer_parts/decisions.py +92 -0
  110. package/hooks/blocking/tdd_enforcer_parts/freshness.py +80 -0
  111. package/hooks/blocking/tdd_enforcer_parts/git_tracking.py +63 -0
  112. package/hooks/blocking/tdd_enforcer_parts/path_classification.py +119 -0
  113. package/hooks/blocking/tdd_enforcer_parts/tests/conftest.py +10 -0
  114. package/hooks/blocking/tdd_enforcer_parts/tests/test_candidate_paths.py +31 -0
  115. package/hooks/blocking/tdd_enforcer_parts/tests/test_content_analysis.py +30 -0
  116. package/hooks/blocking/tdd_enforcer_parts/tests/test_decisions.py +34 -0
  117. package/hooks/blocking/tdd_enforcer_parts/tests/test_freshness.py +28 -0
  118. package/hooks/blocking/tdd_enforcer_parts/tests/test_git_tracking.py +48 -0
  119. package/hooks/blocking/tdd_enforcer_parts/tests/test_path_classification.py +36 -0
  120. package/hooks/blocking/test_code_rules_enforcer_scratchpad.py +105 -0
  121. package/hooks/blocking/test_code_rules_shared.py +181 -0
  122. package/hooks/blocking/test_inventory_deadlock_resolution.py +154 -0
  123. package/hooks/blocking/test_pii_payload_scan.py +168 -0
  124. package/hooks/blocking/test_plain_language_blocker_allowlist.py +184 -0
  125. package/hooks/blocking/test_sensitive_file_protector.py +185 -0
  126. package/hooks/blocking/test_tdd_enforcer_restore.py +108 -0
  127. package/hooks/blocking/test_tdd_enforcer_scratchpad.py +105 -0
  128. package/hooks/blocking/tests/conftest.py +10 -0
  129. package/hooks/blocking/tests/test_pii_prevention_blocker.py +260 -0
  130. package/hooks/blocking/tests/test_repository_exemption.py +105 -0
  131. package/hooks/blocking/tests/test_repository_resolution.py +108 -0
  132. package/hooks/diagnostic/hook_log_extractor.py +12 -10
  133. package/hooks/git-hooks/post_commit.py +3 -4
  134. package/hooks/hooks_constants/CLAUDE.md +4 -2
  135. package/hooks/hooks_constants/banned_identifiers_constants.py +0 -1
  136. package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
  137. package/hooks/hooks_constants/harness_scratchpad_constants.py +17 -0
  138. package/hooks/hooks_constants/local_identity.py +59 -8
  139. package/hooks/hooks_constants/pii_prevention_constants.py +0 -6
  140. package/hooks/hooks_constants/plain_language_blocker_constants.py +5 -0
  141. package/hooks/hooks_constants/sensitive_file_protector_constants.py +42 -0
  142. package/hooks/hooks_constants/test_local_identity.py +105 -3
  143. package/hooks/pyproject.toml +52 -4
  144. package/hooks/session/plugin_data_dir_cleanup.py +0 -1
  145. package/hooks/validation/mypy_validator.py +2 -2
  146. package/hooks/validators/CLAUDE.md +1 -1
  147. package/hooks/validators/README.md +2 -0
  148. package/hooks/validators/health_check.py +1 -0
  149. package/hooks/validators/mypy_integration.py +2 -0
  150. package/hooks/validators/python_style_checks.py +114 -136
  151. package/hooks/validators/python_style_helpers.py +95 -0
  152. package/hooks/validators/ruff_integration.py +3 -0
  153. package/hooks/validators/test_python_style_checks.py +0 -164
  154. package/hooks/validators/test_python_style_checks_decorator_gap.py +119 -0
  155. package/hooks/validators/test_python_style_fixes.py +251 -0
  156. package/hooks/validators/test_python_style_helpers.py +125 -0
  157. package/hooks/workflow/auto_formatter.py +5 -4
  158. package/package.json +1 -1
  159. package/rules/CLAUDE.md +1 -0
  160. package/rules/anti-corollary-tests.md +69 -0
  161. package/rules/bdd.md +1 -3
  162. package/rules/code-reviews.md +1 -1
  163. package/rules/gh-paginate.md +1 -1
  164. package/rules/plain-language.md +2 -0
  165. package/scripts/CLAUDE.md +4 -0
  166. package/scripts/dev_env_scripts_constants/CLAUDE.md +6 -4
  167. package/scripts/dev_env_scripts_constants/code_review_constants.py +71 -0
  168. package/scripts/dev_env_scripts_constants/grok_worker_constants.py +435 -0
  169. package/scripts/dev_env_scripts_constants/timing.py +7 -1
  170. package/scripts/grok_headless_runner.py +294 -0
  171. package/scripts/grok_worker_preflight.py +410 -0
  172. package/scripts/invoke_code_review.py +463 -0
  173. package/scripts/resolve_worker_spawn.py +619 -0
  174. package/scripts/spawn_grok_batch.py +672 -0
  175. package/scripts/test_grok_headless_runner.py +626 -0
  176. package/scripts/test_grok_worker_preflight.py +1054 -0
  177. package/scripts/test_invoke_code_review.py +672 -0
  178. package/scripts/test_resolve_worker_spawn.py +1014 -0
  179. package/scripts/test_spawn_grok_batch.py +1017 -0
  180. package/skills/CLAUDE.md +6 -3
  181. package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +72 -13
  182. package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +121 -14
  183. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +78 -0
  184. package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +121 -0
  185. package/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +196 -6
  186. package/skills/autoconverge/CLAUDE.md +3 -3
  187. package/skills/autoconverge/SKILL.md +9 -3
  188. package/skills/autoconverge/reference/CLAUDE.md +2 -2
  189. package/skills/autoconverge/reference/convergence.md +33 -11
  190. package/skills/autoconverge/reference/stop-conditions.md +16 -5
  191. package/skills/autoconverge/workflow/CLAUDE.md +2 -1
  192. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +7 -2
  193. package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +300 -0
  194. package/skills/autoconverge/workflow/converge.contract.test.mjs +5 -5
  195. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +29 -29
  196. package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
  197. package/skills/autoconverge/workflow/converge.mjs +202 -18
  198. package/skills/bugteam/CLAUDE.md +2 -2
  199. package/skills/bugteam/CONSTRAINTS.md +3 -2
  200. package/skills/bugteam/PROMPTS.md +7 -6
  201. package/skills/bugteam/SKILL.md +18 -13
  202. package/skills/bugteam/reference/README.md +2 -3
  203. package/skills/bugteam/reference/audit-and-teammates.md +215 -35
  204. package/skills/bugteam/reference/design-rationale.md +1 -1
  205. package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -1
  206. package/skills/bugteam/reference/team-setup.md +8 -2
  207. package/skills/closeout/SKILL.md +153 -0
  208. package/skills/closeout/reference/handoff-prompt-template.md +72 -0
  209. package/skills/closeout/reference/issue-body-templates.md +108 -0
  210. package/skills/closeout/reference/pii-redaction-checklist.md +36 -0
  211. package/skills/codex-review/CLAUDE.md +46 -0
  212. package/skills/codex-review/SKILL.md +181 -0
  213. package/skills/codex-review/reference/CLAUDE.md +15 -0
  214. package/skills/codex-review/reference/cli-contract.md +253 -0
  215. package/skills/codex-review/reference/loop-integration.md +118 -0
  216. package/skills/codex-review/scripts/codex_down_classifier.py +98 -0
  217. package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +18 -0
  218. package/skills/codex-review/scripts/codex_review_scripts_constants/__init__.py +1 -0
  219. package/skills/codex-review/scripts/codex_review_scripts_constants/classifier_constants.py +35 -0
  220. package/skills/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +86 -0
  221. package/skills/codex-review/scripts/codex_review_scripts_constants/findings_constants.py +18 -0
  222. package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +45 -0
  223. package/skills/codex-review/scripts/codex_usage_probe.py +573 -0
  224. package/skills/codex-review/scripts/fixtures/auth_failure_synthetic.txt +1 -0
  225. package/skills/codex-review/scripts/fixtures/config_load_failure_v0.125.0.txt +1 -0
  226. package/skills/codex-review/scripts/fixtures/freeform_findings_v0.144.3.txt +6 -0
  227. package/skills/codex-review/scripts/fixtures/model_rejection_v0.125.0.jsonl +5 -0
  228. package/skills/codex-review/scripts/fixtures/structured_findings.txt +13 -0
  229. package/skills/codex-review/scripts/fixtures/success_stream_v0.144.3.jsonl +6 -0
  230. package/skills/codex-review/scripts/fixtures/unknown_failure_synthetic.txt +1 -0
  231. package/skills/codex-review/scripts/fixtures/usage_limit_synthetic.txt +1 -0
  232. package/skills/codex-review/scripts/parse_codex_findings.py +207 -0
  233. package/skills/codex-review/scripts/run_codex_review.py +415 -0
  234. package/skills/codex-review/scripts/test_codex_down_classifier.py +143 -0
  235. package/skills/codex-review/scripts/test_codex_usage_probe.py +678 -0
  236. package/skills/codex-review/scripts/test_parse_codex_findings.py +130 -0
  237. package/skills/codex-review/scripts/test_run_codex_review.py +812 -0
  238. package/skills/codex-review/test_skill_scaffold.py +192 -0
  239. package/skills/grok-spawn/CLAUDE.md +28 -0
  240. package/skills/grok-spawn/SKILL.md +226 -0
  241. package/skills/grok-spawn/reference/flag-profiles.md +132 -0
  242. package/skills/grok-spawn/reference/worker-briefs.md +152 -0
  243. package/skills/grokify/SKILL.md +9 -1
  244. package/skills/grokify/capability-claims.test.mjs +28 -0
  245. package/skills/grokify/evals/README.md +72 -0
  246. package/skills/grokify/evals/parse-payload.test.mjs +171 -0
  247. package/skills/grokify/evals/run-capability-evals.mjs +545 -0
  248. package/skills/orchestrator/SKILL.md +32 -22
  249. package/skills/orchestrator-refresh/SKILL.md +12 -8
  250. package/skills/pr-converge/CLAUDE.md +1 -1
  251. package/skills/pr-converge/SKILL.md +34 -13
  252. package/skills/pr-converge/reference/convergence-gates.md +42 -15
  253. package/skills/pr-converge/reference/fix-protocol.md +1 -1
  254. package/skills/pr-converge/reference/ground-rules.md +1 -1
  255. package/skills/pr-converge/reference/per-tick.md +130 -42
  256. package/skills/pr-converge/reference/state-schema.md +10 -0
  257. package/skills/pr-converge/scripts/CLAUDE.md +2 -0
  258. package/skills/pr-converge/scripts/_pr_converge_path_setup.py +5 -1
  259. package/skills/pr-converge/scripts/check_convergence.py +605 -29
  260. package/skills/pr-converge/scripts/check_convergence_availability.py +232 -0
  261. package/skills/pr-converge/scripts/check_convergence_gates.py +279 -235
  262. package/skills/pr-converge/scripts/check_convergence_thread_gates.py +1 -1
  263. package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -2
  264. package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +4 -0
  265. package/skills/pr-converge/scripts/test_check_convergence.py +71 -3
  266. package/skills/pr-converge/scripts/test_check_convergence_availability.py +326 -0
  267. package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -0
  268. package/skills/pr-converge/scripts/test_check_convergence_contract.py +89 -17
  269. package/skills/pr-converge/scripts/test_check_convergence_fixture.py +179 -0
  270. package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -68
  271. package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +24 -0
  272. package/skills/pr-converge/test_step5_host_branch.py +106 -0
  273. package/skills/pr-fix-protocol/SKILL.md +65 -0
  274. package/skills/pr-loop-cloud-transport/SKILL.md +2 -0
  275. package/skills/reviewer-gates/SKILL.md +7 -5
  276. package/skills/skill-builder/references/skill-modularity.md +1 -1
  277. package/skills/team-advisor/SKILL.md +16 -12
  278. package/system-prompts/software-engineer.xml +7 -6
  279. package/hooks/validators/test_verify_paths.py +0 -32
  280. package/hooks/validators/verify_paths.py +0 -57
@@ -33,7 +33,7 @@ On Windows the same file sits at %USERPROFILE%\.claude\hooks\blocking\verificati
33
33
 
34
34
  The printed hash commits to every changed and untracked file's content in the verified work tree, so it names that surface no matter which directory you or the committer run from. If the CLI prints an empty-surface or wrong-work-tree error and no hash, you are pointed at a work tree with no changes versus origin/main — re-run with the branch mode to locate the correct work tree.
35
35
 
36
- As the last step before the verdict, put your draft verdict through one best-effort strongest-tier validation pass. Spawn a single validation subagent through the Task tool as the `Explore` agent type at the strongest reachable tier: set the Task `subagent_type` to `Explore`, detect the host profile first per `~/.claude/_shared/advisor/advisor-protocol.md` — the source of truth for host detection, the ladder, and its aliases — then on a Claude host pick the strongest reachable tier on the Fable → Opus → Sonnet → Haiku ladder and on a Grok host use the single Grok tier, and set the Task `model:` field to that tier's alias. The `Explore` type carries no Edit or Write tools and cannot spawn further agents, so the harness itself holds the validator to the no-edit, no-spawn contract the next paragraph names. Hand it the draft verdict together with your evidence — every command you ran with its output, and your two-way diff-to-task mapping — and state that its task is adversarial verification of that supplied draft verdict: refute it against the supplied evidence rather than discover code, naming any gate you misread, any task item you mapped wrong, or any finding that does not hold. When the spawn is unavailable — a Task tool error, an unreachable tier at every rung, or this subagent being barred from spawning further agents — skip the validation pass and emit the draft verdict as it stands, noting the skip in your final message; a spawn failure never blocks the verdict fence from being emitted.
36
+ As the last step before the verdict, put your draft verdict through one best-effort strongest-tier validation pass. Spawn a single validation subagent through the Task tool as the `Explore` agent type at the strongest reachable tier: set the Task `subagent_type` to `Explore`, detect the host profile first per `~/.claude/_shared/advisor/advisor-protocol.md` — the source of truth for host detection, the ladder, and its aliases — then on a Claude host pick the strongest reachable tier on the Fable → Opus → Sonnet → Haiku ladder and on a third-party host use the single third-party tier, and set the Task `model:` field to that tier's alias. The `Explore` type carries no Edit or Write tools and cannot spawn further agents, so the harness itself holds the validator to the no-edit, no-spawn contract the next paragraph names. Hand it the draft verdict together with your evidence — every command you ran with its output, and your two-way diff-to-task mapping — and state that its task is adversarial verification of that supplied draft verdict: refute it against the supplied evidence rather than discover code, naming any gate you misread, any task item you mapped wrong, or any finding that does not hold. When the spawn is unavailable — a Task tool error, an unreachable tier at every rung, or this subagent being barred from spawning further agents — skip the validation pass and emit the draft verdict as it stands, noting the skip in your final message; a spawn failure never blocks the verdict fence from being emitted.
37
37
 
38
38
  This validation pass is terminal: the `Explore` type gives the validation subagent no way to spawn a further agent or edit a file, so it answers with prose only. When it refutes any part, re-check that part yourself against the commands and the diff, and correct the verdict before you emit it. When it refutes nothing, the draft verdict stands. Then emit the fenced verdict block below — it stays the last thing in your message so the verifier_verdict_minter hook reads it.
39
39
 
package/bin/CLAUDE.md CHANGED
@@ -6,7 +6,8 @@ The installer and its companion modules. Running `npx claude-dev-env` (or `node
6
6
 
7
7
  | File | Purpose |
8
8
  |---|---|
9
- | `install.mjs` | Main installer: discovers install groups, copies content directories (`rules`, `docs`, `commands`, `agents`, `system-prompts`, `scripts`, `_shared`, `audit-rubrics`), merges hooks into `settings.json`, installs skills, runs `git_hooks_installer.mjs` and `install_mypy_ini.mjs` |
9
+ | `install.mjs` | Main installer: discovers install groups, copies content directories (`rules`, `docs`, `commands`, `agents`, `system-prompts`, `scripts`, `_shared`, `audit-rubrics`), merges hooks into `settings.json`, installs skills, prunes retired skills on a full install, runs `git_hooks_installer.mjs` and `install_mypy_ini.mjs` |
10
+ | `ever-shipped-skills.mjs` | Static `EVER_SHIPPED_SKILL_NAMES` set of every top-level skill directory name the package has shipped; the installer subtracts the current skill set from it to prune retired skills left under `~/.claude/skills` |
10
11
  | `expand_home_directory_tokens.mjs` | Expands residual `$HOME` / `${HOME}` / `~/` tokens in settings.json hook and statusLine commands to absolute home paths at install time (literal-safe for homes that contain `$`) |
11
12
  | `git_hooks_installer.mjs` | Installs or updates the `pre-commit`, `pre-push`, and `post-commit` Git hooks in the user's git config; writes hook scripts that delegate to the installed Python hooks |
12
13
  | `install_mypy_ini.mjs` | Writes `~/.mypy.ini` with settings that make mypy find the hooks package and enforce strict type checking |
@@ -14,6 +15,14 @@ The installer and its companion modules. Running `npx claude-dev-env` (or `node
14
15
  | `git_hooks_installer.test.mjs` | Tests for `git_hooks_installer.mjs` |
15
16
  | `install_mypy_ini.test.mjs` | Tests for `install_mypy_ini.mjs` |
16
17
 
18
+ ## Retired-skill prune
19
+
20
+ The full-install prune renames a retired skill directory into a timestamped backup rather than deleting it. Each pruned directory is renamed to `~/.claude/.claude-dev-env-pruned/<timestamp>/<skill-name>/`, a backup root outside `~/.claude/skills` so a backed-up directory is never re-discovered as a skill. Backups accumulate — nothing cleans them — so a user can recover a directory. A rename that fails leaves the directory in place with a logged warning and never falls back to deletion, so a prune failure costs at most a cosmetic leftover.
21
+
22
+ Matching is by directory name alone, so a user-authored directory whose name collides with a retired skill is backed up as if it were that skill. A directory is pruned when the prior install's manifest recorded it or the ever-shipped set names it, and the current install did not just write it. A name absent from all three of those sets, together with `~/.claude/skills/_shared`, is left in place.
23
+
24
+ The prune is skipped for the whole run — with a logged notice naming the unresolved group — when any declared dependency group fails to resolve. An unresolved dependency contributes no skills to the installed set, so a live skill that a dependency package supplies would look retired; holding the prune until every dependency resolves keeps such a skill from being backed up.
25
+
17
26
  ## Key exports from install.mjs
18
27
 
19
28
  | Export | Description |
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The top-level skill directory names this package has ever shipped.
3
+ *
4
+ * The installer subtracts the skills the current package installs from this set
5
+ * to learn which shipped skills have retired, so a full reinstall can remove a
6
+ * retired skill left behind under ~/.claude/skills. Because the current set is
7
+ * subtracted at runtime, restoring a retired skill to the package protects it
8
+ * automatically — it re-enters the installed set and drops out of the retired
9
+ * set on the next install.
10
+ *
11
+ * The list is a committed literal rather than a git query, because a user
12
+ * installs from the npm tarball, which carries no source-repo history. Refresh
13
+ * it by running, from the repository root:
14
+ *
15
+ * git log --all --pretty=format: --name-only -- 'packages/claude-dev-env/skills/*\/SKILL.md' | sort -u
16
+ *
17
+ * and pasting each distinct top-level skill directory name below.
18
+ */
19
+ export const EVER_SHIPPED_SKILL_NAMES = new Set([
20
+ 'anthropic-plan',
21
+ 'auditing-claude-config',
22
+ 'autoconverge',
23
+ 'bdd-protocol',
24
+ 'bg-agent',
25
+ 'bugteam',
26
+ 'caveman',
27
+ 'closeout',
28
+ 'code',
29
+ 'codex-review',
30
+ 'copilot-finding-triage',
31
+ 'copilot-review',
32
+ 'deep-research',
33
+ 'everything-search',
34
+ 'findbugs',
35
+ 'fixbugs',
36
+ 'fresh-branch',
37
+ 'gh-paginate',
38
+ 'gotcha',
39
+ 'grok-spawn',
40
+ 'grokify',
41
+ 'implement',
42
+ 'log-audit',
43
+ 'logifix',
44
+ 'monitor-open-prs',
45
+ 'orchestrator',
46
+ 'orchestrator-refresh',
47
+ 'post-audit-findings',
48
+ 'pr-consistency-audit',
49
+ 'pr-converge',
50
+ 'pr-fix-protocol',
51
+ 'pr-loop-cloud-transport',
52
+ 'pr-loop-lifecycle',
53
+ 'pr-review-responder',
54
+ 'pr-scope-resolve',
55
+ 'pre-compact',
56
+ 'privacy-hygiene',
57
+ 'qbug',
58
+ 'rebase',
59
+ 'recall',
60
+ 'refine',
61
+ 'remember',
62
+ 'research-mode',
63
+ 'reviewer-gates',
64
+ 'session-log',
65
+ 'session-tidy',
66
+ 'skill-builder',
67
+ 'structure-prompt',
68
+ 'task-build',
69
+ 'team-advisor',
70
+ 'update',
71
+ 'usage-pause',
72
+ 'verified-build',
73
+ ]);
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Install-time expansion of $HOME / ${HOME} / ~/ in settings.json commands.
3
3
  *
4
- * Hosts that require referenced env vars at hook load time (for example Grok)
4
+ * Hosts that require referenced env vars at hook load time (for example a third-party host)
5
5
  * skip the hook when HOME is unset — common on Windows — so the installer
6
6
  * rewrites residual tokens to absolute home paths.
7
7
  */
package/bin/install.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync, copyFileSync, unlinkSync, rmSync, realpathSync } from 'node:fs';
4
- import { join, dirname, resolve, relative } from 'node:path';
3
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync, copyFileSync, unlinkSync, rmSync, renameSync, realpathSync } from 'node:fs';
4
+ import { join, dirname, resolve, relative, basename } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
6
  import { execSync, execFileSync } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
@@ -9,6 +9,7 @@ import { createRequire } from 'node:module';
9
9
  import { installAllGitHooks } from './git_hooks_installer.mjs';
10
10
  import { installMypyIniForClaudeHooks } from './install_mypy_ini.mjs';
11
11
  import { expandHomeDirectoryTokensInSettings } from './expand_home_directory_tokens.mjs';
12
+ import { EVER_SHIPPED_SKILL_NAMES } from './ever-shipped-skills.mjs';
12
13
 
13
14
  const CLAUDE_HOME = join(homedir(), '.claude');
14
15
  const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
@@ -19,6 +20,10 @@ const packageRequire = createRequire(import.meta.url);
19
20
 
20
21
  export const CONTENT_DIRECTORIES = ['rules', 'docs', 'commands', 'agents', 'system-prompts', 'scripts', '_shared', 'audit-rubrics'];
21
22
 
23
+ const SKILL_MANIFEST_FILENAME = 'SKILL.md';
24
+ const NEVER_PRUNED_SKILL_DIRECTORIES = new Set(['_shared']);
25
+ const PRUNED_SKILLS_BACKUP_DIRECTORY_NAME = '.claude-dev-env-pruned';
26
+
22
27
  export const CORE_INCLUDE_DIRECTORIES = [
23
28
  'rules', 'docs', 'commands', 'agents', 'audit-rubrics', '_shared', 'scripts',
24
29
  ];
@@ -94,17 +99,33 @@ function resolveDependencyPackageRoot(dependencyPackageName) {
94
99
  return dirname(dependencyPackageJsonPath);
95
100
  }
96
101
 
102
+ /**
103
+ * Discovers the install groups contributed by resolvable dependency packages and
104
+ * the names of the declared dependencies that failed to resolve.
105
+ *
106
+ * A dependency that cannot be resolved contributes no group, so its skills never
107
+ * enter the installed set. The retired-skill prune subtracts the installed set
108
+ * from the ever-shipped set, so an unresolved dependency whose skills migrated
109
+ * out of the main package would leave those live skills looking retired. The
110
+ * returned unresolved-name list lets the caller skip the prune in that degraded
111
+ * mode rather than delete a live skill.
112
+ *
113
+ * @returns {{groups: object, unresolvedDependencyNames: string[]}} The resolvable
114
+ * dependency groups keyed by group name, and the names that failed to resolve.
115
+ */
97
116
  function discoverDependencyGroups() {
98
117
  const ownPackageJsonPath = join(PACKAGE_ROOT, 'package.json');
99
118
  const ownPackageJson = JSON.parse(readFileSync(ownPackageJsonPath, 'utf8'));
100
119
  const dependencies = ownPackageJson.dependencies || {};
101
120
  const discoveredGroups = {};
121
+ const unresolvedDependencyNames = [];
102
122
  for (const dependencyName of Object.keys(dependencies)) {
103
123
  let dependencyRoot;
104
124
  try {
105
125
  dependencyRoot = resolveDependencyPackageRoot(dependencyName);
106
126
  } catch {
107
127
  console.error(` WARNING: Could not resolve dependency ${dependencyName}, skipping`);
128
+ unresolvedDependencyNames.push(dependencyName);
108
129
  continue;
109
130
  }
110
131
  const dependencyPackageJson = JSON.parse(
@@ -145,14 +166,18 @@ function discoverDependencyGroups() {
145
166
  }
146
167
  discoveredGroups[groupName] = group;
147
168
  }
148
- return discoveredGroups;
169
+ return { groups: discoveredGroups, unresolvedDependencyNames };
149
170
  }
150
171
 
172
+ const dependencyDiscovery = discoverDependencyGroups();
173
+ const UNRESOLVED_DEPENDENCY_NAMES = dependencyDiscovery.unresolvedDependencyNames;
174
+
151
175
  const INSTALL_GROUPS = {
152
176
  core: {
153
177
  description: 'Development standards, hooks, agents, commands',
154
178
  skills: [
155
179
  'orchestrator', 'orchestrator-refresh', 'team-advisor', 'grokify',
180
+ 'grok-spawn',
156
181
  'anthropic-plan', 'everything-search',
157
182
  'privacy-hygiene',
158
183
  'recall', 'remember', 'task-build'
@@ -164,7 +189,7 @@ const INSTALL_GROUPS = {
164
189
  description: 'Session logging and memory',
165
190
  skills: ['session-log', 'session-tidy'],
166
191
  },
167
- ...discoverDependencyGroups(),
192
+ ...dependencyDiscovery.groups,
168
193
  };
169
194
 
170
195
  /**
@@ -511,7 +536,7 @@ function pruneManagedHooksFromEvent(settings, eventType, managedHookRelativePath
511
536
  * a new matcher group does not double-run. User-authored hooks are preserved as
512
537
  * entries, but residual $HOME / ${HOME} / ~/ tokens in every hook and statusLine
513
538
  * command are expanded to absolute home paths so hosts that require referenced
514
- * env vars at load time (Grok on Windows) can execute them.
539
+ * env vars at load time (a third-party host on Windows) can execute them.
515
540
  *
516
541
  * @param {object} settings The parsed settings.json object (mutated in place).
517
542
  * @param {{hooks: object}} hooksConfig Parsed hooks.json.
@@ -606,11 +631,99 @@ function mergeHooks(hooksSourceRoot, pythonCommand) {
606
631
  return groupCount;
607
632
  }
608
633
 
609
- function writeManifest(installedFiles) {
634
+ function writeManifest(installedFiles, skillNames) {
610
635
  const manifest = { package: PACKAGE_NAME, version: PACKAGE_VERSION, installedAt: new Date().toISOString(), files: installedFiles };
636
+ if (skillNames) {
637
+ manifest.skills = skillNames;
638
+ }
611
639
  writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2) + '\n');
612
640
  }
613
641
 
642
+ /**
643
+ * Read the skill directory names the previous install recorded.
644
+ *
645
+ * Returns null when no manifest exists or the manifest predates the skills key,
646
+ * so a caller can treat "unknown prior skills" the same in both cases and lean
647
+ * on the ever-shipped set to find retired skills.
648
+ *
649
+ * @returns {string[]|null} The prior manifest's skill names, or null when absent.
650
+ */
651
+ function readPriorManifestSkills() {
652
+ if (!existsSync(MANIFEST_FILE)) return null;
653
+ try {
654
+ const priorManifest = JSON.parse(readFileSync(MANIFEST_FILE, 'utf8'));
655
+ return Array.isArray(priorManifest.skills) ? priorManifest.skills : null;
656
+ } catch {
657
+ return null;
658
+ }
659
+ }
660
+
661
+ /**
662
+ * Move retired skill directories a prior install left under ~/.claude/skills into
663
+ * a timestamped backup directory rather than deleting them.
664
+ *
665
+ * A directory is pruned only when the package no longer ships it and either the
666
+ * prior manifest recorded it or the package once shipped it. Subtracting the
667
+ * just-installed set from the ever-shipped set at call time means restoring a
668
+ * skill to the package protects it — it re-enters the installed set and never
669
+ * counts as retired. Matching is by directory name alone, so a personal
670
+ * directory a user authored under a name that collides with a retired skill
671
+ * (for example `code`, `implement`, `refine`, `gotcha`, `caveman`) is treated as
672
+ * that retired skill and moved to backup; only a name in neither set, and
673
+ * ~/.claude/skills/_shared, are left in place.
674
+ *
675
+ * Each pruned directory is renamed to
676
+ * ~/.claude/.claude-dev-env-pruned/<timestamp>/<skill-name>/ — a backup root
677
+ * outside ~/.claude/skills, so a moved directory is never re-discovered as a
678
+ * skill — under one shared timestamp per run. A backup is never cleaned up, so a
679
+ * user can recover a wrongly-matched directory. One directory whose rename fails
680
+ * (for example a read-only file or a cross-device move) is logged and left in
681
+ * place, never deleted, so a prune failure costs at most a cosmetic leftover.
682
+ *
683
+ * @param {Set<string>} installedSkillNames Skill names this install just wrote.
684
+ * @param {string[]|null} priorManifestSkills The prior manifest's skill names, or null.
685
+ */
686
+ function pruneRetiredSkills(installedSkillNames, priorManifestSkills) {
687
+ const skillsDirectory = join(CLAUDE_HOME, 'skills');
688
+ if (!existsSync(skillsDirectory)) return;
689
+ const retiredSkillNames = new Set(
690
+ [...EVER_SHIPPED_SKILL_NAMES].filter(skillName => !installedSkillNames.has(skillName))
691
+ );
692
+ const priorSkillNames = new Set(priorManifestSkills || []);
693
+ const runTimestamp = new Date().toISOString().replace(/[:.]/g, '-');
694
+ const backupRoot = join(CLAUDE_HOME, PRUNED_SKILLS_BACKUP_DIRECTORY_NAME, runTimestamp);
695
+ const existingSkillDirs = readdirSync(skillsDirectory, { withFileTypes: true })
696
+ .filter(entry => entry.isDirectory());
697
+ for (const skillDir of existingSkillDirs) {
698
+ const skillName = skillDir.name;
699
+ if (NEVER_PRUNED_SKILL_DIRECTORIES.has(skillName)) continue;
700
+ if (installedSkillNames.has(skillName)) continue;
701
+ const isPruneCandidate = priorSkillNames.has(skillName) || retiredSkillNames.has(skillName);
702
+ if (!isPruneCandidate) continue;
703
+ moveRetiredSkillToBackup(skillsDirectory, backupRoot, skillName);
704
+ }
705
+ }
706
+
707
+ /**
708
+ * Move one retired skill directory into the run's backup root, leaving it in
709
+ * place when the move fails.
710
+ *
711
+ * @param {string} skillsDirectory The ~/.claude/skills directory holding the skill.
712
+ * @param {string} backupRoot The run's timestamped backup directory.
713
+ * @param {string} skillName The retired skill directory name to move.
714
+ */
715
+ function moveRetiredSkillToBackup(skillsDirectory, backupRoot, skillName) {
716
+ const skillPath = join(skillsDirectory, skillName);
717
+ const backupPath = join(backupRoot, skillName);
718
+ try {
719
+ mkdirSync(backupRoot, { recursive: true });
720
+ renameSync(skillPath, backupPath);
721
+ console.log(` ✗ ${join('skills', skillName)} (retired — moved to ${join(PRUNED_SKILLS_BACKUP_DIRECTORY_NAME, basename(backupRoot), skillName)})`);
722
+ } catch (moveError) {
723
+ console.warn(` Warning: could not move retired ${join('skills', skillName)} to backup, leaving in place (${moveError.message})`);
724
+ }
725
+ }
726
+
614
727
  function install(selectedGroups, options = {}) {
615
728
  const isUpdateRefresh = Boolean(options.isUpdateRefresh);
616
729
  if (isUpdateRefresh && !selectedGroups && existsSync(MANIFEST_FILE)) {
@@ -708,6 +821,8 @@ function install(selectedGroups, options = {}) {
708
821
  let skillsCreated = 0;
709
822
  let skillsUpdated = 0;
710
823
  const skillPaths = [];
824
+ const installedSkillNames = new Set();
825
+ const copiedSkillNames = new Set();
711
826
  for (const sourceRoot of allSourceRoots) {
712
827
  const skillsSource = join(sourceRoot, 'skills');
713
828
  if (!existsSync(skillsSource)) continue;
@@ -718,6 +833,10 @@ function install(selectedGroups, options = {}) {
718
833
  skillsCreated += stats.created;
719
834
  skillsUpdated += stats.updated;
720
835
  skillPaths.push(...stats.paths);
836
+ copiedSkillNames.add(skillDir.name);
837
+ if (existsSync(join(skillsSource, skillDir.name, SKILL_MANIFEST_FILENAME))) {
838
+ installedSkillNames.add(skillDir.name);
839
+ }
721
840
  }
722
841
  }
723
842
  summary.skills = { created: skillsCreated, updated: skillsUpdated, paths: skillPaths };
@@ -804,7 +923,21 @@ function install(selectedGroups, options = {}) {
804
923
  allInstalledFiles.push(claudeHubDest);
805
924
  console.log(` \u2713 ${relative(CLAUDE_HOME, claudeHubDest)} (hub)`);
806
925
  }
807
- writeManifest(allInstalledFiles);
926
+ const isFullInstall = !selectedGroups;
927
+ const priorManifestSkills = readPriorManifestSkills();
928
+ let manifestSkillNames = priorManifestSkills;
929
+ if (isFullInstall) {
930
+ if (UNRESOLVED_DEPENDENCY_NAMES.length > 0) {
931
+ console.log(
932
+ ` Skipping retired-skill prune — unresolved dependency group(s): ${UNRESOLVED_DEPENDENCY_NAMES.join(', ')}. `
933
+ + 'A skill that migrated to a dependency package would look retired and be moved to backup, so the prune is held until every dependency resolves.',
934
+ );
935
+ } else {
936
+ pruneRetiredSkills(copiedSkillNames, priorManifestSkills);
937
+ }
938
+ manifestSkillNames = [...installedSkillNames].sort();
939
+ }
940
+ writeManifest(allInstalledFiles, manifestSkillNames);
808
941
  console.log(`\nInstalled ${PACKAGE_NAME}:`);
809
942
  for (const directory of CONTENT_DIRECTORIES) {
810
943
  if (summary[directory]) {