claude-dev-env 8.47.2 → 8.47.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.
@@ -207,17 +207,21 @@ def aggregate_bash_hook_results(
207
207
 
208
208
 
209
209
  def _emit_decision(decision: BashDispatcherDecision) -> None:
210
- """Write one PreToolUse permission payload to stdout when an outcome exists."""
210
+ """Write one PreToolUse permission payload to stdout when an outcome exists.
211
+
212
+ A rewrite carries updatedInput with no permissionDecision, so the harness
213
+ applies the rewritten command and then runs its normal permission prompt.
214
+ """
211
215
  if not decision.decision:
212
216
  return
213
- hook_specific: dict[str, object] = {
214
- "hookEventName": HOOK_EVENT_NAME,
215
- _PERMISSION_DECISION_KEY: decision.decision,
216
- }
217
+ hook_specific: dict[str, object] = {"hookEventName": HOOK_EVENT_NAME}
218
+ is_rewrite = decision.decision == ALLOW_DECISION and decision.updated_input is not None
219
+ if is_rewrite:
220
+ hook_specific[_UPDATED_INPUT_KEY] = decision.updated_input
221
+ else:
222
+ hook_specific[_PERMISSION_DECISION_KEY] = decision.decision
217
223
  if decision.reasons:
218
224
  hook_specific[_PERMISSION_REASON_KEY] = REASON_JOIN_SEPARATOR.join(decision.reasons)
219
- if decision.decision == ALLOW_DECISION and decision.updated_input is not None:
220
- hook_specific[_UPDATED_INPUT_KEY] = decision.updated_input
221
225
  if decision.all_additional_context:
222
226
  hook_specific[_ADDITIONAL_CONTEXT_KEY] = CONTEXT_JOIN_SEPARATOR.join(
223
227
  decision.all_additional_context
@@ -235,3 +235,19 @@ def test_emit_decision_re_emits_silent_deny_fields(
235
235
  assert hook_specific["additionalContext"] == "see docs/runbook.md"
236
236
  assert emitted_payload["systemMessage"] == "[gh-gate] blocked redirected gh pr create"
237
237
  assert emitted_payload["suppressOutput"] is True
238
+
239
+
240
+ def test_emit_decision_sends_a_rewrite_through_the_permission_prompt(
241
+ capsys: pytest.CaptureFixture[str],
242
+ ) -> None:
243
+ """A rewritten command reaches the harness with updatedInput and no permissionDecision."""
244
+ rewrite = BashDispatcherDecision(
245
+ decision="allow",
246
+ updated_input={"command": "MSYS2_ARG_CONV_EXCL='*' git show HEAD:a.py"},
247
+ )
248
+ _emit_decision(rewrite)
249
+ hook_specific = json.loads(capsys.readouterr().out.strip())["hookSpecificOutput"]
250
+ assert "permissionDecision" not in hook_specific
251
+ assert hook_specific["updatedInput"] == {
252
+ "command": "MSYS2_ARG_CONV_EXCL='*' git show HEAD:a.py"
253
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.47.2",
3
+ "version": "8.47.3",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -54,7 +54,6 @@ export const PRE_TOOL_USE_EVENT_NAME = 'PreToolUse';
54
54
  export const SESSION_START_HOOK_SCRIPT_NAME = 'working_style_prompt.py';
55
55
  export const BASH_HOOK_SCRIPT_NAME = 'bash_pre_tool_use_dispatcher.py';
56
56
  export const BASH_MATCHER = 'Bash';
57
- export const ALLOW_DECISION = 'allow';
58
57
 
59
58
  export const SESSION_START_PAYLOAD = {
60
59
  hook_event_name: SESSION_START_EVENT_NAME,
@@ -238,8 +237,8 @@ function runBlockingStage(settingsPath, scratchHome, evidenceDirectory) {
238
237
  );
239
238
  const hookResult = runInstalledHook(command, scratchHome, BASH_HOOK_PAYLOAD);
240
239
  const envelope = readEnvelope(hookResult.stdout, PRE_TOOL_USE_EVENT_NAME);
241
- if (envelope.permissionDecision !== ALLOW_DECISION) {
242
- throw new Error(`The PreToolUse envelope decided ${envelope.permissionDecision}`);
240
+ if (envelope.permissionDecision !== undefined) {
241
+ throw new Error(`The PreToolUse envelope skipped the permission prompt with ${envelope.permissionDecision}`);
243
242
  }
244
243
  const rewrittenCommand = envelope.updatedInput?.command ?? '';
245
244
  if (!rewrittenCommand.includes(REWRITE_MARKER)) {