claude-dev-env 8.51.2 → 8.51.3

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.
@@ -9,8 +9,9 @@ stop once and tells the model to set '<emoji> <name>'.
9
9
  The hook stays silent when:
10
10
 
11
11
  - the stop is already a retry after a block (stop_hook_active is true);
12
- - the session shows no title tool: no remote session id in the environment,
13
- and no title tool named anywhere in the transcript;
12
+ - the session shows no title tool: no title tool named anywhere in the
13
+ transcript, and either no remote session id in the environment or a
14
+ deferred tool listing that leaves the title tool out;
14
15
  - the transcript cannot be read.
15
16
  """
16
17
 
@@ -79,6 +80,11 @@ def _title_tool_names_in(all_entry_fields: dict[str, object]) -> list[str]:
79
80
  return [each_name for each_name in all_names if TITLE_TOOL_NAME_PATTERN.match(each_name)]
80
81
 
81
82
 
83
+ def _lists_deferred_tools(all_entry_fields: dict[str, object]) -> bool:
84
+ attachment = all_entry_fields.get("attachment")
85
+ return isinstance(attachment, dict) and attachment.get("type") in DEFERRED_TOOL_ATTACHMENT_TYPES
86
+
87
+
82
88
  def _is_title_call(entry_type: object, all_block_fields: dict[str, object]) -> bool:
83
89
  return (
84
90
  entry_type == "assistant"
@@ -125,10 +131,11 @@ def stop_block_reason(all_entries: list[dict[str, object]], is_remote_session: b
125
131
  prompt -> set_session_title -> ok result => None
126
132
  prompt -> work, no title call => block reason
127
133
  no title tool anywhere, not remote => None
134
+ remote, tool list lacks the title tool => None
128
135
 
129
136
  Args:
130
137
  all_entries: The transcript entries, oldest first.
131
- is_remote_session: True when the session runs in a cloud session with a title tool.
138
+ is_remote_session: True when the session runs in a cloud session.
132
139
 
133
140
  Returns:
134
141
  The reason to send back to the model, or None to let the turn end.
@@ -136,7 +143,9 @@ def stop_block_reason(all_entries: list[dict[str, object]], is_remote_session: b
136
143
  all_seen_tool_names = [
137
144
  each_name for each_entry in all_entries for each_name in _title_tool_names_in(each_entry)
138
145
  ]
139
- if not all_seen_tool_names and not is_remote_session:
146
+ if not all_seen_tool_names and (
147
+ not is_remote_session or any(_lists_deferred_tools(each) for each in all_entries)
148
+ ):
140
149
  return None
141
150
  if _title_set_since(all_entries[_turn_start_index(all_entries) :]):
142
151
  return None
@@ -172,6 +172,33 @@ def test_should_stay_silent_in_a_session_with_no_title_tool(
172
172
  assert run_gate(monkeypatch, capsys, tmp_path, all_entries) == (0, "")
173
173
 
174
174
 
175
+ def test_should_stay_silent_in_a_remote_session_whose_tool_list_lacks_the_title_tool(
176
+ monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], tmp_path: Path
177
+ ) -> None:
178
+ monkeypatch.setenv(REMOTE_SESSION_VARIABLE, "cse_1")
179
+ all_entries = [
180
+ deferred_tools("TaskCreate", "mcp__claude-code-remote__get_session"),
181
+ prompt("Fix the runner"),
182
+ tool_call("t1", "Bash"),
183
+ tool_result("t1"),
184
+ ]
185
+ assert run_gate(monkeypatch, capsys, tmp_path, all_entries) == (0, "")
186
+
187
+
188
+ def test_should_block_a_remote_session_whose_tool_list_names_the_title_tool(
189
+ monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], tmp_path: Path
190
+ ) -> None:
191
+ monkeypatch.setenv(REMOTE_SESSION_VARIABLE, "cse_1")
192
+ all_entries = [
193
+ deferred_tools("TaskCreate", REMOTE_TITLE_TOOL),
194
+ prompt("Fix the runner"),
195
+ tool_call("t1", "Bash"),
196
+ tool_result("t1"),
197
+ ]
198
+ _, stdout_text = run_gate(monkeypatch, capsys, tmp_path, all_entries)
199
+ assert json.loads(stdout_text)["decision"] == "block"
200
+
201
+
175
202
  def test_should_stay_silent_on_the_retry_after_a_block(
176
203
  monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], tmp_path: Path
177
204
  ) -> None:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.51.2",
3
+ "version": "8.51.3",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {