claude-dev-env 8.102.1 → 8.103.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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.102.1",
3
+ "version": "8.103.0",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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"],