claude-dev-env 8.54.3 → 8.54.5

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.
@@ -38,6 +38,8 @@ from hooks_constants.bash_post_call_dispatcher_constants import (
38
38
  ADDITIONAL_CONTEXT_JOIN_SEPARATOR,
39
39
  ADDITIONAL_CONTEXT_KEY,
40
40
  ALL_BASH_POST_TOOL_USE_HOSTED_HOOK_ENTRIES,
41
+ )
42
+ from hooks_constants.hook_specific_output_keys import (
41
43
  HOOK_SPECIFIC_OUTPUT_KEY,
42
44
  )
43
45
  from hooks_constants.post_tool_use_context import write_post_tool_use_context_to_stdout
@@ -34,11 +34,9 @@ from hooks_constants.pr_lifecycle_skill_gate_constants import (
34
34
  GIT_ACTIONS,
35
35
  GIT_GLOBAL_OPTIONS_WITH_VALUE,
36
36
  HOOK_EVENT_NAME,
37
- HOOK_SPECIFIC_OUTPUT_KEY,
38
37
  MERGE_PATH_SUFFIX,
39
38
  OPTION_AND_VALUE_WORD_COUNT,
40
39
  PATH_SEPARATOR_PATTERN,
41
- PERMISSION_DECISION_KEY,
42
40
  PERMISSION_DECISION_REASON_KEY,
43
41
  PULL_REQUEST_SCRIPT_NAME,
44
42
  PYTHON_OPTIONS_WITH_VALUE,
@@ -48,6 +46,10 @@ from hooks_constants.pr_lifecycle_skill_gate_constants import (
48
46
  SUBAGENT_TRANSCRIPT_DIRECTORY_NAME,
49
47
  SUBAGENT_TRANSCRIPT_FILE_TEMPLATE,
50
48
  )
49
+ from hooks_constants.hook_specific_output_keys import (
50
+ HOOK_SPECIFIC_OUTPUT_KEY,
51
+ PERMISSION_DECISION_KEY,
52
+ )
51
53
  from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin
52
54
  from hooks_constants.setup_project_paths_constants import DECODE_ERRORS_POLICY, UTF8_ENCODING
53
55
  from blocking.followup_pr_dedupe import duplicate_followup_reason
@@ -42,9 +42,11 @@ if hooks_root_directory not in sys.path:
42
42
  sys.path.insert(0, hooks_root_directory)
43
43
 
44
44
  from hooks_constants.bash_post_call_dispatcher_constants import (
45
+ POST_TOOL_USE_HOOK_EVENT_NAME,
46
+ )
47
+ from hooks_constants.hook_specific_output_keys import (
45
48
  HOOK_EVENT_NAME_KEY,
46
49
  HOOK_SPECIFIC_OUTPUT_KEY,
47
- POST_TOOL_USE_HOOK_EVENT_NAME,
48
50
  )
49
51
  from hooks_constants.hook_block_logger import log_hook_block
50
52
  from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin
@@ -22,8 +22,6 @@ __all__ = [
22
22
  "ALL_BASH_POST_TOOL_USE_HOSTED_HOOK_ENTRIES",
23
23
  "EXIT_CODE_ERROR_PREFIX",
24
24
  "POST_TOOL_USE_HOOK_EVENT_NAME",
25
- "HOOK_SPECIFIC_OUTPUT_KEY",
26
- "HOOK_EVENT_NAME_KEY",
27
25
  "ADDITIONAL_CONTEXT_KEY",
28
26
  "ADDITIONAL_CONTEXT_JOIN_SEPARATOR",
29
27
  ]
@@ -34,7 +32,5 @@ ALL_BASH_POST_TOOL_USE_HOSTED_HOOK_ENTRIES: tuple[BashHostedHookEntry, ...] = (
34
32
 
35
33
  EXIT_CODE_ERROR_PREFIX: str = "Error: Exit code "
36
34
  POST_TOOL_USE_HOOK_EVENT_NAME: str = "PostToolUse"
37
- HOOK_SPECIFIC_OUTPUT_KEY: str = "hookSpecificOutput"
38
- HOOK_EVENT_NAME_KEY: str = "hookEventName"
39
35
  ADDITIONAL_CONTEXT_KEY: str = "additionalContext"
40
36
  ADDITIONAL_CONTEXT_JOIN_SEPARATOR: str = "\n\n"
@@ -1,4 +1,4 @@
1
- """JSON keys of the hookSpecificOutput object a PreToolUse hook writes to stdout."""
1
+ """JSON keys of the hookSpecificOutput object a hook writes to stdout."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -29,9 +29,6 @@ ALL_SHELL_PROGRAM_NAMES: tuple[str, ...] = ("bash", "sh")
29
29
  SHELL_COMMAND_FLAG: str = "-c"
30
30
 
31
31
  BLOCKING_EXIT_CODE: int = 2
32
- HOOK_SPECIFIC_OUTPUT_KEY: str = "hookSpecificOutput"
33
- HOOK_EVENT_NAME_OUTPUT_KEY: str = "hookEventName"
34
- PERMISSION_DECISION_KEY: str = "permissionDecision"
35
32
  ALL_FORWARDED_PERMISSION_DECISIONS: frozenset[str] = frozenset({"deny", "ask"})
36
33
  ADDITIONAL_CONTEXT_KEY: str = "additionalContext"
37
34
  CONTEXT_SECTION_SEPARATOR: str = "\n\n"
@@ -7,9 +7,11 @@ import sys
7
7
 
8
8
  from hooks_constants.bash_post_call_dispatcher_constants import (
9
9
  ADDITIONAL_CONTEXT_KEY,
10
+ POST_TOOL_USE_HOOK_EVENT_NAME,
11
+ )
12
+ from hooks_constants.hook_specific_output_keys import (
10
13
  HOOK_EVENT_NAME_KEY,
11
14
  HOOK_SPECIFIC_OUTPUT_KEY,
12
- POST_TOOL_USE_HOOK_EVENT_NAME,
13
15
  )
14
16
 
15
17
 
@@ -1,8 +1,6 @@
1
1
  """Constants for the pull request lifecycle skill gate."""
2
2
 
3
3
  HOOK_EVENT_NAME = "PreToolUse"
4
- HOOK_SPECIFIC_OUTPUT_KEY = "hookSpecificOutput"
5
- PERMISSION_DECISION_KEY = "permissionDecision"
6
4
  PERMISSION_DECISION_REASON_KEY = "permissionDecisionReason"
7
5
  DENY_DECISION = "deny"
8
6
  DENY_REASON = (
@@ -9,7 +9,7 @@ from hooks_constants.bash_pre_tool_use_dispatcher_constants import (
9
9
  ALLOW_DECISION,
10
10
  HOOK_EVENT_NAME,
11
11
  )
12
- from hooks_constants.pre_tool_use_output_keys import (
12
+ from hooks_constants.hook_specific_output_keys import (
13
13
  HOOK_EVENT_NAME_KEY,
14
14
  HOOK_SPECIFIC_OUTPUT_KEY,
15
15
  PERMISSION_DECISION_KEY,
@@ -89,8 +89,6 @@ ALL_INTERACTIVE_MCP_ACTIONS = frozenset({"ask_decision", "post_widget"})
89
89
  SCOPE_SETTLED_PREFIX = "Scope settled:"
90
90
 
91
91
  PRE_TOOL_USE_EVENT_NAME = "PreToolUse"
92
- HOOK_SPECIFIC_OUTPUT_KEY = "hookSpecificOutput"
93
- HOOK_EVENT_NAME_KEY = "hookEventName"
94
92
  ADDITIONAL_CONTEXT_KEY = "additionalContext"
95
93
  MISSING_INVESTIGATION_REASON = (
96
94
  "This spawn comes before any read of the request. Read the files, threads, "
@@ -24,7 +24,6 @@ def test_pr_done_reminder_also_serves_the_powershell_tool() -> None:
24
24
 
25
25
  def test_context_forwarding_keys_match_the_hook_output_contract() -> None:
26
26
  assert constants.POST_TOOL_USE_HOOK_EVENT_NAME == "PostToolUse"
27
- assert constants.HOOK_SPECIFIC_OUTPUT_KEY == "hookSpecificOutput"
28
27
  assert constants.ADDITIONAL_CONTEXT_KEY == "additionalContext"
29
28
  assert constants.ADDITIONAL_CONTEXT_JOIN_SEPARATOR == "\n\n"
30
29
 
@@ -0,0 +1,17 @@
1
+ """Tests for the hookSpecificOutput key names."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from hooks_constants import hook_specific_output_keys
6
+
7
+
8
+ def test_keys_match_the_harness_hook_specific_output_field_names() -> None:
9
+ assert {
10
+ each_name: getattr(hook_specific_output_keys, each_name)
11
+ for each_name in hook_specific_output_keys.__all__
12
+ } == {
13
+ "HOOK_SPECIFIC_OUTPUT_KEY": "hookSpecificOutput",
14
+ "HOOK_EVENT_NAME_KEY": "hookEventName",
15
+ "PERMISSION_DECISION_KEY": "permissionDecision",
16
+ "UPDATED_INPUT_KEY": "updatedInput",
17
+ }
@@ -48,13 +48,10 @@ from hooks_constants.nested_project_hooks_constants import (
48
48
  CONTEXT_SECTION_SEPARATOR,
49
49
  DEFAULT_HOOK_TIMEOUT_SECONDS,
50
50
  HOOK_EVENT_NAME_KEY,
51
- HOOK_EVENT_NAME_OUTPUT_KEY,
52
- HOOK_SPECIFIC_OUTPUT_KEY,
53
51
  HOOK_TYPE_KEY,
54
52
  HOOKS_KEY,
55
53
  ALL_MATCH_TARGET_KEYS_BY_EVENT,
56
54
  MATCHER_KEY,
57
- PERMISSION_DECISION_KEY,
58
55
  PRE_TOOL_USE_EVENT,
59
56
  PROJECT_DIRECTORY_ENVIRONMENT_VARIABLE,
60
57
  SESSION_START_EVENT,
@@ -65,6 +62,11 @@ from hooks_constants.nested_project_hooks_constants import (
65
62
  ALL_SHELL_PROGRAM_NAMES,
66
63
  TIMEOUT_KEY,
67
64
  )
65
+ from hooks_constants.hook_specific_output_keys import (
66
+ HOOK_EVENT_NAME_KEY as HOOK_EVENT_NAME_OUTPUT_KEY,
67
+ HOOK_SPECIFIC_OUTPUT_KEY,
68
+ PERMISSION_DECISION_KEY,
69
+ )
68
70
  from hooks_constants.subprocess_window import hidden_window_creation_flags
69
71
 
70
72
 
@@ -48,8 +48,6 @@ from hooks_constants.spawn_readiness_hook_constants import (
48
48
  DISPATCH_METHOD_INPUT_KEY,
49
49
  DISPATCH_PROMPT_INPUT_KEY,
50
50
  DISPATCH_RUN_WORKFLOW_METHOD,
51
- HOOK_EVENT_NAME_KEY,
52
- HOOK_SPECIFIC_OUTPUT_KEY,
53
51
  LOG_APPEND_MODE,
54
52
  LOG_LINE_END,
55
53
  LOG_OUTCOME_KEY,
@@ -75,6 +73,10 @@ from hooks_constants.spawn_readiness_hook_constants import (
75
73
  WORKFLOW_SCRIPT_PATH_INPUT_KEY,
76
74
  WORKFLOW_TOOL_NAME,
77
75
  )
76
+ from hooks_constants.hook_specific_output_keys import (
77
+ HOOK_EVENT_NAME_KEY,
78
+ HOOK_SPECIFIC_OUTPUT_KEY,
79
+ )
78
80
  from spawn_readiness_steps import readiness_gaps, session_steps
79
81
 
80
82
 
@@ -25,7 +25,7 @@ if hooks_root_directory not in sys.path:
25
25
  sys.path.insert(0, hooks_root_directory)
26
26
 
27
27
  from hooks_constants.bash_pre_tool_use_dispatcher_constants import HOOK_EVENT_NAME
28
- from hooks_constants.pre_tool_use_output_keys import (
28
+ from hooks_constants.hook_specific_output_keys import (
29
29
  HOOK_EVENT_NAME_KEY,
30
30
  HOOK_SPECIFIC_OUTPUT_KEY,
31
31
  UPDATED_INPUT_KEY,
@@ -28,7 +28,7 @@ if hooks_root_directory not in sys.path:
28
28
  sys.path.insert(0, hooks_root_directory)
29
29
 
30
30
  from hooks_constants.bash_pre_tool_use_dispatcher_constants import HOOK_EVENT_NAME
31
- from hooks_constants.pre_tool_use_output_keys import (
31
+ from hooks_constants.hook_specific_output_keys import (
32
32
  HOOK_EVENT_NAME_KEY,
33
33
  HOOK_SPECIFIC_OUTPUT_KEY,
34
34
  PERMISSION_DECISION_KEY,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.54.3",
3
+ "version": "8.54.5",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,9 +15,32 @@ is in place by the time any test module runs its top-level imports.
15
15
 
16
16
  from __future__ import annotations
17
17
 
18
+ import importlib
18
19
  import sys
19
20
  from pathlib import Path
20
21
 
22
+ import pytest
23
+
21
24
  _scripts_directory = str(Path(__file__).resolve().parent)
22
25
  if _scripts_directory not in sys.path:
23
26
  sys.path.insert(0, _scripts_directory)
27
+
28
+ _live_broker_state_resolver = importlib.import_module("account_broker_support").broker_state_path
29
+
30
+
31
+ @pytest.fixture(autouse=True)
32
+ def isolated_broker_state_path(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> Path:
33
+ """Point every module's ``broker_state_path`` at a per-test file.
34
+
35
+ ``account_broker`` binds ``broker_state_path`` by name from
36
+ ``account_broker_support``, so each bound copy is replaced. A test that
37
+ patches only one copy still keeps the other off the home directory.
38
+
39
+ Returns:
40
+ The isolated broker state path for this test.
41
+ """
42
+ isolated_path = tmp_path / "account-broker" / "state.json"
43
+ for each_module in list(sys.modules.values()):
44
+ if getattr(each_module, "broker_state_path", None) is _live_broker_state_resolver:
45
+ monkeypatch.setattr(each_module, "broker_state_path", lambda: isolated_path)
46
+ return isolated_path
@@ -0,0 +1,48 @@
1
+ """Broker state written during a test stays out of the home directory."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import subprocess
6
+ from datetime import datetime, timezone
7
+ from pathlib import Path
8
+
9
+ import pytest
10
+
11
+ import account_broker
12
+ from dev_env_scripts_constants.account_broker_constants import (
13
+ Account,
14
+ Meters,
15
+ Product,
16
+ ProductAdapter,
17
+ )
18
+
19
+
20
+ def test_meter_cache_write_lands_outside_the_home_directory(
21
+ monkeypatch: pytest.MonkeyPatch,
22
+ tmp_path: Path,
23
+ isolated_broker_state_path: Path,
24
+ ) -> None:
25
+ fake_home = tmp_path / "home"
26
+ monkeypatch.setenv("HOME", str(fake_home))
27
+ monkeypatch.setenv("USERPROFILE", str(fake_home))
28
+ account = Account(Product.CLAUDE, "only", tmp_path / "only")
29
+ reset_at = datetime(2026, 10, 5, tzinfo=timezone.utc)
30
+ adapter = ProductAdapter(
31
+ lambda: (account,),
32
+ lambda each_account: Meters(80, reset_at, 80, reset_at),
33
+ "CLAUDE_CONFIG_DIR",
34
+ ("usage limit",),
35
+ )
36
+ monkeypatch.setitem(account_broker.all_product_adapters, Product.CLAUDE, adapter)
37
+
38
+ def runner(argv: list[str], **options: object) -> subprocess.CompletedProcess[str]:
39
+ return subprocess.CompletedProcess(argv, 0, "", "")
40
+
41
+ with account_broker.override_subprocess_runner(runner):
42
+ account_broker.run_job(Product.CLAUDE, ["claude"])
43
+
44
+ home_state_path = fake_home / ".claude" / "account-broker" / "state.json"
45
+ assert not home_state_path.exists()
46
+ assert f"claude:only:{account.home}" in isolated_broker_state_path.read_text(
47
+ encoding="utf-8"
48
+ )
@@ -1129,7 +1129,6 @@ def test_usage_limit_fallover_delivers_full_prompt_to_each_account(
1129
1129
  return subprocess.CompletedProcess(argv, 0, FIXTURE_CLAUDE_STDOUT, "")
1130
1130
 
1131
1131
  monkeypatch.setitem(account_broker.all_product_adapters, Product.CLAUDE, adapter)
1132
- monkeypatch.setattr(account_broker, "broker_state_path", lambda: tmp_path / "state.json")
1133
1132
  monkeypatch.setattr(
1134
1133
  dispatcher,
1135
1134
  "spawn_preflight_runner",
@@ -1,17 +0,0 @@
1
- """Tests for the PreToolUse hookSpecificOutput key names."""
2
-
3
- from __future__ import annotations
4
-
5
- from hooks_constants import pre_tool_use_output_keys
6
-
7
-
8
- def test_keys_match_the_harness_pre_tool_use_output_field_names() -> None:
9
- assert {
10
- each_name: getattr(pre_tool_use_output_keys, each_name)
11
- for each_name in pre_tool_use_output_keys.__all__
12
- } == {
13
- "HOOK_SPECIFIC_OUTPUT_KEY": "hookSpecificOutput",
14
- "HOOK_EVENT_NAME_KEY": "hookEventName",
15
- "PERMISSION_DECISION_KEY": "permissionDecision",
16
- "UPDATED_INPUT_KEY": "updatedInput",
17
- }