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
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import {
|
|
5
|
+
FOLDED_HOOK_RELATIVE_PATHS,
|
|
6
|
+
RETIRED_HOOK_REGISTRATION_RELATIVE_PATHS,
|
|
7
|
+
mergeHooksIntoSettings,
|
|
8
|
+
} from './install.mjs';
|
|
9
|
+
|
|
10
|
+
const allRetiredWriteHooks = [
|
|
11
|
+
'blocking/code_rules_enforcer.py',
|
|
12
|
+
'blocking/tdd_enforcer.py',
|
|
13
|
+
'blocking/windows_rmtree_blocker.py',
|
|
14
|
+
'blocking/state_description_blocker.py',
|
|
15
|
+
'blocking/subprocess_budget_completeness.py',
|
|
16
|
+
'blocking/hook_prose_detector_consistency.py',
|
|
17
|
+
'blocking/workflow_substitution_slot_blocker.py',
|
|
18
|
+
];
|
|
19
|
+
const allRetiredPolicyRegistrations = [
|
|
20
|
+
'blocking/plain_language_blocker.py',
|
|
21
|
+
'lifecycle/config_change_guard.py',
|
|
22
|
+
'blocking/docstring_rule_gate_count_blocker.py',
|
|
23
|
+
'blocking/write_existing_file_blocker.py',
|
|
24
|
+
'blocking/sensitive_file_protector.py',
|
|
25
|
+
'blocking/pii_prevention_blocker.py',
|
|
26
|
+
'blocking/duplicate_rmtree_helper_blocker.py',
|
|
27
|
+
'blocking/claude_md_orphan_file_blocker.py',
|
|
28
|
+
'blocking/package_inventory_stale_blocker.py',
|
|
29
|
+
'blocking/env_var_table_code_drift_blocker.py',
|
|
30
|
+
'blocking/pytest_testpaths_orphan_blocker.py',
|
|
31
|
+
'blocking/destructive_command_blocker.py',
|
|
32
|
+
'blocking/shell_substitution_blocker.py',
|
|
33
|
+
'blocking/piped_pytest_blocker.py',
|
|
34
|
+
'blocking/cursor_cli_python_misfire_blocker.py',
|
|
35
|
+
'blocking/unscoped_search_blocker.py',
|
|
36
|
+
'blocking/nas_ssh_binary_enforcer.py',
|
|
37
|
+
'blocking/block_main_commit.py',
|
|
38
|
+
'blocking/session_edit_stage_gate.py',
|
|
39
|
+
'blocking/bash_pre_tool_use_dispatcher.py',
|
|
40
|
+
'blocking/stop_dispatcher.py',
|
|
41
|
+
'blocking/bot_mention_comment_blocker.py',
|
|
42
|
+
'blocking/fable_spawn_gate.py',
|
|
43
|
+
'blocking/luna_fast_mode_gate.py',
|
|
44
|
+
'blocking/orchestrator_refresh_reschedule_gate.py',
|
|
45
|
+
'blocking/ask_user_question_shape_blocker.py',
|
|
46
|
+
'blocking/send_user_file_open_locally_blocker.py',
|
|
47
|
+
'blocking/question_to_user_enforcer.py',
|
|
48
|
+
'blocking/session_handoff_blocker.py',
|
|
49
|
+
];
|
|
50
|
+
const sourceHooks = JSON.parse(readFileSync(new URL('../hooks/hooks.json', import.meta.url), 'utf8'));
|
|
51
|
+
const installedRoot = '/fixture-home/.claude';
|
|
52
|
+
const foreignCommand = 'python /unmanaged/keep_this_hook.py';
|
|
53
|
+
const foreignSessionCommand = 'node /unmanaged/session.js';
|
|
54
|
+
|
|
55
|
+
function legacySettings() {
|
|
56
|
+
return {
|
|
57
|
+
permissions: {
|
|
58
|
+
allow: ['Read'],
|
|
59
|
+
deny: ['Bash(git push --force)'],
|
|
60
|
+
ask: ['Bash(git push)'],
|
|
61
|
+
},
|
|
62
|
+
hooks: {
|
|
63
|
+
PreToolUse: [{
|
|
64
|
+
matcher: 'Write|Edit|MultiEdit|apply_patch',
|
|
65
|
+
hooks: [
|
|
66
|
+
{ type: 'command', command: foreignCommand, timeout: 17 },
|
|
67
|
+
...allRetiredWriteHooks.map(relativePath => ({
|
|
68
|
+
type: 'command',
|
|
69
|
+
command: `python ${installedRoot}/hooks/${relativePath}`,
|
|
70
|
+
timeout: 10,
|
|
71
|
+
})),
|
|
72
|
+
...allRetiredPolicyRegistrations.map(relativePath => ({
|
|
73
|
+
type: 'command',
|
|
74
|
+
command: `python ${installedRoot}/hooks/${relativePath}`,
|
|
75
|
+
timeout: 10,
|
|
76
|
+
})),
|
|
77
|
+
],
|
|
78
|
+
}],
|
|
79
|
+
SessionStart: [{
|
|
80
|
+
matcher: '',
|
|
81
|
+
hooks: [{ type: 'command', command: foreignSessionCommand, timeout: 9 }],
|
|
82
|
+
}],
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function allCommands(settings) {
|
|
88
|
+
return Object.values(settings.hooks).flatMap(groups =>
|
|
89
|
+
groups.flatMap(group => group.hooks.map(hook => hook.command)));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
test('every retired write registration already has an installer-owned prune path', () => {
|
|
93
|
+
for (const relativePath of allRetiredWriteHooks) {
|
|
94
|
+
assert.ok(FOLDED_HOOK_RELATIVE_PATHS.has(relativePath), relativePath);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('every retired policy registration has an installer-owned prune path', () => {
|
|
99
|
+
for (const relativePath of allRetiredPolicyRegistrations) {
|
|
100
|
+
assert.ok(RETIRED_HOOK_REGISTRATION_RELATIVE_PATHS.has(relativePath), relativePath);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('upgrade prunes legacy direct file checks while retaining foreign hooks', () => {
|
|
105
|
+
const settings = legacySettings();
|
|
106
|
+
mergeHooksIntoSettings(settings, sourceHooks, installedRoot, 'python3');
|
|
107
|
+
const commands = allCommands(settings);
|
|
108
|
+
for (const relativePath of [...allRetiredWriteHooks, ...allRetiredPolicyRegistrations]) {
|
|
109
|
+
assert.ok(!commands.some(command => command.includes(relativePath)), relativePath);
|
|
110
|
+
}
|
|
111
|
+
assert.ok(commands.includes(foreignCommand));
|
|
112
|
+
assert.ok(commands.includes(foreignSessionCommand));
|
|
113
|
+
assert.deepEqual(settings.permissions, {
|
|
114
|
+
allow: ['Read'],
|
|
115
|
+
deny: ['Bash(git push --force)'],
|
|
116
|
+
ask: ['Bash(git push)'],
|
|
117
|
+
});
|
|
118
|
+
assert.ok(commands.every(command => !command.includes('/blocking/stop_dispatcher.py')));
|
|
119
|
+
assert.ok(commands.some(command => command.includes('/blocking/pre_tool_use_dispatcher.py')));
|
|
120
|
+
assert.ok(commands.every(command => !command.includes('/blocking/bash_pre_tool_use_dispatcher.py')));
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('reinstall does not restore retired direct checks or duplicate foreign hooks', () => {
|
|
124
|
+
const settings = legacySettings();
|
|
125
|
+
mergeHooksIntoSettings(settings, sourceHooks, installedRoot, 'python3');
|
|
126
|
+
const firstInstall = structuredClone(settings);
|
|
127
|
+
mergeHooksIntoSettings(settings, sourceHooks, installedRoot, 'python3');
|
|
128
|
+
assert.deepEqual(settings, firstInstall);
|
|
129
|
+
assert.equal(allCommands(settings).filter(command => command === foreignCommand).length, 1);
|
|
130
|
+
});
|
|
@@ -244,6 +244,16 @@ test('install.mjs --help documents profile target selection', () => {
|
|
|
244
244
|
assert.match(result.stdout, /targetIdentity|target identity|per target/i);
|
|
245
245
|
});
|
|
246
246
|
|
|
247
|
+
test('install.mjs --help states that a positional path argument carries no meaning', () => {
|
|
248
|
+
const result = spawnSync(process.execPath, [INSTALL_MODULE_PATH, '--help'], {
|
|
249
|
+
encoding: 'utf8',
|
|
250
|
+
env: process.env,
|
|
251
|
+
});
|
|
252
|
+
assert.equal(result.status, 0, result.stderr);
|
|
253
|
+
assert.match(result.stdout, /reads only flags/i);
|
|
254
|
+
assert.match(result.stdout, /path argument/i);
|
|
255
|
+
});
|
|
256
|
+
|
|
247
257
|
test('install.mjs wires select-install-targets for multi-profile entry', () => {
|
|
248
258
|
const source = readFileSync(INSTALL_MODULE_PATH, 'utf8');
|
|
249
259
|
assert.match(source, /select-install-targets\.mjs/);
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { tmpdir } from 'node:os';
|
|
14
14
|
import { join, dirname, basename } from 'node:path';
|
|
15
15
|
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { detectPython } from './install.mjs';
|
|
16
17
|
const THIS_DIRECTORY = dirname(fileURLToPath(import.meta.url));
|
|
17
18
|
const INSTALLER_PATH = join(THIS_DIRECTORY, 'install.mjs');
|
|
18
19
|
const INSTALLER_PROCESS_TIMEOUT_MS = 60_000;
|
|
@@ -25,16 +26,43 @@ const RETIRED_SKILL_DIRECTORIES = [
|
|
|
25
26
|
'post-audit-findings',
|
|
26
27
|
'pr-consistency-audit',
|
|
27
28
|
'bdd-protocol',
|
|
29
|
+
'hitl',
|
|
30
|
+
'autoconverge',
|
|
31
|
+
'pr-cleanup',
|
|
32
|
+
'pr-name-by-capability',
|
|
33
|
+
'pr-plain-language-cleanup',
|
|
34
|
+
'pr-refinement',
|
|
35
|
+
'pr-shared-extraction',
|
|
36
|
+
'pr-small-cl',
|
|
37
|
+
'pr-title-description',
|
|
38
|
+
'prototype',
|
|
39
|
+
'rebase',
|
|
40
|
+
'review-router',
|
|
41
|
+
'review-tier',
|
|
42
|
+
'run-claude-dev-env',
|
|
43
|
+
'session-log',
|
|
44
|
+
'session-tidy',
|
|
45
|
+
'source-command-sr-loop',
|
|
46
|
+
'update',
|
|
28
47
|
];
|
|
29
48
|
const PERSONAL_SKILL_DIRECTORIES = ['credit-card-picker', 'midjourney-sref'];
|
|
30
|
-
const SHIPPED_SKILL_DIRECTORY = '
|
|
49
|
+
const SHIPPED_SKILL_DIRECTORY = 'e-code-review';
|
|
31
50
|
const PRUNED_BACKUP_DIRECTORY_NAME = '.claude-dev-env-pruned';
|
|
32
51
|
const SKIP_PRUNE_NOTICE_MARKER = 'Skipping retired-skill and stale-file prune';
|
|
33
52
|
const STALE_SKILL_FILE_RELATIVE_SEGMENTS = ['scripts', 'retired_module.py'];
|
|
34
53
|
const RUNTIME_ARTIFACT_RELATIVE_SEGMENTS = ['scripts', '__pycache__', 'helper.cpython-312.pyc'];
|
|
35
54
|
const SCOPED_GROUP_SKILL_DIRECTORY = 'orchestrator';
|
|
36
|
-
const
|
|
37
|
-
'
|
|
55
|
+
const CORE_SKILL_DIRECTORIES = [
|
|
56
|
+
'orchestrator',
|
|
57
|
+
'orchestrator-refresh',
|
|
58
|
+
'team-advisor',
|
|
59
|
+
'grok-spawn',
|
|
60
|
+
'everything-search',
|
|
61
|
+
'test-runner',
|
|
62
|
+
'privacy-hygiene',
|
|
63
|
+
'issue-tracker',
|
|
64
|
+
'task-build',
|
|
65
|
+
'eli5',
|
|
38
66
|
];
|
|
39
67
|
const PRIOR_RUN_BACKUP_DIRECTORY_NAMES = [
|
|
40
68
|
'2020-01-01T00-00-00-000Z',
|
|
@@ -57,6 +85,8 @@ const STALE_RULE_FILE_NAME = 'retired-rule.md';
|
|
|
57
85
|
const STALE_SHARED_FILE_SEGMENTS = ['pr-loop', 'retired_shared.md'];
|
|
58
86
|
const STALE_SHARED_SKILL_FILE_SEGMENTS = [SHARED_DIRECTORY_NAME, 'retired_skill_shared.md'];
|
|
59
87
|
const RETIRED_HOOK_RELATIVE_SEGMENTS = ['blocking', 'retired_gate.py'];
|
|
88
|
+
const RETIRED_HOOK_SEEDED_CONTENTS = 'a gate an earlier revision shipped\n';
|
|
89
|
+
const FOLDED_RETIRED_HOOK_RELATIVE_SEGMENTS = ['blocking', 'ask_user_question_shape_blocker.py'];
|
|
60
90
|
const USER_HOOK_COMMAND = 'python3 my_own_gate.py --user-authored';
|
|
61
91
|
const RETIRED_HOOK_EVENT_TYPE = 'PreToolUse';
|
|
62
92
|
const DROPPED_HOOK_EVENT_TYPE = 'PreCompact';
|
|
@@ -150,7 +180,7 @@ function createSandbox(homeDirectoryPrefix = 'cdev-prune-home-') {
|
|
|
150
180
|
return { homeDirectory, claudeDirectory, skillsDirectory, manifestPath };
|
|
151
181
|
}
|
|
152
182
|
|
|
153
|
-
test('a core install runs the spaced-path Write dispatcher and
|
|
183
|
+
test('a core install runs the spaced-path Write dispatcher and allows the write', () => {
|
|
154
184
|
const sandbox = createSandbox('cdev prune & (home)-');
|
|
155
185
|
try {
|
|
156
186
|
runInstaller(sandbox.homeDirectory, ['--only', 'core']);
|
|
@@ -177,12 +207,8 @@ test('a core install runs the spaced-path Write dispatcher and captures its red
|
|
|
177
207
|
});
|
|
178
208
|
|
|
179
209
|
assert.equal(dispatcherRun.status, 0, dispatcherRun.stderr);
|
|
180
|
-
|
|
181
|
-
assert.equal(
|
|
182
|
-
assert.match(
|
|
183
|
-
shownRedDecision.hookSpecificOutput.permissionDecisionReason,
|
|
184
|
-
/BLOCKED: Write on existing file/,
|
|
185
|
-
);
|
|
210
|
+
assert.equal(dispatcherRun.stdout.trim(), '', 'no hosted hook denies a Write');
|
|
211
|
+
assert.equal(readFileSync(protectedFilePath, 'utf8'), 'existing content\n');
|
|
186
212
|
} finally {
|
|
187
213
|
rmSync(sandbox.homeDirectory, { recursive: true, force: true });
|
|
188
214
|
}
|
|
@@ -693,25 +719,33 @@ test('a scoped --only install leaves retired skills in place because prune runs
|
|
|
693
719
|
}
|
|
694
720
|
});
|
|
695
721
|
|
|
696
|
-
test('a full reinstall keeps
|
|
722
|
+
test('a full reinstall keeps the skill-builder stub and archives its obsolete support files', () => {
|
|
697
723
|
const sandbox = createSandbox();
|
|
698
724
|
try {
|
|
699
|
-
|
|
700
|
-
|
|
725
|
+
const skillName = 'skill-builder';
|
|
726
|
+
plantSkillDirectory(sandbox.skillsDirectory, skillName, true);
|
|
727
|
+
const stubPath = join(sandbox.skillsDirectory, skillName, 'SKILL.md');
|
|
728
|
+
const obsoleteReferencePath = join(sandbox.skillsDirectory, skillName, 'references', 'retired.md');
|
|
729
|
+
writeFileWithParents(obsoleteReferencePath, 'obsolete skill-builder guidance\n');
|
|
730
|
+
writeFileSync(sandbox.manifestPath, JSON.stringify({
|
|
731
|
+
package: 'claude-dev-env',
|
|
732
|
+
version: '0.0.0',
|
|
733
|
+
installedAt: new Date().toISOString(),
|
|
734
|
+
files: [stubPath, obsoleteReferencePath],
|
|
735
|
+
skills: [skillName],
|
|
736
|
+
}, null, 2) + '\n');
|
|
701
737
|
|
|
702
738
|
const installerOutput = runInstaller(sandbox.homeDirectory, []);
|
|
703
739
|
|
|
740
|
+
assert.equal(installerOutput.includes(SKIP_PRUNE_NOTICE_MARKER), false);
|
|
741
|
+
assert.equal(existsSync(stubPath), true, 'the requested stub stays installed');
|
|
742
|
+
assert.match(readFileSync(stubPath, 'utf8'), /TODO: Rework to follow pstack philosophy\./);
|
|
743
|
+
assert.ok(readManifest(sandbox.manifestPath).skills.includes(skillName));
|
|
744
|
+
assert.equal(existsSync(obsoleteReferencePath), false);
|
|
704
745
|
assert.equal(
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
'
|
|
708
|
-
);
|
|
709
|
-
const restoredSkillPath = join(sandbox.skillsDirectory, 'autoconverge', 'SKILL.md');
|
|
710
|
-
assert.equal(existsSync(restoredSkillPath), true, 'autoconverge survives and is reinstalled');
|
|
711
|
-
assert.notEqual(
|
|
712
|
-
readFileSync(restoredSkillPath, 'utf8'),
|
|
713
|
-
'stale seeded copy\n',
|
|
714
|
-
'the shipped autoconverge overwrites the stale seeded copy',
|
|
746
|
+
prunedSkillBackupContains(sandbox.claudeDirectory, join(skillName, 'references', 'retired.md')),
|
|
747
|
+
true,
|
|
748
|
+
'old implementation files move to the recovery backup while the stub stays',
|
|
715
749
|
);
|
|
716
750
|
} finally {
|
|
717
751
|
rmSync(sandbox.homeDirectory, { recursive: true, force: true });
|
|
@@ -739,20 +773,24 @@ test('a scoped --only install records the skill names it installed', () => {
|
|
|
739
773
|
}
|
|
740
774
|
});
|
|
741
775
|
|
|
742
|
-
test('a scoped core install ships
|
|
776
|
+
test('a fresh scoped core install ships active skills and omits retired skills', () => {
|
|
743
777
|
const sandbox = createSandbox();
|
|
744
778
|
try {
|
|
745
779
|
runInstaller(sandbox.homeDirectory, ['--only', 'core']);
|
|
746
780
|
|
|
747
|
-
const
|
|
781
|
+
const missingSkillNames = CORE_SKILL_DIRECTORIES.filter(
|
|
748
782
|
eachSkillName => !existsSync(join(sandbox.skillsDirectory, eachSkillName, 'SKILL.md')),
|
|
749
783
|
);
|
|
750
|
-
assert.deepEqual(
|
|
784
|
+
assert.deepEqual(missingSkillNames, []);
|
|
751
785
|
|
|
752
786
|
const recordedSkillNames = new Set(readManifest(sandbox.manifestPath).skills);
|
|
753
|
-
for (const eachSkillName of
|
|
787
|
+
for (const eachSkillName of CORE_SKILL_DIRECTORIES) {
|
|
754
788
|
assert.equal(recordedSkillNames.has(eachSkillName), true);
|
|
755
789
|
}
|
|
790
|
+
for (const eachSkillName of RETIRED_SKILL_DIRECTORIES) {
|
|
791
|
+
assert.equal(existsSync(join(sandbox.skillsDirectory, eachSkillName)), false);
|
|
792
|
+
assert.equal(recordedSkillNames.has(eachSkillName), false);
|
|
793
|
+
}
|
|
756
794
|
} finally {
|
|
757
795
|
rmSync(sandbox.homeDirectory, { recursive: true, force: true });
|
|
758
796
|
}
|
|
@@ -919,7 +957,7 @@ function seedRetiredManagedHook(sandbox) {
|
|
|
919
957
|
const retiredHookPath = join(
|
|
920
958
|
sandbox.claudeDirectory, HOOKS_DIRECTORY_NAME, ...RETIRED_HOOK_RELATIVE_SEGMENTS,
|
|
921
959
|
);
|
|
922
|
-
writeFileWithParents(retiredHookPath,
|
|
960
|
+
writeFileWithParents(retiredHookPath, RETIRED_HOOK_SEEDED_CONTENTS);
|
|
923
961
|
appendManifestFiles(sandbox.manifestPath, [retiredHookPath]);
|
|
924
962
|
const retiredHookCommand = `python3 "${retiredHookPath.replace(/\\/g, '/')}"`;
|
|
925
963
|
const lookalikeHookCommand = `python3 "${retiredHookPath.replace(/\\/g, '/')}.bak"`;
|
|
@@ -1019,7 +1057,7 @@ test('a full reinstall leaves a manifest-recorded path under no managed root in
|
|
|
1019
1057
|
}
|
|
1020
1058
|
});
|
|
1021
1059
|
|
|
1022
|
-
test('a reinstall
|
|
1060
|
+
test('a reinstall regenerates a stale post-commit shim and preserves a foreign git hook', () => {
|
|
1023
1061
|
const sandbox = createSandbox();
|
|
1024
1062
|
try {
|
|
1025
1063
|
runInstaller(sandbox.homeDirectory, []);
|
|
@@ -1029,18 +1067,18 @@ test('a reinstall removes a retired post-commit shim and preserves a foreign git
|
|
|
1029
1067
|
const postCommitShimPath = join(gitHooksDirectory, POST_COMMIT_HOOK_NAME);
|
|
1030
1068
|
const foreignHookPath = join(gitHooksDirectory, FOREIGN_HOOK_NAME);
|
|
1031
1069
|
const foreignHookContentBytes = Buffer.from('foreign hook content\n', 'utf8');
|
|
1032
|
-
|
|
1070
|
+
const staleShimContent = 'generated by an earlier install\n';
|
|
1071
|
+
writeFileWithParents(postCommitShimPath, staleShimContent);
|
|
1033
1072
|
writeFileWithParents(foreignHookPath, foreignHookContentBytes);
|
|
1034
1073
|
appendManifestFiles(sandbox.manifestPath, [postCommitShimPath]);
|
|
1035
1074
|
|
|
1036
1075
|
runInstaller(sandbox.homeDirectory, []);
|
|
1037
1076
|
|
|
1038
1077
|
const freshManifest = readManifest(sandbox.manifestPath);
|
|
1039
|
-
assert.
|
|
1078
|
+
assert.notEqual(readFileSync(postCommitShimPath, 'utf8'), staleShimContent);
|
|
1040
1079
|
assert.deepEqual(readFileSync(foreignHookPath), foreignHookContentBytes);
|
|
1041
|
-
assert.equal(freshManifest.files.includes(postCommitShimPath), false);
|
|
1042
1080
|
assert.equal(freshManifest.files.includes(foreignHookPath), false);
|
|
1043
|
-
for (const gitHookName of [PRE_COMMIT_HOOK_NAME, PRE_PUSH_HOOK_NAME]) {
|
|
1081
|
+
for (const gitHookName of [PRE_COMMIT_HOOK_NAME, PRE_PUSH_HOOK_NAME, POST_COMMIT_HOOK_NAME]) {
|
|
1044
1082
|
const generatedShimPath = join(gitHooksDirectory, gitHookName);
|
|
1045
1083
|
assert.equal(existsSync(generatedShimPath), true);
|
|
1046
1084
|
assert.equal(freshManifest.files.includes(generatedShimPath), true);
|
|
@@ -1050,7 +1088,7 @@ test('a reinstall removes a retired post-commit shim and preserves a foreign git
|
|
|
1050
1088
|
}
|
|
1051
1089
|
});
|
|
1052
1090
|
|
|
1053
|
-
test('a full reinstall
|
|
1091
|
+
test('a full reinstall retires a hook script and leaves an inert stand-in the open session can still run', () => {
|
|
1054
1092
|
const sandbox = createSandbox();
|
|
1055
1093
|
try {
|
|
1056
1094
|
runInstaller(sandbox.homeDirectory, []);
|
|
@@ -1058,9 +1096,26 @@ test('a full reinstall drops a retired hook script and its settings entry in one
|
|
|
1058
1096
|
retiredHookPath, retiredHookCommand, lookalikeHookCommand,
|
|
1059
1097
|
} = seedRetiredManagedHook(sandbox);
|
|
1060
1098
|
|
|
1061
|
-
|
|
1099
|
+
const installOutput = runInstallerReadingBothStreams(sandbox.homeDirectory, []);
|
|
1062
1100
|
|
|
1063
|
-
assert.
|
|
1101
|
+
assert.notEqual(
|
|
1102
|
+
readFileSync(retiredHookPath, 'utf8'),
|
|
1103
|
+
RETIRED_HOOK_SEEDED_CONTENTS,
|
|
1104
|
+
'the retired gate itself no longer runs at that path',
|
|
1105
|
+
);
|
|
1106
|
+
const standInRun = spawnSync(detectPython(), [retiredHookPath], { encoding: 'utf8' });
|
|
1107
|
+
assert.equal(
|
|
1108
|
+
standInRun.status,
|
|
1109
|
+
0,
|
|
1110
|
+
'a session holding the old registration runs the stand-in and gets an allow',
|
|
1111
|
+
);
|
|
1112
|
+
assert.equal(standInRun.stdout, '', 'the stand-in writes nothing to stdout');
|
|
1113
|
+
assert.equal(standInRun.stderr, '', 'the stand-in writes nothing to stderr');
|
|
1114
|
+
assert.match(
|
|
1115
|
+
installOutput,
|
|
1116
|
+
/Restart open sessions: this install retired blocking\/retired_gate\.py/,
|
|
1117
|
+
'the run names the retired registration and asks for a restart',
|
|
1118
|
+
);
|
|
1064
1119
|
assert.equal(
|
|
1065
1120
|
prunedBackupContains(
|
|
1066
1121
|
sandbox.claudeDirectory, join(HOOKS_DIRECTORY_NAME, ...RETIRED_HOOK_RELATIVE_SEGMENTS),
|
|
@@ -1084,6 +1139,59 @@ test('a full reinstall drops a retired hook script and its settings entry in one
|
|
|
1084
1139
|
true,
|
|
1085
1140
|
'a user command whose path is the retired tail plus a suffix names another file and stays',
|
|
1086
1141
|
);
|
|
1142
|
+
|
|
1143
|
+
runInstaller(sandbox.homeDirectory, []);
|
|
1144
|
+
|
|
1145
|
+
assert.equal(
|
|
1146
|
+
existsSync(retiredHookPath),
|
|
1147
|
+
false,
|
|
1148
|
+
'the next run finds no registration naming the stand-in, so the stand-in leaves too',
|
|
1149
|
+
);
|
|
1150
|
+
} finally {
|
|
1151
|
+
rmSync(sandbox.homeDirectory, { recursive: true, force: true });
|
|
1152
|
+
}
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
test('a full reinstall leaves a stand-in for a registration the package itself stopped shipping', () => {
|
|
1156
|
+
const sandbox = createSandbox();
|
|
1157
|
+
try {
|
|
1158
|
+
runInstaller(sandbox.homeDirectory, []);
|
|
1159
|
+
const retiredHookPath = join(
|
|
1160
|
+
sandbox.claudeDirectory,
|
|
1161
|
+
HOOKS_DIRECTORY_NAME,
|
|
1162
|
+
...FOLDED_RETIRED_HOOK_RELATIVE_SEGMENTS,
|
|
1163
|
+
);
|
|
1164
|
+
writeFileWithParents(retiredHookPath, RETIRED_HOOK_SEEDED_CONTENTS);
|
|
1165
|
+
appendManifestFiles(sandbox.manifestPath, [retiredHookPath]);
|
|
1166
|
+
const retiredHookCommand = `python3 "${retiredHookPath.replace(/\\/g, '/')}"`;
|
|
1167
|
+
const settings = readSettings(sandbox.claudeDirectory);
|
|
1168
|
+
settings.hooks[RETIRED_HOOK_EVENT_TYPE].push({
|
|
1169
|
+
matcher: 'AskUserQuestion',
|
|
1170
|
+
hooks: [{ type: 'command', command: retiredHookCommand }],
|
|
1171
|
+
});
|
|
1172
|
+
writeFileSync(
|
|
1173
|
+
join(sandbox.claudeDirectory, SETTINGS_FILE_NAME),
|
|
1174
|
+
JSON.stringify(settings, null, 4) + '\n',
|
|
1175
|
+
);
|
|
1176
|
+
|
|
1177
|
+
const installOutput = runInstallerReadingBothStreams(sandbox.homeDirectory, []);
|
|
1178
|
+
|
|
1179
|
+
assert.equal(
|
|
1180
|
+
allHookCommands(readSettings(sandbox.claudeDirectory)).includes(retiredHookCommand),
|
|
1181
|
+
false,
|
|
1182
|
+
'the registration the package stopped shipping leaves settings.json',
|
|
1183
|
+
);
|
|
1184
|
+
const standInRun = spawnSync(detectPython(), [retiredHookPath], { encoding: 'utf8' });
|
|
1185
|
+
assert.equal(
|
|
1186
|
+
standInRun.status,
|
|
1187
|
+
0,
|
|
1188
|
+
'a session still holding that registration runs a stand-in and gets an allow',
|
|
1189
|
+
);
|
|
1190
|
+
assert.match(
|
|
1191
|
+
installOutput,
|
|
1192
|
+
/Restart open sessions: this install retired blocking\/ask_user_question_shape_blocker\.py/,
|
|
1193
|
+
'the run names the registration the merge would otherwise drop in silence',
|
|
1194
|
+
);
|
|
1087
1195
|
} finally {
|
|
1088
1196
|
rmSync(sandbox.homeDirectory, { recursive: true, force: true });
|
|
1089
1197
|
}
|
|
@@ -1189,7 +1297,23 @@ test('a full reinstall removes retired pull request hooks and preserves foreign
|
|
|
1189
1297
|
|
|
1190
1298
|
const freshManifest = readManifest(sandbox.manifestPath);
|
|
1191
1299
|
const allCommands = allHookCommands(readSettings(sandbox.claudeDirectory));
|
|
1192
|
-
|
|
1300
|
+
const registeredRetiredPaths = new Set(RETIRED_PULL_REQUEST_REGISTRATION_PATHS);
|
|
1301
|
+
for (const relativePath of RETIRED_PULL_REQUEST_HOOK_RELATIVE_PATHS) {
|
|
1302
|
+
const retiredPath = join(
|
|
1303
|
+
sandbox.claudeDirectory, HOOKS_DIRECTORY_NAME, ...relativePath.split('/'),
|
|
1304
|
+
);
|
|
1305
|
+
if (registeredRetiredPaths.has(relativePath)) {
|
|
1306
|
+
assert.equal(
|
|
1307
|
+
spawnSync(detectPython(), [retiredPath], { encoding: 'utf8' }).status,
|
|
1308
|
+
0,
|
|
1309
|
+
`${relativePath} keeps an inert stand-in while a session still registers it`,
|
|
1310
|
+
);
|
|
1311
|
+
assert.ok(
|
|
1312
|
+
freshManifest.files.includes(retiredPath),
|
|
1313
|
+
`${relativePath} is recorded, so a later run retires the stand-in`,
|
|
1314
|
+
);
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1193
1317
|
assert.equal(existsSync(retiredPath), false, `${retiredPath} leaves installed hooks`);
|
|
1194
1318
|
assert.equal(freshManifest.files.includes(retiredPath), false);
|
|
1195
1319
|
}
|
|
@@ -1203,6 +1327,131 @@ test('a full reinstall removes retired pull request hooks and preserves foreign
|
|
|
1203
1327
|
}
|
|
1204
1328
|
});
|
|
1205
1329
|
|
|
1330
|
+
|
|
1331
|
+
const CODEX_DIRECTORY_NAME = '.codex';
|
|
1332
|
+
const CODEX_HOOKS_CONFIGURATION_FILE_NAME = 'hooks.json';
|
|
1333
|
+
const FOREIGN_TOOL_DIRECTORY_NAME = 'other-tool';
|
|
1334
|
+
const FOREIGN_CODEX_HOOK_COMMAND = 'python3 my_own_codex_gate.py --user-authored';
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Build the command one hook registration runs for a script under a hooks root.
|
|
1339
|
+
*
|
|
1340
|
+
* @param {string} hooksRoot The hooks directory the registration names.
|
|
1341
|
+
* @param {string[]} relativeSegments The script path segments under that directory.
|
|
1342
|
+
* @returns {string} The registration command.
|
|
1343
|
+
*/
|
|
1344
|
+
function hookCommandUnder(hooksRoot, relativeSegments) {
|
|
1345
|
+
return `python3 "${join(hooksRoot, ...relativeSegments).replace(/\\/g, '/')}"`;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
|
|
1349
|
+
/**
|
|
1350
|
+
* Write a Codex hooks file registering one retired script beside a foreign entry.
|
|
1351
|
+
*
|
|
1352
|
+
* @param {{homeDirectory: string}} sandbox The sandbox paths.
|
|
1353
|
+
* @returns {{codexHooksPath: string, codexRetiredCommand: string}} The file and its retired entry.
|
|
1354
|
+
*/
|
|
1355
|
+
function seedCodexHookRegistrations(sandbox) {
|
|
1356
|
+
const codexDirectory = join(sandbox.homeDirectory, CODEX_DIRECTORY_NAME);
|
|
1357
|
+
const codexHooksPath = join(codexDirectory, CODEX_HOOKS_CONFIGURATION_FILE_NAME);
|
|
1358
|
+
const codexRetiredCommand = hookCommandUnder(
|
|
1359
|
+
join(codexDirectory, HOOKS_DIRECTORY_NAME), RETIRED_HOOK_RELATIVE_SEGMENTS,
|
|
1360
|
+
);
|
|
1361
|
+
writeFileWithParents(codexHooksPath, JSON.stringify({
|
|
1362
|
+
hooks: {
|
|
1363
|
+
[RETIRED_HOOK_EVENT_TYPE]: [{
|
|
1364
|
+
matcher: 'Bash',
|
|
1365
|
+
hooks: [
|
|
1366
|
+
{ type: 'command', command: codexRetiredCommand },
|
|
1367
|
+
{ type: 'command', command: FOREIGN_CODEX_HOOK_COMMAND },
|
|
1368
|
+
],
|
|
1369
|
+
}],
|
|
1370
|
+
},
|
|
1371
|
+
}, null, 4) + '\n');
|
|
1372
|
+
return { codexHooksPath, codexRetiredCommand };
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
|
|
1376
|
+
test('a full reinstall prunes a retired registration the Codex hooks file holds and leaves a stand-in', () => {
|
|
1377
|
+
const sandbox = createSandbox();
|
|
1378
|
+
try {
|
|
1379
|
+
runInstaller(sandbox.homeDirectory, []);
|
|
1380
|
+
const retiredHookPath = join(
|
|
1381
|
+
sandbox.claudeDirectory, HOOKS_DIRECTORY_NAME, ...RETIRED_HOOK_RELATIVE_SEGMENTS,
|
|
1382
|
+
);
|
|
1383
|
+
writeFileWithParents(retiredHookPath, RETIRED_HOOK_SEEDED_CONTENTS);
|
|
1384
|
+
appendManifestFiles(sandbox.manifestPath, [retiredHookPath]);
|
|
1385
|
+
const { codexHooksPath, codexRetiredCommand } = seedCodexHookRegistrations(sandbox);
|
|
1386
|
+
|
|
1387
|
+
const installOutput = runInstallerReadingBothStreams(sandbox.homeDirectory, []);
|
|
1388
|
+
|
|
1389
|
+
const codexCommands = allHookCommands(JSON.parse(readFileSync(codexHooksPath, 'utf8')));
|
|
1390
|
+
assert.equal(
|
|
1391
|
+
codexCommands.includes(codexRetiredCommand),
|
|
1392
|
+
false,
|
|
1393
|
+
'the Codex registration naming the retired script is pruned like a settings.json one',
|
|
1394
|
+
);
|
|
1395
|
+
assert.equal(
|
|
1396
|
+
codexCommands.includes(FOREIGN_CODEX_HOOK_COMMAND),
|
|
1397
|
+
true,
|
|
1398
|
+
'the user-authored Codex entry sharing the matcher group survives',
|
|
1399
|
+
);
|
|
1400
|
+
assert.equal(
|
|
1401
|
+
spawnSync(detectPython(), [retiredHookPath], { encoding: 'utf8' }).status,
|
|
1402
|
+
0,
|
|
1403
|
+
'the Codex registration alone earns the retired path an inert stand-in',
|
|
1404
|
+
);
|
|
1405
|
+
assert.match(
|
|
1406
|
+
installOutput,
|
|
1407
|
+
/Restart open sessions: this install retired blocking\/retired_gate\.py/,
|
|
1408
|
+
'the run names the retired registration and asks for a restart',
|
|
1409
|
+
);
|
|
1410
|
+
} finally {
|
|
1411
|
+
rmSync(sandbox.homeDirectory, { recursive: true, force: true });
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
test('a full reinstall leaves a registration under a hooks root the installer does not own', () => {
|
|
1417
|
+
const sandbox = createSandbox();
|
|
1418
|
+
try {
|
|
1419
|
+
runInstaller(sandbox.homeDirectory, []);
|
|
1420
|
+
seedRetiredManagedHook(sandbox);
|
|
1421
|
+
const foreignHooksRoot = join(
|
|
1422
|
+
sandbox.homeDirectory, FOREIGN_TOOL_DIRECTORY_NAME, '.claude', HOOKS_DIRECTORY_NAME,
|
|
1423
|
+
);
|
|
1424
|
+
const foreignHookPath = join(foreignHooksRoot, ...RETIRED_HOOK_RELATIVE_SEGMENTS);
|
|
1425
|
+
const foreignHookBytes = Buffer.from('another tool owns this gate\n', 'utf8');
|
|
1426
|
+
writeFileWithParents(foreignHookPath, foreignHookBytes);
|
|
1427
|
+
const foreignCommand = hookCommandUnder(foreignHooksRoot, RETIRED_HOOK_RELATIVE_SEGMENTS);
|
|
1428
|
+
const settings = readSettings(sandbox.claudeDirectory);
|
|
1429
|
+
settings.hooks[RETIRED_HOOK_EVENT_TYPE].push({
|
|
1430
|
+
matcher: 'Bash',
|
|
1431
|
+
hooks: [{ type: 'command', command: foreignCommand }],
|
|
1432
|
+
});
|
|
1433
|
+
writeFileSync(
|
|
1434
|
+
join(sandbox.claudeDirectory, SETTINGS_FILE_NAME),
|
|
1435
|
+
JSON.stringify(settings, null, 4) + '\n',
|
|
1436
|
+
);
|
|
1437
|
+
|
|
1438
|
+
runInstaller(sandbox.homeDirectory, []);
|
|
1439
|
+
|
|
1440
|
+
assert.equal(
|
|
1441
|
+
allHookCommands(readSettings(sandbox.claudeDirectory)).includes(foreignCommand),
|
|
1442
|
+
true,
|
|
1443
|
+
'a registration whose hooks root the installer does not own keeps its entry',
|
|
1444
|
+
);
|
|
1445
|
+
assert.deepEqual(
|
|
1446
|
+
readFileSync(foreignHookPath),
|
|
1447
|
+
foreignHookBytes,
|
|
1448
|
+
'the foreign script keeps its own bytes, so no stand-in replaced it',
|
|
1449
|
+
);
|
|
1450
|
+
} finally {
|
|
1451
|
+
rmSync(sandbox.homeDirectory, { recursive: true, force: true });
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1206
1455
|
test('a full reinstall removes a retired hook entry under an event type the current config leaves out', () => {
|
|
1207
1456
|
const sandbox = createSandbox();
|
|
1208
1457
|
try {
|