claude-dev-env 1.94.0 → 2.0.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 +41 -33
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +17 -13
- package/_shared/advisor/scripts/model_tier_run_validator.py +27 -18
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +95 -25
- package/_shared/advisor/scripts/tests/test_tier_model_ids.py +17 -17
- package/_shared/advisor/scripts/tier_model_ids.py +18 -18
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/CLAUDE.md +2 -1
- package/_shared/pr-loop/scripts/README.md +1 -0
- package/_shared/pr-loop/scripts/code_rules_gate.py +253 -1980
- package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +32 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/__init__.py +7 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/added_line_maps.py +268 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/enforcer_loading.py +172 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_arguments.py +70 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_running.py +326 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +85 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_file_sets.py +331 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +369 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/conftest.py +14 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_added_line_maps.py +118 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_enforcer_loading.py +17 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_arguments.py +29 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_running.py +99 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_blob_readers.py +69 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_file_sets.py +137 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +116 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_violation_scoping.py +75 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_wrapper_plumb_check.py +49 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/violation_scoping.py +328 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/wrapper_plumb_check.py +206 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +24 -17
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviews_disabled_constants.py +1 -0
- package/_shared/pr-loop/scripts/reviews_disabled.py +19 -2
- package/_shared/pr-loop/scripts/test_code_rules_gate.py +278 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +6 -39
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +43 -0
- package/_shared/pr-loop/worker-spawn.md +186 -0
- package/agents/code-verifier.md +1 -1
- package/bin/CLAUDE.md +10 -1
- package/bin/ever-shipped-skills.mjs +73 -0
- package/bin/expand_home_directory_tokens.mjs +1 -1
- package/bin/install.mjs +140 -7
- package/bin/install.prune.test.mjs +457 -0
- package/docs/CODE_RULES.md +1 -1
- package/hooks/advisory/refactor_guard.py +3 -4
- package/hooks/blocking/CLAUDE.md +7 -1
- package/hooks/blocking/block_main_commit.py +2 -2
- package/hooks/blocking/claude_md_orphan_file_blocker.py +75 -699
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +28 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/orphan_blocker_constants.py +18 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/decision.py +81 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/references.py +307 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/scan_plan.py +124 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/subtree_scan.py +179 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/conftest.py +10 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_decision.py +34 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_references.py +42 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_scan_plan.py +27 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_subtree_scan.py +30 -0
- package/hooks/blocking/code_rules_boolean_mustcheck.py +1 -1
- package/hooks/blocking/code_rules_enforcer.py +4 -0
- package/hooks/blocking/code_rules_mock_completeness.py +1 -1
- package/hooks/blocking/code_rules_optional_params.py +2 -2
- package/hooks/blocking/code_rules_shared.py +83 -1
- package/hooks/blocking/code_rules_test_assertions.py +1 -1
- package/hooks/blocking/code_rules_test_layout.py +9 -3
- package/hooks/blocking/code_rules_typeddict_stub.py +1 -1
- package/hooks/blocking/gh_pr_author_enforcer.py +1 -1
- package/hooks/blocking/inventory_intent_records/CLAUDE.md +26 -0
- package/hooks/blocking/inventory_intent_records/__init__.py +1 -0
- package/hooks/blocking/inventory_intent_records/config/__init__.py +1 -0
- package/hooks/blocking/inventory_intent_records/config/intent_records_constants.py +20 -0
- package/hooks/blocking/inventory_intent_records/records.py +271 -0
- package/hooks/blocking/inventory_intent_records/tests/conftest.py +10 -0
- package/hooks/blocking/inventory_intent_records/tests/test_records.py +80 -0
- package/hooks/blocking/package_inventory_stale_blocker.py +54 -384
- package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +26 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/config/inventory_blocker_constants.py +16 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/decision.py +84 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/inventory_detection.py +307 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/conftest.py +10 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_decision.py +38 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_inventory_detection.py +61 -0
- package/hooks/blocking/pii_payload_scan.py +138 -42
- package/hooks/blocking/pii_prevention_blocker.py +185 -291
- package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +24 -0
- package/hooks/blocking/pii_prevention_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/pii_prevention_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/pii_prevention_blocker_parts/config/repository_resolution_constants.py +28 -0
- package/hooks/blocking/pii_prevention_blocker_parts/repository_exemption.py +214 -0
- package/hooks/blocking/pii_prevention_blocker_parts/repository_resolution.py +208 -0
- package/hooks/blocking/plain_language_blocker.py +138 -4
- package/hooks/blocking/pr_description_command_parser.py +8 -4
- package/hooks/blocking/precommit_code_rules_gate.py +3 -3
- package/hooks/blocking/sensitive_file_protector.py +114 -48
- package/hooks/blocking/tdd_enforcer.py +97 -601
- package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +30 -0
- package/hooks/blocking/tdd_enforcer_parts/__init__.py +1 -0
- package/hooks/blocking/tdd_enforcer_parts/candidate_paths.py +142 -0
- package/hooks/blocking/tdd_enforcer_parts/config/__init__.py +1 -0
- package/hooks/blocking/tdd_enforcer_parts/config/tdd_enforcer_constants.py +32 -0
- package/hooks/blocking/tdd_enforcer_parts/content_analysis.py +268 -0
- package/hooks/blocking/tdd_enforcer_parts/decisions.py +92 -0
- package/hooks/blocking/tdd_enforcer_parts/freshness.py +80 -0
- package/hooks/blocking/tdd_enforcer_parts/git_tracking.py +63 -0
- package/hooks/blocking/tdd_enforcer_parts/path_classification.py +119 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/conftest.py +10 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_candidate_paths.py +31 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_content_analysis.py +30 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_decisions.py +34 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_freshness.py +28 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_git_tracking.py +48 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_path_classification.py +36 -0
- 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_inventory_deadlock_resolution.py +154 -0
- package/hooks/blocking/test_pii_payload_scan.py +168 -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_restore.py +108 -0
- package/hooks/blocking/test_tdd_enforcer_scratchpad.py +105 -0
- package/hooks/blocking/tests/conftest.py +10 -0
- package/hooks/blocking/tests/test_pii_prevention_blocker.py +260 -0
- package/hooks/blocking/tests/test_repository_exemption.py +105 -0
- package/hooks/blocking/tests/test_repository_resolution.py +108 -0
- package/hooks/diagnostic/hook_log_extractor.py +12 -10
- package/hooks/git-hooks/post_commit.py +3 -4
- package/hooks/hooks_constants/CLAUDE.md +4 -2
- package/hooks/hooks_constants/banned_identifiers_constants.py +0 -1
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
- package/hooks/hooks_constants/harness_scratchpad_constants.py +17 -0
- package/hooks/hooks_constants/local_identity.py +59 -8
- package/hooks/hooks_constants/pii_prevention_constants.py +0 -6
- package/hooks/hooks_constants/plain_language_blocker_constants.py +5 -0
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +42 -0
- package/hooks/hooks_constants/test_local_identity.py +105 -3
- package/hooks/pyproject.toml +52 -4
- package/hooks/session/plugin_data_dir_cleanup.py +0 -1
- package/hooks/validation/mypy_validator.py +2 -2
- package/hooks/validators/CLAUDE.md +1 -1
- package/hooks/validators/README.md +2 -0
- package/hooks/validators/health_check.py +1 -0
- package/hooks/validators/mypy_integration.py +2 -0
- package/hooks/validators/python_style_checks.py +114 -136
- package/hooks/validators/python_style_helpers.py +95 -0
- package/hooks/validators/ruff_integration.py +3 -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/hooks/workflow/auto_formatter.py +5 -4
- 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 +4 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +6 -4
- package/scripts/dev_env_scripts_constants/code_review_constants.py +71 -0
- package/scripts/dev_env_scripts_constants/grok_worker_constants.py +435 -0
- package/scripts/dev_env_scripts_constants/timing.py +7 -1
- package/scripts/grok_headless_runner.py +294 -0
- package/scripts/grok_worker_preflight.py +410 -0
- package/scripts/invoke_code_review.py +463 -0
- package/scripts/resolve_worker_spawn.py +619 -0
- package/scripts/spawn_grok_batch.py +672 -0
- package/scripts/test_grok_headless_runner.py +626 -0
- package/scripts/test_grok_worker_preflight.py +1054 -0
- package/scripts/test_invoke_code_review.py +672 -0
- package/scripts/test_resolve_worker_spawn.py +1014 -0
- package/scripts/test_spawn_grok_batch.py +1017 -0
- package/skills/CLAUDE.md +6 -3
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +72 -13
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +121 -14
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +78 -0
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +121 -0
- package/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +196 -6
- package/skills/autoconverge/CLAUDE.md +3 -3
- package/skills/autoconverge/SKILL.md +9 -3
- package/skills/autoconverge/reference/CLAUDE.md +2 -2
- package/skills/autoconverge/reference/convergence.md +33 -11
- package/skills/autoconverge/reference/stop-conditions.md +16 -5
- package/skills/autoconverge/workflow/CLAUDE.md +2 -1
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +7 -2
- package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +300 -0
- package/skills/autoconverge/workflow/converge.contract.test.mjs +5 -5
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +29 -29
- package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.mjs +202 -18
- package/skills/bugteam/CLAUDE.md +2 -2
- package/skills/bugteam/CONSTRAINTS.md +3 -2
- package/skills/bugteam/PROMPTS.md +7 -6
- package/skills/bugteam/SKILL.md +18 -13
- package/skills/bugteam/reference/README.md +2 -3
- package/skills/bugteam/reference/audit-and-teammates.md +215 -35
- package/skills/bugteam/reference/design-rationale.md +1 -1
- package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -1
- package/skills/bugteam/reference/team-setup.md +8 -2
- 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/codex-review/CLAUDE.md +46 -0
- package/skills/codex-review/SKILL.md +181 -0
- package/skills/codex-review/reference/CLAUDE.md +15 -0
- package/skills/codex-review/reference/cli-contract.md +253 -0
- package/skills/codex-review/reference/loop-integration.md +118 -0
- package/skills/codex-review/scripts/codex_down_classifier.py +98 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/__init__.py +1 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/classifier_constants.py +35 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +86 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/findings_constants.py +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +45 -0
- package/skills/codex-review/scripts/codex_usage_probe.py +573 -0
- package/skills/codex-review/scripts/fixtures/auth_failure_synthetic.txt +1 -0
- package/skills/codex-review/scripts/fixtures/config_load_failure_v0.125.0.txt +1 -0
- package/skills/codex-review/scripts/fixtures/freeform_findings_v0.144.3.txt +6 -0
- package/skills/codex-review/scripts/fixtures/model_rejection_v0.125.0.jsonl +5 -0
- package/skills/codex-review/scripts/fixtures/structured_findings.txt +13 -0
- package/skills/codex-review/scripts/fixtures/success_stream_v0.144.3.jsonl +6 -0
- package/skills/codex-review/scripts/fixtures/unknown_failure_synthetic.txt +1 -0
- package/skills/codex-review/scripts/fixtures/usage_limit_synthetic.txt +1 -0
- package/skills/codex-review/scripts/parse_codex_findings.py +207 -0
- package/skills/codex-review/scripts/run_codex_review.py +415 -0
- package/skills/codex-review/scripts/test_codex_down_classifier.py +143 -0
- package/skills/codex-review/scripts/test_codex_usage_probe.py +678 -0
- package/skills/codex-review/scripts/test_parse_codex_findings.py +130 -0
- package/skills/codex-review/scripts/test_run_codex_review.py +812 -0
- package/skills/codex-review/test_skill_scaffold.py +192 -0
- package/skills/grok-spawn/CLAUDE.md +28 -0
- package/skills/grok-spawn/SKILL.md +226 -0
- package/skills/grok-spawn/reference/flag-profiles.md +132 -0
- package/skills/grok-spawn/reference/worker-briefs.md +152 -0
- package/skills/grokify/SKILL.md +9 -1
- package/skills/grokify/capability-claims.test.mjs +28 -0
- package/skills/grokify/evals/README.md +72 -0
- package/skills/grokify/evals/parse-payload.test.mjs +171 -0
- package/skills/grokify/evals/run-capability-evals.mjs +545 -0
- package/skills/orchestrator/SKILL.md +32 -22
- package/skills/orchestrator-refresh/SKILL.md +12 -8
- package/skills/pr-converge/CLAUDE.md +1 -1
- package/skills/pr-converge/SKILL.md +34 -13
- package/skills/pr-converge/reference/convergence-gates.md +42 -15
- package/skills/pr-converge/reference/fix-protocol.md +1 -1
- package/skills/pr-converge/reference/ground-rules.md +1 -1
- package/skills/pr-converge/reference/per-tick.md +130 -42
- package/skills/pr-converge/reference/state-schema.md +10 -0
- package/skills/pr-converge/scripts/CLAUDE.md +2 -0
- package/skills/pr-converge/scripts/_pr_converge_path_setup.py +5 -1
- package/skills/pr-converge/scripts/check_convergence.py +605 -29
- package/skills/pr-converge/scripts/check_convergence_availability.py +232 -0
- package/skills/pr-converge/scripts/check_convergence_gates.py +279 -235
- package/skills/pr-converge/scripts/check_convergence_thread_gates.py +1 -1
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -2
- package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +4 -0
- package/skills/pr-converge/scripts/test_check_convergence.py +71 -3
- package/skills/pr-converge/scripts/test_check_convergence_availability.py +326 -0
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -0
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +89 -17
- package/skills/pr-converge/scripts/test_check_convergence_fixture.py +179 -0
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -68
- package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +24 -0
- package/skills/pr-converge/test_step5_host_branch.py +106 -0
- package/skills/pr-fix-protocol/SKILL.md +65 -0
- package/skills/pr-loop-cloud-transport/SKILL.md +2 -0
- package/skills/reviewer-gates/SKILL.md +7 -5
- package/skills/skill-builder/references/skill-modularity.md +1 -1
- package/skills/team-advisor/SKILL.md +16 -12
- 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/agents/code-verifier.md
CHANGED
|
@@ -33,7 +33,7 @@ On Windows the same file sits at %USERPROFILE%\.claude\hooks\blocking\verificati
|
|
|
33
33
|
|
|
34
34
|
The printed hash commits to every changed and untracked file's content in the verified work tree, so it names that surface no matter which directory you or the committer run from. If the CLI prints an empty-surface or wrong-work-tree error and no hash, you are pointed at a work tree with no changes versus origin/main — re-run with the branch mode to locate the correct work tree.
|
|
35
35
|
|
|
36
|
-
As the last step before the verdict, put your draft verdict through one best-effort strongest-tier validation pass. Spawn a single validation subagent through the Task tool as the `Explore` agent type at the strongest reachable tier: set the Task `subagent_type` to `Explore`, detect the host profile first per `~/.claude/_shared/advisor/advisor-protocol.md` — the source of truth for host detection, the ladder, and its aliases — then on a Claude host pick the strongest reachable tier on the Fable → Opus → Sonnet → Haiku ladder and on a
|
|
36
|
+
As the last step before the verdict, put your draft verdict through one best-effort strongest-tier validation pass. Spawn a single validation subagent through the Task tool as the `Explore` agent type at the strongest reachable tier: set the Task `subagent_type` to `Explore`, detect the host profile first per `~/.claude/_shared/advisor/advisor-protocol.md` — the source of truth for host detection, the ladder, and its aliases — then on a Claude host pick the strongest reachable tier on the Fable → Opus → Sonnet → Haiku ladder and on a third-party host use the single third-party tier, and set the Task `model:` field to that tier's alias. The `Explore` type carries no Edit or Write tools and cannot spawn further agents, so the harness itself holds the validator to the no-edit, no-spawn contract the next paragraph names. Hand it the draft verdict together with your evidence — every command you ran with its output, and your two-way diff-to-task mapping — and state that its task is adversarial verification of that supplied draft verdict: refute it against the supplied evidence rather than discover code, naming any gate you misread, any task item you mapped wrong, or any finding that does not hold. When the spawn is unavailable — a Task tool error, an unreachable tier at every rung, or this subagent being barred from spawning further agents — skip the validation pass and emit the draft verdict as it stands, noting the skip in your final message; a spawn failure never blocks the verdict fence from being emitted.
|
|
37
37
|
|
|
38
38
|
This validation pass is terminal: the `Explore` type gives the validation subagent no way to spawn a further agent or edit a file, so it answers with prose only. When it refutes any part, re-check that part yourself against the commands and the diff, and correct the verdict before you emit it. When it refutes nothing, the draft verdict stands. Then emit the fenced verdict block below — it stays the last thing in your message so the verifier_verdict_minter hook reads it.
|
|
39
39
|
|
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 |
|
|
@@ -0,0 +1,73 @@
|
|
|
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
|
+
'closeout',
|
|
28
|
+
'code',
|
|
29
|
+
'codex-review',
|
|
30
|
+
'copilot-finding-triage',
|
|
31
|
+
'copilot-review',
|
|
32
|
+
'deep-research',
|
|
33
|
+
'everything-search',
|
|
34
|
+
'findbugs',
|
|
35
|
+
'fixbugs',
|
|
36
|
+
'fresh-branch',
|
|
37
|
+
'gh-paginate',
|
|
38
|
+
'gotcha',
|
|
39
|
+
'grok-spawn',
|
|
40
|
+
'grokify',
|
|
41
|
+
'implement',
|
|
42
|
+
'log-audit',
|
|
43
|
+
'logifix',
|
|
44
|
+
'monitor-open-prs',
|
|
45
|
+
'orchestrator',
|
|
46
|
+
'orchestrator-refresh',
|
|
47
|
+
'post-audit-findings',
|
|
48
|
+
'pr-consistency-audit',
|
|
49
|
+
'pr-converge',
|
|
50
|
+
'pr-fix-protocol',
|
|
51
|
+
'pr-loop-cloud-transport',
|
|
52
|
+
'pr-loop-lifecycle',
|
|
53
|
+
'pr-review-responder',
|
|
54
|
+
'pr-scope-resolve',
|
|
55
|
+
'pre-compact',
|
|
56
|
+
'privacy-hygiene',
|
|
57
|
+
'qbug',
|
|
58
|
+
'rebase',
|
|
59
|
+
'recall',
|
|
60
|
+
'refine',
|
|
61
|
+
'remember',
|
|
62
|
+
'research-mode',
|
|
63
|
+
'reviewer-gates',
|
|
64
|
+
'session-log',
|
|
65
|
+
'session-tidy',
|
|
66
|
+
'skill-builder',
|
|
67
|
+
'structure-prompt',
|
|
68
|
+
'task-build',
|
|
69
|
+
'team-advisor',
|
|
70
|
+
'update',
|
|
71
|
+
'usage-pause',
|
|
72
|
+
'verified-build',
|
|
73
|
+
]);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Install-time expansion of $HOME / ${HOME} / ~/ in settings.json commands.
|
|
3
3
|
*
|
|
4
|
-
* Hosts that require referenced env vars at hook load time (for example
|
|
4
|
+
* Hosts that require referenced env vars at hook load time (for example a third-party host)
|
|
5
5
|
* skip the hook when HOME is unset — common on Windows — so the installer
|
|
6
6
|
* rewrites residual tokens to absolute home paths.
|
|
7
7
|
*/
|
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,14 +166,18 @@ 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
179
|
'orchestrator', 'orchestrator-refresh', 'team-advisor', 'grokify',
|
|
180
|
+
'grok-spawn',
|
|
156
181
|
'anthropic-plan', 'everything-search',
|
|
157
182
|
'privacy-hygiene',
|
|
158
183
|
'recall', 'remember', 'task-build'
|
|
@@ -164,7 +189,7 @@ const INSTALL_GROUPS = {
|
|
|
164
189
|
description: 'Session logging and memory',
|
|
165
190
|
skills: ['session-log', 'session-tidy'],
|
|
166
191
|
},
|
|
167
|
-
...
|
|
192
|
+
...dependencyDiscovery.groups,
|
|
168
193
|
};
|
|
169
194
|
|
|
170
195
|
/**
|
|
@@ -511,7 +536,7 @@ function pruneManagedHooksFromEvent(settings, eventType, managedHookRelativePath
|
|
|
511
536
|
* a new matcher group does not double-run. User-authored hooks are preserved as
|
|
512
537
|
* entries, but residual $HOME / ${HOME} / ~/ tokens in every hook and statusLine
|
|
513
538
|
* command are expanded to absolute home paths so hosts that require referenced
|
|
514
|
-
* env vars at load time (
|
|
539
|
+
* env vars at load time (a third-party host on Windows) can execute them.
|
|
515
540
|
*
|
|
516
541
|
* @param {object} settings The parsed settings.json object (mutated in place).
|
|
517
542
|
* @param {{hooks: object}} hooksConfig Parsed hooks.json.
|
|
@@ -606,11 +631,99 @@ function mergeHooks(hooksSourceRoot, pythonCommand) {
|
|
|
606
631
|
return groupCount;
|
|
607
632
|
}
|
|
608
633
|
|
|
609
|
-
function writeManifest(installedFiles) {
|
|
634
|
+
function writeManifest(installedFiles, skillNames) {
|
|
610
635
|
const manifest = { package: PACKAGE_NAME, version: PACKAGE_VERSION, installedAt: new Date().toISOString(), files: installedFiles };
|
|
636
|
+
if (skillNames) {
|
|
637
|
+
manifest.skills = skillNames;
|
|
638
|
+
}
|
|
611
639
|
writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2) + '\n');
|
|
612
640
|
}
|
|
613
641
|
|
|
642
|
+
/**
|
|
643
|
+
* Read the skill directory names the previous install recorded.
|
|
644
|
+
*
|
|
645
|
+
* Returns null when no manifest exists or the manifest predates the skills key,
|
|
646
|
+
* so a caller can treat "unknown prior skills" the same in both cases and lean
|
|
647
|
+
* on the ever-shipped set to find retired skills.
|
|
648
|
+
*
|
|
649
|
+
* @returns {string[]|null} The prior manifest's skill names, or null when absent.
|
|
650
|
+
*/
|
|
651
|
+
function readPriorManifestSkills() {
|
|
652
|
+
if (!existsSync(MANIFEST_FILE)) return null;
|
|
653
|
+
try {
|
|
654
|
+
const priorManifest = JSON.parse(readFileSync(MANIFEST_FILE, 'utf8'));
|
|
655
|
+
return Array.isArray(priorManifest.skills) ? priorManifest.skills : null;
|
|
656
|
+
} catch {
|
|
657
|
+
return null;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Move retired skill directories a prior install left under ~/.claude/skills into
|
|
663
|
+
* a timestamped backup directory rather than deleting them.
|
|
664
|
+
*
|
|
665
|
+
* A directory is pruned only when the package no longer ships it and either the
|
|
666
|
+
* prior manifest recorded it or the package once shipped it. Subtracting the
|
|
667
|
+
* just-installed set from the ever-shipped set at call time means restoring a
|
|
668
|
+
* skill to the package protects it — it re-enters the installed set and never
|
|
669
|
+
* counts as retired. Matching is by directory name alone, so a personal
|
|
670
|
+
* directory a user authored under a name that collides with a retired skill
|
|
671
|
+
* (for example `code`, `implement`, `refine`, `gotcha`, `caveman`) is treated as
|
|
672
|
+
* that retired skill and moved to backup; only a name in neither set, and
|
|
673
|
+
* ~/.claude/skills/_shared, are left in place.
|
|
674
|
+
*
|
|
675
|
+
* Each pruned directory is renamed to
|
|
676
|
+
* ~/.claude/.claude-dev-env-pruned/<timestamp>/<skill-name>/ — a backup root
|
|
677
|
+
* outside ~/.claude/skills, so a moved directory is never re-discovered as a
|
|
678
|
+
* skill — under one shared timestamp per run. A backup is never cleaned up, so a
|
|
679
|
+
* user can recover a wrongly-matched directory. One directory whose rename fails
|
|
680
|
+
* (for example a read-only file or a cross-device move) is logged and left in
|
|
681
|
+
* place, never deleted, so a prune failure costs at most a cosmetic leftover.
|
|
682
|
+
*
|
|
683
|
+
* @param {Set<string>} installedSkillNames Skill names this install just wrote.
|
|
684
|
+
* @param {string[]|null} priorManifestSkills The prior manifest's skill names, or null.
|
|
685
|
+
*/
|
|
686
|
+
function pruneRetiredSkills(installedSkillNames, priorManifestSkills) {
|
|
687
|
+
const skillsDirectory = join(CLAUDE_HOME, 'skills');
|
|
688
|
+
if (!existsSync(skillsDirectory)) return;
|
|
689
|
+
const retiredSkillNames = new Set(
|
|
690
|
+
[...EVER_SHIPPED_SKILL_NAMES].filter(skillName => !installedSkillNames.has(skillName))
|
|
691
|
+
);
|
|
692
|
+
const priorSkillNames = new Set(priorManifestSkills || []);
|
|
693
|
+
const runTimestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
694
|
+
const backupRoot = join(CLAUDE_HOME, PRUNED_SKILLS_BACKUP_DIRECTORY_NAME, runTimestamp);
|
|
695
|
+
const existingSkillDirs = readdirSync(skillsDirectory, { withFileTypes: true })
|
|
696
|
+
.filter(entry => entry.isDirectory());
|
|
697
|
+
for (const skillDir of existingSkillDirs) {
|
|
698
|
+
const skillName = skillDir.name;
|
|
699
|
+
if (NEVER_PRUNED_SKILL_DIRECTORIES.has(skillName)) continue;
|
|
700
|
+
if (installedSkillNames.has(skillName)) continue;
|
|
701
|
+
const isPruneCandidate = priorSkillNames.has(skillName) || retiredSkillNames.has(skillName);
|
|
702
|
+
if (!isPruneCandidate) continue;
|
|
703
|
+
moveRetiredSkillToBackup(skillsDirectory, backupRoot, skillName);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Move one retired skill directory into the run's backup root, leaving it in
|
|
709
|
+
* place when the move fails.
|
|
710
|
+
*
|
|
711
|
+
* @param {string} skillsDirectory The ~/.claude/skills directory holding the skill.
|
|
712
|
+
* @param {string} backupRoot The run's timestamped backup directory.
|
|
713
|
+
* @param {string} skillName The retired skill directory name to move.
|
|
714
|
+
*/
|
|
715
|
+
function moveRetiredSkillToBackup(skillsDirectory, backupRoot, skillName) {
|
|
716
|
+
const skillPath = join(skillsDirectory, skillName);
|
|
717
|
+
const backupPath = join(backupRoot, skillName);
|
|
718
|
+
try {
|
|
719
|
+
mkdirSync(backupRoot, { recursive: true });
|
|
720
|
+
renameSync(skillPath, backupPath);
|
|
721
|
+
console.log(` ✗ ${join('skills', skillName)} (retired — moved to ${join(PRUNED_SKILLS_BACKUP_DIRECTORY_NAME, basename(backupRoot), skillName)})`);
|
|
722
|
+
} catch (moveError) {
|
|
723
|
+
console.warn(` Warning: could not move retired ${join('skills', skillName)} to backup, leaving in place (${moveError.message})`);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
614
727
|
function install(selectedGroups, options = {}) {
|
|
615
728
|
const isUpdateRefresh = Boolean(options.isUpdateRefresh);
|
|
616
729
|
if (isUpdateRefresh && !selectedGroups && existsSync(MANIFEST_FILE)) {
|
|
@@ -708,6 +821,8 @@ function install(selectedGroups, options = {}) {
|
|
|
708
821
|
let skillsCreated = 0;
|
|
709
822
|
let skillsUpdated = 0;
|
|
710
823
|
const skillPaths = [];
|
|
824
|
+
const installedSkillNames = new Set();
|
|
825
|
+
const copiedSkillNames = new Set();
|
|
711
826
|
for (const sourceRoot of allSourceRoots) {
|
|
712
827
|
const skillsSource = join(sourceRoot, 'skills');
|
|
713
828
|
if (!existsSync(skillsSource)) continue;
|
|
@@ -718,6 +833,10 @@ function install(selectedGroups, options = {}) {
|
|
|
718
833
|
skillsCreated += stats.created;
|
|
719
834
|
skillsUpdated += stats.updated;
|
|
720
835
|
skillPaths.push(...stats.paths);
|
|
836
|
+
copiedSkillNames.add(skillDir.name);
|
|
837
|
+
if (existsSync(join(skillsSource, skillDir.name, SKILL_MANIFEST_FILENAME))) {
|
|
838
|
+
installedSkillNames.add(skillDir.name);
|
|
839
|
+
}
|
|
721
840
|
}
|
|
722
841
|
}
|
|
723
842
|
summary.skills = { created: skillsCreated, updated: skillsUpdated, paths: skillPaths };
|
|
@@ -804,7 +923,21 @@ function install(selectedGroups, options = {}) {
|
|
|
804
923
|
allInstalledFiles.push(claudeHubDest);
|
|
805
924
|
console.log(` \u2713 ${relative(CLAUDE_HOME, claudeHubDest)} (hub)`);
|
|
806
925
|
}
|
|
807
|
-
|
|
926
|
+
const isFullInstall = !selectedGroups;
|
|
927
|
+
const priorManifestSkills = readPriorManifestSkills();
|
|
928
|
+
let manifestSkillNames = priorManifestSkills;
|
|
929
|
+
if (isFullInstall) {
|
|
930
|
+
if (UNRESOLVED_DEPENDENCY_NAMES.length > 0) {
|
|
931
|
+
console.log(
|
|
932
|
+
` Skipping retired-skill prune — unresolved dependency group(s): ${UNRESOLVED_DEPENDENCY_NAMES.join(', ')}. `
|
|
933
|
+
+ '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.',
|
|
934
|
+
);
|
|
935
|
+
} else {
|
|
936
|
+
pruneRetiredSkills(copiedSkillNames, priorManifestSkills);
|
|
937
|
+
}
|
|
938
|
+
manifestSkillNames = [...installedSkillNames].sort();
|
|
939
|
+
}
|
|
940
|
+
writeManifest(allInstalledFiles, manifestSkillNames);
|
|
808
941
|
console.log(`\nInstalled ${PACKAGE_NAME}:`);
|
|
809
942
|
for (const directory of CONTENT_DIRECTORIES) {
|
|
810
943
|
if (summary[directory]) {
|