claude-dev-env 8.98.9 → 8.98.10

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.
@@ -2,7 +2,7 @@ from pathlib import Path
2
2
 
3
3
  import pytest
4
4
 
5
- from open_task_asks import is_ask, record_prompt, recorded_prompts
5
+ from open_task_asks import claude_home, is_ask
6
6
 
7
7
 
8
8
  @pytest.fixture(autouse=True)
@@ -41,17 +41,10 @@ def test_should_classify_work_prompts_as_asks(prompt_text: str, is_expected_ask:
41
41
  assert is_ask(prompt_text) is is_expected_ask
42
42
 
43
43
 
44
- def test_should_number_asks_and_keep_every_prompt_in_order() -> None:
45
- assert record_prompt("s-1", "Export the APKs") == "ask-1"
46
- assert record_prompt("s-1", "did it fail?") is None
47
- assert record_prompt("s-1", "Submit them") == "ask-2"
48
- assert [each_prompt.ask_id for each_prompt in recorded_prompts("s-1")] == [
49
- "ask-1",
50
- None,
51
- "ask-2",
52
- ]
44
+ def test_should_read_the_claude_home_from_claude_config_dir(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
45
+ monkeypatch.setenv("CLAUDE_CONFIG_DIR", str(tmp_path / "profile"))
46
+ assert claude_home() == tmp_path / "profile"
53
47
 
54
48
 
55
- def test_should_skip_a_session_id_that_is_not_a_plain_name() -> None:
56
- assert record_prompt("../escape", "Export the APKs") is None
57
- assert recorded_prompts("../escape") == []
49
+ def test_should_default_the_claude_home_to_the_user_home(tmp_path: Path) -> None:
50
+ assert claude_home() == tmp_path / ".claude"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.98.9",
3
+ "version": "8.98.10",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
package/rules/index.md CHANGED
@@ -24,7 +24,7 @@ Each line gives a rule in brief. When its trigger matches your task, open the gu
24
24
  **Shipping**
25
25
  - **Pull request lifecycle**. Before a commit, push, pull request action, or merge. Invoke the `pr-lifecycle` skill; a hook denies those commands until it loads.
26
26
  - **Proof before a pull request** ([guide](../docs/rule-guides/proof-before-pull-request.md)). Before you open a pull request. Run the change with and without it, and quote both under "Proof in practice".
27
- - **Session title** ([guide](../docs/rule-guides/session-title.md)). The session has a tool whose name ends in `__set_session_title`. Set `<emoji> <name>` before each turn ends.
27
+ - **Session title** ([guide](../docs/rule-guides/session-title.md)). The session has a tool whose name ends in `__set_session_title`. Set `<emoji> <name>` when the status changes.
28
28
 
29
29
  **Memory**
30
30
  - **Durable facts only** ([guide](../docs/rule-guides/memory-stores-durable-facts.md)). Writing auto-memory or a `MEMORY.md` entry. Keep only a fact that helps a fresh session weeks later.
@@ -175,8 +175,9 @@ def _names_exempt_registration_path(registered_string: str) -> bool:
175
175
  asks the model to check a hedged claim, and the call it reads stays in place.
176
176
 
177
177
  The session title gates keep the session title in its status format. One
178
- denies a malformed title, and the other asks for a title before a turn
179
- ends. They decide the shape of a title and no code or safety policy.
178
+ denies a malformed title, and the other asks for a new title when the
179
+ shown status stops fitting. They decide the shape of a title and no code
180
+ or safety policy.
180
181
 
181
182
  The artifact page gate denies a page publish whose text is unreadable in
182
183
  dark mode. It decides how a page renders and no code or safety policy.
@@ -193,10 +194,9 @@ def _names_exempt_registration_path(registered_string: str) -> bool:
193
194
  names open review findings. It reads only the wording of a chat card and
194
195
  decides no code or safety policy; the merge review gate holds the merge.
195
196
 
196
- The open tasks Stop gate holds the end of a turn while the session's task
197
- list has items the closing reply neither completes nor reports as
198
- ``Blocked:`` or ``Open:``. It decides when a turn may end and no code or
199
- safety policy.
197
+ The open tasks Stop gate holds the end of a turn once when the turn worked
198
+ on an ask and made no task list call. It decides when a turn may end and
199
+ no code or safety policy.
200
200
 
201
201
  The owned pull request Stop gate holds the end of a turn while a pull
202
202
  request the session opened on an owned repository is still open and the
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env python3
2
- """UserPromptSubmit hook that records each prompt for the open tasks Stop gate.
3
-
4
- A prompt that asks for work becomes ``#ask-N`` in
5
- ``~/.claude/open-task-asks/<session id>.json``. The gate then holds the turn
6
- until a reply closes it with ``Done: #ask-N``, or with
7
- ``Dropped: #ask-N - "<the user's words>"`` quoting a later prompt. The hook
8
- prints nothing and always exits 0.
9
- """
10
-
11
- from __future__ import annotations
12
-
13
- import sys
14
- from pathlib import Path
15
-
16
- hooks_root_directory = str(Path(__file__).resolve().parent.parent)
17
- if hooks_root_directory not in sys.path:
18
- sys.path.insert(0, hooks_root_directory)
19
-
20
- from hooks_constants.open_tasks_stop_gate_constants import (
21
- ALLOW_EXIT_CODE,
22
- PROMPT_KEY,
23
- SESSION_ID_KEY,
24
- )
25
- from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin
26
- from open_task_asks import record_prompt
27
-
28
-
29
- def main() -> int:
30
- hook_input = read_hook_input_dictionary_from_stdin()
31
- prompt_value = hook_input.get(PROMPT_KEY) if hook_input is not None else None
32
- if hook_input is not None and isinstance(prompt_value, str):
33
- record_prompt(hook_input.get(SESSION_ID_KEY), prompt_value)
34
- return ALLOW_EXIT_CODE
35
-
36
-
37
- if __name__ == "__main__":
38
- sys.exit(main())
@@ -1,57 +0,0 @@
1
- import json
2
- import subprocess
3
- import sys
4
- from pathlib import Path
5
-
6
- RECORDER_PATH = Path(__file__).resolve().parent / "open_task_ask_recorder.py"
7
- SESSION_ID = "s-123"
8
-
9
-
10
- def submit_prompt(home_directory: Path, prompt_text: str) -> subprocess.CompletedProcess[str]:
11
- hook_input = {
12
- "hook_event_name": "UserPromptSubmit",
13
- "session_id": SESSION_ID,
14
- "prompt": prompt_text,
15
- }
16
- return subprocess.run(
17
- [sys.executable, str(RECORDER_PATH)],
18
- input=json.dumps(hook_input),
19
- capture_output=True,
20
- text=True,
21
- env={
22
- "HOME": str(home_directory),
23
- "USERPROFILE": str(home_directory),
24
- "PATH": "",
25
- },
26
- check=False,
27
- )
28
-
29
-
30
- def recorded_ask_ids(home_directory: Path) -> list[str]:
31
- asks_path = home_directory / ".claude" / "open-task-asks" / f"{SESSION_ID}.json"
32
- all_prompts = json.loads(asks_path.read_text(encoding="utf-8"))["prompts"]
33
- return [each_prompt["ask_id"] for each_prompt in all_prompts if each_prompt["ask_id"]]
34
-
35
-
36
- def test_should_record_each_ask_outside_the_task_store_and_stay_silent(
37
- tmp_path: Path,
38
- ) -> None:
39
- first_process = submit_prompt(tmp_path, "Export the OneUI8 and OneUI9 APKs and submit them")
40
- submit_prompt(tmp_path, "did the export fail?")
41
- submit_prompt(tmp_path, "<task-notification>\n<task-id>b1</task-id>\n</task-notification>")
42
- submit_prompt(tmp_path, "can you check the release dates in neon?")
43
- assert (first_process.returncode, first_process.stdout) == (0, "")
44
- assert recorded_ask_ids(tmp_path) == ["ask-1", "ask-2"]
45
- assert not (tmp_path / ".claude" / "tasks").exists()
46
-
47
-
48
- def test_should_allow_malformed_input(tmp_path: Path) -> None:
49
- completed_process = subprocess.run(
50
- [sys.executable, str(RECORDER_PATH)],
51
- input="not json",
52
- capture_output=True,
53
- text=True,
54
- env={"HOME": str(tmp_path), "USERPROFILE": str(tmp_path), "PATH": ""},
55
- check=False,
56
- )
57
- assert (completed_process.returncode, completed_process.stdout) == (0, "")