claude-dev-env 1.38.1 → 1.40.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 (282) hide show
  1. package/CLAUDE.md +10 -36
  2. package/_shared/pr-loop/audit-reply-template.md +147 -0
  3. package/_shared/pr-loop/fix-protocol.md +25 -4
  4. package/_shared/pr-loop/gh-payloads.md +37 -50
  5. package/_shared/pr-loop/scripts/code_rules_gate.py +0 -60
  6. package/_shared/pr-loop/scripts/config/post_audit_thread_constants.py +199 -0
  7. package/_shared/pr-loop/scripts/config/reviews_disabled_constants.py +8 -0
  8. package/_shared/pr-loop/scripts/post_audit_thread.py +1242 -0
  9. package/_shared/pr-loop/scripts/preflight.py +129 -2
  10. package/_shared/pr-loop/scripts/reviews_disabled.py +59 -0
  11. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -19
  12. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +1116 -0
  13. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +127 -0
  14. package/_shared/pr-loop/scripts/tests/test_preflight.py +41 -0
  15. package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +36 -0
  16. package/_shared/pr-loop/state-schema.md +1 -1
  17. package/agents/clean-coder.md +2 -2
  18. package/agents/pr-description-writer.md +150 -52
  19. package/bin/install.mjs +6 -7
  20. package/bin/install.test.mjs +8 -0
  21. package/commands/doc-gist.md +16 -0
  22. package/commands/plan.md +0 -2
  23. package/commands/review-plan.md +1 -1
  24. package/docs/CODE_RULES.md +122 -2
  25. package/docs/PR_DESCRIPTION_GUIDE.md +127 -64
  26. package/hooks/blocking/bot_mention_comment_blocker.py +75 -0
  27. package/hooks/blocking/code_rules_enforcer.py +1143 -129
  28. package/hooks/blocking/convergence_gate_blocker.py +130 -0
  29. package/hooks/blocking/destructive_command_blocker.py +74 -0
  30. package/hooks/blocking/gh_body_arg_blocker.py +30 -0
  31. package/hooks/blocking/md_to_html_blocker.py +119 -0
  32. package/hooks/blocking/pr_description_enforcer.py +57 -22
  33. package/hooks/blocking/test_bot_mention_comment_blocker.py +131 -0
  34. package/hooks/blocking/test_code_rules_enforcer.py +21 -0
  35. package/hooks/blocking/test_code_rules_enforcer_any_exempt_files.py +70 -0
  36. package/hooks/blocking/test_code_rules_enforcer_any_imports_and_cast.py +92 -0
  37. package/hooks/blocking/test_code_rules_enforcer_banned_import_alias.py +143 -0
  38. package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +152 -0
  39. package/hooks/blocking/test_code_rules_enforcer_bare_except.py +120 -0
  40. package/hooks/blocking/test_code_rules_enforcer_boundary_types.py +175 -0
  41. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
  42. package/hooks/blocking/test_code_rules_enforcer_collection_prefix.py +50 -0
  43. package/hooks/blocking/test_code_rules_enforcer_docstring_format.py +255 -0
  44. package/hooks/blocking/test_code_rules_enforcer_inline_tuple_string_magic.py +130 -0
  45. package/hooks/blocking/test_code_rules_enforcer_stub_implementations.py +141 -0
  46. package/hooks/blocking/test_code_rules_enforcer_test_branching.py +143 -0
  47. package/hooks/blocking/test_code_rules_enforcer_thin_wrapper_files.py +169 -0
  48. package/hooks/blocking/test_code_rules_enforcer_todo_markers.py +99 -0
  49. package/hooks/blocking/test_code_rules_enforcer_typed_dict_pairs.py +141 -0
  50. package/hooks/blocking/test_convergence_gate_blocker.py +63 -0
  51. package/hooks/blocking/test_destructive_command_blocker.py +146 -0
  52. package/hooks/blocking/test_destructive_command_blocker_no_verify.py +102 -0
  53. package/hooks/blocking/test_gh_body_arg_blocker.py +45 -0
  54. package/hooks/blocking/test_md_to_html_blocker.py +317 -0
  55. package/hooks/blocking/test_pr_description_enforcer.py +69 -8
  56. package/hooks/config/any_type_config.py +7 -0
  57. package/hooks/config/banned_identifiers_constants.py +11 -0
  58. package/hooks/config/blocking_check_limits.py +38 -0
  59. package/hooks/config/bot_mention_comment_blocker_constants.py +20 -0
  60. package/hooks/config/code_rules_enforcer_constants.py +53 -0
  61. package/hooks/config/convergence_branch_constants.py +9 -0
  62. package/hooks/config/doc_gist_auto_publish_constants.py +18 -0
  63. package/hooks/config/html_companion_constants.py +20 -0
  64. package/hooks/config/inline_tuple_string_magic_constants.py +22 -0
  65. package/hooks/config/pr_description_enforcer_constants.py +14 -0
  66. package/hooks/config/test_banned_identifiers_constants.py +17 -0
  67. package/hooks/hooks.json +28 -20
  68. package/hooks/pyproject.toml +69 -0
  69. package/hooks/validators/mypy_integration.py +47 -1
  70. package/hooks/validators/run_all_validators.py +3 -3
  71. package/hooks/validators/test_mypy_integration.py +50 -1
  72. package/hooks/workflow/doc_gist_auto_publish.py +144 -0
  73. package/hooks/workflow/md_to_html_companion.py +365 -0
  74. package/hooks/workflow/test_doc_gist_auto_publish.py +117 -0
  75. package/hooks/workflow/test_md_to_html_companion.py +452 -0
  76. package/package.json +1 -1
  77. package/rules/gh-body-file.md +2 -0
  78. package/scripts/Install-SweepEmptyDirs.ps1 +111 -0
  79. package/scripts/check.ps1 +106 -0
  80. package/scripts/config/timing.py +11 -0
  81. package/scripts/sweep_empty_dirs.py +138 -0
  82. package/scripts/sync_to_cursor/rules.py +1 -1
  83. package/scripts/test_sweep_empty_dirs.py +183 -0
  84. package/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +323 -0
  85. package/skills/_shared/pr-loop/scripts/_cli_utils.py +22 -0
  86. package/skills/_shared/pr-loop/scripts/_path_resolver.py +165 -0
  87. package/skills/_shared/pr-loop/scripts/_xml_utils.py +20 -0
  88. package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +182 -0
  89. package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +185 -0
  90. package/skills/_shared/pr-loop/scripts/config/__init__.py +0 -0
  91. package/skills/_shared/pr-loop/scripts/config/path_resolver_constants.py +78 -0
  92. package/skills/_shared/pr-loop/scripts/init_loop_state.py +135 -0
  93. package/skills/_shared/pr-loop/scripts/teardown_worktrees.py +175 -0
  94. package/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +182 -0
  95. package/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +206 -0
  96. package/skills/bugteam/CONSTRAINTS.md +21 -22
  97. package/skills/bugteam/EXAMPLES.md +3 -3
  98. package/skills/bugteam/PROMPTS.md +227 -67
  99. package/skills/bugteam/SKILL.md +132 -455
  100. package/skills/bugteam/reference/README.md +1 -1
  101. package/skills/bugteam/reference/audit-and-teammates.md +112 -39
  102. package/skills/bugteam/reference/audit-contract.md +4 -22
  103. package/skills/bugteam/reference/copilot-gap-analysis.md +8 -5
  104. package/skills/bugteam/reference/design-rationale.md +2 -2
  105. package/skills/bugteam/reference/github-pr-reviews.md +50 -57
  106. package/skills/bugteam/reference/obstacles/audit-assign-ids.md +13 -0
  107. package/skills/bugteam/reference/obstacles/audit-capture-excerpts.md +13 -0
  108. package/skills/bugteam/reference/obstacles/audit-walk-categories.md +13 -0
  109. package/skills/bugteam/reference/obstacles/audit-write-xml.md +13 -0
  110. package/skills/bugteam/reference/obstacles/fix-append-summary.md +13 -0
  111. package/skills/bugteam/reference/obstacles/fix-apply-fixes.md +13 -0
  112. package/skills/bugteam/reference/obstacles/fix-git-add-commit.md +13 -0
  113. package/skills/bugteam/reference/obstacles/fix-git-push.md +13 -0
  114. package/skills/bugteam/reference/obstacles/fix-post-reply.md +13 -0
  115. package/skills/bugteam/reference/obstacles/fix-publish-summary.md +13 -0
  116. package/skills/bugteam/reference/obstacles/fix-py-compile.md +13 -0
  117. package/skills/bugteam/reference/obstacles/fix-read-files.md +13 -0
  118. package/skills/bugteam/reference/obstacles/fix-resolve-thread.md +13 -0
  119. package/skills/bugteam/reference/obstacles/fix-test-suite.md +13 -0
  120. package/skills/bugteam/reference/obstacles/fix-violation-count.md +13 -0
  121. package/skills/bugteam/reference/obstacles/fix-write-xml.md +13 -0
  122. package/skills/bugteam/reference/team-setup.md +111 -9
  123. package/skills/bugteam/reference/teardown-publish-permissions.md +39 -8
  124. package/skills/bugteam/scripts/README.md +60 -0
  125. package/skills/bugteam/scripts/_claude_permissions_common.py +358 -0
  126. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +976 -0
  127. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +375 -0
  128. package/skills/bugteam/scripts/bugteam_preflight.py +328 -0
  129. package/skills/bugteam/scripts/config/bugteam_code_rules_gate_constants.py +25 -0
  130. package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +26 -0
  131. package/skills/bugteam/scripts/config/bugteam_preflight_constants.py +35 -0
  132. package/skills/bugteam/scripts/config/claude_permissions_common_constants.py +20 -0
  133. package/skills/bugteam/scripts/config/probe_code_rules_enforcer_check_constants.py +12 -0
  134. package/skills/bugteam/scripts/config/windows_safe_rmtree_constants.py +7 -0
  135. package/skills/bugteam/scripts/grant_project_claude_permissions.py +175 -0
  136. package/skills/bugteam/scripts/probe_code_rules_enforcer_check.py +107 -0
  137. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +220 -0
  138. package/skills/bugteam/scripts/test__claude_permissions_common.py +112 -0
  139. package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +400 -0
  140. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +384 -0
  141. package/skills/bugteam/scripts/test_bugteam_preflight.py +309 -0
  142. package/skills/bugteam/scripts/test_claude_permissions_common.py +195 -0
  143. package/skills/bugteam/scripts/test_grant_project_claude_permissions.py +55 -0
  144. package/skills/bugteam/scripts/test_probe_code_rules_enforcer_check.py +76 -0
  145. package/skills/bugteam/scripts/test_revoke_project_claude_permissions.py +55 -0
  146. package/skills/bugteam/scripts/test_windows_safe_rmtree.py +108 -0
  147. package/skills/bugteam/scripts/windows_safe_rmtree.py +100 -0
  148. package/skills/bugteam/test_skill_additions.py +1 -11
  149. package/skills/code/SKILL.md +176 -0
  150. package/skills/copilot-review/SKILL.md +16 -0
  151. package/skills/doc-gist/SKILL.md +99 -0
  152. package/skills/doc-gist/references/examples/01-exploration-code-approaches.html +453 -0
  153. package/skills/doc-gist/references/examples/02-exploration-visual-designs.html +515 -0
  154. package/skills/doc-gist/references/examples/03-code-review-pr.html +638 -0
  155. package/skills/doc-gist/references/examples/04-code-understanding.html +491 -0
  156. package/skills/doc-gist/references/examples/05-design-system.html +629 -0
  157. package/skills/doc-gist/references/examples/06-component-variants.html +605 -0
  158. package/skills/doc-gist/references/examples/07-prototype-animation.html +455 -0
  159. package/skills/doc-gist/references/examples/08-prototype-interaction.html +396 -0
  160. package/skills/doc-gist/references/examples/09-slide-deck.html +592 -0
  161. package/skills/doc-gist/references/examples/10-svg-illustrations.html +492 -0
  162. package/skills/doc-gist/references/examples/11-status-report.html +528 -0
  163. package/skills/doc-gist/references/examples/12-incident-report.html +596 -0
  164. package/skills/doc-gist/references/examples/13-flowchart-diagram.html +395 -0
  165. package/skills/doc-gist/references/examples/14-research-feature-explainer.html +381 -0
  166. package/skills/doc-gist/references/examples/15-research-concept-explainer.html +368 -0
  167. package/skills/doc-gist/references/examples/16-implementation-plan.html +702 -0
  168. package/skills/doc-gist/references/examples/17-pr-writeup.html +595 -0
  169. package/skills/doc-gist/references/examples/18-editor-triage-board.html +573 -0
  170. package/skills/doc-gist/references/examples/19-editor-feature-flags.html +663 -0
  171. package/skills/doc-gist/references/examples/20-editor-prompt-tuner.html +722 -0
  172. package/skills/doc-gist/references/examples/README.md +5 -0
  173. package/skills/doc-gist/scripts/config/__init__.py +0 -0
  174. package/skills/doc-gist/scripts/config/gist_upload_constants.py +16 -0
  175. package/skills/doc-gist/scripts/gist_upload.py +177 -0
  176. package/skills/doc-gist/scripts/test_gist_upload.py +51 -0
  177. package/skills/findbugs/SKILL.md +96 -2
  178. package/skills/monitor-open-prs/SKILL.md +14 -32
  179. package/skills/monitor-open-prs/test_skill_contract.py +0 -11
  180. package/skills/pr-consistency-audit/SKILL.md +112 -0
  181. package/skills/pr-consistency-audit/reference/detection-rules.md +96 -0
  182. package/skills/pr-consistency-audit/reference/illustrations.md +78 -0
  183. package/skills/pr-converge/SKILL.md +229 -23
  184. package/skills/pr-converge/config/__init__.py +0 -0
  185. package/skills/pr-converge/config/constants.py +63 -0
  186. package/skills/pr-converge/reference/convergence-gates.md +138 -44
  187. package/skills/pr-converge/reference/examples.md +43 -11
  188. package/skills/pr-converge/reference/fix-protocol.md +6 -5
  189. package/skills/pr-converge/reference/ground-rules.md +5 -3
  190. package/skills/pr-converge/reference/multi-pr-orchestration.md +44 -19
  191. package/skills/pr-converge/reference/obstacles/fix-post-replies.md +13 -0
  192. package/skills/pr-converge/reference/obstacles/fix-publish-summary.md +13 -0
  193. package/skills/pr-converge/reference/obstacles/fix-push.md +13 -0
  194. package/skills/pr-converge/reference/obstacles/fix-read-filelines.md +13 -0
  195. package/skills/pr-converge/reference/obstacles/fix-reset-state.md +13 -0
  196. package/skills/pr-converge/reference/obstacles/fix-resolve-threads.md +13 -0
  197. package/skills/pr-converge/reference/obstacles/fix-spawn-clean-coder.md +13 -0
  198. package/skills/pr-converge/reference/obstacles/fix-stage-commit.md +13 -0
  199. package/skills/pr-converge/reference/obstacles/fix-trigger-bugbot.md +13 -0
  200. package/skills/pr-converge/reference/obstacles/fix-write-test.md +13 -0
  201. package/skills/pr-converge/reference/per-tick.md +107 -31
  202. package/skills/pr-converge/reference/state-schema.md +22 -1
  203. package/skills/pr-converge/reference/stop-conditions.md +9 -7
  204. package/skills/pr-converge/scripts/README.md +34 -46
  205. package/skills/pr-converge/scripts/check_bugbot_ci.py +279 -0
  206. package/skills/pr-converge/scripts/check_convergence.py +497 -0
  207. package/skills/pr-converge/scripts/check_pending_reviews.py +154 -0
  208. package/skills/pr-converge/scripts/config/pr_converge_constants.py +118 -0
  209. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +134 -0
  210. package/skills/pr-converge/scripts/post_fix_reply.py +168 -0
  211. package/skills/pr-converge/scripts/test_check_bugbot_ci.py +312 -0
  212. package/skills/pr-converge/workflows/schedule-wakeup-loop.md +5 -12
  213. package/skills/qbug/SKILL.md +157 -27
  214. package/skills/session-log/SKILL.md +216 -114
  215. package/skills/session-tidy/SKILL.md +1 -1
  216. package/skills/skill-builder/SKILL.md +138 -56
  217. package/skills/skill-builder/references/delegation-map.md +72 -113
  218. package/skills/skill-builder/references/progressive-disclosure.md +122 -0
  219. package/skills/skill-builder/references/self-audit-checklist.md +92 -0
  220. package/skills/skill-builder/references/skill-types.md +228 -0
  221. package/skills/skill-builder/references/thariq-x-post-skills.json +33 -0
  222. package/skills/skill-builder/templates/gap-analysis.md +15 -8
  223. package/skills/skill-builder/workflows/improve-skill.md +86 -57
  224. package/skills/skill-builder/workflows/new-skill.md +80 -168
  225. package/skills/skill-builder/workflows/polish-skill.md +78 -54
  226. package/skills/structure-prompt/SKILL.md +50 -0
  227. package/skills/structure-prompt/reference/adversarial-tuning.md +62 -0
  228. package/skills/structure-prompt/reference/block-classification.md +27 -0
  229. package/skills/structure-prompt/reference/canonical-case.md +48 -0
  230. package/skills/structure-prompt/reference/citation-depth.md +70 -0
  231. package/skills/structure-prompt/reference/cleanup.md +33 -0
  232. package/skills/structure-prompt/reference/constraints.md +33 -0
  233. package/skills/structure-prompt/reference/directives.md +37 -0
  234. package/skills/structure-prompt/reference/examples.md +72 -0
  235. package/skills/structure-prompt/reference/instantiation.md +51 -0
  236. package/skills/structure-prompt/reference/output-contract.md +72 -0
  237. package/skills/structure-prompt/reference/per-category.md +23 -0
  238. package/skills/structure-prompt/reference/persona.md +38 -0
  239. package/skills/structure-prompt/reference/research.md +33 -0
  240. package/skills/structure-prompt/reference/structure.md +28 -0
  241. package/agents/code-standards-agent.md +0 -93
  242. package/agents/groq-coder.md +0 -113
  243. package/agents/plan-executor.md +0 -226
  244. package/agents/project-docs-analyzer.md +0 -53
  245. package/agents/project-structure-organizer-agent.md +0 -72
  246. package/agents/skill-to-agent-converter.md +0 -370
  247. package/agents/skill-writer-agent.md +0 -470
  248. package/agents/user-docs-writer.md +0 -67
  249. package/agents/workflow-visual-documenter.md +0 -82
  250. package/commands/readability-review.md +0 -20
  251. package/hooks/mypy.ini +0 -2
  252. package/hooks/notification/attention_needed_notify.py +0 -71
  253. package/hooks/notification/claude_notification_handler.py +0 -67
  254. package/hooks/notification/notification_utils.py +0 -267
  255. package/hooks/notification/subagent_complete_notify.py +0 -381
  256. package/hooks/notification/test_attention_needed_notify.py +0 -47
  257. package/hooks/notification/test_claude_notification_handler.py +0 -54
  258. package/hooks/notification/test_notification_utils.py +0 -91
  259. package/hooks/notification/test_subagent_complete_notify.py +0 -79
  260. package/scripts/config/groq_bugteam_config.py +0 -230
  261. package/scripts/config/test_groq_bugteam_config.py +0 -83
  262. package/scripts/config/test_spec_implementer_prompt.py +0 -32
  263. package/scripts/groq_bugteam.README.md +0 -131
  264. package/scripts/groq_bugteam.py +0 -647
  265. package/scripts/groq_bugteam_dotenv.py +0 -40
  266. package/scripts/groq_bugteam_spec.py +0 -226
  267. package/scripts/test_groq_bugteam.py +0 -529
  268. package/scripts/test_groq_bugteam_apply_fix_from_spec.py +0 -426
  269. package/scripts/test_groq_bugteam_dotenv.py +0 -66
  270. package/scripts/test_groq_bugteam_spec.py +0 -338
  271. package/skills/bugteam/SKILL_EVALS.md +0 -309
  272. package/skills/dream/SKILL.md +0 -118
  273. package/skills/ingest/SKILL.md +0 -40
  274. package/skills/npm-creator/SKILL.md +0 -187
  275. package/skills/readability-review/SKILL.md +0 -127
  276. package/skills/resume-review/SKILL.md +0 -261
  277. package/skills/rule-audit/SKILL.md +0 -307
  278. package/skills/rule-creator/SKILL.md +0 -150
  279. package/skills/searching-obsidian-vault/SKILL.md +0 -131
  280. package/skills/skill-writer/REFERENCE.md +0 -284
  281. package/skills/skill-writer/SKILL.md +0 -222
  282. package/skills/tdd-team/SKILL.md +0 -128
@@ -52,6 +52,16 @@ These rules are automatically enforced by `code_rules_enforcer.py`. Violations b
52
52
  | Hardcoded user paths | No string literals naming a specific user's home directory in production code (`C:/Users/jon/...`, `/Users/alice/...`, `/home/bob/...`). Use `pathlib.Path.home()` or `os.path.expanduser('~')`. **Exempt:** test files, `config/` files, workflow registry paths (`/workflow/`, `_tab.py`, `/states.py`, `/modules.py`), Django migrations (`/migrations/`), and hook infrastructure (the enforcer embeds path patterns and must not self-block). |
53
53
  | sys.path.insert dedup | `sys.path.insert(0, X)` must be guarded by `if X not in sys.path:` (or equivalent membership test) so reloads do not push the same entry repeatedly. **Test files exempt.** |
54
54
  | Unused module-level imports | Module-level imports never referenced in the file body are flagged. Skipped for files declaring `__all__` (re-exports), files using `TYPE_CHECKING` (annotation-only imports), and lines marked `# noqa`. **Test files exempt.** |
55
+ | Banned identifiers | Single-letter or 2–4 char abbreviations in production code: `ctx`, `cfg`, `msg`, `btn`, `idx`, `cnt`, `tmp`, `elem`, `val`. Test files exempt; loop counters `i`/`j`/`k` and exception `e` exempt. See §5 for the full list and rationale. |
56
+ | Banned function prefixes | Function names starting with `handle_`, `process_`, `manage_`, or `do_` are flagged — these prefixes describe nothing about behavior. Name functions after the noun they produce or the verb they perform (`fetch_user`, `validate_payload`). **Test files exempt.** |
57
+ | Type escape hatches | `from typing import Any`, `cast()`, and inline `Any` in production are flagged outside boundary files (`__init__.py`, `protocols.py`, `types.py`, `conftest.py`). Name the concrete shape instead. |
58
+ | Bare except | `except:` and `except BaseException:` swallow KeyboardInterrupt/SystemExit; `except Exception:` hides bugs by catching nearly every error class. Name the specific exception(s) you intend to catch (tuple form `except (ValueError, KeyError):` is fine). **Test files and hook infrastructure exempt.** |
59
+ | Boundary types — Any in signatures | `Any` appearing directly or nested inside a generic in a function signature (parameters, return type) or class attribute annotation is flagged. Local variable annotations are exempt. Files named `protocols.py` or `types.py` are interface-declaration surfaces and exempt. |
60
+ | Stub implementations | Functions whose body is `pass`, `...`, or `raise NotImplementedError` in production code are flagged unless declared abstract (`@abstractmethod`) or part of a `Protocol`. Implement the function or remove it. |
61
+ | TypedDict encode/decode pairs | Every `class FooPayload(TypedDict):` in production code must have companion `_encode_foo_payload(...)` and `_decode_foo_payload(...)` functions in the same module — boundary serialization should not leak to callers. |
62
+ | Test-mode branching in production | Reading `TESTING`, `PYTEST_CURRENT_TEST`, `IS_TEST`, etc. from production code creates two parallel implementations. Use dependency injection so production stays single-path. **Test files and hook infrastructure exempt.** |
63
+ | Thin wrapper files | A non-`__init__.py` module whose body is only imports (optionally with an `__all__` assignment) is a re-export indirection with no payload. Callers should import from the real module. `__init__.py` is the canonical re-export surface and is exempt. |
64
+ | Docstring format (Google-style) | Public functions/methods (no leading underscore, not dunder, body > 3 lines, not `@property`/`@abstractmethod`) require Google-style `Args:` / `Returns:` (or `Yields:`) / `Raises:` sections matching the signature. **Test files exempt.** |
55
65
 
56
66
  ### Where UPPER_SNAKE is allowed
57
67
 
@@ -112,7 +122,7 @@ Full words only. No mental translation.
112
122
 
113
123
  **Exception:** `i`, `j`, `k` in loops; `e` for exception.
114
124
 
115
- **Extended naming rules** (from readability-review rubric):
125
+ **Extended naming rules** :
116
126
  - Loop vars: `each_order`, `each_user` (prefix `each_`)
117
127
  - Booleans: `is_valid`, `has_permission`, `should_retry` (prefix `is_`/`has_`/`should_`/`can_`)
118
128
  - Collections: `all_orders`, `all_users` (prefix `all_`)
@@ -227,6 +237,57 @@ Parent knows: nothing about child's internals
227
237
 
228
238
  ---
229
239
 
240
+ ## 9.5 NO THIN WRAPPER MODULES
241
+
242
+ A non-`__init__.py` module whose body is only imports (optionally with an `__all__` assignment) is a thin wrapper. Callers should import from the real module. The wrapper adds no payload — only an indirection layer that obscures where things live.
243
+
244
+ `__init__.py` is the canonical re-export surface and is exempt; package surface aggregation is its job.
245
+
246
+ ---
247
+
248
+ ## 9.6 NO BACKWARDS-COMPATIBILITY SHIMS
249
+
250
+ Removed code is removed. Do not leave behind:
251
+
252
+ - Renamed functions that re-export the old name with a `# deprecated` comment
253
+ - `_old_*` aliases pointing to the new implementation
254
+ - Wrapper modules whose only purpose is to keep an old import path alive
255
+ - `// removed in vX` comment markers next to deleted blocks
256
+
257
+ When a symbol moves or its signature changes, update the call sites in the same commit. The git log records what changed; the codebase records what exists now.
258
+
259
+ > **See also:** [`skills/code/SKILL.md`](../skills/code/SKILL.md) §5 ("No fallback paths, no back-compat shims, no legacy code") states the same principle as a session-start prompt directive. This section is the authoritative wording; the skill amplifies it for `/code` sessions.
260
+
261
+ ---
262
+
263
+ ## 9.7 NO FALLBACK / BEST-EFFORT WRAPPERS
264
+
265
+ Do not wrap a call in `try/except` that swallows the failure and returns a default unless the caller has explicitly opted in to that behavior at the boundary.
266
+
267
+ ```python
268
+ # BAD — silently hides every failure mode
269
+ def fetch_user(user_id: int) -> User | None:
270
+ try:
271
+ return _registry[user_id]
272
+ except Exception:
273
+ return None
274
+ ```
275
+
276
+ ```python
277
+ # GOOD — names the specific failure and propagates the rest
278
+ def fetch_user(user_id: int) -> User | None:
279
+ try:
280
+ return _registry[user_id]
281
+ except KeyError:
282
+ return None
283
+ ```
284
+
285
+ Fallback values mask programming errors (KeyError vs RuntimeError vs AttributeError all collapse to "None"), making debugging impossible. Production code names the specific failure mode it intends to handle.
286
+
287
+ > **See also:** [`skills/code/SKILL.md`](../skills/code/SKILL.md) §2 ("No `try`/`except` in core logic that recovers, softens, or best-efforts a failure") states the same principle as a session-start prompt directive. The hook check `check_bare_except` enforces the narrowest case (bare/`Exception`/`BaseException` handlers); this section + the `code` skill cover the broader "any swallowing handler" intent.
288
+
289
+ ---
290
+
230
291
  ## 10. NO REDUNDANT DATA FETCHES
231
292
 
232
293
  If you already have data, don't fetch again.
@@ -243,6 +304,53 @@ const profile = await db.profile.first();
243
304
 
244
305
  ---
245
306
 
307
+ ## 11. ENFORCEMENT SURFACES
308
+
309
+ Rules in this document are enforced through three distinct surfaces, each with different latency and reach. Knowing which surface owns a rule tells you what happens when you violate it.
310
+
311
+ | Surface | What it catches | When it runs | Failure mode |
312
+ |---------|-----------------|--------------|--------------|
313
+ | **⚡ Hook** | Pattern-matchable violations: comments, magic values, banned identifiers, bare except, boundary `Any`, thin wrappers, docstring shape, etc. | PreToolUse on Write/Edit (and PostToolUse advisories) | Blocks the write — Claude must fix and retry |
314
+ | **🤖 Prompt** | Judgment-driven principles: SRP, Right-Sized Engineering, KISS, conservative-action, BDD discovery; strict-mode standards via the `/code` skill (no `Any`/`cast`, immutable TypedDicts, DI hooks, 100% branch coverage, no fallback `try/except`) | Read into the model context at session start (CLAUDE.md, rules/*.md, skill prepends) | Influences Claude's decisions; no automatic block |
315
+ | **👥 Audit rubric** | Cross-file architectural concerns: SOLID misapplication, abstraction leaks, multi-file coupling | Run on demand via `/check`, `/readability-review`, agent-driven audits | Surfaces in audit output; humans decide whether to act |
316
+
317
+ ### Prompt-enforced rules (no hook coverage)
318
+
319
+ These principles cannot be reduced to a regex or AST visitor. They live in user-private `~/.claude/rules/` and `~/.claude/CLAUDE.md`, and are read into context every session:
320
+
321
+ - **Right-Sized Engineering** — concrete classes for single concretions, functions when no state, no DI frameworks for solo-scale code
322
+ - **SRP / SOLID misapplication signals** — see §7.5
323
+ - **conservative-action** — when intent is ambiguous, ask before acting
324
+ - **explore-thoroughly** — investigate before committing to an approach
325
+ - **agent-spawn-protocol** — verify context before delegating to a subagent
326
+ - **`code` skill ([`skills/code/SKILL.md`](../skills/code/SKILL.md))** — invoked via `/code`, prepends strict-mode standards for the entire session: no `Any`/`cast()`/`# type: ignore`, immutable TypedDicts with manual `_encode_*`/`_decode_*` and `require_*` validation, per-module `_test_hooks.py` for DI, 100% statement + branch coverage, zero mocks. Several criteria overlap with §9.6/§9.7 and the ⚡ B-series checks; the skill is the canonical entry point when the user explicitly opts into strict mode for an implementation task.
327
+
328
+ ### Audit-rubric reference
329
+
330
+ For multi-file architectural reviews see [`packages/claude-dev-env/audit-rubrics/`](../audit-rubrics/). Categories A–F, I, K stay as agent rubrics rather than ⚡ blocking rules because they require multi-file reasoning that single-file hooks cannot perform.
331
+
332
+ ---
333
+
334
+ ## 12. DEFERRED RULES (P1 — TRACKED, NOT ENFORCED)
335
+
336
+ The following rules are documented in `~/.claude/rules/` and applied by Claude through prompt context, but have no hook coverage in `code_rules_enforcer.py`. Promotion to ⚡ blocking enforcement is on the backlog and will land as separate hardening commits.
337
+
338
+ | Rule | Source | Promotion path |
339
+ |------|--------|----------------|
340
+ | Context7 before web search for library/SDK questions | [`rules/context7.md`](../rules/context7.md) | New PreToolUse hook on WebSearch when query mentions a library name |
341
+ | Verify-before-asking checklist | [`rules/verify-before-asking.md`](../rules/verify-before-asking.md) | Stop hook scanning AskUserQuestion calls for "where is", "what file", etc. |
342
+ | BDD naming (`should_…` / `describe…it…`) | [`rules/bdd.md`](../rules/bdd.md) | `code_rules_enforcer.py::check_test_naming` (new) — flag test functions starting with `test_` lacking corresponding `should_…` form |
343
+ | `gh` pagination requires `--paginate --slurp` plus external `jq` | [`rules/gh-paginate.md`](../rules/gh-paginate.md) | PreToolUse Bash hook matching `gh api .*/(reviews\|comments)` without `--paginate --slurp` |
344
+ | Self-contained documentation (no "as discussed", "Option A", session refs) | [`rules/self-contained-docs.md`](../rules/self-contained-docs.md) | PostToolUse Write hook scanning new `.md` content for conversational refs |
345
+ | Temp file cleanup at end of task | [`rules/cleanup-temp-files.md`](../rules/cleanup-temp-files.md) | Stop hook scanning for files Claude created and did not delete |
346
+ | No credentials committed to git | [`rules/git-workflow.md`](../rules/git-workflow.md) | PreToolUse Bash hook on `git commit` scanning staged diff for high-entropy strings, `.env` patterns, and known credential file extensions |
347
+ | Per-module DI hooks (`_test_hooks.py` sibling) instead of `if TESTING:` branching | [`skills/code/SKILL.md`](../skills/code/SKILL.md) §4 | New `check_test_hooks_sibling()` — when a module imports a side-effect dependency and a test exists, require a `<module>_test_hooks.py` sibling exposing the injection points |
348
+ | 100% statement + branch coverage with zero mocks | [`skills/code/SKILL.md`](../skills/code/SKILL.md) §3 | CI check (not write-time) — `pytest --cov-branch --cov-fail-under=100` on touched packages; covers only what the diff added |
349
+ | `TypedDict` `_decode_*` calls `require_*` on every field | [`skills/code/SKILL.md`](../skills/code/SKILL.md) §6 | Extend `check_typed_dict_encode_decode` (B2) — when the decoder is present, AST-verify every TypedDict field appears as a `require_*` call before the return |
350
+ | `Protocol` signatures match the real implementation exactly | [`skills/code/SKILL.md`](../skills/code/SKILL.md) Gotchas | mypy `--strict` over the protocol module catches this; promotion = add `[tool.mypy] strict = true` per-module override in `pyproject.toml` for files declaring `Protocol` subclasses |
351
+
352
+ ---
353
+
246
354
  ## QUICK CHECKLIST
247
355
 
248
356
  ```
@@ -257,6 +365,16 @@ Hook will enforce:
257
365
  [⚡] Logging format args
258
366
  [ ] File length reasonable (advisory at 400, strong nudge at 1000 — see §6.5)
259
367
  [⚡] Constants in config/
368
+ [⚡] No banned identifiers (ctx, cfg, msg, btn, idx, cnt, tmp, elem, val)
369
+ [⚡] No banned function prefixes (handle_, process_, manage_, do_)
370
+ [⚡] No type escape hatches (Any imports, cast(), inline Any)
371
+ [⚡] No bare except / except Exception / except BaseException
372
+ [⚡] No Any in function signatures or class attributes (boundary types)
373
+ [⚡] No stub bodies (pass / ... / raise NotImplementedError) outside abstract methods
374
+ [⚡] TypedDict has companion _encode_*/_decode_* in same module
375
+ [⚡] No test-mode branching in production (TESTING / PYTEST_CURRENT_TEST)
376
+ [⚡] No thin wrapper modules (imports only, optionally with __all__, outside __init__.py)
377
+ [⚡] Public functions have Google-style Args:/Returns:/Raises: when warranted
260
378
 
261
379
  Manual check:
262
380
  [ ] No abbreviations?
@@ -264,5 +382,7 @@ Manual check:
264
382
  [ ] Self-contained components?
265
383
  [ ] SRP holds (one reason to change per function/class/module)?
266
384
  [ ] OCP/LSP/ISP/DIP only applied where abstractions already earn their keep (see §7.5)?
267
- [ ] Readability: /check or /readability-review
385
+ [ ] No backwards-compatibility shims (§9.6)?
386
+ [ ] No fallback/best-effort wrappers (§9.7)?
387
+ [ ] Readability: /check
268
388
  ```
@@ -1,95 +1,158 @@
1
- # GitHub PR Summary Writing Guide for AI
1
+ # PR Description Guide
2
2
 
3
- Use this guide when writing pull request descriptions. Follow these best practices to create clear, professional, and reviewable PR summaries.
3
+ This guide describes the PR-body shape that the `pr-description-writer` agent produces and that the `pr_description_enforcer` PreToolUse hook validates against. The style mirrors merged pull requests in `anthropics/claude-code`, `anthropics/claude-code-action`, and `anthropics/claude-cli-internal`.
4
4
 
5
- ## Required Sections
5
+ ## Three shapes, picked from the diff
6
6
 
7
- ### What (Changes)
7
+ Pick the shape from the size and risk of the change, not from a template.
8
8
 
9
- - Concise statement of what was changed
10
- - What files or systems were modified
11
- - What functionality was added, removed, or improved
12
- - Keep to 2-3 sentences maximum
9
+ | Signal | Shape |
10
+ |---|---|
11
+ | 1-3 files, mechanical change (pin bump, link fix, typo, single-line config), no behavior change | **Trivial** -- one declarative sentence, no headers |
12
+ | Behavior change, bug fix, small feature; under ~15 files | **Standard** -- intro paragraph + `## Changes` + `## Test plan` (or `## Validation`) |
13
+ | New subsystem, refactor across many files, schema or contract change, anything with a caveat | **Heavy** -- intro + `## Problem` + `## Fix` (or `## Changes`) + `## Verification` + extra sections as needed |
13
14
 
14
- ### Why (Problem/Context)
15
+ Prefer the smaller shape when in doubt.
15
16
 
16
- - Explain the problem this PR solves
17
- - Provide business or technical context
18
- - Reference related issue numbers using `#123` or `Fixes #123`, `Closes #456`
19
- - If no issue exists, briefly explain the motivation
17
+ ## Shape 1: Trivial
20
18
 
21
- ### How (Approach/Solution)
19
+ One sentence. No Markdown headers. Optional `Fixes #N` line.
22
20
 
23
- - Describe your implementation approach
24
- - Explain any design decisions or trade-offs
25
- - Include architectural changes if applicable
26
- - Note any breaking changes prominently
21
+ ```
22
+ Pin third-party GitHub Actions references to immutable commit SHAs.
23
+ ```
27
24
 
28
- ## Supporting Details
25
+ ```
26
+ Bump pinned Bun from 1.3.6 to 1.3.14.
29
27
 
30
- ### Testing and Quality
28
+ Fixes #1311.
29
+ ```
31
30
 
32
- - What tests were added/modified
33
- - How to manually verify the changes (if applicable)
34
- - Any areas of concern or limitations
35
- - Performance impact (if relevant)
31
+ ## Shape 2: Standard
36
32
 
37
- ### Dependencies and Risk
33
+ ```
34
+ <One short intro paragraph stating the change and why it matters.
35
+ Reference the failure mode or user-visible symptom when there is one.>
38
36
 
39
- - New dependencies introduced (if any)
40
- - Backward compatibility status
41
- - Potential side effects
42
- - Migration steps (if needed)
37
+ Fixes #<n>.
43
38
 
44
- ## Optional but Valuable
39
+ ## Changes
45
40
 
46
- ### Related Issues/PRs
41
+ - `path/to/file.ext`: short clause describing the change
42
+ - `path/to/other.ext`: short clause
43
+ - `tests/foo.test.ts`: 2 new cases for X
47
44
 
48
- - Link to dependent PRs or issues
49
- - Note any follow-up work needed
45
+ ## Test plan
50
46
 
51
- ### Screenshots/Examples (for UI changes)
47
+ - `bun test test/foo.test.ts`
48
+ - `bun run typecheck`
49
+ - Manual: reproduce on a branch named `feature/a,b`; confirm no rejection
50
+ ```
52
51
 
53
- - Before/after comparisons when visual changes are involved
52
+ The intro paragraph carries the Why -- no `## Why` header needed when one paragraph is enough.
54
53
 
55
- ### Reviewer Guidance
54
+ ## Shape 3: Heavy
56
55
 
57
- - Specific areas to focus on
58
- - Questions for reviewers
59
- - Deployment considerations
56
+ ```
57
+ <Two- to four-sentence intro: scope, motivation, user-visible effect.
58
+ Link to the prior PR or issue that motivates this one if applicable.>
60
59
 
61
- ## Tone and Style Guidelines
60
+ Fixes #<n>.
62
61
 
63
- - Be clear and concise -- reviewers scan quickly
64
- - Use second person sparingly -- focus on what the code does, not what the reviewer should do
65
- - Avoid jargon -- explain technical terms if non-obvious
66
- - Use markdown formatting -- bullets, code blocks, headers for readability
67
- - Be honest about limitations -- acknowledge trade-offs and known issues
68
- - Assume reviewers are unfamiliar -- provide sufficient context
62
+ ## Problem
69
63
 
70
- ## What to Avoid
64
+ <Concrete description of the failure mode or gap. Include the actual
65
+ error text, a reproduction, or the symptomatic log line in a fenced
66
+ code block when it helps.>
71
67
 
72
- - Vague statements like "fix bug" or "update code"
73
- - AI-generated summaries without human verification
74
- - Large walls of text -- break into sections
75
- - Repeating information from commit messages
76
- - References to temporary branch names or internal jargon without context
68
+ ```
69
+ <example error or reproduction>
70
+ ```
71
+
72
+ ## Fix
77
73
 
78
- ## Example Structure
74
+ <What the change does at the level a reviewer needs to evaluate it.
75
+ Reference the file or function by path/name. Don't restate the diff
76
+ line-by-line -- the reviewer can read the code.>
79
77
 
80
- ```markdown
81
- ## Description
82
- Brief 1-2 sentence overview of the change.
78
+ - `src/path/file.ts`: brief description
79
+ - `src/path/other.ts`: brief description
83
80
 
84
- ## Why
85
- Problem/context and reference to related issue (#123).
81
+ ## Verification
86
82
 
87
- ## How
88
- Implementation approach and design decisions.
83
+ - Command 1
84
+ - Command 2 (with output count when useful: "666 pass, 0 fail")
85
+ - Manual scenarios walked through
89
86
 
90
- ## Testing
91
- How this was tested and verified.
87
+ ## Caveat
92
88
 
93
- ## Risk Assessment
94
- Any breaking changes, dependencies, or concerns.
89
+ <Anything a reviewer or downstream user needs to know that isn't in
90
+ the diff. Omit this section when there is no caveat.>
95
91
  ```
92
+
93
+ Optional heavy-shape sections, used when they earn their place:
94
+
95
+ - `## Runtime behavior` -- when the change preserves behavior but moves it.
96
+ - `## Components` -- a small table when the PR introduces multiple named artifacts.
97
+ - `## Backward compatibility` -- when an older consumer might still hit this code path.
98
+ - `## Context` -- background a reviewer outside the area would need.
99
+
100
+ ## Section vocabulary
101
+
102
+ Pick from these. Don't invent new ones, and don't use synonyms within one PR:
103
+
104
+ | Intent | Header (pick one) |
105
+ |---|---|
106
+ | What this PR is and why | `## Summary` -- or no header (preferred when 1-3 sentences) |
107
+ | The failure being fixed | `## Problem` -- or no header when the intro paragraph carries it |
108
+ | The change itself | `## Changes` or `## Fix` |
109
+ | How it was verified | `## Test plan`, `## Validation`, `## Verification`, or `## Testing` |
110
+ | Things to know | `## Caveat`, `## Runtime behavior`, `## Backward compatibility`, `## Context` |
111
+
112
+ ## File reference style
113
+
114
+ - Always backtick file paths: `` `src/github/operations/branch.ts` ``.
115
+ - Use the full path from repo root, not just the basename, unless the basename is unambiguous within the PR.
116
+ - Bullet lists describing per-file changes lead with the backticked path and a colon:
117
+ - `` `src/foo.ts`: whitelists `,` in branch names ``
118
+ - `` `test/foo.test.ts`: 3 new cases for comma-bearing branches ``
119
+ - Prose calling out a single primary file bolds the backticked filename plus a colon: `` **`branch.ts`**: ... ``.
120
+
121
+ ## Cross-references
122
+
123
+ - Issue/PR shorthand: `#1311` (same repo), `anthropics/claude-code#40576` (cross-repo).
124
+ - `Fixes #N` and `Closes #N` close the linked issue on merge -- use them deliberately.
125
+ - `Linear: CC-1723` -- one line, no Markdown, after the intro paragraph or at the bottom.
126
+ - "Same change as <repo>#<n>" / "Follow-up to #<n>" -- one-liners that orient a reviewer.
127
+
128
+ ## Markers and footers
129
+
130
+ - `<!-- NO CHANGELOG -->` on its own line, at the very end, for docs-only or CI-only PRs in repos that auto-generate changelogs from PR titles.
131
+ - Don't add a "Generated with Claude Code" footer -- merged Anthropic PRs don't use one consistently, and the repo's commit trailer covers attribution.
132
+
133
+ ## What the hook checks
134
+
135
+ `pr_description_enforcer.py` runs on `gh pr create` and `gh pr edit` invocations that include a body. It blocks when any of the following are true:
136
+
137
+ - The body, after stripping Markdown ceremony (headers, code fences, bullet markers, bold/emphasis, link text), contains fewer than 40 characters of prose. A skeleton of `## Summary` + `## Changes` + bullets with no Why paragraph fails here.
138
+ - The body contains vague phrases like `fix bug`, `update code`, `minor changes`, or `various fixes`.
139
+
140
+ The hook does not require any specific section headers -- `## Summary`, `## Problem`, `## Fix`, `## Changes`, `## Test plan` are all optional, including any combination of them. A single substantive sentence ("Pin third-party GitHub Actions references to immutable commit SHAs.") satisfies the check.
141
+
142
+ When the hook blocks, it points the caller at the `pr-description-writer` agent and at this guide.
143
+
144
+ ## Tone
145
+
146
+ - Plain language. "The pull engine would blindly overwrite any record marked as 'synced'" -- not "PullEngine.run() exhibited non-idempotent behavior".
147
+ - Active voice. "Add `,` to the whitelist" -- not "`,` was added to the whitelist".
148
+ - No filler. Start with the content, not "This PR..." or "In this change...".
149
+ - No restating the diff. Trust the reviewer to read the code; explain the parts they can't infer.
150
+ - No hedging ("should", "might", "I think") unless the uncertainty is real -- in which case say "not yet verified" and call it out.
151
+
152
+ ## What to avoid
153
+
154
+ - Code snippets that simply repeat the diff. Code blocks are for error reproductions, failing commands, or before/after when the contrast is the point.
155
+ - Technical jargon for non-obvious internals ("Dexie transaction" -> "database transaction").
156
+ - Multi-line preamble. The PR title already says what the change is.
157
+ - Section headers over empty content. If `## Caveat` would be empty, drop the header.
158
+ - Second-person commentary directed at the reviewer ("please review carefully"). The reviewer knows their job.
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env python3
2
+ """PreToolUse hook: block @cursor or @copilot mentions in add_issue_comment body.
3
+
4
+ Bugbot trigger requires exactly ``bugbot run`` with no other text. Copilot review
5
+ requires the GitHub REST API endpoint, not an issue comment mention. This hook
6
+ catches both mistakes and returns the correct procedure for each.
7
+ """
8
+
9
+ import json
10
+ import sys
11
+ from pathlib import Path
12
+
13
+
14
+ def _insert_hooks_tree_for_imports() -> None:
15
+ hooks_tree = Path(__file__).resolve().parent.parent
16
+ hooks_tree_string = str(hooks_tree)
17
+ if hooks_tree_string not in sys.path:
18
+ sys.path.insert(0, hooks_tree_string)
19
+
20
+
21
+ _insert_hooks_tree_for_imports()
22
+
23
+ from config.bot_mention_comment_blocker_constants import (
24
+ COPILOT_MENTION_TOKEN,
25
+ CORRECTIVE_MESSAGE_COPILOT,
26
+ CORRECTIVE_MESSAGE_CURSOR,
27
+ CURSOR_MENTION_TOKEN,
28
+ TOOL_NAME,
29
+ )
30
+
31
+
32
+ def _body_contains_token(body: str, token: str) -> bool:
33
+ return token.lower() in body.lower()
34
+
35
+
36
+ def _detect_bot_mention(body: str) -> str | None:
37
+ """Return corrective message if body contains a blocked mention, else None."""
38
+ if _body_contains_token(body, COPILOT_MENTION_TOKEN):
39
+ return CORRECTIVE_MESSAGE_COPILOT
40
+ if _body_contains_token(body, CURSOR_MENTION_TOKEN):
41
+ return CORRECTIVE_MESSAGE_CURSOR
42
+ return None
43
+
44
+
45
+ def main() -> None:
46
+ try:
47
+ hook_input = json.load(sys.stdin)
48
+ except json.JSONDecodeError:
49
+ sys.exit(0)
50
+
51
+ if hook_input.get("tool_name", "") != TOOL_NAME:
52
+ sys.exit(0)
53
+
54
+ body = hook_input.get("tool_input", {}).get("body", "")
55
+ if not body:
56
+ sys.exit(0)
57
+
58
+ corrective_message = _detect_bot_mention(body)
59
+ if corrective_message is None:
60
+ sys.exit(0)
61
+
62
+ deny_payload = {
63
+ "hookSpecificOutput": {
64
+ "hookEventName": "PreToolUse",
65
+ "permissionDecision": "deny",
66
+ "permissionDecisionReason": corrective_message,
67
+ }
68
+ }
69
+ print(json.dumps(deny_payload))
70
+ sys.stdout.flush()
71
+ sys.exit(0)
72
+
73
+
74
+ if __name__ == "__main__":
75
+ main()