claude-dev-env 1.32.0 → 1.33.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.
- package/hooks/blocking/code_rules_enforcer.py +109 -0
- package/hooks/blocking/test_windows_rmtree_blocker.py +7 -0
- package/hooks/blocking/windows_rmtree_blocker.py +45 -49
- package/hooks/config/session_env_cleanup_constants.py +3 -1
- package/hooks/config/test_session_env_cleanup_constants.py +6 -1
- package/hooks/session/session_env_cleanup.py +5 -4
- package/hooks/session/test_session_env_cleanup.py +2 -0
- package/package.json +1 -1
- package/rules/windows-filesystem-safe.md +1 -3
- package/skills/bugteam/PROMPTS.md +39 -0
- package/skills/bugteam/SKILL.md +34 -0
- package/skills/bugteam/reference/copilot-gap-analysis.md +496 -0
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +94 -0
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +34 -12
- package/skills/bugteam/scripts/config/__init__.py +0 -0
- package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +17 -0
- package/skills/rebase/SKILL.md +15 -8
|
@@ -5,17 +5,29 @@ import subprocess
|
|
|
5
5
|
import sys
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
+
sys.modules.pop("config", None)
|
|
9
|
+
if str(Path(__file__).resolve().parent) not in sys.path:
|
|
10
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
11
|
+
|
|
12
|
+
from config.bugteam_fix_hookspath_constants import (
|
|
13
|
+
ALL_CANONICAL_HOOKS_DIRECTORY_COMPONENTS,
|
|
14
|
+
ALL_HOME_ENV_VAR_NAMES,
|
|
15
|
+
HOOKS_PATH_SUFFIX,
|
|
16
|
+
PREFLIGHT_NO_PYTEST_FLAG,
|
|
17
|
+
PREFLIGHT_REPO_ROOT_FLAG,
|
|
18
|
+
)
|
|
19
|
+
|
|
8
20
|
|
|
9
21
|
def _expected_hooks_path_suffix() -> str:
|
|
10
|
-
return
|
|
22
|
+
return HOOKS_PATH_SUFFIX
|
|
11
23
|
|
|
12
24
|
|
|
13
|
-
def _canonical_hooks_directory_components() -> tuple[str,
|
|
14
|
-
return
|
|
25
|
+
def _canonical_hooks_directory_components() -> tuple[str, str, str]:
|
|
26
|
+
return ALL_CANONICAL_HOOKS_DIRECTORY_COMPONENTS
|
|
15
27
|
|
|
16
28
|
|
|
17
|
-
def _home_env_var_names() -> tuple[str,
|
|
18
|
-
return
|
|
29
|
+
def _home_env_var_names() -> tuple[str, str]:
|
|
30
|
+
return ALL_HOME_ENV_VAR_NAMES
|
|
19
31
|
|
|
20
32
|
|
|
21
33
|
def resolve_canonical_hooks_directory(
|
|
@@ -82,7 +94,7 @@ def read_global_core_hooks_path(
|
|
|
82
94
|
def unset_local_core_hooks_path(
|
|
83
95
|
repository_root: Path,
|
|
84
96
|
environment_overrides: dict[str, str] | None,
|
|
85
|
-
) ->
|
|
97
|
+
) -> int:
|
|
86
98
|
git_command = [
|
|
87
99
|
"git",
|
|
88
100
|
"-C",
|
|
@@ -92,13 +104,14 @@ def unset_local_core_hooks_path(
|
|
|
92
104
|
"--unset-all",
|
|
93
105
|
"core.hooksPath",
|
|
94
106
|
]
|
|
95
|
-
subprocess.run(
|
|
107
|
+
completed_process = subprocess.run(
|
|
96
108
|
git_command,
|
|
97
109
|
capture_output=True,
|
|
98
110
|
text=True,
|
|
99
111
|
check=False,
|
|
100
112
|
env=environment_overrides,
|
|
101
113
|
)
|
|
114
|
+
return completed_process.returncode
|
|
102
115
|
|
|
103
116
|
|
|
104
117
|
def set_global_core_hooks_path(
|
|
@@ -144,8 +157,8 @@ def rerun_preflight(
|
|
|
144
157
|
rerun_command = [
|
|
145
158
|
sys.executable,
|
|
146
159
|
str(preflight_script_path),
|
|
147
|
-
|
|
148
|
-
|
|
160
|
+
PREFLIGHT_NO_PYTEST_FLAG,
|
|
161
|
+
PREFLIGHT_REPO_ROOT_FLAG,
|
|
149
162
|
str(repository_root),
|
|
150
163
|
]
|
|
151
164
|
completed_process = subprocess.run(
|
|
@@ -156,7 +169,7 @@ def rerun_preflight(
|
|
|
156
169
|
return completed_process.returncode
|
|
157
170
|
|
|
158
171
|
|
|
159
|
-
def parse_arguments(argv: list[str]) -> argparse.Namespace:
|
|
172
|
+
def parse_arguments(argv: list[str] | None) -> argparse.Namespace:
|
|
160
173
|
parser = argparse.ArgumentParser(
|
|
161
174
|
description=(
|
|
162
175
|
"Auto-fix core.hooksPath when bugteam preflight detects a stale override. "
|
|
@@ -178,7 +191,7 @@ def main(
|
|
|
178
191
|
*,
|
|
179
192
|
environment_overrides: dict[str, str] | None = None,
|
|
180
193
|
) -> int:
|
|
181
|
-
arguments = parse_arguments(
|
|
194
|
+
arguments = parse_arguments(argv)
|
|
182
195
|
start_directory = Path.cwd()
|
|
183
196
|
repository_root = (
|
|
184
197
|
arguments.repo_root.resolve()
|
|
@@ -206,7 +219,16 @@ def main(
|
|
|
206
219
|
for each_value in local_hooks_path_values
|
|
207
220
|
)
|
|
208
221
|
if has_non_canonical_local_override:
|
|
209
|
-
unset_local_core_hooks_path(
|
|
222
|
+
unset_local_returncode = unset_local_core_hooks_path(
|
|
223
|
+
repository_root, environment_overrides
|
|
224
|
+
)
|
|
225
|
+
if unset_local_returncode != 0:
|
|
226
|
+
print(
|
|
227
|
+
"bugteam_fix_hookspath: failed to unset local core.hooksPath on "
|
|
228
|
+
f"{repository_root} (git exit {unset_local_returncode}).",
|
|
229
|
+
file=sys.stderr,
|
|
230
|
+
)
|
|
231
|
+
return 1
|
|
210
232
|
print(
|
|
211
233
|
"bugteam_fix_hookspath: removed stale local core.hooksPath override on "
|
|
212
234
|
f"{repository_root}",
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Configuration constants for bugteam_fix_hookspath auto-remediation script."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
HOOKS_PATH_SUFFIX: str = "hooks/git-hooks"
|
|
6
|
+
|
|
7
|
+
ALL_CANONICAL_HOOKS_DIRECTORY_COMPONENTS: tuple[str, str, str] = (
|
|
8
|
+
".claude",
|
|
9
|
+
"hooks",
|
|
10
|
+
"git-hooks",
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
ALL_HOME_ENV_VAR_NAMES: tuple[str, str] = ("HOME", "USERPROFILE")
|
|
14
|
+
|
|
15
|
+
PREFLIGHT_NO_PYTEST_FLAG: str = "--no-pytest"
|
|
16
|
+
|
|
17
|
+
PREFLIGHT_REPO_ROOT_FLAG: str = "--repo-root"
|
package/skills/rebase/SKILL.md
CHANGED
|
@@ -11,16 +11,23 @@ The default failure mode is shipping a rebase that compiled but doesn't run. Thi
|
|
|
11
11
|
|
|
12
12
|
## When to rebase vs. merge
|
|
13
13
|
|
|
14
|
+
> **Decision thresholds**
|
|
15
|
+
>
|
|
16
|
+
> | Threshold | Value |
|
|
17
|
+
> |---|---|
|
|
18
|
+
> | Maximum commits for solo rebase | 5 |
|
|
19
|
+
> | Divergence age that triggers merge-instead | 2 weeks |
|
|
20
|
+
|
|
14
21
|
**Default to rebase** when:
|
|
15
22
|
|
|
16
23
|
- Solo branch (you are the only author of the commits being rebased).
|
|
17
|
-
- 1–5 commits ahead of base.
|
|
24
|
+
- 1–5 commits ahead of base (see Decision thresholds above).
|
|
18
25
|
- Stacked PR whose base just merged via squash.
|
|
19
26
|
|
|
20
27
|
**Default to merge** when:
|
|
21
28
|
|
|
22
29
|
- Branch has multiple authors (force-push would clobber their state).
|
|
23
|
-
- More than
|
|
30
|
+
- More than 5 commits or more than 2 weeks of divergence (see Decision thresholds above; rebase complexity grows non-linearly).
|
|
24
31
|
- The user said "merge", not "rebase".
|
|
25
32
|
- Open PR with approving reviews already on the current SHA (force-push invalidates them).
|
|
26
33
|
|
|
@@ -74,10 +81,10 @@ When in doubt, ask. Both work; the choice affects history shape, not correctness
|
|
|
74
81
|
6. **Audit auto-merged files.** Files that git merged without conflict markers are not automatically correct. After each commit applies, run:
|
|
75
82
|
|
|
76
83
|
```
|
|
77
|
-
git diff --name-only --diff-filter=M
|
|
84
|
+
git diff --name-only --diff-filter=M ORIG_HEAD
|
|
78
85
|
```
|
|
79
86
|
|
|
80
|
-
For each modified file with no conflict markers, eyeball the changes — auto-merge can produce duplicate blocks (when both sides added similar content) or silently drop content (when both sides removed adjacent lines). Pay extra attention to `config/`, `constants.*`, and `__init__.py` files where additions often sit near each other.
|
|
87
|
+
Use `ORIG_HEAD`, which git sets at rebase start; the reflog index `HEAD@{1}` shifts as each rebase step runs and is unreliable mid-rebase. For each modified file with no conflict markers, eyeball the changes — auto-merge can produce duplicate blocks (when both sides added similar content) or silently drop content (when both sides removed adjacent lines). Pay extra attention to `config/`, `constants.*`, and `__init__.py` files where additions often sit near each other.
|
|
81
88
|
|
|
82
89
|
7. **At every conflict, take both sides' intent seriously.** Read both, then decide based on the post-rebase logical state. Do not reflex-pick HEAD or `origin/main`. Document the resolution reasoning in the commit message if it is non-obvious.
|
|
83
90
|
|
|
@@ -88,10 +95,10 @@ When in doubt, ask. Both work; the choice affects history shape, not correctness
|
|
|
88
95
|
8. **Real import check.** For Python:
|
|
89
96
|
|
|
90
97
|
```
|
|
91
|
-
python -
|
|
98
|
+
python -m compileall -q <package>
|
|
92
99
|
```
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
Follow immediately with the test-collection step below. `compileall` catches import-time failures across every module file; combined with `--collect-only` it surfaces `NameError`, `AttributeError`, and `ImportError` cases that a syntax-only check misses.
|
|
95
102
|
|
|
96
103
|
9. **Test collection.** `pytest --collect-only -q` on the changed packages catches NameError, AttributeError, and ImportError surfaces beyond plain imports.
|
|
97
104
|
|
|
@@ -102,7 +109,7 @@ When in doubt, ask. Both work; the choice affects history shape, not correctness
|
|
|
102
109
|
- **Preferred:** `mcp__serena__find_referencing_symbols` (symbol-aware; ignores false matches in comments and string literals).
|
|
103
110
|
- **Fallback:** `mcp__zoekt__search` for cross-repo or large trees.
|
|
104
111
|
- **Then:** the `Grep` tool (e.g., `Grep(pattern="<symbol>", type="py")`) for fast in-repo scans.
|
|
105
|
-
- **Last resort:** `grep -rn "<symbol>"
|
|
112
|
+
- **Last resort:** `grep -rn "<symbol>" .` (let ripgrep defaults and `.gitignore` handle scoping)
|
|
106
113
|
|
|
107
114
|
Any reference outside the rebased commits' own changes is a stale reference. Either update it (with user authorization) or surface it and refuse to push.
|
|
108
115
|
|
|
@@ -117,7 +124,7 @@ When in doubt, ask. Both work; the choice affects history shape, not correctness
|
|
|
117
124
|
- Ask for explicit authorization.
|
|
118
125
|
- If denied: leave the rebase result locally, report merge-instead as the alternative, stop.
|
|
119
126
|
|
|
120
|
-
14. **Refuse to force-push** `main`, `master`, `release/*`, `production`, or any branch with more than one author in `git log --format='%ae' origin/<branch> |
|
|
127
|
+
14. **Refuse to force-push** `main`, `master`, `release/*`, `production`, or any branch with more than one unique author. Count unique authors in a cross-platform way: `git log --format='%ae' origin/<branch> | python -c "import sys; print(len(set(sys.stdin)))"`. This form works on both Windows (PowerShell) and Unix without shell pipeline extensions. Surface the refusal; do not ask for authorization on these.
|
|
121
128
|
|
|
122
129
|
15. **Always `--force-with-lease=<branch>:<sha>`**, never bare `--force`. Pin the lease to the SHA you started from so concurrent pushes are detected as a lease mismatch instead of clobbered.
|
|
123
130
|
|