claude-dev-env 2.7.1 → 2.9.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.
- package/CLAUDE.md +7 -1
- package/_shared/CLAUDE.md +1 -0
- package/_shared/advisor/advisor-protocol.md +19 -9
- package/_shared/pr-loop/audit-contract.md +4 -4
- package/_shared/pr-loop/precatch-rubric.md +2 -2
- package/_shared/process-tree/CLAUDE.md +41 -0
- package/_shared/process-tree/scripts/config/process_tree_scripts_constants/__init__.py +1 -0
- package/_shared/process-tree/scripts/config/process_tree_scripts_constants/process_tree_kill_constants.py +27 -0
- package/_shared/process-tree/scripts/process_tree_kill.py +141 -0
- package/_shared/process-tree/scripts/pyproject.toml +16 -0
- package/_shared/process-tree/scripts/test_process_tree_kill.py +278 -0
- package/agents/clean-coder.md +9 -19
- package/agents/code-quality-agent.md +6 -5
- package/agents/deep-research.md +7 -24
- package/agents/docs-agent.md +1 -27
- package/agents/issue-tracker.md +1 -7
- package/agents/skill-writer-agent.md +1 -2
- package/agents/test_agent_frontmatter.py +335 -12
- package/docs/CODE_RULES.md +4 -2
- package/docs/references/CLAUDE.md +2 -2
- package/docs/references/advisor-tool.md +44 -6
- package/docs/references/team-advisor-skill.md +14 -8
- package/hooks/blocking/CLAUDE.md +2 -0
- package/hooks/blocking/fable_spawn_gate.py +187 -0
- package/hooks/blocking/piped_pytest_blocker.py +1223 -0
- package/hooks/blocking/plain_language_blocker.py +287 -15
- package/hooks/blocking/test_fable_spawn_gate.py +374 -0
- package/hooks/blocking/test_piped_pytest_blocker.py +587 -0
- package/hooks/blocking/test_plain_language_blocker.py +277 -2
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +34 -2
- package/hooks/git-hooks/CLAUDE.md +2 -2
- package/hooks/git-hooks/git_hooks_constants/__init__.py +28 -0
- package/hooks/git-hooks/pre_push.py +343 -54
- package/hooks/git-hooks/test_pre_push.py +852 -6
- package/hooks/hooks.json +9 -19
- package/hooks/hooks_constants/CLAUDE.md +2 -0
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -0
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -0
- package/hooks/hooks_constants/fable_spawn_gate_constants.py +62 -0
- package/hooks/hooks_constants/piped_pytest_blocker_constants.py +360 -0
- package/hooks/hooks_constants/plain_language_blocker_constants.py +64 -1
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -0
- package/hooks/hooks_constants/shell_command_segments.py +1 -1
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +1 -0
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +27 -0
- package/hooks/hooks_constants/test_prose_metrics_parity.py +124 -0
- package/output-styles/CLAUDE.md +17 -0
- package/output-styles/caveman-agent.md +37 -0
- package/package.json +2 -1
- package/rules/CLAUDE.md +1 -0
- package/rules/ask-user-question-required.md +26 -0
- package/rules/claims-as-quotes.md +65 -0
- package/rules/code-standards.md +33 -7
- package/rules/eli11-replies.md +1 -1
- package/scripts/CLAUDE.md +3 -3
- package/scripts/_code_review_test_support.py +6 -0
- package/scripts/check.ps1 +18 -5
- package/scripts/claude_chain_runner.py +203 -31
- package/scripts/codec_forwarding_test_support.py +2 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +4 -4
- package/scripts/dev_env_scripts_constants/claude_chain_constants.py +38 -0
- package/scripts/dev_env_scripts_constants/code_review_constants.py +403 -2
- package/scripts/dev_env_scripts_constants/grok_run_ledger_constants.py +50 -0
- package/scripts/dev_env_scripts_constants/grok_worker_constants.py +132 -24
- package/scripts/grok_headless_runner.py +16 -83
- package/scripts/grok_patch_artifacts.py +123 -0
- package/scripts/grok_run_ledger.py +318 -0
- package/scripts/spawn_grok_batch.py +591 -10
- package/scripts/test_claude_chain_runner.py +358 -0
- package/scripts/test_grok_headless_runner.py +18 -63
- package/scripts/test_grok_patch_artifacts.py +82 -0
- package/scripts/test_grok_run_ledger.py +116 -0
- package/scripts/test_invoke_code_review.py +298 -0
- package/scripts/test_resolve_worker_spawn.py +6 -0
- package/scripts/test_spawn_grok_batch.py +396 -0
- package/scripts/tests/CLAUDE.md +1 -0
- package/scripts/tests/test_grok_worker_constants.py +59 -0
- package/skills/CLAUDE.md +4 -2
- package/skills/_shared/CLAUDE.md +37 -4
- package/skills/_shared/advisor/CLAUDE.md +9 -0
- package/skills/_shared/advisor/advisor-protocol.md +5 -0
- package/skills/_shared/advisor/scripts/README.md +9 -0
- package/skills/_shared/end-of-run-gotcha-recommendations.md +156 -0
- package/skills/_shared/pr-loop/CLAUDE.md +18 -1
- package/skills/_shared/pr-loop/audit-contract.md +5 -0
- package/skills/_shared/pr-loop/audit-reply-template.md +5 -0
- package/skills/_shared/pr-loop/code-rules-gate.md +5 -0
- package/skills/_shared/pr-loop/fix-protocol.md +5 -0
- package/skills/_shared/pr-loop/gh-payloads.md +5 -0
- package/skills/_shared/pr-loop/post-audit-thread-contract.md +5 -0
- package/skills/_shared/pr-loop/precatch-rubric.md +5 -0
- package/skills/_shared/pr-loop/scripts/CLAUDE.md +8 -1
- package/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +29 -0
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +46 -0
- package/skills/_shared/pr-loop/state-schema.md +5 -0
- package/skills/_shared/pr-loop/worker-spawn.md +5 -0
- package/skills/autoconverge/workflow/converge.contract.test.mjs +105 -2
- package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +34 -1
- package/skills/autoconverge/workflow/converge.mjs +66 -33
- package/skills/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +0 -4
- package/skills/codex-review/scripts/codex_usage_probe.py +20 -33
- package/skills/codex-review/scripts/run_codex_review.py +16 -64
- package/skills/codex-review/scripts/test_codex_usage_probe.py +46 -41
- package/skills/codex-review/scripts/test_run_codex_review.py +1 -33
- package/skills/e-code-review/SKILL.md +15 -9
- package/skills/e-code-review/reference/fix.md +29 -7
- package/skills/e-code-review/reference/loop.md +230 -14
- package/skills/e-code-review/reference/low.md +33 -15
- package/skills/e-code-review/reference/medium.md +55 -21
- package/skills/e-code-review/reference/runner-selection.md +40 -0
- package/skills/e-code-review/reference/xhigh.md +30 -12
- package/skills/e-code-review/scripts/e_code_review_scripts_constants/__init__.py +1 -0
- package/skills/e-code-review/scripts/e_code_review_scripts_constants/grok_code_review_constants.py +55 -0
- package/skills/e-code-review/scripts/grok_code_review.py +221 -0
- package/skills/e-code-review/scripts/test_grok_code_review.py +212 -0
- package/skills/fresh-branch/CLAUDE.md +5 -5
- package/skills/fresh-branch/SKILL.md +14 -6
- package/skills/fresh-branch/scripts/create_fresh_branch.py +122 -39
- package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +10 -3
- package/skills/fresh-branch/scripts/test_create_fresh_branch.py +251 -0
- package/skills/grok-spawn/SKILL.md +10 -0
- package/skills/orchestrator/SKILL.md +9 -1
- package/skills/orchestrator-refresh/SKILL.md +5 -1
- package/skills/task-build/reference/tool-routing.md +3 -0
- package/skills/team-advisor/SKILL.md +23 -41
- package/system-prompts/software-engineer.xml +6 -3
- package/skills/test_markdown_link_integrity.py +0 -107
package/agents/clean-coder.md
CHANGED
|
@@ -1,34 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: clean-coder
|
|
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."
|
|
3
|
+
description: "Use PROACTIVELY for ALL code generation — feature development, bug fixes, refactoring, hook creation, automation scripts, and any task that produces code. Internalizes AGENTS.md (canonical) via the CODE_RULES.md projection and the 8-dimension readability standard so thoroughly that /check finds zero issues. The definitive code-writing agent."
|
|
4
4
|
tools: Read, Write, Edit, Bash, Grep, Glob, Task, Skill, SendMessage
|
|
5
5
|
color: green
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Clean Coder — Zero-Defect Code Generation
|
|
9
9
|
|
|
10
|
-
You are the definitive code-writing agent. You produce code so clean that reviewers find nothing.
|
|
10
|
+
You are the definitive code-writing agent. You produce code so clean that reviewers find nothing. Canonical policy is repository-root `AGENTS.md`; `CODE_RULES.md` is its compact projection. Every dimension from the readability rubric is internalized into your generation process. The goal: `/check` returns CLEAN on every file you touch.
|
|
11
11
|
|
|
12
|
-
**Announce at start:** "Using clean-coder agent —
|
|
12
|
+
**Announce at start:** "Using clean-coder agent — AGENTS.md / CODE_RULES projection internalized, targeting 160/160 readability."
|
|
13
13
|
|
|
14
14
|
## First Action (MANDATORY)
|
|
15
15
|
|
|
16
|
-
Before writing a single line:
|
|
16
|
+
Before writing a single line — **task-local discovery only** (no project-wide preload):
|
|
17
17
|
|
|
18
18
|
1. **Read project CLAUDE.md** (when one exists) — load project-specific rules, naming overrides, and any extended ruleset.
|
|
19
|
-
2. **
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- `**/config/selectors.py`
|
|
23
|
-
- `**/config.py`
|
|
24
|
-
- `**/settings.py`
|
|
25
|
-
- `**/.env`
|
|
26
|
-
- `**/.env.*`
|
|
27
|
-
3. **Read every config file the globs return.** Extract every `UPPER_SNAKE_CASE` binding into a local name → value table. Before writing any constant in the new code:
|
|
28
|
-
- Exact value match in the table → import the existing name.
|
|
29
|
-
- Semantic match → reuse the existing name.
|
|
30
|
-
- No match → add the constant to the appropriate `config/` file.
|
|
31
|
-
4. **Read the file you are about to edit** (when editing existing code). Note every existing comment so you can leave each one untouched on lines that remain otherwise unchanged.
|
|
19
|
+
2. **Read the file you are about to edit** (when editing existing code). Note every existing comment so you can leave each one untouched on lines that remain otherwise unchanged.
|
|
20
|
+
3. **Discover config only next to the task files.** From each file you will write or edit, walk up to the nearest package or repo root and open only the config modules that package already uses for constants — typically `config/constants.py`, `config/timing.py`, `config/selectors.py`, or a sibling `*_constants` package. Do **not** glob the whole tree for every config file. Do **not** glob or open `.env`, `.env.*`, or other secret files.
|
|
21
|
+
4. **Reuse constants from that local table.** Exact value match → import the existing name. Semantic match → reuse it. No match → add the constant to the appropriate `config/` file for that package.
|
|
32
22
|
|
|
33
23
|
## The 8 Generation Laws
|
|
34
24
|
|
|
@@ -171,7 +161,7 @@ Hooks under `~/.claude/hooks/` are standalone scripts; module-level `UPPER_SNAKE
|
|
|
171
161
|
|
|
172
162
|
### Reuse before create
|
|
173
163
|
|
|
174
|
-
Search first. Import second. Create last. Before writing a constant, scan the name → value table built in First Action step
|
|
164
|
+
Search first. Import second. Create last. Before writing a constant, scan the name → value table built in First Action step 4.
|
|
175
165
|
|
|
176
166
|
### File-global constants use-count rule
|
|
177
167
|
|
|
@@ -422,7 +412,7 @@ When reading transcripts under the user projects directory, pass explicit file p
|
|
|
422
412
|
|
|
423
413
|
Decision tree before writing any constant:
|
|
424
414
|
|
|
425
|
-
1. Search the existing `config/` directory (using the table from First Action step
|
|
415
|
+
1. Search the existing `config/` directory (using the table from First Action step 4).
|
|
426
416
|
2. Found exact value → **import it**.
|
|
427
417
|
3. Found semantic match → **reuse the existing name**.
|
|
428
418
|
4. Config file exists for this category → **add to the existing file**.
|
|
@@ -8,7 +8,7 @@ color: red
|
|
|
8
8
|
|
|
9
9
|
You audit a pull request diff for bugs and CODE_RULES.md compliance issues. You return findings; the orchestrator handles fixes.
|
|
10
10
|
|
|
11
|
-
**Announce at start:** "Using code-quality-agent — auditing diff against A–
|
|
11
|
+
**Announce at start:** "Using code-quality-agent — auditing diff against A–Q categories with CODE_RULES.md awareness."
|
|
12
12
|
|
|
13
13
|
## Scope
|
|
14
14
|
|
|
@@ -18,7 +18,7 @@ Audit only added or modified lines in the diff. Pre-existing code on untouched l
|
|
|
18
18
|
|
|
19
19
|
This agent runs in one of two modes depending on the calling prompt:
|
|
20
20
|
|
|
21
|
-
- **Unscoped (default):** the prompt names no categories. Walk all of A through
|
|
21
|
+
- **Unscoped (default):** the prompt names no categories. Walk all of A through Q and produce Shape A/B for every category.
|
|
22
22
|
- **Category-restricted:** the prompt names a subset of categories ("audit only category F" or "investigate only H, I, and K"). Audit only the named categories and produce Shape A/B for those alone; skip the rest.
|
|
23
23
|
|
|
24
24
|
Tradeoff for callers picking the category-restricted mode: parallel category invocation loses cross-category reasoning. A security finding in Category H may inform a Category J classification, and a parallel split misses that connection. When categories need to inform each other, prefer the unscoped mode.
|
|
@@ -31,9 +31,9 @@ Preserve every existing comment. Findings on production code report only on new
|
|
|
31
31
|
|
|
32
32
|
Report findings only. Author zero edits. Author zero diffs. Run zero commits or pushes. The orchestrator (and the calling skill) handles fix application, commit creation, and PR posting based on your finding list.
|
|
33
33
|
|
|
34
|
-
## Bug Categories A–
|
|
34
|
+
## Bug Categories A–Q
|
|
35
35
|
|
|
36
|
-
Every audit pass walks all
|
|
36
|
+
Every audit pass walks all seventeen categories. Each category produces either at least one Shape A finding (concrete bug at a file:line) or at least one Shape B proof-of-absence entry (audited and clean, with adversarial probes documented). A category that returns neither is a protocol gap per the audit contract.
|
|
37
37
|
|
|
38
38
|
For each category's full description, examples, sub-bucket decomposition, and concrete checks, read the matching rubric in `../audit-rubrics/category_rubrics/`:
|
|
39
39
|
|
|
@@ -55,6 +55,7 @@ For each category's full description, examples, sub-bucket decomposition, and co
|
|
|
55
55
|
| N | Test-name scenario verifier | `../audit-rubrics/category_rubrics/category-n-test-name-scenario-verifier.md` |
|
|
56
56
|
| O | Docstring / fixture-prose vs implementation drift | `../audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md` |
|
|
57
57
|
| P | Name / regex / word-list vs behavior-contract precision | `../audit-rubrics/category_rubrics/category-p-name-vs-behavior-contract.md` |
|
|
58
|
+
| Q | Cross-surface claim consistency (terminology, PR-description claims, message-vs-guard) | `../audit-rubrics/category_rubrics/category-q-cross-surface-claims.md` |
|
|
58
59
|
|
|
59
60
|
Test files (`test_*.py`, `*_test.py`, `*.test.*`, `*.spec.*`, `conftest.py`, and any path under `/tests/`) are exempt from category J. The exempt path families documented in the J reference also opt out of the constants-location sub-item.
|
|
60
61
|
|
|
@@ -114,7 +115,7 @@ A bare verified-clean label is inadequate: every Shape B entry lists the files o
|
|
|
114
115
|
|
|
115
116
|
## Per-Category Expectation
|
|
116
117
|
|
|
117
|
-
Every category A through
|
|
118
|
+
Every category A through Q is investigated. The output for each category is one of:
|
|
118
119
|
- one or more Shape A findings, or
|
|
119
120
|
- one Shape B proof-of-absence entry with concrete files, quoted lines, and adversarial probes.
|
|
120
121
|
|
package/agents/deep-research.md
CHANGED
|
@@ -1,25 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: deep-research
|
|
3
|
-
description: Use this agent for iterative, multi-source deep research that produces comprehensive Obsidian reports with full citations. Official-docs-first methodology with anti-hallucination constraints. Examples:
|
|
4
|
-
|
|
5
|
-
<example>
|
|
6
|
-
Context: User wants thorough research on a technical topic
|
|
7
|
-
user: "Research the current state of WebSocket authentication best practices"
|
|
8
|
-
assistant: "I'll use the deep-research agent to conduct iterative multi-source research and produce a cited report."
|
|
9
|
-
<commentary>
|
|
10
|
-
Multi-source research requiring iteration and synthesis — exactly what deep-research handles.
|
|
11
|
-
</commentary>
|
|
12
|
-
</example>
|
|
13
|
-
|
|
14
|
-
<example>
|
|
15
|
-
Context: User needs a landscape survey with citations
|
|
16
|
-
user: "Compare the major vector database options for production RAG systems in 2026"
|
|
17
|
-
assistant: "I'll launch the deep-research agent to survey the landscape across multiple sources."
|
|
18
|
-
<commentary>
|
|
19
|
-
Broad survey requiring many sources, comparison, and synthesis — deep-research with exhaustive depth.
|
|
20
|
-
</commentary>
|
|
21
|
-
</example>
|
|
22
|
-
|
|
3
|
+
description: "Use this agent for iterative, multi-source deep research that produces comprehensive Obsidian reports with full citations. Official-docs-first methodology with anti-hallucination constraints. Examples:\n\n <example>\n Context: User wants thorough research on a technical topic\n user: \"Research the current state of WebSocket authentication best practices\"\n assistant: \"I'll use the deep-research agent to conduct iterative multi-source research and produce a cited report.\"\n <commentary>\n Multi-source research requiring iteration and synthesis — exactly what deep-research handles.\n </commentary>\n </example>\n\n <example>\n Context: User needs a landscape survey with citations\n user: \"Compare the major vector database options for production RAG systems in 2026\"\n assistant: \"I'll launch the deep-research agent to survey the landscape across multiple sources.\"\n <commentary>\n Broad survey requiring many sources, comparison, and synthesis — deep-research with exhaustive depth.\n </commentary>\n </example>"
|
|
23
4
|
color: cyan
|
|
24
5
|
---
|
|
25
6
|
|
|
@@ -31,10 +12,9 @@ You receive a `<research_brief>` from the orchestrating skill. Your job is to ex
|
|
|
31
12
|
|
|
32
13
|
On receiving the research brief, write the state file:
|
|
33
14
|
|
|
34
|
-
`.deep-research-state.md
|
|
15
|
+
`.deep-research-state.md` opens with a YAML frontmatter block — a three-hyphen fence line, these fields, then a closing three-hyphen fence line:
|
|
35
16
|
|
|
36
|
-
```
|
|
37
|
-
---
|
|
17
|
+
```yaml
|
|
38
18
|
topic: "[from brief]"
|
|
39
19
|
brief: "[one-line summary from brief]"
|
|
40
20
|
iteration: 0
|
|
@@ -42,8 +22,11 @@ max_iterations: [from brief]
|
|
|
42
22
|
status: researching
|
|
43
23
|
source_count: 0
|
|
44
24
|
official_docs_found: false
|
|
45
|
-
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The rest of the file holds the tracking sections:
|
|
46
28
|
|
|
29
|
+
```markdown
|
|
47
30
|
## Sources Found
|
|
48
31
|
|
|
49
32
|
(none yet)
|
package/agents/docs-agent.md
CHANGED
|
@@ -1,32 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: docs-agent
|
|
3
|
-
description: Use this agent for all documentation tasks — managing/consolidating project docs, analyzing existing documentation to prevent code duplication, and writing user-facing guides for non-technical audiences. Trigger with requests like 'update our docs', 'check if we already have this documented', 'write a user guide', or 'consolidate documentation'
|
|
4
|
-
|
|
5
|
-
Examples:
|
|
6
|
-
- <example>
|
|
7
|
-
Context: User wants comprehensive documentation management
|
|
8
|
-
user: "call the doc manager"
|
|
9
|
-
assistant: "I'll use the docs-agent to analyze and update the documentation"
|
|
10
|
-
<commentary>
|
|
11
|
-
Documentation management request — use docs-agent in orchestration mode.
|
|
12
|
-
</commentary>
|
|
13
|
-
</example>
|
|
14
|
-
- <example>
|
|
15
|
-
Context: User is implementing a new feature and wants to avoid duplicating existing functionality
|
|
16
|
-
user: "I need to add a function that validates user input"
|
|
17
|
-
assistant: "Let me consult the docs-agent to check if we already have validation methods documented"
|
|
18
|
-
<commentary>
|
|
19
|
-
Before implementing new functionality, use docs-agent in analysis mode to check for existing documented methods.
|
|
20
|
-
</commentary>
|
|
21
|
-
</example>
|
|
22
|
-
- <example>
|
|
23
|
-
Context: User needs documentation for non-technical staff
|
|
24
|
-
user: "Write documentation for the new export feature so our office staff can use it"
|
|
25
|
-
assistant: "I'll use the docs-agent to create clear, step-by-step documentation that anyone can follow"
|
|
26
|
-
<commentary>
|
|
27
|
-
Non-technical audience — use docs-agent in user-docs writing mode.
|
|
28
|
-
</commentary>
|
|
29
|
-
</example>
|
|
3
|
+
description: "Use this agent for all documentation tasks — managing/consolidating project docs, analyzing existing documentation to prevent code duplication, and writing user-facing guides for non-technical audiences. Trigger with requests like 'update our docs', 'check if we already have this documented', 'write a user guide', or 'consolidate documentation'.\n\nExamples:\n- <example>\n Context: User wants comprehensive documentation management\n user: \"call the doc manager\"\n assistant: \"I'll use the docs-agent to analyze and update the documentation\"\n <commentary>\n Documentation management request — use docs-agent in orchestration mode.\n </commentary>\n</example>\n- <example>\n Context: User is implementing a new feature and wants to avoid duplicating existing functionality\n user: \"I need to add a function that validates user input\"\n assistant: \"Let me consult the docs-agent to check if we already have validation methods documented\"\n <commentary>\n Before implementing new functionality, use docs-agent in analysis mode to check for existing documented methods.\n </commentary>\n</example>\n- <example>\n Context: User needs documentation for non-technical staff\n user: \"Write documentation for the new export feature so our office staff can use it\"\n assistant: \"I'll use the docs-agent to create clear, step-by-step documentation that anyone can follow\"\n <commentary>\n Non-technical audience — use docs-agent in user-docs writing mode.\n </commentary>\n</example>"
|
|
30
4
|
color: cyan
|
|
31
5
|
---
|
|
32
6
|
|
package/agents/issue-tracker.md
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
---
|
|
2
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.
|
|
3
|
+
description: "Primary handler for one GitHub issue action per spawn on a work-stream: open an epic, file a sub-issue, update status in place, refresh the epic checklist, or close a sub-issue. Spawn with one action plus the issue-candidate or issue number it needs; loads the issue-tracker skill; returns affected issue numbers and URLs. Prefer the same warm agent for follow-ups on the same issue or the same epic work-stream."
|
|
10
4
|
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
5
|
color: green
|
|
12
6
|
---
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: skill-writer-agent
|
|
3
|
-
description:
|
|
4
|
-
Authors a skill package — SKILL.md plus companion files — to skill-builder conventions: hub layout, trigger-catalog description, deterministic work shipped as scripts and task-seed lists. Triggers: author a skill, write a SKILL.md, skill-writer-agent, delegate skill file authoring, produce skill package files.
|
|
3
|
+
description: "Authors a skill package — SKILL.md plus companion files — to skill-builder conventions: hub layout, trigger-catalog description, deterministic work shipped as scripts and task-seed lists. Triggers: author a skill, write a SKILL.md, skill-writer-agent, delegate skill file authoring, produce skill package files."
|
|
5
4
|
tools: Read, Write, Edit, Grep, Glob
|
|
6
5
|
color: green
|
|
7
6
|
---
|
|
@@ -16,6 +16,53 @@ model on every spawn, so no agent definition names one, concrete or
|
|
|
16
16
|
flag: model: inherit <- caller can no longer choose the model
|
|
17
17
|
flag: model: opus <- pinned concrete model, caller can't override
|
|
18
18
|
|
|
19
|
+
Every block must also load through `yaml.safe_load`. An unquoted colon inside
|
|
20
|
+
a plain scalar reads as a mapping key and makes the whole block unloadable::
|
|
21
|
+
|
|
22
|
+
ok: description: ... constraints. Examples are below.
|
|
23
|
+
flag: description: ... constraints. Examples: <- block no longer loads
|
|
24
|
+
|
|
25
|
+
Two parsers read these files, and a block that satisfies one can still be
|
|
26
|
+
unreadable by the other. `scripts/codex_compat_materializer.py` reads the
|
|
27
|
+
block line by line, so every field fits on the line that names it::
|
|
28
|
+
|
|
29
|
+
ok: description: "Use this agent ... Examples:\\n\\n <example> ..."
|
|
30
|
+
flag: description: | <- the line scan cannot follow the block scalar
|
|
31
|
+
|
|
32
|
+
That reader also counts fence lines across the whole file, so a bare `---`
|
|
33
|
+
anywhere in the body reads as a second frontmatter fence::
|
|
34
|
+
|
|
35
|
+
ok: ```yaml ... ``` example blocks in the body
|
|
36
|
+
flag: a `---` line inside a body example <- counted as a fence
|
|
37
|
+
|
|
38
|
+
That reader also cannot express an empty `tools: []` list, which is a defect
|
|
39
|
+
in the reader rather than in the one definition that declares it, so that
|
|
40
|
+
definition is named in `_codex_materializable_paths` and covered by every
|
|
41
|
+
other check here.
|
|
42
|
+
|
|
43
|
+
Each definition also has to carry a `name` and a `description` bound to a
|
|
44
|
+
non-empty string, and a `name` equal to its file stem — a mapping that loads
|
|
45
|
+
but binds `description` to nothing, or names an agent the file does not,
|
|
46
|
+
registers a subagent the caller cannot spawn::
|
|
47
|
+
|
|
48
|
+
ok: docs-agent.md -> name: docs-agent
|
|
49
|
+
flag: docs-agent.md -> name: doc-manager <- wrong spawn id
|
|
50
|
+
flag: description: <- loads as None, loader needs text
|
|
51
|
+
|
|
52
|
+
Every check above is parametrized over the definitions that yield a
|
|
53
|
+
frontmatter block, so a file that yields none would drop out of all of them
|
|
54
|
+
and leave the suite green while unreadable. The block is what the fence lines
|
|
55
|
+
delimit, so the file that opens no fence or never closes one is exactly the
|
|
56
|
+
broken file these checks exist to catch::
|
|
57
|
+
|
|
58
|
+
ok: docs-agent.md -> --- name/description --- <- block found
|
|
59
|
+
flag: docs-agent.md -> --- name/description <- no closing fence,
|
|
60
|
+
silently uncovered
|
|
61
|
+
|
|
62
|
+
`test_every_agent_definition_yields_a_frontmatter_block` holds that floor: it
|
|
63
|
+
walks the same directory the parametrized checks draw from and fails on any
|
|
64
|
+
definition missing from them.
|
|
65
|
+
|
|
19
66
|
Frontmatter parsing is self-contained here (stdlib only): the block is the
|
|
20
67
|
text between the file's opening and closing `---` fence lines, and top-level
|
|
21
68
|
keys are read with a line scan so an agent whose `description` embeds
|
|
@@ -24,18 +71,27 @@ informal `<example>` prose is not mistaken for one carrying extra keys.
|
|
|
24
71
|
|
|
25
72
|
from __future__ import annotations
|
|
26
73
|
|
|
74
|
+
import importlib.util
|
|
27
75
|
import re
|
|
76
|
+
import sys
|
|
28
77
|
from functools import cache
|
|
29
78
|
from pathlib import Path
|
|
79
|
+
from types import ModuleType
|
|
30
80
|
|
|
31
81
|
import pytest
|
|
32
82
|
import yaml
|
|
33
83
|
|
|
34
84
|
ACCEPTED_FRONTMATTER_KEYS = frozenset({"name", "description", "tools", "color"})
|
|
35
|
-
|
|
85
|
+
EMPTY_TOOLS_LIST_FILENAME = "code-advisor.md"
|
|
36
86
|
EXEMPT_MARKDOWN_FILENAME = "CLAUDE.md"
|
|
37
87
|
FRONTMATTER_FENCE_LINE = "---"
|
|
88
|
+
MATERIALIZER_MODULE_NAME = "codex_compat_materializer"
|
|
89
|
+
MATERIALIZER_MODULE_PATH = (
|
|
90
|
+
Path(__file__).resolve().parent.parent / "scripts" / f"{MATERIALIZER_MODULE_NAME}.py"
|
|
91
|
+
)
|
|
38
92
|
MODEL_KEY_NAME = "model"
|
|
93
|
+
RENAMED_AGENT_NAME = "an-agent-name-no-file-carries"
|
|
94
|
+
REQUIRED_FRONTMATTER_FIELDS = ("name", "description")
|
|
39
95
|
TOP_LEVEL_KEY_PATTERN = re.compile(r"^([a-z][a-z0-9_]*):", re.MULTILINE)
|
|
40
96
|
|
|
41
97
|
|
|
@@ -60,14 +116,30 @@ def _extract_frontmatter_block(markdown_text: str) -> str | None:
|
|
|
60
116
|
|
|
61
117
|
|
|
62
118
|
@cache
|
|
63
|
-
def
|
|
119
|
+
def _agent_definition_candidate_paths() -> tuple[Path, ...]:
|
|
120
|
+
"""Return every markdown file in this directory that must be a definition.
|
|
121
|
+
|
|
122
|
+
This is the floor the parametrized checks are measured against: each of
|
|
123
|
+
these files is expected to yield a frontmatter block, so one that does not
|
|
124
|
+
is a broken definition rather than a file to pass over.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Every `*.md` path in this directory except the exempt one, sorted.
|
|
128
|
+
"""
|
|
64
129
|
agents_directory = Path(__file__).parent
|
|
65
|
-
all_markdown_files = sorted(agents_directory.glob("*.md"))
|
|
66
130
|
return tuple(
|
|
67
131
|
each_markdown_file
|
|
68
|
-
for each_markdown_file in
|
|
132
|
+
for each_markdown_file in sorted(agents_directory.glob("*.md"))
|
|
69
133
|
if each_markdown_file.name != EXEMPT_MARKDOWN_FILENAME
|
|
70
|
-
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@cache
|
|
138
|
+
def _agent_definition_paths() -> tuple[Path, ...]:
|
|
139
|
+
return tuple(
|
|
140
|
+
each_markdown_file
|
|
141
|
+
for each_markdown_file in _agent_definition_candidate_paths()
|
|
142
|
+
if _extract_frontmatter_block(each_markdown_file.read_text(encoding="utf-8"))
|
|
71
143
|
is not None
|
|
72
144
|
)
|
|
73
145
|
|
|
@@ -84,6 +156,124 @@ def _top_level_keys(frontmatter_block: str) -> set[str]:
|
|
|
84
156
|
return set(TOP_LEVEL_KEY_PATTERN.findall(frontmatter_block))
|
|
85
157
|
|
|
86
158
|
|
|
159
|
+
@cache
|
|
160
|
+
def _codex_materializable_paths() -> tuple[Path, ...]:
|
|
161
|
+
"""Return the definitions the Codex materializer is expected to read.
|
|
162
|
+
|
|
163
|
+
`code-advisor.md` declares `tools: []`, and its body states the agent has
|
|
164
|
+
zero tools, so the empty list is the field saying what the author meant.
|
|
165
|
+
The materializer's list parser raises on an empty list, so it cannot
|
|
166
|
+
express a correct declaration::
|
|
167
|
+
|
|
168
|
+
tools: Read, Bash -> ok: parsed
|
|
169
|
+
tools: [] -> flag: MaterializerError, though the file is right
|
|
170
|
+
|
|
171
|
+
The gap belongs to that parser, so the definition stays as written and only
|
|
172
|
+
this one check steps around it. The YAML-load, required-field, accepted-key,
|
|
173
|
+
model-ban, and name checks all still cover the file.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Every agent definition path except the one whose correct frontmatter
|
|
177
|
+
the materializer's list parser cannot express.
|
|
178
|
+
"""
|
|
179
|
+
return tuple(
|
|
180
|
+
each_path
|
|
181
|
+
for each_path in _agent_definition_paths()
|
|
182
|
+
if each_path.name != EMPTY_TOOLS_LIST_FILENAME
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@cache
|
|
187
|
+
def _codex_materializer_module() -> ModuleType:
|
|
188
|
+
"""Load the package's own line-oriented frontmatter parser from disk.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
The imported `codex_compat_materializer` module.
|
|
192
|
+
"""
|
|
193
|
+
module_specification = importlib.util.spec_from_file_location(
|
|
194
|
+
MATERIALIZER_MODULE_NAME, MATERIALIZER_MODULE_PATH
|
|
195
|
+
)
|
|
196
|
+
assert module_specification is not None, (
|
|
197
|
+
f"no import specification for {MATERIALIZER_MODULE_PATH}"
|
|
198
|
+
)
|
|
199
|
+
assert module_specification.loader is not None, (
|
|
200
|
+
f"no import loader for {MATERIALIZER_MODULE_PATH}"
|
|
201
|
+
)
|
|
202
|
+
materializer_module = importlib.util.module_from_spec(module_specification)
|
|
203
|
+
sys.modules.setdefault(MATERIALIZER_MODULE_NAME, materializer_module)
|
|
204
|
+
module_specification.loader.exec_module(materializer_module)
|
|
205
|
+
return materializer_module
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _required_field_problem(parsed_frontmatter: object) -> str | None:
|
|
209
|
+
"""Describe what stops the loader using this frontmatter, or None.
|
|
210
|
+
|
|
211
|
+
The loader needs text for every required field, so a field that loads as
|
|
212
|
+
nothing or as a nested mapping is a problem even though the block itself
|
|
213
|
+
is valid YAML::
|
|
214
|
+
|
|
215
|
+
description: Use this agent ... -> ok: None
|
|
216
|
+
description: -> flag: bound to NoneType
|
|
217
|
+
description: -> flag: bound to dict
|
|
218
|
+
nested: value
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
parsed_frontmatter: Value `yaml.safe_load` produced for the block.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
A sentence naming the problem, or None when every required field is a
|
|
225
|
+
non-empty string.
|
|
226
|
+
"""
|
|
227
|
+
if not isinstance(parsed_frontmatter, dict):
|
|
228
|
+
return (
|
|
229
|
+
f"frontmatter loads as {type(parsed_frontmatter).__name__} rather "
|
|
230
|
+
"than a key/value mapping"
|
|
231
|
+
)
|
|
232
|
+
for each_field in REQUIRED_FRONTMATTER_FIELDS:
|
|
233
|
+
if each_field not in parsed_frontmatter:
|
|
234
|
+
return f"frontmatter carries no {each_field} field"
|
|
235
|
+
field_value = parsed_frontmatter[each_field]
|
|
236
|
+
if not isinstance(field_value, str):
|
|
237
|
+
return (
|
|
238
|
+
f"frontmatter binds {each_field} to "
|
|
239
|
+
f"{type(field_value).__name__} rather than a string"
|
|
240
|
+
)
|
|
241
|
+
if not field_value.strip():
|
|
242
|
+
return f"frontmatter binds {each_field} to an empty string"
|
|
243
|
+
return None
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _agent_name_problem(parsed_frontmatter: object, expected_name: str) -> str | None:
|
|
247
|
+
"""Describe a name that disagrees with the file stem, or None.
|
|
248
|
+
|
|
249
|
+
A subagent registers under the name in its frontmatter, so a name that is
|
|
250
|
+
not the file stem is spawned by an id no caller uses::
|
|
251
|
+
|
|
252
|
+
docs-agent.md -> name: docs-agent -> ok: None
|
|
253
|
+
docs-agent.md -> name: doc-manager -> flag: wrong spawn id
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
parsed_frontmatter: Value `yaml.safe_load` produced for the block.
|
|
257
|
+
expected_name: The definition file's stem.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
A sentence naming the problem, or None when the name matches the stem.
|
|
261
|
+
"""
|
|
262
|
+
if not isinstance(parsed_frontmatter, dict):
|
|
263
|
+
return (
|
|
264
|
+
f"frontmatter loads as {type(parsed_frontmatter).__name__} rather "
|
|
265
|
+
"than a key/value mapping"
|
|
266
|
+
)
|
|
267
|
+
declared_name = parsed_frontmatter.get("name")
|
|
268
|
+
if declared_name == expected_name:
|
|
269
|
+
return None
|
|
270
|
+
return (
|
|
271
|
+
f"frontmatter declares name {declared_name!r} rather than "
|
|
272
|
+
f"{expected_name!r}, so the agent registers under a spawn id that does "
|
|
273
|
+
"not match its file"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
|
|
87
277
|
@pytest.mark.parametrize(
|
|
88
278
|
"agent_definition_path",
|
|
89
279
|
_agent_definition_paths(),
|
|
@@ -100,14 +290,121 @@ def test_agent_frontmatter_uses_only_accepted_keys(
|
|
|
100
290
|
)
|
|
101
291
|
|
|
102
292
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
293
|
+
@pytest.mark.parametrize(
|
|
294
|
+
"agent_definition_path",
|
|
295
|
+
_agent_definition_paths(),
|
|
296
|
+
ids=lambda each_path: each_path.name,
|
|
297
|
+
)
|
|
298
|
+
def test_agent_frontmatter_loads_as_a_yaml_mapping(
|
|
299
|
+
agent_definition_path: Path,
|
|
300
|
+
) -> None:
|
|
301
|
+
frontmatter_block = _frontmatter_block(agent_definition_path)
|
|
302
|
+
try:
|
|
303
|
+
parsed_frontmatter = yaml.safe_load(frontmatter_block)
|
|
304
|
+
except yaml.YAMLError as yaml_error:
|
|
305
|
+
pytest.fail(
|
|
306
|
+
f"{agent_definition_path.name} frontmatter is not loadable YAML, so "
|
|
307
|
+
f"the subagent loader cannot read the definition: {yaml_error}"
|
|
308
|
+
)
|
|
309
|
+
assert isinstance(parsed_frontmatter, dict), (
|
|
310
|
+
f"{agent_definition_path.name} frontmatter loads as "
|
|
311
|
+
f"{type(parsed_frontmatter).__name__} rather than a key/value mapping"
|
|
312
|
+
)
|
|
313
|
+
unaccepted_keys = set(parsed_frontmatter) - ACCEPTED_FRONTMATTER_KEYS
|
|
314
|
+
assert not unaccepted_keys, (
|
|
315
|
+
f"{agent_definition_path.name} loads frontmatter keys the subagent "
|
|
316
|
+
f"loader does not accept: {sorted(unaccepted_keys)}"
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def test_every_agent_definition_yields_a_frontmatter_block() -> None:
|
|
321
|
+
covered_names = {each_path.name for each_path in _agent_definition_paths()}
|
|
322
|
+
uncovered_names = sorted(
|
|
323
|
+
each_path.name
|
|
324
|
+
for each_path in _agent_definition_candidate_paths()
|
|
325
|
+
if each_path.name not in covered_names
|
|
326
|
+
)
|
|
327
|
+
assert not uncovered_names, (
|
|
328
|
+
f"{', '.join(uncovered_names)} yield no frontmatter block, so every "
|
|
329
|
+
"parametrized check in this module passes over them and the suite "
|
|
330
|
+
"stays green while the definitions are unreadable"
|
|
107
331
|
)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
@pytest.mark.parametrize(
|
|
335
|
+
"agent_definition_path",
|
|
336
|
+
_codex_materializable_paths(),
|
|
337
|
+
ids=lambda each_path: each_path.name,
|
|
338
|
+
)
|
|
339
|
+
def test_agent_frontmatter_parses_with_the_codex_materializer(
|
|
340
|
+
agent_definition_path: Path,
|
|
341
|
+
) -> None:
|
|
342
|
+
materializer_module = _codex_materializer_module()
|
|
343
|
+
try:
|
|
344
|
+
materializer_module.parse_frontmatter(
|
|
345
|
+
agent_definition_path,
|
|
346
|
+
agent_definition_path.read_text(encoding="utf-8"),
|
|
347
|
+
agent_definition_path.name,
|
|
348
|
+
)
|
|
349
|
+
except materializer_module.MaterializerError as materializer_error:
|
|
350
|
+
pytest.fail(
|
|
351
|
+
f"{agent_definition_path.name} frontmatter is unreadable by "
|
|
352
|
+
f"{MATERIALIZER_MODULE_NAME}, so the agent cannot be materialized "
|
|
353
|
+
f"for Codex: {materializer_error}"
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@pytest.mark.parametrize(
|
|
358
|
+
"agent_definition_path",
|
|
359
|
+
_agent_definition_paths(),
|
|
360
|
+
ids=lambda each_path: each_path.name,
|
|
361
|
+
)
|
|
362
|
+
def test_agent_frontmatter_binds_required_fields_to_non_empty_strings(
|
|
363
|
+
agent_definition_path: Path,
|
|
364
|
+
) -> None:
|
|
365
|
+
parsed_frontmatter = yaml.safe_load(_frontmatter_block(agent_definition_path))
|
|
366
|
+
field_problem = _required_field_problem(parsed_frontmatter)
|
|
367
|
+
assert field_problem is None, f"{agent_definition_path.name} {field_problem}"
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def test_required_field_check_rejects_a_description_bound_to_nothing() -> None:
|
|
371
|
+
parsed_frontmatter = yaml.safe_load("name: docs-agent\ndescription:\n")
|
|
372
|
+
assert _required_field_problem(parsed_frontmatter) == (
|
|
373
|
+
"frontmatter binds description to NoneType rather than a string"
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def test_required_field_check_rejects_a_description_bound_to_a_mapping() -> None:
|
|
378
|
+
parsed_frontmatter = yaml.safe_load(
|
|
379
|
+
"name: docs-agent\ndescription:\n nested: value\n"
|
|
380
|
+
)
|
|
381
|
+
assert _required_field_problem(parsed_frontmatter) == (
|
|
382
|
+
"frontmatter binds description to dict rather than a string"
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
@pytest.mark.parametrize(
|
|
387
|
+
"agent_definition_path",
|
|
388
|
+
_agent_definition_paths(),
|
|
389
|
+
ids=lambda each_path: each_path.name,
|
|
390
|
+
)
|
|
391
|
+
def test_agent_frontmatter_names_the_agent_after_its_file(
|
|
392
|
+
agent_definition_path: Path,
|
|
393
|
+
) -> None:
|
|
394
|
+
parsed_frontmatter = yaml.safe_load(_frontmatter_block(agent_definition_path))
|
|
395
|
+
name_problem = _agent_name_problem(parsed_frontmatter, agent_definition_path.stem)
|
|
396
|
+
assert name_problem is None, f"{agent_definition_path.name} {name_problem}"
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def test_name_check_rejects_an_agent_renamed_away_from_its_file_stem() -> None:
|
|
400
|
+
agent_definition_path = _agent_definition_paths()[0]
|
|
401
|
+
renamed_block = _frontmatter_block(agent_definition_path).replace(
|
|
402
|
+
f"name: {agent_definition_path.stem}", f"name: {RENAMED_AGENT_NAME}", 1
|
|
403
|
+
)
|
|
404
|
+
name_problem = _agent_name_problem(
|
|
405
|
+
yaml.safe_load(renamed_block), agent_definition_path.stem
|
|
406
|
+
)
|
|
407
|
+
assert name_problem is not None and RENAMED_AGENT_NAME in name_problem
|
|
111
408
|
|
|
112
409
|
|
|
113
410
|
@pytest.mark.parametrize(
|
|
@@ -125,3 +422,29 @@ def test_agent_frontmatter_carries_no_model_key(
|
|
|
125
422
|
"the caller supplies the model on every spawn, so agent definitions "
|
|
126
423
|
"carry no model key at all, not even model: inherit"
|
|
127
424
|
)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _clean_coder_body() -> str:
|
|
428
|
+
return (Path(__file__).parent / "clean-coder.md").read_text(encoding="utf-8")
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def test_clean_coder_never_globs_or_reads_dotenv_files() -> None:
|
|
432
|
+
body = _clean_coder_body()
|
|
433
|
+
assert "`**/.env`" not in body
|
|
434
|
+
assert "`**/.env.*`" not in body
|
|
435
|
+
assert "Never open `.env`" in body or "Do **not** glob or open `.env`" in body
|
|
436
|
+
assert re.search(r"(?i)glob.*\.env|\.env.*glob", body) is None or "Do **not** glob or open `.env`" in body
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def test_clean_coder_uses_task_local_config_discovery() -> None:
|
|
440
|
+
body = _clean_coder_body()
|
|
441
|
+
assert "task-local" in body.lower()
|
|
442
|
+
assert "project-wide preload" in body.lower() or "Do **not** glob the whole tree" in body
|
|
443
|
+
assert "Issue all five Glob calls" not in body
|
|
444
|
+
assert "Issue all seven Glob calls" not in body
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def test_clean_coder_examples_import_constants_from_config() -> None:
|
|
448
|
+
body = _clean_coder_body()
|
|
449
|
+
assert "from config.timing import MAXIMUM_RETRIES" in body
|
|
450
|
+
assert re.search(r"(?m)^MAXIMUM_RETRIES\s*=\s*\d+", body) is None
|
package/docs/CODE_RULES.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Code Rules Reference
|
|
2
2
|
|
|
3
|
-
Compact
|
|
3
|
+
Compact **projection** of the repository-root canonical policy [`AGENTS.md`](../../../AGENTS.md). When this file and `AGENTS.md` disagree, **`AGENTS.md` wins** — update this projection in the same change.
|
|
4
|
+
|
|
5
|
+
⚡ marks rules enforced by hand-maintained `code_rules_enforcer.py` — the hook blocks the Write/Edit and returns the corrective detail at violation time, so this document lists those rules by name only. Session policy (question routing, task tracking) lives in `rules/*.md`; see [`code-standards.md`](../rules/code-standards.md).
|
|
4
6
|
|
|
5
7
|
---
|
|
6
8
|
|
|
@@ -46,7 +48,7 @@ Full words only (`context`, not `ctx`). Exceptions: `i`/`j`/`k` in loops, `e` fo
|
|
|
46
48
|
|
|
47
49
|
## 6. COMPLETE TYPE HINTS
|
|
48
50
|
|
|
49
|
-
ALL parameters typed, ALL returns typed. No `Any
|
|
51
|
+
ALL parameters typed, ALL returns typed. No `Any`. A `# type: ignore` is allowed only with a second trailing `#` justification of at least five characters (e.g. `# type: ignore[misc] # stubs missing in foo library`); bare ignores are blocked by `code_rules_enforcer.py`. Prefer fixing the type over an ignore when a real annotation is available.
|
|
50
52
|
|
|
51
53
|
## 6.5 FILE LENGTH GUIDANCE
|
|
52
54
|
|
|
@@ -8,8 +8,8 @@ Pointer documents to external sources, standard terminology, and internal tool o
|
|
|
8
8
|
|---|---|
|
|
9
9
|
| `dead-code-elimination.md` | External sources and standard terms behind CODE_RULES §9.8 (remove code you orphan): DCE, tree shaking, reachability analysis, and the Lava Flow anti-pattern |
|
|
10
10
|
| `code-review-enforcement.md` | How the code-review gates work: the two required efforts (push at low, PR creation at xhigh), the stamp bound to the branch-surface hash, the single sanctioned minter, the two-layer stamp-directory guard, and the bypass surfaces the gates leave open |
|
|
11
|
-
| `advisor-tool.md` |
|
|
12
|
-
| `team-advisor-skill.md` |
|
|
11
|
+
| `advisor-tool.md` | Canonical consult bones for any stronger reviewer: when to call, hard rule before first write, how to treat advice; maps to the Anthropic advisor tool |
|
|
12
|
+
| `team-advisor-skill.md` | `/team-advisor` map: sole-consumer warm bind, ref index, and how it pairs with `advisor()` |
|
|
13
13
|
|
|
14
14
|
## Role
|
|
15
15
|
|