claude-dev-env 8.46.19 → 8.46.21

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.
@@ -42,7 +42,7 @@ for each_member in all_members:
42
42
 
43
43
  The re-raise comes first, sending an escalation directly through the boundary.
44
44
 
45
- **The boundary recognizes declared types.** A runtime crash inside member work — a `TypeError`, an `AttributeError` — ends the run because its type identifies a code defect. `except Exception` triggers the rule (`CODE_RULES.md` §31).
45
+ **The boundary recognizes declared types.** A runtime crash inside member work — a `TypeError`, an `AttributeError` — ends the run because its type identifies a code defect. `except Exception` triggers the rule (`CODE_RULES.md` §9.7).
46
46
 
47
47
  ## Repair, park, and the deliverable
48
48
 
@@ -431,6 +431,26 @@ def _existing_scratchpad_root(
431
431
  return None
432
432
 
433
433
 
434
+ def _is_at_or_under_payload_working_directory(file_path: str, hook_payload: dict) -> bool:
435
+ """Return True when file_path's real path sits at or under the payload ``cwd``.
436
+
437
+ Args:
438
+ file_path: The path the write targets.
439
+ hook_payload: The PreToolUse payload that may carry ``cwd``.
440
+
441
+ Returns:
442
+ True when the payload names a working directory and file_path resolves
443
+ at or under its real path.
444
+ """
445
+ working_directory = str(hook_payload.get("cwd") or "")
446
+ if not working_directory:
447
+ return False
448
+ return _is_at_or_under_root(
449
+ _normalized_absolute_comparison_path(os.path.realpath(file_path)),
450
+ _normalized_absolute_comparison_path(os.path.realpath(working_directory)),
451
+ )
452
+
453
+
434
454
  def is_under_session_scratchpad(file_path: str, hook_payload: dict) -> bool:
435
455
  """Return True when file_path resolves under the harness session scratchpad.
436
456
 
@@ -460,13 +480,9 @@ def is_under_session_scratchpad(file_path: str, hook_payload: dict) -> bool:
460
480
  session_id = _session_id_for_scratchpad(hook_payload)
461
481
  if not session_id:
462
482
  return False
463
- real_target = os.path.realpath(file_path)
464
- working_directory = str(hook_payload.get("cwd") or "")
465
- if working_directory and _is_at_or_under_root(
466
- _normalized_absolute_comparison_path(real_target),
467
- _normalized_absolute_comparison_path(os.path.realpath(working_directory)),
468
- ):
483
+ if _is_at_or_under_payload_working_directory(file_path, hook_payload):
469
484
  return False
485
+ real_target = os.path.realpath(file_path)
470
486
  real_temp_root = os.path.realpath(tempfile.gettempdir())
471
487
  all_relative_parts = _relative_parts_under_temp_root(real_target, real_temp_root)
472
488
  return (
@@ -482,10 +498,10 @@ def is_ephemeral_path(file_path: str, hook_payload: dict | None = None) -> bool:
482
498
  scratch directories (``/tmp`` and ``$CLAUDE_JOB_DIR/tmp``), the harness
483
499
  session scratchpad, and a coding agent's own home-directory tooling such as
484
500
  ``~/.grok/runs/``. A path inside the payload's own working directory
485
- (``cwd``) is never treated as scratch, so a repository checked out under
486
- ``/tmp`` still receives full enforcement. The scratchpad match reads the
487
- session id from the payload when supplied, else the harness environment
488
- variable. The code-rules and TDD gates call the underlying predicates
501
+ (``cwd``) is never treated as scratch or agent tooling, so a repository
502
+ checked out under ``/tmp`` or ``~/.grok/runs/`` still receives full
503
+ enforcement. The scratchpad match reads the session id from the payload
504
+ when supplied, else the harness environment variable. The code-rules and TDD gates call the underlying predicates
489
505
  ``is_ephemeral_script_path``, ``is_agent_home_tooling``, and
490
506
  ``is_under_session_scratchpad`` directly.
491
507
 
@@ -501,7 +517,9 @@ def is_ephemeral_path(file_path: str, hook_payload: dict | None = None) -> bool:
501
517
  repository_root = str((hook_payload or {}).get("cwd") or "")
502
518
  if is_ephemeral_script_path(file_path, repository_root):
503
519
  return True
504
- if is_agent_home_tooling(file_path):
520
+ if is_agent_home_tooling(file_path) and not _is_at_or_under_payload_working_directory(
521
+ file_path, hook_payload or {}
522
+ ):
505
523
  return True
506
524
  return is_under_session_scratchpad(file_path, hook_payload or {})
507
525
 
@@ -357,3 +357,20 @@ def test_ephemeral_path_stays_true_for_scratchpad_file_outside_payload_cwd(
357
357
  assert (
358
358
  _SHARED_MODULE.is_ephemeral_path(str(throwaway_script), payload_with_worktree_cwd) is True
359
359
  )
360
+
361
+
362
+ def test_ephemeral_path_is_false_for_file_under_payload_cwd_inside_agent_home() -> None:
363
+ checkout_directory = os.path.join("/home/example", ".grok", "runs", "checkout")
364
+ repository_module = os.path.join(checkout_directory, "src", "service.py")
365
+
366
+ assert (
367
+ _SHARED_MODULE.is_ephemeral_path(repository_module, {"cwd": checkout_directory})
368
+ is False
369
+ )
370
+
371
+
372
+ def test_ephemeral_path_stays_true_for_agent_home_file_outside_payload_cwd() -> None:
373
+ checkout_directory = os.path.join("/home/example", ".grok", "runs", "checkout")
374
+ helper_script = os.path.join("/home/example", ".grok", "runs", "worktree-health", "health.py")
375
+
376
+ assert _SHARED_MODULE.is_ephemeral_path(helper_script, {"cwd": checkout_directory}) is True
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.46.19",
3
+ "version": "8.46.21",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,7 +27,8 @@ _TEXT_SUFFIXES = frozenset({".md", ".py", ".js", ".mjs", ".ts", ".json", ".xml"}
27
27
  _MARKDOWN_LINK = re.compile(r"(?<!!)\[[^]]+\]\(([^)\s]+)(?:\s+[^)]*)?\)")
28
28
  _INDEX_ANCHOR = re.compile(r"CODE_RULES\.md#([^\s)\]<>\"`]+)", re.IGNORECASE)
29
29
  _NUMBERED_CITATION = re.compile(
30
- r"(?:CODE_RULES\s*\u00a7\s*|\bsection\s+)(\d+(?:\.\d+)?)", re.IGNORECASE
30
+ r"CODE_RULES(?:\.md)?[`'\"]*\s*(?:\u00a7\s*|section\s+)(\d+(?:\.\d+)?)",
31
+ re.IGNORECASE,
31
32
  )
32
33
 
33
34
 
@@ -129,6 +130,27 @@ def test_should_report_dead_code_rules_anchor() -> None:
129
130
  _assert_citations_resolve(index_text, [dead_citation])
130
131
 
131
132
 
133
+ def test_should_ignore_section_number_of_another_document() -> None:
134
+ index_text = _INDEX_PATH.read_text(encoding="utf-8")
135
+ foreign_citation = (_INDEX_PATH, "See the runbook section 12 appendix row.")
136
+ _assert_citations_resolve(index_text, [foreign_citation])
137
+
138
+
139
+ @pytest.mark.parametrize(
140
+ "citation_text",
141
+ [
142
+ "CODE_RULES.md" + " section 12",
143
+ "``docs/CODE_RULES.md``" + " section 12",
144
+ "CODE_RULES" + " \u00a712",
145
+ "CODE_RULES.md" + " \u00a712",
146
+ ],
147
+ )
148
+ def test_should_report_dead_code_rules_section(citation_text: str) -> None:
149
+ index_text = _INDEX_PATH.read_text(encoding="utf-8")
150
+ with pytest.raises(AssertionError, match="dead CODE_RULES" + " section 12"):
151
+ _assert_citations_resolve(index_text, [(_INDEX_PATH, citation_text)])
152
+
153
+
132
154
  def _assert_destination_resolves(markdown_path: Path, destination: str) -> None:
133
155
  if "://" in destination or destination.startswith("#"):
134
156
  return