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
@@ -57,6 +57,12 @@ from config.preflight_constants import (
57
57
  PYTHON_FILE_SUFFIX,
58
58
  TESTS_DIRECTORY_NAME,
59
59
  )
60
+ from reviews_disabled import (
61
+ CLAUDE_REVIEWS_DISABLED_BUGTEAM_TOKEN,
62
+ CLAUDE_REVIEWS_DISABLED_ENV_VAR_NAME,
63
+ EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV,
64
+ is_bugteam_disabled_via_env,
65
+ )
60
66
 
61
67
 
62
68
  def verify_git_hooks_path(repository_root: Path | None = None) -> int:
@@ -67,8 +73,13 @@ def verify_git_hooks_path(repository_root: Path | None = None) -> int:
67
73
  overrides such as Husky or lefthook. Falls back to the current working
68
74
  directory's effective config when *repository_root* is None.
69
75
 
70
- Returns zero when the configured path ends with the expected hooks suffix.
71
- Returns non-zero and prints a correction message when unset or pointing elsewhere.
76
+ Args:
77
+ repository_root: Optional repository root to check. When None, uses
78
+ the current working directory's effective config.
79
+
80
+ Returns:
81
+ Zero when the configured path ends with the expected hooks suffix.
82
+ Non-zero and prints a correction message when unset or pointing elsewhere.
72
83
  """
73
84
  expected_hooks_path_suffix = HOOKS_PATH_VERIFICATION_SUFFIX
74
85
  enforcement_absent_message = (
@@ -123,6 +134,18 @@ def verify_git_hooks_path(repository_root: Path | None = None) -> int:
123
134
 
124
135
 
125
136
  def find_repository_root(start: Path) -> Path:
137
+ """Find the repository root by walking up from the starting directory.
138
+
139
+ Searches for a ``.git`` directory or file in parent directories. Falls
140
+ back to the nearest ancestor containing ``pytest.ini`` when no git
141
+ repository is found.
142
+
143
+ Args:
144
+ start: The directory to start searching from.
145
+
146
+ Returns:
147
+ The repository root path, or *start* when no repository is found.
148
+ """
126
149
  resolved = start.resolve()
127
150
  all_candidates = [resolved, *resolved.parents]
128
151
  for each_candidate in all_candidates:
@@ -136,6 +159,17 @@ def find_repository_root(start: Path) -> Path:
136
159
 
137
160
 
138
161
  def has_pytest_configuration(root: Path) -> bool:
162
+ """Check whether a directory has pytest configuration available.
163
+
164
+ Checks for ``pytest.ini`` directly, then falls back to searching for
165
+ ``[tool.pytest]`` in ``pyproject.toml``.
166
+
167
+ Args:
168
+ root: The directory to check for pytest configuration.
169
+
170
+ Returns:
171
+ True when pytest configuration is found in either location.
172
+ """
139
173
  if (root / PYTEST_INI_FILENAME).is_file():
140
174
  return True
141
175
  pyproject = root / PYPROJECT_TOML_FILENAME
@@ -146,6 +180,20 @@ def has_pytest_configuration(root: Path) -> bool:
146
180
 
147
181
 
148
182
  def has_discoverable_tests(root: Path) -> bool | None:
183
+ """Check whether the repository contains discoverable test files via git ls-files.
184
+
185
+ When the root has no ``.git`` marker, returns True without invoking git.
186
+ Otherwise asks git for tracked plus untracked test files matching the
187
+ discovery patterns, respecting ``.gitignore``.
188
+
189
+ Args:
190
+ root: The directory tree root to search.
191
+
192
+ Returns:
193
+ True when at least one matching test file is found. False when git
194
+ succeeds and returns an empty list. None when git is unavailable or
195
+ the ls-files invocation fails.
196
+ """
149
197
  git_marker = root / GIT_DIRECTORY_NAME
150
198
  if not (git_marker.is_dir() or git_marker.is_file()):
151
199
  return True
@@ -192,6 +240,21 @@ def run_pytest(
192
240
  verbose: bool,
193
241
  all_test_paths: list[Path] | None = None,
194
242
  ) -> int:
243
+ """Run pytest in the repository root and return the exit code.
244
+
245
+ Passes ``--ff`` (failed-first) and ``-q`` unless *verbose* is True. When
246
+ *all_test_paths* is provided, restricts the run to those paths via the
247
+ ``--`` positional separator so pytest does not misinterpret leading
248
+ hyphens as options. Treats the "no tests collected" exit code as a pass.
249
+
250
+ Args:
251
+ repository_root: The repository root for running pytest.
252
+ verbose: When True, omit ``-q`` so individual test names show.
253
+ all_test_paths: Optional list of test paths to restrict the run.
254
+
255
+ Returns:
256
+ The pytest exit code, or 0 when no tests were collected.
257
+ """
195
258
  command = [sys.executable, "-m", "pytest", PYTEST_FAILED_FIRST_FLAG]
196
259
  if not verbose:
197
260
  command.append("-q")
@@ -209,6 +272,20 @@ def run_pytest(
209
272
 
210
273
 
211
274
  def get_changed_files(repository_root: Path, base_ref: str) -> list[Path] | None:
275
+ """Return the list of files changed between *base_ref* and HEAD.
276
+
277
+ Refuses base refs beginning with ``-`` to prevent option injection into
278
+ git diff. Logs a warning and returns None on every failure path so the
279
+ caller can fall back to running the full suite.
280
+
281
+ Args:
282
+ repository_root: The repository root for running git diff.
283
+ base_ref: The git base ref to diff against (e.g., ``origin/main``).
284
+
285
+ Returns:
286
+ A list of relative file paths changed vs *base_ref*. None when
287
+ *base_ref* is invalid or git diff fails.
288
+ """
212
289
  if base_ref.startswith("-"):
213
290
  print(
214
291
  f"bugteam_preflight: invalid base_ref '{base_ref}' starts "
@@ -295,6 +372,18 @@ def _find_related_test_files(changed_path: Path, repository_root: Path) -> list[
295
372
  def discover_related_tests(
296
373
  all_changed_files: list[Path], repository_root: Path
297
374
  ) -> list[Path]:
375
+ """Discover all test files related to the given changed files.
376
+
377
+ Walks every changed path through :func:`_find_related_test_files` and
378
+ returns the sorted, de-duplicated union.
379
+
380
+ Args:
381
+ all_changed_files: The list of changed source files to map to tests.
382
+ repository_root: The repository root for resolving relative paths.
383
+
384
+ Returns:
385
+ Sorted list of unique related test file paths.
386
+ """
298
387
  related: set[Path] = set()
299
388
  for each_file in all_changed_files:
300
389
  related.update(_find_related_test_files(each_file, repository_root))
@@ -302,6 +391,14 @@ def discover_related_tests(
302
391
 
303
392
 
304
393
  def run_pre_commit(repository_root: Path) -> int:
394
+ """Run pre-commit on all files and return its exit code.
395
+
396
+ Args:
397
+ repository_root: The repository root for running pre-commit.
398
+
399
+ Returns:
400
+ The pre-commit exit code (0 on success, non-zero on failure).
401
+ """
305
402
  completed = subprocess.run(
306
403
  list(ALL_PRE_COMMIT_RUN_ALL_FILES_COMMAND),
307
404
  cwd=str(repository_root),
@@ -311,6 +408,15 @@ def run_pre_commit(repository_root: Path) -> int:
311
408
 
312
409
 
313
410
  def parse_arguments(all_arguments: list[str]) -> argparse.Namespace:
411
+ """Parse command-line arguments for the preflight script.
412
+
413
+ Args:
414
+ all_arguments: Command-line argument list.
415
+
416
+ Returns:
417
+ Parsed namespace with repo_root, no_pytest, pre_commit, verbose,
418
+ base_ref, and scope attributes.
419
+ """
314
420
  parser = argparse.ArgumentParser(
315
421
  description="Run local checks before /bugteam (pytest, optional pre-commit).",
316
422
  )
@@ -360,6 +466,16 @@ def parse_arguments(all_arguments: list[str]) -> argparse.Namespace:
360
466
 
361
467
 
362
468
  def main(all_arguments: list[str]) -> int:
469
+ """Run the preflight checks (git-hooks path, pytest, optional pre-commit).
470
+
471
+ Args:
472
+ all_arguments: Command-line argument list to forward to argparse.
473
+
474
+ Returns:
475
+ Zero on success. Non-zero exit code on the first failing check.
476
+ Returns :data:`EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV` when
477
+ ``CLAUDE_REVIEWS_DISABLED`` lists the ``bugteam`` token.
478
+ """
363
479
  arguments = parse_arguments(all_arguments)
364
480
  skip_env_var_name = BUGTEAM_PREFLIGHT_SKIP_ENV_VAR_NAME
365
481
  skip_enabled_value = BUGTEAM_PREFLIGHT_SKIP_ENABLED_VALUE
@@ -369,6 +485,17 @@ def main(all_arguments: list[str]) -> int:
369
485
  file=sys.stderr,
370
486
  )
371
487
  return 0
488
+ reviews_disabled_env_var_name = CLAUDE_REVIEWS_DISABLED_ENV_VAR_NAME
489
+ reviews_disabled_bugteam_token = CLAUDE_REVIEWS_DISABLED_BUGTEAM_TOKEN
490
+ disabled_via_env_exit_code = EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV
491
+ if is_bugteam_disabled_via_env():
492
+ print(
493
+ f"bugteam_preflight: halted "
494
+ f"({reviews_disabled_env_var_name} contains "
495
+ f"'{reviews_disabled_bugteam_token}').",
496
+ file=sys.stderr,
497
+ )
498
+ return disabled_via_env_exit_code
372
499
  start = Path.cwd()
373
500
  repository_root = (
374
501
  arguments.repo_root.resolve()
@@ -0,0 +1,59 @@
1
+ """Shared helper for the CLAUDE_REVIEWS_DISABLED opt-out gate.
2
+
3
+ Both ``skills/bugteam/scripts/bugteam_preflight.py`` and
4
+ ``_shared/pr-loop/scripts/preflight.py`` consume this helper so the parsing
5
+ rules and disabled-token taxonomy live in exactly one place.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ for each_cached_module_name in [
15
+ each_module_key
16
+ for each_module_key in list(sys.modules)
17
+ if each_module_key == "config" or each_module_key.startswith("config.")
18
+ ]:
19
+ sys.modules.pop(each_cached_module_name, None)
20
+ _shared_pr_loop_scripts_directory = str(Path(__file__).absolute().parent)
21
+ while _shared_pr_loop_scripts_directory in sys.path:
22
+ sys.path.remove(_shared_pr_loop_scripts_directory)
23
+ if _shared_pr_loop_scripts_directory not in sys.path:
24
+ sys.path.insert(0, _shared_pr_loop_scripts_directory)
25
+
26
+ from config.reviews_disabled_constants import (
27
+ CLAUDE_REVIEWS_DISABLED_BUGTEAM_TOKEN,
28
+ CLAUDE_REVIEWS_DISABLED_ENV_VAR_NAME,
29
+ CLAUDE_REVIEWS_DISABLED_TOKEN_SEPARATOR,
30
+ EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV,
31
+ )
32
+
33
+
34
+ __all__ = [
35
+ "CLAUDE_REVIEWS_DISABLED_BUGTEAM_TOKEN",
36
+ "CLAUDE_REVIEWS_DISABLED_ENV_VAR_NAME",
37
+ "CLAUDE_REVIEWS_DISABLED_TOKEN_SEPARATOR",
38
+ "EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV",
39
+ "is_bugteam_disabled_via_env",
40
+ ]
41
+
42
+
43
+ def is_bugteam_disabled_via_env() -> bool:
44
+ """Check whether CLAUDE_REVIEWS_DISABLED opts the bug-audit family out of running.
45
+
46
+ Returns:
47
+ True when the env var contains the literal ``bugteam`` token
48
+ (comma-separated, case-insensitive, whitespace-tolerant).
49
+ """
50
+ reviews_disabled_env_var_name = CLAUDE_REVIEWS_DISABLED_ENV_VAR_NAME
51
+ reviews_disabled_token_separator = CLAUDE_REVIEWS_DISABLED_TOKEN_SEPARATOR
52
+ reviews_disabled_bugteam_token = CLAUDE_REVIEWS_DISABLED_BUGTEAM_TOKEN
53
+ raw_value = os.environ.get(reviews_disabled_env_var_name, "")
54
+ all_disabled_tokens = frozenset(
55
+ each_raw_token.strip().lower()
56
+ for each_raw_token in raw_value.split(reviews_disabled_token_separator)
57
+ if each_raw_token.strip()
58
+ )
59
+ return reviews_disabled_bugteam_token in all_disabled_tokens
@@ -552,25 +552,6 @@ def test_check_wrapper_plumb_through_accepts_positional_or_keyword_forwarder() -
552
552
  assert issues == []
553
553
 
554
554
 
555
- def test_check_database_column_string_magic_dedupes_nested_function_tuples() -> None:
556
- """Regression: tuples inside nested FunctionDefs must produce one finding, not many.
557
-
558
- The outer ast.walk previously enumerated every FunctionDef including nested
559
- ones, then the inner ast.walk(each_node) walked the full subtree, so a tuple
560
- inside a nested function was visited via every enclosing function. This must
561
- surface exactly one finding per tuple site.
562
- """
563
- source = (
564
- "def outer():\n"
565
- " def inner():\n"
566
- ' x = ("some_column_name", 42)\n'
567
- " return x\n"
568
- " return inner\n"
569
- )
570
- issues = gate_module.check_database_column_string_magic(source, "module.py")
571
- assert len(issues) == 1, f"expected 1 finding, got {len(issues)}: {issues!r}"
572
-
573
-
574
555
  def test_check_wrapper_plumb_through_skips_uppercase_js_extension() -> None:
575
556
  """Regression: case-insensitive filesystem (Windows, macOS) can yield
576
557
  file paths like 'Foo.JS'. The skip predicate must normalize case so