claude-dev-env 1.38.0 → 1.39.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 (271) 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 +189 -0
  7. package/_shared/pr-loop/scripts/post_audit_thread.py +947 -0
  8. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -19
  9. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +923 -0
  10. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +127 -0
  11. package/_shared/pr-loop/state-schema.md +1 -1
  12. package/agents/clean-coder.md +2 -2
  13. package/bin/install.mjs +6 -7
  14. package/bin/install.test.mjs +8 -0
  15. package/commands/doc-gist.md +16 -0
  16. package/commands/plan.md +0 -2
  17. package/commands/review-plan.md +1 -1
  18. package/docs/CODE_RULES.md +122 -2
  19. package/hooks/blocking/bot_mention_comment_blocker.py +75 -0
  20. package/hooks/blocking/code_rules_enforcer.py +1236 -161
  21. package/hooks/blocking/convergence_gate_blocker.py +130 -0
  22. package/hooks/blocking/destructive_command_blocker.py +74 -0
  23. package/hooks/blocking/gh_body_arg_blocker.py +30 -0
  24. package/hooks/blocking/md_to_html_blocker.py +119 -0
  25. package/hooks/blocking/test_bot_mention_comment_blocker.py +131 -0
  26. package/hooks/blocking/test_code_rules_enforcer.py +21 -0
  27. package/hooks/blocking/test_code_rules_enforcer_any_exempt_files.py +70 -0
  28. package/hooks/blocking/test_code_rules_enforcer_any_imports_and_cast.py +92 -0
  29. package/hooks/blocking/test_code_rules_enforcer_banned_import_alias.py +143 -0
  30. package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +152 -0
  31. package/hooks/blocking/test_code_rules_enforcer_bare_except.py +120 -0
  32. package/hooks/blocking/test_code_rules_enforcer_boundary_types.py +175 -0
  33. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
  34. package/hooks/blocking/test_code_rules_enforcer_collection_prefix.py +50 -0
  35. package/hooks/blocking/test_code_rules_enforcer_docstring_format.py +255 -0
  36. package/hooks/blocking/test_code_rules_enforcer_inline_tuple_string_magic.py +130 -0
  37. package/hooks/blocking/test_code_rules_enforcer_stub_implementations.py +141 -0
  38. package/hooks/blocking/test_code_rules_enforcer_test_branching.py +143 -0
  39. package/hooks/blocking/test_code_rules_enforcer_thin_wrapper_files.py +169 -0
  40. package/hooks/blocking/test_code_rules_enforcer_todo_markers.py +99 -0
  41. package/hooks/blocking/test_code_rules_enforcer_typed_dict_pairs.py +141 -0
  42. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +158 -0
  43. package/hooks/blocking/test_convergence_gate_blocker.py +63 -0
  44. package/hooks/blocking/test_destructive_command_blocker.py +146 -0
  45. package/hooks/blocking/test_destructive_command_blocker_no_verify.py +102 -0
  46. package/hooks/blocking/test_gh_body_arg_blocker.py +45 -0
  47. package/hooks/blocking/test_md_to_html_blocker.py +317 -0
  48. package/hooks/config/any_type_config.py +7 -0
  49. package/hooks/config/banned_identifiers_constants.py +11 -0
  50. package/hooks/config/blocking_check_limits.py +38 -0
  51. package/hooks/config/bot_mention_comment_blocker_constants.py +20 -0
  52. package/hooks/config/code_rules_enforcer_constants.py +53 -0
  53. package/hooks/config/convergence_branch_constants.py +9 -0
  54. package/hooks/config/doc_gist_auto_publish_constants.py +18 -0
  55. package/hooks/config/html_companion_constants.py +20 -0
  56. package/hooks/config/inline_tuple_string_magic_constants.py +22 -0
  57. package/hooks/config/test_banned_identifiers_constants.py +17 -0
  58. package/hooks/hooks.json +28 -20
  59. package/hooks/pyproject.toml +69 -0
  60. package/hooks/validators/mypy_integration.py +47 -1
  61. package/hooks/validators/run_all_validators.py +3 -3
  62. package/hooks/validators/test_mypy_integration.py +50 -1
  63. package/hooks/workflow/doc_gist_auto_publish.py +144 -0
  64. package/hooks/workflow/md_to_html_companion.py +365 -0
  65. package/hooks/workflow/test_doc_gist_auto_publish.py +117 -0
  66. package/hooks/workflow/test_md_to_html_companion.py +452 -0
  67. package/package.json +1 -1
  68. package/rules/gh-body-file.md +2 -0
  69. package/scripts/Install-SweepEmptyDirs.ps1 +111 -0
  70. package/scripts/check.ps1 +106 -0
  71. package/scripts/config/timing.py +11 -0
  72. package/scripts/sweep_empty_dirs.py +138 -0
  73. package/scripts/sync_to_cursor/rules.py +1 -1
  74. package/scripts/test_sweep_empty_dirs.py +183 -0
  75. package/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +323 -0
  76. package/skills/_shared/pr-loop/scripts/_cli_utils.py +22 -0
  77. package/skills/_shared/pr-loop/scripts/_path_resolver.py +165 -0
  78. package/skills/_shared/pr-loop/scripts/_xml_utils.py +20 -0
  79. package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +182 -0
  80. package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +185 -0
  81. package/skills/_shared/pr-loop/scripts/config/__init__.py +0 -0
  82. package/skills/_shared/pr-loop/scripts/config/path_resolver_constants.py +78 -0
  83. package/skills/_shared/pr-loop/scripts/init_loop_state.py +135 -0
  84. package/skills/_shared/pr-loop/scripts/teardown_worktrees.py +175 -0
  85. package/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +182 -0
  86. package/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +206 -0
  87. package/skills/bugteam/CONSTRAINTS.md +21 -22
  88. package/skills/bugteam/EXAMPLES.md +3 -3
  89. package/skills/bugteam/PROMPTS.md +227 -67
  90. package/skills/bugteam/SKILL.md +114 -455
  91. package/skills/bugteam/reference/README.md +1 -1
  92. package/skills/bugteam/reference/audit-and-teammates.md +112 -39
  93. package/skills/bugteam/reference/audit-contract.md +4 -22
  94. package/skills/bugteam/reference/copilot-gap-analysis.md +8 -5
  95. package/skills/bugteam/reference/design-rationale.md +2 -2
  96. package/skills/bugteam/reference/github-pr-reviews.md +50 -57
  97. package/skills/bugteam/reference/obstacles/audit-assign-ids.md +13 -0
  98. package/skills/bugteam/reference/obstacles/audit-capture-excerpts.md +13 -0
  99. package/skills/bugteam/reference/obstacles/audit-walk-categories.md +13 -0
  100. package/skills/bugteam/reference/obstacles/audit-write-xml.md +13 -0
  101. package/skills/bugteam/reference/obstacles/fix-append-summary.md +13 -0
  102. package/skills/bugteam/reference/obstacles/fix-apply-fixes.md +13 -0
  103. package/skills/bugteam/reference/obstacles/fix-git-add-commit.md +13 -0
  104. package/skills/bugteam/reference/obstacles/fix-git-push.md +13 -0
  105. package/skills/bugteam/reference/obstacles/fix-post-reply.md +13 -0
  106. package/skills/bugteam/reference/obstacles/fix-publish-summary.md +13 -0
  107. package/skills/bugteam/reference/obstacles/fix-py-compile.md +13 -0
  108. package/skills/bugteam/reference/obstacles/fix-read-files.md +13 -0
  109. package/skills/bugteam/reference/obstacles/fix-resolve-thread.md +13 -0
  110. package/skills/bugteam/reference/obstacles/fix-test-suite.md +13 -0
  111. package/skills/bugteam/reference/obstacles/fix-violation-count.md +13 -0
  112. package/skills/bugteam/reference/obstacles/fix-write-xml.md +13 -0
  113. package/skills/bugteam/reference/team-setup.md +106 -9
  114. package/skills/bugteam/reference/teardown-publish-permissions.md +39 -8
  115. package/skills/bugteam/scripts/README.md +60 -0
  116. package/skills/bugteam/scripts/_claude_permissions_common.py +358 -0
  117. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +976 -0
  118. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +375 -0
  119. package/skills/bugteam/scripts/bugteam_preflight.py +294 -0
  120. package/skills/bugteam/scripts/config/bugteam_code_rules_gate_constants.py +25 -0
  121. package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +26 -0
  122. package/skills/bugteam/scripts/config/bugteam_preflight_constants.py +35 -0
  123. package/skills/bugteam/scripts/config/claude_permissions_common_constants.py +20 -0
  124. package/skills/bugteam/scripts/config/probe_code_rules_enforcer_check_constants.py +12 -0
  125. package/skills/bugteam/scripts/config/windows_safe_rmtree_constants.py +7 -0
  126. package/skills/bugteam/scripts/grant_project_claude_permissions.py +175 -0
  127. package/skills/bugteam/scripts/probe_code_rules_enforcer_check.py +107 -0
  128. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +220 -0
  129. package/skills/bugteam/scripts/test__claude_permissions_common.py +112 -0
  130. package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +400 -0
  131. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +384 -0
  132. package/skills/bugteam/scripts/test_bugteam_preflight.py +268 -0
  133. package/skills/bugteam/scripts/test_claude_permissions_common.py +195 -0
  134. package/skills/bugteam/scripts/test_grant_project_claude_permissions.py +55 -0
  135. package/skills/bugteam/scripts/test_probe_code_rules_enforcer_check.py +76 -0
  136. package/skills/bugteam/scripts/test_revoke_project_claude_permissions.py +55 -0
  137. package/skills/bugteam/scripts/test_windows_safe_rmtree.py +108 -0
  138. package/skills/bugteam/scripts/windows_safe_rmtree.py +100 -0
  139. package/skills/bugteam/test_skill_additions.py +1 -11
  140. package/skills/code/SKILL.md +176 -0
  141. package/skills/doc-gist/SKILL.md +99 -0
  142. package/skills/doc-gist/references/examples/01-exploration-code-approaches.html +453 -0
  143. package/skills/doc-gist/references/examples/02-exploration-visual-designs.html +515 -0
  144. package/skills/doc-gist/references/examples/03-code-review-pr.html +638 -0
  145. package/skills/doc-gist/references/examples/04-code-understanding.html +491 -0
  146. package/skills/doc-gist/references/examples/05-design-system.html +629 -0
  147. package/skills/doc-gist/references/examples/06-component-variants.html +605 -0
  148. package/skills/doc-gist/references/examples/07-prototype-animation.html +455 -0
  149. package/skills/doc-gist/references/examples/08-prototype-interaction.html +396 -0
  150. package/skills/doc-gist/references/examples/09-slide-deck.html +592 -0
  151. package/skills/doc-gist/references/examples/10-svg-illustrations.html +492 -0
  152. package/skills/doc-gist/references/examples/11-status-report.html +528 -0
  153. package/skills/doc-gist/references/examples/12-incident-report.html +596 -0
  154. package/skills/doc-gist/references/examples/13-flowchart-diagram.html +395 -0
  155. package/skills/doc-gist/references/examples/14-research-feature-explainer.html +381 -0
  156. package/skills/doc-gist/references/examples/15-research-concept-explainer.html +368 -0
  157. package/skills/doc-gist/references/examples/16-implementation-plan.html +702 -0
  158. package/skills/doc-gist/references/examples/17-pr-writeup.html +595 -0
  159. package/skills/doc-gist/references/examples/18-editor-triage-board.html +573 -0
  160. package/skills/doc-gist/references/examples/19-editor-feature-flags.html +663 -0
  161. package/skills/doc-gist/references/examples/20-editor-prompt-tuner.html +722 -0
  162. package/skills/doc-gist/references/examples/README.md +5 -0
  163. package/skills/doc-gist/scripts/config/__init__.py +0 -0
  164. package/skills/doc-gist/scripts/config/gist_upload_constants.py +16 -0
  165. package/skills/doc-gist/scripts/gist_upload.py +177 -0
  166. package/skills/doc-gist/scripts/test_gist_upload.py +51 -0
  167. package/skills/findbugs/SKILL.md +68 -2
  168. package/skills/monitor-open-prs/SKILL.md +13 -32
  169. package/skills/monitor-open-prs/test_skill_contract.py +0 -11
  170. package/skills/pr-consistency-audit/SKILL.md +112 -0
  171. package/skills/pr-consistency-audit/reference/detection-rules.md +96 -0
  172. package/skills/pr-consistency-audit/reference/illustrations.md +78 -0
  173. package/skills/pr-converge/SKILL.md +227 -23
  174. package/skills/pr-converge/config/__init__.py +0 -0
  175. package/skills/pr-converge/config/constants.py +62 -0
  176. package/skills/pr-converge/reference/convergence-gates.md +138 -44
  177. package/skills/pr-converge/reference/examples.md +43 -11
  178. package/skills/pr-converge/reference/fix-protocol.md +6 -5
  179. package/skills/pr-converge/reference/ground-rules.md +5 -3
  180. package/skills/pr-converge/reference/multi-pr-orchestration.md +44 -19
  181. package/skills/pr-converge/reference/obstacles/fix-post-replies.md +13 -0
  182. package/skills/pr-converge/reference/obstacles/fix-publish-summary.md +13 -0
  183. package/skills/pr-converge/reference/obstacles/fix-push.md +13 -0
  184. package/skills/pr-converge/reference/obstacles/fix-read-filelines.md +13 -0
  185. package/skills/pr-converge/reference/obstacles/fix-reset-state.md +13 -0
  186. package/skills/pr-converge/reference/obstacles/fix-resolve-threads.md +13 -0
  187. package/skills/pr-converge/reference/obstacles/fix-spawn-clean-coder.md +13 -0
  188. package/skills/pr-converge/reference/obstacles/fix-stage-commit.md +13 -0
  189. package/skills/pr-converge/reference/obstacles/fix-trigger-bugbot.md +13 -0
  190. package/skills/pr-converge/reference/obstacles/fix-write-test.md +13 -0
  191. package/skills/pr-converge/reference/per-tick.md +90 -31
  192. package/skills/pr-converge/reference/state-schema.md +22 -1
  193. package/skills/pr-converge/reference/stop-conditions.md +9 -7
  194. package/skills/pr-converge/scripts/README.md +34 -46
  195. package/skills/pr-converge/scripts/check_bugbot_ci.py +174 -0
  196. package/skills/pr-converge/scripts/check_convergence.py +497 -0
  197. package/skills/pr-converge/scripts/check_pending_reviews.py +154 -0
  198. package/skills/pr-converge/scripts/config/pr_converge_constants.py +118 -0
  199. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +134 -0
  200. package/skills/pr-converge/scripts/post_fix_reply.py +168 -0
  201. package/skills/pr-converge/workflows/schedule-wakeup-loop.md +5 -12
  202. package/skills/qbug/SKILL.md +132 -27
  203. package/skills/session-log/SKILL.md +216 -114
  204. package/skills/session-tidy/SKILL.md +1 -1
  205. package/skills/skill-builder/SKILL.md +138 -56
  206. package/skills/skill-builder/references/delegation-map.md +72 -113
  207. package/skills/skill-builder/references/progressive-disclosure.md +122 -0
  208. package/skills/skill-builder/references/self-audit-checklist.md +92 -0
  209. package/skills/skill-builder/references/skill-types.md +228 -0
  210. package/skills/skill-builder/references/thariq-x-post-skills.json +33 -0
  211. package/skills/skill-builder/templates/gap-analysis.md +15 -8
  212. package/skills/skill-builder/workflows/improve-skill.md +86 -57
  213. package/skills/skill-builder/workflows/new-skill.md +80 -168
  214. package/skills/skill-builder/workflows/polish-skill.md +78 -54
  215. package/skills/structure-prompt/SKILL.md +50 -0
  216. package/skills/structure-prompt/reference/adversarial-tuning.md +62 -0
  217. package/skills/structure-prompt/reference/block-classification.md +27 -0
  218. package/skills/structure-prompt/reference/canonical-case.md +48 -0
  219. package/skills/structure-prompt/reference/citation-depth.md +70 -0
  220. package/skills/structure-prompt/reference/cleanup.md +33 -0
  221. package/skills/structure-prompt/reference/constraints.md +33 -0
  222. package/skills/structure-prompt/reference/directives.md +37 -0
  223. package/skills/structure-prompt/reference/examples.md +72 -0
  224. package/skills/structure-prompt/reference/instantiation.md +51 -0
  225. package/skills/structure-prompt/reference/output-contract.md +72 -0
  226. package/skills/structure-prompt/reference/per-category.md +23 -0
  227. package/skills/structure-prompt/reference/persona.md +38 -0
  228. package/skills/structure-prompt/reference/research.md +33 -0
  229. package/skills/structure-prompt/reference/structure.md +28 -0
  230. package/agents/code-standards-agent.md +0 -93
  231. package/agents/groq-coder.md +0 -113
  232. package/agents/plan-executor.md +0 -226
  233. package/agents/project-docs-analyzer.md +0 -53
  234. package/agents/project-structure-organizer-agent.md +0 -72
  235. package/agents/skill-to-agent-converter.md +0 -370
  236. package/agents/skill-writer-agent.md +0 -470
  237. package/agents/user-docs-writer.md +0 -67
  238. package/agents/workflow-visual-documenter.md +0 -82
  239. package/commands/readability-review.md +0 -20
  240. package/hooks/mypy.ini +0 -2
  241. package/hooks/notification/attention_needed_notify.py +0 -71
  242. package/hooks/notification/claude_notification_handler.py +0 -67
  243. package/hooks/notification/notification_utils.py +0 -267
  244. package/hooks/notification/subagent_complete_notify.py +0 -381
  245. package/hooks/notification/test_attention_needed_notify.py +0 -47
  246. package/hooks/notification/test_claude_notification_handler.py +0 -54
  247. package/hooks/notification/test_notification_utils.py +0 -91
  248. package/hooks/notification/test_subagent_complete_notify.py +0 -79
  249. package/scripts/config/groq_bugteam_config.py +0 -230
  250. package/scripts/config/test_groq_bugteam_config.py +0 -83
  251. package/scripts/config/test_spec_implementer_prompt.py +0 -32
  252. package/scripts/groq_bugteam.README.md +0 -131
  253. package/scripts/groq_bugteam.py +0 -647
  254. package/scripts/groq_bugteam_dotenv.py +0 -40
  255. package/scripts/groq_bugteam_spec.py +0 -226
  256. package/scripts/test_groq_bugteam.py +0 -529
  257. package/scripts/test_groq_bugteam_apply_fix_from_spec.py +0 -426
  258. package/scripts/test_groq_bugteam_dotenv.py +0 -66
  259. package/scripts/test_groq_bugteam_spec.py +0 -338
  260. package/skills/bugteam/SKILL_EVALS.md +0 -309
  261. package/skills/dream/SKILL.md +0 -118
  262. package/skills/ingest/SKILL.md +0 -40
  263. package/skills/npm-creator/SKILL.md +0 -187
  264. package/skills/readability-review/SKILL.md +0 -127
  265. package/skills/resume-review/SKILL.md +0 -261
  266. package/skills/rule-audit/SKILL.md +0 -307
  267. package/skills/rule-creator/SKILL.md +0 -150
  268. package/skills/searching-obsidian-vault/SKILL.md +0 -131
  269. package/skills/skill-writer/REFERENCE.md +0 -284
  270. package/skills/skill-writer/SKILL.md +0 -222
  271. package/skills/tdd-team/SKILL.md +0 -128
@@ -0,0 +1,127 @@
1
+ """Tests for post_audit_thread_constants.py extracted constant set."""
2
+
3
+ import importlib.util
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _load_constants_module() -> ModuleType:
9
+ module_path = (
10
+ Path(__file__).parent.parent / "config" / "post_audit_thread_constants.py"
11
+ )
12
+ specification = importlib.util.spec_from_file_location(
13
+ "config.post_audit_thread_constants", module_path
14
+ )
15
+ assert specification is not None
16
+ assert specification.loader is not None
17
+ module = importlib.util.module_from_spec(specification)
18
+ specification.loader.exec_module(module)
19
+ return module
20
+
21
+
22
+ constants_module = _load_constants_module()
23
+
24
+
25
+ def test_http_request_content_type_is_application_json() -> None:
26
+ assert constants_module.HTTP_REQUEST_CONTENT_TYPE == "application/json"
27
+
28
+
29
+ def test_http_method_post_constant_is_post() -> None:
30
+ assert constants_module.HTTP_METHOD_POST == "POST"
31
+
32
+
33
+ def test_http_header_authorization_constant_is_authorization() -> None:
34
+ assert constants_module.HTTP_HEADER_AUTHORIZATION == "Authorization"
35
+
36
+
37
+ def test_http_header_accept_constant_is_accept() -> None:
38
+ assert constants_module.HTTP_HEADER_ACCEPT == "Accept"
39
+
40
+
41
+ def test_http_header_content_type_constant_is_content_type() -> None:
42
+ assert constants_module.HTTP_HEADER_CONTENT_TYPE == "Content-Type"
43
+
44
+
45
+ def test_http_header_github_api_version_constant_is_x_github_api_version() -> None:
46
+ assert constants_module.HTTP_HEADER_GITHUB_API_VERSION == "X-GitHub-Api-Version"
47
+
48
+
49
+ def test_http_header_user_agent_constant_is_user_agent() -> None:
50
+ assert constants_module.HTTP_HEADER_USER_AGENT == "User-Agent"
51
+
52
+
53
+ def test_http_authorization_bearer_prefix_is_bearer_with_trailing_space() -> None:
54
+ prefix = constants_module.HTTP_AUTHORIZATION_BEARER_PREFIX
55
+ assert prefix == "Bearer "
56
+ assert prefix.endswith(" ")
57
+
58
+
59
+ def test_http_request_timeout_seconds_is_positive_int() -> None:
60
+ timeout_seconds = constants_module.HTTP_REQUEST_TIMEOUT_SECONDS
61
+ assert isinstance(timeout_seconds, int)
62
+ assert timeout_seconds > 0
63
+
64
+
65
+ def test_error_response_preview_chars_is_positive_int() -> None:
66
+ preview_chars = constants_module.ERROR_RESPONSE_PREVIEW_CHARS
67
+ assert isinstance(preview_chars, int)
68
+ assert preview_chars > 0
69
+
70
+
71
+ def test_single_review_api_path_template_uses_pr_number_placeholder() -> None:
72
+ template_text = constants_module.SINGLE_REVIEW_API_PATH_TEMPLATE
73
+ assert "{owner}" in template_text
74
+ assert "{repo}" in template_text
75
+ assert "{pr_number}" in template_text
76
+ assert "{review_id}" in template_text
77
+
78
+
79
+ def test_single_review_comments_api_path_template_uses_pr_number_placeholder() -> None:
80
+ template_text = constants_module.SINGLE_REVIEW_COMMENTS_API_PATH_TEMPLATE
81
+ assert "{owner}" in template_text
82
+ assert "{repo}" in template_text
83
+ assert "{pr_number}" in template_text
84
+ assert "{review_id}" in template_text
85
+ assert template_text.endswith("/comments")
86
+
87
+
88
+ def test_audit_body_skeleton_marker_tokens_present() -> None:
89
+ open_marker = constants_module.AUDIT_BODY_SKELETON_OPEN_MARKER
90
+ close_marker = constants_module.AUDIT_BODY_SKELETON_CLOSE_MARKER
91
+ assert open_marker.startswith("<!--") and open_marker.endswith("-->")
92
+ assert close_marker.startswith("<!--") and close_marker.endswith("-->")
93
+ assert open_marker != close_marker
94
+
95
+
96
+ def test_template_path_resolves_to_existing_markdown_file() -> None:
97
+ resolved_path = constants_module.template_path()
98
+ assert resolved_path.is_file(), f"missing: {resolved_path}"
99
+ assert resolved_path.suffix == ".md"
100
+
101
+
102
+ def test_template_contains_skeleton_markers() -> None:
103
+ resolved_path = constants_module.template_path()
104
+ template_text = resolved_path.read_text(encoding="utf-8")
105
+ assert constants_module.AUDIT_BODY_SKELETON_OPEN_MARKER in template_text
106
+ assert constants_module.AUDIT_BODY_SKELETON_CLOSE_MARKER in template_text
107
+
108
+
109
+ def test_live_test_fixture_names_are_not_exposed_from_production_config_module() -> None:
110
+ forbidden_attribute_names = [
111
+ "LIVE_TEST_OWNER",
112
+ "LIVE_TEST_REPO",
113
+ "LIVE_TEST_BRANCH_PREFIX",
114
+ "LIVE_TEST_PR_TITLE",
115
+ "LIVE_TEST_PR_BODY",
116
+ "LIVE_TEST_BASE_BRANCH",
117
+ "LIVE_TEST_FIXTURE_FILENAME",
118
+ "LIVE_TEST_FIXTURE_CONTENT",
119
+ "LIVE_TEST_FIXTURE_LINE_FOR_FINDING_ONE",
120
+ "LIVE_TEST_FIXTURE_LINE_FOR_FINDING_TWO",
121
+ "LIVE_TEST_FIXTURE_LINE_FOR_FINDING_THREE",
122
+ ]
123
+ for each_attribute_name in forbidden_attribute_names:
124
+ assert not hasattr(constants_module, each_attribute_name), (
125
+ f"production config module exposes test-only fixture "
126
+ f"{each_attribute_name!r}; move it to test_post_audit_thread.py"
127
+ )
@@ -11,7 +11,7 @@ State each PR-loop workflow tracks across iterations. Workflows differ on persis
11
11
  | `last_findings` | object | `{p0, p1, p2, total}` count of findings from most recent AUDIT |
12
12
  | `audit_log` | list[str] | Per-iteration one-line summaries for the final report |
13
13
  | `starting_sha` | str | `git rev-parse HEAD` at workflow start |
14
- | `loop_comment_index` | dict | `{finding_id: {finding_comment_id, finding_comment_url, used_fallback, fix_status, ...}}` |
14
+ | `loop_comment_index` | dict | `{finding_id: {finding_comment_id, finding_comment_url, thread_node_id, fix_status, ...}}` (`thread_node_id` is the PR review thread node id — `PRRT_kwDOxxx` — captured at audit time when calling `get_review_comments`, used by `resolve_thread` at FIX time) |
15
15
 
16
16
  ## Workflow-specific extensions
17
17
 
@@ -8,7 +8,7 @@ color: green
8
8
 
9
9
  # Clean Coder — Zero-Defect Code Generation
10
10
 
11
- You are the definitive code-writing agent. You produce code so clean that reviewers find nothing. Every rule from CODE_RULES.md and every dimension from the readability rubric is internalized into your generation process. The goal: `/check` and `/readability-review` return CLEAN on every file you touch.
11
+ You are the definitive code-writing agent. You produce code so clean that reviewers find nothing. Every rule from CODE_RULES.md and every dimension from the readability rubric is internalized into your generation process. The goal: `/check` returns CLEAN on every file you touch.
12
12
 
13
13
  **Announce at start:** "Using clean-coder agent — CODE_RULES.md internalized, targeting 160/160 readability."
14
14
 
@@ -445,7 +445,7 @@ Code clean-coder writes will be audited later against the A–K bug categories f
445
445
  Every line you write or modify will:
446
446
  - Score 160/160 on the 8-dimension readability rubric
447
447
  - Satisfy every hook-enforced gate so each write succeeds on the first attempt
448
- - Return CLEAN from `/check`, `/review-code`, and `/readability-review`
448
+ - Return CLEAN from `/check` and `/review-code`
449
449
  - Use complete type hints on every parameter and return
450
450
  - Pass `mypy_validator.py` cleanly — every file is mypy-clean at write time
451
451
  - Land in the format the project's `auto_formatter.py` produces — the formatter runs at write time, but generation should already match the canonical Black/Prettier output
package/bin/install.mjs CHANGED
@@ -16,7 +16,7 @@ const PACKAGE_NAME = 'claude-dev-env';
16
16
  const PACKAGE_VERSION = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf8')).version;
17
17
  const packageRequire = createRequire(import.meta.url);
18
18
 
19
- export const CONTENT_DIRECTORIES = ['rules', 'docs', 'commands', 'agents', 'system-prompts', 'scripts', '_shared'];
19
+ export const CONTENT_DIRECTORIES = ['rules', 'docs', 'commands', 'agents', 'system-prompts', 'scripts', '_shared', 'audit-rubrics'];
20
20
 
21
21
  export function collectPackageSourceConflicts(packageDirectory) {
22
22
  const gitConflictStatusCodes = new Set(['DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU']);
@@ -147,17 +147,16 @@ const INSTALL_GROUPS = {
147
147
  core: {
148
148
  description: 'Development standards, hooks, agents, commands',
149
149
  skills: [
150
- 'anthropic-plan', 'everything-search', 'ingest',
151
- 'npm-creator', 'pr-review-responder', 'readability-review',
152
- 'recall', 'remember', 'rule-audit', 'rule-creator',
153
- 'skill-writer', 'tdd-team'
150
+ 'anthropic-plan', 'everything-search',
151
+ 'pr-review-responder',
152
+ 'recall', 'remember'
154
153
  ],
155
- includeDirectories: ['rules', 'docs', 'commands', 'agents'],
154
+ includeDirectories: ['rules', 'docs', 'commands', 'agents', 'audit-rubrics'],
156
155
  includeAllHooks: true,
157
156
  },
158
157
  journal: {
159
158
  description: 'Session logging and memory',
160
- skills: ['dream', 'session-log', 'session-tidy'],
159
+ skills: ['session-log', 'session-tidy'],
161
160
  },
162
161
  research: {
163
162
  description: 'Deep research and citation tools',
@@ -135,6 +135,14 @@ test('CONTENT_DIRECTORIES includes _shared so installer copies _shared/pr-loop/
135
135
  });
136
136
 
137
137
 
138
+ test('CONTENT_DIRECTORIES includes audit-rubrics so installer copies category rubrics and prompts to ~/.claude/audit-rubrics/', () => {
139
+ assert.ok(
140
+ CONTENT_DIRECTORIES.includes('audit-rubrics'),
141
+ 'audit-rubrics must be in CONTENT_DIRECTORIES so bugteam can resolve $HOME/.claude/audit-rubrics/{category_rubrics,prompts}/',
142
+ );
143
+ });
144
+
145
+
138
146
  test('collectPackageSourceConflicts surfaces both-added and deleted-by-them entries', () => {
139
147
  const repositoryRoot = createTemporaryGitRepository();
140
148
  try {
@@ -0,0 +1,16 @@
1
+ ---
2
+ description: Upload an HTML file as a secret gist and open the htmlpreview URL. Manual escape hatch when the auto-publish hook's marker route doesn't apply.
3
+ allowed-tools: Bash
4
+ ---
5
+
6
+ Argument: `$ARGUMENTS` is the path to an existing `.html` file.
7
+
8
+ Run the doc-gist skill's transport script directly:
9
+
10
+ ```
11
+ python3 "${CLAUDE_PLUGIN_ROOT}/skills/doc-gist/scripts/gist_upload.py" --input "$ARGUMENTS"
12
+ ```
13
+
14
+ Quote both URLs (`Gist:` and `Preview:`) from stderr back to the user as clickable markdown links. Surface any `gh gist create failed` error with the suggested `gh auth login` next-step.
15
+
16
+ When the user asks for a writeup or report rather than handing you a file path, design fresh HTML per [`skills/doc-gist/SKILL.md`](../skills/doc-gist/SKILL.md), include the `<!-- @publish-as-gist -->` marker, and write the file — the auto-publish hook will run on Write. Use this command only when the marker route doesn't fit (existing file you want to publish, HTML from a separate tool).
package/commands/plan.md CHANGED
@@ -41,8 +41,6 @@ Plan a feature with full validation workflow.
41
41
  | Design | `plan` skill | Collaborative design with config discovery |
42
42
  | Write | `write-plan` skill | TDD plan with CODE_RULES.md compliance |
43
43
  | Review Plan | `review-plan` skill | Validate plan against standards |
44
- | Execute | `plan-executor` agent | Implement with standards enforcement |
45
- | Review Code | `readability-review` skill | Validate code before commit |
46
44
 
47
45
  ## Standards Reference
48
46
 
@@ -1,5 +1,5 @@
1
1
  Review the current plan against code standards.
2
2
 
3
3
  1. Identify plan files in `.planning/phases/` or `docs/plans/`
4
- 2. Invoke the `readability-review` skill via the Skill tool with the plan file paths
4
+ 2. Review the plan against CODE_RULES.md standards
5
5
  3. Report the verdict back to the user
@@ -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
  ```
@@ -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()