claude-dev-env 1.93.1 → 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 (215) 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/fix_hookspath.py +47 -12
  7. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -69
  8. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -374
  9. package/_shared/pr-loop/state-schema.md +3 -22
  10. package/bin/CLAUDE.md +1 -1
  11. package/bin/install.mjs +2 -7
  12. package/bin/install.test.mjs +10 -4
  13. package/hooks/blocking/CLAUDE.md +2 -0
  14. package/hooks/blocking/pii_commit_command.py +398 -0
  15. package/hooks/blocking/pii_payload_scan.py +133 -0
  16. package/hooks/blocking/pii_prevention_blocker.py +151 -449
  17. package/hooks/blocking/test_pii_prevention_blocker.py +299 -0
  18. package/hooks/blocking/test_verdict_directory_write_blocker.py +42 -7
  19. package/hooks/hooks_constants/CLAUDE.md +2 -2
  20. package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -2
  21. package/hooks/hooks_constants/local_identity.py +65 -15
  22. package/hooks/hooks_constants/pii_prevention_constants.py +16 -0
  23. package/hooks/hooks_constants/pr_description_enforcer_constants.py +1 -1
  24. package/hooks/hooks_constants/test_local_identity.py +68 -0
  25. package/package.json +1 -1
  26. package/scripts/CLAUDE.md +10 -1
  27. package/scripts/Get-SessionAccount.ps1 +210 -0
  28. package/scripts/tests/CLAUDE.md +15 -1
  29. package/scripts/tests/Get-SessionAccount.Tests.ps1 +350 -0
  30. package/skills/CLAUDE.md +3 -25
  31. package/skills/anthropic-plan/SKILL.md +1 -13
  32. package/skills/autoconverge/CLAUDE.md +5 -2
  33. package/skills/autoconverge/SKILL.md +112 -387
  34. package/skills/autoconverge/reference/CLAUDE.md +4 -1
  35. package/skills/autoconverge/reference/closing-report.md +9 -2
  36. package/skills/autoconverge/reference/convergence.md +26 -22
  37. package/skills/autoconverge/reference/headless-safety.md +44 -0
  38. package/skills/autoconverge/reference/multi-pr.md +88 -0
  39. package/skills/autoconverge/reference/self-closing-loop.md +84 -0
  40. package/skills/autoconverge/reference/stop-conditions.md +21 -13
  41. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +87 -27
  42. package/skills/autoconverge/workflow/converge.contract.test.mjs +40 -10
  43. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +179 -47
  44. package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
  45. package/skills/autoconverge/workflow/converge.mjs +284 -94
  46. package/skills/bugteam/CLAUDE.md +1 -1
  47. package/skills/bugteam/CONSTRAINTS.md +3 -3
  48. package/skills/bugteam/PROMPTS.md +8 -19
  49. package/skills/bugteam/SKILL.md +13 -14
  50. package/skills/bugteam/reference/CLAUDE.md +1 -1
  51. package/skills/bugteam/reference/audit-and-teammates.md +2 -2
  52. package/skills/bugteam/reference/copilot-gap-analysis.md +2 -0
  53. package/skills/bugteam/reference/github-pr-reviews.md +56 -38
  54. package/skills/bugteam/reference/team-setup.md +11 -11
  55. package/skills/bugteam/scripts/CLAUDE.md +6 -6
  56. package/skills/bugteam/scripts/README.md +20 -8
  57. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +13 -1463
  58. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +23 -358
  59. package/skills/bugteam/scripts/bugteam_preflight.py +13 -315
  60. package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +2 -3
  61. package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +34 -1053
  62. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +30 -347
  63. package/skills/bugteam/scripts/test_bugteam_preflight.py +30 -305
  64. package/skills/copilot-finding-triage/SKILL.md +80 -28
  65. package/skills/copilot-finding-triage/reference/tier-rubric.md +70 -11
  66. package/skills/copilot-finding-triage/templates/notification.md +8 -3
  67. package/skills/everything-search/SKILL.md +1 -2
  68. package/skills/grokify/SKILL.md +58 -0
  69. package/skills/grokify/templates/handoff-template.md +92 -0
  70. package/skills/orchestrator/SKILL.md +12 -8
  71. package/skills/orchestrator-refresh/SKILL.md +3 -3
  72. package/skills/pr-converge/SKILL.md +132 -233
  73. package/skills/pr-converge/reference/convergence-gates.md +102 -66
  74. package/skills/pr-converge/reference/examples.md +37 -25
  75. package/skills/pr-converge/reference/fix-protocol.md +7 -7
  76. package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -4
  77. package/skills/pr-converge/reference/per-tick.md +64 -41
  78. package/skills/pr-converge/reference/state-schema.md +27 -2
  79. package/skills/pr-converge/scripts/CLAUDE.md +8 -0
  80. package/skills/pr-converge/scripts/README.md +4 -3
  81. package/skills/pr-converge/scripts/_pr_converge_path_setup.py +23 -0
  82. package/skills/pr-converge/scripts/check_convergence.py +189 -645
  83. package/skills/pr-converge/scripts/check_convergence_gates.py +235 -0
  84. package/skills/pr-converge/scripts/check_convergence_thread_gates.py +219 -0
  85. package/skills/pr-converge/scripts/conftest.py +8 -0
  86. package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -0
  87. package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -0
  88. package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +26 -0
  89. package/skills/pr-converge/scripts/test_check_convergence.py +73 -228
  90. package/skills/pr-converge/scripts/test_check_convergence_contract.py +321 -0
  91. package/skills/pr-converge/scripts/test_check_convergence_gates.py +68 -0
  92. package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +75 -0
  93. package/skills/privacy-hygiene/SKILL.md +4 -3
  94. package/skills/reviewer-gates/SKILL.md +26 -8
  95. package/skills/session-log/SKILL.md +11 -12
  96. package/skills/session-tidy/SKILL.md +40 -30
  97. package/skills/skill-builder/CLAUDE.md +13 -10
  98. package/skills/skill-builder/SKILL.md +31 -31
  99. package/skills/skill-builder/references/CLAUDE.md +10 -6
  100. package/skills/skill-builder/references/delegation-map.md +13 -4
  101. package/skills/skill-builder/references/description-field.md +113 -0
  102. package/skills/skill-builder/references/self-audit-checklist.md +21 -4
  103. package/skills/skill-builder/references/skill-modularity.md +113 -0
  104. package/skills/skill-builder/references/skill-types.md +4 -0
  105. package/skills/skill-builder/templates/CLAUDE.md +4 -4
  106. package/skills/skill-builder/templates/gap-analysis.md +25 -1
  107. package/skills/skill-builder/workflows/CLAUDE.md +6 -4
  108. package/skills/skill-builder/workflows/improve-skill.md +17 -16
  109. package/skills/skill-builder/workflows/new-skill.md +34 -10
  110. package/skills/skill-builder/workflows/polish-skill.md +59 -43
  111. package/skills/usage-pause/SKILL.md +10 -14
  112. package/skills/bdd-protocol/CLAUDE.md +0 -26
  113. package/skills/bdd-protocol/SKILL.md +0 -30
  114. package/skills/bdd-protocol/references/CLAUDE.md +0 -10
  115. package/skills/bdd-protocol/references/anti-patterns.md +0 -26
  116. package/skills/bdd-protocol/references/example-mapping.md +0 -23
  117. package/skills/bg-agent/CLAUDE.md +0 -17
  118. package/skills/bg-agent/SKILL.md +0 -69
  119. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py +0 -55
  120. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_fix_hookspath_constants.py +0 -26
  121. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_preflight_constants.py +0 -35
  122. package/skills/caveman/CLAUDE.md +0 -15
  123. package/skills/caveman/SKILL.md +0 -38
  124. package/skills/code/CLAUDE.md +0 -17
  125. package/skills/code/SKILL.md +0 -178
  126. package/skills/deep-research/CLAUDE.md +0 -17
  127. package/skills/deep-research/SKILL.md +0 -80
  128. package/skills/findbugs/CLAUDE.md +0 -20
  129. package/skills/findbugs/SKILL.md +0 -241
  130. package/skills/fixbugs/CLAUDE.md +0 -19
  131. package/skills/fixbugs/SKILL.md +0 -142
  132. package/skills/gh-paginate/CLAUDE.md +0 -18
  133. package/skills/gh-paginate/SKILL.md +0 -84
  134. package/skills/gotcha/CLAUDE.md +0 -33
  135. package/skills/gotcha/SKILL.md +0 -73
  136. package/skills/implement/CLAUDE.md +0 -27
  137. package/skills/implement/SKILL.md +0 -66
  138. package/skills/implement/scripts/CLAUDE.md +0 -22
  139. package/skills/implement/scripts/append_note.py +0 -133
  140. package/skills/implement/scripts/implement_scripts_constants/CLAUDE.md +0 -22
  141. package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
  142. package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +0 -12
  143. package/skills/implement/scripts/test_append_note.py +0 -191
  144. package/skills/log-audit/CLAUDE.md +0 -20
  145. package/skills/log-audit/SKILL.md +0 -66
  146. package/skills/log-audit/reference/CLAUDE.md +0 -9
  147. package/skills/log-audit/reference/charter.md +0 -52
  148. package/skills/log-audit/scripts/CLAUDE.md +0 -27
  149. package/skills/log-audit/scripts/cluster_recurrences.py +0 -261
  150. package/skills/log-audit/scripts/collect_log_window.py +0 -199
  151. package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +0 -12
  152. package/skills/log-audit/scripts/log_audit_constants/__init__.py +0 -0
  153. package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +0 -23
  154. package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +0 -24
  155. package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +0 -49
  156. package/skills/log-audit/scripts/mine_copilot_findings.py +0 -302
  157. package/skills/log-audit/scripts/test_cluster_recurrences.py +0 -160
  158. package/skills/log-audit/scripts/test_collect_log_window.py +0 -111
  159. package/skills/log-audit/scripts/test_mine_copilot_findings.py +0 -126
  160. package/skills/logifix/CLAUDE.md +0 -36
  161. package/skills/logifix/SKILL.md +0 -69
  162. package/skills/logifix/scripts/CLAUDE.md +0 -16
  163. package/skills/logifix/scripts/logifix.ps1 +0 -205
  164. package/skills/monitor-open-prs/CLAUDE.md +0 -34
  165. package/skills/monitor-open-prs/SKILL.md +0 -88
  166. package/skills/monitor-open-prs/scripts/CLAUDE.md +0 -17
  167. package/skills/monitor-open-prs/scripts/discover_open_prs.py +0 -69
  168. package/skills/monitor-open-prs/scripts/test_discover_open_prs.py +0 -149
  169. package/skills/monitor-open-prs/test_skill_contract.py +0 -27
  170. package/skills/post-audit-findings/SKILL.md +0 -80
  171. package/skills/pr-consistency-audit/CLAUDE.md +0 -34
  172. package/skills/pr-consistency-audit/SKILL.md +0 -116
  173. package/skills/pr-consistency-audit/reference/CLAUDE.md +0 -16
  174. package/skills/pr-consistency-audit/reference/detection-rules.md +0 -96
  175. package/skills/pr-consistency-audit/reference/illustrations.md +0 -78
  176. package/skills/pr-fix-protocol/SKILL.md +0 -65
  177. package/skills/pr-review-responder/CLAUDE.md +0 -35
  178. package/skills/pr-review-responder/EXAMPLES.md +0 -590
  179. package/skills/pr-review-responder/PRINCIPLES.md +0 -533
  180. package/skills/pr-review-responder/README.md +0 -168
  181. package/skills/pr-review-responder/SKILL.md +0 -203
  182. package/skills/pr-review-responder/TESTING.md +0 -350
  183. package/skills/pr-scope-resolve/SKILL.md +0 -45
  184. package/skills/pre-compact/CLAUDE.md +0 -24
  185. package/skills/pre-compact/SKILL.md +0 -134
  186. package/skills/qbug/CLAUDE.md +0 -40
  187. package/skills/qbug/SKILL.md +0 -387
  188. package/skills/qbug/test_qbug_skill_audit_schema.py +0 -152
  189. package/skills/qbug/test_qbug_skill_post_fix_audit.py +0 -103
  190. package/skills/refine/CLAUDE.md +0 -44
  191. package/skills/refine/SKILL.md +0 -262
  192. package/skills/refine/templates/CLAUDE.md +0 -17
  193. package/skills/refine/templates/implementation-notes-template.html +0 -56
  194. package/skills/refine/templates/plan-template.md +0 -60
  195. package/skills/research-mode/CLAUDE.md +0 -35
  196. package/skills/research-mode/SKILL.md +0 -53
  197. package/skills/structure-prompt/CLAUDE.md +0 -42
  198. package/skills/structure-prompt/SKILL.md +0 -44
  199. package/skills/structure-prompt/reference/CLAUDE.md +0 -28
  200. package/skills/structure-prompt/reference/adversarial-tuning.md +0 -62
  201. package/skills/structure-prompt/reference/block-classification.md +0 -27
  202. package/skills/structure-prompt/reference/canonical-case.md +0 -48
  203. package/skills/structure-prompt/reference/citation-depth.md +0 -70
  204. package/skills/structure-prompt/reference/cleanup.md +0 -33
  205. package/skills/structure-prompt/reference/constraints.md +0 -33
  206. package/skills/structure-prompt/reference/directives.md +0 -37
  207. package/skills/structure-prompt/reference/examples.md +0 -72
  208. package/skills/structure-prompt/reference/instantiation.md +0 -51
  209. package/skills/structure-prompt/reference/output-contract.md +0 -72
  210. package/skills/structure-prompt/reference/per-category.md +0 -23
  211. package/skills/structure-prompt/reference/persona.md +0 -38
  212. package/skills/structure-prompt/reference/research.md +0 -33
  213. package/skills/structure-prompt/reference/structure.md +0 -28
  214. package/skills/verified-build/CLAUDE.md +0 -33
  215. package/skills/verified-build/SKILL.md +0 -33
@@ -8,6 +8,7 @@ Runtime documents and scripts shared by every PR-loop skill. Changes here affect
8
8
  |---|---|
9
9
  | `audit-contract.md` | Canonical finding schema (Shape A / Shape B) and loop contract; defines the JSON shapes every audit skill must emit |
10
10
  | `audit-reply-template.md` | Canonical reply skeleton Claude posts to each unresolved review thread; single source of truth for reply structure |
11
+ | `post-audit-thread-contract.md` | Single source of truth for the `post_audit_thread.py` invocation string, exit-code table, and per-caller policy (bugteam exit 2 = hard blocker; autoconverge clean-audit failed post = recorded bypass) |
11
12
  | `fix-protocol.md` | Ordered sequence a fix lens follows: read, capture SHA, TDD, apply, validate, self-audit, commit, push, reply + resolve |
12
13
  | `gh-payloads.md` | How to build GitHub review and reply payloads via MCP tools; describes the one-review-per-loop pattern |
13
14
  | `state-schema.md` | Fields each PR-loop workflow tracks across iterations; documents common fields and per-skill extensions |
@@ -120,7 +120,7 @@ Sequence:
120
120
  2. Apply edits.
121
121
  3. Run `py_compile` (or language-equivalent) on each modified file.
122
122
  4. Compute `fix_diff` against pre-fix contents for the modified set.
123
- 5. Run `bugteam_code_rules_gate.py` with explicit paths for every modified file.
123
+ 5. Run `_shared/pr-loop/scripts/code_rules_gate.py` with explicit paths for every modified file.
124
124
  6. Spawn a scoped audit of `fix_diff` with full A–P rigor, Shape A/B contract, adversarial pass, AND Haiku secondary in parallel (paranoid mode on post-fix).
125
125
  7. Read the previous loop's outcome XML (`<worktree_path>/.bugteam-pr<N>-loop<L-1>.outcomes.xml`) and obtain its total finding count. If this is the first loop (L <= 1) or the file does not exist, skip this comparison. Compute the post-fix total: previous total minus bugs fixed in this round plus new violations found in the post-fix audit (step 6). If the post-fix total exceeds the previous total, flag all new findings as same-loop fix-targets and revise. An increase in total findings across loop transitions is a regression.
126
126
  8. Any new findings become same-loop fix-targets. Internal iteration count increments by one.
@@ -55,9 +55,9 @@ user-facing output (chat reply to the user) rather than in the PR review body.
55
55
 
56
56
  ## Review POST failure
57
57
 
58
- There is no fallback path. `post_audit_thread.py` exits `2` on retry
59
- exhaustion (four non-2xx responses across the built-in `1s / 4s / 16s` backoff)
60
- and the orchestrator halts with `error: post_audit_thread retry exhausted`.
58
+ There is no fallback payload to build. On retry exhaustion
59
+ `post_audit_thread.py` exits `2` and the orchestrator halts see
60
+ [`./post-audit-thread-contract.md`](./post-audit-thread-contract.md) § Exit codes.
61
61
  A hard blocker on the audit-posting path is a halt condition, not a degraded
62
62
  flat-issue-comment fall-through.
63
63
 
@@ -0,0 +1,51 @@
1
+ # post_audit_thread.py invocation contract
2
+
3
+ Single source of truth for how every PR-loop caller runs
4
+ [`scripts/post_audit_thread.py`](scripts/post_audit_thread.py) to post one
5
+ audit review per pass. The script posts an `APPROVE` review on `CLEAN` (an
6
+ empty findings list, zero inline comments) and a `REQUEST_CHANGES` review on
7
+ `DIRTY` (one inline anchored comment per finding). It reads its body skeleton
8
+ from [`audit-reply-template.md`](audit-reply-template.md) at runtime.
9
+
10
+ ## Invocation
11
+
12
+ ```
13
+ python "<pr-loop-scripts>/post_audit_thread.py" \
14
+ --skill <caller> \
15
+ --owner <owner> \
16
+ --repo <repo> \
17
+ --pr-number <N> \
18
+ --commit <head_sha> \
19
+ --state <CLEAN|DIRTY> \
20
+ --findings-json <path>
21
+ ```
22
+
23
+ Capture `<head_sha>` with `git rev-parse HEAD` in the caller's cwd right before
24
+ the call, so the review attaches to the commit the audit scoped. `--findings-json`
25
+ points to a JSON file whose root is a list; on `CLEAN` the list is empty (`[]`).
26
+
27
+ ## Exit codes
28
+
29
+ The script retries on its own with 1s / 4s / 16s backoff across four tries (one
30
+ plus three retries). It then exits:
31
+
32
+ | Exit | Meaning |
33
+ |---|---|
34
+ | `0` | Review posted. The new review's `html_url` is on stdout. |
35
+ | `1` | User input error: bad arguments, malformed findings JSON, or a missing template. |
36
+ | `2` | Retry exhaustion: every try failed. |
37
+
38
+ ## Per-caller policy
39
+
40
+ The same exit codes carry different weight per caller:
41
+
42
+ - **bugteam** — an exit `2` is a hard blocker. The lead exits with
43
+ `error: post_audit_thread retry exhausted`, and does not retry or fall back to
44
+ a flat issue comment.
45
+ - **autoconverge clean-audit** — any post that does not land (a denied post, an
46
+ error, or an agent that never ran) is a recorded bypass, not a blocker. Every
47
+ review lens already cleared the HEAD, so the CLEAN post is a record-keeping
48
+ artifact. The run records a bypass note, sets the convergence check's
49
+ `--bugteam-post-blocked` flag, and proceeds to the terminal Bugbot gate. Full
50
+ policy: [`../../skills/autoconverge/reference/stop-conditions.md`](../../skills/autoconverge/reference/stop-conditions.md)
51
+ § "Clean-audit post bypassed".
@@ -9,11 +9,11 @@ Runnable helpers used by **bugteam**, **qbug**, **pr-converge**, and related ski
9
9
  | `preflight.py` | Local checks before a PR-loop run (pytest discovery, optional pre-commit, hooksPath sanity). |
10
10
  | `reviewer_availability.py` | Unified availability check for Copilot and Bugbot; reuses `copilot_quota.py` and `reviews_disabled.py` and exits 0 when the named `--reviewer` may be spawned. |
11
11
  | `code_rules_gate.py` | CODE_RULES gate over PR-scoped diffs (`--base`, staged-only, path filters). |
12
- | `fix_hookspath.py` | Repair `core.hooksPath` when it does not point at the packaged git-hooks tree. |
12
+ | `fix_hookspath.py` | Repair `core.hooksPath` when it does not point at the packaged git-hooks tree. Single home for unexpected non-empty git stderr (hard-fail `RuntimeError`); skill wrappers may call it later. |
13
13
  | `grant_project_claude_permissions.py` / `revoke_project_claude_permissions.py` | Claude Code permission JSON helpers used during publish-style flows. |
14
14
  | `_claude_permissions_common.py` | Shared implementation for the permission scripts. |
15
15
 
16
- Configuration lives under `config/` next to these scripts (for example `preflight_constants.py`, `code_rules_gate_constants.py`).
16
+ Configuration lives under `pr_loop_shared_constants/` next to these scripts (for example `preflight_constants.py`, `code_rules_gate_constants.py`, `fix_hookspath_constants.py`).
17
17
 
18
18
  ## Gate semantics
19
19
 
@@ -53,6 +53,9 @@ def list_local_core_hooks_path_values(
53
53
  Returns:
54
54
  Non-empty stripped values from ``git config --local --get-all``, or
55
55
  an empty list when no values are configured.
56
+
57
+ Raises:
58
+ RuntimeError: When git exits non-zero with a non-empty stderr diagnostic.
56
59
  """
57
60
  git_command = [
58
61
  "git",
@@ -75,11 +78,9 @@ def list_local_core_hooks_path_values(
75
78
  if completed_process.returncode != 0:
76
79
  diagnostic_stderr = completed_process.stderr.strip()
77
80
  if diagnostic_stderr:
78
- print(
79
- "fix_hookspath: git read of local core.hooksPath on "
80
- f"{repository_root} exited {completed_process.returncode}: "
81
- f"{diagnostic_stderr}",
82
- file=sys.stderr,
81
+ raise RuntimeError(
82
+ f"git read of local core.hooksPath on {repository_root} "
83
+ f"exited {completed_process.returncode}: {diagnostic_stderr}"
83
84
  )
84
85
  return []
85
86
  return [
@@ -99,8 +100,11 @@ def read_global_core_hooks_path(
99
100
  forwarded to ``subprocess.run``.
100
101
 
101
102
  Returns:
102
- The stripped global value, or an empty string when unset or when git
103
- returns non-zero.
103
+ The stripped global value, or an empty string when the key is unset
104
+ (git exits non-zero with empty stderr).
105
+
106
+ Raises:
107
+ RuntimeError: When git exits non-zero with a non-empty stderr diagnostic.
104
108
  """
105
109
  git_command = list(ALL_GIT_GLOBAL_GET_CORE_HOOKS_PATH_COMMAND)
106
110
  completed_process = subprocess.run(
@@ -115,10 +119,9 @@ def read_global_core_hooks_path(
115
119
  if completed_process.returncode != 0:
116
120
  diagnostic_stderr = completed_process.stderr.strip()
117
121
  if diagnostic_stderr:
118
- print(
119
- "fix_hookspath: git read of global core.hooksPath exited "
120
- f"{completed_process.returncode}: {diagnostic_stderr}",
121
- file=sys.stderr,
122
+ raise RuntimeError(
123
+ f"git read of global core.hooksPath exited "
124
+ f"{completed_process.returncode}: {diagnostic_stderr}"
122
125
  )
123
126
  return ""
124
127
  return completed_process.stdout.strip()
@@ -280,7 +283,8 @@ def main(
280
283
  forwarded to every git invocation and to the preflight rerun.
281
284
 
282
285
  Returns:
283
- Zero on success. Non-zero on the first failing git command or on a
286
+ Zero on success. Non-zero on the first failing git command, on a
287
+ hard-fail git stderr diagnostic (caught ``RuntimeError``), or on a
284
288
  non-zero preflight rerun exit code.
285
289
  """
286
290
  arguments = parse_arguments(all_arguments)
@@ -302,6 +306,37 @@ def main(
302
306
  file=sys.stderr,
303
307
  )
304
308
  return 1
309
+ try:
310
+ return _repair_hooks_path_then_rerun_preflight(
311
+ repository_root=repository_root,
312
+ canonical_hooks_directory=canonical_hooks_directory,
313
+ all_environment_overrides=all_environment_overrides,
314
+ )
315
+ except RuntimeError as runtime_error:
316
+ print(f"fix_hookspath: {runtime_error}", file=sys.stderr)
317
+ return 1
318
+
319
+
320
+ def _repair_hooks_path_then_rerun_preflight(
321
+ repository_root: Path,
322
+ canonical_hooks_directory: Path,
323
+ all_environment_overrides: dict[str, str] | None,
324
+ ) -> int:
325
+ """Apply local/global hooksPath repairs, then re-run preflight.
326
+
327
+ Args:
328
+ repository_root: Repository root used for local git config and preflight.
329
+ canonical_hooks_directory: Absolute path to the packaged git-hooks tree.
330
+ all_environment_overrides: Optional environment variable mapping
331
+ forwarded to every git invocation and to the preflight rerun.
332
+
333
+ Returns:
334
+ Zero on success. Non-zero on the first failing git write or on a
335
+ non-zero preflight rerun exit code.
336
+
337
+ Raises:
338
+ RuntimeError: When a git read exits non-zero with non-empty stderr.
339
+ """
305
340
  local_hooks_path_values = list_local_core_hooks_path_values(
306
341
  repository_root,
307
342
  all_environment_overrides,
@@ -483,16 +483,11 @@ def test_check_wrapper_plumb_through_exempts_test_files() -> None:
483
483
  " return _helper('a', clean_name='b')\n"
484
484
  )
485
485
  shared_issues = gate_module.check_wrapper_plumb_through(source, "pkg/test_thing.py")
486
- bugteam_gate = _load_bugteam_gate_module()
487
- bugteam_issues = bugteam_gate.check_wrapper_plumb_through(source, "pkg/test_thing.py")
488
486
  assert shared_issues == [], (
489
487
  "a test_* function in a test-file path that calls a module-level helper "
490
488
  "exposing an optional kwarg is not a wrapper; the shared gate must exempt "
491
489
  "test files and emit zero findings"
492
490
  )
493
- assert bugteam_issues == [], (
494
- "the bugteam gate copy must apply the identical test-file exemption"
495
- )
496
491
 
497
492
 
498
493
  def test_check_wrapper_plumb_through_still_flags_non_test_path_with_test_shape() -> None:
@@ -1245,8 +1240,6 @@ def test_check_wrapper_plumb_through_dedupes_nested_public_function_calls() -> N
1245
1240
  assert len(issues) == 1, f"expected 1 finding, got {len(issues)}: {issues!r}"
1246
1241
 
1247
1242
 
1248
-
1249
-
1250
1243
  def test_check_wrapper_plumb_through_ignores_calls_nested_inside_delegate_arguments() -> None:
1251
1244
  """Regression: nested callees inside another call's arguments are not wrapper sites.
1252
1245
 
@@ -1766,68 +1759,6 @@ def test_main_blocks_banned_noun_on_added_lines_past_document_order(
1766
1759
  )
1767
1760
 
1768
1761
 
1769
- def _load_bugteam_gate_module() -> ModuleType:
1770
- bugteam_scripts_dir = (
1771
- Path(__file__).resolve().parents[4]
1772
- / "skills"
1773
- / "bugteam"
1774
- / "scripts"
1775
- )
1776
- if str(bugteam_scripts_dir) not in sys.path:
1777
- sys.path.insert(0, str(bugteam_scripts_dir))
1778
- module_path = bugteam_scripts_dir / "bugteam_code_rules_gate.py"
1779
- spec = importlib.util.spec_from_file_location("bugteam_code_rules_gate", module_path)
1780
- assert spec is not None
1781
- assert spec.loader is not None
1782
- module = importlib.util.module_from_spec(spec)
1783
- spec.loader.exec_module(module)
1784
- return module
1785
-
1786
-
1787
- def test_both_gates_classify_wrapper_plumb_through_identically() -> None:
1788
- """The bugteam and _shared gate copies of check_wrapper_plumb_through must
1789
- return identical findings. A class method calling a module-level delegate
1790
- is not a wrapper; both gates must exclude it rather than one emitting a
1791
- false positive the other does not."""
1792
- bugteam_gate = _load_bugteam_gate_module()
1793
- class_method_calling_delegate = (
1794
- "def fetch(target, *, retries=3):\n"
1795
- " return target\n"
1796
- "\n"
1797
- "class MyService:\n"
1798
- " def public_method(self, target):\n"
1799
- " return fetch(target)\n"
1800
- )
1801
- nested_call_inside_delegate_argument = (
1802
- "def delegate(value, *, retries=3):\n"
1803
- " return value\n"
1804
- "\n"
1805
- "def helper(value):\n"
1806
- " return value\n"
1807
- "\n"
1808
- "def public_caller(value):\n"
1809
- " return delegate(helper(value))\n"
1810
- )
1811
- name_call_dropping_kwarg = (
1812
- "def delegate(value, *, retries=3):\n"
1813
- " return value\n"
1814
- "\n"
1815
- "def public_wrapper(value):\n"
1816
- " return delegate(value)\n"
1817
- )
1818
- for each_source in (
1819
- class_method_calling_delegate,
1820
- nested_call_inside_delegate_argument,
1821
- name_call_dropping_kwarg,
1822
- ):
1823
- shared_issues = gate_module.check_wrapper_plumb_through(each_source, "module.py")
1824
- bugteam_issues = bugteam_gate.check_wrapper_plumb_through(each_source, "module.py")
1825
- assert shared_issues == bugteam_issues, (
1826
- "both gate copies of check_wrapper_plumb_through must classify "
1827
- f"identically; shared={shared_issues!r} bugteam={bugteam_issues!r}"
1828
- )
1829
-
1830
-
1831
1762
  def test_check_wrapper_plumb_through_stays_under_function_length_threshold() -> None:
1832
1763
  """check_wrapper_plumb_through must stay under the enforcer's function-length
1833
1764
  blocking threshold so editing it (e.g. aligning the two gate copies) does