claude-dev-env 4.3.0 → 7.1.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/pr-description-writer.md +2 -2
- package/.agents/agents/test_agent_frontmatter.py +2 -2
- package/.agents/agents/test_pr_description_writer_style.py +1 -3
- package/.agents/skills/e-code-review/SKILL.md +12 -12
- package/.agents/skills/e-code-review/preflight-proposal.contract.test.mjs +20 -0
- package/.agents/skills/e-code-review/reference/fix.md +15 -17
- package/.agents/skills/e-code-review/reference/loop.md +35 -39
- package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +2 -1
- package/.agents/skills/e-code-review/reference/preflight-proposal.md +44 -9
- package/.agents/skills/e-code-review/reference/runner-selection.md +13 -14
- package/.agents/skills/issue-tracker/SKILL.md +5 -4
- package/.agents/skills/pull-request/SKILL.md +11 -13
- package/.agents/skills/pull-request/reference/publication-tasks.md +3 -3
- package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +9 -1
- package/.agents/skills/pull-request/scripts/pull_request.py +35 -44
- package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +9 -0
- package/.agents/skills/pull-request/scripts/test_pull_request.py +35 -3
- package/.agents/skills/session-continuity/README.md +189 -0
- package/.agents/skills/session-continuity/SKILL.md +114 -0
- package/.agents/skills/session-continuity/continuity.mjs +375 -0
- package/.agents/skills/session-continuity/continuity.test.mjs +303 -0
- package/.agents/skills/skill-builder/SKILL.md +3 -196
- package/.agents/skills/syncing-submodules/scripts/test_sync_parent_pointer.py +0 -7
- package/.agents/skills/windows-scheduled-task/SKILL.md +153 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
- package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
- package/bin/cde.mjs +36 -8
- package/bin/cde.test.mjs +48 -0
- package/bin/ever-shipped-skills.mjs +2 -20
- package/bin/ever-shipped-skills.test.mjs +8 -0
- package/bin/expand_home_directory_tokens.mjs +4 -5
- package/bin/expand_home_directory_tokens.test.mjs +128 -0
- package/bin/git_hooks_installer.mjs +22 -3
- package/bin/git_hooks_installer.test.mjs +159 -5
- package/bin/install-session-continuity.mjs +78 -0
- package/bin/install-session-continuity.test.mjs +42 -0
- package/bin/install.agents-home.test.mjs +15 -13
- package/bin/install.codex-rules.test.mjs +5 -2
- package/bin/install.cursor-rules.test.mjs +5 -2
- package/bin/install.mjs +506 -63
- package/bin/install.policy-lint.test.mjs +130 -0
- package/bin/install.profiles.test.mjs +10 -0
- package/bin/install.prune.test.mjs +286 -37
- package/bin/install.pstack-plugin.test.mjs +114 -0
- package/bin/install.test.mjs +537 -120
- package/bin/install.transaction.test.mjs +3 -3
- package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
- package/docs/CODE_RULES.md +1 -1
- package/hooks/advisory/pr_done_reminder.py +342 -0
- package/hooks/advisory/test_pr_done_reminder.py +249 -0
- package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
- package/hooks/blocking/code_rules_banned_identifiers.py +3 -0
- package/hooks/blocking/code_rules_paired_test.py +22 -15
- package/hooks/blocking/precommit_code_rules_gate.py +5 -8
- package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +7 -52
- package/hooks/blocking/test_bash_post_call_dispatcher.py +65 -4
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -69
- package/hooks/blocking/test_code_rules_banned_identifiers.py +64 -0
- package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
- package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
- package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +43 -342
- package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
- package/hooks/blocking/test_shared_stdin_adoption.py +1 -47
- package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
- package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +116 -0
- package/hooks/git-hooks/post_commit.py +33 -0
- package/hooks/git-hooks/pre_commit.py +18 -49
- package/hooks/git-hooks/pre_push.py +45 -541
- package/hooks/git-hooks/test_installed_policy_lint.py +208 -0
- package/hooks/git-hooks/test_native_hook_support.py +129 -0
- package/hooks/git-hooks/test_native_policy_commit.py +120 -0
- package/hooks/git-hooks/test_post_commit.py +43 -0
- package/hooks/git-hooks/test_pre_commit.py +38 -148
- package/hooks/git-hooks/test_pre_push.py +48 -1631
- package/hooks/git-hooks/test_verification_notice.py +60 -0
- package/hooks/git-hooks/test_verification_start.py +173 -0
- package/hooks/git-hooks/verification_notice.py +192 -0
- package/hooks/git-hooks/verification_notice_context.py +253 -0
- package/hooks/git-hooks/verification_notice_state.py +237 -0
- package/hooks/git-hooks/verification_start.py +100 -0
- package/hooks/hooks.json +1 -120
- package/hooks/hooks_constants/banned_identifiers_constants.py +4 -0
- package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -1
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -15
- package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -4
- package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -93
- package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -4
- package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +19 -2
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +3 -66
- package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +46 -201
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +3 -25
- package/hooks/validation/test_post_tool_use_dispatcher.py +35 -298
- package/hooks/validators/run_all_validators.py +5 -1
- package/hooks/validators/security_checks.py +57 -2
- package/hooks/validators/test_hook_timing_harness.py +2 -5
- package/hooks/validators/test_run_all_validators_mutation_tool_parity.py +3 -11
- package/hooks/validators/test_run_all_validators_policy_lint.py +36 -0
- package/hooks/validators/test_security_checks.py +57 -0
- package/package.json +2 -2
- package/rules/confirm-implementation-forks.md +2 -0
- package/rules/durable-post-artifacts.md +2 -1
- package/rules/git-workflow.md +18 -6
- package/rules/pstack-host-mapping.md +48 -0
- package/rules/vault-context.md +3 -5
- package/rules/verify-runtime-state.md +7 -0
- package/scripts/audit-hooks-cli.mjs +5 -0
- package/scripts/audit-hooks.mjs +72 -16
- package/scripts/automatic_advisory/__init__.py +1 -0
- package/scripts/automatic_advisory/checkout.py +51 -0
- package/scripts/automatic_advisory/cli.py +334 -0
- package/scripts/automatic_advisory/config/__init__.py +1 -0
- package/scripts/automatic_advisory/config/constants.py +93 -0
- package/scripts/automatic_advisory/config/timing.py +4 -0
- package/scripts/automatic_advisory/configuration.py +197 -0
- package/scripts/automatic_advisory/execution.py +350 -0
- package/scripts/automatic_advisory/git.py +69 -0
- package/scripts/automatic_advisory/model.py +107 -0
- package/scripts/automatic_advisory/process_host.py +32 -0
- package/scripts/automatic_advisory/publisher.py +84 -0
- package/scripts/automatic_advisory/runner.py +399 -0
- package/scripts/automatic_advisory/state.py +116 -0
- package/scripts/automatic_advisory/window_flags.py +41 -0
- package/scripts/automatic_advisory/windows_job.py +122 -0
- package/scripts/check.ps1 +0 -1
- package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
- package/scripts/ci/installer-lifecycle.test.mjs +26 -0
- package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
- package/scripts/codex_compat_materializer.py +99 -13
- package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
- package/{.agents/skills/_shared/pr-loop/scripts → scripts}/durable_post_lint.py +35 -43
- package/{.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/durable_post_lint_constants.py → scripts/durable_post_lint_config/config/constants.py} +10 -10
- package/{.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint_constants.py → scripts/durable_post_lint_config/config/test_constants.py} +14 -1
- package/scripts/local_report_cli.py +184 -0
- package/scripts/local_report_core.py +247 -0
- package/scripts/local_report_publisher.py +12 -0
- package/scripts/local_report_validation.py +357 -0
- package/scripts/local_verification/__init__.py +23 -0
- package/scripts/local_verification/check_runner.py +364 -0
- package/scripts/local_verification/check_support.py +99 -0
- package/scripts/local_verification/cli.py +158 -0
- package/scripts/local_verification/command_runner.py +84 -0
- package/scripts/local_verification/config/__init__.py +1 -0
- package/scripts/local_verification/config/constants.py +89 -0
- package/scripts/local_verification/config/timing.py +1 -0
- package/scripts/local_verification/git_state.py +146 -0
- package/scripts/local_verification/manifest.py +224 -0
- package/scripts/local_verification/model.py +174 -0
- package/scripts/local_verification/runner.py +234 -0
- package/scripts/policy_lint/adapter_detectors.py +13 -6
- package/scripts/policy_lint/adapter_pairing.py +58 -3
- package/scripts/policy_lint/adapters.py +7 -4
- package/scripts/policy_lint/config/approved_test_pairs.py +323 -0
- package/scripts/policy_lint/config/constants.py +5 -0
- package/scripts/policy_lint/engine.py +48 -4
- package/scripts/policy_lint/model.py +1 -0
- package/scripts/policy_lint/registry.py +1 -1
- package/scripts/policy_lint/selection.py +2 -1
- package/scripts/policy_lint/selection_git.py +2 -0
- package/scripts/pr_verification/__init__.py +1 -0
- package/scripts/pr_verification/branch_refs.py +114 -0
- package/scripts/pr_verification/config/__init__.py +1 -0
- package/scripts/pr_verification/config/constants.py +84 -0
- package/scripts/pr_verification/config/timing.py +5 -0
- package/scripts/pr_verification/github.py +137 -0
- package/scripts/pr_verification/github_api.py +376 -0
- package/scripts/pr_verification/github_labels.py +88 -0
- package/scripts/pr_verification/github_parsing.py +182 -0
- package/scripts/pr_verification/github_transport.py +91 -0
- package/scripts/pr_verification/lock.py +122 -0
- package/scripts/pr_verification/model.py +43 -0
- package/scripts/pr_verification/requirements.txt +1 -0
- package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
- package/scripts/shared_tree_paths.py +32 -3
- package/{.agents/skills/_shared/pr-loop/scripts → scripts}/test_durable_post_lint.py +97 -0
- package/scripts/test_shared_tree_paths.py +33 -0
- package/scripts/tests/test-audit-hooks-cli.mjs +150 -0
- package/scripts/tests/test-audit-hooks.mjs +94 -2
- package/scripts/tests/test_automatic_advisory.py +376 -0
- package/scripts/tests/test_automatic_advisory_job_cleanup.py +71 -0
- package/scripts/tests/test_automatic_advisory_polling.py +216 -0
- package/scripts/tests/test_automatic_advisory_recovery.py +320 -0
- package/scripts/tests/test_automatic_advisory_repairs.py +400 -0
- package/scripts/tests/test_automatic_advisory_settings.py +48 -0
- package/scripts/tests/test_automatic_advisory_window_flags.py +163 -0
- package/scripts/tests/test_closed_pr_label.py +157 -0
- package/scripts/tests/test_codex_compat_materializer.py +189 -6
- package/scripts/tests/test_github_labels.py +101 -0
- package/scripts/tests/test_github_transport.py +125 -0
- package/scripts/tests/test_local_report_publisher.py +396 -0
- package/scripts/tests/test_local_report_publisher_cli.py +85 -0
- package/scripts/tests/test_local_verification.py +348 -0
- package/scripts/tests/test_local_verification_cli.py +98 -0
- package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
- package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
- package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
- package/scripts/tests/test_policy_lint_rules_pairing.py +163 -0
- package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
- package/scripts/tests/test_policy_lint_selection.py +25 -2
- package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
- package/scripts/tests/test_pr_verification_github.py +392 -0
- package/scripts/tests/test_pr_verification_lock.py +168 -0
- package/scripts/tests/test_rules.py +3 -7
- package/scripts/tests/test_verification_notice.py +347 -0
- package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
- package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
- package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
- package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
- package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
- package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
- package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
- package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
- package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
- package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
- package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
- package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
- package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
- package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
- package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
- package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
- package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
- package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
- package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
- package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
- package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
- package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
- package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
- package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
- package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
- package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
- package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
- package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
- package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
- package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
- package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
- package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
- package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
- package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
- package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
- package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
- package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
- package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
- package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
- package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
- package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/SKILL.md +0 -431
- package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
- package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
- package/.agents/skills/autoconverge/reference/convergence.md +0 -219
- package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
- package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
- package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
- package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
- package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
- package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
- package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
- package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
- package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
- package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
- package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
- package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
- package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
- package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
- package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
- package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
- package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
- package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
- package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
- package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
- package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
- package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
- package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
- package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
- package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
- package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
- package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
- package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
- package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
- package/.agents/skills/hitl/SKILL.md +0 -45
- package/.agents/skills/pr-cleanup/SKILL.md +0 -140
- package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
- package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
- package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
- package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
- package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
- package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
- package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
- package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
- package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
- package/.agents/skills/pr-refinement/SKILL.md +0 -31
- package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
- package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
- package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
- package/.agents/skills/pr-small-cl/SKILL.md +0 -52
- package/.agents/skills/pr-title-description/SKILL.md +0 -96
- package/.agents/skills/prototype/SKILL.md +0 -85
- package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
- package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
- package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
- package/.agents/skills/prototype/scripts/conftest.py +0 -15
- package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
- package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
- package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
- package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
- package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
- package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
- package/.agents/skills/prototype/workflows/promotion.md +0 -25
- package/.agents/skills/prototype/workflows/sandbox.md +0 -35
- package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
- package/.agents/skills/rebase/AGENTS.md +0 -1
- package/.agents/skills/rebase/SKILL.md +0 -178
- package/.agents/skills/review-router/SKILL.md +0 -51
- package/.agents/skills/review-router/reference/route-policy.json +0 -7
- package/.agents/skills/review-router/reference/task-seeds.md +0 -1
- package/.agents/skills/review-router/scripts/conftest.py +0 -4
- package/.agents/skills/review-router/scripts/review_router.py +0 -50
- package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
- package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
- package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
- package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
- package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
- package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
- package/.agents/skills/review-tier/SKILL.md +0 -56
- package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
- package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
- package/.agents/skills/review-tier/scripts/conftest.py +0 -4
- package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
- package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
- package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
- package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
- package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
- package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
- package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
- package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
- package/.agents/skills/session-log/AGENTS.md +0 -1
- package/.agents/skills/session-log/SKILL.md +0 -170
- package/.agents/skills/session-log/templates/frontmatter.md +0 -40
- package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
- package/.agents/skills/session-tidy/AGENTS.md +0 -1
- package/.agents/skills/session-tidy/SKILL.md +0 -191
- package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
- package/.agents/skills/skill-builder/references/description-field.md +0 -111
- package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
- package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
- package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
- package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
- package/.agents/skills/skill-builder/references/skill-types.md +0 -232
- package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
- package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
- package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
- package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
- package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
- package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
- package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
- package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
- package/.agents/skills/update/.claude/CLAUDE.md +0 -1
- package/.agents/skills/update/AGENTS.md +0 -1
- package/.agents/skills/update/SKILL.md +0 -190
- package/_shared/pr-loop/AGENTS.md +0 -1
- package/_shared/pr-loop/audit-contract.md +0 -183
- package/_shared/pr-loop/audit-reply-template.md +0 -151
- package/_shared/pr-loop/code-rules-gate.md +0 -65
- package/_shared/pr-loop/fix-protocol.md +0 -60
- package/_shared/pr-loop/gh-payloads.md +0 -75
- package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
- package/_shared/pr-loop/precatch-rubric.md +0 -66
- package/_shared/pr-loop/preflight-proposal.md +0 -56
- package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
- package/_shared/pr-loop/state-schema.md +0 -45
- package/_shared/pr-loop/worker-spawn.md +0 -189
- package/hooks/blocking/ask_user_question_shape_blocker.py +0 -83
- package/hooks/blocking/bot_mention_comment_blocker.py +0 -76
- package/hooks/blocking/claude_md_orphan_file_blocker.py +0 -129
- package/hooks/blocking/cursor_cli_python_misfire_blocker.py +0 -108
- package/hooks/blocking/destructive_command_blocker.py +0 -2073
- package/hooks/blocking/fable_spawn_gate.py +0 -187
- package/hooks/blocking/luna_fast_mode_gate.py +0 -160
- package/hooks/blocking/nas_ssh_binary_enforcer.py +0 -163
- package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +0 -260
- package/hooks/blocking/package_inventory_stale_blocker.py +0 -107
- package/hooks/blocking/pii_prevention_blocker.py +0 -365
- package/hooks/blocking/piped_pytest_blocker.py +0 -1223
- package/hooks/blocking/question_to_user_enforcer.py +0 -130
- package/hooks/blocking/send_user_file_open_locally_blocker.py +0 -71
- package/hooks/blocking/sensitive_file_protector.py +0 -167
- package/hooks/blocking/session_edit_stage_gate.py +0 -684
- package/hooks/blocking/session_handoff_blocker.py +0 -182
- package/hooks/blocking/shell_substitution_blocker.py +0 -350
- package/hooks/blocking/test_ask_user_question_shape.py +0 -69
- package/hooks/blocking/test_bot_mention_comment_blocker.py +0 -131
- package/hooks/blocking/test_claude_md_orphan_file_blocker.py +0 -1277
- package/hooks/blocking/test_cursor_cli_python_misfire_blocker.py +0 -208
- package/hooks/blocking/test_destructive_command_blocker.py +0 -2024
- package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +0 -105
- package/hooks/blocking/test_destructive_command_blocker_no_verify.py +0 -122
- package/hooks/blocking/test_destructive_command_blocker_patterns.py +0 -133
- package/hooks/blocking/test_fable_spawn_gate.py +0 -381
- package/hooks/blocking/test_inventory_deadlock_resolution.py +0 -154
- package/hooks/blocking/test_luna_fast_mode_gate.py +0 -211
- package/hooks/blocking/test_nas_ssh_binary_enforcer.py +0 -225
- package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +0 -231
- package/hooks/blocking/test_package_inventory_stale_blocker.py +0 -382
- package/hooks/blocking/test_pii_prevention_blocker.py +0 -1222
- package/hooks/blocking/test_pii_prevention_windows_git_path.py +0 -41
- package/hooks/blocking/test_pii_write_surface_ephemeral.py +0 -221
- package/hooks/blocking/test_piped_pytest_blocker.py +0 -587
- package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -233
- package/hooks/blocking/test_question_to_user_enforcer.py +0 -169
- package/hooks/blocking/test_send_user_file_open_locally_blocker.py +0 -118
- package/hooks/blocking/test_sensitive_file_protector.py +0 -365
- package/hooks/blocking/test_session_edit_stage_gate.py +0 -537
- package/hooks/blocking/test_session_edit_stage_gate_staging.py +0 -64
- package/hooks/blocking/test_session_edit_stage_gate_support.py +0 -204
- package/hooks/blocking/test_session_edit_stage_gate_tracker.py +0 -116
- package/hooks/blocking/test_session_handoff_blocker.py +0 -318
- package/hooks/blocking/test_shell_substitution_blocker.py +0 -298
- package/hooks/blocking/test_unscoped_search_blocker.py +0 -102
- package/hooks/blocking/test_write_existing_file_blocker.py +0 -122
- package/hooks/blocking/tests/test_pii_prevention_blocker.py +0 -259
- package/hooks/blocking/unscoped_search_blocker.py +0 -391
- package/hooks/blocking/write_existing_file_blocker.py +0 -124
- package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +0 -19
- package/hooks/hooks_constants/cursor_cli_python_misfire_blocker_constants.py +0 -79
- package/hooks/hooks_constants/fable_spawn_gate_constants.py +0 -61
- package/hooks/hooks_constants/luna_fast_mode_gate_constants.py +0 -45
- package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +0 -20
- package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +0 -57
- package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +0 -17
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +0 -40
- package/hooks/hooks_constants/session_handoff_blocker_constants.py +0 -10
- package/hooks/hooks_constants/shell_substitution_blocker_constants.py +0 -80
- /package/.agents/skills/{_shared/pr-loop → session-continuity}/AGENTS.md +0 -0
- /package/{_shared/pr-loop → .agents/skills/session-continuity}/CLAUDE.md +0 -0
package/bin/install.mjs
CHANGED
|
@@ -11,7 +11,10 @@ import {
|
|
|
11
11
|
KNOWN_GIT_HOOK_NAMES,
|
|
12
12
|
} from './git_hooks_installer.mjs';
|
|
13
13
|
import { installMypyIniForClaudeHooks } from './install_mypy_ini.mjs';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
expandHomeDirectoryTokens,
|
|
16
|
+
expandHomeDirectoryTokensInSettings,
|
|
17
|
+
} from './expand_home_directory_tokens.mjs';
|
|
15
18
|
import { EVER_SHIPPED_SKILL_NAMES } from './ever-shipped-skills.mjs';
|
|
16
19
|
import {
|
|
17
20
|
managedDenyEntriesFromPackageSettings,
|
|
@@ -35,6 +38,11 @@ import {
|
|
|
35
38
|
WINDOWS_PYTHON_LAUNCHER_COMMAND,
|
|
36
39
|
PYTHON_PROBE_TIMEOUT_MILLISECONDS,
|
|
37
40
|
} from './install-constants.mjs';
|
|
41
|
+
import {
|
|
42
|
+
PSTACK_PLUGIN_DIRECTORY_NAME,
|
|
43
|
+
PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH,
|
|
44
|
+
refreshPstackPluginManifest,
|
|
45
|
+
} from '../scripts/refresh_pstack_plugin_skills.mjs';
|
|
38
46
|
import {
|
|
39
47
|
resolveInstallRoot,
|
|
40
48
|
parseExplicitTargetFromArgv,
|
|
@@ -170,7 +178,6 @@ export const CORE_INCLUDE_DIRECTORIES = [
|
|
|
170
178
|
export const CORE_SKILLS = [
|
|
171
179
|
'orchestrator', 'orchestrator-refresh', 'team-advisor',
|
|
172
180
|
'grok-spawn',
|
|
173
|
-
'pr-small-cl',
|
|
174
181
|
'everything-search',
|
|
175
182
|
'test-runner',
|
|
176
183
|
'privacy-hygiene',
|
|
@@ -316,10 +323,6 @@ export const INSTALL_GROUPS = {
|
|
|
316
323
|
includeAllHooks: true,
|
|
317
324
|
includeCodexRules: true,
|
|
318
325
|
},
|
|
319
|
-
journal: {
|
|
320
|
-
description: 'Session logging and memory',
|
|
321
|
-
skills: ['session-log', 'session-tidy'],
|
|
322
|
-
},
|
|
323
326
|
...dependencyDiscovery.groups,
|
|
324
327
|
};
|
|
325
328
|
|
|
@@ -429,6 +432,26 @@ export function runCursorRuleSync(pythonCommand, scriptPath, claudeRoot, cursorR
|
|
|
429
432
|
);
|
|
430
433
|
}
|
|
431
434
|
|
|
435
|
+
/**
|
|
436
|
+
* Refresh the pstack plugin manifest Claude Code reads, when this home has one.
|
|
437
|
+
*
|
|
438
|
+
* Claude Code loads a skills-directory folder as a plugin when it holds
|
|
439
|
+
* `.claude-plugin/plugin.json`, and namespaces its skills as `pstack:how`.
|
|
440
|
+
* Without the manifest, a folder of skill folders loads nothing.
|
|
441
|
+
*
|
|
442
|
+
* @param {string} [skillsRoot] The skills root to read. Defaults to the root
|
|
443
|
+
* this run installs into.
|
|
444
|
+
* @returns {string|null} The manifest path when this run wrote it, else null.
|
|
445
|
+
*/
|
|
446
|
+
export function refreshInstalledPstackPluginManifest(
|
|
447
|
+
skillsRoot = join(CLAUDE_HOME, MANAGED_SKILLS_DIRECTORY_NAME),
|
|
448
|
+
) {
|
|
449
|
+
const pluginRoot = join(skillsRoot, PSTACK_PLUGIN_DIRECTORY_NAME);
|
|
450
|
+
const outcome = refreshPstackPluginManifest(pluginRoot);
|
|
451
|
+
if (!outcome.didWrite) return null;
|
|
452
|
+
return join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
|
|
453
|
+
}
|
|
454
|
+
|
|
432
455
|
function seedCodexPstackPreferences() {
|
|
433
456
|
const preferenceDirectory = join(AGENTS_HOME, CURSOR_RULES_DIRECTORY_NAME);
|
|
434
457
|
const preferencePath = join(
|
|
@@ -1211,6 +1234,35 @@ export const RETIRED_HOOK_REGISTRATION_RELATIVE_PATHS = new Set([
|
|
|
1211
1234
|
'session/untracked_repo_detector.py',
|
|
1212
1235
|
'session/gh_pr_author_session_cleanup.py',
|
|
1213
1236
|
'observability/pr_description_writer_spawn_tracker.py',
|
|
1237
|
+
'blocking/plain_language_blocker.py',
|
|
1238
|
+
'lifecycle/config_change_guard.py',
|
|
1239
|
+
'blocking/docstring_rule_gate_count_blocker.py',
|
|
1240
|
+
'blocking/write_existing_file_blocker.py',
|
|
1241
|
+
'blocking/sensitive_file_protector.py',
|
|
1242
|
+
'blocking/pii_prevention_blocker.py',
|
|
1243
|
+
'blocking/duplicate_rmtree_helper_blocker.py',
|
|
1244
|
+
'blocking/claude_md_orphan_file_blocker.py',
|
|
1245
|
+
'blocking/package_inventory_stale_blocker.py',
|
|
1246
|
+
'blocking/env_var_table_code_drift_blocker.py',
|
|
1247
|
+
'blocking/pytest_testpaths_orphan_blocker.py',
|
|
1248
|
+
'blocking/destructive_command_blocker.py',
|
|
1249
|
+
'blocking/shell_substitution_blocker.py',
|
|
1250
|
+
'blocking/piped_pytest_blocker.py',
|
|
1251
|
+
'blocking/cursor_cli_python_misfire_blocker.py',
|
|
1252
|
+
'blocking/unscoped_search_blocker.py',
|
|
1253
|
+
'blocking/nas_ssh_binary_enforcer.py',
|
|
1254
|
+
'blocking/block_main_commit.py',
|
|
1255
|
+
'blocking/session_edit_stage_gate.py',
|
|
1256
|
+
'blocking/bash_pre_tool_use_dispatcher.py',
|
|
1257
|
+
'blocking/stop_dispatcher.py',
|
|
1258
|
+
'blocking/bot_mention_comment_blocker.py',
|
|
1259
|
+
'blocking/fable_spawn_gate.py',
|
|
1260
|
+
'blocking/luna_fast_mode_gate.py',
|
|
1261
|
+
'blocking/orchestrator_refresh_reschedule_gate.py',
|
|
1262
|
+
'blocking/ask_user_question_shape_blocker.py',
|
|
1263
|
+
'blocking/send_user_file_open_locally_blocker.py',
|
|
1264
|
+
'blocking/question_to_user_enforcer.py',
|
|
1265
|
+
'blocking/session_handoff_blocker.py',
|
|
1214
1266
|
]);
|
|
1215
1267
|
|
|
1216
1268
|
/**
|
|
@@ -1324,15 +1376,26 @@ export function commandReferencesManagedHook(commandString, managedHookRelativeP
|
|
|
1324
1376
|
* @returns {boolean} True when the managed tail ends at a path boundary.
|
|
1325
1377
|
*/
|
|
1326
1378
|
function commandTailEndsAtManagedHook(normalizedCommand, relativePath) {
|
|
1379
|
+
return commandHoldsPathAtBoundary(normalizedCommand, `/.claude/hooks/${relativePath}`);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
/**
|
|
1383
|
+
* Report whether a command holds one path spelling that ends at a path boundary:
|
|
1384
|
+
* end of string, or an argument separator (whitespace, quote, or semicolon).
|
|
1385
|
+
*
|
|
1386
|
+
* @param {string} normalizedCommand Forward-slash-normalized hook command.
|
|
1387
|
+
* @param {string} pathText The path spelling to look for.
|
|
1388
|
+
* @returns {boolean} True when the command holds that path at a boundary.
|
|
1389
|
+
*/
|
|
1390
|
+
function commandHoldsPathAtBoundary(normalizedCommand, pathText) {
|
|
1327
1391
|
const commandArgumentBoundary = /[\s'";]/;
|
|
1328
|
-
|
|
1329
|
-
let searchStart = normalizedCommand.indexOf(managedTail);
|
|
1392
|
+
let searchStart = normalizedCommand.indexOf(pathText);
|
|
1330
1393
|
while (searchStart !== -1) {
|
|
1331
|
-
const
|
|
1332
|
-
if (
|
|
1394
|
+
const characterAfterPath = normalizedCommand[searchStart + pathText.length];
|
|
1395
|
+
if (characterAfterPath === undefined || commandArgumentBoundary.test(characterAfterPath)) {
|
|
1333
1396
|
return true;
|
|
1334
1397
|
}
|
|
1335
|
-
searchStart = normalizedCommand.indexOf(
|
|
1398
|
+
searchStart = normalizedCommand.indexOf(pathText, searchStart + 1);
|
|
1336
1399
|
}
|
|
1337
1400
|
return false;
|
|
1338
1401
|
}
|
|
@@ -1539,29 +1602,139 @@ export function retiredManagedHookRelativePaths(
|
|
|
1539
1602
|
return retiredRelativePaths;
|
|
1540
1603
|
}
|
|
1541
1604
|
|
|
1605
|
+
const MANAGED_HOOK_ROOT_PATHS = [
|
|
1606
|
+
join(CLAUDE_HOME, MANAGED_HOOKS_DIRECTORY_NAME),
|
|
1607
|
+
join(AGENTS_HOME, MANAGED_HOOKS_DIRECTORY_NAME),
|
|
1608
|
+
INSTALL_ROOT_RESOLUTION.codexHooksInstallDirectory,
|
|
1609
|
+
];
|
|
1610
|
+
|
|
1611
|
+
const CODEX_HOOKS_CONFIGURATION_FILE_NAME = 'hooks.json';
|
|
1612
|
+
|
|
1613
|
+
const CODEX_HOOKS_CONFIGURATION_PATH = join(
|
|
1614
|
+
dirname(INSTALL_ROOT_RESOLUTION.codexHooksInstallDirectory),
|
|
1615
|
+
CODEX_HOOKS_CONFIGURATION_FILE_NAME,
|
|
1616
|
+
);
|
|
1617
|
+
|
|
1542
1618
|
/**
|
|
1543
|
-
*
|
|
1544
|
-
* hook scripts.
|
|
1619
|
+
* Resolve a path through every link on it, or return null when it is missing.
|
|
1545
1620
|
*
|
|
1546
|
-
*
|
|
1547
|
-
*
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1621
|
+
* @param {string} filesystemPath The absolute path to resolve.
|
|
1622
|
+
* @returns {string|null} The real path, or null when nothing stands there.
|
|
1623
|
+
*/
|
|
1624
|
+
function realPathOrNull(filesystemPath) {
|
|
1625
|
+
try {
|
|
1626
|
+
return realpathSync(filesystemPath);
|
|
1627
|
+
} catch {
|
|
1628
|
+
return null;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
/**
|
|
1633
|
+
* Return comparison keys for each owned hooks directory, including its real path.
|
|
1551
1634
|
*
|
|
1552
|
-
*
|
|
1553
|
-
*
|
|
1554
|
-
* it names no retired script and its entry stays.
|
|
1635
|
+
* ~/.claude/hooks, the agents-home hooks directory, and ~/.codex/hooks can
|
|
1636
|
+
* point at one directory. Both the given path and its resolved path are keys.
|
|
1555
1637
|
*
|
|
1556
|
-
* @param {
|
|
1638
|
+
* @param {string[]} allHookRootPaths The hooks directories this installer writes.
|
|
1639
|
+
* @returns {Set<string>} The comparison key of every owned spelling.
|
|
1640
|
+
*/
|
|
1641
|
+
export function ownedHookRootComparisonKeys(allHookRootPaths) {
|
|
1642
|
+
const allRootKeys = new Set();
|
|
1643
|
+
for (const hookRootPath of allHookRootPaths) {
|
|
1644
|
+
if (!hookRootPath) continue;
|
|
1645
|
+
allRootKeys.add(comparisonKeyForPath(hookRootPath));
|
|
1646
|
+
const realHookRootPath = realPathOrNull(hookRootPath);
|
|
1647
|
+
if (realHookRootPath) allRootKeys.add(comparisonKeyForPath(realHookRootPath));
|
|
1648
|
+
}
|
|
1649
|
+
return allRootKeys;
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
/**
|
|
1653
|
+
* Expand `$HOME`, `${HOME}`, `~/`, and `%USERPROFILE%` in a hook command.
|
|
1654
|
+
*
|
|
1655
|
+
* `$HOME`, `${HOME}`, and `~/` go through expandHomeDirectoryTokens, the helper
|
|
1656
|
+
* the settings rewrite already uses, so a tilde is not left in front of home.
|
|
1657
|
+
* `%USERPROFILE%` stays here because this comparison only reads a spelling.
|
|
1658
|
+
*
|
|
1659
|
+
* @param {string} normalizedCommand Forward-slash-normalized hook command.
|
|
1660
|
+
* @param {string} homeDirectory The absolute home directory to substitute.
|
|
1661
|
+
* @returns {string} The command with each home reference expanded.
|
|
1662
|
+
*/
|
|
1663
|
+
function expandHomeReferencesInCommand(normalizedCommand, homeDirectory) {
|
|
1664
|
+
const normalizedHome = homeDirectory.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
1665
|
+
const userProfileExpanded = normalizedCommand.replace(
|
|
1666
|
+
/%USERPROFILE%/gi, () => normalizedHome,
|
|
1667
|
+
);
|
|
1668
|
+
return expandHomeDirectoryTokens(userProfileExpanded, homeDirectory);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
/**
|
|
1672
|
+
* Build the spelling a hook command is compared through: forward-slashed, home
|
|
1673
|
+
* references expanded, and case-folded wherever the filesystem ignores case.
|
|
1674
|
+
*
|
|
1675
|
+
* @param {string} commandString The hook command from a host configuration file.
|
|
1676
|
+
* @param {string} homeDirectory The absolute home directory to substitute.
|
|
1677
|
+
* @returns {string} The command spelling the owned-root test reads.
|
|
1678
|
+
*/
|
|
1679
|
+
export function hookCommandComparisonSpelling(commandString, homeDirectory) {
|
|
1680
|
+
const forwardSlashCommand = commandString.replace(/\\/g, '/');
|
|
1681
|
+
const expandedCommand = expandHomeReferencesInCommand(forwardSlashCommand, homeDirectory);
|
|
1682
|
+
return isCaseInsensitiveFilesystem() ? expandedCommand.toLowerCase() : expandedCommand;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
/**
|
|
1686
|
+
* Report whether a hook command runs one script under a hooks directory this
|
|
1687
|
+
* installer owns.
|
|
1688
|
+
*
|
|
1689
|
+
* @param {unknown} commandString The hook command from a host configuration file.
|
|
1690
|
+
* @param {string} relativePath The script path under a hooks directory.
|
|
1691
|
+
* @param {{allOwnedHookRootKeys: Set<string>, homeDirectory: string}} ownership The owned roots.
|
|
1692
|
+
* @returns {boolean} True when the command runs that script under an owned root.
|
|
1693
|
+
*/
|
|
1694
|
+
export function commandNamesOwnedHookScript(commandString, relativePath, ownership) {
|
|
1695
|
+
if (typeof commandString !== 'string') return false;
|
|
1696
|
+
const shouldFoldCase = isCaseInsensitiveFilesystem();
|
|
1697
|
+
const comparisonCommand = hookCommandComparisonSpelling(
|
|
1698
|
+
commandString, ownership.homeDirectory,
|
|
1699
|
+
);
|
|
1700
|
+
const comparisonRelativePath = shouldFoldCase ? relativePath.toLowerCase() : relativePath;
|
|
1701
|
+
for (const ownedRootKey of ownership.allOwnedHookRootKeys) {
|
|
1702
|
+
const ownedScriptPath = `${ownedRootKey}/${comparisonRelativePath}`;
|
|
1703
|
+
if (commandHoldsPathAtBoundary(comparisonCommand, ownedScriptPath)) return true;
|
|
1704
|
+
}
|
|
1705
|
+
return false;
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
/**
|
|
1709
|
+
* Build the ownership one retirement walk reads, filling in this run's own roots.
|
|
1710
|
+
*
|
|
1711
|
+
* @param {{allOwnedHookRootKeys?: Set<string>, homeDirectory?: string}} [options] Test overrides.
|
|
1712
|
+
* @returns {{allOwnedHookRootKeys: Set<string>, homeDirectory: string}} The ownership.
|
|
1713
|
+
*/
|
|
1714
|
+
function retiredHookOwnership(options = {}) {
|
|
1715
|
+
return {
|
|
1716
|
+
allOwnedHookRootKeys: options.allOwnedHookRootKeys
|
|
1717
|
+
?? ownedHookRootComparisonKeys(MANAGED_HOOK_ROOT_PATHS),
|
|
1718
|
+
homeDirectory: options.homeDirectory ?? homedir(),
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
/**
|
|
1723
|
+
* Report whether a host hook command runs a retired managed script under an
|
|
1724
|
+
* owned hooks directory.
|
|
1725
|
+
*
|
|
1726
|
+
* A path-plus-suffix, a command under another tool's tree, a non-string
|
|
1727
|
+
* command, and the inline validators-runner shape (no script name) are left
|
|
1728
|
+
* in place. The merge rewrites the validators-runner entry on every run.
|
|
1729
|
+
*
|
|
1730
|
+
* @param {unknown} commandString The hook command from a host configuration file.
|
|
1557
1731
|
* @param {Set<string>} retiredHookRelativePaths Retired script paths under hooks/.
|
|
1732
|
+
* @param {{allOwnedHookRootKeys: Set<string>, homeDirectory: string}} ownership The owned roots.
|
|
1558
1733
|
* @returns {boolean} True when the command runs a retired managed script.
|
|
1559
1734
|
*/
|
|
1560
|
-
function commandReferencesRetiredHook(commandString, retiredHookRelativePaths) {
|
|
1561
|
-
if (typeof commandString !== 'string') return false;
|
|
1562
|
-
const normalizedCommand = commandString.replace(/\\/g, '/');
|
|
1735
|
+
function commandReferencesRetiredHook(commandString, retiredHookRelativePaths, ownership) {
|
|
1563
1736
|
for (const relativePath of retiredHookRelativePaths) {
|
|
1564
|
-
if (
|
|
1737
|
+
if (commandNamesOwnedHookScript(commandString, relativePath, ownership)) return true;
|
|
1565
1738
|
}
|
|
1566
1739
|
return false;
|
|
1567
1740
|
}
|
|
@@ -1621,15 +1794,18 @@ function groupHookEntries(group) {
|
|
|
1621
1794
|
*
|
|
1622
1795
|
* @param {object} settings The parsed settings.json object (mutated in place).
|
|
1623
1796
|
* @param {Set<string>} retiredHookRelativePaths Retired script paths under hooks/.
|
|
1797
|
+
* @param {{allOwnedHookRootKeys: Set<string>, homeDirectory: string}} ownership The owned roots.
|
|
1624
1798
|
* @returns {number} How many hook entries were removed.
|
|
1625
1799
|
*/
|
|
1626
|
-
function stripRetiredHookEntries(settings, retiredHookRelativePaths) {
|
|
1800
|
+
function stripRetiredHookEntries(settings, retiredHookRelativePaths, ownership) {
|
|
1627
1801
|
let removedCount = 0;
|
|
1628
1802
|
for (const [eventType, matcherGroups] of Object.entries(settings.hooks)) {
|
|
1629
1803
|
if (!Array.isArray(matcherGroups)) continue;
|
|
1630
1804
|
const eventOutcome = retainedMatcherGroups(
|
|
1631
1805
|
matcherGroups,
|
|
1632
|
-
commandString => commandReferencesRetiredHook(
|
|
1806
|
+
commandString => commandReferencesRetiredHook(
|
|
1807
|
+
commandString, retiredHookRelativePaths, ownership,
|
|
1808
|
+
),
|
|
1633
1809
|
);
|
|
1634
1810
|
removedCount += eventOutcome.removedCount;
|
|
1635
1811
|
if (eventOutcome.keptGroups.length === 0) {
|
|
@@ -1641,35 +1817,256 @@ function stripRetiredHookEntries(settings, retiredHookRelativePaths) {
|
|
|
1641
1817
|
return removedCount;
|
|
1642
1818
|
}
|
|
1643
1819
|
|
|
1820
|
+
const DEFAULT_HOST_CONFIGURATION_INDENT = ' ';
|
|
1821
|
+
|
|
1644
1822
|
/**
|
|
1645
|
-
*
|
|
1646
|
-
* writing the file only when an entry left it.
|
|
1823
|
+
* Read the indent the host configuration file already uses.
|
|
1647
1824
|
*
|
|
1648
|
-
*
|
|
1649
|
-
*
|
|
1650
|
-
*
|
|
1825
|
+
* ~/.claude/settings.json uses four spaces and ~/.codex/hooks.json uses two.
|
|
1826
|
+
* Forcing four spaces would rewrite every line of one of those files. The first
|
|
1827
|
+
* indented line supplies the indent, tabs included.
|
|
1651
1828
|
*
|
|
1652
|
-
* @param {string}
|
|
1829
|
+
* @param {string} settingsText The host configuration file as it stands on disk.
|
|
1830
|
+
* @returns {string} The indent one nesting level uses.
|
|
1831
|
+
*/
|
|
1832
|
+
function hostConfigurationIndent(settingsText) {
|
|
1833
|
+
const firstIndentedLine = /\n([ \t]+)\S/.exec(settingsText);
|
|
1834
|
+
return firstIndentedLine ? firstIndentedLine[1] : DEFAULT_HOST_CONFIGURATION_INDENT;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
/**
|
|
1838
|
+
* Remove every entry of one host hook configuration file that runs a retired
|
|
1839
|
+
* managed hook script, writing the file only when an entry left it.
|
|
1840
|
+
*
|
|
1841
|
+
* Claude settings.json and Codex hooks.json share this hook shape. A run that
|
|
1842
|
+
* retires no hook leaves the file byte-identical. A file the installer cannot
|
|
1843
|
+
* read or cannot parse is left alone with a warning, and the prune continues
|
|
1844
|
+
* with the other host configuration file.
|
|
1845
|
+
*
|
|
1846
|
+
* @param {string} settingsPath The absolute host configuration path.
|
|
1653
1847
|
* @param {Set<string>} retiredHookRelativePaths Retired script paths under hooks/.
|
|
1848
|
+
* @param {{allOwnedHookRootKeys?: Set<string>, homeDirectory?: string}} [options] Test overrides.
|
|
1654
1849
|
* @returns {number} How many hook entries were removed.
|
|
1655
1850
|
*/
|
|
1656
|
-
export function pruneRetiredHookEntriesFromSettings(
|
|
1851
|
+
export function pruneRetiredHookEntriesFromSettings(
|
|
1852
|
+
settingsPath, retiredHookRelativePaths, options = {},
|
|
1853
|
+
) {
|
|
1657
1854
|
if (retiredHookRelativePaths.size === 0) return 0;
|
|
1658
1855
|
if (!existsSync(settingsPath)) return 0;
|
|
1856
|
+
let settingsText;
|
|
1659
1857
|
let settings;
|
|
1660
1858
|
try {
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1859
|
+
settingsText = readFileSync(settingsPath, 'utf8');
|
|
1860
|
+
settings = JSON.parse(settingsText);
|
|
1861
|
+
} catch (readError) {
|
|
1862
|
+
console.warn(` Warning: leaving ${basename(settingsPath)} as it stands — the installer cannot read the file as JSON (${readError.message})`);
|
|
1664
1863
|
return 0;
|
|
1665
1864
|
}
|
|
1666
1865
|
if (!settings.hooks || typeof settings.hooks !== 'object') return 0;
|
|
1667
|
-
const removedCount = stripRetiredHookEntries(
|
|
1866
|
+
const removedCount = stripRetiredHookEntries(
|
|
1867
|
+
settings, retiredHookRelativePaths, retiredHookOwnership(options),
|
|
1868
|
+
);
|
|
1668
1869
|
if (removedCount === 0) return 0;
|
|
1669
|
-
|
|
1870
|
+
const settingsIndent = hostConfigurationIndent(settingsText);
|
|
1871
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, settingsIndent) + '\n');
|
|
1670
1872
|
return removedCount;
|
|
1671
1873
|
}
|
|
1672
1874
|
|
|
1875
|
+
const RETIRED_HOOK_STAND_IN_MARKER = 'claude-dev-env retired hook stand-in';
|
|
1876
|
+
|
|
1877
|
+
const RETIRED_HOOK_STAND_IN_SOURCE = `"""${RETIRED_HOOK_STAND_IN_MARKER}.
|
|
1878
|
+
|
|
1879
|
+
This install retired the hook that stood here and removed its settings.json
|
|
1880
|
+
registration. A session that started before the install still holds the old
|
|
1881
|
+
registration and runs this path on every matching tool call, so the path keeps an
|
|
1882
|
+
inert module: it does nothing and exits 0, which the harness reads as "allow".
|
|
1883
|
+
Restart the session to pick up the new registration. A later install removes this
|
|
1884
|
+
file once no registration names it.
|
|
1885
|
+
"""
|
|
1886
|
+
`;
|
|
1887
|
+
|
|
1888
|
+
const PYTHON_HOOK_SUFFIX = '.py';
|
|
1889
|
+
|
|
1890
|
+
|
|
1891
|
+
/**
|
|
1892
|
+
* List every hook command a settings object holds, across every event type.
|
|
1893
|
+
*
|
|
1894
|
+
* A group with no hooks array contributes nothing. An entry whose command is
|
|
1895
|
+
* absent or is not a string still reaches the caller.
|
|
1896
|
+
*
|
|
1897
|
+
* @param {object} settings The parsed settings.json object.
|
|
1898
|
+
* @returns {unknown[]} Every hook entry's command, in the order settings holds them.
|
|
1899
|
+
*/
|
|
1900
|
+
function settingsHookCommands(settings) {
|
|
1901
|
+
const allCommands = [];
|
|
1902
|
+
for (const matcherGroups of Object.values(settings.hooks ?? {})) {
|
|
1903
|
+
if (!Array.isArray(matcherGroups)) continue;
|
|
1904
|
+
for (const group of matcherGroups) {
|
|
1905
|
+
for (const hook of groupHookEntries(group) ?? []) allCommands.push(hook?.command);
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
return allCommands;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
|
|
1912
|
+
/**
|
|
1913
|
+
* Read the hook commands one host configuration file holds, or none when it
|
|
1914
|
+
* cannot be read. Claude settings.json and Codex hooks.json share this shape.
|
|
1915
|
+
*
|
|
1916
|
+
* Call this before the run mutates settings. The hook merge rewrites
|
|
1917
|
+
* settings.json early, and by prune time the file no longer names what a
|
|
1918
|
+
* session started before this install still invokes.
|
|
1919
|
+
*
|
|
1920
|
+
* @param {string} settingsPath The absolute host configuration path.
|
|
1921
|
+
* @returns {unknown[]} Every hook command the file holds.
|
|
1922
|
+
*/
|
|
1923
|
+
export function settingsHookCommandsAtPath(settingsPath) {
|
|
1924
|
+
if (!existsSync(settingsPath)) return [];
|
|
1925
|
+
let settings;
|
|
1926
|
+
try {
|
|
1927
|
+
settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
1928
|
+
} catch {
|
|
1929
|
+
return [];
|
|
1930
|
+
}
|
|
1931
|
+
if (!settings?.hooks || typeof settings.hooks !== 'object') return [];
|
|
1932
|
+
return settingsHookCommands(settings);
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
|
|
1936
|
+
/**
|
|
1937
|
+
* Name the retired hook scripts a stale registration still runs.
|
|
1938
|
+
*
|
|
1939
|
+
* The commands come from the reading taken before the run mutated settings.json.
|
|
1940
|
+
* Those paths are where a stand-in belongs.
|
|
1941
|
+
*
|
|
1942
|
+
* @param {unknown[]} allCommands Hook commands read before the run mutated settings.
|
|
1943
|
+
* @param {Set<string>} retiredHookRelativePaths Retired script paths under hooks/.
|
|
1944
|
+
* @param {{allOwnedHookRootKeys?: Set<string>, homeDirectory?: string}} [options] Test overrides.
|
|
1945
|
+
* @returns {Set<string>} The retired paths a command names.
|
|
1946
|
+
*/
|
|
1947
|
+
export function retiredHookPathsNamedByCommands(
|
|
1948
|
+
allCommands, retiredHookRelativePaths, options = {},
|
|
1949
|
+
) {
|
|
1950
|
+
const ownership = retiredHookOwnership(options);
|
|
1951
|
+
const namedPaths = new Set();
|
|
1952
|
+
for (const relativePath of retiredHookRelativePaths) {
|
|
1953
|
+
const namesThisPath = allCommands.some(
|
|
1954
|
+
command => commandNamesOwnedHookScript(command, relativePath, ownership),
|
|
1955
|
+
);
|
|
1956
|
+
if (namesThisPath) namedPaths.add(relativePath);
|
|
1957
|
+
}
|
|
1958
|
+
return namedPaths;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
|
|
1962
|
+
/**
|
|
1963
|
+
* Write an inert Python module at each retired hook path a stale registration
|
|
1964
|
+
* still runs.
|
|
1965
|
+
*
|
|
1966
|
+
* Deleting a registered script makes python3 exit 2. The harness treats a
|
|
1967
|
+
* PreToolUse exit 2 as a block, so every matching tool call is denied until
|
|
1968
|
+
* the session restarts. An inert module with one docstring exits 0, which the
|
|
1969
|
+
* harness treats as allow.
|
|
1970
|
+
*
|
|
1971
|
+
* An existing file is left in place. Each written path is recorded in the
|
|
1972
|
+
* manifest so a later install removes the stand-in once no registration names
|
|
1973
|
+
* it.
|
|
1974
|
+
*
|
|
1975
|
+
* @param {string} hooksRoot The absolute installed hooks directory.
|
|
1976
|
+
* @param {Set<string>} registeredRetiredRelativePaths Retired paths a registration names.
|
|
1977
|
+
* @returns {string[]} The absolute paths this run wrote a stand-in to.
|
|
1978
|
+
*/
|
|
1979
|
+
export function writeRetiredHookStandIns(hooksRoot, registeredRetiredRelativePaths) {
|
|
1980
|
+
const writtenPaths = [];
|
|
1981
|
+
for (const relativePath of registeredRetiredRelativePaths) {
|
|
1982
|
+
if (!relativePath.endsWith(PYTHON_HOOK_SUFFIX)) continue;
|
|
1983
|
+
const standInPath = join(hooksRoot, ...relativePath.split('/'));
|
|
1984
|
+
if (existsSync(standInPath)) continue;
|
|
1985
|
+
mkdirSync(dirname(standInPath), { recursive: true });
|
|
1986
|
+
writeFileSync(standInPath, RETIRED_HOOK_STAND_IN_SOURCE);
|
|
1987
|
+
writtenPaths.push(standInPath);
|
|
1988
|
+
}
|
|
1989
|
+
return writtenPaths;
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
|
|
1993
|
+
/**
|
|
1994
|
+
* List owned hooks directories that exist, one path per distinct real directory.
|
|
1995
|
+
*
|
|
1996
|
+
* On a healthy install the three roots are pointers to one directory. Keying
|
|
1997
|
+
* on the real path writes the stand-in once. A missing root is skipped.
|
|
1998
|
+
*
|
|
1999
|
+
* @param {string[]} allHookRootPaths The hooks directories this installer writes.
|
|
2000
|
+
* @returns {string[]} One path per distinct hooks directory that exists.
|
|
2001
|
+
*/
|
|
2002
|
+
function distinctExistingHookRoots(allHookRootPaths) {
|
|
2003
|
+
const allRootKeys = new Set();
|
|
2004
|
+
const allDistinctRoots = [];
|
|
2005
|
+
for (const hookRootPath of allHookRootPaths) {
|
|
2006
|
+
const realHookRootPath = realPathOrNull(hookRootPath);
|
|
2007
|
+
if (!realHookRootPath) continue;
|
|
2008
|
+
const rootKey = comparisonKeyForPath(realHookRootPath);
|
|
2009
|
+
if (allRootKeys.has(rootKey)) continue;
|
|
2010
|
+
allRootKeys.add(rootKey);
|
|
2011
|
+
allDistinctRoots.push(hookRootPath);
|
|
2012
|
+
}
|
|
2013
|
+
return allDistinctRoots;
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
/**
|
|
2017
|
+
* Leave an inert stand-in at each retired path under every owned hooks directory.
|
|
2018
|
+
*
|
|
2019
|
+
* @param {string[]} allHookRootPaths The hooks directories this installer writes.
|
|
2020
|
+
* @param {Set<string>} registeredRetiredRelativePaths Retired paths a registration names.
|
|
2021
|
+
* @returns {string[]} The absolute paths this run wrote a stand-in to.
|
|
2022
|
+
*/
|
|
2023
|
+
function writeRetiredHookStandInsAcrossOwnedRoots(
|
|
2024
|
+
allHookRootPaths, registeredRetiredRelativePaths,
|
|
2025
|
+
) {
|
|
2026
|
+
const allWrittenPaths = [];
|
|
2027
|
+
for (const hooksRoot of distinctExistingHookRoots(allHookRootPaths)) {
|
|
2028
|
+
allWrittenPaths.push(
|
|
2029
|
+
...writeRetiredHookStandIns(hooksRoot, registeredRetiredRelativePaths),
|
|
2030
|
+
);
|
|
2031
|
+
}
|
|
2032
|
+
return allWrittenPaths;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
/**
|
|
2036
|
+
* Name each retired registration a stand-in now fills, by its path under hooks/.
|
|
2037
|
+
*
|
|
2038
|
+
* @param {string[]} allHookRootPaths The hooks directories this installer writes.
|
|
2039
|
+
* @param {string[]} standInPaths The stand-ins this run wrote.
|
|
2040
|
+
* @returns {string[]} The sorted relative names, one per registration.
|
|
2041
|
+
*/
|
|
2042
|
+
function standInRegistrationNames(allHookRootPaths, standInPaths) {
|
|
2043
|
+
const allNames = new Set();
|
|
2044
|
+
for (const standInPath of standInPaths) {
|
|
2045
|
+
const owningRoot = allHookRootPaths.find(
|
|
2046
|
+
hookRootPath => isInsideDirectory(resolve(standInPath), resolve(hookRootPath)),
|
|
2047
|
+
);
|
|
2048
|
+
if (!owningRoot) continue;
|
|
2049
|
+
allNames.add(relative(owningRoot, standInPath).replace(/\\/g, '/'));
|
|
2050
|
+
}
|
|
2051
|
+
return [...allNames].sort();
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
/**
|
|
2055
|
+
* Print the one-line restart notice naming the registrations this run retired.
|
|
2056
|
+
*
|
|
2057
|
+
* @param {string[]} allHookRootPaths The hooks directories this installer writes.
|
|
2058
|
+
* @param {string[]} standInPaths The stand-ins this run wrote.
|
|
2059
|
+
* @returns {void}
|
|
2060
|
+
*/
|
|
2061
|
+
function reportRetiredHookStandIns(allHookRootPaths, standInPaths) {
|
|
2062
|
+
if (standInPaths.length === 0) return;
|
|
2063
|
+
const allNames = standInRegistrationNames(allHookRootPaths, standInPaths);
|
|
2064
|
+
console.log(
|
|
2065
|
+
` Restart open sessions: this install retired ${allNames.join(', ')} and left an inert stand-in at each path, so a session started before it keeps working until you restart it.`,
|
|
2066
|
+
);
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
|
|
1673
2070
|
/**
|
|
1674
2071
|
* Load ~/.claude/settings.json for an in-place merge, or `{}` when absent/empty.
|
|
1675
2072
|
*
|
|
@@ -1975,46 +2372,78 @@ function pruneStaleFilesAcrossManagedRoots(priorInstalledFiles, currentInstalled
|
|
|
1975
2372
|
return { prunedCount, skillsPrunedCount, failedPaths };
|
|
1976
2373
|
}
|
|
1977
2374
|
|
|
2375
|
+
/**
|
|
2376
|
+
* Remove every retired registration from each host hook configuration file, and
|
|
2377
|
+
* report each file that lost one.
|
|
2378
|
+
*
|
|
2379
|
+
* Claude Code reads ~/.claude/settings.json and Codex reads ~/.codex/hooks.json.
|
|
2380
|
+
* A session of either host holds the registrations its own file named at start,
|
|
2381
|
+
* so a retirement that reaches only one file leaves the other host's open session
|
|
2382
|
+
* running a script this install moved.
|
|
2383
|
+
*
|
|
2384
|
+
* @param {string} settingsPath The absolute settings.json path.
|
|
2385
|
+
* @param {Set<string>} retiredHookPaths Retired script paths under hooks/.
|
|
2386
|
+
* @returns {void}
|
|
2387
|
+
*/
|
|
2388
|
+
function pruneRetiredHookRegistrations(settingsPath, retiredHookPaths) {
|
|
2389
|
+
const allConfigurationPaths = [settingsPath, CODEX_HOOKS_CONFIGURATION_PATH];
|
|
2390
|
+
for (const configurationPath of allConfigurationPaths) {
|
|
2391
|
+
const removedCount = pruneRetiredHookEntriesFromSettings(
|
|
2392
|
+
configurationPath, retiredHookPaths,
|
|
2393
|
+
);
|
|
2394
|
+
if (removedCount === 0) continue;
|
|
2395
|
+
console.log(
|
|
2396
|
+
` Hook entries: ${removedCount} retired entry(s) removed from ${basename(configurationPath)}`,
|
|
2397
|
+
);
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
|
|
1978
2401
|
/**
|
|
1979
2402
|
* Run every prune a full install performs, in the order that keeps ~/.claude
|
|
1980
2403
|
* consistent at each step.
|
|
1981
2404
|
*
|
|
1982
|
-
*
|
|
1983
|
-
*
|
|
1984
|
-
*
|
|
1985
|
-
*
|
|
1986
|
-
*
|
|
1987
|
-
* Both prunes report how much content reached the run's backup root, and their sum
|
|
1988
|
-
* is what backup retention answers to: the sweep of older recovery points runs
|
|
1989
|
-
* only once this run holds one of its own.
|
|
2405
|
+
* Retired hook entries leave settings before the script is moved, so a live
|
|
2406
|
+
* session does not invoke a missing file. Stand-ins are written last, after
|
|
2407
|
+
* the move has emptied those paths. Paths come from `priorSettingsHookCommands`,
|
|
2408
|
+
* read before the run mutated settings. The caller records each stand-in in
|
|
2409
|
+
* the manifest so a later install removes it once no registration names it.
|
|
1990
2410
|
*
|
|
1991
2411
|
* @param {Set<string>} copiedSkillNames Skill directory names this run wrote.
|
|
1992
2412
|
* @param {string[]|null} priorManifestSkills The prior manifest's skill names, or null.
|
|
1993
2413
|
* @param {string[]|null} priorManifestFiles The prior manifest's file list, or null.
|
|
1994
2414
|
* @param {string[]} installedFiles Every file this run copied.
|
|
1995
|
-
* @
|
|
1996
|
-
*
|
|
1997
|
-
*
|
|
2415
|
+
* @param {unknown[]} priorSettingsHookCommands Hook commands read before this run mutated settings.
|
|
2416
|
+
* @returns {{prunedCount: number, skillsPrunedCount: number, failedPaths: string[],
|
|
2417
|
+
* standInPaths: string[]}} The stale files moved across all roots, the skills
|
|
2418
|
+
* root's share, every path whose move failed, and every stand-in written.
|
|
1998
2419
|
*/
|
|
1999
2420
|
function runFullInstallPrunes(
|
|
2000
|
-
copiedSkillNames,
|
|
2421
|
+
copiedSkillNames,
|
|
2422
|
+
priorManifestSkills,
|
|
2423
|
+
priorManifestFiles,
|
|
2424
|
+
installedFiles,
|
|
2425
|
+
priorSettingsHookCommands,
|
|
2001
2426
|
) {
|
|
2427
|
+
const hooksRoot = join(CLAUDE_HOME, MANAGED_HOOKS_DIRECTORY_NAME);
|
|
2428
|
+
const settingsPath = join(CLAUDE_HOME, SETTINGS_FILE_NAME);
|
|
2002
2429
|
const retiredSkillMovedCount = pruneRetiredSkills(copiedSkillNames, priorManifestSkills);
|
|
2003
|
-
const
|
|
2004
|
-
|
|
2005
|
-
retiredManagedHookRelativePaths(
|
|
2006
|
-
priorManifestFiles, installedFiles, join(CLAUDE_HOME, MANAGED_HOOKS_DIRECTORY_NAME),
|
|
2007
|
-
),
|
|
2430
|
+
const retiredHookPaths = retiredManagedHookRelativePaths(
|
|
2431
|
+
priorManifestFiles, installedFiles, hooksRoot,
|
|
2008
2432
|
);
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2433
|
+
const registeredRetiredPaths = retiredHookPathsNamedByCommands(
|
|
2434
|
+
priorSettingsHookCommands, retiredHookPaths,
|
|
2435
|
+
);
|
|
2436
|
+
pruneRetiredHookRegistrations(settingsPath, retiredHookPaths);
|
|
2012
2437
|
const staleOutcome = pruneStaleFilesAcrossManagedRoots(
|
|
2013
2438
|
priorManifestFiles, installedFiles, currentRunBackupRoot(),
|
|
2014
2439
|
);
|
|
2440
|
+
const standInPaths = writeRetiredHookStandInsAcrossOwnedRoots(
|
|
2441
|
+
MANAGED_HOOK_ROOT_PATHS, registeredRetiredPaths,
|
|
2442
|
+
);
|
|
2443
|
+
reportRetiredHookStandIns(MANAGED_HOOK_ROOT_PATHS, standInPaths);
|
|
2015
2444
|
const didRunMoveContent = retiredSkillMovedCount + staleOutcome.prunedCount > 0;
|
|
2016
2445
|
retainNewestRunBackupOnly(currentRunBackupRoot(), didRunMoveContent);
|
|
2017
|
-
return staleOutcome;
|
|
2446
|
+
return { ...staleOutcome, standInPaths };
|
|
2018
2447
|
}
|
|
2019
2448
|
|
|
2020
2449
|
/**
|
|
@@ -2115,6 +2544,10 @@ function executeInstallPlan(plan) {
|
|
|
2115
2544
|
*/
|
|
2116
2545
|
function executeInstallPlanMutations(plan, transactionHelpers) {
|
|
2117
2546
|
const { throwIfFault, syncWrittenPaths } = transactionHelpers;
|
|
2547
|
+
const priorSettingsHookCommands = [
|
|
2548
|
+
...settingsHookCommandsAtPath(join(CLAUDE_HOME, SETTINGS_FILE_NAME)),
|
|
2549
|
+
...settingsHookCommandsAtPath(CODEX_HOOKS_CONFIGURATION_PATH),
|
|
2550
|
+
];
|
|
2118
2551
|
const selectedGroups = plan.selectedGroups;
|
|
2119
2552
|
const priorManifestFiles = plan.priorManifest.files;
|
|
2120
2553
|
const priorManifestSkills = plan.priorManifest.skills;
|
|
@@ -2320,6 +2753,9 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
|
|
|
2320
2753
|
}
|
|
2321
2754
|
summary.skills = { created: skillsCreated, updated: skillsUpdated, pruned: 0, paths: skillPaths };
|
|
2322
2755
|
allInstalledFiles.push(...skillPaths);
|
|
2756
|
+
const pstackManifestPath = refreshInstalledPstackPluginManifest();
|
|
2757
|
+
if (pstackManifestPath) allInstalledFiles.push(pstackManifestPath);
|
|
2758
|
+
summary.pstackPlugin = { manifestPath: pstackManifestPath };
|
|
2323
2759
|
syncWrittenPaths([...allInstalledFiles, ...publishedPointerPaths]);
|
|
2324
2760
|
throwIfFault(FAULT_PHASES.AFTER_FILE_STAGING);
|
|
2325
2761
|
throwIfFault(FAULT_PHASES.BEFORE_DURABLE_PROMOTION);
|
|
@@ -2459,11 +2895,16 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
|
|
|
2459
2895
|
if (didPruneRun) {
|
|
2460
2896
|
try {
|
|
2461
2897
|
const prunes = runFullInstallPrunes(
|
|
2462
|
-
copiedSkillNames,
|
|
2898
|
+
copiedSkillNames,
|
|
2899
|
+
priorManifestSkills,
|
|
2900
|
+
priorManifestFiles,
|
|
2901
|
+
allInstalledFiles,
|
|
2902
|
+
priorSettingsHookCommands,
|
|
2463
2903
|
);
|
|
2464
2904
|
summary.skills.pruned = prunes.skillsPrunedCount;
|
|
2465
2905
|
stalePrunedTotal = prunes.prunedCount;
|
|
2466
2906
|
failedPrunePaths = prunes.failedPaths;
|
|
2907
|
+
allInstalledFiles.push(...prunes.standInPaths);
|
|
2467
2908
|
didPruneFinish = true;
|
|
2468
2909
|
} catch (pruneError) {
|
|
2469
2910
|
console.warn(
|
|
@@ -2807,7 +3248,6 @@ ${groupLines}
|
|
|
2807
3248
|
|
|
2808
3249
|
Examples:
|
|
2809
3250
|
npx ${PACKAGE_NAME} --only core
|
|
2810
|
-
npx ${PACKAGE_NAME} --only core,journal
|
|
2811
3251
|
npx ${PACKAGE_NAME} --profile editor --only core
|
|
2812
3252
|
npx ${PACKAGE_NAME} --profiles editor,mel --only core
|
|
2813
3253
|
|
|
@@ -2817,6 +3257,9 @@ Named profiles resolve under LLM_SETTINGS_PROFILES_ROOT or ~/.claude-profiles/<d
|
|
|
2817
3257
|
Codex exec-policy files copy into ~/.codex/rules, or CODEX_HOME/rules when CODEX_HOME is set.
|
|
2818
3258
|
Cursor rule files generate into ~/.cursor/rules as stem-named mdc files, one per Claude rule.
|
|
2819
3259
|
|
|
3260
|
+
The installer reads only flags. A bare path argument carries no meaning, so
|
|
3261
|
+
npx ${PACKAGE_NAME} . lands in the same root as a run with no argument.
|
|
3262
|
+
|
|
2820
3263
|
Root precedence: --target > CLAUDE_CONFIG_DIR > ~/.claude
|
|
2821
3264
|
Profile selection (--profile/--profiles) is mutually exclusive with --target.
|
|
2822
3265
|
Target selection finishes before any mutation. Each target writes its own manifest
|