claude-dev-env 1.93.0 → 1.94.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 (220) hide show
  1. package/_shared/pr-loop/CLAUDE.md +1 -0
  2. package/_shared/pr-loop/audit-contract.md +1 -1
  3. package/_shared/pr-loop/gh-payloads.md +3 -3
  4. package/_shared/pr-loop/post-audit-thread-contract.md +51 -0
  5. package/_shared/pr-loop/scripts/README.md +2 -2
  6. package/_shared/pr-loop/scripts/code_rules_gate.py +127 -13
  7. package/_shared/pr-loop/scripts/fix_hookspath.py +47 -12
  8. package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +4 -0
  9. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +146 -69
  10. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -374
  11. package/_shared/pr-loop/state-schema.md +3 -22
  12. package/bin/CLAUDE.md +1 -1
  13. package/bin/install.mjs +2 -7
  14. package/bin/install.test.mjs +10 -4
  15. package/hooks/blocking/CLAUDE.md +2 -0
  16. package/hooks/blocking/pii_commit_command.py +398 -0
  17. package/hooks/blocking/pii_payload_scan.py +133 -0
  18. package/hooks/blocking/pii_prevention_blocker.py +151 -449
  19. package/hooks/blocking/pii_scanner.py +26 -1
  20. package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +2 -2
  21. package/hooks/blocking/test_pii_prevention_blocker.py +299 -0
  22. package/hooks/blocking/test_pii_scanner.py +27 -2
  23. package/hooks/blocking/test_verdict_directory_write_blocker.py +42 -7
  24. package/hooks/hooks_constants/CLAUDE.md +2 -2
  25. package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -2
  26. package/hooks/hooks_constants/local_identity.py +65 -15
  27. package/hooks/hooks_constants/pii_prevention_constants.py +20 -8
  28. package/hooks/hooks_constants/pr_description_enforcer_constants.py +1 -1
  29. package/hooks/hooks_constants/test_local_identity.py +68 -0
  30. package/package.json +1 -1
  31. package/scripts/CLAUDE.md +10 -1
  32. package/scripts/Get-SessionAccount.ps1 +210 -0
  33. package/scripts/tests/CLAUDE.md +15 -1
  34. package/scripts/tests/Get-SessionAccount.Tests.ps1 +350 -0
  35. package/skills/CLAUDE.md +3 -25
  36. package/skills/anthropic-plan/SKILL.md +1 -13
  37. package/skills/autoconverge/CLAUDE.md +5 -2
  38. package/skills/autoconverge/SKILL.md +112 -387
  39. package/skills/autoconverge/reference/CLAUDE.md +4 -1
  40. package/skills/autoconverge/reference/closing-report.md +9 -2
  41. package/skills/autoconverge/reference/convergence.md +26 -22
  42. package/skills/autoconverge/reference/headless-safety.md +44 -0
  43. package/skills/autoconverge/reference/multi-pr.md +88 -0
  44. package/skills/autoconverge/reference/self-closing-loop.md +84 -0
  45. package/skills/autoconverge/reference/stop-conditions.md +21 -13
  46. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +87 -27
  47. package/skills/autoconverge/workflow/converge.contract.test.mjs +40 -10
  48. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +179 -47
  49. package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
  50. package/skills/autoconverge/workflow/converge.mjs +284 -94
  51. package/skills/bugteam/CLAUDE.md +1 -1
  52. package/skills/bugteam/CONSTRAINTS.md +3 -3
  53. package/skills/bugteam/PROMPTS.md +8 -19
  54. package/skills/bugteam/SKILL.md +13 -14
  55. package/skills/bugteam/reference/CLAUDE.md +1 -1
  56. package/skills/bugteam/reference/audit-and-teammates.md +2 -2
  57. package/skills/bugteam/reference/copilot-gap-analysis.md +2 -0
  58. package/skills/bugteam/reference/github-pr-reviews.md +56 -38
  59. package/skills/bugteam/reference/team-setup.md +11 -11
  60. package/skills/bugteam/scripts/CLAUDE.md +6 -6
  61. package/skills/bugteam/scripts/README.md +20 -8
  62. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +13 -1463
  63. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +23 -358
  64. package/skills/bugteam/scripts/bugteam_preflight.py +13 -315
  65. package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +2 -3
  66. package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +34 -1053
  67. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +30 -347
  68. package/skills/bugteam/scripts/test_bugteam_preflight.py +30 -305
  69. package/skills/copilot-finding-triage/SKILL.md +80 -28
  70. package/skills/copilot-finding-triage/reference/tier-rubric.md +70 -11
  71. package/skills/copilot-finding-triage/templates/notification.md +8 -3
  72. package/skills/everything-search/SKILL.md +1 -2
  73. package/skills/grokify/SKILL.md +58 -0
  74. package/skills/grokify/templates/handoff-template.md +92 -0
  75. package/skills/orchestrator/SKILL.md +12 -8
  76. package/skills/orchestrator-refresh/SKILL.md +3 -3
  77. package/skills/pr-converge/SKILL.md +132 -233
  78. package/skills/pr-converge/reference/convergence-gates.md +102 -66
  79. package/skills/pr-converge/reference/examples.md +37 -25
  80. package/skills/pr-converge/reference/fix-protocol.md +7 -7
  81. package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -4
  82. package/skills/pr-converge/reference/per-tick.md +64 -41
  83. package/skills/pr-converge/reference/state-schema.md +27 -2
  84. package/skills/pr-converge/scripts/CLAUDE.md +8 -0
  85. package/skills/pr-converge/scripts/README.md +4 -3
  86. package/skills/pr-converge/scripts/_pr_converge_path_setup.py +23 -0
  87. package/skills/pr-converge/scripts/check_convergence.py +189 -645
  88. package/skills/pr-converge/scripts/check_convergence_gates.py +235 -0
  89. package/skills/pr-converge/scripts/check_convergence_thread_gates.py +219 -0
  90. package/skills/pr-converge/scripts/conftest.py +8 -0
  91. package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -0
  92. package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -0
  93. package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +26 -0
  94. package/skills/pr-converge/scripts/test_check_convergence.py +73 -228
  95. package/skills/pr-converge/scripts/test_check_convergence_contract.py +321 -0
  96. package/skills/pr-converge/scripts/test_check_convergence_gates.py +68 -0
  97. package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +75 -0
  98. package/skills/privacy-hygiene/SKILL.md +8 -7
  99. package/skills/reviewer-gates/SKILL.md +26 -8
  100. package/skills/session-log/SKILL.md +11 -12
  101. package/skills/session-tidy/SKILL.md +40 -30
  102. package/skills/skill-builder/CLAUDE.md +13 -10
  103. package/skills/skill-builder/SKILL.md +31 -31
  104. package/skills/skill-builder/references/CLAUDE.md +10 -6
  105. package/skills/skill-builder/references/delegation-map.md +13 -4
  106. package/skills/skill-builder/references/description-field.md +113 -0
  107. package/skills/skill-builder/references/self-audit-checklist.md +21 -4
  108. package/skills/skill-builder/references/skill-modularity.md +113 -0
  109. package/skills/skill-builder/references/skill-types.md +4 -0
  110. package/skills/skill-builder/templates/CLAUDE.md +4 -4
  111. package/skills/skill-builder/templates/gap-analysis.md +25 -1
  112. package/skills/skill-builder/workflows/CLAUDE.md +6 -4
  113. package/skills/skill-builder/workflows/improve-skill.md +17 -16
  114. package/skills/skill-builder/workflows/new-skill.md +34 -10
  115. package/skills/skill-builder/workflows/polish-skill.md +59 -43
  116. package/skills/usage-pause/SKILL.md +10 -14
  117. package/skills/bdd-protocol/CLAUDE.md +0 -26
  118. package/skills/bdd-protocol/SKILL.md +0 -30
  119. package/skills/bdd-protocol/references/CLAUDE.md +0 -10
  120. package/skills/bdd-protocol/references/anti-patterns.md +0 -26
  121. package/skills/bdd-protocol/references/example-mapping.md +0 -23
  122. package/skills/bg-agent/CLAUDE.md +0 -17
  123. package/skills/bg-agent/SKILL.md +0 -69
  124. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py +0 -55
  125. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_fix_hookspath_constants.py +0 -26
  126. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_preflight_constants.py +0 -35
  127. package/skills/caveman/CLAUDE.md +0 -15
  128. package/skills/caveman/SKILL.md +0 -38
  129. package/skills/code/CLAUDE.md +0 -17
  130. package/skills/code/SKILL.md +0 -178
  131. package/skills/deep-research/CLAUDE.md +0 -17
  132. package/skills/deep-research/SKILL.md +0 -80
  133. package/skills/findbugs/CLAUDE.md +0 -20
  134. package/skills/findbugs/SKILL.md +0 -241
  135. package/skills/fixbugs/CLAUDE.md +0 -19
  136. package/skills/fixbugs/SKILL.md +0 -142
  137. package/skills/gh-paginate/CLAUDE.md +0 -18
  138. package/skills/gh-paginate/SKILL.md +0 -84
  139. package/skills/gotcha/CLAUDE.md +0 -33
  140. package/skills/gotcha/SKILL.md +0 -73
  141. package/skills/implement/CLAUDE.md +0 -27
  142. package/skills/implement/SKILL.md +0 -66
  143. package/skills/implement/scripts/CLAUDE.md +0 -22
  144. package/skills/implement/scripts/append_note.py +0 -133
  145. package/skills/implement/scripts/implement_scripts_constants/CLAUDE.md +0 -22
  146. package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
  147. package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +0 -12
  148. package/skills/implement/scripts/test_append_note.py +0 -191
  149. package/skills/log-audit/CLAUDE.md +0 -20
  150. package/skills/log-audit/SKILL.md +0 -66
  151. package/skills/log-audit/reference/CLAUDE.md +0 -9
  152. package/skills/log-audit/reference/charter.md +0 -52
  153. package/skills/log-audit/scripts/CLAUDE.md +0 -27
  154. package/skills/log-audit/scripts/cluster_recurrences.py +0 -261
  155. package/skills/log-audit/scripts/collect_log_window.py +0 -199
  156. package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +0 -12
  157. package/skills/log-audit/scripts/log_audit_constants/__init__.py +0 -0
  158. package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +0 -23
  159. package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +0 -24
  160. package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +0 -49
  161. package/skills/log-audit/scripts/mine_copilot_findings.py +0 -302
  162. package/skills/log-audit/scripts/test_cluster_recurrences.py +0 -160
  163. package/skills/log-audit/scripts/test_collect_log_window.py +0 -111
  164. package/skills/log-audit/scripts/test_mine_copilot_findings.py +0 -126
  165. package/skills/logifix/CLAUDE.md +0 -36
  166. package/skills/logifix/SKILL.md +0 -69
  167. package/skills/logifix/scripts/CLAUDE.md +0 -16
  168. package/skills/logifix/scripts/logifix.ps1 +0 -205
  169. package/skills/monitor-open-prs/CLAUDE.md +0 -34
  170. package/skills/monitor-open-prs/SKILL.md +0 -88
  171. package/skills/monitor-open-prs/scripts/CLAUDE.md +0 -17
  172. package/skills/monitor-open-prs/scripts/discover_open_prs.py +0 -69
  173. package/skills/monitor-open-prs/scripts/test_discover_open_prs.py +0 -149
  174. package/skills/monitor-open-prs/test_skill_contract.py +0 -27
  175. package/skills/post-audit-findings/SKILL.md +0 -80
  176. package/skills/pr-consistency-audit/CLAUDE.md +0 -34
  177. package/skills/pr-consistency-audit/SKILL.md +0 -116
  178. package/skills/pr-consistency-audit/reference/CLAUDE.md +0 -16
  179. package/skills/pr-consistency-audit/reference/detection-rules.md +0 -96
  180. package/skills/pr-consistency-audit/reference/illustrations.md +0 -78
  181. package/skills/pr-fix-protocol/SKILL.md +0 -65
  182. package/skills/pr-review-responder/CLAUDE.md +0 -35
  183. package/skills/pr-review-responder/EXAMPLES.md +0 -590
  184. package/skills/pr-review-responder/PRINCIPLES.md +0 -533
  185. package/skills/pr-review-responder/README.md +0 -168
  186. package/skills/pr-review-responder/SKILL.md +0 -203
  187. package/skills/pr-review-responder/TESTING.md +0 -350
  188. package/skills/pr-scope-resolve/SKILL.md +0 -45
  189. package/skills/pre-compact/CLAUDE.md +0 -24
  190. package/skills/pre-compact/SKILL.md +0 -134
  191. package/skills/qbug/CLAUDE.md +0 -40
  192. package/skills/qbug/SKILL.md +0 -387
  193. package/skills/qbug/test_qbug_skill_audit_schema.py +0 -152
  194. package/skills/qbug/test_qbug_skill_post_fix_audit.py +0 -103
  195. package/skills/refine/CLAUDE.md +0 -44
  196. package/skills/refine/SKILL.md +0 -262
  197. package/skills/refine/templates/CLAUDE.md +0 -17
  198. package/skills/refine/templates/implementation-notes-template.html +0 -56
  199. package/skills/refine/templates/plan-template.md +0 -60
  200. package/skills/research-mode/CLAUDE.md +0 -35
  201. package/skills/research-mode/SKILL.md +0 -53
  202. package/skills/structure-prompt/CLAUDE.md +0 -42
  203. package/skills/structure-prompt/SKILL.md +0 -44
  204. package/skills/structure-prompt/reference/CLAUDE.md +0 -28
  205. package/skills/structure-prompt/reference/adversarial-tuning.md +0 -62
  206. package/skills/structure-prompt/reference/block-classification.md +0 -27
  207. package/skills/structure-prompt/reference/canonical-case.md +0 -48
  208. package/skills/structure-prompt/reference/citation-depth.md +0 -70
  209. package/skills/structure-prompt/reference/cleanup.md +0 -33
  210. package/skills/structure-prompt/reference/constraints.md +0 -33
  211. package/skills/structure-prompt/reference/directives.md +0 -37
  212. package/skills/structure-prompt/reference/examples.md +0 -72
  213. package/skills/structure-prompt/reference/instantiation.md +0 -51
  214. package/skills/structure-prompt/reference/output-contract.md +0 -72
  215. package/skills/structure-prompt/reference/per-category.md +0 -23
  216. package/skills/structure-prompt/reference/persona.md +0 -38
  217. package/skills/structure-prompt/reference/research.md +0 -33
  218. package/skills/structure-prompt/reference/structure.md +0 -28
  219. package/skills/verified-build/CLAUDE.md +0 -33
  220. package/skills/verified-build/SKILL.md +0 -33
@@ -1,328 +1,26 @@
1
+ """Skill-path entry for bugteam preflight.
2
+
3
+ Delegates to the package shared home:
4
+ ``_shared/pr-loop/scripts/preflight.py``.
5
+ """
6
+
1
7
  from __future__ import annotations
2
8
 
3
- import argparse
4
- import os
5
- import subprocess
6
9
  import sys
7
10
  from pathlib import Path
8
11
 
9
- _bugteam_scripts_directory = str(Path(__file__).absolute().parent)
10
- while _bugteam_scripts_directory in sys.path:
11
- sys.path.remove(_bugteam_scripts_directory)
12
- if _bugteam_scripts_directory not in sys.path:
13
- sys.path.insert(0, _bugteam_scripts_directory)
14
-
15
- from bugteam_scripts_constants.bugteam_preflight_constants import (
16
- ALL_DISCOVERY_IGNORE_DIRECTORIES,
17
- ALL_GIT_CONFIG_HOOKS_PATH_ARGUMENTS,
18
- ALL_PRE_COMMIT_ARGUMENTS,
19
- BUGTEAM_PREFLIGHT_PREFIX,
20
- BUGTEAM_PREFLIGHT_SKIP_ENV_VAR_NAME,
21
- ENFORCEMENT_ABSENT_MESSAGE,
22
- EXIT_CODE_HOOKS_PATH_CHECK_FAILED,
23
- EXPECTED_HOOKS_PATH_SUFFIX,
24
- GIT_DIRECTORY_NAME,
25
- PRE_COMMIT_CONFIG_FILENAME,
26
- PYPROJECT_FILENAME,
27
- PYPROJECT_PYTEST_SECTION_PREFIX,
28
- PYTEST_EXIT_CODE_NO_TESTS_COLLECTED,
29
- PYTEST_INI_FILENAME,
30
- )
31
-
32
12
  _shared_pr_loop_scripts_directory = (
33
- Path(__file__).absolute().parent
34
- / ".." / ".." / ".." / "_shared" / "pr-loop" / "scripts"
35
- ).absolute()
13
+ Path(__file__).resolve().parents[3] / "_shared" / "pr-loop" / "scripts"
14
+ )
36
15
  if str(_shared_pr_loop_scripts_directory) not in sys.path:
37
16
  sys.path.insert(0, str(_shared_pr_loop_scripts_directory))
38
17
 
39
- from preflight_self_heal import silently_clear_stale_local_hooks_path_override # noqa: E402
40
- from reviews_disabled import ( # noqa: E402
41
- CLAUDE_REVIEWS_DISABLED_BUGTEAM_TOKEN,
42
- CLAUDE_REVIEWS_DISABLED_ENV_VAR_NAME,
43
- EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV,
44
- is_bugteam_disabled_via_env,
18
+ from preflight import ( # noqa: E402
19
+ EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV, # noqa: F401
20
+ has_pytest_configuration, # noqa: F401
21
+ main,
22
+ verify_git_hooks_path, # noqa: F401
45
23
  )
46
24
 
47
-
48
- def verify_git_hooks_path(repository_root: Path | None) -> int:
49
- """Check that core.hooksPath resolves to the claude-dev-env git-hooks directory.
50
-
51
- Silently clears any stale, non-canonical local-scope core.hooksPath
52
- override before querying the effective config, so a worktree-seeded local
53
- entry cannot shadow a correctly configured global setting. When
54
- *repository_root* is provided, queries the effective config for that
55
- repository (``git -C <root> config --get``). When a canonical global
56
- ``core.hooksPath`` is already configured, the preceding self-heal step
57
- clears non-canonical local-scope entries, so repo-level overrides such
58
- as Husky or lefthook at local scope are silently removed in favor of
59
- the canonical global; when the global is unset or non-canonical, the
60
- self-heal stands down and the ``--get`` query still surfaces those
61
- overrides through the failure path. Falls back to the current working
62
- directory's effective config when *repository_root* is None.
63
-
64
- Args:
65
- repository_root: Optional repository root to check. When None, uses
66
- the current working directory's effective config.
67
-
68
- Returns:
69
- Zero when the configured path ends with the expected hooks suffix.
70
- Non-zero and prints a correction message when unset or pointing elsewhere.
71
- """
72
- silently_clear_stale_local_hooks_path_override(
73
- repository_root, EXPECTED_HOOKS_PATH_SUFFIX
74
- )
75
- git_command: list[str] = ["git"]
76
- if repository_root is not None:
77
- git_command.extend(["-C", str(repository_root)])
78
- git_command.extend(list(ALL_GIT_CONFIG_HOOKS_PATH_ARGUMENTS))
79
- try:
80
- query_completed_process = subprocess.run(
81
- git_command,
82
- capture_output=True,
83
- text=True,
84
- encoding="utf-8",
85
- errors="replace",
86
- check=False,
87
- )
88
- except FileNotFoundError:
89
- print(
90
- f"{BUGTEAM_PREFLIGHT_PREFIX}git is not installed or not available on PATH.\n"
91
- f"{ENFORCEMENT_ABSENT_MESSAGE}",
92
- file=sys.stderr,
93
- )
94
- return EXIT_CODE_HOOKS_PATH_CHECK_FAILED
95
- except OSError as os_error:
96
- print(
97
- f"{BUGTEAM_PREFLIGHT_PREFIX}failed to run git: {os_error}\n"
98
- f"{ENFORCEMENT_ABSENT_MESSAGE}",
99
- file=sys.stderr,
100
- )
101
- return EXIT_CODE_HOOKS_PATH_CHECK_FAILED
102
- if query_completed_process.returncode != 0:
103
- print(
104
- f"{BUGTEAM_PREFLIGHT_PREFIX}{ENFORCEMENT_ABSENT_MESSAGE}",
105
- file=sys.stderr,
106
- )
107
- return EXIT_CODE_HOOKS_PATH_CHECK_FAILED
108
- configured_path = query_completed_process.stdout.strip().replace("\\", "/").rstrip("/")
109
- if not configured_path.endswith(EXPECTED_HOOKS_PATH_SUFFIX):
110
- print(
111
- f"{BUGTEAM_PREFLIGHT_PREFIX}core.hooksPath is '{configured_path}' — "
112
- f"expected path ending in '{EXPECTED_HOOKS_PATH_SUFFIX}'.\n"
113
- f"{ENFORCEMENT_ABSENT_MESSAGE}",
114
- file=sys.stderr,
115
- )
116
- return EXIT_CODE_HOOKS_PATH_CHECK_FAILED
117
- return 0
118
-
119
-
120
- def find_repository_root(start: Path) -> Path:
121
- """Find the repository root by walking up from the starting directory.
122
-
123
- Searches for a ``.git`` directory or file in parent directories. Falls
124
- back to the nearest ancestor containing ``pytest.ini`` when no git
125
- repository is found.
126
-
127
- Args:
128
- start: The directory to start searching from.
129
-
130
- Returns:
131
- The repository root path, or *start* when no repository is found.
132
- """
133
- resolved = start.resolve()
134
- candidates = [resolved, *resolved.parents]
135
- for each_candidate in candidates:
136
- if (each_candidate / GIT_DIRECTORY_NAME).is_dir() or (each_candidate / GIT_DIRECTORY_NAME).is_file():
137
- return each_candidate
138
- for each_candidate in candidates:
139
- if (each_candidate / PYTEST_INI_FILENAME).is_file():
140
- return each_candidate
141
- return resolved
142
-
143
-
144
- def has_pytest_configuration(root: Path) -> bool:
145
- """Check whether a directory has pytest configuration available.
146
-
147
- Checks for ``pytest.ini`` directly, then falls back to searching for
148
- ``[tool.pytest]`` in ``pyproject.toml``.
149
-
150
- Args:
151
- root: The directory to check for pytest configuration.
152
-
153
- Returns:
154
- True when pytest configuration is found in either location.
155
- """
156
- if (root / PYTEST_INI_FILENAME).is_file():
157
- return True
158
- pyproject = root / PYPROJECT_FILENAME
159
- if not pyproject.is_file():
160
- return False
161
- text = pyproject.read_text(encoding="utf-8", errors="replace")
162
- return PYPROJECT_PYTEST_SECTION_PREFIX in text
163
-
164
-
165
- def has_discoverable_tests(root: Path) -> bool:
166
- """Check whether the directory tree contains discoverable test files.
167
-
168
- Searches for files matching ``test_*.py`` and ``*_test.py`` patterns,
169
- skipping directories in the configured ignore list (virtual environments,
170
- node_modules).
171
-
172
- Args:
173
- root: The directory tree root to search.
174
-
175
- Returns:
176
- True when at least one test file is found outside ignored directories.
177
- """
178
- for each_path in root.rglob("test_*.py"):
179
- if any(part_dir in ALL_DISCOVERY_IGNORE_DIRECTORIES for part_dir in each_path.parts):
180
- continue
181
- return True
182
- for each_path in root.rglob("*_test.py"):
183
- if any(part_dir in ALL_DISCOVERY_IGNORE_DIRECTORIES for part_dir in each_path.parts):
184
- continue
185
- return True
186
- return False
187
-
188
-
189
- def _pytest_exit_code_no_tests_collected() -> int:
190
- return PYTEST_EXIT_CODE_NO_TESTS_COLLECTED
191
-
192
-
193
- def run_pytest(repository_root: Path, is_verbose: bool) -> int:
194
- """Run pytest in the repository root and return the exit code.
195
-
196
- Treats the "no tests collected" exit code as a pass (exit 0).
197
-
198
- Args:
199
- repository_root: The repository root for running pytest.
200
- is_verbose: When True, pass no -q flag (shows individual test names).
201
-
202
- Returns:
203
- The pytest exit code, or 0 when no tests were collected.
204
- """
205
- command = [sys.executable, "-m", "pytest"]
206
- if not is_verbose:
207
- command.append("-q")
208
- completed = subprocess.run(
209
- command,
210
- cwd=str(repository_root),
211
- check=False,
212
- )
213
- if completed.returncode == _pytest_exit_code_no_tests_collected():
214
- return 0
215
- return completed.returncode
216
-
217
-
218
- def run_pre_commit(repository_root: Path) -> int:
219
- """Run pre-commit on all files and return the exit code.
220
-
221
- Args:
222
- repository_root: The repository root for running pre-commit.
223
-
224
- Returns:
225
- The pre-commit exit code (0 on success, non-zero on failure).
226
- """
227
- completed = subprocess.run(
228
- ALL_PRE_COMMIT_ARGUMENTS,
229
- cwd=str(repository_root),
230
- check=False,
231
- )
232
- return completed.returncode
233
-
234
-
235
- def parse_arguments(all_argv: list[str]) -> argparse.Namespace:
236
- """Parse command-line arguments for the preflight script.
237
-
238
- Args:
239
- all_argv: Command-line argument list.
240
-
241
- Returns:
242
- Parsed namespace with repo_root, no_pytest, pre_commit, and verbose.
243
- """
244
- parser = argparse.ArgumentParser(
245
- description="Run local checks before /bugteam (pytest, optional pre-commit).",
246
- )
247
- parser.add_argument(
248
- "--repo-root",
249
- type=Path,
250
- default=None,
251
- help="Repository root (default: discover from cwd).",
252
- )
253
- parser.add_argument(
254
- "--no-pytest",
255
- action="store_true",
256
- help="Skip pytest.",
257
- )
258
- parser.add_argument(
259
- "--pre-commit",
260
- action="store_true",
261
- help="Run pre-commit when .pre-commit-config.yaml exists.",
262
- )
263
- parser.add_argument(
264
- "-v",
265
- "--verbose",
266
- action="store_true",
267
- help="Verbose pytest output.",
268
- )
269
- return parser.parse_args(all_argv)
270
-
271
-
272
- def main(all_argv: list[str] | None = None) -> int:
273
- """Run the bugteam preflight checks (pytest, optional pre-commit).
274
-
275
- Args:
276
- all_argv: Command-line arguments to parse. Pass None to use sys.argv.
277
-
278
- Returns:
279
- Zero on success, non-zero exit code on failure.
280
- """
281
- arguments = parse_arguments(sys.argv[1:] if all_argv is None else all_argv)
282
- if os.environ.get(BUGTEAM_PREFLIGHT_SKIP_ENV_VAR_NAME, "").strip() == "1":
283
- print(f"{BUGTEAM_PREFLIGHT_PREFIX}skipped (BUGTEAM_PREFLIGHT_SKIP=1).", file=sys.stderr)
284
- return 0
285
- reviews_disabled_env_var_name = CLAUDE_REVIEWS_DISABLED_ENV_VAR_NAME
286
- reviews_disabled_bugteam_token = CLAUDE_REVIEWS_DISABLED_BUGTEAM_TOKEN
287
- disabled_via_env_exit_code = EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV
288
- if is_bugteam_disabled_via_env():
289
- print(
290
- f"{BUGTEAM_PREFLIGHT_PREFIX}halted "
291
- f"({reviews_disabled_env_var_name} contains "
292
- f"'{reviews_disabled_bugteam_token}').",
293
- file=sys.stderr,
294
- )
295
- return disabled_via_env_exit_code
296
- start = Path.cwd()
297
- resolved_repository_root: Path = (
298
- arguments.repo_root.resolve()
299
- if arguments.repo_root is not None
300
- else find_repository_root(start)
301
- )
302
- hooks_path_exit_code = verify_git_hooks_path(resolved_repository_root)
303
- if hooks_path_exit_code != 0:
304
- return hooks_path_exit_code
305
- if not arguments.no_pytest and has_pytest_configuration(resolved_repository_root):
306
- if not has_discoverable_tests(resolved_repository_root):
307
- print(
308
- f"{BUGTEAM_PREFLIGHT_PREFIX}pytest configured but no tests found; skipping pytest.",
309
- file=sys.stderr,
310
- )
311
- else:
312
- exit_code = run_pytest(resolved_repository_root, arguments.verbose)
313
- if exit_code != 0:
314
- return exit_code
315
- elif not arguments.no_pytest:
316
- print(
317
- f"{BUGTEAM_PREFLIGHT_PREFIX}no pytest configuration found; skipping pytest.",
318
- file=sys.stderr,
319
- )
320
- if arguments.pre_commit and (resolved_repository_root / PRE_COMMIT_CONFIG_FILENAME).is_file():
321
- exit_code = run_pre_commit(resolved_repository_root)
322
- if exit_code != 0:
323
- return exit_code
324
- return 0
325
-
326
-
327
25
  if __name__ == "__main__":
328
26
  raise SystemExit(main(sys.argv[1:]))
@@ -6,14 +6,13 @@ Python package of named constants imported by the bugteam scripts. Each module h
6
6
 
7
7
  | File | Constants for |
8
8
  |---|---|
9
- | `bugteam_preflight_constants.py` | `bugteam_preflight.py` — env var name, hooks path suffix, exit codes, ignore dirs, argument tuples, config filenames. |
10
- | `bugteam_code_rules_gate_constants.py` | `bugteam_code_rules_gate.py` — gate-related path and exit-code constants. |
11
- | `bugteam_fix_hookspath_constants.py` | `bugteam_fix_hookspath.py` — canonical hooks path, remediation message strings. |
12
9
  | `probe_code_rules_enforcer_check_constants.py` | `probe_code_rules_enforcer_check.py` — enforcer module path and function name constants. |
13
10
  | `reflow_skill_md_constants.py` | `reflow_skill_md.py` — line-length and formatting constants. |
14
11
  | `windows_safe_rmtree_constants.py` | `windows_safe_rmtree.py` — retry count and wait constants. |
15
12
  | `__init__.py` | Empty package marker. |
16
13
 
14
+ Preflight, fix_hookspath, and code_rules_gate constants live under package shared `pr_loop_shared_constants` with the shared implementations.
15
+
17
16
  ## Convention
18
17
 
19
18
  Scripts import from this package at module scope. No constant is defined inline in a script body — the hook enforces this at write time.