claude-dev-env 8.32.2 → 8.33.1
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/advisory/pr_done_reminder.py +16 -75
- package/hooks/advisory/test_pr_done_reminder.py +19 -0
- package/hooks/blocking/test_verify_before_acting.py +655 -0
- package/hooks/blocking/verify_before_acting.py +323 -0
- package/hooks/hooks.json +10 -0
- package/hooks/hooks_constants/piped_pytest_blocker_constants.py +78 -6
- package/hooks/hooks_constants/pr_done_reminder_constants.py +0 -8
- package/hooks/hooks_constants/pytest_invocation.py +60 -22
- package/hooks/hooks_constants/shell_command_mutation.py +202 -0
- package/hooks/hooks_constants/shell_command_segments.py +39 -0
- package/hooks/hooks_constants/shell_command_wrappers.py +68 -0
- package/hooks/hooks_constants/test_pr_done_reminder_constants.py +0 -11
- package/hooks/hooks_constants/test_pytest_invocation.py +6 -0
- package/hooks/hooks_constants/test_shell_command_mutation.py +37 -0
- package/hooks/hooks_constants/test_shell_command_segments.py +29 -0
- package/hooks/hooks_constants/test_shell_command_wrappers.py +61 -0
- package/hooks/hooks_constants/verify_before_acting_constants.py +170 -0
- package/package.json +1 -1
- package/rules/agent-merges-its-own-green-pull-request.md +3 -3
- package/scripts/agent_merge_check.py +157 -14
- package/scripts/dev_env_scripts_constants/agent_merge_check_constants.py +6 -0
- package/scripts/policy_lint/adapter_configuration.py +4 -0
- package/scripts/policy_lint/config/constants.py +1 -0
- package/scripts/test_agent_merge_check.py +266 -0
- package/scripts/tests/test_adapter_configuration.py +8 -0
|
@@ -31,7 +31,6 @@ Hosted by ``blocking/bash_post_call_dispatcher.py``, which forwards the
|
|
|
31
31
|
from __future__ import annotations
|
|
32
32
|
|
|
33
33
|
import json
|
|
34
|
-
import shlex
|
|
35
34
|
import subprocess
|
|
36
35
|
import sys
|
|
37
36
|
from pathlib import Path
|
|
@@ -64,9 +63,6 @@ try:
|
|
|
64
63
|
GH_PR_CREATE_ACTION,
|
|
65
64
|
GH_PR_SUBCOMMAND,
|
|
66
65
|
ALL_GH_PR_VIEW_ARGUMENTS,
|
|
67
|
-
ALL_GIT_OPTIONS_WITH_VALUE,
|
|
68
|
-
ALL_POWERSHELL_COMMAND_FLAGS,
|
|
69
|
-
ALL_POWERSHELL_PROGRAM_NAMES,
|
|
70
66
|
GH_PR_VIEW_TIMEOUT_SECONDS,
|
|
71
67
|
GH_PROGRAM_NAME,
|
|
72
68
|
GIT_PROGRAM_NAME,
|
|
@@ -86,9 +82,13 @@ try:
|
|
|
86
82
|
)
|
|
87
83
|
from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin
|
|
88
84
|
from hooks_constants.shell_command_segments import (
|
|
89
|
-
|
|
85
|
+
command_tokens,
|
|
86
|
+
git_subcommand_tokens,
|
|
90
87
|
split_into_segments,
|
|
91
|
-
|
|
88
|
+
)
|
|
89
|
+
from hooks_constants.shell_command_wrappers import (
|
|
90
|
+
all_wrapped_command_texts,
|
|
91
|
+
segment_program_and_arguments,
|
|
92
92
|
)
|
|
93
93
|
except ImportError as import_error:
|
|
94
94
|
raise ImportError(
|
|
@@ -97,47 +97,10 @@ except ImportError as import_error:
|
|
|
97
97
|
) from import_error
|
|
98
98
|
|
|
99
99
|
|
|
100
|
-
def _command_tokens(command: str) -> list[str]:
|
|
101
|
-
try:
|
|
102
|
-
return shlex.split(command, posix=True)
|
|
103
|
-
except ValueError:
|
|
104
|
-
return command.split()
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def _segment_program_and_arguments(all_segment_tokens: list[str]) -> tuple[str, list[str]]:
|
|
108
|
-
program_token = effective_leading_program(all_segment_tokens)
|
|
109
|
-
if program_token is None:
|
|
110
|
-
return "", []
|
|
111
|
-
program_index = all_segment_tokens.index(program_token)
|
|
112
|
-
return token_basename(program_token), all_segment_tokens[program_index + 1 :]
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def _git_subcommand(all_arguments: list[str]) -> str | None:
|
|
116
|
-
"""Return git's subcommand after its global options.
|
|
117
|
-
|
|
118
|
-
``git -C <path> push`` and ``git -c <key=value> push`` carry an option
|
|
119
|
-
value before the subcommand; ``--git-dir=<path>`` carries none. The first
|
|
120
|
-
token left after those is the subcommand, so ``git stash push`` yields
|
|
121
|
-
``stash`` and ``git log --grep push`` yields ``log``.
|
|
122
|
-
"""
|
|
123
|
-
should_skip_next_token = False
|
|
124
|
-
for each_argument in all_arguments:
|
|
125
|
-
if should_skip_next_token:
|
|
126
|
-
should_skip_next_token = False
|
|
127
|
-
continue
|
|
128
|
-
if each_argument in ALL_GIT_OPTIONS_WITH_VALUE:
|
|
129
|
-
should_skip_next_token = True
|
|
130
|
-
continue
|
|
131
|
-
if each_argument.startswith("-"):
|
|
132
|
-
continue
|
|
133
|
-
return each_argument
|
|
134
|
-
return None
|
|
135
|
-
|
|
136
|
-
|
|
137
100
|
def _is_git_push(program_name: str, all_arguments: list[str]) -> bool:
|
|
138
|
-
return (
|
|
139
|
-
|
|
140
|
-
|
|
101
|
+
return program_name == GIT_PROGRAM_NAME and git_subcommand_tokens(all_arguments)[:1] == [
|
|
102
|
+
GIT_PUSH_SUBCOMMAND
|
|
103
|
+
]
|
|
141
104
|
|
|
142
105
|
|
|
143
106
|
def _is_gh_pr_create(program_name: str, all_arguments: list[str]) -> bool:
|
|
@@ -147,25 +110,6 @@ def _is_gh_pr_create(program_name: str, all_arguments: list[str]) -> bool:
|
|
|
147
110
|
]
|
|
148
111
|
|
|
149
112
|
|
|
150
|
-
def _powershell_inner_commands(all_command_tokens: list[str]) -> list[str]:
|
|
151
|
-
"""Return the script texts every ``pwsh -Command "..."`` wrapper runs.
|
|
152
|
-
|
|
153
|
-
Read from the raw shlex tokens, before segment splitting: the quoted
|
|
154
|
-
script is one token here, and splitting it on ``;`` first would cut a
|
|
155
|
-
``git add -A; git push`` script in half.
|
|
156
|
-
"""
|
|
157
|
-
all_inner_commands: list[str] = []
|
|
158
|
-
has_seen_powershell_program = False
|
|
159
|
-
for each_token_index, each_token in enumerate(all_command_tokens[:-1]):
|
|
160
|
-
if token_basename(each_token) in ALL_POWERSHELL_PROGRAM_NAMES:
|
|
161
|
-
has_seen_powershell_program = True
|
|
162
|
-
continue
|
|
163
|
-
if has_seen_powershell_program and each_token.lower() in ALL_POWERSHELL_COMMAND_FLAGS:
|
|
164
|
-
all_inner_commands.append(all_command_tokens[each_token_index + 1])
|
|
165
|
-
has_seen_powershell_program = False
|
|
166
|
-
return all_inner_commands
|
|
167
|
-
|
|
168
|
-
|
|
169
113
|
def command_triggers_reminder(command: str) -> bool:
|
|
170
114
|
"""Return True when any segment of the command is a git push or a gh pr create.
|
|
171
115
|
|
|
@@ -175,16 +119,13 @@ def command_triggers_reminder(command: str) -> bool:
|
|
|
175
119
|
Args:
|
|
176
120
|
command: The Bash command text the agent ran.
|
|
177
121
|
"""
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
program_name, all_arguments
|
|
186
|
-
):
|
|
187
|
-
return True
|
|
122
|
+
for each_command_text in all_wrapped_command_texts(command):
|
|
123
|
+
for each_segment in split_into_segments(command_tokens(each_command_text)):
|
|
124
|
+
program_name, all_arguments = segment_program_and_arguments(each_segment)
|
|
125
|
+
if _is_git_push(program_name, all_arguments) or _is_gh_pr_create(
|
|
126
|
+
program_name, all_arguments
|
|
127
|
+
):
|
|
128
|
+
return True
|
|
188
129
|
return False
|
|
189
130
|
|
|
190
131
|
|
|
@@ -95,9 +95,24 @@ def _additional_context(stdout_text: str) -> str:
|
|
|
95
95
|
"git add -A && git commit -F m.txt && git push",
|
|
96
96
|
"git -c push.default=current push",
|
|
97
97
|
"git --git-dir=C:/repo/.git push",
|
|
98
|
+
"git --git-dir C:/repo/.git push",
|
|
99
|
+
"git --work-tree C:/repo push",
|
|
100
|
+
"git --namespace n push",
|
|
98
101
|
'pwsh -NoProfile -Command "git push -u origin feat/x"',
|
|
99
102
|
"pwsh -NoProfile -Command 'gh pr create --draft --fill'",
|
|
100
103
|
'powershell -Command "git add -A; git push"',
|
|
104
|
+
"pwsh -Command git push origin HEAD",
|
|
105
|
+
"pwsh -Command git push origin HEAD; echo done",
|
|
106
|
+
"sudo git push",
|
|
107
|
+
'bash -c "git push"',
|
|
108
|
+
"sh -c 'git push'",
|
|
109
|
+
"bash -lc 'gh pr create --draft --fill'",
|
|
110
|
+
"env VAR=x git push",
|
|
111
|
+
"echo origin | xargs git push",
|
|
112
|
+
"git.exe push origin HEAD",
|
|
113
|
+
"env -u HOME git push",
|
|
114
|
+
"timeout --signal KILL 5 git push",
|
|
115
|
+
"echo origin | xargs -n 1 git push",
|
|
101
116
|
],
|
|
102
117
|
)
|
|
103
118
|
def test_should_wake_on_git_push_and_gh_pr_create(command: str) -> None:
|
|
@@ -116,6 +131,10 @@ def test_should_wake_on_git_push_and_gh_pr_create(command: str) -> None:
|
|
|
116
131
|
"gh pr ready --undo",
|
|
117
132
|
"echo git push",
|
|
118
133
|
'pwsh -NoProfile -Command "git status"',
|
|
134
|
+
"pwsh -Command git status",
|
|
135
|
+
'bash -c "git status"',
|
|
136
|
+
"sudo cat notes.txt",
|
|
137
|
+
"rg 'git push' docs",
|
|
119
138
|
"pytest tests/",
|
|
120
139
|
],
|
|
121
140
|
)
|