claude-dev-env 8.85.3 → 8.87.0

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.
Files changed (44) hide show
  1. package/.agents/skills/deepen/SKILL.md +5 -21
  2. package/.agents/skills/efficient-review/README.md +44 -0
  3. package/.agents/skills/efficient-review/SKILL.md +14 -61
  4. package/.agents/skills/efficient-review/reference/criteria.md +21 -0
  5. package/.agents/skills/efficient-review/tools/__init__.py +1 -0
  6. package/.agents/skills/efficient-review/tools/config/__init__.py +1 -0
  7. package/.agents/skills/efficient-review/tools/config/efficient_review_constants.py +28 -0
  8. package/.agents/skills/efficient-review/tools/efficient_review.py +34 -0
  9. package/.agents/skills/efficient-review/tools/efficient_review_plan.py +95 -0
  10. package/.agents/skills/efficient-review/tools/tests/test_review_plan.py +94 -0
  11. package/.agents/skills/search-before-acting/SKILL.md +4 -3
  12. package/.agents/skills/search-before-acting/playbooks/build.md +1 -1
  13. package/.agents/skills/search-before-acting/playbooks/plan-or-log.md +1 -1
  14. package/.agents/skills/search-before-acting/references/report-shape.md +1 -1
  15. package/bin/codex-skill-load-block.mjs +4 -4
  16. package/bin/install.prune.test.mjs +10 -4
  17. package/commands/deepen.md +3 -8
  18. package/docs/account-broker.md +2 -2
  19. package/docs/codex-accounts.md +1 -1
  20. package/docs/decisions/deepstack-gate.md +28 -0
  21. package/docs/rule-guides/agent-board.md +2 -2
  22. package/docs/rule-guides/explore-thoroughly.md +2 -2
  23. package/docs/rule-guides/features-start-with-an-eval.md +2 -2
  24. package/docs/second-claude-account.md +1 -1
  25. package/hooks/features/session-context.md +1 -1
  26. package/hooks/hooks_constants/deepen_first_prompt_constants.py +60 -0
  27. package/hooks/hooks_constants/haiku_spawn_slim_gate_constants.py +47 -7
  28. package/hooks/session/deepen_first_prompt.py +31 -7
  29. package/hooks/session/deepen_role_gate.py +171 -0
  30. package/hooks/session/fixtures/deepstack_replay.json +15 -0
  31. package/hooks/session/grade_deepstack_replay.py +78 -0
  32. package/hooks/session/test_deepen_first_prompt.py +127 -23
  33. package/hooks/session/test_grade_deepstack_replay.py +47 -0
  34. package/package.json +1 -1
  35. package/rules/agent-board.md +1 -1
  36. package/rules/index.md +2 -2
  37. package/scripts/account_broker.py +1 -1
  38. package/scripts/account_broker_support.py +3 -3
  39. package/scripts/ci/install-playtest.mjs +3 -0
  40. package/scripts/codex_account_choice.py +5 -5
  41. package/scripts/dev_env_scripts_constants/codex_account_constants.py +3 -3
  42. package/scripts/test_codex_account_choice.py +1 -1
  43. package/.agents/skills/efficient-review/defaults.json +0 -12
  44. package/.agents/skills/efficient-review/reference/cloud-session.md +0 -13
@@ -2,35 +2,19 @@
2
2
  name: deepen
3
3
  description: >-
4
4
  Deepen a first user task before work starts. Triggers: /deepen, deepen this
5
- task, first-task deepening, Cursor Cloud launch prompt.
5
+ task, first-task deepening, Cursor Cloud launch prompt. DeepStack intake
6
+ holds the contract.
6
7
  ---
7
8
 
8
9
  # deepen
9
10
 
10
- Rewrite a user task so the worker does the omitted expert job. Keep the rewrite short.
11
-
12
- ## When this applies
13
-
14
- - An interactive first user task on Claude Code, Cursor, or Codex
15
- - `/deepen` with a task in the arguments
16
- - Cursor Cloud, where user `~/.cursor/hooks.json` does not run and `sessionStart` is deferred, so the launch prompt is already sent
11
+ On a first task, `/deepen` plus a task, or a routine wake that has work, read DeepStack intake and follow it: [intake](https://github.com/jl-cmd-projects/DeepStack/blob/main/plugins/deepstack/skills/deepstack-mode/references/intake.md).
17
12
 
18
13
  Skip when the prompt already carries `CDE_DEEPEN:v1`, when the text is a thanks or yes/no or status line, when it is a slash command other than `/deepen`, when it is a `/loop` or helper report, or when it is a spawn rewrite that already starts with the poteto-mode prefix.
19
14
 
20
15
  A prompt that is only `/deepen` with no task is skipped. `/deepen` plus a task deepens the remainder.
21
16
 
22
- ## Four answers
23
-
24
- 1. What a top person in that field would do that this prompt omitted.
25
- 2. The raw source to start from.
26
- 3. The methods and tools they would use.
27
- 4. What besides the agent or Jon would show the result is right.
28
-
29
- Then rewrite the user prompt to ask for exactly that job. Put `CDE_DEEPEN:v1` at the top.
30
-
31
- ## How to deepen
32
-
33
- Read the task. Name the field. Fill the four answers from the checkout, the docs, and the tools already in the tree. Keep the rewrite shorter than a new spec. The worker should start from the raw source and finish at the proof.
17
+ The deepening pass writes the worker prompt and stops. The worker runs that prompt.
34
18
 
35
19
  When you spawn a helper to fill the four answers, that helper's prompt starts with `CDE_DEEPEN_AGENT` so the first-task hook leaves it alone.
36
20
 
@@ -38,5 +22,5 @@ When you spawn a helper to fill the four answers, that helper's prompt starts wi
38
22
 
39
23
  - Claude desktop and CLI: a `prompt.submit` mod rewrites the visible text. The Python `UserPromptSubmit` hook is silent when that mod is on outside a cloud session.
40
24
  - Claude cloud and Codex local: the Python hook injects `additionalContext`. Command hooks cannot replace the prompt string.
41
- - Cursor Cloud: run this skill on the launch prompt.
25
+ - Cursor Cloud: run DeepStack intake on the launch prompt. The host adapter for the three lines and the refusal lives with that host. This package keeps the gate.
42
26
  - Codex cloud orchestration skips command hooks. Local Codex only.
@@ -0,0 +1,44 @@
1
+ # One installed copy
2
+
3
+ This package holds the one copy of efficient-review, at `packages/claude-dev-env/.agents/skills/efficient-review/`. A fresh install copies that directory to `~/.claude/skills/efficient-review` and `~/.agents/skills/efficient-review`.
4
+
5
+ A session in any repository runs the review from that installed directory. It does not check out a second repository, name a workflow, or read a credential.
6
+
7
+ ## Plan
8
+
9
+ From the skill directory:
10
+
11
+ ```
12
+ python -m tools.efficient_review plan
13
+ ```
14
+
15
+ On Claude, `CLAUDECODE` or `CLAUDE_CODE_ENTRYPOINT` is set and Cursor and Codex markers are absent. The plan selects haiku and the slim profile from `hooks_constants.haiku_spawn_slim_gate_constants`, and prints the headless command and its environment. Run that command. Send the review task on standard input. The criteria are in [reference/criteria.md](reference/criteria.md).
16
+
17
+ On any other platform, including a plain shell, the plan defers the model. The running agent follows [SKILL.md](SKILL.md) and picks the model.
18
+
19
+ The constants module is the copy installed with this package's hooks, at `hooks/hooks_constants/haiku_spawn_slim_gate_constants.py` next to the skills directory, or inside a checkout of this package. The plan does not carry a second profile.
20
+
21
+ ## A local job in another repository
22
+
23
+ A repository that wants a job to run the review writes that job itself. This package does not ship one. The job runs the same plan from the installed skill directory, then follows [SKILL.md](SKILL.md).
24
+
25
+ ```yaml
26
+ - name: Plan the review
27
+ working-directory: the installed skill directory
28
+ run: python -m tools.efficient_review plan
29
+ ```
30
+
31
+ ## What a consuming repository deletes
32
+
33
+ Delete the vendored skill and the runner that existed only to launch it:
34
+
35
+ - `.claude/skills/efficient-review/`
36
+ - `tools/efficient_review.py`
37
+ - `tools/efficient_review_launch.py`
38
+ - `tools/efficient_review_request.py`
39
+ - `tools/config/efficient_review_constants.py`
40
+ - `.claude/hooks/efficient_review_model_gate.py`
41
+ - `.claude/hooks/efficient_review_model_gate.sh`
42
+ - `.claude/hooks/config/efficient_review_model_gate_constants.py`
43
+
44
+ Also delete a vendored slice of `tools/review_profile`, `tools/review_command`, `tools/review_rules`, `tools/review_stream`, and `tools/review_codex` when that slice exists only so the old runner could import it. Delete a settings file whose keys named a workflow, a checkout credential, a roster, or a broker. Keep `tools/review_model_eval` when the repository still grades reviews with it.
@@ -1,79 +1,32 @@
1
1
  ---
2
2
  name: efficient-review
3
3
  description: >-
4
- Run one low-cost code review of the current branch, or of a branch in another
5
- repository, as a headless Claude run on haiku with the slim review profile.
6
- An auto run lets the account broker pick the account. A headless run names
7
- the account. Triggers: /efficient-review, "efficient review", "cheap review",
8
- "haiku review of this branch".
4
+ Review the current branch from the installed skill. On Claude, the plan
5
+ selects haiku and the slim profile. On any other platform, the running agent
6
+ follows the criteria and picks the model. Triggers: /efficient-review,
7
+ "efficient review", "cheap review".
9
8
  ---
10
9
 
11
10
  # Efficient review
12
11
 
13
- This skill runs one headless code review of a branch on haiku and prints its findings. Pick the mode, run the planner, then run the review on the machine that holds the Claude accounts, or through the agent-pc runner from a cloud session.
12
+ Review the current branch with the criteria in [reference/criteria.md](reference/criteria.md). From this directory, run `python -m tools.efficient_review plan`. The plan names the runtime. This skill makes no network call.
14
13
 
15
- Argument shape: `/efficient-review [auto|headless] [account] [model=] [effort=] [level=] [base=]`. Pass `model=` only as the Defaults section allows.
16
- A blank argument comes from [`defaults.json`](defaults.json).
14
+ ## Claude
17
15
 
18
- ## Defaults
16
+ When the plan runtime is `claude`, the model is haiku and the profile is the slim profile in `hooks_constants.haiku_spawn_slim_gate_constants`. Run the printed command and send the review task on standard input. See [reference/criteria.md](reference/criteria.md).
19
17
 
20
- **Always run the review on haiku.** `defaults.json` names `haiku`, and a blank `model` input runs it. Pass `model=` or the workflow's `model` input only when the owner's own message names the family as a directive, such as "run this review on sonnet". Then run it on that family. Do not ask which model to use. Do not change the model on your own, for depth, for cost, or because passes keep finding issues. A stream of findings is a reason to fix the findings and run haiku again.
18
+ ## Other platforms
21
19
 
22
- **The review must run headless.** Never spawn the review as an Agent subagent in this session. A subagent runs inside the parent session, so it cannot load the slim profile's setting sources, plugin folder, MCP config, or auto-compact window. `defaults.json` holds a headless mode, and `load_defaults` refuses any other mode.
20
+ When the plan runtime is `other`, the model is deferred. The running agent applies [reference/criteria.md](reference/criteria.md) and picks the model.
23
21
 
24
- ## Subcommands
22
+ ## Loop
25
23
 
26
- Run each one as `python -m tools.efficient_review <subcommand> <arguments>` from the checkout that contains `tools/efficient_review.py`.
27
-
28
- | Subcommand | Action |
29
- |---|---|
30
- | `plan` | Prints the mode, account, model family, harness, effort, level, base, and the prompt. |
31
- | `accounts` | Lists the Claude accounts a headless run can name. |
32
- | `run` | Runs a headless or auto review and prints the review, the served models, and the saved file. |
33
-
34
- ## Modes
35
-
36
- | Mode | Picked when | Account |
37
- |---|---|---|
38
- | auto | No mode word and no account. The word `auto` picks it too. | The account broker picks it. Ask nothing. |
39
- | headless | The word `headless`, or an account launcher name. | Required. With no account named, ask for one. |
40
-
41
- A model is a family name: haiku, sonnet, opus, sol, or luna. The planner refuses a versioned model ID. `codex review` takes no level, so the planner refuses `level=` with sol or luna.
42
-
43
- ## 1. Headless mode with no account
44
-
45
- Run `python -m tools.efficient_review accounts`. Ask with the session's question tool, one option per listed account, and wait for the answer. The answer is the account. On the machine that holds the launchers, the list comes from the `claude-*.cmd` launchers, without `claude-base`, `claude-raw`, and `claude-kimi`. In the cloud the list is the known roster.
46
-
47
- ## 2. Headless or auto on that machine
48
-
49
- Run `python -m tools.efficient_review run <arguments> --repository <checkout>`. When the checkout has no launcher folder, go to step 3.
50
-
51
- - It builds the slim profile from `tools.review_profile` into a temp folder.
52
- - Auto asks `account_broker.py choose --product claude` for the account.
53
- - It runs the account launcher with `-p`, the model alias, the effort, the profile arguments, and the environment. The system prompt file holds the review rules from the checkout that contains the runner, and the code standards. The prompt and the diff against the base go on standard input.
54
- - A sol or luna family runs `codex review` on the newest slug of that family. The log prints that slug.
55
- - It prints the review, the served models from the stream's `modelUsage` keys, and the saved file. The review and its record go to a temp folder outside the repository.
56
-
57
- ## 3. Headless or auto from a cloud session
58
-
59
- The cloud container has no other Claude accounts, so the review runs on the agent-pc runner. Read [reference/cloud-session.md](reference/cloud-session.md).
24
+ Check each finding against the code. Fix every one that holds. Run the review again until a pass reports none. The steps are in [reference/criteria.md](reference/criteria.md).
60
25
 
61
26
  ## Reading guide
62
27
 
63
28
  | File | What it does |
64
29
  |---|---|
65
- | [`reference/cloud-session.md`](reference/cloud-session.md) | Dispatching the review from a cloud session and reading its results branch. |
66
- | [`defaults.json`](defaults.json) | The default model family, effort, level, prompt, base, profile, advisor, validator, mode, and timeout. |
67
-
68
- ## Common pitfalls
69
-
70
- - **Picking a stronger model after several passes.** Fix the findings and run haiku again.
71
- - **Setting a valid finding aside.** Check each finding against the code. Fix every one that holds in the same branch, even when it reaches files the diff did not touch. Answer only the findings that fail the check, with the file and line that rules each out.
72
- - **Asking which model to use.** The answer is haiku. When the owner names another family, run it without a confirmation question.
73
- - **Spawning the review as a subagent.** The review loses the slim profile. Run it headless.
74
- - **Dispatching from the branch under review.** The workflow runs only from `main`.
75
-
76
- ## Out of scope
77
-
78
- - The `validator` default stays `off`.
79
- - A fixer step after the review. The skill has no fixer step.
30
+ | [README.md](README.md) | How a session loads this copy, and the shape of a local job a repository may write. |
31
+ | [reference/criteria.md](reference/criteria.md) | The review criteria, the finding block, and the loop. |
32
+ | [tools/efficient_review.py](tools/efficient_review.py) | `plan` prints the runtime selection. |
@@ -0,0 +1,21 @@
1
+ # Review criteria
2
+
3
+ Apply these to the diff. Report a finding only after checking it against the code.
4
+
5
+ - Review supported behavior and the changed repository contract. Current instructions override a stale copy. A contract change is not a defect because an older rule differs.
6
+ - Name a reachable caller, a supported input, and an observable failure for each defect.
7
+ - Check command flags, the environment, the pipeline, and the executable path before calling a command read-only.
8
+ - Security and atomicity findings need deployment and trust-boundary evidence.
9
+ - Coverage advice is separate from a demonstrated defect.
10
+ - A changed context file that passes the context budget and still carries detail a pointer should hold is a finding.
11
+ - Bind a delayed event, a cancellation, and review evidence to the full candidate head.
12
+ - A finished review can contain findings. A resolved thread, a timeout, or a quota notice does not prove a repair.
13
+ - Only a verified pass on the current head supports admission.
14
+
15
+ ## Finding block
16
+
17
+ An empty list is a valid review. End with a json block. Each finding has `file`, `line`, `priority`, `summary`, and `failure_scenario`. Priority 0 blocks the merge. Priority 1 is fixed before the merge. Priority 2 can follow the merge. Priority 3 is a style nit.
18
+
19
+ ## Loop
20
+
21
+ Fix every finding that holds, including a file the diff did not touch. Then run the review again. Stop when a pass reports no finding that holds.
@@ -0,0 +1 @@
1
+ """Plan one efficient-review run from the installed skill."""
@@ -0,0 +1 @@
1
+ """Constants for the efficient-review plan."""
@@ -0,0 +1,28 @@
1
+ """Paths and runtime markers for the efficient-review plan."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ SKILL_ROOT = Path(__file__).resolve().parents[2]
8
+ GATE_CONSTANTS_FILENAME = "haiku_spawn_slim_gate_constants.py"
9
+ GATE_MODULE_NAME = "haiku_spawn_slim_gate_constants"
10
+ HOOKS_DIRECTORY_NAME = "hooks"
11
+ HOOKS_CONSTANTS_DIRECTORY_NAME = "hooks_constants"
12
+ ALL_HOOKS_PARENT_STEPS = (1, 2)
13
+ USER_HOME_PARENT_STEP = 2
14
+ CLAUDE_HOME_DIRECTORY_NAME = ".claude"
15
+ ALL_CLAUDE_HOST_VARIABLES = ("CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT")
16
+ ALL_OTHER_HOST_VARIABLES = (
17
+ "CURSOR_AGENT",
18
+ "CURSOR_TRACE_ID",
19
+ "CODEX_HOME",
20
+ "CODEX_CI",
21
+ )
22
+ RUNTIME_CLAUDE = "claude"
23
+ RUNTIME_OTHER = "other"
24
+ MODEL_DEFERRED = "deferred"
25
+ PROFILE_DEFERRED = "deferred"
26
+ JSON_INDENT = 2
27
+ PLAN_COMMAND = "plan"
28
+ TEXT_ENCODING = "utf-8"
@@ -0,0 +1,34 @@
1
+ """Plan one /efficient-review run from the installed skill."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import os
8
+ import sys
9
+ from typing import TextIO
10
+
11
+ from tools.config import efficient_review_constants as c
12
+ from tools.efficient_review_plan import plan_review
13
+
14
+
15
+ def main(all_arguments: list[str], plan_stream: TextIO) -> int:
16
+ """Print the runtime selection for one review.
17
+
18
+ Args:
19
+ all_arguments: Command-line arguments, without the program name.
20
+ plan_stream: Where the plan JSON is written.
21
+
22
+ Returns:
23
+ Zero when the plan prints.
24
+ """
25
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
26
+ subcommands = parser.add_subparsers(dest="command", required=True)
27
+ subcommands.add_parser(c.PLAN_COMMAND)
28
+ parser.parse_args(all_arguments)
29
+ plan_stream.write(json.dumps(plan_review(os.environ), indent=c.JSON_INDENT) + "\n")
30
+ return 0
31
+
32
+
33
+ if __name__ == "__main__":
34
+ sys.exit(main(sys.argv[1:], sys.stdout))
@@ -0,0 +1,95 @@
1
+ """Choose haiku and the slim profile on Claude, and defer the model elsewhere."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.util
6
+ from collections.abc import Mapping
7
+ from pathlib import Path
8
+ from types import ModuleType
9
+
10
+ from tools.config import efficient_review_constants as c
11
+
12
+
13
+ class ReviewPlanFatal(RuntimeError):
14
+ """The slim-profile constants module is not installed beside this skill."""
15
+
16
+
17
+ def plan_review(all_environment: Mapping[str, str]) -> dict[str, object]:
18
+ """Return the runtime selection for one review.
19
+
20
+ Args:
21
+ all_environment: The process environment.
22
+
23
+ Returns:
24
+ Claude selects haiku and the slim profile. Every other runtime defers the model.
25
+ """
26
+ if _is_claude_runtime(all_environment):
27
+ return _claude_plan(_load_gate_constants())
28
+ return {
29
+ "runtime": c.RUNTIME_OTHER,
30
+ "model": c.MODEL_DEFERRED,
31
+ "profile": c.PROFILE_DEFERRED,
32
+ }
33
+
34
+
35
+ def _is_claude_runtime(all_environment: Mapping[str, str]) -> bool:
36
+ if any(all_environment.get(each_name) for each_name in c.ALL_OTHER_HOST_VARIABLES):
37
+ return False
38
+ return any(all_environment.get(each_name) for each_name in c.ALL_CLAUDE_HOST_VARIABLES)
39
+
40
+
41
+ def _claude_plan(gate_constants: ModuleType) -> dict[str, object]:
42
+ return {
43
+ "runtime": c.RUNTIME_CLAUDE,
44
+ "model": gate_constants.HAIKU_MODEL_FAMILY,
45
+ "profile": gate_constants.SLIM_PROFILE_NAME,
46
+ "command": [
47
+ gate_constants.SLIM_PROFILE_EXECUTABLE,
48
+ *gate_constants.ALL_SLIM_PROFILE_ARGUMENTS,
49
+ ],
50
+ "environment": list(gate_constants.ALL_SLIM_PROFILE_ENVIRONMENT),
51
+ }
52
+
53
+
54
+ def _load_gate_constants() -> ModuleType:
55
+ constants_path = _gate_constants_path()
56
+ spec = importlib.util.spec_from_file_location(c.GATE_MODULE_NAME, constants_path)
57
+ if spec is None or spec.loader is None:
58
+ raise ReviewPlanFatal(f"cannot load slim profile constants from {constants_path}")
59
+ module = importlib.util.module_from_spec(spec)
60
+ spec.loader.exec_module(module)
61
+ return module
62
+
63
+
64
+ def _gate_constants_path() -> Path:
65
+ for each_candidate in _candidate_paths(c.SKILL_ROOT):
66
+ if each_candidate.is_file():
67
+ return each_candidate
68
+ raise ReviewPlanFatal(
69
+ "the slim profile constants are missing; install the package hooks next to this skill"
70
+ )
71
+
72
+
73
+ def _candidate_paths(skill_root: Path) -> tuple[Path, ...]:
74
+ relative_parts = (
75
+ c.HOOKS_DIRECTORY_NAME,
76
+ c.HOOKS_CONSTANTS_DIRECTORY_NAME,
77
+ c.GATE_CONSTANTS_FILENAME,
78
+ )
79
+ all_candidates = [
80
+ skill_root.parents[each_step].joinpath(*relative_parts)
81
+ for each_step in c.ALL_HOOKS_PARENT_STEPS
82
+ if len(skill_root.parents) > each_step
83
+ ]
84
+ user_home = _parent_at(skill_root, c.USER_HOME_PARENT_STEP)
85
+ if user_home is not None:
86
+ all_candidates.append(
87
+ user_home.joinpath(c.CLAUDE_HOME_DIRECTORY_NAME, *relative_parts)
88
+ )
89
+ return tuple(all_candidates)
90
+
91
+
92
+ def _parent_at(start: Path, steps: int) -> Path | None:
93
+ if len(start.parents) <= steps:
94
+ return None
95
+ return start.parents[steps]
@@ -0,0 +1,94 @@
1
+ """The plan selects haiku on Claude and defers the model on every other runtime."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ import pytest
10
+
11
+ SKILL_ROOT = Path(__file__).resolve().parents[2]
12
+ if str(SKILL_ROOT) not in sys.path:
13
+ sys.path.insert(0, str(SKILL_ROOT))
14
+
15
+ from tools.config import efficient_review_constants as c
16
+ from tools.efficient_review_plan import plan_review
17
+
18
+
19
+ def test_should_load_the_slim_profile_from_the_claude_home(
20
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
21
+ ) -> None:
22
+ skill_root = tmp_path / ".agents" / "skills" / "efficient-review"
23
+ skill_root.mkdir(parents=True)
24
+ constants_path = (
25
+ tmp_path
26
+ / ".claude"
27
+ / "hooks"
28
+ / "hooks_constants"
29
+ / c.GATE_CONSTANTS_FILENAME
30
+ )
31
+ constants_path.parent.mkdir(parents=True)
32
+ source = (
33
+ SKILL_ROOT.parents[2]
34
+ / "hooks"
35
+ / "hooks_constants"
36
+ / c.GATE_CONSTANTS_FILENAME
37
+ )
38
+ constants_path.write_text(source.read_text(encoding=c.TEXT_ENCODING), encoding=c.TEXT_ENCODING)
39
+ monkeypatch.setattr(c, "SKILL_ROOT", skill_root)
40
+ plan = plan_review({"CLAUDECODE": "1"})
41
+ assert plan["model"] == "haiku"
42
+ assert plan["profile"] == "slim"
43
+
44
+
45
+ FORBIDDEN_SNIPPETS = (
46
+ "dispatch_repository",
47
+ "dispatch_workflow",
48
+ "dispatch_ref",
49
+ "read_token",
50
+ "results_branch",
51
+ "account_broker",
52
+ "urllib",
53
+ "socket",
54
+ )
55
+
56
+
57
+ def test_should_select_haiku_and_the_slim_profile_on_claude() -> None:
58
+ plan = plan_review({"CLAUDECODE": "1"})
59
+ assert plan["runtime"] == "claude"
60
+ assert plan["model"] == "haiku"
61
+ assert plan["profile"] == "slim"
62
+ assert plan["command"][0] == "claude"
63
+ assert "--model" in plan["command"]
64
+ assert plan["command"][plan["command"].index("--model") + 1] == "haiku"
65
+ assert "CLAUDE_CODE_DISABLE_CLAUDE_MDS=1" in plan["environment"]
66
+ serialized = json.dumps(plan)
67
+ assert "token" not in serialized
68
+ assert "dispatch" not in serialized
69
+
70
+
71
+ def test_should_defer_the_model_when_the_runtime_is_not_claude() -> None:
72
+ for each_environment in (
73
+ {},
74
+ {"CURSOR_AGENT": "1"},
75
+ {"CODEX_HOME": "/tmp/codex"},
76
+ {"CURSOR_AGENT": "1", "CLAUDECODE": "1"},
77
+ ):
78
+ plan = plan_review(each_environment)
79
+ assert plan == {
80
+ "runtime": "other",
81
+ "model": "deferred",
82
+ "profile": "deferred",
83
+ }
84
+
85
+
86
+ def test_should_keep_dispatch_and_network_out_of_the_skill() -> None:
87
+ all_texts = [
88
+ each_path.read_text(encoding=c.TEXT_ENCODING)
89
+ for each_path in SKILL_ROOT.rglob("*.py")
90
+ if "tests" not in each_path.parts
91
+ ]
92
+ combined = "\n".join(all_texts)
93
+ for each_snippet in FORBIDDEN_SNIPPETS:
94
+ assert each_snippet not in combined
@@ -2,8 +2,9 @@
2
2
  name: search-before-acting
3
3
  description: >-
4
4
  Search for what already does an ask before logging, planning, delegating,
5
- or building it, then report what exists in four points and wait for the
6
- user. Use when a user asks for new or changed behavior, a tool, a check,
5
+ or building it, then report what exists in four points. When the role-level
6
+ job is already authorized, continue that job. Use when a user asks for new
7
+ or changed behavior, a tool, a check,
7
8
  a workflow, a report, a tracker entry, or a plan, and before writing a
8
9
  brief for a worker.
9
10
  ---
@@ -17,7 +18,7 @@ The user should hear about work that already does their ask before anyone duplic
17
18
  1. Restate the ask as the outcome the user wants, in their words.
18
19
  2. Search every place in [`references/search-places.md`](references/search-places.md) that this session can reach. Note each place you could not reach.
19
20
  3. Sort each hit as full, partial, or related. A hit is full when it already produces the outcome from step 1.
20
- 4. When any hit is full or partial, stop. Send the four-point report from [`references/report-shape.md`](references/report-shape.md) and wait for the user. When a hit is full, tell the user they already have it.
21
+ 4. When any hit is full or partial, send the four-point report from [`references/report-shape.md`](references/report-shape.md). When the role-level job is already authorized, continue that job. When it is not, wait for the user. When a hit is full, tell the user they already have it.
21
22
  5. When nothing is found, say where you looked in one line, then continue with the playbook below.
22
23
 
23
24
  ## Playbooks
@@ -3,7 +3,7 @@
3
3
  Use this playbook before you build or change code, a hook, a skill, a rule, a workflow, or config for an ask.
4
4
 
5
5
  1. Run the skill steps. Search the places in [`../references/search-places.md`](../references/search-places.md), with production config and launcher options first when the ask names a running system.
6
- 2. When a hit is full or partial, send the [report](../references/report-shape.md) and wait for the user's answer.
6
+ 2. When a hit is full or partial, send the [report](../references/report-shape.md). When the role-level job is already authorized, continue that job. When it is not, wait for the user's answer.
7
7
  3. When the user says go, build only the missing part from point 3 of the report. Extend the existing piece where it lives. Start a new piece only when the existing one cannot hold the addition, and say why in the pull request.
8
8
  4. When nothing exists, follow `prefer-existing-tools.md` to check open-source options, then build.
9
9
  5. Add an "Existing work" section to the pull request body. List each hit with its link or `file:line` and what the pull request adds on top of it. When nothing was found, say so and name the places searched. The pull request gate denies a body without this section.
@@ -4,6 +4,6 @@ Use this playbook before you add an ask to a tracker, open an issue or card for
4
4
 
5
5
  1. Run the skill steps. Search open and closed tracker entries for the ask first, then the rest of [`../references/search-places.md`](../references/search-places.md).
6
6
  2. When a tracker entry already names the ask, update that entry and link it. Skip the new entry.
7
- 3. When code, config, or a merged pull request already does the ask in full or in part, send the [report](../references/report-shape.md) and wait for the user before you log or plan.
7
+ 3. When code, config, or a merged pull request already does the ask in full or in part, send the [report](../references/report-shape.md). When the role-level job is already authorized, continue that job. When it is not, wait for the user before you log or plan.
8
8
  4. When the user says go, log or plan only the missing part. Link each existing piece in the entry or plan, so the reader starts from what is live.
9
9
  5. When nothing exists, write the entry or plan with one line that names the places searched.
@@ -1,6 +1,6 @@
1
1
  # Report shape
2
2
 
3
- Send this report when the search finds something that does the ask in full or in part. Send it before any log entry, plan, brief, or edit, and wait for the user.
3
+ Send this report when the search finds something that does the ask in full or in part. Send it before any log entry, plan, brief, or edit. When the role-level job is already authorized, continue that job. When it is not, wait for the user.
4
4
 
5
5
  1. **What exists and where.** Each hit with a link or `file:line`, and whether it is live.
6
6
  2. **What it covers of the ask.** The parts of the outcome it already produces.
@@ -11,10 +11,10 @@ export const DEEPEN_GUIDANCE_BLOCK_START = '<!-- claude-dev-env deepen: start --
11
11
  export const DEEPEN_GUIDANCE_BLOCK_END = '<!-- claude-dev-env deepen: end -->';
12
12
  export const DEEPEN_GUIDANCE_TEXT = [
13
13
  'Deepen the first interactive user task before any work starts.',
14
- 'Answer four things the prompt left out: what a top person in that field would do, the raw source to start from, the methods and tools they would use, and what besides the agent or Jon would show the result is right.',
15
- 'Then rewrite the task to ask for exactly that job. The rewrite carries the marker CDE_DEEPEN:v1.',
16
- 'On demand, follow the deepen skill or /deepen with a task.',
17
- 'Cursor Cloud has no user hooks.json and defers sessionStart, so the launch prompt is already sent. Use the deepen skill under ~/.cursor/skills there.',
14
+ 'Follow DeepStack intake. Write SCOPE (role), EXAMPLES (illustration only), and ZOOM-IN BLOCKER before any plan.',
15
+ 'The worker prompt carries the marker CDE_DEEPEN:v1 on line 1 and the SCOPE (role) line.',
16
+ 'On demand, follow the DeepStack skill or /deepen with a task.',
17
+ 'Cursor Cloud registers the DeepStack SessionStart hook and the first-task gate. Use DeepStack intake on the launch prompt.',
18
18
  'Codex cloud orchestration skips command hooks. This block is the local Codex instruction.',
19
19
  ].join('\n');
20
20
 
@@ -813,10 +813,16 @@ test('a fresh scoped core install includes efficient-review', () => {
813
813
  assert.match(skillText, /^name: efficient-review$/m);
814
814
  assert.match(skillText, /\/efficient-review/);
815
815
  assert.equal(readManifest(sandbox.manifestPath).skills.includes('efficient-review'), true);
816
- assert.equal(
817
- existsSync(join(sandbox.skillsDirectory, 'efficient-review', 'defaults.json')),
818
- true,
819
- );
816
+ const skillDirectory = join(sandbox.skillsDirectory, 'efficient-review');
817
+ assert.equal(existsSync(join(skillDirectory, 'README.md')), true);
818
+ assert.equal(existsSync(join(skillDirectory, 'reference', 'criteria.md')), true);
819
+ assert.equal(existsSync(join(skillDirectory, 'tools', 'efficient_review.py')), true);
820
+ assert.equal(existsSync(join(skillDirectory, 'settings.json')), false);
821
+ assert.equal(existsSync(join(skillDirectory, 'reference', 'cloud-session.md')), false);
822
+ const readme = readFileSync(join(skillDirectory, 'README.md'), 'utf8');
823
+ assert.match(readme, /python -m tools\.efficient_review plan/);
824
+ assert.equal(readme.includes('dispatch_repository'), false);
825
+ assert.equal(readme.includes('read_token'), false);
820
826
  } finally {
821
827
  rmSync(sandbox.homeDirectory, { recursive: true, force: true });
822
828
  }
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Deepen a task into the omitted expert job before work starts
2
+ description: Deepen a task into the standing job before work starts
3
3
  argument-hint: [task to deepen]
4
4
  ---
5
5
 
@@ -7,13 +7,8 @@ Deepen this task before any work starts: `$ARGUMENTS`
7
7
 
8
8
  When `$ARGUMENTS` is empty, ask the user to paste the task and stop.
9
9
 
10
- When `$ARGUMENTS` holds a task, answer four things the prompt left out:
10
+ When `$ARGUMENTS` holds a task, follow DeepStack intake at https://github.com/jl-cmd-projects/DeepStack/blob/main/plugins/deepstack/skills/deepstack-mode/references/intake.md.
11
11
 
12
- 1. What a top person in that field would do that this prompt omitted.
13
- 2. The raw source to start from.
14
- 3. The methods and tools they would use.
15
- 4. What besides the agent or Jon would show the result is right.
16
-
17
- Rewrite the task so it asks for exactly that job. Put `CDE_DEEPEN:v1` at the top of the rewrite. Then do only that job.
12
+ Write `SCOPE (role):`, `EXAMPLES (illustration only):`, and `ZOOM-IN BLOCKER:` before any plan. Put `CDE_DEEPEN:v1` on line 1 of the worker prompt and carry the scope line there. The deepening pass writes that prompt and stops.
18
13
 
19
14
  Skip this command when the prompt already carries `CDE_DEEPEN:v1`.
@@ -1,12 +1,12 @@
1
1
  # Account broker
2
2
 
3
- `account_broker.py` reads the Claude and Codex account rosters, checks each account's session and weekly usage, and chooses an account with room. It does not select a model or sleep. A Codex decision names the `normal` or `luna` tier for the caller.
3
+ `account_broker.py` reads the Claude and Codex account lists, checks each account's session and weekly usage, and chooses an account with room. It does not select a model or sleep. A Codex decision names the `normal` or `luna` tier for the caller.
4
4
 
5
5
  Claude's main home is `~/.claude`. Its `claude-chain.json` file contains a `chain` list; each entry's `credentials_path` field identifies the credentials file whose parent is that account's home folder. The optional `extra-profiles.json` file in the main home lists additional profile names.
6
6
 
7
7
  The optional `claude-account-order.json` file in the main home sets the Claude pick priority. It is a JSON list of account names, first choice first, such as `["claude-second", "claude"]`. An entry matches an account's name, its command, or its `claude-<name>` launcher, and `claude` names the main account. A new job goes to the first listed account with room. When no listed account has room, the broker picks the unlisted account with the most room, and it waits when no account has room. A resumed session stays on its bound account while that account has room. A listed name absent from the roster is skipped. Without the file, every job goes to the account with the most room. Edit the file to change the order; the next pick reads it.
8
8
 
9
- Codex uses its configured account roster. When no Codex roster is configured, the broker chooses the default Codex home. A new Codex job goes to an account on a Pro plan before any other account in the same tier. The plan comes from the account's meter reply, and `ALL_PRO_PLAN_TYPES` lists the Pro plans: `pro`, `prolite`, and `promax`. Among accounts of the same kind, the one with the most room wins. A Pro account below the `normal` floor still loses to any account with room in the `normal` tier.
9
+ Codex uses its configured account list. When no Codex roster is configured, the broker chooses the default Codex home. A new Codex job goes to an account on a Pro plan before any other account in the same tier. The plan comes from the account's meter reply, and `ALL_PRO_PLAN_TYPES` lists the Pro plans: `pro`, `prolite`, and `promax`. Among accounts of the same kind, the one with the most room wins. A Pro account below the `normal` floor still loses to any account with room in the `normal` tier.
10
10
 
11
11
  ## Commands
12
12
 
@@ -16,7 +16,7 @@ A job asks the broker which account to use.
16
16
 
17
17
  ## Names and order
18
18
 
19
- The account roster names the accounts and their order. It comes from the
19
+ The account list names the accounts and their order. It comes from the
20
20
  `CODEX_ACCOUNT_PROFILES` environment variable, a comma-separated list such as
21
21
  `alpha,beta`. When that variable is unset or empty, the roster is the saved list
22
22
  in `account-launchers.json` under the profiles root. When neither exists, the