claude-dev-env 1.93.1 → 1.95.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/_shared/advisor/CLAUDE.md +2 -2
- package/_shared/advisor/advisor-protocol.md +35 -27
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +3 -2
- package/_shared/advisor/scripts/model_tier_run_validator.py +23 -15
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +81 -17
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/audit-contract.md +1 -1
- package/_shared/pr-loop/gh-payloads.md +3 -3
- package/_shared/pr-loop/post-audit-thread-contract.md +51 -0
- package/_shared/pr-loop/scripts/README.md +2 -2
- package/_shared/pr-loop/scripts/fix_hookspath.py +47 -12
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -69
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -374
- package/_shared/pr-loop/state-schema.md +3 -22
- package/bin/CLAUDE.md +11 -2
- package/bin/ever-shipped-skills.mjs +70 -0
- package/bin/install.mjs +138 -13
- package/bin/install.prune.test.mjs +457 -0
- package/bin/install.test.mjs +10 -4
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/CLAUDE.md +2 -0
- package/hooks/blocking/code_rules_enforcer.py +4 -0
- package/hooks/blocking/code_rules_shared.py +82 -0
- package/hooks/blocking/code_rules_test_layout.py +9 -3
- package/hooks/blocking/pii_commit_command.py +398 -0
- package/hooks/blocking/pii_payload_scan.py +133 -0
- package/hooks/blocking/pii_prevention_blocker.py +151 -449
- package/hooks/blocking/plain_language_blocker.py +138 -4
- package/hooks/blocking/sensitive_file_protector.py +114 -48
- package/hooks/blocking/tdd_enforcer.py +9 -2
- package/hooks/blocking/test_code_rules_enforcer_scratchpad.py +105 -0
- package/hooks/blocking/test_code_rules_shared.py +181 -0
- package/hooks/blocking/test_pii_prevention_blocker.py +299 -0
- package/hooks/blocking/test_plain_language_blocker_allowlist.py +184 -0
- package/hooks/blocking/test_sensitive_file_protector.py +185 -0
- package/hooks/blocking/test_tdd_enforcer_scratchpad.py +105 -0
- package/hooks/blocking/test_verdict_directory_write_blocker.py +42 -7
- package/hooks/hooks_constants/CLAUDE.md +4 -2
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -2
- package/hooks/hooks_constants/harness_scratchpad_constants.py +17 -0
- package/hooks/hooks_constants/local_identity.py +65 -15
- package/hooks/hooks_constants/pii_prevention_constants.py +16 -0
- package/hooks/hooks_constants/plain_language_blocker_constants.py +5 -0
- package/hooks/hooks_constants/pr_description_enforcer_constants.py +1 -1
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +42 -0
- package/hooks/hooks_constants/test_local_identity.py +68 -0
- package/hooks/pyproject.toml +75 -4
- package/hooks/validators/CLAUDE.md +1 -1
- package/hooks/validators/README.md +2 -0
- package/hooks/validators/python_style_checks.py +114 -136
- package/hooks/validators/python_style_helpers.py +95 -0
- package/hooks/validators/test_python_style_checks.py +0 -164
- package/hooks/validators/test_python_style_checks_decorator_gap.py +119 -0
- package/hooks/validators/test_python_style_fixes.py +251 -0
- package/hooks/validators/test_python_style_helpers.py +125 -0
- package/package.json +1 -1
- package/rules/CLAUDE.md +1 -0
- package/rules/anti-corollary-tests.md +69 -0
- package/rules/bdd.md +1 -3
- package/rules/code-reviews.md +1 -1
- package/rules/gh-paginate.md +1 -1
- package/rules/plain-language.md +2 -0
- package/scripts/CLAUDE.md +10 -1
- package/scripts/Get-SessionAccount.ps1 +210 -0
- package/scripts/tests/CLAUDE.md +15 -1
- package/scripts/tests/Get-SessionAccount.Tests.ps1 +350 -0
- package/skills/CLAUDE.md +6 -27
- package/skills/anthropic-plan/SKILL.md +1 -13
- package/skills/autoconverge/CLAUDE.md +5 -2
- package/skills/autoconverge/SKILL.md +112 -387
- package/skills/autoconverge/reference/CLAUDE.md +4 -1
- package/skills/autoconverge/reference/closing-report.md +9 -2
- package/skills/autoconverge/reference/convergence.md +26 -22
- package/skills/autoconverge/reference/headless-safety.md +44 -0
- package/skills/autoconverge/reference/multi-pr.md +88 -0
- package/skills/autoconverge/reference/self-closing-loop.md +84 -0
- package/skills/autoconverge/reference/stop-conditions.md +21 -13
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +87 -27
- package/skills/autoconverge/workflow/converge.contract.test.mjs +40 -10
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +179 -47
- package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.mjs +286 -96
- package/skills/bugteam/CLAUDE.md +1 -1
- package/skills/bugteam/CONSTRAINTS.md +3 -3
- package/skills/bugteam/PROMPTS.md +8 -19
- package/skills/bugteam/SKILL.md +13 -14
- package/skills/bugteam/reference/CLAUDE.md +1 -1
- package/skills/bugteam/reference/README.md +2 -3
- package/skills/bugteam/reference/audit-and-teammates.md +2 -2
- package/skills/bugteam/reference/copilot-gap-analysis.md +2 -0
- package/skills/bugteam/reference/github-pr-reviews.md +56 -38
- package/skills/bugteam/reference/team-setup.md +11 -11
- package/skills/bugteam/scripts/CLAUDE.md +6 -6
- package/skills/bugteam/scripts/README.md +20 -8
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +13 -1463
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +23 -358
- package/skills/bugteam/scripts/bugteam_preflight.py +13 -315
- package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +2 -3
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +34 -1053
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +30 -347
- package/skills/bugteam/scripts/test_bugteam_preflight.py +30 -305
- package/skills/closeout/SKILL.md +153 -0
- package/skills/closeout/reference/handoff-prompt-template.md +72 -0
- package/skills/closeout/reference/issue-body-templates.md +108 -0
- package/skills/closeout/reference/pii-redaction-checklist.md +36 -0
- package/skills/copilot-finding-triage/SKILL.md +80 -28
- package/skills/copilot-finding-triage/reference/tier-rubric.md +70 -11
- package/skills/copilot-finding-triage/templates/notification.md +8 -3
- package/skills/everything-search/SKILL.md +1 -2
- package/skills/grokify/SKILL.md +58 -0
- package/skills/grokify/templates/handoff-template.md +92 -0
- package/skills/orchestrator/SKILL.md +35 -25
- package/skills/orchestrator-refresh/SKILL.md +15 -11
- package/skills/pr-converge/CLAUDE.md +1 -1
- package/skills/pr-converge/SKILL.md +132 -233
- package/skills/pr-converge/reference/convergence-gates.md +102 -66
- package/skills/pr-converge/reference/examples.md +37 -25
- package/skills/pr-converge/reference/fix-protocol.md +7 -7
- package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -4
- package/skills/pr-converge/reference/per-tick.md +64 -41
- package/skills/pr-converge/reference/state-schema.md +27 -2
- package/skills/pr-converge/scripts/CLAUDE.md +8 -0
- package/skills/pr-converge/scripts/README.md +4 -3
- package/skills/pr-converge/scripts/_pr_converge_path_setup.py +23 -0
- package/skills/pr-converge/scripts/check_convergence.py +189 -645
- package/skills/pr-converge/scripts/check_convergence_gates.py +235 -0
- package/skills/pr-converge/scripts/check_convergence_thread_gates.py +219 -0
- package/skills/pr-converge/scripts/conftest.py +8 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -0
- package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +26 -0
- package/skills/pr-converge/scripts/test_check_convergence.py +73 -228
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +321 -0
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +68 -0
- package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +75 -0
- package/skills/pr-fix-protocol/SKILL.md +20 -20
- package/skills/privacy-hygiene/SKILL.md +4 -3
- package/skills/reviewer-gates/SKILL.md +26 -8
- package/skills/session-log/SKILL.md +11 -12
- package/skills/session-tidy/SKILL.md +40 -30
- package/skills/skill-builder/CLAUDE.md +13 -10
- package/skills/skill-builder/SKILL.md +31 -31
- package/skills/skill-builder/references/CLAUDE.md +10 -6
- package/skills/skill-builder/references/delegation-map.md +13 -4
- package/skills/skill-builder/references/description-field.md +113 -0
- package/skills/skill-builder/references/self-audit-checklist.md +21 -4
- package/skills/skill-builder/references/skill-modularity.md +113 -0
- package/skills/skill-builder/references/skill-types.md +4 -0
- package/skills/skill-builder/templates/CLAUDE.md +4 -4
- package/skills/skill-builder/templates/gap-analysis.md +25 -1
- package/skills/skill-builder/workflows/CLAUDE.md +6 -4
- package/skills/skill-builder/workflows/improve-skill.md +17 -16
- package/skills/skill-builder/workflows/new-skill.md +34 -10
- package/skills/skill-builder/workflows/polish-skill.md +59 -43
- package/skills/team-advisor/SKILL.md +15 -11
- package/skills/usage-pause/SKILL.md +10 -14
- package/system-prompts/software-engineer.xml +7 -6
- package/hooks/validators/test_verify_paths.py +0 -32
- package/hooks/validators/verify_paths.py +0 -57
- package/skills/bdd-protocol/CLAUDE.md +0 -26
- package/skills/bdd-protocol/SKILL.md +0 -30
- package/skills/bdd-protocol/references/CLAUDE.md +0 -10
- package/skills/bdd-protocol/references/anti-patterns.md +0 -26
- package/skills/bdd-protocol/references/example-mapping.md +0 -23
- package/skills/bg-agent/CLAUDE.md +0 -17
- package/skills/bg-agent/SKILL.md +0 -69
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py +0 -55
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_fix_hookspath_constants.py +0 -26
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_preflight_constants.py +0 -35
- package/skills/caveman/CLAUDE.md +0 -15
- package/skills/caveman/SKILL.md +0 -38
- package/skills/code/CLAUDE.md +0 -17
- package/skills/code/SKILL.md +0 -178
- package/skills/deep-research/CLAUDE.md +0 -17
- package/skills/deep-research/SKILL.md +0 -80
- package/skills/findbugs/CLAUDE.md +0 -20
- package/skills/findbugs/SKILL.md +0 -241
- package/skills/fixbugs/CLAUDE.md +0 -19
- package/skills/fixbugs/SKILL.md +0 -142
- package/skills/gh-paginate/CLAUDE.md +0 -18
- package/skills/gh-paginate/SKILL.md +0 -84
- package/skills/gotcha/CLAUDE.md +0 -33
- package/skills/gotcha/SKILL.md +0 -73
- package/skills/implement/CLAUDE.md +0 -27
- package/skills/implement/SKILL.md +0 -66
- package/skills/implement/scripts/CLAUDE.md +0 -22
- package/skills/implement/scripts/append_note.py +0 -133
- package/skills/implement/scripts/implement_scripts_constants/CLAUDE.md +0 -22
- package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
- package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +0 -12
- package/skills/implement/scripts/test_append_note.py +0 -191
- package/skills/log-audit/CLAUDE.md +0 -20
- package/skills/log-audit/SKILL.md +0 -66
- package/skills/log-audit/reference/CLAUDE.md +0 -9
- package/skills/log-audit/reference/charter.md +0 -52
- package/skills/log-audit/scripts/CLAUDE.md +0 -27
- package/skills/log-audit/scripts/cluster_recurrences.py +0 -261
- package/skills/log-audit/scripts/collect_log_window.py +0 -199
- package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +0 -12
- package/skills/log-audit/scripts/log_audit_constants/__init__.py +0 -0
- package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +0 -23
- package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +0 -24
- package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +0 -49
- package/skills/log-audit/scripts/mine_copilot_findings.py +0 -302
- package/skills/log-audit/scripts/test_cluster_recurrences.py +0 -160
- package/skills/log-audit/scripts/test_collect_log_window.py +0 -111
- package/skills/log-audit/scripts/test_mine_copilot_findings.py +0 -126
- package/skills/logifix/CLAUDE.md +0 -36
- package/skills/logifix/SKILL.md +0 -69
- package/skills/logifix/scripts/CLAUDE.md +0 -16
- package/skills/logifix/scripts/logifix.ps1 +0 -205
- package/skills/monitor-open-prs/CLAUDE.md +0 -34
- package/skills/monitor-open-prs/SKILL.md +0 -88
- package/skills/monitor-open-prs/scripts/CLAUDE.md +0 -17
- package/skills/monitor-open-prs/scripts/discover_open_prs.py +0 -69
- package/skills/monitor-open-prs/scripts/test_discover_open_prs.py +0 -149
- package/skills/monitor-open-prs/test_skill_contract.py +0 -27
- package/skills/post-audit-findings/SKILL.md +0 -80
- package/skills/pr-consistency-audit/CLAUDE.md +0 -34
- package/skills/pr-consistency-audit/SKILL.md +0 -116
- package/skills/pr-consistency-audit/reference/CLAUDE.md +0 -16
- package/skills/pr-consistency-audit/reference/detection-rules.md +0 -96
- package/skills/pr-consistency-audit/reference/illustrations.md +0 -78
- package/skills/pr-review-responder/CLAUDE.md +0 -35
- package/skills/pr-review-responder/EXAMPLES.md +0 -590
- package/skills/pr-review-responder/PRINCIPLES.md +0 -533
- package/skills/pr-review-responder/README.md +0 -168
- package/skills/pr-review-responder/SKILL.md +0 -203
- package/skills/pr-review-responder/TESTING.md +0 -350
- package/skills/pr-scope-resolve/SKILL.md +0 -45
- package/skills/pre-compact/CLAUDE.md +0 -24
- package/skills/pre-compact/SKILL.md +0 -134
- package/skills/qbug/CLAUDE.md +0 -40
- package/skills/qbug/SKILL.md +0 -387
- package/skills/qbug/test_qbug_skill_audit_schema.py +0 -152
- package/skills/qbug/test_qbug_skill_post_fix_audit.py +0 -103
- package/skills/refine/CLAUDE.md +0 -44
- package/skills/refine/SKILL.md +0 -262
- package/skills/refine/templates/CLAUDE.md +0 -17
- package/skills/refine/templates/implementation-notes-template.html +0 -56
- package/skills/refine/templates/plan-template.md +0 -60
- package/skills/research-mode/CLAUDE.md +0 -35
- package/skills/research-mode/SKILL.md +0 -53
- package/skills/structure-prompt/CLAUDE.md +0 -42
- package/skills/structure-prompt/SKILL.md +0 -44
- package/skills/structure-prompt/reference/CLAUDE.md +0 -28
- package/skills/structure-prompt/reference/adversarial-tuning.md +0 -62
- package/skills/structure-prompt/reference/block-classification.md +0 -27
- package/skills/structure-prompt/reference/canonical-case.md +0 -48
- package/skills/structure-prompt/reference/citation-depth.md +0 -70
- package/skills/structure-prompt/reference/cleanup.md +0 -33
- package/skills/structure-prompt/reference/constraints.md +0 -33
- package/skills/structure-prompt/reference/directives.md +0 -37
- package/skills/structure-prompt/reference/examples.md +0 -72
- package/skills/structure-prompt/reference/instantiation.md +0 -51
- package/skills/structure-prompt/reference/output-contract.md +0 -72
- package/skills/structure-prompt/reference/per-category.md +0 -23
- package/skills/structure-prompt/reference/persona.md +0 -38
- package/skills/structure-prompt/reference/research.md +0 -33
- package/skills/structure-prompt/reference/structure.md +0 -28
- package/skills/verified-build/CLAUDE.md +0 -33
- package/skills/verified-build/SKILL.md +0 -33
|
@@ -31,26 +31,7 @@ Adds nothing beyond common. Single subagent loops internally and returns a final
|
|
|
31
31
|
|
|
32
32
|
### pr-converge
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
| Field | Type | Purpose |
|
|
37
|
-
|---|---|---|
|
|
38
|
-
| `phase` | enum | `BUGBOT`, `BUGTEAM` — which reviewer the current tick drives |
|
|
39
|
-
| `current_head` | str | PR `.head.sha` / `git rev-parse` for the PR under work (each tick; from `pull_request_read(method="get")` when no file store) |
|
|
40
|
-
| `bugbot_clean_at` | str \| null | HEAD SHA at which Cursor Bugbot last reported clean, or `null` (reset on every push) |
|
|
41
|
-
| `copilot_clean_at` | str \| null | HEAD SHA at which the GitHub Copilot reviewer (`copilot-pull-request-reviewer[bot]`) last reported clean (review `state == "APPROVED"`), or `null`. Reset on every push. Convergence gates require this equals `current_head` after bugbot+bugteam are clean (see `skills/pr-converge/SKILL.md` § Convergence gates). |
|
|
42
|
-
| `merge_state_status` | str \| null | Last-observed `mergeable_state` from `pull_request_read(method="get")` (e.g., `clean`, `dirty`, `blocked`, `behind`, `unknown`), or `null` before the first check. Reset on every push. `dirty` triggers the rebase invocation; non-`clean` non-`dirty` is a hard blocker per pr-converge `Stop conditions`. |
|
|
43
|
-
| `inline_lag_streak` | int | Consecutive ticks where bugbot's review body claims findings but inline-comments API returns zero rows for `current_head` |
|
|
44
|
-
| `tick_count` | int | Observability only — **no ceiling**; loop ends on convergence or **Stop conditions** in `pr-converge` |
|
|
45
|
-
|
|
46
|
-
**Dual persistence** (normative: `skills/pr-converge/SKILL.md` § State across ticks, § Multi-PR orchestration model):
|
|
47
|
-
|
|
48
|
-
| Mode | When it applies | Source of truth | `tick_count` bump |
|
|
49
|
-
|---|---|---|---|
|
|
50
|
-
| **`state.json`** | File exists at `<TMPDIR>/pr-converge-<session_id>/state.json` (multi-PR orchestration or other file-backed session) | JSON: top-level `session_id`; per-PR objects under `prs[<number>]` with `owner`, `repo`, `branch`, `phase`, `current_head`, `bugbot_clean_at`, `inline_lag_streak`, `tick_count`, `last_action`, `status`, `last_updated`. Optional sibling `converged.log` (append-only; multi-PR only). Writes use lock + atomic replace per skill **Concurrency** | **Orchestrator only** at tick start (locked merge for every non-terminal PR); **never** bump `tick_count` in Step 1 when this file is in use |
|
|
51
|
-
| **Conversation state line** | **No** `state.json` (typical single-PR `/pr-converge` in Cursor) | Persist **`phase`**, **`bugbot_clean_at`**, **`inline_lag_streak`**, **`tick_count`** as **plain text** in each assistant turn; next tick reads them from the **most recent assistant message**. **`current_head` is not serialized in that line** — re-resolve each tick via `pull_request_read(method="get")` (same contract as `skills/pr-converge/SKILL.md` § State across ticks). | **Step 1** increments `tick_count` in that line **only** when no `state.json` — must not double-count with any file-backed path |
|
|
52
|
-
|
|
53
|
-
**`status` (file-backed `prs[...]` only):** `fresh | in_progress | awaiting_bugbot | awaiting_bugteam | converged | blocked`
|
|
34
|
+
Normative field list, phase enum, dual persistence, and reset semantics: [`../../skills/pr-converge/reference/state-schema.md`](../../skills/pr-converge/reference/state-schema.md). File-backed multi-PR `status` enum: [`../../skills/pr-converge/reference/multi-pr-orchestration.md`](../../skills/pr-converge/reference/multi-pr-orchestration.md).
|
|
54
35
|
|
|
55
36
|
### monitor-many
|
|
56
37
|
|
|
@@ -71,11 +52,11 @@ Adds per-PR JSON state file at `~/.claude/skills/monitor-many/state/<owner>-<rep
|
|
|
71
52
|
|
|
72
53
|
- bugteam: cleared on each new `/bugteam` invocation
|
|
73
54
|
- qbug: cleared on each new `/qbug` invocation
|
|
74
|
-
- pr-converge:
|
|
55
|
+
- pr-converge: see [`../../skills/pr-converge/reference/state-schema.md`](../../skills/pr-converge/reference/state-schema.md)
|
|
75
56
|
- monitor-many: persists across orchestrator runs; only `last_seen_comment_id` advances monotonically
|
|
76
57
|
|
|
77
58
|
## Convergence checks
|
|
78
59
|
|
|
79
60
|
- bugteam, qbug: `last_action == "audited"` AND `last_findings.total == 0` → `converged`
|
|
80
|
-
- pr-converge:
|
|
61
|
+
- pr-converge: see [`../../skills/pr-converge/reference/convergence-gates.md`](../../skills/pr-converge/reference/convergence-gates.md)
|
|
81
62
|
- monitor-many: no unresolved comments requiring code changes AND required checks green AND review policy satisfied → `gh pr ready`
|
package/bin/CLAUDE.md
CHANGED
|
@@ -6,7 +6,8 @@ The installer and its companion modules. Running `npx claude-dev-env` (or `node
|
|
|
6
6
|
|
|
7
7
|
| File | Purpose |
|
|
8
8
|
|---|---|
|
|
9
|
-
| `install.mjs` | Main installer: discovers install groups, copies content directories (`rules`, `docs`, `commands`, `agents`, `system-prompts`, `scripts`, `_shared`, `audit-rubrics`), merges hooks into `settings.json`, installs skills, runs `git_hooks_installer.mjs` and `install_mypy_ini.mjs` |
|
|
9
|
+
| `install.mjs` | Main installer: discovers install groups, copies content directories (`rules`, `docs`, `commands`, `agents`, `system-prompts`, `scripts`, `_shared`, `audit-rubrics`), merges hooks into `settings.json`, installs skills, prunes retired skills on a full install, runs `git_hooks_installer.mjs` and `install_mypy_ini.mjs` |
|
|
10
|
+
| `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 `~/.claude/skills` |
|
|
10
11
|
| `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 `$`) |
|
|
11
12
|
| `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 |
|
|
12
13
|
| `install_mypy_ini.mjs` | Writes `~/.mypy.ini` with settings that make mypy find the hooks package and enforce strict type checking |
|
|
@@ -14,6 +15,14 @@ The installer and its companion modules. Running `npx claude-dev-env` (or `node
|
|
|
14
15
|
| `git_hooks_installer.test.mjs` | Tests for `git_hooks_installer.mjs` |
|
|
15
16
|
| `install_mypy_ini.test.mjs` | Tests for `install_mypy_ini.mjs` |
|
|
16
17
|
|
|
18
|
+
## Retired-skill prune
|
|
19
|
+
|
|
20
|
+
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>/<skill-name>/`, a backup root outside `~/.claude/skills` so a backed-up directory is never re-discovered as a skill. Backups accumulate — nothing cleans them — so a user can recover a directory. 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.
|
|
21
|
+
|
|
22
|
+
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 `~/.claude/skills/_shared`, is left in place.
|
|
23
|
+
|
|
24
|
+
The prune is skipped for the whole run — with a logged notice naming the unresolved group — when any declared dependency group fails to resolve. An unresolved dependency contributes no skills to the installed set, so a live skill that a dependency package supplies would look retired; holding the prune until every dependency resolves keeps such a skill from being backed up.
|
|
25
|
+
|
|
17
26
|
## Key exports from install.mjs
|
|
18
27
|
|
|
19
28
|
| Export | Description |
|
|
@@ -26,4 +35,4 @@ The installer and its companion modules. Running `npx claude-dev-env` (or `node
|
|
|
26
35
|
|
|
27
36
|
## Install groups
|
|
28
37
|
|
|
29
|
-
`install.mjs` defines install groups (`core`, `journal
|
|
38
|
+
`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.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The top-level skill directory names this package has ever shipped.
|
|
3
|
+
*
|
|
4
|
+
* The installer subtracts the skills the current package installs from this set
|
|
5
|
+
* to learn which shipped skills have retired, so a full reinstall can remove a
|
|
6
|
+
* retired skill left behind under ~/.claude/skills. Because the current set is
|
|
7
|
+
* subtracted at runtime, restoring a retired skill to the package protects it
|
|
8
|
+
* automatically — it re-enters the installed set and drops out of the retired
|
|
9
|
+
* set on the next install.
|
|
10
|
+
*
|
|
11
|
+
* The list is a committed literal rather than a git query, because a user
|
|
12
|
+
* installs from the npm tarball, which carries no source-repo history. Refresh
|
|
13
|
+
* it by running, from the repository root:
|
|
14
|
+
*
|
|
15
|
+
* git log --all --pretty=format: --name-only -- 'packages/claude-dev-env/skills/*\/SKILL.md' | sort -u
|
|
16
|
+
*
|
|
17
|
+
* and pasting each distinct top-level skill directory name below.
|
|
18
|
+
*/
|
|
19
|
+
export const EVER_SHIPPED_SKILL_NAMES = new Set([
|
|
20
|
+
'anthropic-plan',
|
|
21
|
+
'auditing-claude-config',
|
|
22
|
+
'autoconverge',
|
|
23
|
+
'bdd-protocol',
|
|
24
|
+
'bg-agent',
|
|
25
|
+
'bugteam',
|
|
26
|
+
'caveman',
|
|
27
|
+
'code',
|
|
28
|
+
'copilot-finding-triage',
|
|
29
|
+
'copilot-review',
|
|
30
|
+
'deep-research',
|
|
31
|
+
'everything-search',
|
|
32
|
+
'findbugs',
|
|
33
|
+
'fixbugs',
|
|
34
|
+
'fresh-branch',
|
|
35
|
+
'gh-paginate',
|
|
36
|
+
'gotcha',
|
|
37
|
+
'grokify',
|
|
38
|
+
'implement',
|
|
39
|
+
'log-audit',
|
|
40
|
+
'logifix',
|
|
41
|
+
'monitor-open-prs',
|
|
42
|
+
'orchestrator',
|
|
43
|
+
'orchestrator-refresh',
|
|
44
|
+
'post-audit-findings',
|
|
45
|
+
'pr-consistency-audit',
|
|
46
|
+
'pr-converge',
|
|
47
|
+
'pr-fix-protocol',
|
|
48
|
+
'pr-loop-cloud-transport',
|
|
49
|
+
'pr-loop-lifecycle',
|
|
50
|
+
'pr-review-responder',
|
|
51
|
+
'pr-scope-resolve',
|
|
52
|
+
'pre-compact',
|
|
53
|
+
'privacy-hygiene',
|
|
54
|
+
'qbug',
|
|
55
|
+
'rebase',
|
|
56
|
+
'recall',
|
|
57
|
+
'refine',
|
|
58
|
+
'remember',
|
|
59
|
+
'research-mode',
|
|
60
|
+
'reviewer-gates',
|
|
61
|
+
'session-log',
|
|
62
|
+
'session-tidy',
|
|
63
|
+
'skill-builder',
|
|
64
|
+
'structure-prompt',
|
|
65
|
+
'task-build',
|
|
66
|
+
'team-advisor',
|
|
67
|
+
'update',
|
|
68
|
+
'usage-pause',
|
|
69
|
+
'verified-build',
|
|
70
|
+
]);
|
package/bin/install.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync, copyFileSync, unlinkSync, rmSync, realpathSync } from 'node:fs';
|
|
4
|
-
import { join, dirname, resolve, relative } from 'node:path';
|
|
3
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync, copyFileSync, unlinkSync, rmSync, renameSync, realpathSync } from 'node:fs';
|
|
4
|
+
import { join, dirname, resolve, relative, basename } from 'node:path';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
6
|
import { execSync, execFileSync } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
@@ -9,6 +9,7 @@ import { createRequire } from 'node:module';
|
|
|
9
9
|
import { installAllGitHooks } from './git_hooks_installer.mjs';
|
|
10
10
|
import { installMypyIniForClaudeHooks } from './install_mypy_ini.mjs';
|
|
11
11
|
import { expandHomeDirectoryTokensInSettings } from './expand_home_directory_tokens.mjs';
|
|
12
|
+
import { EVER_SHIPPED_SKILL_NAMES } from './ever-shipped-skills.mjs';
|
|
12
13
|
|
|
13
14
|
const CLAUDE_HOME = join(homedir(), '.claude');
|
|
14
15
|
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
@@ -19,6 +20,10 @@ const packageRequire = createRequire(import.meta.url);
|
|
|
19
20
|
|
|
20
21
|
export const CONTENT_DIRECTORIES = ['rules', 'docs', 'commands', 'agents', 'system-prompts', 'scripts', '_shared', 'audit-rubrics'];
|
|
21
22
|
|
|
23
|
+
const SKILL_MANIFEST_FILENAME = 'SKILL.md';
|
|
24
|
+
const NEVER_PRUNED_SKILL_DIRECTORIES = new Set(['_shared']);
|
|
25
|
+
const PRUNED_SKILLS_BACKUP_DIRECTORY_NAME = '.claude-dev-env-pruned';
|
|
26
|
+
|
|
22
27
|
export const CORE_INCLUDE_DIRECTORIES = [
|
|
23
28
|
'rules', 'docs', 'commands', 'agents', 'audit-rubrics', '_shared', 'scripts',
|
|
24
29
|
];
|
|
@@ -94,17 +99,33 @@ function resolveDependencyPackageRoot(dependencyPackageName) {
|
|
|
94
99
|
return dirname(dependencyPackageJsonPath);
|
|
95
100
|
}
|
|
96
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Discovers the install groups contributed by resolvable dependency packages and
|
|
104
|
+
* the names of the declared dependencies that failed to resolve.
|
|
105
|
+
*
|
|
106
|
+
* A dependency that cannot be resolved contributes no group, so its skills never
|
|
107
|
+
* enter the installed set. The retired-skill prune subtracts the installed set
|
|
108
|
+
* from the ever-shipped set, so an unresolved dependency whose skills migrated
|
|
109
|
+
* out of the main package would leave those live skills looking retired. The
|
|
110
|
+
* returned unresolved-name list lets the caller skip the prune in that degraded
|
|
111
|
+
* mode rather than delete a live skill.
|
|
112
|
+
*
|
|
113
|
+
* @returns {{groups: object, unresolvedDependencyNames: string[]}} The resolvable
|
|
114
|
+
* dependency groups keyed by group name, and the names that failed to resolve.
|
|
115
|
+
*/
|
|
97
116
|
function discoverDependencyGroups() {
|
|
98
117
|
const ownPackageJsonPath = join(PACKAGE_ROOT, 'package.json');
|
|
99
118
|
const ownPackageJson = JSON.parse(readFileSync(ownPackageJsonPath, 'utf8'));
|
|
100
119
|
const dependencies = ownPackageJson.dependencies || {};
|
|
101
120
|
const discoveredGroups = {};
|
|
121
|
+
const unresolvedDependencyNames = [];
|
|
102
122
|
for (const dependencyName of Object.keys(dependencies)) {
|
|
103
123
|
let dependencyRoot;
|
|
104
124
|
try {
|
|
105
125
|
dependencyRoot = resolveDependencyPackageRoot(dependencyName);
|
|
106
126
|
} catch {
|
|
107
127
|
console.error(` WARNING: Could not resolve dependency ${dependencyName}, skipping`);
|
|
128
|
+
unresolvedDependencyNames.push(dependencyName);
|
|
108
129
|
continue;
|
|
109
130
|
}
|
|
110
131
|
const dependencyPackageJson = JSON.parse(
|
|
@@ -145,18 +166,20 @@ function discoverDependencyGroups() {
|
|
|
145
166
|
}
|
|
146
167
|
discoveredGroups[groupName] = group;
|
|
147
168
|
}
|
|
148
|
-
return discoveredGroups;
|
|
169
|
+
return { groups: discoveredGroups, unresolvedDependencyNames };
|
|
149
170
|
}
|
|
150
171
|
|
|
172
|
+
const dependencyDiscovery = discoverDependencyGroups();
|
|
173
|
+
const UNRESOLVED_DEPENDENCY_NAMES = dependencyDiscovery.unresolvedDependencyNames;
|
|
174
|
+
|
|
151
175
|
const INSTALL_GROUPS = {
|
|
152
176
|
core: {
|
|
153
177
|
description: 'Development standards, hooks, agents, commands',
|
|
154
178
|
skills: [
|
|
155
|
-
'orchestrator', 'orchestrator-refresh', 'team-advisor',
|
|
179
|
+
'orchestrator', 'orchestrator-refresh', 'team-advisor', 'grokify',
|
|
156
180
|
'anthropic-plan', 'everything-search',
|
|
157
|
-
'pr-review-responder',
|
|
158
181
|
'privacy-hygiene',
|
|
159
|
-
'recall', 'remember', 'task-build'
|
|
182
|
+
'recall', 'remember', 'task-build'
|
|
160
183
|
],
|
|
161
184
|
includeDirectories: CORE_INCLUDE_DIRECTORIES,
|
|
162
185
|
includeAllHooks: true,
|
|
@@ -165,11 +188,7 @@ const INSTALL_GROUPS = {
|
|
|
165
188
|
description: 'Session logging and memory',
|
|
166
189
|
skills: ['session-log', 'session-tidy'],
|
|
167
190
|
},
|
|
168
|
-
|
|
169
|
-
description: 'Deep research and citation tools',
|
|
170
|
-
skills: ['deep-research', 'research-mode'],
|
|
171
|
-
},
|
|
172
|
-
...discoverDependencyGroups(),
|
|
191
|
+
...dependencyDiscovery.groups,
|
|
173
192
|
};
|
|
174
193
|
|
|
175
194
|
/**
|
|
@@ -611,11 +630,99 @@ function mergeHooks(hooksSourceRoot, pythonCommand) {
|
|
|
611
630
|
return groupCount;
|
|
612
631
|
}
|
|
613
632
|
|
|
614
|
-
function writeManifest(installedFiles) {
|
|
633
|
+
function writeManifest(installedFiles, skillNames) {
|
|
615
634
|
const manifest = { package: PACKAGE_NAME, version: PACKAGE_VERSION, installedAt: new Date().toISOString(), files: installedFiles };
|
|
635
|
+
if (skillNames) {
|
|
636
|
+
manifest.skills = skillNames;
|
|
637
|
+
}
|
|
616
638
|
writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2) + '\n');
|
|
617
639
|
}
|
|
618
640
|
|
|
641
|
+
/**
|
|
642
|
+
* Read the skill directory names the previous install recorded.
|
|
643
|
+
*
|
|
644
|
+
* Returns null when no manifest exists or the manifest predates the skills key,
|
|
645
|
+
* so a caller can treat "unknown prior skills" the same in both cases and lean
|
|
646
|
+
* on the ever-shipped set to find retired skills.
|
|
647
|
+
*
|
|
648
|
+
* @returns {string[]|null} The prior manifest's skill names, or null when absent.
|
|
649
|
+
*/
|
|
650
|
+
function readPriorManifestSkills() {
|
|
651
|
+
if (!existsSync(MANIFEST_FILE)) return null;
|
|
652
|
+
try {
|
|
653
|
+
const priorManifest = JSON.parse(readFileSync(MANIFEST_FILE, 'utf8'));
|
|
654
|
+
return Array.isArray(priorManifest.skills) ? priorManifest.skills : null;
|
|
655
|
+
} catch {
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Move retired skill directories a prior install left under ~/.claude/skills into
|
|
662
|
+
* a timestamped backup directory rather than deleting them.
|
|
663
|
+
*
|
|
664
|
+
* A directory is pruned only when the package no longer ships it and either the
|
|
665
|
+
* prior manifest recorded it or the package once shipped it. Subtracting the
|
|
666
|
+
* just-installed set from the ever-shipped set at call time means restoring a
|
|
667
|
+
* skill to the package protects it — it re-enters the installed set and never
|
|
668
|
+
* counts as retired. Matching is by directory name alone, so a personal
|
|
669
|
+
* directory a user authored under a name that collides with a retired skill
|
|
670
|
+
* (for example `code`, `implement`, `refine`, `gotcha`, `caveman`) is treated as
|
|
671
|
+
* that retired skill and moved to backup; only a name in neither set, and
|
|
672
|
+
* ~/.claude/skills/_shared, are left in place.
|
|
673
|
+
*
|
|
674
|
+
* Each pruned directory is renamed to
|
|
675
|
+
* ~/.claude/.claude-dev-env-pruned/<timestamp>/<skill-name>/ — a backup root
|
|
676
|
+
* outside ~/.claude/skills, so a moved directory is never re-discovered as a
|
|
677
|
+
* skill — under one shared timestamp per run. A backup is never cleaned up, so a
|
|
678
|
+
* user can recover a wrongly-matched directory. One directory whose rename fails
|
|
679
|
+
* (for example a read-only file or a cross-device move) is logged and left in
|
|
680
|
+
* place, never deleted, so a prune failure costs at most a cosmetic leftover.
|
|
681
|
+
*
|
|
682
|
+
* @param {Set<string>} installedSkillNames Skill names this install just wrote.
|
|
683
|
+
* @param {string[]|null} priorManifestSkills The prior manifest's skill names, or null.
|
|
684
|
+
*/
|
|
685
|
+
function pruneRetiredSkills(installedSkillNames, priorManifestSkills) {
|
|
686
|
+
const skillsDirectory = join(CLAUDE_HOME, 'skills');
|
|
687
|
+
if (!existsSync(skillsDirectory)) return;
|
|
688
|
+
const retiredSkillNames = new Set(
|
|
689
|
+
[...EVER_SHIPPED_SKILL_NAMES].filter(skillName => !installedSkillNames.has(skillName))
|
|
690
|
+
);
|
|
691
|
+
const priorSkillNames = new Set(priorManifestSkills || []);
|
|
692
|
+
const runTimestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
693
|
+
const backupRoot = join(CLAUDE_HOME, PRUNED_SKILLS_BACKUP_DIRECTORY_NAME, runTimestamp);
|
|
694
|
+
const existingSkillDirs = readdirSync(skillsDirectory, { withFileTypes: true })
|
|
695
|
+
.filter(entry => entry.isDirectory());
|
|
696
|
+
for (const skillDir of existingSkillDirs) {
|
|
697
|
+
const skillName = skillDir.name;
|
|
698
|
+
if (NEVER_PRUNED_SKILL_DIRECTORIES.has(skillName)) continue;
|
|
699
|
+
if (installedSkillNames.has(skillName)) continue;
|
|
700
|
+
const isPruneCandidate = priorSkillNames.has(skillName) || retiredSkillNames.has(skillName);
|
|
701
|
+
if (!isPruneCandidate) continue;
|
|
702
|
+
moveRetiredSkillToBackup(skillsDirectory, backupRoot, skillName);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Move one retired skill directory into the run's backup root, leaving it in
|
|
708
|
+
* place when the move fails.
|
|
709
|
+
*
|
|
710
|
+
* @param {string} skillsDirectory The ~/.claude/skills directory holding the skill.
|
|
711
|
+
* @param {string} backupRoot The run's timestamped backup directory.
|
|
712
|
+
* @param {string} skillName The retired skill directory name to move.
|
|
713
|
+
*/
|
|
714
|
+
function moveRetiredSkillToBackup(skillsDirectory, backupRoot, skillName) {
|
|
715
|
+
const skillPath = join(skillsDirectory, skillName);
|
|
716
|
+
const backupPath = join(backupRoot, skillName);
|
|
717
|
+
try {
|
|
718
|
+
mkdirSync(backupRoot, { recursive: true });
|
|
719
|
+
renameSync(skillPath, backupPath);
|
|
720
|
+
console.log(` ✗ ${join('skills', skillName)} (retired — moved to ${join(PRUNED_SKILLS_BACKUP_DIRECTORY_NAME, basename(backupRoot), skillName)})`);
|
|
721
|
+
} catch (moveError) {
|
|
722
|
+
console.warn(` Warning: could not move retired ${join('skills', skillName)} to backup, leaving in place (${moveError.message})`);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
619
726
|
function install(selectedGroups, options = {}) {
|
|
620
727
|
const isUpdateRefresh = Boolean(options.isUpdateRefresh);
|
|
621
728
|
if (isUpdateRefresh && !selectedGroups && existsSync(MANIFEST_FILE)) {
|
|
@@ -713,6 +820,7 @@ function install(selectedGroups, options = {}) {
|
|
|
713
820
|
let skillsCreated = 0;
|
|
714
821
|
let skillsUpdated = 0;
|
|
715
822
|
const skillPaths = [];
|
|
823
|
+
const installedSkillNames = new Set();
|
|
716
824
|
for (const sourceRoot of allSourceRoots) {
|
|
717
825
|
const skillsSource = join(sourceRoot, 'skills');
|
|
718
826
|
if (!existsSync(skillsSource)) continue;
|
|
@@ -723,6 +831,9 @@ function install(selectedGroups, options = {}) {
|
|
|
723
831
|
skillsCreated += stats.created;
|
|
724
832
|
skillsUpdated += stats.updated;
|
|
725
833
|
skillPaths.push(...stats.paths);
|
|
834
|
+
if (existsSync(join(skillsSource, skillDir.name, SKILL_MANIFEST_FILENAME))) {
|
|
835
|
+
installedSkillNames.add(skillDir.name);
|
|
836
|
+
}
|
|
726
837
|
}
|
|
727
838
|
}
|
|
728
839
|
summary.skills = { created: skillsCreated, updated: skillsUpdated, paths: skillPaths };
|
|
@@ -809,7 +920,21 @@ function install(selectedGroups, options = {}) {
|
|
|
809
920
|
allInstalledFiles.push(claudeHubDest);
|
|
810
921
|
console.log(` \u2713 ${relative(CLAUDE_HOME, claudeHubDest)} (hub)`);
|
|
811
922
|
}
|
|
812
|
-
|
|
923
|
+
const isFullInstall = !selectedGroups;
|
|
924
|
+
const priorManifestSkills = readPriorManifestSkills();
|
|
925
|
+
let manifestSkillNames = priorManifestSkills;
|
|
926
|
+
if (isFullInstall) {
|
|
927
|
+
if (UNRESOLVED_DEPENDENCY_NAMES.length > 0) {
|
|
928
|
+
console.log(
|
|
929
|
+
` Skipping retired-skill prune — unresolved dependency group(s): ${UNRESOLVED_DEPENDENCY_NAMES.join(', ')}. `
|
|
930
|
+
+ 'A skill that migrated to a dependency package would look retired and be moved to backup, so the prune is held until every dependency resolves.',
|
|
931
|
+
);
|
|
932
|
+
} else {
|
|
933
|
+
pruneRetiredSkills(installedSkillNames, priorManifestSkills);
|
|
934
|
+
}
|
|
935
|
+
manifestSkillNames = [...installedSkillNames].sort();
|
|
936
|
+
}
|
|
937
|
+
writeManifest(allInstalledFiles, manifestSkillNames);
|
|
813
938
|
console.log(`\nInstalled ${PACKAGE_NAME}:`);
|
|
814
939
|
for (const directory of CONTENT_DIRECTORIES) {
|
|
815
940
|
if (summary[directory]) {
|