claude-dev-env 2.28.1 → 2.29.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/.agents/agents/AGENTS.md +0 -31
- package/.agents/agents/clean-coder.md +1 -1
- package/.agents/agents/test_agent_frontmatter.py +3 -1
- package/.agents/skills/AGENTS.md +0 -70
- package/.agents/skills/_shared/AGENTS.md +0 -44
- package/.agents/skills/_shared/advisor/AGENTS.md +0 -8
- package/.agents/skills/_shared/pr-loop/AGENTS.md +0 -57
- package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -8
- package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -34
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -23
- package/.agents/skills/autoconverge/AGENTS.md +0 -35
- package/.agents/skills/autoconverge/reference/AGENTS.md +0 -15
- package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -25
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -15
- package/.agents/skills/everything-search/AGENTS.md +0 -16
- package/.agents/skills/fresh-branch/AGENTS.md +0 -13
- package/.agents/skills/grok-spawn/AGENTS.md +0 -27
- package/.agents/skills/orchestrator/.claude/CLAUDE.md +1 -0
- package/.agents/skills/orchestrator/AGENTS.md +1 -0
- package/.agents/skills/orchestrator/SKILL.md +44 -53
- package/.agents/skills/orchestrator/reference/.claude/CLAUDE.md +1 -0
- package/.agents/skills/orchestrator/reference/AGENTS.md +1 -0
- package/.agents/skills/orchestrator/reference/consult-the-orchestrator.md +70 -0
- package/.agents/skills/orchestrator/reference/executor-consult-block.md +62 -0
- package/.agents/skills/orchestrator/reference/host-detect.md +15 -0
- package/.agents/skills/orchestrator/test_orchestrator_skill_contract.py +62 -0
- package/.agents/skills/orchestrator-refresh/SKILL.md +16 -34
- package/.agents/skills/rebase/AGENTS.md +0 -31
- package/.agents/skills/session-log/AGENTS.md +0 -31
- package/.agents/skills/session-tidy/AGENTS.md +0 -35
- package/.agents/skills/skill-builder/AGENTS.md +0 -48
- package/.agents/skills/skill-builder/references/AGENTS.md +0 -24
- package/.agents/skills/skill-builder/templates/AGENTS.md +0 -13
- package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -18
- package/.agents/skills/task-build/AGENTS.md +0 -28
- package/.agents/skills/update/AGENTS.md +0 -37
- package/.agents/skills-archived/AGENTS.md +0 -44
- package/.agents/skills-archived/anthropic-plan/AGENTS.md +0 -33
- package/.agents/skills-archived/anthropic-plan/scripts/AGENTS.md +0 -10
- package/.agents/skills-archived/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +0 -15
- package/.agents/skills-archived/anthropic-plan/templates/AGENTS.md +0 -12
- package/.agents/skills-archived/anthropic-plan/workflow/AGENTS.md +0 -13
- package/.agents/skills-archived/auditing-claude-config/AGENTS.md +0 -20
- package/.agents/skills-archived/bugteam/AGENTS.md +0 -29
- package/.agents/skills-archived/bugteam/reference/AGENTS.md +0 -19
- package/.agents/skills-archived/bugteam/reference/obstacles/AGENTS.md +0 -23
- package/.agents/skills-archived/bugteam/scripts/AGENTS.md +0 -29
- package/.agents/skills-archived/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +0 -17
- package/.agents/skills-archived/codex-review/AGENTS.md +0 -45
- package/.agents/skills-archived/codex-review/reference/AGENTS.md +0 -14
- package/.agents/skills-archived/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +0 -17
- package/.agents/skills-archived/codex-review/test_skill_scaffold.py +13 -3
- package/.agents/skills-archived/copilot-review/AGENTS.md +0 -17
- package/.agents/skills-archived/pr-converge/AGENTS.md +0 -31
- package/.agents/skills-archived/pr-converge/pr_converge_skill_constants/AGENTS.md +0 -25
- package/.agents/skills-archived/pr-converge/reference/AGENTS.md +0 -27
- package/.agents/skills-archived/pr-converge/reference/obstacles/AGENTS.md +0 -22
- package/.agents/skills-archived/pr-converge/scripts/AGENTS.md +0 -45
- package/.agents/skills-archived/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +0 -17
- package/.agents/skills-archived/pr-converge/workflows/AGENTS.md +0 -15
- package/.agents/skills-archived/recall/AGENTS.md +0 -29
- package/.agents/skills-archived/remember/AGENTS.md +0 -30
- package/AGENTS.md +0 -112
- package/_shared/AGENTS.md +0 -16
- package/_shared/advisor/AGENTS.md +0 -21
- package/_shared/advisor/advisor-protocol.md +4 -1
- package/_shared/advisor/reference/consult-format.md +4 -2
- package/_shared/pr-loop/AGENTS.md +0 -27
- package/_shared/pr-loop/scripts/AGENTS.md +0 -49
- package/_shared/pr-loop/scripts/code_rules_gate_parts/AGENTS.md +0 -41
- package/_shared/pr-loop/scripts/codex_review_scripts_constants/AGENTS.md +0 -17
- package/_shared/pr-loop/scripts/pr_converge_scripts_constants/AGENTS.md +0 -17
- package/_shared/pr-loop/scripts/pr_converge_skill_constants/AGENTS.md +0 -25
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/AGENTS.md +0 -25
- package/_shared/pr-loop/scripts/tests/AGENTS.md +0 -43
- package/_shared/process-tree/AGENTS.md +0 -40
- package/audit-rubrics/AGENTS.md +0 -42
- package/audit-rubrics/category_rubrics/AGENTS.md +0 -36
- package/audit-rubrics/prompts/AGENTS.md +0 -36
- package/bin/AGENTS.md +0 -118
- package/bin/install.test.mjs +1 -1
- package/commands/AGENTS.md +0 -14
- package/docs/AGENTS.md +0 -31
- package/docs/references/AGENTS.md +0 -16
- package/hooks/AGENTS.md +0 -27
- package/hooks/advisory/AGENTS.md +0 -15
- package/hooks/blocking/AGENTS.md +0 -112
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/AGENTS.md +0 -27
- package/hooks/blocking/config/AGENTS.md +0 -9
- package/hooks/blocking/inventory_intent_records/AGENTS.md +0 -25
- package/hooks/blocking/package_inventory_stale_blocker_parts/AGENTS.md +0 -25
- package/hooks/blocking/pii_prevention_blocker_parts/AGENTS.md +0 -23
- package/hooks/blocking/tdd_enforcer_parts/AGENTS.md +0 -29
- package/hooks/git-hooks/AGENTS.md +0 -31
- package/hooks/git-hooks/git_hooks_constants/AGENTS.md +0 -20
- package/hooks/hooks_constants/AGENTS.md +0 -99
- package/hooks/lifecycle/AGENTS.md +0 -17
- package/hooks/observability/AGENTS.md +0 -19
- package/hooks/session/AGENTS.md +0 -37
- package/hooks/validation/AGENTS.md +0 -19
- package/hooks/validators/AGENTS.md +0 -51
- package/hooks/workflow/AGENTS.md +0 -15
- package/output-styles/AGENTS.md +0 -14
- package/package.json +1 -1
- package/rules/AGENTS.md +0 -55
- package/scripts/AGENTS.md +0 -55
- package/scripts/dev_env_scripts_constants/AGENTS.md +0 -20
- package/scripts/sync_to_cursor/AGENTS.md +0 -22
- package/scripts/tests/AGENTS.md +0 -34
- package/system-prompts/AGENTS.md +0 -24
|
@@ -1,44 +1 @@
|
|
|
1
|
-
# _shared/pr-loop/scripts/tests
|
|
2
1
|
|
|
3
|
-
pytest suite for the scripts and constants in `_shared/pr-loop/scripts/`. Each test file covers one script or one constants module.
|
|
4
|
-
|
|
5
|
-
## Test files
|
|
6
|
-
|
|
7
|
-
| File | Covers |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `test__claude_permissions_common.py` | Internal helpers in `_claude_permissions_common.py` (legacy underscore prefix) |
|
|
10
|
-
| `test_claude_permissions_common.py` | Public API of `_claude_permissions_common.py` |
|
|
11
|
-
| `test_claude_permissions_constants.py` | `pr_loop_shared_constants/claude_permissions_constants.py` |
|
|
12
|
-
| `test_claude_settings_keys_constants.py` | `pr_loop_shared_constants/claude_settings_keys_constants.py` |
|
|
13
|
-
| `test_code_rules_gate.py` | `code_rules_gate.py` gate logic |
|
|
14
|
-
| `test_terminology_sweep.py` | `terminology_sweep.py` near-miss detection and exit codes |
|
|
15
|
-
| `test_code_rules_gate_constants.py` | `pr_loop_shared_constants/code_rules_gate_constants.py` |
|
|
16
|
-
| `test_fix_hookspath.py` | `fix_hookspath.py` repair logic |
|
|
17
|
-
| `test_fix_hookspath_constants.py` | `pr_loop_shared_constants/fix_hookspath_constants.py` |
|
|
18
|
-
| `test_grant_project_claude_permissions.py` | `grant_project_claude_permissions.py` end-to-end |
|
|
19
|
-
| `test_post_audit_thread.py` | `post_audit_thread.py` review-posting flow |
|
|
20
|
-
| `test_post_audit_thread_constants.py` | `pr_loop_shared_constants/post_audit_thread_constants.py` |
|
|
21
|
-
| `test_preflight.py` | `preflight.py` pre-flight checks |
|
|
22
|
-
| `test_preflight_constants.py` | `pr_loop_shared_constants/preflight_constants.py` |
|
|
23
|
-
| `test_preflight_self_heal.py` | `preflight_self_heal.py` hooks-path repair |
|
|
24
|
-
| `test_reviews_disabled.py` | `reviews_disabled.py` opt-out gate parsing |
|
|
25
|
-
| `test_copilot_quota.py` | `copilot_quota.py` end-to-end: account resolution, premium-quota classification, exit codes, and skip logging |
|
|
26
|
-
| `test_copilot_quota_constants.py` | `pr_loop_shared_constants/copilot_quota_constants.py` |
|
|
27
|
-
| `test_reviewer_availability.py` | `reviewer_availability.py` end-to-end: Copilot and Bugbot availability, opt-out via `CLAUDE_REVIEWS_DISABLED`, and every Copilot quota outcome |
|
|
28
|
-
| `test_reviewer_availability_constants.py` | `pr_loop_shared_constants/reviewer_availability_constants.py` |
|
|
29
|
-
| `test_revoke_project_claude_permissions.py` | `revoke_project_claude_permissions.py` end-to-end |
|
|
30
|
-
| `test_stale_worktree_rule_sweep.py` | `stale_worktree_rule_sweep.py` sweep and deduplication end-to-end |
|
|
31
|
-
| `test_stale_worktree_rule_sweep_constants.py` | `pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py` worktrees-root resolution and rule-format readers |
|
|
32
|
-
| `test_agent_config_carveout.py` | Agent-config deny-rule carve-out logic |
|
|
33
|
-
| `conftest.py` | Shared pytest fixtures |
|
|
34
|
-
|
|
35
|
-
## Fixtures
|
|
36
|
-
|
|
37
|
-
`fixtures/copilot_internal_user_example.json` — a captured `gh api
|
|
38
|
-
copilot_internal/user` response driving `test_copilot_quota.py`.
|
|
39
|
-
|
|
40
|
-
## Running
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
python -m pytest packages/claude-dev-env/_shared/pr-loop/scripts/tests/
|
|
44
|
-
```
|
|
@@ -1,41 +1 @@
|
|
|
1
|
-
# _shared/process-tree
|
|
2
1
|
|
|
3
|
-
One home for ending a spawned process together with every descendant it
|
|
4
|
-
started. Callers that capture a CLI's output import it so a grandchild can
|
|
5
|
-
never outlive the run and hold the capture pipe open.
|
|
6
|
-
|
|
7
|
-
## Consumers
|
|
8
|
-
|
|
9
|
-
| Caller | Use |
|
|
10
|
-
|---|---|
|
|
11
|
-
| `scripts/grok_headless_runner.py` | Kills a timed-out worker's tree between drain attempts |
|
|
12
|
-
| `_shared/pr-loop/scripts/run_codex_review.py` | Kills the review tree on timeout, then drains |
|
|
13
|
-
| `_shared/pr-loop/scripts/codex_usage_probe.py` | Tears the app-server tree down after the rate-limits exchange |
|
|
14
|
-
|
|
15
|
-
A PR-loop script reaches this home the way convergence scripts reach
|
|
16
|
-
`_shared/pr-loop/scripts`: it puts the directory on `sys.path` and imports by
|
|
17
|
-
module name. `bin/install.mjs` copies `_shared` and `scripts` together, and
|
|
18
|
-
every install group that carries a consumer carries `_shared`, so the import
|
|
19
|
-
resolves in the repository and under `~/.claude` alike.
|
|
20
|
-
|
|
21
|
-
## Key files
|
|
22
|
-
|
|
23
|
-
| File | Purpose |
|
|
24
|
-
|---|---|
|
|
25
|
-
| `scripts/process_tree_kill.py` | `terminate_process_tree` (poll, platform tree kill, re-poll, `Popen.kill()` fallback), `kill_process_tree_by_identifier`, and `should_start_new_session` for the matching `Popen` flag |
|
|
26
|
-
| `scripts/test_process_tree_kill.py` | Behavioral tests for every public entry point here, each platform branch, each failure the helper swallows, and the `Popen.kill()` fallback |
|
|
27
|
-
| `scripts/config/process_tree_scripts_constants/process_tree_kill_constants.py` | The taskkill command, its `/T`, `/F`, and `/PID` flags, and the bound on the kill command |
|
|
28
|
-
| `scripts/pyproject.toml` | mypy configuration; `check.ps1` runs it under the `mypy-process-tree` label |
|
|
29
|
-
|
|
30
|
-
## Platform guard
|
|
31
|
-
|
|
32
|
-
Both the branch selector and the POSIX helper compare `sys.platform` against
|
|
33
|
-
the literal `"win32"`. That literal is what mypy narrows on: behind a named
|
|
34
|
-
constant, `os.getpgid`, `os.killpg`, and `signal.SIGKILL` fail type checking on
|
|
35
|
-
Windows.
|
|
36
|
-
|
|
37
|
-
## Pairing rule
|
|
38
|
-
|
|
39
|
-
`os.killpg` signals a whole process group, so a child sharing the caller's
|
|
40
|
-
group takes the caller down with it. Every `Popen` whose tree this module ends
|
|
41
|
-
passes `start_new_session=should_start_new_session()`.
|
package/audit-rubrics/AGENTS.md
CHANGED
|
@@ -1,43 +1 @@
|
|
|
1
|
-
# audit-rubrics
|
|
2
1
|
|
|
3
|
-
Audit rubrics for the PR-loop code-review suite. The rubrics define the 17 bug categories (A–Q), their sub-bucket decompositions, and the prompt templates agents use during an audit pass. Installed into `~/.claude/audit-rubrics/` by `bin/install.mjs`.
|
|
4
|
-
|
|
5
|
-
## Key file
|
|
6
|
-
|
|
7
|
-
| File | Purpose |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `audit-categories.json` | Machine-readable A-Q schema: id, title, slug, and sub-bucket id/axis pairs; single source for rubric and prompt skeleton parity |
|
|
10
|
-
| `source-material-section-types.md` | Lookup table for how to chunk an artifact into sections for an audit prompt; covers code PRs, docs, SQL schemas, config files, and more |
|
|
11
|
-
|
|
12
|
-
## Subdirectories
|
|
13
|
-
|
|
14
|
-
| Entry | Description |
|
|
15
|
-
|---|---|
|
|
16
|
-
| `category_rubrics/` | One `.md` per category (A–Q): defines what the category audits, example findings, and a sub-bucket decomposition table |
|
|
17
|
-
| `prompts/` | One `.md` per category: the ready-to-use audit prompt template an agent inlines the artifact into |
|
|
18
|
-
|
|
19
|
-
## Categories
|
|
20
|
-
|
|
21
|
-
| ID | Name |
|
|
22
|
-
|---|---|
|
|
23
|
-
| A | API contract verification |
|
|
24
|
-
| B | Selector engine compatibility |
|
|
25
|
-
| C | Resource cleanup |
|
|
26
|
-
| D | Scoping and ordering |
|
|
27
|
-
| E | Dead code |
|
|
28
|
-
| F | Silent failures |
|
|
29
|
-
| G | Bounds and overflow |
|
|
30
|
-
| H | Security boundaries |
|
|
31
|
-
| I | Concurrency |
|
|
32
|
-
| J | Code-rules compliance |
|
|
33
|
-
| K | Codebase conflicts |
|
|
34
|
-
| L | Behavior equivalence |
|
|
35
|
-
| M | Producer-consumer cardinality |
|
|
36
|
-
| N | Test name / scenario verifier |
|
|
37
|
-
| O | Docstring vs implementation drift |
|
|
38
|
-
| P | Name vs behavior contract |
|
|
39
|
-
| Q | Cross-surface claim consistency |
|
|
40
|
-
|
|
41
|
-
## Breaking-change rule
|
|
42
|
-
|
|
43
|
-
Adding a sub-bucket to a category rubric requires updating `audit-categories.json` and the matching prompt template in `prompts/` in the same commit, then running `audit_category_schema.py --validate`. Skills that reference category IDs (`bugteam`, `findbugs`) rely on stable sub-bucket IDs (A1, A2, … Q-n). Worked examples stay in the rubric markdown only; they are outside the schema.
|
|
@@ -1,37 +1 @@
|
|
|
1
|
-
# audit-rubrics/category_rubrics
|
|
2
1
|
|
|
3
|
-
One rubric file per audit category (A–Q). Each file defines what the category covers, gives concrete examples of findings, and provides the sub-bucket decomposition an audit agent uses to structure its pass.
|
|
4
|
-
|
|
5
|
-
## Files
|
|
6
|
-
|
|
7
|
-
| File | Category |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `category-a-api-contracts.md` | A — API contract verification |
|
|
10
|
-
| `category-b-selector-engine-compat.md` | B — Selector engine compatibility |
|
|
11
|
-
| `category-c-resource-cleanup.md` | C — Resource cleanup |
|
|
12
|
-
| `category-d-scoping-and-ordering.md` | D — Scoping and ordering |
|
|
13
|
-
| `category-e-dead-code.md` | E — Dead code |
|
|
14
|
-
| `category-f-silent-failures.md` | F — Silent failures |
|
|
15
|
-
| `category-g-bounds-and-overflow.md` | G — Bounds and overflow |
|
|
16
|
-
| `category-h-security-boundaries.md` | H — Security boundaries |
|
|
17
|
-
| `category-i-concurrency.md` | I — Concurrency |
|
|
18
|
-
| `category-j-code-rules-compliance.md` | J — Code-rules compliance |
|
|
19
|
-
| `category-k-codebase-conflicts.md` | K — Codebase conflicts |
|
|
20
|
-
| `category-l-behavior-equivalence.md` | L — Behavior equivalence |
|
|
21
|
-
| `category-m-producer-consumer-cardinality.md` | M — Producer-consumer cardinality |
|
|
22
|
-
| `category-n-test-name-scenario-verifier.md` | N — Test name / scenario verifier |
|
|
23
|
-
| `category-o-docstring-vs-impl-drift.md` | O — Docstring vs implementation drift |
|
|
24
|
-
| `category-p-name-vs-behavior-contract.md` | P — Name vs behavior contract |
|
|
25
|
-
| `category-q-cross-surface-claims.md` | Q — Cross-surface claim consistency |
|
|
26
|
-
|
|
27
|
-
## Rubric structure
|
|
28
|
-
|
|
29
|
-
Each file has:
|
|
30
|
-
- A plain-language description of what the category audits
|
|
31
|
-
- Concrete finding examples
|
|
32
|
-
- A companion-reference pointer to `../source-material-section-types.md`
|
|
33
|
-
- A sub-bucket decomposition table with stable IDs (A1, A2, …) and the concrete checks each bucket requires
|
|
34
|
-
|
|
35
|
-
## Relationship to prompts/
|
|
36
|
-
|
|
37
|
-
`category_rubrics/` is the human-readable reference. `prompts/` holds the agent-ready prompt templates that inline the same sub-bucket list in a structured prompt format. Keep both in sync when sub-buckets change.
|
|
@@ -1,37 +1 @@
|
|
|
1
|
-
# audit-rubrics/prompts
|
|
2
1
|
|
|
3
|
-
Agent-ready audit prompt templates, one per category (A–Q). An agent inlines the artifact under review into the `[INLINE THE FULL ARTIFACT HERE]` placeholder and runs the prompt as-is.
|
|
4
|
-
|
|
5
|
-
## Files
|
|
6
|
-
|
|
7
|
-
| File | Category |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `category-a-api-contracts.md` | A — API contract verification |
|
|
10
|
-
| `category-b-selector-engine-compat.md` | B — Selector engine compatibility |
|
|
11
|
-
| `category-c-resource-cleanup.md` | C — Resource cleanup |
|
|
12
|
-
| `category-d-scoping-and-ordering.md` | D — Scoping and ordering |
|
|
13
|
-
| `category-e-dead-code.md` | E — Dead code |
|
|
14
|
-
| `category-f-silent-failures.md` | F — Silent failures |
|
|
15
|
-
| `category-g-bounds-and-overflow.md` | G — Bounds and overflow |
|
|
16
|
-
| `category-h-security-boundaries.md` | H — Security boundaries |
|
|
17
|
-
| `category-i-concurrency.md` | I — Concurrency |
|
|
18
|
-
| `category-j-code-rules-compliance.md` | J — Code-rules compliance |
|
|
19
|
-
| `category-k-codebase-conflicts.md` | K — Codebase conflicts |
|
|
20
|
-
| `category-l-behavior-equivalence.md` | L — Behavior equivalence |
|
|
21
|
-
| `category-m-producer-consumer-cardinality.md` | M — Producer-consumer cardinality |
|
|
22
|
-
| `category-n-test-name-scenario-verifier.md` | N — Test name / scenario verifier |
|
|
23
|
-
| `category-o-docstring-vs-impl-drift.md` | O — Docstring vs implementation drift |
|
|
24
|
-
| `category-p-name-vs-behavior-contract.md` | P — Name vs behavior contract |
|
|
25
|
-
| `category-q-cross-surface-claims.md` | Q — Cross-surface claim consistency |
|
|
26
|
-
|
|
27
|
-
## Prompt structure
|
|
28
|
-
|
|
29
|
-
Each template:
|
|
30
|
-
- Scopes the audit to one category only (skip the others)
|
|
31
|
-
- Lists all sub-buckets from the matching `category_rubrics/` file
|
|
32
|
-
- Requires each sub-bucket to produce at least one Shape A finding OR one Shape B proof-of-absence with three or more adversarial probes
|
|
33
|
-
- Uses `find` as the finding ID prefix (single-pass audits) rather than `loop<N>-<K>`
|
|
34
|
-
|
|
35
|
-
## Relationship to category_rubrics/
|
|
36
|
-
|
|
37
|
-
`prompts/` is the executable form; `category_rubrics/` is the reference form. When a sub-bucket decomposition changes in a rubric, update the matching prompt in the same commit.
|
package/bin/AGENTS.md
CHANGED
|
@@ -1,119 +1 @@
|
|
|
1
|
-
# bin
|
|
2
1
|
|
|
3
|
-
The installer and its companion modules. Running `npx claude-dev-env` (or `node bin/install.mjs`) copies package files into the managed root (`~/.claude/` by default; `CLAUDE_CONFIG_DIR` or `--target` selects another), copies skills and agents into the agents home (`~/.agents/` for the default root) and publishes directory pointers at `skills/` and `agents/` under the managed root, merges hook entries into that root's `settings.json`, installs Git hooks, writes `~/.mypy.ini` under the process home, and copies Codex exec-policy files into `~/.codex/rules` (`CODEX_HOME/rules` when that variable is set), and generates Cursor `.mdc` files into `~/.cursor/rules` from the installed Claude rules.
|
|
4
|
-
|
|
5
|
-
## Files
|
|
6
|
-
|
|
7
|
-
| File | Purpose |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `install.mjs` | Main installer: builds a read-only plan via `install-plan.mjs`, then runs mutations inside `install-transaction.mjs` recovery (publish skill/agent lookup pointers, copy content directories, merge hooks, install skills, prune, git hooks, mypy.ini); routes `CLAUDE_HOME`, the agents home, the manifest path, and `~/.mypy.ini` through `resolve-install-root.mjs`; resolves single or multi-profile targets before mutation and writes one ownership manifest per target |
|
|
10
|
-
| `resolve-install-root.mjs` | Pure install-root resolver: precedence `--target` > `CLAUDE_CONFIG_DIR` > `~/.claude`, sibling `.agents` home (or `<root>.agents` for a named profile), separator-boundary containment, and the declared external allowlist for `~/.mypy.ini` plus files under the Codex rules directory, the Cursor home, and the agents home |
|
|
11
|
-
| `resolve-package-managed-directory.mjs` | Package-source resolver: skills and agents under `.agents/<name>/`, with a package-root `<name>/` fallback for dependency packages |
|
|
12
|
-
| `resolve-package-managed-directory.test.mjs` | Real-filesystem tests for the source resolver plus the live package `.agents` trees and `.claude` pointers |
|
|
13
|
-
| `publish-directory-pointer.mjs` | Directory-pointer helper: POSIX symlink or Windows junction from a Claude lookup path to the agents home; relocates a real directory at the lookup path into the target |
|
|
14
|
-
| `publish-directory-pointer.test.mjs` | Real-filesystem tests for pointer create, refresh, relocate, and unlink |
|
|
15
|
-
| `select-install-targets.mjs` | Pure target selection for main-default, explicit `--target`, and `--profile`/`--profiles`; rejects ambiguous or duplicate targets; builds per-target manifest records with `targetIdentity` and `managedRoot` |
|
|
16
|
-
| `install-plan.mjs` | Read-only install and uninstall plans: install preflight (managed root, source conflicts, Python, settings when hooks install) and uninstall preflight (settings JSON before removal, removable vs skipped manifest records), freezes plans E2/F execute |
|
|
17
|
-
| `install-transaction.mjs` | Install, update, and uninstall transaction journal: captures prior settings, manifest, managed files, and `core.hooksPath`, restores them on failure, and supports fault injection phases for recovery tests |
|
|
18
|
-
| `install.transaction.test.mjs` | Unit and sandbox installer tests for snapshot/restore and fault phases (`after_file_staging`, `after_settings_write`, `after_git_config`, `after_manifest_write`) |
|
|
19
|
-
| `install.uninstall-transaction.test.mjs` | Uninstall plan preflight and recovery: malformed/non-object settings fail before removal, each fault phase restores files/settings/manifest/`core.hooksPath`, retry succeeds, selected-root containment |
|
|
20
|
-
| `install.profile-root.test.mjs` | Contract tests for the install-root resolver: precedence, containment boundary, external allowlist, agents-home pairing, and the install.mjs import smoke check |
|
|
21
|
-
| `install.agents-home.test.mjs` | End-to-end tests that a sandbox install writes skills and agents under `.agents` and publishes `.claude/skills` and `.claude/agents` as directory pointers |
|
|
22
|
-
| `install.codex-rules.test.mjs` | Tests that Codex exec-policy files copy to `~/.codex/rules`, honor `CODEX_HOME`, skip `--only journal`, and uninstall without touching `default.rules` |
|
|
23
|
-
| `install.cursor-rules.test.mjs` | Tests that Cursor `.mdc` files generate into `~/.cursor/rules` from Claude rules, skip `--only journal`, and leave a local extra `.mdc` in place |
|
|
24
|
-
| `install-constants.mjs` | The named values `install.mjs` reads: `SKIPPED_SOURCE_ENTRY_NAMES` and `SKIPPED_SOURCE_FILE_EXTENSIONS` for the build artifacts the source walk leaves behind, `RUN_BACKUP_DIRECTORY_NAME_PATTERN` for the timestamp shape a run backup directory carries, `PACKAGE_AGENTS_HOME_DIRECTORY_NAME` for the `.agents` source home, `MANAGED_SKILLS_DIRECTORY_NAME` and `MANAGED_AGENTS_DIRECTORY_NAME` for the directory name each of those trees carries in a package source and under the agents home — read by the copy loops, the pointer publisher, and the prunes alike — `MANAGED_HOOKS_DIRECTORY_NAME` for the hooks tree under `~/.claude`, `SETTINGS_FILE_NAME` for the settings file the merge, the retired-hook prune, and the uninstall purge share, and `MYPY_INI_FILE_NAME` for the home-directory file `install_mypy_ini.mjs` writes, plus the Codex home and rules directory names `resolve-install-root.mjs` uses |
|
|
25
|
-
| `ever-shipped-skills.mjs` | Static `EVER_SHIPPED_SKILL_NAMES` set of every top-level skill directory name the package has shipped; the installer subtracts the current skill set from it to prune retired skills left under `~/.agents/skills` |
|
|
26
|
-
| `expand_home_directory_tokens.mjs` | Expands residual `$HOME` / `${HOME}` / `~/` tokens in settings.json hook and statusLine commands to absolute home paths at install time (literal-safe for homes that contain `$`) |
|
|
27
|
-
| `git_hooks_installer.mjs` | Installs or updates the `pre-commit`, `pre-push`, and `post-commit` Git hooks in the user's git config; writes hook scripts that delegate to the installed Python hooks |
|
|
28
|
-
| `install_mypy_ini.mjs` | Writes `~/.mypy.ini` with settings that make mypy find the hooks package and enforce strict type checking |
|
|
29
|
-
| `install.test.mjs` | Unit tests for `install.mjs` — covers conflict detection, interpreter detection, settings merging, the settings shapes the installer never wrote — those every hook walk hands back untouched, and the shipped-event value the merge replaces with a warning — the source-artifact skip in `collectFiles`, the case-only rename decision and the `copyTree` copy that acts on it, the retired-hook diff and settings prune, the stale-file prune: the manifest diff, path-key case folding, emptied-parent cleanup, and the warn-and-keep paths, and backup retention: the sweep a moved-content run drives, the empty root a run whose moves failed gives up, and the populated root retention keeps |
|
|
30
|
-
| `install.profiles.test.mjs` | Profile target-selection and per-target ownership manifest contract tests (main-default, multi-profile, ambiguity/duplicate rejection, help text) |
|
|
31
|
-
| `install.plan.test.mjs` | Read-only plan and preflight tests: zero-write plan construction, source-conflict and missing-Python fail-closed, settings check only when hooks install, tolerant broken-manifest, invalid managed root, mutation-kind list for E2 |
|
|
32
|
-
| `install.prune.test.mjs` | End-to-end prune tests that run the real installer against a sandbox `HOME` — retired-skill, retired-hook, and stale-file moves into one timestamped backup, the settings entry a retired hook loses, the top-level paths every root's diff leaves alone, the manifest record a failed move keeps, the full-install and resolved-dependency gates, backup retention, and the uninstall: the `~/.mypy.ini` removal, the containment guard, and nested-directory cleanup |
|
|
33
|
-
| `git_hooks_installer.test.mjs` | Tests for `git_hooks_installer.mjs` |
|
|
34
|
-
| `install_mypy_ini.test.mjs` | Tests for `install_mypy_ini.mjs` |
|
|
35
|
-
|
|
36
|
-
## Source build artifacts
|
|
37
|
-
|
|
38
|
-
`collectFiles` walks the package source and skips the artifacts a contributor's tooling writes beside it: the entry names `__pycache__`, `.ruff_cache`, `.pytest_cache`, `.mypy_cache`, `node_modules`, `.DS_Store`, and any file ending `.pyc` or `.pyo`. A skipped directory takes everything under it out of the walk. The `files` negations in `package.json` (`!**/__pycache__/**`, `!**/*.py[cod]`, the cache directories, `!**/*.log`, `!**/*.egg-info/**`) keep the same artifacts out of the published tarball, and `.npmignore` carries those patterns for tooling that reads it — keep the two in step. An `npx` install reads a clean tree; the walk covers a local `node bin/install.mjs` run against a working tree that holds the artifacts.
|
|
39
|
-
|
|
40
|
-
The skip and the cleanup of artifacts an earlier install copied are one code path. A `.pyc` a prior manifest records under any managed root sits outside the set the walk returns, so the next full install reads it as stale, moves it into that run's backup root, and drops it from the manifest the run writes.
|
|
41
|
-
|
|
42
|
-
## Copying a file whose name changed letter case
|
|
43
|
-
|
|
44
|
-
`copyTree` renames a destination entry that differs from the shipped file name only in letter case to the shipped name, then copies. On a case-insensitive volume `copyFileSync` writes its bytes through whichever entry the filesystem resolves the path to, so a package shipping `README.md` over an installed `Readme.md` would fill the installed entry and leave the earlier spelling standing. The rename runs first because `renameSync` inside one directory is atomic: a run interrupted between the rename and the copy leaves the file present under the shipped name holding the earlier content, which the next install overwrites.
|
|
45
|
-
|
|
46
|
-
The decision reads the destination directory's entry names, cached one listing per directory for the whole copy run. On a case-sensitive volume the two names are two files, so the rename is skipped and each name keeps its own content. `caseOnlyRenameSourceName(shippedName, existingNames, options)` holds the decision, and `options.isCaseInsensitive` carries the platform answer as a value so a test drives either branch on a host of either kind.
|
|
47
|
-
|
|
48
|
-
## Retired-skill prune
|
|
49
|
-
|
|
50
|
-
The full-install prune renames a retired skill directory into a timestamped backup rather than deleting it. Each pruned directory is renamed to `~/.claude/.claude-dev-env-pruned/<timestamp>/skills/<skill-name>/`, a backup root outside `~/.agents/skills` so a backed-up directory is never re-discovered as a skill. The `skills/` segment matches the layout the stale-file prune writes, so one recovery point reads as a copy of the tree it came from. One run shares one timestamped root, so a run leaves one recovery point. A rename that fails leaves the directory in place with a logged warning and never falls back to deletion, so a prune failure costs at most a cosmetic leftover.
|
|
51
|
-
|
|
52
|
-
Matching is by directory name alone, so a user-authored directory whose name collides with a retired skill is backed up as if it were that skill. A directory is pruned when the prior install's manifest recorded it or the ever-shipped set names it, and the current install did not just write it. A name absent from all three of those sets, together with `~/.agents/skills/_shared`, is left in place. Recovery of a wrongly-matched directory runs until the next pruning install, which keeps its own backup and retires the rest.
|
|
53
|
-
|
|
54
|
-
## Stale-file prune
|
|
55
|
-
|
|
56
|
-
A full install also moves aside a file under a managed root that the run leaves unwritten. `copyTree` adds and overwrites but never removes, so every root the installer writes carries the same drift, and the prune covers all of them: `rules`, `docs`, `commands`, `agents`, `system-prompts`, `scripts`, `_shared`, `audit-rubrics`, `skills`, and `hooks` — the names in `MANAGED_TOP_LEVEL_DIRECTORY_NAMES`.
|
|
57
|
-
|
|
58
|
-
Nothing moves unless a prior install recorded it. That single rule is what makes covering ten roots as safe as covering one: the installer reads the file list from `~/.claude/.claude-dev-env-manifest.json`, subtracts every file the run copied across all source roots, and moves what remains.
|
|
59
|
-
|
|
60
|
-
The prune runs once per root, each call confined to its own root. Per-root iteration gives the containment guard and the emptied-parent walk the root that owns each file, and it settles `_shared`: `~/.claude/_shared` and `~/.agents/skills/_shared` are distinct absolute paths, so the `_shared` call and the `skills` call each see their own files and no path enters two diffs. Each root's content lands under `~/.claude/.claude-dev-env-pruned/<timestamp>/<root-name>/<relative>`. Every prune in a run shares that one timestamped root. A recorded path under no managed root — `~/.claude/CLAUDE.md`, `settings.json`, the manifest itself, and the `~/.mypy.ini` that sits in the home directory beside `~/.claude` — reaches no root's diff and stays where it is. The install summary reports the skills root's own count on the `skills:` line and the sum across roots on its own line.
|
|
61
|
-
|
|
62
|
-
The manifest diff limits the move to files the installer itself wrote. Runtime-generated content — a Python `__pycache__` entry, a ruff cache, a log — and any file a user authored under a managed root stay in place, because no install recorded them. Path comparison ignores letter case on Windows and macOS, so a package shipping `README.md` over an installed `Readme.md` keeps the bytes the run just wrote. A directory or a link standing where the manifest records a file is skipped with a warning, so the mover never renames a whole tree and never follows a link out of `~/.claude`. A directory emptied by a move is removed, walking up to the root the file sat under. A move that fails logs a warning and leaves the file in place, so a prune failure costs at most a stale file. The installer records each such path in the fresh manifest when the file is still on disk, so the file stays inside the next full install's diff and gets another attempt.
|
|
63
|
-
|
|
64
|
-
A missing or unreadable manifest, or one carrying no file list, holds the stale-file prune for that run: with no record of what an install wrote, the run has nothing to diff against.
|
|
65
|
-
|
|
66
|
-
A run writes both manifest keys — the file list and the skill-name list — wholesale from what it just installed only when the prunes ran that run and read the prior record all the way through, so the next diff reads as "the package stopped shipping this". Every other run unions what it wrote onto the prior lists: a scoped `--only` install, a full install holding its prunes behind an unresolved dependency group, and a run whose prune step ends early with a logged warning. The union keeps every entry a later prune needs to spot a stale file or a retired skill, and keeps `--uninstall` able to name the whole tree. The prune itself bounds the lists: a stale path leaves the record on the first full install that moves it aside.
|
|
67
|
-
|
|
68
|
-
## Retired-hook entries in settings.json
|
|
69
|
-
|
|
70
|
-
A hook script under `~/.claude/hooks` carries a second reference: the `settings.json` entry that runs it. A full install removes that entry in the same run that moves the script aside, and removes it first — a `settings.json` naming a script that has left the hooks directory makes every session start invoke a missing file.
|
|
71
|
-
|
|
72
|
-
The retired set comes from the manifest diff alone: the hook files a prior install recorded that this run leaves unwritten, each taken relative to `~/.claude/hooks`. A script the run still writes stays out of the set, and a path no install of ours recorded never enters it, so a user-authored hook is out of reach of the prune. Each command is matched on the anchored `/.claude/hooks/<relative>` tail the merge uses to tell this installer's entries from a user's, so a command whose path is a retired tail plus a suffix (`retired_gate.py.bak`) names another file and stays.
|
|
73
|
-
|
|
74
|
-
The walk covers every event type the settings file holds rather than the ones the current `hooks.json` names, so an entry under an event type the package stopped shipping is reached too. A matcher group left empty is dropped, and an event type left empty goes with it. The file is written once, and only when an entry left it, so a run that retires no hook leaves `settings.json` byte-identical.
|
|
75
|
-
|
|
76
|
-
Every settings walk recognizes the shapes the installer writes and steps around the rest, so a hand-edited or third-party `settings.json` carries an install through rather than ending it. A matcher group carrying no `hooks` array and a hook entry whose `command` is not a string survive every walk untouched. An event type whose value is not an array of groups survives this prune and the uninstall purge; the merge replaces that value with the group list it ships for that event type, warning with the event type named so the user can recover the value from their own history. An event type the package ships no groups for keeps whatever value the file holds.
|
|
77
|
-
|
|
78
|
-
## Backup retention
|
|
79
|
-
|
|
80
|
-
A run that moves content into `~/.claude/.claude-dev-env-pruned/<timestamp>/` then retires the other run backups, so the directory holds the one recovery point closest to what sits on disk. The retired-skill prune and the stale-file prune each report how many moves succeeded, and their sum is the signal the sweep answers to. The sweep removes a direct child whose name matches the installer's timestamp shape (`2026-07-25T18-04-11-923Z`), which leaves anything else under the pruned-backup directory in place, along with the directory itself. A removal that fails logs a warning and the sweep carries on, so retention never ends an install. The install output names the count when the sweep removes anything.
|
|
81
|
-
|
|
82
|
-
A run that moves nothing sweeps nothing, so every recovery point the user holds stays where it is. `moveIntoRunBackup` creates the directories leading to a backup path before it renames, so a run whose every move fails — the antivirus scanner or open editor case — leaves that timestamped root standing empty. Retention clears it with `rmdirSync` alone, depth first, so a directory holding anything survives every step.
|
|
83
|
-
|
|
84
|
-
## Uninstall
|
|
85
|
-
|
|
86
|
-
`--uninstall` builds a read-only uninstall plan, captures a recovery snapshot, then removes each file the plan lists.
|
|
87
|
-
|
|
88
|
-
Settings JSON is validated before any removal. A malformed or non-object `settings.json` fails closed with the managed files still on disk. Each manifest record passes a containment guard: the path resolves under `~/.claude`, or it names the `~/.mypy.ini` the install writes in the home directory, or it sits under the Codex rules directory, or it sits under the Cursor home, or it sits under the agents home. Every other record is skipped with a warning and counted. Skipping keeps one malformed record — hand-edited, or written by an installer that ran against a different home — from stranding the user with a half-removed install. The purge removes every legitimate record, clears the manifest, and reports the skipped count.
|
|
89
|
-
|
|
90
|
-
The uninstall runs inside the same snapshot/restore journal as install: prior settings, manifest, managed files, and `core.hooksPath` restore when a later phase fails, so a retry starts from a complete ownership record. The journal is discarded only after a successful commit.
|
|
91
|
-
|
|
92
|
-
Removing a file leaves its directory a candidate for cleanup. Once the file loop ends, the purge walks up from each such directory to the managed top-level directory the file sits under (`MANAGED_TOP_LEVEL_DIRECTORY_NAMES`), removing each directory it finds empty. That reaches a nested tree such as `skills/<name>/scripts/`. A record under no managed root gets no walk, so `~/.claude` itself is never a stop root and a directory the installer never wrote stays. A separate pass drops each managed top-level directory the purge empties.
|
|
93
|
-
|
|
94
|
-
## Prune gates
|
|
95
|
-
|
|
96
|
-
Every prune runs behind the same two gates: a full install, and every declared dependency group resolved. When any dependency group fails to resolve, all of them are skipped for the whole run with a logged notice naming the unresolved group. An unresolved dependency contributes no skills to the installed set, so a live skill that a dependency package supplies would look retired and its files would look stale; holding every prune until each dependency resolves keeps that skill's files in place and keeps their manifest records, so a run with every dependency resolved can still prune them.
|
|
97
|
-
|
|
98
|
-
## Key exports from install.mjs
|
|
99
|
-
|
|
100
|
-
| Export | Description |
|
|
101
|
-
|---|---|
|
|
102
|
-
| `CONTENT_DIRECTORIES` | Array of package subdirectory names copied verbatim to `~/.claude/` (skills and agents are outside this list; they copy into the agents home) |
|
|
103
|
-
| `MANAGED_TOP_LEVEL_DIRECTORY_NAMES` | The content directories plus `skills`, `agents`, and `hooks`; the stale-file prune walks it to give each root its own diff, and the uninstall purge reads it to find the root a recorded file belongs to |
|
|
104
|
-
| `collectFiles(directory)` | Lists every file under a source directory, skipping the build-artifact names and extensions in `install-constants.mjs` |
|
|
105
|
-
| `pythonCandidatesForPlatform(platform)` | Returns ordered Python interpreter candidates to probe; `py -3` first on Windows to avoid Microsoft Store alias issues |
|
|
106
|
-
| `isWindowsStorePythonStub(path)` | Returns true when the path resolves to the non-spawnable WindowsApps stub |
|
|
107
|
-
| `interpreterCommandFromPath(path)` | Formats an absolute interpreter path as a settings.json hook command prefix |
|
|
108
|
-
| `collectPackageSourceConflicts(dir)` | Returns any unmerged git conflicts in the package source; installer aborts when any exist |
|
|
109
|
-
| `pruneStaleInstalledFiles(priorFiles, currentFiles, destinationRoot, backupRoot, options)` | Moves each manifest-recorded file under the destination root that the run leaves unwritten into the run's backup root; returns `{ prunedCount, failedPaths }`. `options.isCaseInsensitive` drives path-key case folding, defaulting to this host's filesystem; `options.managedHomeDirectory` sets the home the containment guard tests against, defaulting to `~/.claude` |
|
|
110
|
-
| `copyTree(sourceBase, destBase, options)` | Copies every file under a source directory, renaming a destination entry that differs from the shipped name only in letter case to the shipped name first; returns `{ created, updated, paths }`. `options.isCaseInsensitive` drives that rename, defaulting to this host's filesystem |
|
|
111
|
-
| `caseOnlyRenameSourceName(shippedName, existingNames, options)` | Returns the existing directory entry a shipped file name would overwrite through a case-only spelling difference, or null; `options.isCaseInsensitive` defaults to this host's filesystem |
|
|
112
|
-
| `retiredManagedHookRelativePaths(priorFiles, currentFiles, hooksRoot)` | Returns the hook script paths a prior install recorded under the hooks root that this run leaves unwritten, each relative to that root |
|
|
113
|
-
| `pruneRetiredHookEntriesFromSettings(settingsPath, retiredPaths)` | Removes each settings.json entry running a retired managed hook script, writing the file only when an entry left it; returns the removed count |
|
|
114
|
-
| `retainNewestRunBackupOnly(runBackupRoot, didRunMoveContent)` | Retires every run backup sitting beside the run's own when `didRunMoveContent` holds; clears the run's empty root with `rmdirSync` when it does not |
|
|
115
|
-
| `comparisonKeyForPath(path, options)` | Builds the key two paths are compared through: resolved, forward-slashed, and lowercased when `options.isCaseInsensitive` holds — which defaults to true on Windows and macOS |
|
|
116
|
-
|
|
117
|
-
## Install groups
|
|
118
|
-
|
|
119
|
-
`install.mjs` defines install groups (`core`, `journal`) plus any dependency groups discovered from `package.json` `dependencies`. The `core` group installs skills, all hooks, and the content directories. `journal` installs only its skill set.
|
package/bin/install.test.mjs
CHANGED
|
@@ -190,7 +190,7 @@ test('CONTENT_DIRECTORIES omits agents because that tree installs to the agents
|
|
|
190
190
|
test('core includeDirectories ships _shared and scripts for advisor protocol and CLI fallback', () => {
|
|
191
191
|
assert.ok(
|
|
192
192
|
CORE_INCLUDE_DIRECTORIES.includes('_shared'),
|
|
193
|
-
'_shared must ship with --only core so advisor-protocol.md lands for team-advisor
|
|
193
|
+
'_shared must ship with --only core so advisor-protocol.md lands for team-advisor',
|
|
194
194
|
);
|
|
195
195
|
assert.ok(
|
|
196
196
|
CORE_INCLUDE_DIRECTORIES.includes('scripts'),
|
package/commands/AGENTS.md
CHANGED
|
@@ -1,15 +1 @@
|
|
|
1
|
-
# commands
|
|
2
1
|
|
|
3
|
-
Slash-command definitions installed into `~/.claude/commands/` by `bin/install.mjs`. Each `.md` file registers a `/command-name` the user can type in Claude Code. The file name (without `.md`) becomes the command name.
|
|
4
|
-
|
|
5
|
-
Command bodies use `rules/asd-ste100-language.md` for user-facing word choice, sentence style, tone, punctuation, and prose form. Each command keeps its workflow contract.
|
|
6
|
-
|
|
7
|
-
## Command files
|
|
8
|
-
|
|
9
|
-
| File | Command | What it does |
|
|
10
|
-
|---|---|---|
|
|
11
|
-
| `sr-loop.md` | `/sr-loop` | Runs the converging cleanup loop: /simplify passes until clean, then a code-review fix pass |
|
|
12
|
-
|
|
13
|
-
## Format
|
|
14
|
-
|
|
15
|
-
Each file is plain Markdown. The first paragraph is the command's help text shown in the Claude Code UI. The body is the full instruction set Claude follows when the command runs.
|
package/docs/AGENTS.md
CHANGED
|
@@ -1,32 +1 @@
|
|
|
1
|
-
# docs
|
|
2
1
|
|
|
3
|
-
Reference documentation installed into `~/.claude/docs/` by `bin/install.mjs`. These files are loaded on demand by rules, skills, and agents — they are not always-on context.
|
|
4
|
-
|
|
5
|
-
## Files
|
|
6
|
-
|
|
7
|
-
| File | Purpose |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `CODE_RULES.md` | Compact agent reference for all code rules; ⚡ marks hook-enforced rules; canonical source agents load before writing code |
|
|
10
|
-
| `TEST_QUALITY.md` | Test writing standards: what to test, what to remove, React testing patterns, anti-patterns |
|
|
11
|
-
| `BDD_DISCOVERY_PROTOCOL.md` | Example Mapping algorithm for discovery before implementation; based on Smart & Molak *BDD in Action* §6.4 |
|
|
12
|
-
| `BDD_SCENARIO_QUALITY.md` | Seven scenario quality patterns (§7.6-style catalog) |
|
|
13
|
-
| `BDD_TEST_LAYOUT.md` | `describe/when/should` test layout and soap-opera personas |
|
|
14
|
-
| `DJANGO_PATTERNS.md` | Django-specific coding patterns |
|
|
15
|
-
| `REACT_PATTERNS.md` | React-specific coding patterns |
|
|
16
|
-
| `agent-spawn-protocol.md` | Full agent-spawn protocol behind the `rules/agent-spawn-protocol.md` kernel: context-sufficiency check, `/prompt-generator` prompt crafting, and the spawn step |
|
|
17
|
-
| `nas-ssh-invocation.md` | Full NAS ssh policy behind the `rules/nas-ssh-invocation.md` kernel: the OpenSSH binary form, config sources, and hook enforcement |
|
|
18
|
-
| `worker-completion-gate.md` | Full worker-completion gate behind the `rules/workers-done-before-complete.md` kernel: the checklist, examples, and run-state records |
|
|
19
|
-
| `wsl-docker-cowork-starter-matrix.md` | Host matrix: WSL/Docker/cowork component → starter → required? → shutdown; policy options with costs; no unmeasured `.wslconfig` memory cap |
|
|
20
|
-
| `host-pool-health-monitor.md` | Operator recipe for Windows pool/handle health: thresholds, clean-shell re-run of `Capture-PoolHealth.ps1`, RC2/RC3/RC4 remediation map |
|
|
21
|
-
|
|
22
|
-
## Subdirectory
|
|
23
|
-
|
|
24
|
-
| Entry | Description |
|
|
25
|
-
|---|---|
|
|
26
|
-
| `references/` | Pointer documents to external sources and standard terminology; loaded on demand |
|
|
27
|
-
|
|
28
|
-
## Load pattern
|
|
29
|
-
|
|
30
|
-
A rule points to a doc with the path wrapped in backticks, such as `@~/.claude/docs/<file>.md`. The backticks make it a plain pointer: Claude Code reads the doc only when a rule, skill, or agent opens it, so the doc stays out of session-start context. The same path without backticks expands into context at launch when it sits in a file that loads at session start.
|
|
31
|
-
|
|
32
|
-
The `InstructionsLoaded` hook confirms this: a bare `@`-import fires an `include` load event; a backtick-wrapped path fires none.
|
|
@@ -1,17 +1 @@
|
|
|
1
|
-
# docs/references
|
|
2
1
|
|
|
3
|
-
Pointer documents to external sources, standard terminology, and internal tool or skill usage. Files here are loaded on demand by rules that cite them.
|
|
4
|
-
|
|
5
|
-
## Files
|
|
6
|
-
|
|
7
|
-
| File | Purpose |
|
|
8
|
-
|---|---|
|
|
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
|
-
| `prose-style-enforcement.md` | How `CLAUDE_PROSE_STYLE_ENFORCEMENT` arms opinionated prose gates (default off) while AskUserQuestion lean-block stays always on |
|
|
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 advisor selection |
|
|
13
|
-
| `weak-executor-advisor.md` | Consult profile a below-advisor-tier executor (Sonnet, Haiku) follows on top of `advisor-tool.md`: spawn-prompt steering, context packaging, two-timing rule, consult budget, failure branches |
|
|
14
|
-
|
|
15
|
-
## Role
|
|
16
|
-
|
|
17
|
-
A file naming an external concept gives a one-line definition and links a direct source. A file naming an internal tool or skill describes what it does and when to use it. They back the rule text in `rules/` and `packages/claude-dev-env/docs/CODE_RULES.md` without embedding full third-party content inline.
|
package/hooks/AGENTS.md
CHANGED
|
@@ -1,28 +1 @@
|
|
|
1
|
-
# hooks
|
|
2
1
|
|
|
3
|
-
Python hook scripts wired into Claude Code's lifecycle via `settings.json`. Each hook answers one or more lifecycle events (`PreToolUse`, `PostToolUse`, `Stop`, `SubagentStop`, `SessionStart`, `SessionEnd`) and either blocks a tool call, annotates it, or performs a side-effect.
|
|
4
|
-
|
|
5
|
-
## Subdirectories
|
|
6
|
-
|
|
7
|
-
| Directory | Role |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `advisory/` | Hooks that warn but do not block (`permissionDecision: "ask"`) |
|
|
10
|
-
| `blocking/` | Hooks that deny tool calls when a rule is violated |
|
|
11
|
-
| `blocking/config/` | Shared constants for blocking hooks |
|
|
12
|
-
| `git-hooks/` | Native git hooks (`pre-commit`, `pre-push`, `post-commit`) installed via the git-hooks path |
|
|
13
|
-
| `git-hooks/git_hooks_constants/` | Shared constants for the git-hook scripts |
|
|
14
|
-
| `hooks_constants/` | Shared constant modules imported by multiple hooks across this tree (includes `pii_prevention_constants.py` for personal-data and secret scan patterns) |
|
|
15
|
-
| `lifecycle/` | Hooks that run at session or config-change boundaries |
|
|
16
|
-
| `observability/` | PostToolUse hooks that record agent behavior for diagnostics |
|
|
17
|
-
| `session/` | SessionStart and SessionEnd hooks for per-session cleanup |
|
|
18
|
-
| `validation/` | PostToolUse hooks that validate code quality after a write (mypy, auto-format) |
|
|
19
|
-
| `validators/` | Library modules used by the validation hooks — checks split by concern |
|
|
20
|
-
| `workflow/` | PostToolUse hooks that trigger doc publishing and companion-file generation |
|
|
21
|
-
|
|
22
|
-
## Conventions
|
|
23
|
-
|
|
24
|
-
- **Event mapping:** Every hook reads JSON from stdin and exits 0 (allow) or prints a `hookSpecificOutput` block (block/ask). Blocking hooks set `permissionDecision: "block"`.
|
|
25
|
-
- **Constants companion:** Each hook with more than a handful of tunable strings keeps them in a `hooks_constants/<hook_name>_constants.py` sibling. Import from there; do not repeat literals.
|
|
26
|
-
- **Tests:** Each hook has one or more `test_<hookname>*.py` files beside it. Run with `python -m pytest <test_file>`.
|
|
27
|
-
- **Registration:** Hooks are declared in `settings.json` under the right lifecycle event. The installer (`packages/claude-dev-env/bin/install.mjs`) merges the hook entries during `npx claude-dev-env`.
|
|
28
|
-
- **Top-level utilities:** `_gh_pr_author_swap_utils.py` and `rewrite_plugin_paths.py` are shared helpers imported by multiple blocking hooks. `hooks.json` records the canonical hook-to-event mapping for auditing.
|
package/hooks/advisory/AGENTS.md
CHANGED
|
@@ -1,16 +1 @@
|
|
|
1
|
-
# hooks/advisory
|
|
2
1
|
|
|
3
|
-
Hooks that produce a warning prompt (`permissionDecision: "ask"`) rather than an outright block. The user sees the warning and can continue or cancel.
|
|
4
|
-
|
|
5
|
-
## Key files
|
|
6
|
-
|
|
7
|
-
| File | Event | What it guards |
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| `migration_safety_advisor.py` | PreToolUse (Write/Edit) | Django migration files containing `RemoveField`, `RenameField`, `DeleteModel`, or `RenameModel` — warns that these operations must be backwards-compatible during deployment |
|
|
10
|
-
| `refactor_guard.py` | PreToolUse (Edit) | Edits that rename or restructure existing code not present in the current git diff — warns that the change may be out of scope |
|
|
11
|
-
|
|
12
|
-
## Conventions
|
|
13
|
-
|
|
14
|
-
- Both hooks exit 0 (silent) when their trigger condition is not met.
|
|
15
|
-
- `refactor_guard.py` respects a bypass token at `~/.claude/.refactor-bypass-token`; when that file exists the hook stays silent.
|
|
16
|
-
- Tests live beside each hook following the `test_<name>.py` pattern used in `blocking/`. Run with `python -m pytest <test_file>`.
|
package/hooks/blocking/AGENTS.md
CHANGED
|
@@ -1,113 +1 @@
|
|
|
1
|
-
# hooks/blocking
|
|
2
1
|
|
|
3
|
-
PreToolUse hooks that deny (block) tool calls when a rule is violated. The main enforcer is `code_rules_enforcer.py`, which routes each Write/Edit through a suite of focused check modules. Every other file in this directory is either a standalone blocker, a check module, or a test.
|
|
4
|
-
|
|
5
|
-
## Subdirectory
|
|
6
|
-
|
|
7
|
-
| Directory | Role |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `tdd_enforcer_parts/` | Concern modules the `tdd_enforcer.py` entry hook wires together: path classification, content analysis, candidate-path resolution, freshness, git-tracking restore detection, decisions, and constants |
|
|
10
|
-
| `claude_md_orphan_file_blocker_parts/` | Concern modules the `claude_md_orphan_file_blocker.py` entry hook wires together: reference extraction, subtree scan, scan plan, decision, and constants |
|
|
11
|
-
| `package_inventory_stale_blocker_parts/` | Concern modules the `package_inventory_stale_blocker.py` entry hook wires together: inventory detection, decision, and constants |
|
|
12
|
-
| `inventory_intent_records/` | The shared per-session pending-intent store both inventory blockers read to break the file/row add-order deadlock |
|
|
13
|
-
| `pii_prevention_blocker_parts/` | Concern modules the `pii_prevention_blocker.py` entry hook wires together: per-repository scan exemption, the per-repository allowlist of exact values a commit may carry, and resolving the repository a commit command targets (with `config/` for the resolution deny-message constants) |
|
|
14
|
-
| `tests/` | pytest suite for `pii_prevention_blocker.py` repository resolution and the `pii_prevention_blocker_parts` modules |
|
|
15
|
-
|
|
16
|
-
## Core enforcer
|
|
17
|
-
|
|
18
|
-
| File | What it does |
|
|
19
|
-
|---|---|
|
|
20
|
-
| `code_rules_enforcer.py` | Entry point — reads PreToolUse stdin, reconstructs post-edit content, dispatches all check modules, and returns a block with a per-issue list when any check fails |
|
|
21
|
-
|
|
22
|
-
The check modules it calls are the `code_rules_<concern>.py` files below.
|
|
23
|
-
|
|
24
|
-
## Check modules (imported by `code_rules_enforcer.py`)
|
|
25
|
-
|
|
26
|
-
| Module | Concern |
|
|
27
|
-
|---|---|
|
|
28
|
-
| `code_rules_annotations_length.py` | Parameter/return annotations, function length, pytest fixture annotation requirements |
|
|
29
|
-
| `code_rules_banned_identifiers.py` | Banned short names (`ctx`, `cfg`, `msg`, etc.), banned prefixes (`handle_`, `process_`, etc.) |
|
|
30
|
-
| `code_rules_boolean_mustcheck.py` | Boolean naming (`is_`/`has_`/… prefixes) and must-check return values |
|
|
31
|
-
| `code_rules_command_dispatch.py` | A `hooks/blocking/` command classifier matching a multi-word command regex without a start anchor or first-word tokenization |
|
|
32
|
-
| `code_rules_comments.py` | No new inline comments; advisory on deletion of existing ones |
|
|
33
|
-
| `code_rules_constants_config.py` | Constants must live in `config/`; file-global constant use-count |
|
|
34
|
-
| `code_rules_docstrings.py` | Google-style docstrings; `Args:` section matches signature; run-on-sentence and prose-wall narrative backstops; undefined-constant references |
|
|
35
|
-
| `code_rules_duplicate_body.py` | A function body copied from a sibling module, or a helper body inlined as a block inside a larger function in the same file |
|
|
36
|
-
| `code_rules_imports_logging.py` | Imports at top of file; logging format-arg style; printf tokens in `str.format`-logger messages |
|
|
37
|
-
| `code_rules_js_conventions.py` | Boolean-prefix naming and banned identifiers for JavaScript/TypeScript declarations and `@param {boolean}` JSDoc, scoped to changed lines |
|
|
38
|
-
| `code_rules_magic_values.py` | No magic numbers or strings in production code bodies |
|
|
39
|
-
| `code_rules_naming_collection.py` | Collection names must use `all_*` prefix |
|
|
40
|
-
| `code_rules_optional_params.py` | No optional parameters where a required one would do |
|
|
41
|
-
| `code_rules_orphan_css_class.py` | CSS class attributes in Python markup with no matching `.<class>` selector |
|
|
42
|
-
| `code_rules_paired_test.py` | A public function omitted by a module's established paired test suite must get a behavioral test — checked on both the production-module write and the stem-matched test-file write |
|
|
43
|
-
| `code_rules_path_utils.py` | Path utility helpers shared across check modules |
|
|
44
|
-
| `code_rules_paths_syspath.py` | `sys.path.insert` must be guarded |
|
|
45
|
-
| `code_rules_probe_chains.py` | Probe-chain detection logic |
|
|
46
|
-
| `code_rules_probe_detection.py` | Probe pattern detection helpers |
|
|
47
|
-
| `code_rules_probe_recording.py` | Probe recording utilities |
|
|
48
|
-
| `code_rules_shared.py` | Shared dataclasses and helpers used by multiple check modules |
|
|
49
|
-
| `code_rules_string_magic.py` | Magic string detection with masking and f-string support; whitespace-only indentation literals in function bodies |
|
|
50
|
-
| `code_rules_test_assertions.py` | Test assertion style rules |
|
|
51
|
-
| `code_rules_test_layout.py` | Dead scaffolding in a test module: a private module constant read by no other line, and an unused parameter on a private test helper |
|
|
52
|
-
| `code_rules_test_branching_except.py` | No bare or broad `except` in test branches |
|
|
53
|
-
| `code_rules_test_isolation.py` | Tests must not rely on home-dir or temp-dir side effects |
|
|
54
|
-
| `code_rules_type_escape.py` | No `Any` imports, `cast()`, or `# type: ignore` outside boundary files |
|
|
55
|
-
| `code_rules_typeddict_stub.py` | TypedDict pairs (`_encode_*`/`_decode_*`) must both exist in the same module |
|
|
56
|
-
|
|
57
|
-
## Other standalone blockers
|
|
58
|
-
|
|
59
|
-
| File | Event | What it blocks |
|
|
60
|
-
|---|---|---|
|
|
61
|
-
| `orchestrator_refresh_reschedule_gate.py` | PreToolUse (ScheduleWakeup/CronCreate) | `/orchestrator-refresh` re-arm when run status is not `active` |
|
|
62
|
-
| `block_main_commit.py` | PreToolUse (Bash) | `git commit`/`git push` directly to `main` |
|
|
63
|
-
| `bot_mention_comment_blocker.py` | PreToolUse (Write/Edit) | PR review comments that @-mention a bot |
|
|
64
|
-
| `claude_md_orphan_file_blocker.py` | PreToolUse (Write/Edit/MultiEdit) | Per-directory `CLAUDE.md` table cells naming a bare filename absent from the directory subtree |
|
|
65
|
-
| `conventional_pr_title_gate.py` | PreToolUse (Bash) | `gh pr create`/`gh pr edit` with a `--title` that is not a Conventional Commit, in a repo whose CI runs a semantic-pull-request title check |
|
|
66
|
-
| `cursor_cli_python_misfire_blocker.py` | PreToolUse (Bash/PowerShell) | `cursor` / `Cursor.exe` launched against a Python script with code-rules-gate flags, which raises Cursor's EPIPE unknown-option dialog |
|
|
67
|
-
| `destructive_command_blocker.py` | PreToolUse (Bash/PowerShell) | Shell commands with destructive literals (`rm -rf`, `git reset --hard`, etc.) |
|
|
68
|
-
| `docstring_rule_gate_count_blocker.py` | PreToolUse (Write/Edit/MultiEdit) | A stale spelled-out gate-validator count in `docstring-prose-matches-implementation.md` — the "N more gate validators" / "M gated slices" count drifting from the `check_docstring_*` validators the prose names |
|
|
69
|
-
| `duplicate_rmtree_helper_blocker.py` | PreToolUse (Write/Edit) | A local re-definition of the Windows-safe rmtree helper trio (`_strip_read_only_and_retry`, `_force_remove_tree` / `force_rmtree`) in place of importing a shared helper |
|
|
70
|
-
| `env_var_table_code_drift_blocker.py` | PreToolUse (Write/Edit/MultiEdit) | A markdown env-var summary table row attributing an environment variable to a code file whose source never references that variable name |
|
|
71
|
-
| `es_exe_path_rewriter.py` | PreToolUse | Rewrites paths referencing `.exe` under the Everything search path |
|
|
72
|
-
| `fable_spawn_gate.py` | PreToolUse (Agent/Task) | An `Agent` or `Task` spawn whose prompt carries no `FABLE-SPAWN-AUTHORIZED` token and whose model field reads `fable` in any letter case — the bare alias, or a delimiter segment of a full model id, so `claude-fable-5` is denied too |
|
|
73
|
-
| `gh_body_arg_blocker.py` | PreToolUse (Bash) | `gh` commands passing `--body`/`-b` directly (requires `--body-file` instead) |
|
|
74
|
-
| `gh_pr_author_enforcer.py` | PreToolUse | Enforces PR author identity rules |
|
|
75
|
-
| `gh_pr_author_restore.py` | PostToolUse | Restores PR author after a tool call |
|
|
76
|
-
| `hook_prose_detector_consistency.py` | PreToolUse (Write/Edit) | Hook docstrings/messages that claim a trigger the detector cannot fire on; armed only when `CLAUDE_PROSE_STYLE_ENFORCEMENT` is on (default off) |
|
|
77
|
-
| `open_questions_in_plans_blocker.py` | PreToolUse (Write/Edit) | Plan documents with unresolved open questions |
|
|
78
|
-
| `nas_ssh_binary_enforcer.py` | PreToolUse (Bash) | A bare `ssh`/`scp`/`sftp` command word targeting the NAS (Git Bash's MSYS ssh stalls on an interactive password prompt), or the full `System32/OpenSSH` binary to that host without `-o BatchMode=yes` |
|
|
79
|
-
| `package_inventory_stale_blocker.py` | PreToolUse (Write) | A new production code file created in a directory whose `README.md`/`CLAUDE.md` inventory (or a parent skill's `SKILL.md` Layout table mapping the `scripts/` subdirectory) names two or more sibling files but no entry for the new file |
|
|
80
|
-
| `pii_commit_command.py` | library | Token-aware git-commit detection reused by `pii_prevention_blocker.py` |
|
|
81
|
-
| `pii_payload_scan.py` | library | Write/Edit and durable post-body PII evaluation reused by `pii_prevention_blocker.py` |
|
|
82
|
-
| `pii_prevention_blocker.py` | PreToolUse (Write/Edit/MultiEdit/Bash/PowerShell/MCP GitHub) | Entry hook — content that carries high-confidence personal data or secrets (real emails, home-dir paths, private IPs, credential material) on write, durable GitHub posts, or staged commit paths; resolves the staged-commit repository from the command it gates (via `pii_prevention_blocker_parts`), not the session working directory |
|
|
83
|
-
| `pii_scanner.py` | library | Pure text scanners shared by `pii_prevention_blocker.py` |
|
|
84
|
-
| `piped_pytest_blocker.py` | PreToolUse (Bash) | A pytest run whose output feeds a pipe, where the pipeline reports the exit code of the command on the right |
|
|
85
|
-
| `precommit_code_rules_gate.py` | library | Resolves a directory's Git repository root; reused by `pii_prevention_blocker.py`, `pii_payload_scan.py`, and `session_edit_stage_gate.py` |
|
|
86
|
-
| `pytest_testpaths_orphan_blocker.py` | PreToolUse (Write/Edit/MultiEdit) | New `test_*.py` files created under a directory absent from a package's explicit pytest `testpaths` allowlist |
|
|
87
|
-
| `question_to_user_enforcer.py` | Stop | User-directed questions not routed through `AskUserQuestion` |
|
|
88
|
-
| `send_user_file_open_locally_blocker.py` | PreToolUse (SendUserFile) | A desk-side file attach (`SendUserFile` with `status` not `proactive`); points to `Invoke-Item -LiteralPath` for the native Windows app |
|
|
89
|
-
| `sensitive_file_protector.py` | PreToolUse (Write/Edit/MultiEdit) | Writes to sensitive credential or config files |
|
|
90
|
-
| `session_edit_stage_gate.py` | PreToolUse (Bash) | A `git commit` that would drop files edited this session because they are tracked but left unstaged |
|
|
91
|
-
| `session_handoff_blocker.py` | Stop | Responses suggesting a new session mid-task |
|
|
92
|
-
| `shell_substitution_blocker.py` | PreToolUse (Bash) | A command carrying `$(...)`, a live backtick, or `<(...)`/`>(...)` process substitution, which the allowlist matcher cannot descend into |
|
|
93
|
-
| `stale_comment_reference_blocker.py` | PreToolUse (Edit) | An Edit that rewrites a Python code line while keeping the standalone comment directly above it, when that comment names an identifier the rewrite removes from the line |
|
|
94
|
-
| `state_description_blocker.py` | PreToolUse (Write/Edit) | Historical/comparative language in documentation |
|
|
95
|
-
| `subprocess_budget_completeness.py` | PreToolUse | Subprocess calls missing required budget arguments |
|
|
96
|
-
| `tdd_enforcer.py` | PreToolUse (Write/Edit) | Production code written without a matching failing test |
|
|
97
|
-
| `unscoped_search_blocker.py` | PreToolUse (Bash/PowerShell) | A `find` or recursive listing that walks from the filesystem root, a drive root, bare home, or a network share root |
|
|
98
|
-
| `volatile_path_in_post_blocker.py` | PreToolUse (Bash/MCP GitHub) | `gh` post commands and GitHub MCP post tools whose body references a volatile path (job scratch dir, worktree, or system temp) that outlives the durable post |
|
|
99
|
-
| `windows_rmtree_blocker.py` | PreToolUse (Write/Edit) | `shutil.rmtree` with `ignore_errors=True` on Windows |
|
|
100
|
-
| `workflow_substitution_slot_blocker.py` | PreToolUse (Write/Edit) | Workflow templates with bare per-iteration tokens missing angle-bracket slots |
|
|
101
|
-
| `write_existing_file_blocker.py` | PreToolUse (Write) | Write to a path where a file already exists |
|
|
102
|
-
|
|
103
|
-
## Supporting modules
|
|
104
|
-
|
|
105
|
-
| File | Role |
|
|
106
|
-
|---|---|
|
|
107
|
-
| `_gh_body_arg_utils.py` | Parsing helpers for `gh_body_arg_blocker.py` |
|
|
108
|
-
| `test_hook_subprocess_support.py` | Shared subprocess runner for blocking-hook behavior tests |
|
|
109
|
-
|
|
110
|
-
## Conventions
|
|
111
|
-
|
|
112
|
-
- Tests live beside each hook as `test_<hookname>.py` or `test_<hookname>_<suffix>.py`. Run with `python -m pytest <test_file>`.
|
|
113
|
-
- Tunable constants live in `hooks_constants/<hook_name>_constants.py`.
|