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.
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/audit-contract.md +1 -1
- package/_shared/pr-loop/gh-payloads.md +3 -3
- package/_shared/pr-loop/post-audit-thread-contract.md +51 -0
- package/_shared/pr-loop/scripts/README.md +2 -2
- package/_shared/pr-loop/scripts/code_rules_gate.py +127 -13
- package/_shared/pr-loop/scripts/fix_hookspath.py +47 -12
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +4 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +146 -69
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -374
- package/_shared/pr-loop/state-schema.md +3 -22
- package/bin/CLAUDE.md +1 -1
- package/bin/install.mjs +2 -7
- package/bin/install.test.mjs +10 -4
- package/hooks/blocking/CLAUDE.md +2 -0
- package/hooks/blocking/pii_commit_command.py +398 -0
- package/hooks/blocking/pii_payload_scan.py +133 -0
- package/hooks/blocking/pii_prevention_blocker.py +151 -449
- package/hooks/blocking/pii_scanner.py +26 -1
- package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +2 -2
- package/hooks/blocking/test_pii_prevention_blocker.py +299 -0
- package/hooks/blocking/test_pii_scanner.py +27 -2
- package/hooks/blocking/test_verdict_directory_write_blocker.py +42 -7
- package/hooks/hooks_constants/CLAUDE.md +2 -2
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -2
- package/hooks/hooks_constants/local_identity.py +65 -15
- package/hooks/hooks_constants/pii_prevention_constants.py +20 -8
- package/hooks/hooks_constants/pr_description_enforcer_constants.py +1 -1
- package/hooks/hooks_constants/test_local_identity.py +68 -0
- package/package.json +1 -1
- package/scripts/CLAUDE.md +10 -1
- package/scripts/Get-SessionAccount.ps1 +210 -0
- package/scripts/tests/CLAUDE.md +15 -1
- package/scripts/tests/Get-SessionAccount.Tests.ps1 +350 -0
- package/skills/CLAUDE.md +3 -25
- package/skills/anthropic-plan/SKILL.md +1 -13
- package/skills/autoconverge/CLAUDE.md +5 -2
- package/skills/autoconverge/SKILL.md +112 -387
- package/skills/autoconverge/reference/CLAUDE.md +4 -1
- package/skills/autoconverge/reference/closing-report.md +9 -2
- package/skills/autoconverge/reference/convergence.md +26 -22
- package/skills/autoconverge/reference/headless-safety.md +44 -0
- package/skills/autoconverge/reference/multi-pr.md +88 -0
- package/skills/autoconverge/reference/self-closing-loop.md +84 -0
- package/skills/autoconverge/reference/stop-conditions.md +21 -13
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +87 -27
- package/skills/autoconverge/workflow/converge.contract.test.mjs +40 -10
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +179 -47
- package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.mjs +284 -94
- package/skills/bugteam/CLAUDE.md +1 -1
- package/skills/bugteam/CONSTRAINTS.md +3 -3
- package/skills/bugteam/PROMPTS.md +8 -19
- package/skills/bugteam/SKILL.md +13 -14
- package/skills/bugteam/reference/CLAUDE.md +1 -1
- package/skills/bugteam/reference/audit-and-teammates.md +2 -2
- package/skills/bugteam/reference/copilot-gap-analysis.md +2 -0
- package/skills/bugteam/reference/github-pr-reviews.md +56 -38
- package/skills/bugteam/reference/team-setup.md +11 -11
- package/skills/bugteam/scripts/CLAUDE.md +6 -6
- package/skills/bugteam/scripts/README.md +20 -8
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +13 -1463
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +23 -358
- package/skills/bugteam/scripts/bugteam_preflight.py +13 -315
- package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +2 -3
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +34 -1053
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +30 -347
- package/skills/bugteam/scripts/test_bugteam_preflight.py +30 -305
- package/skills/copilot-finding-triage/SKILL.md +80 -28
- package/skills/copilot-finding-triage/reference/tier-rubric.md +70 -11
- package/skills/copilot-finding-triage/templates/notification.md +8 -3
- package/skills/everything-search/SKILL.md +1 -2
- package/skills/grokify/SKILL.md +58 -0
- package/skills/grokify/templates/handoff-template.md +92 -0
- package/skills/orchestrator/SKILL.md +12 -8
- package/skills/orchestrator-refresh/SKILL.md +3 -3
- package/skills/pr-converge/SKILL.md +132 -233
- package/skills/pr-converge/reference/convergence-gates.md +102 -66
- package/skills/pr-converge/reference/examples.md +37 -25
- package/skills/pr-converge/reference/fix-protocol.md +7 -7
- package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -4
- package/skills/pr-converge/reference/per-tick.md +64 -41
- package/skills/pr-converge/reference/state-schema.md +27 -2
- package/skills/pr-converge/scripts/CLAUDE.md +8 -0
- package/skills/pr-converge/scripts/README.md +4 -3
- package/skills/pr-converge/scripts/_pr_converge_path_setup.py +23 -0
- package/skills/pr-converge/scripts/check_convergence.py +189 -645
- package/skills/pr-converge/scripts/check_convergence_gates.py +235 -0
- package/skills/pr-converge/scripts/check_convergence_thread_gates.py +219 -0
- package/skills/pr-converge/scripts/conftest.py +8 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -0
- package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +26 -0
- package/skills/pr-converge/scripts/test_check_convergence.py +73 -228
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +321 -0
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +68 -0
- package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +75 -0
- package/skills/privacy-hygiene/SKILL.md +8 -7
- package/skills/reviewer-gates/SKILL.md +26 -8
- package/skills/session-log/SKILL.md +11 -12
- package/skills/session-tidy/SKILL.md +40 -30
- package/skills/skill-builder/CLAUDE.md +13 -10
- package/skills/skill-builder/SKILL.md +31 -31
- package/skills/skill-builder/references/CLAUDE.md +10 -6
- package/skills/skill-builder/references/delegation-map.md +13 -4
- package/skills/skill-builder/references/description-field.md +113 -0
- package/skills/skill-builder/references/self-audit-checklist.md +21 -4
- package/skills/skill-builder/references/skill-modularity.md +113 -0
- package/skills/skill-builder/references/skill-types.md +4 -0
- package/skills/skill-builder/templates/CLAUDE.md +4 -4
- package/skills/skill-builder/templates/gap-analysis.md +25 -1
- package/skills/skill-builder/workflows/CLAUDE.md +6 -4
- package/skills/skill-builder/workflows/improve-skill.md +17 -16
- package/skills/skill-builder/workflows/new-skill.md +34 -10
- package/skills/skill-builder/workflows/polish-skill.md +59 -43
- package/skills/usage-pause/SKILL.md +10 -14
- package/skills/bdd-protocol/CLAUDE.md +0 -26
- package/skills/bdd-protocol/SKILL.md +0 -30
- package/skills/bdd-protocol/references/CLAUDE.md +0 -10
- package/skills/bdd-protocol/references/anti-patterns.md +0 -26
- package/skills/bdd-protocol/references/example-mapping.md +0 -23
- package/skills/bg-agent/CLAUDE.md +0 -17
- package/skills/bg-agent/SKILL.md +0 -69
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py +0 -55
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_fix_hookspath_constants.py +0 -26
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_preflight_constants.py +0 -35
- package/skills/caveman/CLAUDE.md +0 -15
- package/skills/caveman/SKILL.md +0 -38
- package/skills/code/CLAUDE.md +0 -17
- package/skills/code/SKILL.md +0 -178
- package/skills/deep-research/CLAUDE.md +0 -17
- package/skills/deep-research/SKILL.md +0 -80
- package/skills/findbugs/CLAUDE.md +0 -20
- package/skills/findbugs/SKILL.md +0 -241
- package/skills/fixbugs/CLAUDE.md +0 -19
- package/skills/fixbugs/SKILL.md +0 -142
- package/skills/gh-paginate/CLAUDE.md +0 -18
- package/skills/gh-paginate/SKILL.md +0 -84
- package/skills/gotcha/CLAUDE.md +0 -33
- package/skills/gotcha/SKILL.md +0 -73
- package/skills/implement/CLAUDE.md +0 -27
- package/skills/implement/SKILL.md +0 -66
- package/skills/implement/scripts/CLAUDE.md +0 -22
- package/skills/implement/scripts/append_note.py +0 -133
- package/skills/implement/scripts/implement_scripts_constants/CLAUDE.md +0 -22
- package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
- package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +0 -12
- package/skills/implement/scripts/test_append_note.py +0 -191
- package/skills/log-audit/CLAUDE.md +0 -20
- package/skills/log-audit/SKILL.md +0 -66
- package/skills/log-audit/reference/CLAUDE.md +0 -9
- package/skills/log-audit/reference/charter.md +0 -52
- package/skills/log-audit/scripts/CLAUDE.md +0 -27
- package/skills/log-audit/scripts/cluster_recurrences.py +0 -261
- package/skills/log-audit/scripts/collect_log_window.py +0 -199
- package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +0 -12
- package/skills/log-audit/scripts/log_audit_constants/__init__.py +0 -0
- package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +0 -23
- package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +0 -24
- package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +0 -49
- package/skills/log-audit/scripts/mine_copilot_findings.py +0 -302
- package/skills/log-audit/scripts/test_cluster_recurrences.py +0 -160
- package/skills/log-audit/scripts/test_collect_log_window.py +0 -111
- package/skills/log-audit/scripts/test_mine_copilot_findings.py +0 -126
- package/skills/logifix/CLAUDE.md +0 -36
- package/skills/logifix/SKILL.md +0 -69
- package/skills/logifix/scripts/CLAUDE.md +0 -16
- package/skills/logifix/scripts/logifix.ps1 +0 -205
- package/skills/monitor-open-prs/CLAUDE.md +0 -34
- package/skills/monitor-open-prs/SKILL.md +0 -88
- package/skills/monitor-open-prs/scripts/CLAUDE.md +0 -17
- package/skills/monitor-open-prs/scripts/discover_open_prs.py +0 -69
- package/skills/monitor-open-prs/scripts/test_discover_open_prs.py +0 -149
- package/skills/monitor-open-prs/test_skill_contract.py +0 -27
- package/skills/post-audit-findings/SKILL.md +0 -80
- package/skills/pr-consistency-audit/CLAUDE.md +0 -34
- package/skills/pr-consistency-audit/SKILL.md +0 -116
- package/skills/pr-consistency-audit/reference/CLAUDE.md +0 -16
- package/skills/pr-consistency-audit/reference/detection-rules.md +0 -96
- package/skills/pr-consistency-audit/reference/illustrations.md +0 -78
- package/skills/pr-fix-protocol/SKILL.md +0 -65
- package/skills/pr-review-responder/CLAUDE.md +0 -35
- package/skills/pr-review-responder/EXAMPLES.md +0 -590
- package/skills/pr-review-responder/PRINCIPLES.md +0 -533
- package/skills/pr-review-responder/README.md +0 -168
- package/skills/pr-review-responder/SKILL.md +0 -203
- package/skills/pr-review-responder/TESTING.md +0 -350
- package/skills/pr-scope-resolve/SKILL.md +0 -45
- package/skills/pre-compact/CLAUDE.md +0 -24
- package/skills/pre-compact/SKILL.md +0 -134
- package/skills/qbug/CLAUDE.md +0 -40
- package/skills/qbug/SKILL.md +0 -387
- package/skills/qbug/test_qbug_skill_audit_schema.py +0 -152
- package/skills/qbug/test_qbug_skill_post_fix_audit.py +0 -103
- package/skills/refine/CLAUDE.md +0 -44
- package/skills/refine/SKILL.md +0 -262
- package/skills/refine/templates/CLAUDE.md +0 -17
- package/skills/refine/templates/implementation-notes-template.html +0 -56
- package/skills/refine/templates/plan-template.md +0 -60
- package/skills/research-mode/CLAUDE.md +0 -35
- package/skills/research-mode/SKILL.md +0 -53
- package/skills/structure-prompt/CLAUDE.md +0 -42
- package/skills/structure-prompt/SKILL.md +0 -44
- package/skills/structure-prompt/reference/CLAUDE.md +0 -28
- package/skills/structure-prompt/reference/adversarial-tuning.md +0 -62
- package/skills/structure-prompt/reference/block-classification.md +0 -27
- package/skills/structure-prompt/reference/canonical-case.md +0 -48
- package/skills/structure-prompt/reference/citation-depth.md +0 -70
- package/skills/structure-prompt/reference/cleanup.md +0 -33
- package/skills/structure-prompt/reference/constraints.md +0 -33
- package/skills/structure-prompt/reference/directives.md +0 -37
- package/skills/structure-prompt/reference/examples.md +0 -72
- package/skills/structure-prompt/reference/instantiation.md +0 -51
- package/skills/structure-prompt/reference/output-contract.md +0 -72
- package/skills/structure-prompt/reference/per-category.md +0 -23
- package/skills/structure-prompt/reference/persona.md +0 -38
- package/skills/structure-prompt/reference/research.md +0 -33
- package/skills/structure-prompt/reference/structure.md +0 -28
- package/skills/verified-build/CLAUDE.md +0 -33
- package/skills/verified-build/SKILL.md +0 -33
|
@@ -1,358 +1,41 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Smoke tests for the skill-path fix_hookspath wrap.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
-
|
|
5
|
-
- sets global core.hooksPath when missing
|
|
6
|
-
- idempotent: second invocation produces the same final state with no errors
|
|
7
|
-
- no-op when no override exists and global is already canonical
|
|
8
|
-
- exits non-zero with a clear message when canonical hooks dir is missing
|
|
3
|
+
Behavioral coverage lives at
|
|
4
|
+
``_shared/pr-loop/scripts/tests/test_fix_hookspath.py``.
|
|
9
5
|
"""
|
|
10
6
|
|
|
11
7
|
from __future__ import annotations
|
|
12
8
|
|
|
13
9
|
import importlib.util
|
|
14
|
-
import os
|
|
15
|
-
import subprocess
|
|
16
|
-
import sys
|
|
17
10
|
from pathlib import Path
|
|
18
11
|
from types import ModuleType
|
|
19
|
-
from unittest.mock import patch
|
|
20
12
|
|
|
21
|
-
import pytest
|
|
22
13
|
|
|
23
|
-
|
|
24
|
-
def _load_fix_module() -> ModuleType:
|
|
14
|
+
def _load_wrap_module() -> ModuleType:
|
|
25
15
|
module_path = Path(__file__).parent / "bugteam_fix_hookspath.py"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return isolated_environment
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def _initialize_repository(repository_path: Path, environment: dict[str, str]) -> None:
|
|
56
|
-
repository_path.mkdir(parents=True, exist_ok=True)
|
|
57
|
-
subprocess.run(
|
|
58
|
-
["git", "init", "--quiet", str(repository_path)],
|
|
59
|
-
check=True,
|
|
60
|
-
env=environment,
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def _set_local_hooks_path(
|
|
65
|
-
repository_path: Path,
|
|
66
|
-
hooks_path_value: str,
|
|
67
|
-
environment: dict[str, str],
|
|
68
|
-
) -> None:
|
|
69
|
-
subprocess.run(
|
|
70
|
-
[
|
|
71
|
-
"git",
|
|
72
|
-
"-C",
|
|
73
|
-
str(repository_path),
|
|
74
|
-
"config",
|
|
75
|
-
"--local",
|
|
76
|
-
"core.hooksPath",
|
|
77
|
-
hooks_path_value,
|
|
78
|
-
],
|
|
79
|
-
check=True,
|
|
80
|
-
env=environment,
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def _set_global_hooks_path(hooks_path_value: str, environment: dict[str, str]) -> None:
|
|
85
|
-
subprocess.run(
|
|
86
|
-
["git", "config", "--global", "core.hooksPath", hooks_path_value],
|
|
87
|
-
check=True,
|
|
88
|
-
env=environment,
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def _read_local_hooks_path(repository_path: Path, environment: dict[str, str]) -> str:
|
|
93
|
-
completed_process = subprocess.run(
|
|
94
|
-
[
|
|
95
|
-
"git",
|
|
96
|
-
"-C",
|
|
97
|
-
str(repository_path),
|
|
98
|
-
"config",
|
|
99
|
-
"--local",
|
|
100
|
-
"--get",
|
|
101
|
-
"core.hooksPath",
|
|
102
|
-
],
|
|
103
|
-
capture_output=True,
|
|
104
|
-
text=True,
|
|
105
|
-
check=False,
|
|
106
|
-
env=environment,
|
|
107
|
-
)
|
|
108
|
-
return completed_process.stdout.strip()
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def _read_global_hooks_path(environment: dict[str, str]) -> str:
|
|
112
|
-
completed_process = subprocess.run(
|
|
113
|
-
["git", "config", "--global", "--get", "core.hooksPath"],
|
|
114
|
-
capture_output=True,
|
|
115
|
-
text=True,
|
|
116
|
-
check=False,
|
|
117
|
-
env=environment,
|
|
118
|
-
)
|
|
119
|
-
return completed_process.stdout.strip()
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def _create_canonical_hooks_directory(home_directory: Path) -> Path:
|
|
123
|
-
canonical_hooks_directory = home_directory / ".claude" / "hooks" / "git-hooks"
|
|
124
|
-
canonical_hooks_directory.mkdir(parents=True)
|
|
125
|
-
return canonical_hooks_directory
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def test_should_remove_local_override_and_pass_preflight(tmp_path: Path) -> None:
|
|
129
|
-
home_directory = tmp_path / "home"
|
|
130
|
-
home_directory.mkdir()
|
|
131
|
-
environment = _make_isolated_git_environment(home_directory)
|
|
132
|
-
canonical_hooks_directory = _create_canonical_hooks_directory(home_directory)
|
|
133
|
-
_set_global_hooks_path(str(canonical_hooks_directory), environment)
|
|
134
|
-
repository_path = tmp_path / "synthetic-repo"
|
|
135
|
-
_initialize_repository(repository_path, environment)
|
|
136
|
-
stale_local_value = str(repository_path / ".git" / "hooks")
|
|
137
|
-
_set_local_hooks_path(repository_path, stale_local_value, environment)
|
|
138
|
-
|
|
139
|
-
exit_code = bugteam_fix_hookspath.main(
|
|
140
|
-
["--repo-root", str(repository_path)],
|
|
141
|
-
all_environment_overrides=environment,
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
assert exit_code == 0, (
|
|
145
|
-
"fix script must succeed when canonical global hooks dir exists"
|
|
146
|
-
)
|
|
147
|
-
assert _read_local_hooks_path(repository_path, environment) == "", (
|
|
148
|
-
"local core.hooksPath override must be removed"
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def test_should_set_global_hooks_path_when_missing(tmp_path: Path) -> None:
|
|
153
|
-
home_directory = tmp_path / "home"
|
|
154
|
-
home_directory.mkdir()
|
|
155
|
-
environment = _make_isolated_git_environment(home_directory)
|
|
156
|
-
canonical_hooks_directory = _create_canonical_hooks_directory(home_directory)
|
|
157
|
-
repository_path = tmp_path / "synthetic-repo"
|
|
158
|
-
_initialize_repository(repository_path, environment)
|
|
159
|
-
stale_local_value = str(repository_path / ".git" / "hooks")
|
|
160
|
-
_set_local_hooks_path(repository_path, stale_local_value, environment)
|
|
161
|
-
|
|
162
|
-
exit_code = bugteam_fix_hookspath.main(
|
|
163
|
-
["--repo-root", str(repository_path)],
|
|
164
|
-
all_environment_overrides=environment,
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
assert exit_code == 0
|
|
168
|
-
global_value_after_fix = _read_global_hooks_path(environment)
|
|
169
|
-
assert (
|
|
170
|
-
global_value_after_fix.replace("\\", "/")
|
|
171
|
-
.rstrip("/")
|
|
172
|
-
.endswith("hooks/git-hooks")
|
|
173
|
-
), (
|
|
174
|
-
"fix script must set canonical global core.hooksPath when missing; "
|
|
175
|
-
f"got '{global_value_after_fix}'"
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
def test_should_be_idempotent(tmp_path: Path) -> None:
|
|
180
|
-
home_directory = tmp_path / "home"
|
|
181
|
-
home_directory.mkdir()
|
|
182
|
-
environment = _make_isolated_git_environment(home_directory)
|
|
183
|
-
canonical_hooks_directory = _create_canonical_hooks_directory(home_directory)
|
|
184
|
-
_set_global_hooks_path(str(canonical_hooks_directory), environment)
|
|
185
|
-
repository_path = tmp_path / "synthetic-repo"
|
|
186
|
-
_initialize_repository(repository_path, environment)
|
|
187
|
-
stale_local_value = str(repository_path / ".git" / "hooks")
|
|
188
|
-
_set_local_hooks_path(repository_path, stale_local_value, environment)
|
|
189
|
-
|
|
190
|
-
first_exit_code = bugteam_fix_hookspath.main(
|
|
191
|
-
["--repo-root", str(repository_path)],
|
|
192
|
-
all_environment_overrides=environment,
|
|
193
|
-
)
|
|
194
|
-
second_exit_code = bugteam_fix_hookspath.main(
|
|
195
|
-
["--repo-root", str(repository_path)],
|
|
196
|
-
all_environment_overrides=environment,
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
assert first_exit_code == 0
|
|
200
|
-
assert second_exit_code == 0, "second invocation must succeed without errors"
|
|
201
|
-
assert _read_local_hooks_path(repository_path, environment) == ""
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def test_should_no_op_when_already_clean(tmp_path: Path) -> None:
|
|
205
|
-
home_directory = tmp_path / "home"
|
|
206
|
-
home_directory.mkdir()
|
|
207
|
-
environment = _make_isolated_git_environment(home_directory)
|
|
208
|
-
canonical_hooks_directory = _create_canonical_hooks_directory(home_directory)
|
|
209
|
-
_set_global_hooks_path(str(canonical_hooks_directory), environment)
|
|
210
|
-
repository_path = tmp_path / "synthetic-repo"
|
|
211
|
-
_initialize_repository(repository_path, environment)
|
|
212
|
-
|
|
213
|
-
exit_code = bugteam_fix_hookspath.main(
|
|
214
|
-
["--repo-root", str(repository_path)],
|
|
215
|
-
all_environment_overrides=environment,
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
assert exit_code == 0
|
|
219
|
-
assert _read_local_hooks_path(repository_path, environment) == ""
|
|
220
|
-
assert (
|
|
221
|
-
_read_global_hooks_path(environment)
|
|
222
|
-
.replace("\\", "/")
|
|
223
|
-
.rstrip("/")
|
|
224
|
-
.endswith("hooks/git-hooks")
|
|
225
|
-
)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
def test_should_exit_nonzero_when_canonical_hooks_directory_missing(
|
|
229
|
-
tmp_path: Path,
|
|
230
|
-
capsys: pytest.CaptureFixture[str],
|
|
231
|
-
) -> None:
|
|
232
|
-
home_directory = tmp_path / "home"
|
|
233
|
-
home_directory.mkdir()
|
|
234
|
-
environment = _make_isolated_git_environment(home_directory)
|
|
235
|
-
repository_path = tmp_path / "synthetic-repo"
|
|
236
|
-
_initialize_repository(repository_path, environment)
|
|
237
|
-
stale_local_value = str(repository_path / ".git" / "hooks")
|
|
238
|
-
_set_local_hooks_path(repository_path, stale_local_value, environment)
|
|
239
|
-
|
|
240
|
-
exit_code = bugteam_fix_hookspath.main(
|
|
241
|
-
["--repo-root", str(repository_path)],
|
|
242
|
-
all_environment_overrides=environment,
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
assert exit_code != 0, (
|
|
246
|
-
"fix script must fail clearly when ~/.claude/hooks/git-hooks does not exist "
|
|
247
|
-
"so the user knows to run `npx claude-dev-env .`"
|
|
248
|
-
)
|
|
249
|
-
captured_streams = capsys.readouterr()
|
|
250
|
-
assert "hooks/git-hooks" in captured_streams.err.replace("\\", "/")
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
def test_should_handle_paths_with_spaces(tmp_path: Path) -> None:
|
|
254
|
-
home_directory = tmp_path / "home with space"
|
|
255
|
-
home_directory.mkdir()
|
|
256
|
-
environment = _make_isolated_git_environment(home_directory)
|
|
257
|
-
canonical_hooks_directory = _create_canonical_hooks_directory(home_directory)
|
|
258
|
-
_set_global_hooks_path(str(canonical_hooks_directory), environment)
|
|
259
|
-
repository_path = tmp_path / "repo with space"
|
|
260
|
-
_initialize_repository(repository_path, environment)
|
|
261
|
-
stale_local_value = str(repository_path / ".git" / "hooks")
|
|
262
|
-
_set_local_hooks_path(repository_path, stale_local_value, environment)
|
|
263
|
-
|
|
264
|
-
exit_code = bugteam_fix_hookspath.main(
|
|
265
|
-
["--repo-root", str(repository_path)],
|
|
266
|
-
all_environment_overrides=environment,
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
assert exit_code == 0
|
|
270
|
-
assert _read_local_hooks_path(repository_path, environment) == ""
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
def test_list_local_core_hooks_path_values_raises_on_unexpected_git_failure(
|
|
274
|
-
tmp_path: Path,
|
|
275
|
-
) -> None:
|
|
276
|
-
"""A non-empty stderr from git config must propagate as an error.
|
|
277
|
-
|
|
278
|
-
Regression for loop1-5: returning [] on every non-zero git exit collapses
|
|
279
|
-
"key unset" with "git failed for some other reason" — the caller then
|
|
280
|
-
skips the unset call, leaving a stale local override in place.
|
|
281
|
-
"""
|
|
282
|
-
repository_path = tmp_path / "repo"
|
|
283
|
-
repository_path.mkdir()
|
|
284
|
-
|
|
285
|
-
def fake_run(*_args: object, **_kwargs: object) -> subprocess.CompletedProcess:
|
|
286
|
-
return subprocess.CompletedProcess(
|
|
287
|
-
args=[],
|
|
288
|
-
returncode=128,
|
|
289
|
-
stdout="",
|
|
290
|
-
stderr="fatal: unable to read config file: permission denied\n",
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
with patch.object(subprocess, "run", fake_run):
|
|
294
|
-
with pytest.raises(RuntimeError):
|
|
295
|
-
bugteam_fix_hookspath.list_local_core_hooks_path_values(
|
|
296
|
-
repository_path,
|
|
297
|
-
None,
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
def test_read_global_core_hooks_path_raises_on_unexpected_git_failure() -> None:
|
|
302
|
-
"""A non-empty stderr from git config must propagate as an error.
|
|
303
|
-
|
|
304
|
-
Regression for loop1-6: returning "" on every non-zero git exit conflates
|
|
305
|
-
"global hooksPath unset" with "git failed for some other reason" — the
|
|
306
|
-
caller then overwrites global git config based on a non-truthful read.
|
|
307
|
-
"""
|
|
308
|
-
|
|
309
|
-
def fake_run(*_args: object, **_kwargs: object) -> subprocess.CompletedProcess:
|
|
310
|
-
return subprocess.CompletedProcess(
|
|
311
|
-
args=[],
|
|
312
|
-
returncode=128,
|
|
313
|
-
stdout="",
|
|
314
|
-
stderr="fatal: bad config line\n",
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
with patch.object(subprocess, "run", fake_run):
|
|
318
|
-
with pytest.raises(RuntimeError):
|
|
319
|
-
bugteam_fix_hookspath.read_global_core_hooks_path(None)
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
def test_list_local_core_hooks_path_values_returns_empty_when_key_unset(
|
|
323
|
-
tmp_path: Path,
|
|
324
|
-
) -> None:
|
|
325
|
-
"""Genuine key-unset (exit 1 + empty stderr) must continue to return []."""
|
|
326
|
-
repository_path = tmp_path / "repo"
|
|
327
|
-
repository_path.mkdir()
|
|
328
|
-
|
|
329
|
-
def fake_run(*_args: object, **_kwargs: object) -> subprocess.CompletedProcess:
|
|
330
|
-
return subprocess.CompletedProcess(
|
|
331
|
-
args=[],
|
|
332
|
-
returncode=1,
|
|
333
|
-
stdout="",
|
|
334
|
-
stderr="",
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
with patch.object(subprocess, "run", fake_run):
|
|
338
|
-
result = bugteam_fix_hookspath.list_local_core_hooks_path_values(
|
|
339
|
-
repository_path,
|
|
340
|
-
None,
|
|
341
|
-
)
|
|
342
|
-
assert result == []
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
def test_read_global_core_hooks_path_returns_empty_when_key_unset() -> None:
|
|
346
|
-
"""Genuine key-unset (exit 1 + empty stderr) must continue to return ''."""
|
|
347
|
-
|
|
348
|
-
def fake_run(*_args: object, **_kwargs: object) -> subprocess.CompletedProcess:
|
|
349
|
-
return subprocess.CompletedProcess(
|
|
350
|
-
args=[],
|
|
351
|
-
returncode=1,
|
|
352
|
-
stdout="",
|
|
353
|
-
stderr="",
|
|
354
|
-
)
|
|
355
|
-
|
|
356
|
-
with patch.object(subprocess, "run", fake_run):
|
|
357
|
-
result = bugteam_fix_hookspath.read_global_core_hooks_path(None)
|
|
358
|
-
assert result == ""
|
|
16
|
+
specification = importlib.util.spec_from_file_location(
|
|
17
|
+
"bugteam_fix_hookspath",
|
|
18
|
+
module_path,
|
|
19
|
+
)
|
|
20
|
+
assert specification is not None
|
|
21
|
+
assert specification.loader is not None
|
|
22
|
+
wrap_module = importlib.util.module_from_spec(specification)
|
|
23
|
+
specification.loader.exec_module(wrap_module)
|
|
24
|
+
return wrap_module
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_wrap_main_delegates_to_shared_module() -> None:
|
|
28
|
+
wrap_module = _load_wrap_module()
|
|
29
|
+
assert wrap_module.main.__module__ == "fix_hookspath"
|
|
30
|
+
shared_filename = Path(wrap_module.main.__code__.co_filename).name
|
|
31
|
+
assert shared_filename == "fix_hookspath.py"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_wrap_main_help_exits_cleanly() -> None:
|
|
35
|
+
wrap_module = _load_wrap_module()
|
|
36
|
+
try:
|
|
37
|
+
wrap_module.main(["--help"], None)
|
|
38
|
+
except SystemExit as exit_signal:
|
|
39
|
+
assert exit_signal.code in (0, None)
|
|
40
|
+
return
|
|
41
|
+
raise AssertionError("main(['--help'], None) must raise SystemExit")
|