claude-dev-env 8.88.1 → 8.88.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.
@@ -0,0 +1,90 @@
1
+ """Tests for the role-shaped default rewrite."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
9
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
10
+
11
+ from deepen_default_rewrite import role_default_rewrite
12
+ from deepen_role_gate import refusal_reason
13
+ from hooks_constants.deepen_first_prompt_constants import ALL_FOUR_QUESTION_STEMS, DEEPEN_MARKER
14
+
15
+
16
+ def _role_line(rewrite: str, label: str) -> str:
17
+ return next(each for each in rewrite.splitlines() if each.startswith(label))[len(label) :].strip()
18
+
19
+
20
+ def test_should_pass_the_gate_for_a_plain_task() -> None:
21
+ task = "Add dark mode to the settings page"
22
+ rewrite = role_default_rewrite(task)
23
+ assert rewrite.startswith(DEEPEN_MARKER)
24
+ assert refusal_reason(task, rewrite) is None
25
+ assert _role_line(rewrite, "SCOPE (role):") == task
26
+ assert _role_line(rewrite, "EXAMPLES (illustration only):") == "none"
27
+
28
+
29
+ def test_should_not_paste_the_four_question_stems() -> None:
30
+ rewrite = role_default_rewrite("Add dark mode to the settings page")
31
+ assert not any(each_stem in rewrite for each_stem in ALL_FOUR_QUESTION_STEMS)
32
+
33
+
34
+ def test_should_move_a_marked_example_out_of_the_scope() -> None:
35
+ task = "fix the flaky tests, e.g. test_auth and test_cache, and keep CI fast"
36
+ rewrite = role_default_rewrite(task)
37
+ assert refusal_reason(task, rewrite) is None
38
+ assert _role_line(rewrite, "SCOPE (role):") == "fix the flaky tests and keep CI fast"
39
+ assert _role_line(rewrite, "EXAMPLES (illustration only):") == "test_auth and test_cache"
40
+
41
+
42
+ def test_should_ask_before_narrowing_when_the_scope_names_the_example() -> None:
43
+ task = "Fix the login failure. For example, the login failure on Safari."
44
+ rewrite = role_default_rewrite(task)
45
+ assert refusal_reason(task, rewrite) is None
46
+ assert _role_line(rewrite, "ZOOM-IN BLOCKER:").startswith("the scope names something")
47
+
48
+
49
+ def test_should_keep_every_hard_constraint_line() -> None:
50
+ task = "Keep the build green. For example, the login failure.\n\nHARD CONSTRAINTS\n- Never open a pull request.\n"
51
+ rewrite = role_default_rewrite(task)
52
+ assert refusal_reason(task, rewrite) is None
53
+ assert "- Never open a pull request." in rewrite.splitlines()
54
+
55
+
56
+ def test_should_keep_every_task_sentence_in_scope_when_the_task_opens_with_context() -> None:
57
+ task = "We have a Django app. Add CSV export to the reports page, e.g. the monthly sales report, and keep the existing URLs."
58
+ rewrite = role_default_rewrite(task)
59
+ assert refusal_reason(task, rewrite) is None
60
+ assert _role_line(rewrite, "SCOPE (role):") == (
61
+ "We have a Django app. Add CSV export to the reports page and keep the existing URLs."
62
+ )
63
+ assert _role_line(rewrite, "EXAMPLES (illustration only):") == "the monthly sales report"
64
+
65
+
66
+ def test_should_clip_a_long_scope_and_point_at_the_full_task() -> None:
67
+ task = "Refactor the module. " * 40
68
+ rewrite = role_default_rewrite(task.strip())
69
+ scope = _role_line(rewrite, "SCOPE (role):")
70
+ assert scope.endswith("(the rest is in the task below)")
71
+ assert len(scope) < len(task)
72
+ assert refusal_reason(task.strip(), rewrite) is None
73
+
74
+
75
+ def test_should_keep_a_marker_word_inside_a_longer_word_in_the_scope() -> None:
76
+ task = "Search the codebase for examples of deprecated API calls and replace each one with the new client."
77
+ rewrite = role_default_rewrite(task)
78
+ assert refusal_reason(task, rewrite) is None
79
+ assert _role_line(rewrite, "SCOPE (role):") == task
80
+ assert _role_line(rewrite, "EXAMPLES (illustration only):") == "none"
81
+ assumption_task = "Avoid such assumptions in the parser and add input validation."
82
+ assert _role_line(role_default_rewrite(assumption_task), "SCOPE (role):") == assumption_task
83
+
84
+
85
+ def test_should_move_every_marked_example_in_a_sentence_out_of_the_scope() -> None:
86
+ task = "Speed up the slow pages such as the dashboard, and cache reads, e.g. the user lookup, and keep the API stable."
87
+ rewrite = role_default_rewrite(task)
88
+ assert refusal_reason(task, rewrite) is None
89
+ assert _role_line(rewrite, "SCOPE (role):") == "Speed up the slow pages and cache reads and keep the API stable."
90
+ assert _role_line(rewrite, "EXAMPLES (illustration only):") == "the dashboard; the user lookup"
@@ -31,6 +31,7 @@ from hooks_constants.deepen_first_prompt_constants import (
31
31
  REFUSAL_EXAMPLE_NOUN_IN_SCOPE,
32
32
  REFUSAL_MISSING_ROLE_LINES,
33
33
  REFUSAL_RESTATES_FOUR_QUESTIONS,
34
+ ALL_FOUR_QUESTION_STEMS,
34
35
  SKIP_DEEPEN_ENV_VAR,
35
36
  USER_PROMPT_SUBMIT_EVENT_NAME,
36
37
  CURSOR_BEFORE_SUBMIT_PROMPT_EVENT_NAME,
@@ -341,12 +342,20 @@ def test_should_strip_the_deepen_slash_before_rewriting() -> None:
341
342
  assert deepen.task_text_for_deepen("/deepen fix the login page") == "fix the login page"
342
343
 
343
344
 
344
- def test_should_refuse_the_paste_path_as_the_known_bad() -> None:
345
+ def test_should_refuse_the_paste_path_as_the_known_bad(monkeypatch: pytest.MonkeyPatch) -> None:
346
+ pasted_questions = "\n".join(ALL_FOUR_QUESTION_STEMS)
347
+ monkeypatch.setenv(DEEPEN_REWRITE_ENV_VAR, _role_rewrite(pasted_questions))
345
348
  decision = deepen.decide_first_prompt(_payload("Add dark mode to the settings page"))
346
349
  assert isinstance(decision, deepen.Refuse)
347
350
  assert decision.reason == REFUSAL_RESTATES_FOUR_QUESTIONS
348
351
 
349
352
 
353
+ def test_should_deepen_with_the_default_rewrite_when_nothing_is_configured() -> None:
354
+ decision = deepen.decide_first_prompt(_payload("Add dark mode to the settings page"))
355
+ assert isinstance(decision, deepen.Deepen)
356
+ assert "SCOPE (role): Add dark mode to the settings page" in decision.rewritten_prompt
357
+
358
+
350
359
  def test_should_refuse_the_filled_four_answer_stub_without_role_lines(
351
360
  monkeypatch: pytest.MonkeyPatch,
352
361
  ) -> None:
@@ -427,6 +436,7 @@ def test_should_print_a_json_refusal_for_the_paste_path(
427
436
  monkeypatch: pytest.MonkeyPatch,
428
437
  ) -> None:
429
438
  monkeypatch.setenv("CDE_DEEPEN_OUTPUT", DEEPEN_OUTPUT_JSON)
439
+ monkeypatch.setenv(DEEPEN_REWRITE_ENV_VAR, _role_rewrite("\n".join(ALL_FOUR_QUESTION_STEMS)))
430
440
  printed = _run_main(_payload("Add dark mode to the settings page"))
431
441
  assert json.loads(printed) == {
432
442
  "decision": "refuse",
@@ -438,4 +448,5 @@ def test_should_stay_silent_in_rewrite_mode_when_the_gate_refuses(
438
448
  monkeypatch: pytest.MonkeyPatch,
439
449
  ) -> None:
440
450
  monkeypatch.setenv("CDE_DEEPEN_OUTPUT", DEEPEN_OUTPUT_REWRITE)
451
+ monkeypatch.setenv(DEEPEN_REWRITE_ENV_VAR, _FILLED_FOUR_ANSWERS)
441
452
  assert _run_main(_payload("Add dark mode to the settings page")) == ""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.88.1",
3
+ "version": "8.88.3",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {