claude-dev-env 8.102.1 → 8.103.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/.agents/skills/pull-request/reference/body-sections.md +2 -1
- package/bin/install.prompt-context.test.mjs +2 -1
- package/docs/rule-guides/proof-before-pull-request.md +3 -3
- package/docs/templates/artifact-page/README.md +2 -0
- package/hooks/blocking/pull_request_proof.py +64 -6
- package/hooks/blocking/test_pull_request_proof.py +87 -0
- package/hooks/blocking/test_turn_review_findings.py +25 -0
- package/hooks/blocking/test_turn_review_gate.py +462 -0
- package/hooks/blocking/test_turn_review_snapshot.py +107 -0
- package/hooks/blocking/turn_review_findings.py +102 -0
- package/hooks/blocking/turn_review_gate.py +362 -0
- package/hooks/blocking/turn_review_snapshot.py +116 -0
- package/hooks/features/conduct-gates.md +5 -0
- package/hooks/hooks.json +10 -0
- package/hooks/hooks_constants/pull_request_proof_constants.py +14 -1
- package/hooks/hooks_constants/turn_review_gate_constants.py +161 -0
- package/package.json +1 -1
- package/scripts/policy_lint/adapter_configuration.py +6 -0
- package/scripts/policy_lint/config/constants.py +1 -0
- package/scripts/tests/test_adapter_configuration.py +8 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Constants for the Stop hook that reviews each turn's changes in a fresh context."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"REPOSITORIES_ENVIRONMENT_VARIABLE",
|
|
7
|
+
"REPOSITORY_SEPARATOR",
|
|
8
|
+
"PROJECT_DIRECTORY_ENVIRONMENT_VARIABLE",
|
|
9
|
+
"REVIEW_CHILD_ENVIRONMENT_VARIABLE",
|
|
10
|
+
"REVIEW_CHILD_MARKER",
|
|
11
|
+
"ALL_PARENT_SESSION_VARIABLES",
|
|
12
|
+
"PROMPT_EVENT_NAME",
|
|
13
|
+
"STOP_EVENT_NAME",
|
|
14
|
+
"HOOK_EVENT_NAME_KEY",
|
|
15
|
+
"SESSION_ID_KEY",
|
|
16
|
+
"DECISION_KEY",
|
|
17
|
+
"REASON_KEY",
|
|
18
|
+
"BLOCK_DECISION",
|
|
19
|
+
"GIT_DIRECTORY_NAME",
|
|
20
|
+
"GIT_COMMAND_TIMEOUT_SECONDS",
|
|
21
|
+
"GIT_INDEX_FILE_ENVIRONMENT_VARIABLE",
|
|
22
|
+
"TEMPORARY_INDEX_FILE_NAME",
|
|
23
|
+
"ALL_INDEX_PATH_ARGUMENTS",
|
|
24
|
+
"ALL_CHANGED_PATH_ARGUMENTS",
|
|
25
|
+
"CHANGED_PATH_SEPARATOR",
|
|
26
|
+
"GIT_OUTPUT_ENCODING",
|
|
27
|
+
"GIT_OUTPUT_ERROR_HANDLING",
|
|
28
|
+
"SNAPSHOT_COMMIT_MESSAGE",
|
|
29
|
+
"ALL_SNAPSHOT_IDENTITY_VARIABLES",
|
|
30
|
+
"MAXIMUM_TURN_FILES",
|
|
31
|
+
"MAXIMUM_BLOCKS_IN_A_ROW",
|
|
32
|
+
"REVIEW_EFFORT",
|
|
33
|
+
"REVIEW_MODEL_ALIAS",
|
|
34
|
+
"REVIEW_TIMEOUT_SECONDS",
|
|
35
|
+
"MINIMUM_REVIEW_SECONDS",
|
|
36
|
+
"REVIEW_PROMPT_TEMPLATE",
|
|
37
|
+
"ALL_REVIEW_ALLOWED_TOOLS",
|
|
38
|
+
"ALL_REVIEW_DISALLOWED_TOOLS",
|
|
39
|
+
"REVIEW_EXECUTABLE_NAME",
|
|
40
|
+
"REVIEW_SETTING_SOURCES_TEXT",
|
|
41
|
+
"REVIEW_TOOLS_TEXT",
|
|
42
|
+
"REVIEW_SETTINGS_TEXT",
|
|
43
|
+
"REVIEW_RESULT_KEY",
|
|
44
|
+
"FINDING_LINE_PATTERN_TEMPLATE",
|
|
45
|
+
"JSON_FINDING_FILE_KEY",
|
|
46
|
+
"JSON_FINDING_LINE_KEY",
|
|
47
|
+
"JSON_FINDING_SUMMARY_KEY",
|
|
48
|
+
"JSON_FINDING_LINE_TEMPLATE",
|
|
49
|
+
"JSON_ARRAY_START",
|
|
50
|
+
"NO_FINDINGS_MARKER",
|
|
51
|
+
"STATE_FILE_TEMPLATE",
|
|
52
|
+
"STATE_BASELINES_KEY",
|
|
53
|
+
"BASELINE_COMMIT_PATTERN",
|
|
54
|
+
"STATE_BLOCKS_KEY",
|
|
55
|
+
"STATE_TEMPORARY_PREFIX",
|
|
56
|
+
"STATE_TEXT_ENCODING",
|
|
57
|
+
"REPOSITORY_FINDINGS_TEMPLATE",
|
|
58
|
+
"FINDING_LINE_SEPARATOR",
|
|
59
|
+
"REPOSITORY_FINDINGS_SEPARATOR",
|
|
60
|
+
"BLOCK_REASON_TEMPLATE",
|
|
61
|
+
"TOO_MANY_FILES_DIAGNOSTIC_TEMPLATE",
|
|
62
|
+
"REVIEW_FAILURE_DIAGNOSTIC_TEMPLATE",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
REPOSITORIES_ENVIRONMENT_VARIABLE = "CLAUDE_TURN_REVIEW_REPOSITORIES"
|
|
66
|
+
REPOSITORY_SEPARATOR = ","
|
|
67
|
+
PROJECT_DIRECTORY_ENVIRONMENT_VARIABLE = "CLAUDE_PROJECT_DIR"
|
|
68
|
+
REVIEW_CHILD_ENVIRONMENT_VARIABLE = "CLAUDE_TURN_REVIEW_CHILD"
|
|
69
|
+
REVIEW_CHILD_MARKER = "1"
|
|
70
|
+
ALL_PARENT_SESSION_VARIABLES = frozenset(
|
|
71
|
+
{
|
|
72
|
+
"CLAUDE_CODE_SESSION_ID",
|
|
73
|
+
"CLAUDE_CODE_REMOTE_SESSION_ID",
|
|
74
|
+
"CLAUDE_CODE_MESSAGING_SOCKET",
|
|
75
|
+
"CLAUDE_CODE_MESSAGING_TOKEN",
|
|
76
|
+
"CLAUDE_SESSION_INGRESS_TOKEN_FILE",
|
|
77
|
+
"CLAUDE_CODE_POST_FOR_SESSION_INGRESS_V2",
|
|
78
|
+
"CLAUDE_CODE_TEE_SDK_STDOUT",
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
PROMPT_EVENT_NAME = "UserPromptSubmit"
|
|
82
|
+
STOP_EVENT_NAME = "Stop"
|
|
83
|
+
HOOK_EVENT_NAME_KEY = "hook_event_name"
|
|
84
|
+
SESSION_ID_KEY = "session_id"
|
|
85
|
+
DECISION_KEY = "decision"
|
|
86
|
+
REASON_KEY = "reason"
|
|
87
|
+
BLOCK_DECISION = "block"
|
|
88
|
+
GIT_DIRECTORY_NAME = ".git"
|
|
89
|
+
GIT_COMMAND_TIMEOUT_SECONDS = 30
|
|
90
|
+
GIT_INDEX_FILE_ENVIRONMENT_VARIABLE = "GIT_INDEX_FILE"
|
|
91
|
+
TEMPORARY_INDEX_FILE_NAME = "index"
|
|
92
|
+
ALL_INDEX_PATH_ARGUMENTS = ("rev-parse", "--git-path", "index")
|
|
93
|
+
ALL_CHANGED_PATH_ARGUMENTS = ("diff", "--name-only", "-z")
|
|
94
|
+
CHANGED_PATH_SEPARATOR = "\0"
|
|
95
|
+
GIT_OUTPUT_ENCODING = "utf-8"
|
|
96
|
+
GIT_OUTPUT_ERROR_HANDLING = "surrogateescape"
|
|
97
|
+
SNAPSHOT_COMMIT_MESSAGE = "turn review snapshot"
|
|
98
|
+
ALL_SNAPSHOT_IDENTITY_VARIABLES = {
|
|
99
|
+
"GIT_AUTHOR_NAME": "turn review",
|
|
100
|
+
"GIT_AUTHOR_EMAIL": "turn-review",
|
|
101
|
+
"GIT_COMMITTER_NAME": "turn review",
|
|
102
|
+
"GIT_COMMITTER_EMAIL": "turn-review",
|
|
103
|
+
}
|
|
104
|
+
MAXIMUM_TURN_FILES = 30
|
|
105
|
+
MAXIMUM_BLOCKS_IN_A_ROW = 3
|
|
106
|
+
REVIEW_EFFORT = "low"
|
|
107
|
+
REVIEW_MODEL_ALIAS = "opus"
|
|
108
|
+
REVIEW_TIMEOUT_SECONDS = 240
|
|
109
|
+
MINIMUM_REVIEW_SECONDS = 1
|
|
110
|
+
REVIEW_PROMPT_TEMPLATE = "/code-review {effort} {base_commit}..{turn_commit}"
|
|
111
|
+
ALL_REVIEW_ALLOWED_TOOLS = (
|
|
112
|
+
"Read",
|
|
113
|
+
"Grep",
|
|
114
|
+
"Glob",
|
|
115
|
+
"Bash(git diff:*)",
|
|
116
|
+
"Bash(git show:*)",
|
|
117
|
+
"Bash(git log:*)",
|
|
118
|
+
)
|
|
119
|
+
ALL_REVIEW_DISALLOWED_TOOLS = (
|
|
120
|
+
"Bash(git * -c *)",
|
|
121
|
+
"Bash(git *--config-env*)",
|
|
122
|
+
"Bash(git *--output*)",
|
|
123
|
+
"Bash(git *--ext-diff*)",
|
|
124
|
+
"Bash(git *--textconv*)",
|
|
125
|
+
"Bash(git *--git-dir*)",
|
|
126
|
+
"Bash(git *--work-tree*)",
|
|
127
|
+
"Bash(git *--exec-path*)",
|
|
128
|
+
)
|
|
129
|
+
REVIEW_EXECUTABLE_NAME = "claude"
|
|
130
|
+
REVIEW_SETTING_SOURCES_TEXT = ""
|
|
131
|
+
REVIEW_TOOLS_TEXT = "Read,Grep,Glob,Bash"
|
|
132
|
+
REVIEW_SETTINGS_TEXT = '{"disableAllHooks": true}'
|
|
133
|
+
REVIEW_RESULT_KEY = "result"
|
|
134
|
+
FINDING_LINE_PATTERN_TEMPLATE = r"^\s*(?:[-*+]|\d+[.)])?\s*[`*_]*{path}:\d+"
|
|
135
|
+
JSON_FINDING_FILE_KEY = "file"
|
|
136
|
+
JSON_FINDING_LINE_KEY = "line"
|
|
137
|
+
JSON_FINDING_SUMMARY_KEY = "summary"
|
|
138
|
+
JSON_FINDING_LINE_TEMPLATE = "{path}:{line} {summary}"
|
|
139
|
+
JSON_ARRAY_START = "["
|
|
140
|
+
NO_FINDINGS_MARKER = "(none)"
|
|
141
|
+
STATE_FILE_TEMPLATE = "turn-review-{session_id}.json"
|
|
142
|
+
STATE_BASELINES_KEY = "baselines"
|
|
143
|
+
BASELINE_COMMIT_PATTERN = r"[0-9a-f]{40}|[0-9a-f]{64}"
|
|
144
|
+
STATE_BLOCKS_KEY = "blocks_in_a_row"
|
|
145
|
+
STATE_TEMPORARY_PREFIX = ".turn-review-"
|
|
146
|
+
STATE_TEXT_ENCODING = "utf-8"
|
|
147
|
+
REPOSITORY_FINDINGS_TEMPLATE = "{repository}:\n{findings}"
|
|
148
|
+
FINDING_LINE_SEPARATOR = "\n"
|
|
149
|
+
REPOSITORY_FINDINGS_SEPARATOR = "\n\n"
|
|
150
|
+
BLOCK_REASON_TEMPLATE = (
|
|
151
|
+
"A fresh-context review of this turn's changes found these problems. "
|
|
152
|
+
"Fix each one, or say in your reply why it is wrong, before you end the turn:\n\n"
|
|
153
|
+
"{all_findings}"
|
|
154
|
+
)
|
|
155
|
+
TOO_MANY_FILES_DIAGNOSTIC_TEMPLATE = (
|
|
156
|
+
"[turn_review_gate] {repository}: {count} files changed this turn, over the "
|
|
157
|
+
"{limit}-file limit, so no review ran\n"
|
|
158
|
+
)
|
|
159
|
+
REVIEW_FAILURE_DIAGNOSTIC_TEMPLATE = (
|
|
160
|
+
"[turn_review_gate] review of {repository} did not finish: {reason}\n"
|
|
161
|
+
)
|
package/package.json
CHANGED
|
@@ -145,6 +145,7 @@ def _names_exempt_registration_path(registered_string: str) -> bool:
|
|
|
145
145
|
hooks/blocking/owned_pr_merge_stop_gate.py -> exempt
|
|
146
146
|
hooks/blocking/session_title_turn_gate.py -> exempt
|
|
147
147
|
hooks/blocking/resolve_thread_reply_gate.py -> exempt
|
|
148
|
+
hooks/blocking/turn_review_gate.py -> exempt
|
|
148
149
|
hooks/blocking/some_new_blocker.py -> flagged
|
|
149
150
|
|
|
150
151
|
The Bash PreToolUse dispatcher sits under ``blocking/`` for layout reasons
|
|
@@ -207,6 +208,11 @@ def _names_exempt_registration_path(registered_string: str) -> bool:
|
|
|
207
208
|
thread has posted. It decides the order of two review calls and no code or
|
|
208
209
|
safety policy.
|
|
209
210
|
|
|
211
|
+
The turn review Stop gate holds the end of a turn while a fresh review run
|
|
212
|
+
reports a problem in the turn's edits. The review run reads the code and
|
|
213
|
+
the agent fixes or answers each finding; the gate itself only decides when
|
|
214
|
+
a turn may end.
|
|
215
|
+
|
|
210
216
|
Args:
|
|
211
217
|
registered_string: One command, path, script, or entrypoint string.
|
|
212
218
|
"""
|
|
@@ -84,6 +84,7 @@ ALL_ACTION_BOUNDARY_EXEMPT_REGISTRATION_PATHS = (
|
|
|
84
84
|
"blocking/owned_pr_merge_stop_gate.py",
|
|
85
85
|
"blocking/session_title_turn_gate.py",
|
|
86
86
|
"blocking/resolve_thread_reply_gate.py",
|
|
87
|
+
"blocking/turn_review_gate.py",
|
|
87
88
|
)
|
|
88
89
|
ALL_TEST_DIRECTORY_NAMES = frozenset({"tests"})
|
|
89
90
|
ALL_TEST_FILE_PREFIXES = ("test_",)
|
|
@@ -66,6 +66,14 @@ def test_new_resolve_thread_reply_gate_registration_is_clean(tmp_path: Path) ->
|
|
|
66
66
|
assert adapters.hook_configuration_diagnostics(current_document, tmp_path) == ()
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
def test_new_turn_review_gate_registration_is_clean(tmp_path: Path) -> None:
|
|
70
|
+
current_document = _hook_document(
|
|
71
|
+
["hooks/blocking/turn_review_gate.py"],
|
|
72
|
+
'{"hooks": {}}',
|
|
73
|
+
)
|
|
74
|
+
assert adapters.hook_configuration_diagnostics(current_document, tmp_path) == ()
|
|
75
|
+
|
|
76
|
+
|
|
69
77
|
def test_new_merge_offer_gate_registration_is_clean(tmp_path: Path) -> None:
|
|
70
78
|
current_document = _hook_document(
|
|
71
79
|
["hooks/blocking/merge_over_review_offer_gate.py"],
|