claude-dev-env 2.4.0 → 2.5.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 (195) hide show
  1. package/CLAUDE.md +53 -49
  2. package/_shared/pr-loop/scripts/_claude_permissions_common.py +84 -0
  3. package/_shared/pr-loop/scripts/code_rules_gate.py +4 -2
  4. package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -306
  5. package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +44 -0
  6. package/_shared/pr-loop/scripts/pr_loop_shared_constants/copilot_quota_constants.py +24 -24
  7. package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -107
  8. package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +290 -48
  9. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +42 -2
  10. package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +36 -0
  11. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +100 -1
  12. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -497
  13. package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +311 -2
  14. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -301
  15. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -85
  16. package/_shared/pr-loop/worker-spawn.md +1 -1
  17. package/agents/CLAUDE.md +2 -1
  18. package/agents/caveman.md +0 -1
  19. package/agents/clasp-deployment-orchestrator.md +0 -1
  20. package/agents/clean-coder.md +0 -1
  21. package/agents/code-advisor.md +0 -1
  22. package/agents/code-quality-agent.md +1 -2
  23. package/agents/code-verifier.md +0 -1
  24. package/agents/deep-research.md +0 -1
  25. package/agents/docs-agent.md +0 -1
  26. package/agents/git-commit-crafter.md +0 -1
  27. package/agents/issue-tracker.md +42 -0
  28. package/agents/plan-packet-validator.md +0 -1
  29. package/agents/pr-description-writer.md +0 -1
  30. package/agents/test_agent_frontmatter.py +67 -18
  31. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +143 -141
  32. package/bin/CLAUDE.md +68 -5
  33. package/bin/ever-shipped-skills.mjs +1 -0
  34. package/bin/install-constants.mjs +88 -0
  35. package/bin/install.mjs +1138 -114
  36. package/bin/install.prune.test.mjs +869 -19
  37. package/bin/install.test.mjs +906 -2
  38. package/commands/implement.md +1 -1
  39. package/commands/right-size.md +1 -1
  40. package/docs/CLAUDE.md +1 -0
  41. package/docs/host-pool-health-monitor.md +102 -0
  42. package/docs/references/CLAUDE.md +4 -2
  43. package/docs/references/advisor-tool.md +13 -0
  44. package/docs/references/code-review-enforcement.md +10 -0
  45. package/docs/references/team-advisor-skill.md +14 -0
  46. package/hooks/blocking/CLAUDE.md +1 -0
  47. package/hooks/blocking/code_review_pr_create_gate.py +7 -3
  48. package/hooks/blocking/code_review_push_gate.py +9 -4
  49. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +8 -0
  50. package/hooks/blocking/config/__init__.py +5 -5
  51. package/hooks/blocking/config/code_review_enforcement_constants.py +4 -1
  52. package/hooks/blocking/config/test_code_review_enforcement_constants.py +5 -0
  53. package/hooks/blocking/config/verified_commit_constants.py +160 -159
  54. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
  55. package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
  56. package/hooks/blocking/test_code_review_pr_create_gate.py +14 -0
  57. package/hooks/blocking/test_code_review_push_gate.py +16 -0
  58. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +19 -0
  59. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
  60. package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -1
  61. package/hooks/blocking/test_verdict_directory_write_blocker.py +808 -808
  62. package/hooks/blocking/test_verification_verdict_store.py +54 -0
  63. package/hooks/blocking/test_verified_commit_gate.py +581 -581
  64. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
  65. package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
  66. package/hooks/blocking/verification_verdict_store.py +1039 -1036
  67. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
  68. package/hooks/blocking/verifier_verdict_minter.py +280 -280
  69. package/hooks/git-hooks/test_pre_push.py +25 -0
  70. package/hooks/hooks.json +10 -0
  71. package/hooks/hooks_constants/CLAUDE.md +2 -1
  72. package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
  73. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
  74. package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
  75. package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
  76. package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
  77. package/hooks/pyproject.toml +1 -0
  78. package/hooks/validators/CLAUDE.md +1 -0
  79. package/hooks/validators/config/__init__.py +0 -0
  80. package/hooks/validators/config/directory_exemption_constants.py +183 -0
  81. package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
  82. package/hooks/validators/conftest.py +4 -0
  83. package/hooks/validators/ruff_integration.py +49 -5
  84. package/hooks/validators/run_all_validators.py +206 -9
  85. package/hooks/validators/test_directory_exemption_constants.py +185 -0
  86. package/hooks/validators/test_python_antipattern_checks.py +110 -5
  87. package/hooks/validators/test_ruff_integration.py +92 -1
  88. package/hooks/validators/test_run_all_validators.py +115 -68
  89. package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
  90. package/package.json +10 -2
  91. package/rules/CLAUDE.md +1 -0
  92. package/rules/docstring-prose-matches-implementation.md +45 -44
  93. package/rules/state-what-is.md +25 -0
  94. package/rules/verified-commit-gate-skip.md +1 -1
  95. package/scripts/CLAUDE.md +1 -0
  96. package/scripts/Capture-PoolHealth.ps1 +410 -0
  97. package/scripts/_code_review_test_support.py +404 -0
  98. package/scripts/claude_chain_runner.py +141 -1
  99. package/scripts/conftest.py +16 -1
  100. package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -1
  101. package/scripts/dev_env_scripts_constants/claude_chain_constants.py +9 -0
  102. package/scripts/resolve_worker_spawn.py +626 -626
  103. package/scripts/spawn_grok_batch.py +672 -672
  104. package/scripts/test_claude_chain_runner.py +131 -0
  105. package/scripts/test_invoke_code_review_chain.py +70 -0
  106. package/scripts/test_invoke_code_review_cli.py +192 -0
  107. package/scripts/test_invoke_code_review_contract.py +256 -0
  108. package/scripts/test_invoke_code_review_git.py +123 -0
  109. package/scripts/test_invoke_code_review_mode.py +99 -0
  110. package/scripts/test_resolve_worker_spawn.py +1014 -1014
  111. package/skills/CLAUDE.md +2 -0
  112. package/skills/auditing-claude-config/SKILL.md +114 -114
  113. package/skills/autoconverge/SKILL.md +427 -427
  114. package/skills/autoconverge/reference/convergence.md +24 -3
  115. package/skills/autoconverge/workflow/CLAUDE.md +1 -0
  116. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +3 -3
  117. package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1263
  118. package/skills/autoconverge/workflow/converge.mjs +167 -0
  119. package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
  120. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
  121. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
  122. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
  123. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
  124. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
  125. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
  126. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
  127. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
  128. package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
  129. package/skills/closeout/SKILL.md +33 -50
  130. package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
  131. package/skills/codex-review/scripts/run_codex_review.py +233 -1
  132. package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
  133. package/skills/condensing-instructions/SKILL.md +81 -0
  134. package/skills/copilot-review/SKILL.md +119 -119
  135. package/skills/e-code-review/SKILL.md +52 -0
  136. package/skills/e-code-review/reference/fix.md +54 -0
  137. package/skills/e-code-review/reference/loop.md +43 -0
  138. package/skills/e-code-review/reference/low.md +57 -0
  139. package/skills/e-code-review/reference/medium.md +153 -0
  140. package/skills/e-code-review/reference/xhigh.md +182 -0
  141. package/skills/e-simplify/SKILL.md +97 -0
  142. package/skills/issue-tracker/SKILL.md +92 -0
  143. package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
  144. package/skills/issue-tracker/reference/handoff-schema.md +64 -0
  145. package/skills/issue-tracker/reference/operation-matrix.md +41 -0
  146. package/skills/orchestrator/SKILL.md +162 -21
  147. package/skills/orchestrator/scripts/status_gate.py +625 -0
  148. package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
  149. package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
  150. package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
  151. package/skills/orchestrator/scripts/test_status_gate.py +439 -0
  152. package/skills/orchestrator-refresh/SKILL.md +110 -35
  153. package/skills/plan-to-pr/SKILL.md +155 -0
  154. package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
  155. package/skills/plan-to-pr/reference/model-routing.md +36 -0
  156. package/skills/plan-to-pr/reference/packet-contract.md +43 -0
  157. package/skills/plan-to-pr/reference/packet-schema.json +57 -0
  158. package/skills/plan-to-pr/reference/process-inventory.md +22 -0
  159. package/skills/plan-to-pr/reference/review-loop.md +33 -0
  160. package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
  161. package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
  162. package/skills/plan-to-pr/reference/task-seeds.md +14 -0
  163. package/skills/plan-to-pr/reference/task-ticket.md +38 -0
  164. package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
  165. package/skills/plan-to-pr/scripts/config/constants.py +193 -0
  166. package/skills/plan-to-pr/scripts/create_packet.py +173 -0
  167. package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
  168. package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
  169. package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
  170. package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
  171. package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
  172. package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
  173. package/skills/plan-to-pr/scripts/validate_run.py +173 -0
  174. package/skills/plan-to-pr/test_skill_contract.py +207 -0
  175. package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
  176. package/skills/pr-converge/SKILL.md +472 -469
  177. package/skills/pr-converge/reference/examples.md +3 -3
  178. package/skills/pr-converge/reference/fix-protocol.md +1 -1
  179. package/skills/pr-converge/reference/ground-rules.md +7 -4
  180. package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
  181. package/skills/pr-converge/reference/per-tick.md +5 -5
  182. package/skills/pr-converge/reference/progress-checklist.md +1 -1
  183. package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
  184. package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
  185. package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
  186. package/skills/pr-converge/test_step5_host_branch.py +1 -1
  187. package/skills/pr-fix-protocol/SKILL.md +1 -1
  188. package/skills/privacy-hygiene/SKILL.md +68 -68
  189. package/skills/prototype/workflows/promotion.md +1 -1
  190. package/skills/release-notes-html/SKILL.md +164 -0
  191. package/skills/task-build/CLAUDE.md +8 -7
  192. package/skills/task-build/SKILL.md +16 -8
  193. package/skills/task-build/reference/tool-routing.md +19 -0
  194. package/scripts/test_invoke_code_review.py +0 -966
  195. package/skills/closeout/reference/issue-body-templates.md +0 -108
@@ -1,85 +1,85 @@
1
- """Behavioral tests for stale_worktree_rule_sweep_constants.
2
-
3
- Confirms get_claude_worktrees_root resolves the worktrees directory under
4
- the user's ~/.claude home, and that the rule-parsing constants carry the
5
- literal shapes the sweep relies on.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- import importlib.util
11
- import sys
12
- from pathlib import Path
13
- from types import ModuleType
14
-
15
- import pytest
16
-
17
-
18
- def _load_constants_module() -> ModuleType:
19
- scripts_directory = Path(__file__).parent.parent
20
- parent_directory = str(scripts_directory.resolve())
21
- if parent_directory not in sys.path:
22
- sys.path.insert(0, parent_directory)
23
- module_path = (
24
- scripts_directory
25
- / "pr_loop_shared_constants"
26
- / "stale_worktree_rule_sweep_constants.py"
27
- )
28
- specification = importlib.util.spec_from_file_location(
29
- "pr_loop_shared_constants.stale_worktree_rule_sweep_constants", module_path
30
- )
31
- assert specification is not None
32
- assert specification.loader is not None
33
- module = importlib.util.module_from_spec(specification)
34
- specification.loader.exec_module(module)
35
- return module
36
-
37
-
38
- def test_worktrees_root_resolves_under_the_user_claude_home(
39
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
40
- ) -> None:
41
- constants_module = _load_constants_module()
42
- monkeypatch.setenv("HOME", str(tmp_path))
43
- monkeypatch.setenv("USERPROFILE", str(tmp_path))
44
- resolved_worktrees_root = constants_module.get_claude_worktrees_root()
45
- assert resolved_worktrees_root == tmp_path / ".claude" / "worktrees"
46
-
47
-
48
- def test_extract_rule_target_path_reads_the_delimited_path() -> None:
49
- constants_module = _load_constants_module()
50
- extracted_path = constants_module.extract_rule_target_path(
51
- "Edit(/repo/wt/.claude/**)"
52
- )
53
- assert extracted_path == "/repo/wt/.claude/**"
54
-
55
-
56
- def test_extract_rule_target_path_returns_none_for_a_non_rule() -> None:
57
- constants_module = _load_constants_module()
58
- assert constants_module.extract_rule_target_path("not a rule") is None
59
-
60
-
61
- def test_worktree_directory_for_rule_keeps_nested_segments_below_the_root() -> None:
62
- constants_module = _load_constants_module()
63
- worktrees_root = Path("/home/dev/.claude/worktrees")
64
- resolved_directory = constants_module.worktree_directory_for_rule(
65
- "Edit(/home/dev/.claude/worktrees/repo/feature/.claude/**)", worktrees_root
66
- )
67
- assert resolved_directory == worktrees_root / "repo" / "feature"
68
-
69
-
70
- def test_worktree_directory_for_rule_keeps_one_segment_flat_layout() -> None:
71
- constants_module = _load_constants_module()
72
- worktrees_root = Path("/home/dev/.claude/worktrees")
73
- resolved_directory = constants_module.worktree_directory_for_rule(
74
- "Edit(/home/dev/.claude/worktrees/flat-worktree/.claude/**)", worktrees_root
75
- )
76
- assert resolved_directory == worktrees_root / "flat-worktree"
77
-
78
-
79
- def test_worktree_directory_for_rule_returns_none_outside_the_root() -> None:
80
- constants_module = _load_constants_module()
81
- worktrees_root = Path("/home/dev/.claude/worktrees")
82
- resolved_directory = constants_module.worktree_directory_for_rule(
83
- "Edit(/home/dev/project/.claude/**)", worktrees_root
84
- )
85
- assert resolved_directory is None
1
+ """Behavioral tests for stale_worktree_rule_sweep_constants.
2
+
3
+ Confirms get_claude_worktrees_root resolves the worktrees directory under
4
+ the user's ~/.claude home, and that the rule-parsing constants carry the
5
+ literal shapes the sweep relies on.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import importlib.util
11
+ import sys
12
+ from pathlib import Path
13
+ from types import ModuleType
14
+
15
+ import pytest
16
+
17
+
18
+ def _load_constants_module() -> ModuleType:
19
+ scripts_directory = Path(__file__).parent.parent
20
+ parent_directory = str(scripts_directory.resolve())
21
+ if parent_directory not in sys.path:
22
+ sys.path.insert(0, parent_directory)
23
+ module_path = (
24
+ scripts_directory
25
+ / "pr_loop_shared_constants"
26
+ / "stale_worktree_rule_sweep_constants.py"
27
+ )
28
+ specification = importlib.util.spec_from_file_location(
29
+ "pr_loop_shared_constants.stale_worktree_rule_sweep_constants", module_path
30
+ )
31
+ assert specification is not None
32
+ assert specification.loader is not None
33
+ module = importlib.util.module_from_spec(specification)
34
+ specification.loader.exec_module(module)
35
+ return module
36
+
37
+
38
+ def test_worktrees_root_resolves_under_the_user_claude_home(
39
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
40
+ ) -> None:
41
+ constants_module = _load_constants_module()
42
+ monkeypatch.setenv("HOME", str(tmp_path))
43
+ monkeypatch.setenv("USERPROFILE", str(tmp_path))
44
+ resolved_worktrees_root = constants_module.get_claude_worktrees_root()
45
+ assert resolved_worktrees_root == tmp_path / ".claude" / "worktrees"
46
+
47
+
48
+ def test_extract_rule_target_path_reads_the_delimited_path() -> None:
49
+ constants_module = _load_constants_module()
50
+ extracted_path = constants_module.extract_rule_target_path(
51
+ "Edit(/repo/wt/.claude/**)"
52
+ )
53
+ assert extracted_path == "/repo/wt/.claude/**"
54
+
55
+
56
+ def test_extract_rule_target_path_returns_none_for_a_non_rule() -> None:
57
+ constants_module = _load_constants_module()
58
+ assert constants_module.extract_rule_target_path("not a rule") is None
59
+
60
+
61
+ def test_worktree_directory_for_rule_keeps_nested_segments_below_the_root() -> None:
62
+ constants_module = _load_constants_module()
63
+ worktrees_root = Path("/home/dev/.claude/worktrees")
64
+ resolved_directory = constants_module.worktree_directory_for_rule(
65
+ "Edit(/home/dev/.claude/worktrees/repo/feature/.claude/**)", worktrees_root
66
+ )
67
+ assert resolved_directory == worktrees_root / "repo" / "feature"
68
+
69
+
70
+ def test_worktree_directory_for_rule_keeps_one_segment_flat_layout() -> None:
71
+ constants_module = _load_constants_module()
72
+ worktrees_root = Path("/home/dev/.claude/worktrees")
73
+ resolved_directory = constants_module.worktree_directory_for_rule(
74
+ "Edit(/home/dev/.claude/worktrees/flat-worktree/.claude/**)", worktrees_root
75
+ )
76
+ assert resolved_directory == worktrees_root / "flat-worktree"
77
+
78
+
79
+ def test_worktree_directory_for_rule_returns_none_outside_the_root() -> None:
80
+ constants_module = _load_constants_module()
81
+ worktrees_root = Path("/home/dev/.claude/worktrees")
82
+ resolved_directory = constants_module.worktree_directory_for_rule(
83
+ "Edit(/home/dev/project/.claude/**)", worktrees_root
84
+ )
85
+ assert resolved_directory is None
@@ -146,7 +146,7 @@ Mode decision:
146
146
  | `ThirdParty` | any | `chain` | Headless spawn through `claude_chain_runner` |
147
147
 
148
148
  The review always runs at effort **high** on model **opus**. Chain mode builds
149
- a single-turn prompt `/code-review xhigh --fix`, pins `--model opus`, sets
149
+ a single-turn prompt `/code-review ultra --fix`, pins `--model opus`, sets
150
150
  subprocess `cwd` to the PR working tree, and reads stdin from the empty stream
151
151
  so the spawn does not wait for input.
152
152
 
package/agents/CLAUDE.md CHANGED
@@ -15,6 +15,7 @@ Agent definition files installed into `~/.claude/agents/` by `bin/install.mjs`.
15
15
  | `deep-research.md` | Deep Research | Citation-grounded research with web search |
16
16
  | `docs-agent.md` | Docs Agent | Documentation authoring and maintenance |
17
17
  | `git-commit-crafter.md` | Git Commit Crafter | Stages changes, writes conventional commit messages, creates commits |
18
+ | `issue-tracker.md` | Issue Tracker | Primary handler for one GitHub issue action per spawn; loads the issue-tracker skill (plain-brief); returns issue numbers and URLs |
18
19
  | `plan-packet-validator.md` | Plan Packet Validator | Fresh-context validator for workflow-generated plan packets under `docs/plans/` |
19
20
  | `pr-description-writer.md` | PR Description Writer | Authors PR descriptions in Anthropic-style shapes that pass the `pr_description_enforcer` hook's body audit |
20
21
  | `session-advisor.md` | Session Advisor | Standing multi-consumer reviewer; SendMessage only; returns endorse/correction/plan/stop |
@@ -22,7 +23,7 @@ Agent definition files installed into `~/.claude/agents/` by `bin/install.mjs`.
22
23
 
23
24
  ## Format
24
25
 
25
- Each file uses YAML frontmatter (`name`, `description`, `tools`, optional `color`) followed by a Markdown body with the agent's behavioral instructions. The `description` field appears in the Claude Code agent picker.
26
+ Each file uses YAML frontmatter (`name`, `description`, `tools`, optional `color`) followed by a Markdown body with the agent's behavioral instructions. The `description` field appears in the Claude Code agent picker. An agent definition carries no `model` key — the caller supplies the model on each spawn.
26
27
 
27
28
  ## Adding an agent
28
29
 
package/agents/caveman.md CHANGED
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: caveman
3
3
  description: Trims noise from an artifact the main caller has already authored. Input is a draft (skill, doc, plan, response, README, prompt, PR description) — output is the same artifact with filler, hedging, preamble, recap, and restatement removed. Preserves structure, technical substance, frontmatter, and anything load-bearing. Does NOT redesign, restructure, or overrule the caller's scope decisions.
4
- model: inherit
5
4
  color: red
6
5
  ---
7
6
 
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: clasp-deployment-orchestrator
3
3
  description: Use PROACTIVELY when creating or deploying Google Apps Script projects with multiple files, complex configuration, or batch operations. Invokes the clasp-deployment skill for command execution.
4
- model: inherit
5
4
  tools: Task, Read, Write, Glob, Grep
6
5
  color: green
7
6
  ---
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: clean-coder
3
3
  description: "Use PROACTIVELY for ALL code generation — feature development, bug fixes, refactoring, hook creation, automation scripts, and any task that produces code. Internalizes CODE_RULES.md and the 8-dimension readability standard so thoroughly that /check finds zero issues. The definitive code-writing agent."
4
- model: opus
5
4
  tools: Read, Write, Edit, Bash, Grep, Glob, Task, Skill, SendMessage
6
5
  color: green
7
6
  ---
@@ -2,7 +2,6 @@
2
2
  name: code-advisor
3
3
  description: Single-executor mid-run advisor. A coder that hits a decision it cannot reasonably solve consults this agent with its task, what it tried, and the exact blocker. Returns PLAN, CORRECTION, or STOP as final reply text (not SendMessage). Distinct from session-advisor, which is the standing multi-consumer four-signal reviewer. Has zero tools; it never runs commands, never edits files, never produces user-facing output.
4
4
  tools: []
5
- model: inherit
6
5
  color: purple
7
6
  ---
8
7
 
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: code-quality-agent
3
3
  description: Use this agent for comprehensive code quality reviews across multiple files.
4
- model: opus
5
4
  color: red
6
5
  ---
7
6
 
@@ -175,7 +174,7 @@ Followed by the Shape A finding list, the Shape B proof-of-absence list, and the
175
174
 
176
175
  ## Caller Context
177
176
 
178
- Callers /bugteam, /qbug, and /findbugs invoke this agent at different models per call (opus for /bugteam, sonnet primary for /findbugs, haiku secondary for both /qbug and /findbugs). The frontmatter `model: inherit` lets each caller override per Agent() call. Persistence files such as `loop-N-audit.json` and `loop-N-diagnostics.json` are the calling skill's responsibility — your output is the structured finding list defined above.
177
+ Callers /bugteam, /qbug, and /findbugs invoke this agent at different models per call (opus for /bugteam, sonnet primary for /findbugs, haiku secondary for both /qbug and /findbugs). The frontmatter carries no `model:` key, so each caller's `Agent()` model applies. Persistence files such as `loop-N-audit.json` and `loop-N-diagnostics.json` are the calling skill's responsibility — your output is the structured finding list defined above.
179
178
 
180
179
  ## Examples
181
180
 
@@ -2,7 +2,6 @@
2
2
  name: code-verifier
3
3
  description: Post-hoc verification agent for the three-phase code workflow. Spawned by the main session after coder agents finish. Runs every check itself in a fresh context — named gates, tests against recorded baselines, two-way diff-vs-task reading — puts the draft verdict through one strongest-tier validation subagent that tries to refute it, then ends with a fenced verdict block the verifier_verdict_minter hook turns into the commit-gate verdict. Read and execute only; it never edits files.
4
4
  tools: Read, Grep, Glob, Bash, Task
5
- model: sonnet
6
5
  color: orange
7
6
  ---
8
7
 
@@ -20,7 +20,6 @@ description: Use this agent for iterative, multi-source deep research that produ
20
20
  </commentary>
21
21
  </example>
22
22
 
23
- model: opus
24
23
  color: cyan
25
24
  ---
26
25
 
@@ -27,7 +27,6 @@ Examples:
27
27
  Non-technical audience — use docs-agent in user-docs writing mode.
28
28
  </commentary>
29
29
  </example>
30
- model: inherit
31
30
  color: cyan
32
31
  ---
33
32
 
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: git-commit-crafter
3
3
  description: Use this agent when you need to create git commits, stage changes, or organize multiple file changes into atomic commits. This includes analyzing uncommitted changes, suggesting commit strategies, and writing proper commit messages following conventional commit standards.
4
- model: inherit
5
4
  color: yellow
6
5
  ---
7
6
 
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: issue-tracker
3
+ description: >-
4
+ Primary handler for one GitHub issue action per spawn on a work-stream:
5
+ open an epic, file a sub-issue, update status in place, refresh the epic
6
+ checklist, or close a sub-issue. Spawn with one action plus the issue-candidate
7
+ or issue number it needs; loads the issue-tracker skill; returns affected
8
+ issue numbers and URLs. Prefer the same warm agent for follow-ups on the same
9
+ issue or the same epic work-stream.
10
+ tools: Read, Bash, Skill, mcp__github__search_issues, mcp__github__issue_read, mcp__github__issue_write, mcp__github__sub_issue_write, mcp__github__add_issue_comment
11
+ color: green
12
+ ---
13
+
14
+ # Issue tracker agent
15
+
16
+ **Caller wants one issue action. You run that action and hand back numbers + URLs.**
17
+
18
+ **dedup -> one action -> markers only -> numbers + URLs**
19
+
20
+ You are the primary handler for one action per turn. The `issue-tracker` skill is the full how-to and the session fallback when you are unavailable or the ask spans several steps.
21
+
22
+ ## Warm reuse (callers)
23
+
24
+ Keep the same warm `issue-tracker` agent for follow-up actions on the **same issue** or a **related issue on the same epic**. Message that agent again with the next action. Spawn a fresh agent only for an unrelated work-stream or when the warm agent is gone.
25
+
26
+ ## Voice
27
+
28
+ Use the `plain-brief` output style (`output-styles/plain-brief.md`). Final message is issue number(s) and URL(s) only.
29
+
30
+ ## On each turn
31
+
32
+ 1. **Load the skill** if this is the first turn of this agent. Use the Skill tool to load `issue-tracker`. Follow it for markers, dedup, tools, handoff schema, PR auto-close, and gotchas. Do not invent a second path.
33
+ 2. **Run the named action.** The ticket names one action: file a sub-issue from an issue-candidate, open an epic, update status in place, refresh the epic checklist, or close a sub-issue. Run that action end to end (every step *that* action needs: labels, attach, checklist refresh when the action creates or closes a child). Do not start a second, unrelated action in this turn.
34
+ 3. **Return numbers and URLs.** Affected issue number(s) and URL(s) only, no narration after that payload, so the caller can send the next action to this same agent or spawn another when the work-stream changes.
35
+
36
+ ## Hold these lines
37
+
38
+ - Dedup open **and** closed before create.
39
+ - Edit only between marker pairs; comments are cross-links only.
40
+ - Attach with REST `.id` and `gh -F`, never display `#N` or `-f`.
41
+ - Prefer GitHub MCP; fall back to `gh` per the skill matrix.
42
+ - When a fix ships: **PR body first** for auto-close. On the PR that merges into the **default branch**, put `Closes #N` for each finished sub-issue (`Closes #12` and `Closes #13`, not `Closes #12, #13`). Prefer the same line on the first commit. Stacked PRs that are not default-branch merges use plain `#N` only. A related UI link without a closing keyword does not close the issue. You own picking the correct `#N`.
@@ -2,7 +2,6 @@
2
2
  name: plan-packet-validator
3
3
  description: Fresh-context validator for workflow-generated plan packets. Use after a plan packet is written under docs/plans/<slug>/ to verify source accuracy, completeness, TDD readiness, scope control, handoff quality, and no invented repo behavior. Read-only; never edits files.
4
4
  tools: Read, Grep, Glob, Bash
5
- model: inherit
6
5
  color: purple
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: pr-description-writer
3
3
  description: "Optional agent for PR descriptions and PR comments. Authors bodies in one of three Anthropic-derived shapes (Trivial / Standard / Heavy) so PRs match merged-PR style in `anthropics/claude-code`, `anthropics/claude-code-action`, and `anthropics/claude-code-sdk-python`, and pass the `pr_description_enforcer` PreToolUse hook's body audit on `gh pr create`, `gh pr edit`, and `gh pr comment`."
4
4
  tools: Read,Grep,Glob,Bash
5
- model: haiku
6
5
  ---
7
6
 
8
7
  # PR Description Writer
@@ -5,47 +5,79 @@ Code subagent loader reads. The loader accepts a fixed key set; an unrecognized
5
5
  top-level key breaks the spawn — the subagent starts with a broken definition,
6
6
  idles, and dies without a report::
7
7
 
8
- ok: name / description / tools / model / color
8
+ ok: name / description / tools / color
9
9
  flag: effort <- unrecognized, subagent dies delivering nothing
10
10
 
11
- The accepted set is the one `agents/CLAUDE.md` documents (name, description,
12
- tools, color) plus the `model` pin the working agents carry (clean-coder pins
13
- opus, pr-description-writer pins haiku). Top-level keys are read with a line
14
- scan so an agent whose `description` embeds informal `<example>` prose is not
15
- mistaken for one carrying extra keys.
11
+ An agent definition carries no `model` key at all — the caller supplies the
12
+ model on every spawn, so no agent definition names one, concrete or
13
+ `inherit`::
14
+
15
+ ok: <no model key at all>
16
+ flag: model: inherit <- caller can no longer choose the model
17
+ flag: model: opus <- pinned concrete model, caller can't override
18
+
19
+ Frontmatter parsing is self-contained here (stdlib only): the block is the
20
+ text between the file's opening and closing `---` fence lines, and top-level
21
+ keys are read with a line scan so an agent whose `description` embeds
22
+ informal `<example>` prose is not mistaken for one carrying extra keys.
16
23
  """
17
24
 
18
25
  from __future__ import annotations
19
26
 
20
27
  import re
28
+ from functools import cache
21
29
  from pathlib import Path
22
30
 
23
31
  import pytest
24
32
  import yaml
25
33
 
26
- ACCEPTED_FRONTMATTER_KEYS = frozenset(
27
- {"name", "description", "tools", "model", "color"}
28
- )
29
- FRONTMATTER_FENCE = "---"
30
- FRONTMATTER_SEGMENT_COUNT = 3
34
+ ACCEPTED_FRONTMATTER_KEYS = frozenset({"name", "description", "tools", "color"})
31
35
  CODE_VERIFIER_AGENT_NAME = "code-verifier"
36
+ EXEMPT_MARKDOWN_FILENAME = "CLAUDE.md"
37
+ FRONTMATTER_FENCE_LINE = "---"
38
+ MODEL_KEY_NAME = "model"
32
39
  TOP_LEVEL_KEY_PATTERN = re.compile(r"^([a-z][a-z0-9_]*):", re.MULTILINE)
33
40
 
34
41
 
35
- def _agent_definition_paths() -> list[Path]:
42
+ def _extract_frontmatter_block(markdown_text: str) -> str | None:
43
+ """Return the YAML text between the file's opening and closing --- fences.
44
+
45
+ Args:
46
+ markdown_text: The full text of an agent definition markdown file.
47
+
48
+ Returns:
49
+ The frontmatter text (the lines strictly between the two fence
50
+ lines), or None when the file does not open with a --- fence line or
51
+ never closes one.
52
+ """
53
+ all_lines = markdown_text.splitlines()
54
+ if not all_lines or all_lines[0].strip() != FRONTMATTER_FENCE_LINE:
55
+ return None
56
+ for each_line_index in range(1, len(all_lines)):
57
+ if all_lines[each_line_index].strip() == FRONTMATTER_FENCE_LINE:
58
+ return "\n".join(all_lines[1:each_line_index])
59
+ return None
60
+
61
+
62
+ @cache
63
+ def _agent_definition_paths() -> tuple[Path, ...]:
36
64
  agents_directory = Path(__file__).parent
37
65
  all_markdown_files = sorted(agents_directory.glob("*.md"))
38
- return [
66
+ return tuple(
39
67
  each_markdown_file
40
68
  for each_markdown_file in all_markdown_files
41
- if each_markdown_file.read_text(encoding="utf-8").startswith(FRONTMATTER_FENCE)
42
- ]
69
+ if each_markdown_file.name != EXEMPT_MARKDOWN_FILENAME
70
+ and _extract_frontmatter_block(each_markdown_file.read_text(encoding="utf-8"))
71
+ is not None
72
+ )
43
73
 
44
74
 
45
75
  def _frontmatter_block(agent_definition_path: Path) -> str:
46
- agent_text = agent_definition_path.read_text(encoding="utf-8")
47
- fence_segments = agent_text.split(FRONTMATTER_FENCE, FRONTMATTER_SEGMENT_COUNT - 1)
48
- return fence_segments[1]
76
+ frontmatter_block = _extract_frontmatter_block(
77
+ agent_definition_path.read_text(encoding="utf-8")
78
+ )
79
+ assert frontmatter_block is not None
80
+ return frontmatter_block
49
81
 
50
82
 
51
83
  def _top_level_keys(frontmatter_block: str) -> set[str]:
@@ -76,3 +108,20 @@ def test_code_verifier_frontmatter_parses_and_names_the_agent() -> None:
76
108
  parsed_frontmatter = yaml.safe_load(code_verifier_block)
77
109
  assert parsed_frontmatter["name"] == CODE_VERIFIER_AGENT_NAME
78
110
  assert set(parsed_frontmatter) <= ACCEPTED_FRONTMATTER_KEYS
111
+
112
+
113
+ @pytest.mark.parametrize(
114
+ "agent_definition_path",
115
+ _agent_definition_paths(),
116
+ ids=lambda each_path: each_path.name,
117
+ )
118
+ def test_agent_frontmatter_carries_no_model_key(
119
+ agent_definition_path: Path,
120
+ ) -> None:
121
+ frontmatter_block = _frontmatter_block(agent_definition_path)
122
+ declared_keys = _top_level_keys(frontmatter_block)
123
+ assert MODEL_KEY_NAME not in declared_keys, (
124
+ f"{agent_definition_path.name} carries a model: key in frontmatter; "
125
+ "the caller supplies the model on every spawn, so agent definitions "
126
+ "carry no model key at all, not even model: inherit"
127
+ )