claude-dev-env 2.13.0 → 2.14.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 (241) hide show
  1. package/AGENTS.md +61 -0
  2. package/CLAUDE.md +1 -57
  3. package/_shared/AGENTS.md +15 -0
  4. package/_shared/CLAUDE.md +1 -15
  5. package/_shared/advisor/AGENTS.md +22 -0
  6. package/_shared/advisor/CLAUDE.md +1 -22
  7. package/_shared/pr-loop/AGENTS.md +27 -0
  8. package/_shared/pr-loop/CLAUDE.md +1 -27
  9. package/_shared/pr-loop/scripts/AGENTS.md +35 -0
  10. package/_shared/pr-loop/scripts/CLAUDE.md +1 -35
  11. package/_shared/pr-loop/scripts/code_rules_gate_parts/AGENTS.md +42 -0
  12. package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +1 -42
  13. package/_shared/pr-loop/scripts/pr_loop_shared_constants/AGENTS.md +26 -0
  14. package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -26
  15. package/_shared/pr-loop/scripts/tests/AGENTS.md +44 -0
  16. package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -44
  17. package/_shared/process-tree/AGENTS.md +41 -0
  18. package/_shared/process-tree/CLAUDE.md +1 -41
  19. package/agents/AGENTS.md +31 -0
  20. package/agents/CLAUDE.md +1 -31
  21. package/agents/clean-coder.md +6 -6
  22. package/agents/test_agent_frontmatter.py +4 -4
  23. package/audit-rubrics/AGENTS.md +43 -0
  24. package/audit-rubrics/CLAUDE.md +1 -43
  25. package/audit-rubrics/category_rubrics/AGENTS.md +37 -0
  26. package/audit-rubrics/category_rubrics/CLAUDE.md +1 -37
  27. package/audit-rubrics/prompts/AGENTS.md +37 -0
  28. package/audit-rubrics/prompts/CLAUDE.md +1 -37
  29. package/bin/AGENTS.md +112 -0
  30. package/bin/CLAUDE.md +1 -112
  31. package/bin/ever-shipped-skills.mjs +1 -0
  32. package/bin/install-constants.mjs +6 -7
  33. package/bin/install.mjs +24 -5
  34. package/bin/install.test.mjs +34 -0
  35. package/commands/AGENTS.md +23 -0
  36. package/commands/CLAUDE.md +1 -23
  37. package/commands/hook-log-extract.md +3 -3
  38. package/docs/AGENTS.md +32 -0
  39. package/docs/CLAUDE.md +1 -32
  40. package/docs/CODE_RULES.md +1 -1
  41. package/docs/imagegen.md +22 -0
  42. package/docs/references/AGENTS.md +17 -0
  43. package/docs/references/CLAUDE.md +1 -17
  44. package/hooks/AGENTS.md +31 -0
  45. package/hooks/CLAUDE.md +1 -31
  46. package/hooks/_gh_pr_author_swap_utils.py +14 -0
  47. package/hooks/advisory/AGENTS.md +16 -0
  48. package/hooks/advisory/CLAUDE.md +1 -16
  49. package/hooks/blocking/AGENTS.md +123 -0
  50. package/hooks/blocking/CLAUDE.md +1 -123
  51. package/hooks/blocking/claude_md_orphan_file_blocker_parts/AGENTS.md +28 -0
  52. package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +1 -28
  53. package/hooks/blocking/code_rules_banned_identifiers.py +1 -1
  54. package/hooks/blocking/code_rules_dead_module_constant.py +149 -37
  55. package/hooks/blocking/code_rules_probe_chains.py +6 -2
  56. package/hooks/blocking/config/AGENTS.md +10 -0
  57. package/hooks/blocking/config/CLAUDE.md +1 -10
  58. package/hooks/blocking/gh_pr_author_restore.py +7 -2
  59. package/hooks/blocking/intent_only_ending_blocker.py +11 -16
  60. package/hooks/blocking/inventory_intent_records/AGENTS.md +26 -0
  61. package/hooks/blocking/inventory_intent_records/CLAUDE.md +1 -26
  62. package/hooks/blocking/package_inventory_stale_blocker_parts/AGENTS.md +26 -0
  63. package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +1 -26
  64. package/hooks/blocking/pii_prevention_blocker_parts/AGENTS.md +24 -0
  65. package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +1 -24
  66. package/hooks/blocking/tdd_enforcer_parts/AGENTS.md +30 -0
  67. package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +1 -30
  68. package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +1 -1
  69. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +171 -0
  70. package/hooks/blocking/test_code_rules_enforcer_magic_allowlist.py +1 -1
  71. package/hooks/blocking/test_gh_pr_author_restore.py +2 -1
  72. package/hooks/blocking/test_intent_only_ending_blocker.py +4 -0
  73. package/hooks/diagnostic/AGENTS.md +43 -0
  74. package/hooks/diagnostic/CLAUDE.md +1 -43
  75. package/hooks/diagnostic/hook_log_extractor.py +2 -2
  76. package/hooks/diagnostic/migrations/AGENTS.md +16 -0
  77. package/hooks/diagnostic/migrations/CLAUDE.md +1 -16
  78. package/hooks/diagnostic/queries/AGENTS.md +19 -0
  79. package/hooks/diagnostic/queries/CLAUDE.md +1 -19
  80. package/hooks/diagnostic/test_hook_log_extractor.py +2 -2
  81. package/hooks/git-hooks/AGENTS.md +31 -0
  82. package/hooks/git-hooks/CLAUDE.md +1 -31
  83. package/hooks/git-hooks/git_hooks_constants/AGENTS.md +21 -0
  84. package/hooks/git-hooks/git_hooks_constants/CLAUDE.md +1 -21
  85. package/hooks/git-hooks/git_hooks_constants/__init__.py +4 -6
  86. package/hooks/git-hooks/pre_push.py +47 -34
  87. package/hooks/git-hooks/test_pre_push.py +111 -7
  88. package/hooks/hooks_constants/AGENTS.md +104 -0
  89. package/hooks/hooks_constants/CLAUDE.md +1 -104
  90. package/hooks/hooks_constants/hook_log_extractor_constants.py +1 -2
  91. package/hooks/hooks_constants/task_list_loop_starter_constants.py +4 -3
  92. package/hooks/hooks_constants/working_style_prompt_constants.py +29 -20
  93. package/hooks/lifecycle/AGENTS.md +18 -0
  94. package/hooks/lifecycle/CLAUDE.md +1 -18
  95. package/hooks/observability/AGENTS.md +20 -0
  96. package/hooks/observability/CLAUDE.md +1 -20
  97. package/hooks/session/AGENTS.md +33 -0
  98. package/hooks/session/CLAUDE.md +1 -33
  99. package/hooks/session/gh_pr_author_session_cleanup.py +7 -2
  100. package/hooks/session/task_list_loop_starter.py +3 -3
  101. package/hooks/session/test_gh_pr_author_session_cleanup.py +2 -1
  102. package/hooks/session/test_task_list_loop_starter.py +10 -2
  103. package/hooks/session/test_working_style_prompt.py +9 -4
  104. package/hooks/validation/AGENTS.md +20 -0
  105. package/hooks/validation/CLAUDE.md +1 -20
  106. package/hooks/validators/AGENTS.md +51 -0
  107. package/hooks/validators/CLAUDE.md +1 -51
  108. package/hooks/validators/health_check.py +52 -19
  109. package/hooks/validators/python_style_checks.py +1 -1
  110. package/hooks/validators/test_health_check.py +112 -27
  111. package/hooks/workflow/AGENTS.md +16 -0
  112. package/hooks/workflow/CLAUDE.md +1 -16
  113. package/installable-surfaces.manifest.json +1 -0
  114. package/output-styles/AGENTS.md +15 -0
  115. package/output-styles/CLAUDE.md +1 -15
  116. package/package.json +2 -1
  117. package/rules/AGENTS.md +61 -0
  118. package/rules/CLAUDE.md +1 -61
  119. package/rules/code-standards.md +5 -9
  120. package/rules/doc-prose-cuts.md +1 -1
  121. package/scripts/AGENTS.md +62 -0
  122. package/scripts/CLAUDE.md +1 -62
  123. package/scripts/codex_compat_materializer.py +4 -0
  124. package/scripts/dev_env_scripts_constants/AGENTS.md +21 -0
  125. package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -21
  126. package/scripts/sync_to_cursor/AGENTS.md +23 -0
  127. package/scripts/sync_to_cursor/CLAUDE.md +1 -23
  128. package/scripts/test_validate_instruction_pairs.py +90 -0
  129. package/scripts/test_verify_installable_package.py +5 -1
  130. package/scripts/tests/AGENTS.md +33 -0
  131. package/scripts/tests/CLAUDE.md +1 -33
  132. package/scripts/tests/test_codex_compat_materializer.py +36 -1
  133. package/scripts/validate_instruction_pairs.py +183 -0
  134. package/skills/AGENTS.md +72 -0
  135. package/skills/CLAUDE.md +1 -72
  136. package/skills/_shared/AGENTS.md +44 -0
  137. package/skills/_shared/CLAUDE.md +1 -44
  138. package/skills/_shared/advisor/AGENTS.md +9 -0
  139. package/skills/_shared/advisor/CLAUDE.md +1 -9
  140. package/skills/_shared/pr-loop/AGENTS.md +50 -0
  141. package/skills/_shared/pr-loop/CLAUDE.md +1 -50
  142. package/skills/_shared/pr-loop/prompts/AGENTS.md +9 -0
  143. package/skills/_shared/pr-loop/prompts/CLAUDE.md +1 -9
  144. package/skills/_shared/pr-loop/scripts/AGENTS.md +35 -0
  145. package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -35
  146. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +24 -0
  147. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -24
  148. package/skills/anthropic-plan/AGENTS.md +34 -0
  149. package/skills/anthropic-plan/CLAUDE.md +1 -34
  150. package/skills/anthropic-plan/scripts/AGENTS.md +11 -0
  151. package/skills/anthropic-plan/scripts/CLAUDE.md +1 -11
  152. package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +16 -0
  153. package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/CLAUDE.md +1 -16
  154. package/skills/anthropic-plan/templates/AGENTS.md +13 -0
  155. package/skills/anthropic-plan/templates/CLAUDE.md +1 -13
  156. package/skills/anthropic-plan/workflow/AGENTS.md +14 -0
  157. package/skills/anthropic-plan/workflow/CLAUDE.md +1 -14
  158. package/skills/auditing-claude-config/AGENTS.md +21 -0
  159. package/skills/auditing-claude-config/CLAUDE.md +1 -21
  160. package/skills/autoconverge/AGENTS.md +36 -0
  161. package/skills/autoconverge/CLAUDE.md +1 -36
  162. package/skills/autoconverge/reference/AGENTS.md +16 -0
  163. package/skills/autoconverge/reference/CLAUDE.md +1 -16
  164. package/skills/autoconverge/workflow/AGENTS.md +26 -0
  165. package/skills/autoconverge/workflow/CLAUDE.md +1 -26
  166. package/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +16 -0
  167. package/skills/autoconverge/workflow/autoconverge_report_constants/CLAUDE.md +1 -16
  168. package/skills/bugteam/AGENTS.md +30 -0
  169. package/skills/bugteam/CLAUDE.md +1 -30
  170. package/skills/bugteam/reference/AGENTS.md +20 -0
  171. package/skills/bugteam/reference/CLAUDE.md +1 -20
  172. package/skills/bugteam/reference/obstacles/AGENTS.md +24 -0
  173. package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -24
  174. package/skills/bugteam/scripts/AGENTS.md +30 -0
  175. package/skills/bugteam/scripts/CLAUDE.md +1 -30
  176. package/skills/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +18 -0
  177. package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +1 -18
  178. package/skills/closeout/reference/handoff-prompt-template.md +1 -1
  179. package/skills/codex-review/AGENTS.md +46 -0
  180. package/skills/codex-review/CLAUDE.md +1 -46
  181. package/skills/codex-review/reference/AGENTS.md +15 -0
  182. package/skills/codex-review/reference/CLAUDE.md +1 -15
  183. package/skills/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +18 -0
  184. package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +1 -18
  185. package/skills/codex-review/test_skill_scaffold.py +3 -3
  186. package/skills/copilot-review/AGENTS.md +18 -0
  187. package/skills/copilot-review/CLAUDE.md +1 -18
  188. package/skills/everything-search/AGENTS.md +17 -0
  189. package/skills/everything-search/CLAUDE.md +1 -17
  190. package/skills/fresh-branch/AGENTS.md +14 -0
  191. package/skills/fresh-branch/CLAUDE.md +1 -14
  192. package/skills/grok-spawn/AGENTS.md +28 -0
  193. package/skills/grok-spawn/CLAUDE.md +1 -28
  194. package/skills/pr-converge/AGENTS.md +32 -0
  195. package/skills/pr-converge/CLAUDE.md +1 -32
  196. package/skills/pr-converge/pr_converge_skill_constants/AGENTS.md +26 -0
  197. package/skills/pr-converge/pr_converge_skill_constants/CLAUDE.md +1 -26
  198. package/skills/pr-converge/reference/AGENTS.md +28 -0
  199. package/skills/pr-converge/reference/CLAUDE.md +1 -28
  200. package/skills/pr-converge/reference/obstacles/AGENTS.md +23 -0
  201. package/skills/pr-converge/reference/obstacles/CLAUDE.md +1 -23
  202. package/skills/pr-converge/scripts/AGENTS.md +46 -0
  203. package/skills/pr-converge/scripts/CLAUDE.md +1 -46
  204. package/skills/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +18 -0
  205. package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -18
  206. package/skills/pr-converge/workflows/AGENTS.md +16 -0
  207. package/skills/pr-converge/workflows/CLAUDE.md +1 -16
  208. package/skills/prototype/reference/honest-limitations.md +1 -1
  209. package/skills/rebase/AGENTS.md +32 -0
  210. package/skills/rebase/CLAUDE.md +1 -32
  211. package/skills/recall/AGENTS.md +30 -0
  212. package/skills/recall/CLAUDE.md +1 -30
  213. package/skills/remember/AGENTS.md +31 -0
  214. package/skills/remember/CLAUDE.md +1 -31
  215. package/skills/reviews/SKILL.md +1 -1
  216. package/skills/session-log/AGENTS.md +32 -0
  217. package/skills/session-log/CLAUDE.md +1 -32
  218. package/skills/session-tidy/AGENTS.md +36 -0
  219. package/skills/session-tidy/CLAUDE.md +1 -36
  220. package/skills/skill-builder/AGENTS.md +49 -0
  221. package/skills/skill-builder/CLAUDE.md +1 -49
  222. package/skills/skill-builder/references/AGENTS.md +25 -0
  223. package/skills/skill-builder/references/CLAUDE.md +1 -25
  224. package/skills/skill-builder/references/deterministic-elements.md +1 -1
  225. package/skills/skill-builder/templates/AGENTS.md +14 -0
  226. package/skills/skill-builder/templates/CLAUDE.md +1 -14
  227. package/skills/skill-builder/workflows/AGENTS.md +19 -0
  228. package/skills/skill-builder/workflows/CLAUDE.md +1 -19
  229. package/skills/task-build/AGENTS.md +29 -0
  230. package/skills/task-build/CLAUDE.md +1 -29
  231. package/skills/update/AGENTS.md +38 -0
  232. package/skills/update/CLAUDE.md +1 -38
  233. package/system-prompts/AGENTS.md +17 -0
  234. package/system-prompts/CLAUDE.md +1 -17
  235. package/system-prompts/software-engineer.xml +3 -3
  236. package/skills/imagegen/SKILL.md +0 -21
  237. package/skills/imagegen/scripts/config/__init__.py +0 -1
  238. package/skills/imagegen/scripts/config/constants.py +0 -28
  239. package/skills/imagegen/scripts/imagegen.py +0 -45
  240. package/skills/imagegen/scripts/imagegen_core.py +0 -528
  241. package/skills/imagegen/scripts/test_imagegen.py +0 -204
package/bin/CLAUDE.md CHANGED
@@ -1,112 +1 @@
1
- # bin
2
-
3
- The installer and its companion modules. Running `npx claude-dev-env` (or `node bin/install.mjs`) copies package files into the managed root (`~/.claude/` by default; `CLAUDE_CONFIG_DIR` or `--target` selects another), merges hook entries into that root's `settings.json`, installs Git hooks, and writes `~/.mypy.ini` under the process home.
4
-
5
- ## Files
6
-
7
- | File | Purpose |
8
- |---|---|
9
- | `install.mjs` | Main installer: builds a read-only plan via `install-plan.mjs`, then runs mutations inside `install-transaction.mjs` recovery (copy content directories, merge hooks, install skills, prune, git hooks, mypy.ini); routes `CLAUDE_HOME`, the manifest path, and `~/.mypy.ini` through `resolve-install-root.mjs`; resolves single or multi-profile targets before mutation and writes one ownership manifest per target |
10
- | `resolve-install-root.mjs` | Pure install-root resolver: precedence `--target` > `CLAUDE_CONFIG_DIR` > `~/.claude`, separator-boundary containment, and the declared external allowlist for `~/.mypy.ini` |
11
- | `select-install-targets.mjs` | Pure target selection for main-default, explicit `--target`, and `--profile`/`--profiles`; rejects ambiguous or duplicate targets; builds per-target manifest records with `targetIdentity` and `managedRoot` |
12
- | `install-plan.mjs` | Read-only install and uninstall plans: install preflight (managed root, source conflicts, Python, settings when hooks install) and uninstall preflight (settings JSON before removal, removable vs skipped manifest records), freezes plans E2/F execute |
13
- | `install-transaction.mjs` | Install, update, and uninstall transaction journal: captures prior settings, manifest, managed files, and `core.hooksPath`, restores them on failure, and supports fault injection phases for recovery tests |
14
- | `install.transaction.test.mjs` | Unit and sandbox installer tests for snapshot/restore and fault phases (`after_file_staging`, `after_settings_write`, `after_git_config`, `after_manifest_write`) |
15
- | `install.uninstall-transaction.test.mjs` | Uninstall plan preflight and recovery: malformed/non-object settings fail before removal, each fault phase restores files/settings/manifest/`core.hooksPath`, retry succeeds, selected-root containment |
16
- | `install.profile-root.test.mjs` | Contract tests for the install-root resolver: precedence, containment boundary, external allowlist, and the install.mjs import smoke check |
17
- | `install-constants.mjs` | The named values `install.mjs` reads: `SKIPPED_SOURCE_ENTRY_NAMES` and `SKIPPED_SOURCE_FILE_EXTENSIONS` for the build artifacts the source walk leaves behind, `RUN_BACKUP_DIRECTORY_NAME_PATTERN` for the timestamp shape a run backup directory carries, `MANAGED_SKILLS_DIRECTORY_NAME` and `MANAGED_HOOKS_DIRECTORY_NAME` for the directory name each of those trees carries in a package source and under `~/.claude` — read by the copy loops, the hooks.json reads, the git-hook shims, the mypy configuration, and the prunes alike — `SETTINGS_FILE_NAME` for the settings file the merge, the retired-hook prune, and the uninstall purge share, and `MYPY_INI_FILE_NAME` for the home-directory file `install_mypy_ini.mjs` writes (also imported by `resolve-install-root.mjs`) |
18
- | `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` |
19
- | `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 `$`) |
20
- | `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 |
21
- | `install_mypy_ini.mjs` | Writes `~/.mypy.ini` with settings that make mypy find the hooks package and enforce strict type checking |
22
- | `install.test.mjs` | Unit tests for `install.mjs` — covers conflict detection, interpreter detection, settings merging, the settings shapes the installer never wrote — those every hook walk hands back untouched, and the shipped-event value the merge replaces with a warning — the source-artifact skip in `collectFiles`, the case-only rename decision and the `copyTree` copy that acts on it, the retired-hook diff and settings prune, the stale-file prune: the manifest diff, path-key case folding, emptied-parent cleanup, and the warn-and-keep paths, and backup retention: the sweep a moved-content run drives, the empty root a run whose moves failed gives up, and the populated root retention keeps |
23
- | `install.profiles.test.mjs` | Profile target-selection and per-target ownership manifest contract tests (main-default, multi-profile, ambiguity/duplicate rejection, help text) |
24
- | `install.plan.test.mjs` | Read-only plan and preflight tests: zero-write plan construction, source-conflict and missing-Python fail-closed, settings check only when hooks install, tolerant broken-manifest, invalid managed root, mutation-kind list for E2 |
25
- | `install.prune.test.mjs` | End-to-end prune tests that run the real installer against a sandbox `HOME` — retired-skill, retired-hook, and stale-file moves into one timestamped backup, the settings entry a retired hook loses, the top-level paths every root's diff leaves alone, the manifest record a failed move keeps, the full-install and resolved-dependency gates, backup retention, and the uninstall: the `~/.mypy.ini` removal, the containment guard, and nested-directory cleanup |
26
- | `git_hooks_installer.test.mjs` | Tests for `git_hooks_installer.mjs` |
27
- | `install_mypy_ini.test.mjs` | Tests for `install_mypy_ini.mjs` |
28
-
29
- ## Source build artifacts
30
-
31
- `collectFiles` walks the package source and skips the artifacts a contributor's tooling writes beside it: the entry names `__pycache__`, `.ruff_cache`, `.pytest_cache`, `.mypy_cache`, `node_modules`, `.DS_Store`, and any file ending `.pyc` or `.pyo`. A skipped directory takes everything under it out of the walk. The `files` negations in `package.json` (`!**/__pycache__/**`, `!**/*.py[cod]`, the cache directories, `!**/*.log`, `!**/*.egg-info/**`) keep the same artifacts out of the published tarball, and `.npmignore` carries those patterns for tooling that reads it — keep the two in step. An `npx` install reads a clean tree; the walk covers a local `node bin/install.mjs` run against a working tree that holds the artifacts.
32
-
33
- The skip and the cleanup of artifacts an earlier install copied are one code path. A `.pyc` a prior manifest records under any managed root sits outside the set the walk returns, so the next full install reads it as stale, moves it into that run's backup root, and drops it from the manifest the run writes.
34
-
35
- ## Copying a file whose name changed letter case
36
-
37
- `copyTree` renames a destination entry that differs from the shipped file name only in letter case to the shipped name, then copies. On a case-insensitive volume `copyFileSync` writes its bytes through whichever entry the filesystem resolves the path to, so a package shipping `README.md` over an installed `Readme.md` would fill the installed entry and leave the earlier spelling standing. The rename runs first because `renameSync` inside one directory is atomic: a run interrupted between the rename and the copy leaves the file present under the shipped name holding the earlier content, which the next install overwrites.
38
-
39
- The decision reads the destination directory's entry names, cached one listing per directory for the whole copy run. On a case-sensitive volume the two names are two files, so the rename is skipped and each name keeps its own content. `caseOnlyRenameSourceName(shippedName, existingNames, options)` holds the decision, and `options.isCaseInsensitive` carries the platform answer as a value so a test drives either branch on a host of either kind.
40
-
41
- ## Retired-skill prune
42
-
43
- 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>/skills/<skill-name>/`, a backup root outside `~/.claude/skills` so a backed-up directory is never re-discovered as a skill. The `skills/` segment mirrors `~/.claude`, matching the layout the stale-file prune writes, so one recovery point reads as a copy of the tree it came from. One run shares one timestamped root, so a run leaves one recovery point. 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.
44
-
45
- 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. Recovery of a wrongly-matched directory runs until the next pruning install, which keeps its own backup and retires the rest.
46
-
47
- ## Stale-file prune
48
-
49
- A full install also moves aside a file under a managed root that the run leaves unwritten. `copyTree` adds and overwrites but never removes, so every root the installer writes carries the same drift, and the prune covers all of them: `rules`, `docs`, `commands`, `agents`, `system-prompts`, `scripts`, `_shared`, `audit-rubrics`, `skills`, and `hooks` — the names in `MANAGED_TOP_LEVEL_DIRECTORY_NAMES`.
50
-
51
- Nothing moves unless a prior install recorded it. That single rule is what makes covering ten roots as safe as covering one: the installer reads the file list from `~/.claude/.claude-dev-env-manifest.json`, subtracts every file the run copied across all source roots, and moves what remains.
52
-
53
- The prune runs once per root, each call confined to its own root. Per-root iteration gives the containment guard and the emptied-parent walk the root that owns each file, and it settles `_shared`: `~/.claude/_shared` and `~/.claude/skills/_shared` are distinct absolute paths, so the `_shared` call and the `skills` call each see their own files and no path enters two diffs. Each root's content lands under `~/.claude/.claude-dev-env-pruned/<timestamp>/<root-name>/<relative>`, so the backup mirrors `~/.claude`. Every prune in a run shares that one timestamped root. A recorded path under no managed root — `~/.claude/CLAUDE.md`, `settings.json`, the manifest itself, and the `~/.mypy.ini` that sits in the home directory beside `~/.claude` — reaches no root's diff and stays where it is. The install summary reports the skills root's own count on the `skills:` line and the sum across roots on its own line.
54
-
55
- The manifest diff limits the move to files the installer itself wrote. Runtime-generated content — a Python `__pycache__` entry, a ruff cache, a log — and any file a user authored under a managed root stay in place, because no install recorded them. Path comparison ignores letter case on Windows and macOS, so a package shipping `README.md` over an installed `Readme.md` keeps the bytes the run just wrote. A directory or a link standing where the manifest records a file is skipped with a warning, so the mover never renames a whole tree and never follows a link out of `~/.claude`. A directory emptied by a move is removed, walking up to the root the file sat under. A move that fails logs a warning and leaves the file in place, so a prune failure costs at most a stale file. The installer records each such path in the fresh manifest when the file is still on disk, so the file stays inside the next full install's diff and gets another attempt.
56
-
57
- A missing or unreadable manifest, or one carrying no file list, holds the stale-file prune for that run: with no record of what an install wrote, the run has nothing to diff against.
58
-
59
- A run writes both manifest keys — the file list and the skill-name list — wholesale from what it just installed only when the prunes ran that run and read the prior record all the way through, so the next diff reads as "the package stopped shipping this". Every other run unions what it wrote onto the prior lists: a scoped `--only` install, a full install holding its prunes behind an unresolved dependency group, and a run whose prune step ends early with a logged warning. The union keeps every entry a later prune needs to spot a stale file or a retired skill, and keeps `--uninstall` able to name the whole tree. The prune itself bounds the lists: a stale path leaves the record on the first full install that moves it aside.
60
-
61
- ## Retired-hook entries in settings.json
62
-
63
- A hook script under `~/.claude/hooks` carries a second reference: the `settings.json` entry that runs it. A full install removes that entry in the same run that moves the script aside, and removes it first — a `settings.json` naming a script that has left the hooks directory makes every session start invoke a missing file.
64
-
65
- The retired set comes from the manifest diff alone: the hook files a prior install recorded that this run leaves unwritten, each taken relative to `~/.claude/hooks`. A script the run still writes stays out of the set, and a path no install of ours recorded never enters it, so a user-authored hook is out of reach of the prune. Each command is matched on the anchored `/.claude/hooks/<relative>` tail the merge uses to tell this installer's entries from a user's, so a command whose path is a retired tail plus a suffix (`retired_gate.py.bak`) names another file and stays.
66
-
67
- The walk covers every event type the settings file holds rather than the ones the current `hooks.json` names, so an entry under an event type the package stopped shipping is reached too. A matcher group left empty is dropped, and an event type left empty goes with it. The file is written once, and only when an entry left it, so a run that retires no hook leaves `settings.json` byte-identical.
68
-
69
- Every settings walk recognizes the shapes the installer writes and steps around the rest, so a hand-edited or third-party `settings.json` carries an install through rather than ending it. A matcher group carrying no `hooks` array and a hook entry whose `command` is not a string survive every walk untouched. An event type whose value is not an array of groups survives this prune and the uninstall purge; the merge replaces that value with the group list it ships for that event type, warning with the event type named so the user can recover the value from their own history. An event type the package ships no groups for keeps whatever value the file holds.
70
-
71
- ## Backup retention
72
-
73
- A run that moves content into `~/.claude/.claude-dev-env-pruned/<timestamp>/` then retires the other run backups, so the directory holds the one recovery point closest to what sits on disk. The retired-skill prune and the stale-file prune each report how many moves succeeded, and their sum is the signal the sweep answers to. The sweep removes a direct child whose name matches the installer's timestamp shape (`2026-07-25T18-04-11-923Z`), which leaves anything else under the pruned-backup directory in place, along with the directory itself. A removal that fails logs a warning and the sweep carries on, so retention never ends an install. The install output names the count when the sweep removes anything.
74
-
75
- A run that moves nothing sweeps nothing, so every recovery point the user holds stays where it is. `moveIntoRunBackup` creates the directories leading to a backup path before it renames, so a run whose every move fails — the antivirus scanner or open editor case — leaves that timestamped root standing empty. Retention clears it with `rmdirSync` alone, depth first, so a directory holding anything survives every step.
76
-
77
- ## Uninstall
78
-
79
- `--uninstall` builds a read-only uninstall plan, captures a recovery snapshot, then removes each file the plan lists.
80
-
81
- Settings JSON is validated before any removal. A malformed or non-object `settings.json` fails closed with the managed files still on disk. Each manifest record passes a containment guard: the path resolves under `~/.claude`, or it names the `~/.mypy.ini` the install writes in the home directory. Every other record is skipped with a warning and counted. Skipping keeps one malformed record — hand-edited, or written by an installer that ran against a different home — from stranding the user with a half-removed install. The purge removes every legitimate record, clears the manifest, and reports the skipped count.
82
-
83
- The uninstall runs inside the same snapshot/restore journal as install: prior settings, manifest, managed files, and `core.hooksPath` restore when a later phase fails, so a retry starts from a complete ownership record. The journal is discarded only after a successful commit.
84
-
85
- Removing a file leaves its directory a candidate for cleanup. Once the file loop ends, the purge walks up from each such directory to the managed top-level directory the file sits under (`MANAGED_TOP_LEVEL_DIRECTORY_NAMES`), removing each directory it finds empty. That reaches a nested tree such as `skills/<name>/scripts/`. A record under no managed root gets no walk, so `~/.claude` itself is never a stop root and a directory the installer never wrote stays. A separate pass drops each managed top-level directory the purge empties.
86
-
87
- ## Prune gates
88
-
89
- Every prune runs behind the same two gates: a full install, and every declared dependency group resolved. When any dependency group fails to resolve, all of them are skipped for the whole run with a logged notice naming the unresolved group. An unresolved dependency contributes no skills to the installed set, so a live skill that a dependency package supplies would look retired and its files would look stale; holding every prune until each dependency resolves keeps that skill's files in place and keeps their manifest records, so a run with every dependency resolved can still prune them.
90
-
91
- ## Key exports from install.mjs
92
-
93
- | Export | Description |
94
- |---|---|
95
- | `CONTENT_DIRECTORIES` | Array of package subdirectory names copied verbatim to `~/.claude/` |
96
- | `MANAGED_TOP_LEVEL_DIRECTORY_NAMES` | The content directories plus `skills` and `hooks`; the stale-file prune walks it to give each root its own diff, and the uninstall purge reads it to find the root a recorded file belongs to |
97
- | `collectFiles(directory)` | Lists every file under a source directory, skipping the build-artifact names and extensions in `install-constants.mjs` |
98
- | `pythonCandidatesForPlatform(platform)` | Returns ordered Python interpreter candidates to probe; `py -3` first on Windows to avoid Microsoft Store alias issues |
99
- | `isWindowsStorePythonStub(path)` | Returns true when the path resolves to the non-spawnable WindowsApps stub |
100
- | `interpreterCommandFromPath(path)` | Formats an absolute interpreter path as a settings.json hook command prefix |
101
- | `collectPackageSourceConflicts(dir)` | Returns any unmerged git conflicts in the package source; installer aborts when any exist |
102
- | `pruneStaleInstalledFiles(priorFiles, currentFiles, destinationRoot, backupRoot, options)` | Moves each manifest-recorded file under the destination root that the run leaves unwritten into the run's backup root; returns `{ prunedCount, failedPaths }`. `options.isCaseInsensitive` drives path-key case folding, defaulting to this host's filesystem; `options.managedHomeDirectory` sets the home the containment guard tests against, defaulting to `~/.claude` |
103
- | `copyTree(sourceBase, destBase, options)` | Copies every file under a source directory, renaming a destination entry that differs from the shipped name only in letter case to the shipped name first; returns `{ created, updated, paths }`. `options.isCaseInsensitive` drives that rename, defaulting to this host's filesystem |
104
- | `caseOnlyRenameSourceName(shippedName, existingNames, options)` | Returns the existing directory entry a shipped file name would overwrite through a case-only spelling difference, or null; `options.isCaseInsensitive` defaults to this host's filesystem |
105
- | `retiredManagedHookRelativePaths(priorFiles, currentFiles, hooksRoot)` | Returns the hook script paths a prior install recorded under the hooks root that this run leaves unwritten, each relative to that root |
106
- | `pruneRetiredHookEntriesFromSettings(settingsPath, retiredPaths)` | Removes each settings.json entry running a retired managed hook script, writing the file only when an entry left it; returns the removed count |
107
- | `retainNewestRunBackupOnly(runBackupRoot, didRunMoveContent)` | Retires every run backup sitting beside the run's own when `didRunMoveContent` holds; clears the run's empty root with `rmdirSync` when it does not |
108
- | `comparisonKeyForPath(path, options)` | Builds the key two paths are compared through: resolved, forward-slashed, and lowercased when `options.isCaseInsensitive` holds — which defaults to true on Windows and macOS |
109
-
110
- ## Install groups
111
-
112
- `install.mjs` defines install groups (`core`, `journal`) plus any dependency groups discovered from `package.json` `dependencies`. The `core` group installs skills, all hooks, and the content directories. `journal` installs only its skill set.
1
+ @AGENTS.md
@@ -39,6 +39,7 @@ export const EVER_SHIPPED_SKILL_NAMES = new Set([
39
39
  'gotcha',
40
40
  'grok-spawn',
41
41
  'grokify',
42
+ 'imagegen',
42
43
  'implement',
43
44
  'issue-tracker',
44
45
  'log-audit',
@@ -6,14 +6,13 @@
6
6
  */
7
7
 
8
8
  /**
9
- * Entry names the source walk leaves behind, whatever directory they sit in.
9
+ * Entry names the source walk keeps out of installed trees.
10
10
  *
11
- * Each name belongs to a tool that writes beside the source it reads: Python
12
- * bytecode caches, the ruff, pytest, and mypy caches, an installed
13
- * `node_modules` tree, and the macOS Finder's `.DS_Store` marker. A contributor
14
- * who runs the test suites and then runs `node bin/install.mjs` copies from a
15
- * source tree carrying all of them, so skipping the names at the walk keeps them
16
- * out of `~/.claude` and out of the install manifest.
11
+ * Each name represents local metadata or build output.
12
+ * A contributor who runs the test suites and then runs `node bin/install.mjs`
13
+ * copies from a source tree carrying these entries, so skipping the names at
14
+ * the walk keeps installed trees and the install manifest focused on runtime
15
+ * files.
17
16
  */
18
17
  export const SKIPPED_SOURCE_ENTRY_NAMES = new Set([
19
18
  '__pycache__',
package/bin/install.mjs CHANGED
@@ -115,7 +115,6 @@ export const CORE_INCLUDE_DIRECTORIES = [
115
115
  ];
116
116
 
117
117
  export const CORE_SKILLS = [
118
- 'imagegen',
119
118
  'orchestrator', 'orchestrator-refresh', 'team-advisor', 'grokify',
120
119
  'grok-spawn',
121
120
  'small-cl', 'comments', 'reviews', 'descriptions', 'emergencies',
@@ -896,9 +895,14 @@ export function copyTree(sourceBase, destBase, options = {}) {
896
895
 
897
896
  /**
898
897
  * If destPath exists and differs from incomingPath, copy the existing file to
899
- * ~/.claude/backups/CLAUDE.md.<timestamp>.bak before the installer overwrites it.
898
+ * ~/.claude/backups/<backupName>.<timestamp>.bak before the installer overwrites it.
899
+ *
900
+ * @param {string} destPath The managed file the installer writes.
901
+ * @param {string} incomingPath The package file that replaces it.
902
+ * @param {string} backupName The managed file name used in the backup path.
903
+ * @returns {string|null} The backup path when existing content differs.
900
904
  */
901
- function backupClaudeHubBeforeOverwrite(destPath, incomingPath) {
905
+ function backupHubBeforeOverwrite(destPath, incomingPath, backupName) {
902
906
  if (!existsSync(destPath)) return null;
903
907
  const existingBytes = readFileSync(destPath);
904
908
  const incomingBytes = readFileSync(incomingPath);
@@ -906,7 +910,7 @@ function backupClaudeHubBeforeOverwrite(destPath, incomingPath) {
906
910
  const backupsDir = join(CLAUDE_HOME, 'backups');
907
911
  mkdirSync(backupsDir, { recursive: true });
908
912
  const stamp = new Date().toISOString().replace(/[:.]/g, '-');
909
- const backupPath = join(backupsDir, `CLAUDE.md.${stamp}.bak`);
913
+ const backupPath = join(backupsDir, `${backupName}.${stamp}.bak`);
910
914
  copyFileSync(destPath, backupPath);
911
915
  return backupPath;
912
916
  }
@@ -2060,7 +2064,7 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
2060
2064
  const claudeHubSource = join(PACKAGE_ROOT, 'CLAUDE.md');
2061
2065
  if (existsSync(claudeHubSource)) {
2062
2066
  const claudeHubDest = join(CLAUDE_HOME, 'CLAUDE.md');
2063
- const backupPath = backupClaudeHubBeforeOverwrite(claudeHubDest, claudeHubSource);
2067
+ const backupPath = backupHubBeforeOverwrite(claudeHubDest, claudeHubSource, 'CLAUDE.md');
2064
2068
  if (backupPath) {
2065
2069
  console.log(
2066
2070
  ` \u21bb ${relative(CLAUDE_HOME, backupPath)} (previous CLAUDE.md hub preserved)`
@@ -2070,6 +2074,19 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
2070
2074
  allInstalledFiles.push(claudeHubDest);
2071
2075
  console.log(` \u2713 ${relative(CLAUDE_HOME, claudeHubDest)} (hub)`);
2072
2076
  }
2077
+ const agentsHubSource = join(PACKAGE_ROOT, 'AGENTS.md');
2078
+ if (existsSync(agentsHubSource)) {
2079
+ const agentsHubDest = join(CLAUDE_HOME, 'AGENTS.md');
2080
+ const backupPath = backupHubBeforeOverwrite(agentsHubDest, agentsHubSource, 'AGENTS.md');
2081
+ if (backupPath) {
2082
+ console.log(
2083
+ ` \u21bb ${relative(CLAUDE_HOME, backupPath)} (previous AGENTS.md guidance preserved)`
2084
+ );
2085
+ }
2086
+ copyFileSync(agentsHubSource, agentsHubDest);
2087
+ allInstalledFiles.push(agentsHubDest);
2088
+ console.log(` \u2713 ${relative(CLAUDE_HOME, agentsHubDest)} (canonical guidance)`);
2089
+ }
2073
2090
  const isFullInstall = !selectedGroups;
2074
2091
  const didPruneRun = isFullInstall && UNRESOLVED_DEPENDENCY_NAMES.length === 0;
2075
2092
  let failedPrunePaths = [];
@@ -2424,6 +2441,8 @@ with package, version, targetIdentity, managedRoot, files, and skills.
2424
2441
 
2425
2442
  If ~/.claude/CLAUDE.md already exists and differs from the package copy, the installer
2426
2443
  writes the previous contents to ~/.claude/backups/CLAUDE.md.<timestamp>.bak first.
2444
+ If ~/.claude/AGENTS.md already exists and differs from the package copy, the installer
2445
+ writes the previous contents to ~/.claude/backups/AGENTS.md.<timestamp>.bak first.
2427
2446
  `);
2428
2447
  }
2429
2448
 
@@ -42,6 +42,7 @@ import {
42
42
  pruneRetiredHookEntriesFromSettings,
43
43
  retainNewestRunBackupOnly,
44
44
  } from './install.mjs';
45
+ import { EVER_SHIPPED_SKILL_NAMES } from './ever-shipped-skills.mjs';
45
46
  import {
46
47
  expandHomeDirectoryTokens,
47
48
  expandHomeDirectoryTokensInSettings,
@@ -204,6 +205,14 @@ test('CORE_SKILLS ships issue-tracker so the core group installs the skill the S
204
205
  });
205
206
 
206
207
 
208
+ test('EVER_SHIPPED_SKILL_NAMES retains imagegen so reinstall prunes the retired skill', () => {
209
+ assert.ok(
210
+ EVER_SHIPPED_SKILL_NAMES.has('imagegen'),
211
+ 'imagegen must remain in the retirement registry after the package stops shipping it',
212
+ );
213
+ });
214
+
215
+
207
216
  test('CONTENT_DIRECTORIES includes audit-rubrics so installer copies category rubrics and prompts to ~/.claude/audit-rubrics/', () => {
208
217
  assert.ok(
209
218
  CONTENT_DIRECTORIES.includes('audit-rubrics'),
@@ -1658,6 +1667,31 @@ test('collectFiles skips every named cache directory and loose bytecode file', (
1658
1667
  });
1659
1668
 
1660
1669
 
1670
+ test('copyTree copies AGENTS.md with agent definitions', () => {
1671
+ const sourceRoot = mkdtempSync(join(tmpdir(), 'cdev-copy-agents-source-'));
1672
+ const destinationRoot = mkdtempSync(join(tmpdir(), 'cdev-copy-agents-destination-'));
1673
+ try {
1674
+ writeFileSync(join(sourceRoot, 'AGENTS.md'), '# Shared guidance\n');
1675
+ const agentDefinitionPath = join(sourceRoot, 'docs-agent.md');
1676
+ writeFileSync(
1677
+ agentDefinitionPath,
1678
+ '---\nname: docs-agent\ndescription: fixture agent\n---\n',
1679
+ );
1680
+
1681
+ const copyStats = copyTree(sourceRoot, destinationRoot);
1682
+ const copiedAgentsPath = join(destinationRoot, 'AGENTS.md');
1683
+ const copiedAgentPath = join(destinationRoot, 'docs-agent.md');
1684
+
1685
+ assert.equal(existsSync(copiedAgentsPath), true, 'the canonical instructions install');
1686
+ assert.equal(existsSync(copiedAgentPath), true, 'the real agent definition installs');
1687
+ assert.deepEqual(copyStats.paths, [copiedAgentsPath, copiedAgentPath]);
1688
+ } finally {
1689
+ rmSync(sourceRoot, { recursive: true, force: true });
1690
+ rmSync(destinationRoot, { recursive: true, force: true });
1691
+ }
1692
+ });
1693
+
1694
+
1661
1695
  const SHIPPED_README_NAME = 'README.md';
1662
1696
  const INSTALLED_README_NAME = 'Readme.md';
1663
1697
  const RETIRED_HOOK_RELATIVE_PATH = 'blocking/retired_gate.py';
@@ -0,0 +1,23 @@
1
+ # commands
2
+
3
+ Slash-command definitions installed into `~/.claude/commands/` by `bin/install.mjs`. Each `.md` file registers a `/command-name` the user can type in Claude Code. The file name (without `.md`) becomes the command name.
4
+
5
+ ## Command files
6
+
7
+ | File | Command | What it does |
8
+ |---|---|---|
9
+ | `commit.md` | `/commit` | Commits and pushes changes to GitHub |
10
+ | `docupdate.md` | `/docupdate` | Updates documentation to match current code state |
11
+ | `hook-log-extract.md` | `/hook-log-extract` | Extracts and formats hook log entries for a session |
12
+ | `hook-log-init.md` | `/hook-log-init` | Initializes the Neon Postgres schema that backs the hook-log extractor (one-time per machine) |
13
+ | `implement.md` | `/implement` | Provides full implementation context to a right-sized engineer in XML format |
14
+ | `plan.md` | `/plan` | Plans a feature through the `anthropic-plan` skill and workflow |
15
+ | `pr-comments.md` | `/pr-comments` | Fetches and formats PR review comments for response |
16
+ | `review-plan.md` | `/review-plan` | Reviews the current plan packet against code standards |
17
+ | `right-size.md` | `/right-size` | Checks an implementation against the Right-Sized Engineering rules |
18
+ | `sr-loop.md` | `/sr-loop` | Runs the converging cleanup loop: /simplify passes until clean, then a code-review fix pass |
19
+ | `sum.md` | `/sum` | Generates a formatted session summary for quick pickup in a new session |
20
+
21
+ ## Format
22
+
23
+ Each file is plain Markdown. The first paragraph is the command's help text shown in the Claude Code UI. The body is the full instruction set Claude follows when the command runs.
@@ -1,23 +1 @@
1
- # commands
2
-
3
- Slash-command definitions installed into `~/.claude/commands/` by `bin/install.mjs`. Each `.md` file registers a `/command-name` the user can type in Claude Code. The file name (without `.md`) becomes the command name.
4
-
5
- ## Command files
6
-
7
- | File | Command | What it does |
8
- |---|---|---|
9
- | `commit.md` | `/commit` | Commits and pushes changes to GitHub |
10
- | `docupdate.md` | `/docupdate` | Updates documentation to match current code state |
11
- | `hook-log-extract.md` | `/hook-log-extract` | Extracts and formats hook log entries for a session |
12
- | `hook-log-init.md` | `/hook-log-init` | Initializes the Neon Postgres schema that backs the hook-log extractor (one-time per machine) |
13
- | `implement.md` | `/implement` | Provides full implementation context to a right-sized engineer in XML format |
14
- | `plan.md` | `/plan` | Plans a feature through the `anthropic-plan` skill and workflow |
15
- | `pr-comments.md` | `/pr-comments` | Fetches and formats PR review comments for response |
16
- | `review-plan.md` | `/review-plan` | Reviews the current plan packet against code standards |
17
- | `right-size.md` | `/right-size` | Checks an implementation against the Right-Sized Engineering rules |
18
- | `sr-loop.md` | `/sr-loop` | Runs the converging cleanup loop: /simplify passes until clean, then a code-review fix pass |
19
- | `sum.md` | `/sum` | Generates a formatted session summary for quick pickup in a new session |
20
-
21
- ## Format
22
-
23
- Each file is plain Markdown. The first paragraph is the command's help text shown in the Claude Code UI. The body is the full instruction set Claude follows when the command runs.
1
+ @AGENTS.md
@@ -38,9 +38,9 @@ suspected of drift.
38
38
  bws run -- python packages/claude-dev-env/hooks/diagnostic/hook_log_extractor.py --summary
39
39
  ```
40
40
 
41
- Skip extraction. Print the top-10 blockers of the last 24 hours with
42
- their block count and a single truncated command preview, or
43
- `No new blocks since last run.` when the window is empty.
41
+ Run summary mode to print the top-10 blockers of the last 24 hours with
42
+ their block count and a single truncated command preview. An empty window
43
+ prints `Blocked events recorded in the last 24 hours: 0.`.
44
44
 
45
45
  ```
46
46
  bws run -- python packages/claude-dev-env/hooks/diagnostic/hook_log_extractor.py --query <name>
package/docs/AGENTS.md ADDED
@@ -0,0 +1,32 @@
1
+ # docs
2
+
3
+ Reference documentation installed into `~/.claude/docs/` by `bin/install.mjs`. These files are loaded on demand by rules, skills, and agents — they are not always-on context.
4
+
5
+ ## Files
6
+
7
+ | File | Purpose |
8
+ |---|---|
9
+ | `CODE_RULES.md` | Compact agent reference for all code rules; ⚡ marks hook-enforced rules; canonical source agents load before writing code |
10
+ | `TEST_QUALITY.md` | Test writing standards: what to test, what to remove, React testing patterns, anti-patterns |
11
+ | `BDD_DISCOVERY_PROTOCOL.md` | Example Mapping algorithm for discovery before implementation; based on Smart & Molak *BDD in Action* §6.4 |
12
+ | `BDD_SCENARIO_QUALITY.md` | Seven scenario quality patterns (§7.6-style catalog) |
13
+ | `BDD_TEST_LAYOUT.md` | `describe/when/should` test layout and soap-opera personas |
14
+ | `DJANGO_PATTERNS.md` | Django-specific coding patterns |
15
+ | `REACT_PATTERNS.md` | React-specific coding patterns |
16
+ | `agent-spawn-protocol.md` | Full agent-spawn protocol behind the `rules/agent-spawn-protocol.md` kernel: context-sufficiency check, `/prompt-generator` prompt crafting, and the spawn step |
17
+ | `nas-ssh-invocation.md` | Full NAS ssh policy behind the `rules/nas-ssh-invocation.md` kernel: the OpenSSH binary form, config sources, and hook enforcement |
18
+ | `worker-completion-gate.md` | Full worker-completion gate behind the `rules/workers-done-before-complete.md` kernel: the checklist, examples, and run-state records |
19
+ | `wsl-docker-cowork-starter-matrix.md` | Host matrix: WSL/Docker/cowork component → starter → required? → shutdown; policy options with costs; no unmeasured `.wslconfig` memory cap |
20
+ | `host-pool-health-monitor.md` | Operator recipe for Windows pool/handle health: thresholds, clean-shell re-run of `Capture-PoolHealth.ps1`, RC2/RC3/RC4 remediation map |
21
+
22
+ ## Subdirectory
23
+
24
+ | Entry | Description |
25
+ |---|---|
26
+ | `references/` | Pointer documents to external sources and standard terminology; loaded on demand |
27
+
28
+ ## Load pattern
29
+
30
+ A rule points to a doc with the path wrapped in backticks, such as `@~/.claude/docs/<file>.md`. The backticks make it a plain pointer: Claude Code reads the doc only when a rule, skill, or agent opens it, so the doc stays out of session-start context. The same path without backticks expands into context at launch when it sits in a file that loads at session start.
31
+
32
+ The `InstructionsLoaded` hook confirms this: a bare `@`-import fires an `include` load event; a backtick-wrapped path fires none.
package/docs/CLAUDE.md CHANGED
@@ -1,32 +1 @@
1
- # docs
2
-
3
- Reference documentation installed into `~/.claude/docs/` by `bin/install.mjs`. These files are loaded on demand by rules, skills, and agents — they are not always-on context.
4
-
5
- ## Files
6
-
7
- | File | Purpose |
8
- |---|---|
9
- | `CODE_RULES.md` | Compact agent reference for all code rules; ⚡ marks hook-enforced rules; canonical source agents load before writing code |
10
- | `TEST_QUALITY.md` | Test writing standards: what to test, what to remove, React testing patterns, anti-patterns |
11
- | `BDD_DISCOVERY_PROTOCOL.md` | Example Mapping algorithm for discovery before implementation; based on Smart & Molak *BDD in Action* §6.4 |
12
- | `BDD_SCENARIO_QUALITY.md` | Seven scenario quality patterns (§7.6-style catalog) |
13
- | `BDD_TEST_LAYOUT.md` | `describe/when/should` test layout and soap-opera personas |
14
- | `DJANGO_PATTERNS.md` | Django-specific coding patterns |
15
- | `REACT_PATTERNS.md` | React-specific coding patterns |
16
- | `agent-spawn-protocol.md` | Full agent-spawn protocol behind the `rules/agent-spawn-protocol.md` kernel: context-sufficiency check, `/prompt-generator` prompt crafting, and the spawn step |
17
- | `nas-ssh-invocation.md` | Full NAS ssh policy behind the `rules/nas-ssh-invocation.md` kernel: the OpenSSH binary form, config sources, and hook enforcement |
18
- | `worker-completion-gate.md` | Full worker-completion gate behind the `rules/workers-done-before-complete.md` kernel: the checklist, examples, and run-state records |
19
- | `wsl-docker-cowork-starter-matrix.md` | Host matrix: WSL/Docker/cowork component → starter → required? → shutdown; policy options with costs; no unmeasured `.wslconfig` memory cap |
20
- | `host-pool-health-monitor.md` | Operator recipe for Windows pool/handle health: thresholds, clean-shell re-run of `Capture-PoolHealth.ps1`, RC2/RC3/RC4 remediation map |
21
-
22
- ## Subdirectory
23
-
24
- | Entry | Description |
25
- |---|---|
26
- | `references/` | Pointer documents to external sources and standard terminology; loaded on demand |
27
-
28
- ## Load pattern
29
-
30
- A rule points to a doc with the path wrapped in backticks, such as `@~/.claude/docs/<file>.md`. The backticks make it a plain pointer: Claude Code reads the doc only when a rule, skill, or agent opens it, so the doc stays out of session-start context. The same path without backticks expands into context at launch when it sits in a file that loads at session start.
31
-
32
- The `InstructionsLoaded` hook confirms this: a bare `@`-import fires an `include` load event; a backtick-wrapped path fires none.
1
+ @AGENTS.md
@@ -1,6 +1,6 @@
1
1
  # Code Rules Reference
2
2
 
3
- Compact **projection** of the repository-root canonical policy [`AGENTS.md`](../../../AGENTS.md). When this file and `AGENTS.md` disagree, **`AGENTS.md` wins** update this projection in the same change.
3
+ Compact **projection** of the checked-in review contract [`.cursor/BUGBOT.md`](../../../.cursor/BUGBOT.md). Update this reference alongside the full review contract when review rules change.
4
4
 
5
5
  ⚡ marks rules enforced by hand-maintained `code_rules_enforcer.py` — the hook blocks the Write/Edit and returns the corrective detail at violation time, so this document lists those rules by name only. Session policy (question routing, task tracking) lives in `rules/*.md`; see [`code-standards.md`](../rules/code-standards.md).
6
6
 
@@ -0,0 +1,22 @@
1
+ # Image generation
2
+
3
+ The canonical image-generation entrypoint lives in the
4
+ `python-automation` repository at `shared_utils.imagegen`.
5
+
6
+ Resolve `python_automation_root` and `python_exe` from the repository's
7
+ `run/tooling.json`, set `PYTHONPATH` to the resolved root, and run:
8
+
9
+ ```powershell
10
+ $tooling = Get-Content '<REPO>\\run\\tooling.json' | ConvertFrom-Json
11
+ $python_automation_root = $tooling.python_automation_root
12
+ $python_executable = $tooling.python_exe
13
+ $env:PYTHONPATH = $python_automation_root
14
+
15
+ & $python_executable -m shared_utils.imagegen `
16
+ --prompt-file <PROMPT.txt> `
17
+ --size 2880x2880 `
18
+ --out <IMAGE.png>
19
+ ```
20
+
21
+ Repeat `--reference-image <IMAGE.png>` for up to two visual references.
22
+ The command verifies the decoded PNG dimensions before reporting success.
@@ -0,0 +1,17 @@
1
+ # docs/references
2
+
3
+ Pointer documents to external sources, standard terminology, and internal tool or skill usage. Files here are loaded on demand by rules that cite them.
4
+
5
+ ## Files
6
+
7
+ | File | Purpose |
8
+ |---|---|
9
+ | `dead-code-elimination.md` | External sources and standard terms behind CODE_RULES §9.8 (remove code you orphan): DCE, tree shaking, reachability analysis, and the Lava Flow anti-pattern |
10
+ | `prose-style-enforcement.md` | How `CLAUDE_PROSE_STYLE_ENFORCEMENT` arms opinionated prose gates (default off) while AskUserQuestion lean-block stays always on |
11
+ | `advisor-tool.md` | Canonical consult bones for any stronger reviewer: when to call, hard rule before first write, how to treat advice; maps to the Anthropic advisor tool |
12
+ | `team-advisor-skill.md` | `/team-advisor` map: sole-consumer warm bind, ref index, and advisor selection |
13
+ | `weak-executor-advisor.md` | Consult profile a below-advisor-tier executor (Sonnet, Haiku) follows on top of `advisor-tool.md`: spawn-prompt steering, context packaging, two-timing rule, consult budget, failure branches |
14
+
15
+ ## Role
16
+
17
+ A file naming an external concept gives a one-line definition and links a direct source. A file naming an internal tool or skill describes what it does and when to use it. They back the rule text in `rules/` and `packages/claude-dev-env/docs/CODE_RULES.md` without embedding full third-party content inline.
@@ -1,17 +1 @@
1
- # docs/references
2
-
3
- Pointer documents to external sources, standard terminology, and internal tool or skill usage. Files here are loaded on demand by rules that cite them.
4
-
5
- ## Files
6
-
7
- | File | Purpose |
8
- |---|---|
9
- | `dead-code-elimination.md` | External sources and standard terms behind CODE_RULES §9.8 (remove code you orphan): DCE, tree shaking, reachability analysis, and the Lava Flow anti-pattern |
10
- | `prose-style-enforcement.md` | How `CLAUDE_PROSE_STYLE_ENFORCEMENT` arms opinionated prose gates (default off) while AskUserQuestion lean-block stays always on |
11
- | `advisor-tool.md` | Canonical consult bones for any stronger reviewer: when to call, hard rule before first write, how to treat advice; maps to the Anthropic advisor tool |
12
- | `team-advisor-skill.md` | `/team-advisor` map: sole-consumer warm bind, ref index, and advisor selection |
13
- | `weak-executor-advisor.md` | Consult profile a below-advisor-tier executor (Sonnet, Haiku) follows on top of `advisor-tool.md`: spawn-prompt steering, context packaging, two-timing rule, consult budget, failure branches |
14
-
15
- ## Role
16
-
17
- A file naming an external concept gives a one-line definition and links a direct source. A file naming an internal tool or skill describes what it does and when to use it. They back the rule text in `rules/` and `packages/claude-dev-env/docs/CODE_RULES.md` without embedding full third-party content inline.
1
+ @AGENTS.md
@@ -0,0 +1,31 @@
1
+ # hooks
2
+
3
+ Python hook scripts wired into Claude Code's lifecycle via `settings.json`. Each hook answers one or more lifecycle events (`PreToolUse`, `PostToolUse`, `Stop`, `SubagentStop`, `SessionStart`, `SessionEnd`) and either blocks a tool call, annotates it, or performs a side-effect.
4
+
5
+ ## Subdirectories
6
+
7
+ | Directory | Role |
8
+ |---|---|
9
+ | `advisory/` | Hooks that warn but do not block (`permissionDecision: "ask"`) |
10
+ | `blocking/` | Hooks that deny tool calls when a rule is violated |
11
+ | `blocking/config/` | Shared constants for blocking hooks |
12
+ | `diagnostic/` | Hooks that record and extract hook-firing records into Neon |
13
+ | `diagnostic/migrations/` | SQL migrations for the `hook_events` Neon schema |
14
+ | `diagnostic/queries/` | Parameterized SQL queries for inspecting blocked commands |
15
+ | `git-hooks/` | Native git hooks (`pre-commit`, `pre-push`, `post-commit`) installed via the git-hooks path |
16
+ | `git-hooks/git_hooks_constants/` | Shared constants for the git-hook scripts |
17
+ | `hooks_constants/` | Shared constant modules imported by multiple hooks across this tree (includes `pii_prevention_constants.py` for personal-data and secret scan patterns) |
18
+ | `lifecycle/` | Hooks that run at session or config-change boundaries |
19
+ | `observability/` | PostToolUse hooks that record agent behavior for diagnostics |
20
+ | `session/` | SessionStart and SessionEnd hooks for per-session cleanup |
21
+ | `validation/` | PostToolUse hooks that validate code quality after a write (mypy, auto-format) |
22
+ | `validators/` | Library modules used by the validation hooks — checks split by concern |
23
+ | `workflow/` | PostToolUse hooks that trigger doc publishing and companion-file generation |
24
+
25
+ ## Conventions
26
+
27
+ - **Event mapping:** Every hook reads JSON from stdin and exits 0 (allow) or prints a `hookSpecificOutput` block (block/ask). Blocking hooks set `permissionDecision: "block"`.
28
+ - **Constants companion:** Each hook with more than a handful of tunable strings keeps them in a `hooks_constants/<hook_name>_constants.py` sibling. Import from there; do not repeat literals.
29
+ - **Tests:** Each hook has one or more `test_<hookname>*.py` files beside it. Run with `python -m pytest <test_file>`.
30
+ - **Registration:** Hooks are declared in `settings.json` under the right lifecycle event. The installer (`packages/claude-dev-env/bin/install.mjs`) merges the hook entries during `npx claude-dev-env`.
31
+ - **Top-level utilities:** `_gh_pr_author_swap_utils.py` and `rewrite_plugin_paths.py` are shared helpers imported by multiple blocking hooks. `hooks.json` records the canonical hook-to-event mapping for auditing.
package/hooks/CLAUDE.md CHANGED
@@ -1,31 +1 @@
1
- # hooks
2
-
3
- Python hook scripts wired into Claude Code's lifecycle via `settings.json`. Each hook answers one or more lifecycle events (`PreToolUse`, `PostToolUse`, `Stop`, `SubagentStop`, `SessionStart`, `SessionEnd`) and either blocks a tool call, annotates it, or performs a side-effect.
4
-
5
- ## Subdirectories
6
-
7
- | Directory | Role |
8
- |---|---|
9
- | `advisory/` | Hooks that warn but do not block (`permissionDecision: "ask"`) |
10
- | `blocking/` | Hooks that deny tool calls when a rule is violated |
11
- | `blocking/config/` | Shared constants for blocking hooks |
12
- | `diagnostic/` | Hooks that record and extract hook-firing records into Neon |
13
- | `diagnostic/migrations/` | SQL migrations for the `hook_events` Neon schema |
14
- | `diagnostic/queries/` | Parameterized SQL queries for inspecting blocked commands |
15
- | `git-hooks/` | Native git hooks (`pre-commit`, `pre-push`, `post-commit`) installed via the git-hooks path |
16
- | `git-hooks/git_hooks_constants/` | Shared constants for the git-hook scripts |
17
- | `hooks_constants/` | Shared constant modules imported by multiple hooks across this tree (includes `pii_prevention_constants.py` for personal-data and secret scan patterns) |
18
- | `lifecycle/` | Hooks that run at session or config-change boundaries |
19
- | `observability/` | PostToolUse hooks that record agent behavior for diagnostics |
20
- | `session/` | SessionStart and SessionEnd hooks for per-session cleanup |
21
- | `validation/` | PostToolUse hooks that validate code quality after a write (mypy, auto-format) |
22
- | `validators/` | Library modules used by the validation hooks — checks split by concern |
23
- | `workflow/` | PostToolUse hooks that trigger doc publishing and companion-file generation |
24
-
25
- ## Conventions
26
-
27
- - **Event mapping:** Every hook reads JSON from stdin and exits 0 (allow) or prints a `hookSpecificOutput` block (block/ask). Blocking hooks set `permissionDecision: "block"`.
28
- - **Constants companion:** Each hook with more than a handful of tunable strings keeps them in a `hooks_constants/<hook_name>_constants.py` sibling. Import from there; do not repeat literals.
29
- - **Tests:** Each hook has one or more `test_<hookname>*.py` files beside it. Run with `python -m pytest <test_file>`.
30
- - **Registration:** Hooks are declared in `settings.json` under the right lifecycle event. The installer (`packages/claude-dev-env/bin/install.mjs`) merges the hook entries during `npx claude-dev-env`.
31
- - **Top-level utilities:** `_gh_pr_author_swap_utils.py` and `rewrite_plugin_paths.py` are shared helpers imported by multiple blocking hooks. `hooks.json` records the canonical hook-to-event mapping for auditing.
1
+ @AGENTS.md
@@ -155,6 +155,20 @@ def _switch_gh_account(to_account: str) -> bool:
155
155
  return completed_process.returncode == 0
156
156
 
157
157
 
158
+ def _build_restore_failure_message(
159
+ hook_label: str,
160
+ original_account: str,
161
+ state_file: Path,
162
+ state_file_context: str,
163
+ ) -> str:
164
+ """Build the shared diagnostic for a failed account restoration."""
165
+ return (
166
+ f"[{hook_label}] Restore the gh account to {original_account!r}; "
167
+ f"gh account switch requires attention for {state_file_context} {state_file}. "
168
+ "The file remains for the next session."
169
+ )
170
+
171
+
158
172
  def _read_original_account(state_file: Path) -> str | None:
159
173
  """Read the original-account login from a swap-state file.
160
174
 
@@ -0,0 +1,16 @@
1
+ # hooks/advisory
2
+
3
+ Hooks that produce a warning prompt (`permissionDecision: "ask"`) rather than an outright block. The user sees the warning and can continue or cancel.
4
+
5
+ ## Key files
6
+
7
+ | File | Event | What it guards |
8
+ |---|---|---|
9
+ | `migration_safety_advisor.py` | PreToolUse (Write/Edit) | Django migration files containing `RemoveField`, `RenameField`, `DeleteModel`, or `RenameModel` — warns that these operations must be backwards-compatible during deployment |
10
+ | `refactor_guard.py` | PreToolUse (Edit) | Edits that rename or restructure existing code not present in the current git diff — warns that the change may be out of scope |
11
+
12
+ ## Conventions
13
+
14
+ - Both hooks exit 0 (silent) when their trigger condition is not met.
15
+ - `refactor_guard.py` respects a bypass token at `~/.claude/.refactor-bypass-token`; when that file exists the hook stays silent.
16
+ - Tests live beside each hook following the `test_<name>.py` pattern used in `blocking/`. Run with `python -m pytest <test_file>`.
@@ -1,16 +1 @@
1
- # hooks/advisory
2
-
3
- Hooks that produce a warning prompt (`permissionDecision: "ask"`) rather than an outright block. The user sees the warning and can continue or cancel.
4
-
5
- ## Key files
6
-
7
- | File | Event | What it guards |
8
- |---|---|---|
9
- | `migration_safety_advisor.py` | PreToolUse (Write/Edit) | Django migration files containing `RemoveField`, `RenameField`, `DeleteModel`, or `RenameModel` — warns that these operations must be backwards-compatible during deployment |
10
- | `refactor_guard.py` | PreToolUse (Edit) | Edits that rename or restructure existing code not present in the current git diff — warns that the change may be out of scope |
11
-
12
- ## Conventions
13
-
14
- - Both hooks exit 0 (silent) when their trigger condition is not met.
15
- - `refactor_guard.py` respects a bypass token at `~/.claude/.refactor-bypass-token`; when that file exists the hook stays silent.
16
- - Tests live beside each hook following the `test_<name>.py` pattern used in `blocking/`. Run with `python -m pytest <test_file>`.
1
+ @AGENTS.md