claude-dev-env 4.2.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.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 +7 -0
- package/.agents/skills/pull-request/SKILL.md +216 -0
- package/.agents/skills/pull-request/reference/publication-tasks.md +18 -0
- package/.agents/skills/pull-request/scripts/github_pr_command_constants/__init__.py +1 -0
- package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/__init__.py +1 -0
- package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +69 -0
- package/.agents/skills/pull-request/scripts/pull_request.py +265 -0
- package/.agents/skills/pull-request/scripts/recover_legacy_author.py +251 -0
- package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +58 -0
- package/.agents/skills/pull-request/scripts/test_pull_request.py +396 -0
- package/.agents/skills/pull-request/scripts/test_recover_legacy_author.py +221 -0
- package/.agents/skills/skill-builder/SKILL.md +3 -196
- package/.agents/skills/test-runner/SKILL.md +69 -0
- package/.agents/skills/test-runner/scripts/preflight_checks.py +365 -0
- package/.agents/skills/test-runner/scripts/run_tests.py +316 -0
- package/.agents/skills/test-runner/scripts/test_preflight_checks.py +185 -0
- package/.agents/skills/test-runner/scripts/test_run_tests.py +281 -0
- package/.agents/skills/test-runner/scripts/test_runner_constants/__init__.py +1 -0
- package/.agents/skills/test-runner/scripts/test_runner_constants/config/__init__.py +1 -0
- package/.agents/skills/test-runner/scripts/test_runner_constants/config/constants.py +96 -0
- package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
- 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/ever-shipped-skills.mjs +2 -0
- package/bin/ever-shipped-skills.test.mjs +8 -0
- package/bin/install.agents-home.test.mjs +15 -13
- package/bin/install.mjs +37 -1
- package/bin/install.policy-lint.test.mjs +78 -0
- package/bin/install.prune.test.mjs +149 -20
- package/bin/install.pstack-plugin.test.mjs +114 -0
- package/bin/install.test.mjs +61 -7
- package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
- 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_paired_test.py +22 -15
- package/hooks/blocking/pii_prevention_blocker.py +32 -51
- package/hooks/blocking/test_bash_post_call_dispatcher.py +55 -17
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +1 -4
- package/hooks/blocking/test_code_rules_command_dispatch.py +2 -2
- 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_pii_prevention_blocker.py +37 -89
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +68 -133
- package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
- package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +18 -18
- package/hooks/gh_post_body_texts.py +195 -0
- package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
- package/hooks/git-hooks/pre_commit.py +52 -11
- package/hooks/git-hooks/test_installed_policy_lint.py +159 -0
- package/hooks/git-hooks/test_native_policy_commit.py +87 -0
- package/hooks/git-hooks/test_pre_commit.py +246 -0
- package/hooks/hooks.json +1 -16
- package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -2
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -8
- package/hooks/hooks_constants/gh_post_body_texts_constants.py +12 -0
- package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -43
- package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +17 -7
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -7
- package/hooks/hooks_constants/test_failure_recorder_constants.py +3 -3
- package/hooks/hooks_constants/test_gh_post_body_texts_constants.py +19 -0
- package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +20 -32
- package/hooks/observability/test_failure_recorder.py +1 -1
- package/hooks/pyproject.toml +1 -1
- package/hooks/test_gh_post_body_texts.py +79 -0
- package/package.json +2 -2
- package/rules/durable-post-artifacts.md +32 -20
- package/rules/gh-cli-conventions.md +23 -13
- package/rules/git-workflow.md +21 -8
- 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/dev_env_scripts_constants/shared_tree_constants.py +4 -5
- package/scripts/durable_post_lint.py +206 -0
- package/scripts/durable_post_lint_config/config/constants.py +96 -0
- package/scripts/durable_post_lint_config/config/test_constants.py +25 -0
- package/scripts/policy_lint/adapter_configuration.py +53 -12
- package/scripts/policy_lint/adapter_detectors.py +13 -6
- package/scripts/policy_lint/adapters.py +7 -4
- 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/refresh_pstack_plugin_skills.mjs +200 -0
- package/scripts/repository_checks/config/constants.py +0 -5
- package/scripts/shared_tree_paths.py +32 -3
- package/scripts/test_durable_post_lint.py +332 -0
- package/scripts/test_shared_tree_paths.py +33 -0
- package/scripts/tests/test_adapter_configuration.py +54 -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_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_rules.py +3 -7
- package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
- package/.agents/skills/_shared/pr-loop/AGENTS.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/CLAUDE.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/_gh_pr_author_swap_utils.py +0 -1225
- package/hooks/blocking/conventional_pr_title_gate.py +0 -443
- package/hooks/blocking/gh_body_arg_blocker.py +0 -176
- package/hooks/blocking/gh_pr_author_enforcer.py +0 -487
- package/hooks/blocking/gh_pr_author_restore.py +0 -105
- package/hooks/blocking/pr_description_writer_gate.py +0 -256
- package/hooks/blocking/test_conventional_pr_title_gate.py +0 -640
- package/hooks/blocking/test_gh_body_arg_blocker.py +0 -441
- package/hooks/blocking/test_gh_pr_author_enforcer.py +0 -1166
- package/hooks/blocking/test_gh_pr_author_restore.py +0 -513
- package/hooks/blocking/test_gh_pr_author_swap_utils.py +0 -910
- package/hooks/blocking/test_pr_description_writer_gate.py +0 -234
- package/hooks/blocking/test_preflight_check.py +0 -343
- package/hooks/blocking/test_test_preflight_check.py +0 -80
- package/hooks/blocking/test_volatile_path_in_post_blocker.py +0 -322
- package/hooks/blocking/volatile_path_in_post_blocker.py +0 -461
- package/hooks/hooks_constants/conventional_pr_title_gate_constants.py +0 -58
- package/hooks/hooks_constants/gh_pr_author_swap_constants.py +0 -76
- package/hooks/hooks_constants/pr_description_writer_gate_constants.py +0 -73
- package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +0 -55
- package/hooks/observability/pr_description_writer_spawn_tracker.py +0 -113
- package/hooks/observability/test_pr_description_writer_spawn_tracker.py +0 -150
- package/hooks/session/gh_pr_author_session_cleanup.py +0 -175
- package/hooks/session/test_gh_pr_author_session_cleanup.py +0 -576
- package/hooks/test__gh_pr_author_swap_utils.py +0 -333
|
@@ -215,12 +215,27 @@ def _collect_referenced_identifiers(source: str) -> set[str]:
|
|
|
215
215
|
return all_referenced_identifiers
|
|
216
216
|
|
|
217
217
|
|
|
218
|
+
def _sibling_test_paths(stem_test_path: Path) -> list[Path]:
|
|
219
|
+
"""Return the capped, sorted test files beside the stem test, stem excluded."""
|
|
220
|
+
suite_directory = stem_test_path.parent
|
|
221
|
+
stem_path_key = os.path.normcase(str(stem_test_path.resolve()))
|
|
222
|
+
all_sibling_paths: list[Path] = []
|
|
223
|
+
for each_glob in ALL_TEST_FILENAME_GLOBS:
|
|
224
|
+
all_sibling_paths.extend(
|
|
225
|
+
each_path
|
|
226
|
+
for each_path in sorted(suite_directory.glob(each_glob))
|
|
227
|
+
if os.path.normcase(str(each_path.resolve())) != stem_path_key
|
|
228
|
+
)
|
|
229
|
+
return all_sibling_paths[:MAX_TEST_FILES_SCANNED]
|
|
230
|
+
|
|
231
|
+
|
|
218
232
|
def _suite_referenced_identifiers(stem_test_path: Path) -> set[str]:
|
|
219
233
|
"""Return identifiers referenced across every test file beside the stem test.
|
|
220
234
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
each one references.
|
|
235
|
+
Reads the stem-matched test file first, then scans every other ``test_*.py``
|
|
236
|
+
and ``*_test.py`` file in its directory, bounded by the scan cap, and unions
|
|
237
|
+
the identifiers each one references. The stem test always counts, so a
|
|
238
|
+
directory with more test files than the cap cannot hide it.
|
|
224
239
|
|
|
225
240
|
Args:
|
|
226
241
|
stem_test_path: The stem-matched test file whose directory is scanned.
|
|
@@ -228,12 +243,9 @@ def _suite_referenced_identifiers(stem_test_path: Path) -> set[str]:
|
|
|
228
243
|
Returns:
|
|
229
244
|
The union of referenced identifiers across the scanned test files.
|
|
230
245
|
"""
|
|
231
|
-
|
|
232
|
-
all_test_file_paths: list[Path] = []
|
|
233
|
-
for each_glob in ALL_TEST_FILENAME_GLOBS:
|
|
234
|
-
all_test_file_paths.extend(sorted(suite_directory.glob(each_glob)))
|
|
246
|
+
all_test_file_paths = [stem_test_path, *_sibling_test_paths(stem_test_path)]
|
|
235
247
|
all_referenced_identifiers: set[str] = set()
|
|
236
|
-
for each_test_file_path in all_test_file_paths
|
|
248
|
+
for each_test_file_path in all_test_file_paths:
|
|
237
249
|
try:
|
|
238
250
|
test_source = each_test_file_path.read_text(encoding="utf-8")
|
|
239
251
|
except (OSError, UnicodeDecodeError):
|
|
@@ -364,16 +376,12 @@ def _production_stem_from_test_filename(test_filename: str) -> str | None:
|
|
|
364
376
|
if test_filename.startswith(STEM_TEST_FILENAME_PREFIX) and test_filename.endswith(
|
|
365
377
|
PYTHON_SOURCE_SUFFIX
|
|
366
378
|
):
|
|
367
|
-
production_stem = test_filename[
|
|
368
|
-
len(STEM_TEST_FILENAME_PREFIX) : -len(PYTHON_SOURCE_SUFFIX)
|
|
369
|
-
]
|
|
379
|
+
production_stem = test_filename[len(STEM_TEST_FILENAME_PREFIX) : -len(PYTHON_SOURCE_SUFFIX)]
|
|
370
380
|
return production_stem or None
|
|
371
381
|
return None
|
|
372
382
|
|
|
373
383
|
|
|
374
|
-
def _paired_production_module_path(
|
|
375
|
-
test_path: Path, production_stem: str
|
|
376
|
-
) -> Path | None:
|
|
384
|
+
def _paired_production_module_path(test_path: Path, production_stem: str) -> Path | None:
|
|
377
385
|
"""Return the production module a stem-matched test file pairs with, or None.
|
|
378
386
|
|
|
379
387
|
Mirrors ``_stem_matched_test_path`` in reverse for the two common layouts: a
|
|
@@ -515,4 +523,3 @@ def check_test_file_omits_module_public_function(
|
|
|
515
523
|
if len(all_omission_violations) >= MAX_PAIRED_TEST_COVERAGE_ISSUES:
|
|
516
524
|
break
|
|
517
525
|
return all_omission_violations
|
|
518
|
-
|
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""PreToolUse hook: block writes, durable posts, and commits that carry PII.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
A repository named in ``CLAUDE_PII_EXEMPT_REPOS`` or the ``pii_exempt_repositories``
|
|
18
|
-
list in ``~/.claude/local-identity.json`` skips the staged-commit scan; a
|
|
19
|
-
repository without a readable origin remote is never exempt. Exact values listed
|
|
20
|
-
under ``pii_allowlisted_values`` for a repository's origin slug may pass the
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
Write/Edit/MultiEdit -> scan new disk content
|
|
7
|
+
gh post / GitHub MCP -> scan durable bodies
|
|
8
|
+
git commit -> scan staged blob text, not the commit message
|
|
9
|
+
|
|
10
|
+
``pii_payload_scan`` and ``pii_commit_command`` re-export through this
|
|
11
|
+
entry module so hooks.json wiring and importers stay unchanged.
|
|
12
|
+
``CLAUDE_PII_EXEMPT_REPOS`` or ``pii_exempt_repositories`` in
|
|
13
|
+
``~/.claude/local-identity.json`` skip the staged-commit scan. A repo
|
|
14
|
+
without a readable origin remote is never exempt.
|
|
15
|
+
``pii_allowlisted_values`` for a repository origin slug may pass
|
|
21
16
|
Write/Edit and staged-commit scans in that repository only.
|
|
22
17
|
"""
|
|
23
18
|
|
|
@@ -56,7 +51,7 @@ try:
|
|
|
56
51
|
)
|
|
57
52
|
from pii_scanner import is_path_exempt_from_pii_scan, scan_text_for_pii
|
|
58
53
|
from precommit_code_rules_gate import resolve_repository_root
|
|
59
|
-
from
|
|
54
|
+
from gh_post_body_texts import (
|
|
60
55
|
extract_gh_post_body_texts_for_privacy_gate,
|
|
61
56
|
extract_mcp_body_texts,
|
|
62
57
|
)
|
|
@@ -124,7 +119,8 @@ def list_staged_file_paths(
|
|
|
124
119
|
try:
|
|
125
120
|
completed_process = subprocess.run(
|
|
126
121
|
list(ALL_STAGED_FILES_COMMAND),
|
|
127
|
-
check=False,
|
|
122
|
+
check=False,
|
|
123
|
+
capture_output=True,
|
|
128
124
|
text=True,
|
|
129
125
|
timeout=GIT_COMMAND_TIMEOUT_SECONDS,
|
|
130
126
|
cwd=str(repository_root),
|
|
@@ -148,7 +144,8 @@ def _git_show_staged_blob(
|
|
|
148
144
|
try:
|
|
149
145
|
completed_process = subprocess.run(
|
|
150
146
|
list(ALL_STAGED_BLOB_SHOW_COMMAND_PREFIX) + [staged_blob_reference],
|
|
151
|
-
check=False,
|
|
147
|
+
check=False,
|
|
148
|
+
capture_output=True,
|
|
152
149
|
timeout=GIT_COMMAND_TIMEOUT_SECONDS,
|
|
153
150
|
cwd=str(repository_root),
|
|
154
151
|
)
|
|
@@ -197,9 +194,7 @@ def _scan_staged_path(
|
|
|
197
194
|
repository_root: Path, relative_path: str, all_allowlisted_values: frozenset[str]
|
|
198
195
|
) -> str | None:
|
|
199
196
|
"""Return a deny reason for one staged path, or None when it is clean."""
|
|
200
|
-
staged_text, unscannable_reason = read_staged_file_text(
|
|
201
|
-
repository_root, relative_path
|
|
202
|
-
)
|
|
197
|
+
staged_text, unscannable_reason = read_staged_file_text(repository_root, relative_path)
|
|
203
198
|
if unscannable_reason is not None:
|
|
204
199
|
return unscannable_reason
|
|
205
200
|
if staged_text is None:
|
|
@@ -217,9 +212,9 @@ def _scan_staged_path(
|
|
|
217
212
|
def evaluate_staged_commit(repository_root: Path) -> str | None:
|
|
218
213
|
"""Return a deny reason when staged content carries PII or is unscannable.
|
|
219
214
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
215
|
+
Git list or show failures and unscannable blobs deny the commit. A value
|
|
216
|
+
in the repository's PII allowlist is dropped from the findings, so its
|
|
217
|
+
commits may carry it.
|
|
223
218
|
|
|
224
219
|
Args:
|
|
225
220
|
repository_root: Repository whose index is about to be committed.
|
|
@@ -234,22 +229,18 @@ def evaluate_staged_commit(repository_root: Path) -> str | None:
|
|
|
234
229
|
for each_relative_path in all_relative_paths:
|
|
235
230
|
if is_path_exempt_from_pii_scan(each_relative_path):
|
|
236
231
|
continue
|
|
237
|
-
deny_reason = _scan_staged_path(
|
|
238
|
-
repository_root, each_relative_path, all_allowlisted_values
|
|
239
|
-
)
|
|
232
|
+
deny_reason = _scan_staged_path(repository_root, each_relative_path, all_allowlisted_values)
|
|
240
233
|
if deny_reason is not None:
|
|
241
234
|
return deny_reason
|
|
242
235
|
return None
|
|
243
236
|
|
|
244
237
|
|
|
245
|
-
def _evaluate_commit_staged_content(
|
|
246
|
-
bash_command: str, working_directory: str | None
|
|
247
|
-
) -> str | None:
|
|
238
|
+
def _evaluate_commit_staged_content(bash_command: str, working_directory: str | None) -> str | None:
|
|
248
239
|
"""Scan the staged content of the repository the commit command targets.
|
|
249
240
|
|
|
250
|
-
Resolves the repository from the command's ``-C
|
|
251
|
-
to the session working directory only when the command names none
|
|
252
|
-
removed cwd
|
|
241
|
+
Resolves the repository from the command's ``-C`` or ``cd`` path. Falls
|
|
242
|
+
back to the session working directory only when the command names none. A
|
|
243
|
+
removed cwd does not block a commit that targets a valid repo.
|
|
253
244
|
|
|
254
245
|
Args:
|
|
255
246
|
bash_command: The Bash or PowerShell tool command string.
|
|
@@ -259,9 +250,7 @@ def _evaluate_commit_staged_content(
|
|
|
259
250
|
Deny reason text, or None when the staged content is clean or exempt.
|
|
260
251
|
"""
|
|
261
252
|
command_directory = compose_command_working_directory(bash_command)
|
|
262
|
-
attempted_directory =
|
|
263
|
-
command_directory if command_directory is not None else working_directory
|
|
264
|
-
)
|
|
253
|
+
attempted_directory = command_directory if command_directory is not None else working_directory
|
|
265
254
|
expanded_directory = expand_user_directory(attempted_directory)
|
|
266
255
|
repository_root = resolve_repository_root(expanded_directory)
|
|
267
256
|
if repository_root is None:
|
|
@@ -271,9 +260,7 @@ def _evaluate_commit_staged_content(
|
|
|
271
260
|
return evaluate_staged_commit(repository_root)
|
|
272
261
|
|
|
273
262
|
|
|
274
|
-
def evaluate_bash_command(
|
|
275
|
-
bash_command: str, working_directory: str | None
|
|
276
|
-
) -> str | None:
|
|
263
|
+
def evaluate_bash_command(bash_command: str, working_directory: str | None) -> str | None:
|
|
277
264
|
"""Return a deny reason for a shell gh post or git commit with PII.
|
|
278
265
|
|
|
279
266
|
Args:
|
|
@@ -284,10 +271,8 @@ def evaluate_bash_command(
|
|
|
284
271
|
Returns:
|
|
285
272
|
Deny reason text, or None when the command is clean or out of scope.
|
|
286
273
|
"""
|
|
287
|
-
all_post_bodies, body_file_failure_reason = (
|
|
288
|
-
|
|
289
|
-
bash_command, working_directory=working_directory
|
|
290
|
-
)
|
|
274
|
+
all_post_bodies, body_file_failure_reason = extract_gh_post_body_texts_for_privacy_gate(
|
|
275
|
+
bash_command, working_directory=working_directory
|
|
291
276
|
)
|
|
292
277
|
if body_file_failure_reason is not None:
|
|
293
278
|
return body_file_failure_reason
|
|
@@ -308,9 +293,7 @@ def _evaluate_shell_tool(
|
|
|
308
293
|
return None
|
|
309
294
|
working_directory_value = all_tool_input.get("working_directory")
|
|
310
295
|
working_directory = (
|
|
311
|
-
working_directory_value
|
|
312
|
-
if isinstance(working_directory_value, str)
|
|
313
|
-
else None
|
|
296
|
+
working_directory_value if isinstance(working_directory_value, str) else None
|
|
314
297
|
)
|
|
315
298
|
if working_directory is None:
|
|
316
299
|
cwd_value = payload_by_key.get("cwd")
|
|
@@ -332,9 +315,7 @@ def evaluate(payload_by_key: dict[str, object]) -> str | None:
|
|
|
332
315
|
raw_tool_input = payload_by_key.get("tool_input", {})
|
|
333
316
|
all_tool_input = raw_tool_input if isinstance(raw_tool_input, dict) else {}
|
|
334
317
|
if tool_name in ALL_WRITE_EDIT_MULTI_EDIT_TOOL_NAMES:
|
|
335
|
-
return evaluate_write_edit_payload(
|
|
336
|
-
tool_name, all_tool_input, hook_payload=payload_by_key
|
|
337
|
-
)
|
|
318
|
+
return evaluate_write_edit_payload(tool_name, all_tool_input, hook_payload=payload_by_key)
|
|
338
319
|
if tool_name == APPLY_PATCH_TOOL_NAME:
|
|
339
320
|
return evaluate_apply_patch_payload(all_tool_input, hook_payload=payload_by_key)
|
|
340
321
|
if tool_name in ALL_SHELL_TOOL_NAMES:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Behavior tests for the Bash PostToolUse dispatcher.
|
|
2
2
|
|
|
3
3
|
One interpreter start runs every hosted PostToolUse hook a Bash call fires.
|
|
4
|
-
The unit tests pin selection
|
|
4
|
+
The unit tests pin selection and call order with a fake
|
|
5
5
|
runner; the end-to-end test drives the real hosted hooks through a real
|
|
6
6
|
payload and confirms the recorder's own real side effect lands.
|
|
7
7
|
``hooks/conftest.py`` already puts the hooks and blocking directories on
|
|
@@ -15,19 +15,26 @@ from pathlib import Path
|
|
|
15
15
|
|
|
16
16
|
import pytest
|
|
17
17
|
|
|
18
|
-
from bash_post_call_dispatcher import
|
|
18
|
+
from bash_post_call_dispatcher import (
|
|
19
|
+
additional_context_from_hook_output,
|
|
20
|
+
dispatch,
|
|
21
|
+
main,
|
|
22
|
+
select_applicable_entries,
|
|
23
|
+
)
|
|
19
24
|
from hooks_constants.bash_pre_tool_use_dispatcher_constants import BASH_TOOL_NAME
|
|
20
25
|
from hooks_constants.hosted_hook_runner import HostedHookRun
|
|
21
26
|
from tdd_enforcer_parts import content_hash_store
|
|
22
27
|
|
|
28
|
+
_ALL_EXPECTED_ROSTER_PATHS = [
|
|
29
|
+
"observability/test_failure_recorder.py",
|
|
30
|
+
"advisory/pr_done_reminder.py",
|
|
31
|
+
]
|
|
23
32
|
|
|
24
|
-
|
|
33
|
+
|
|
34
|
+
def test_select_applicable_entries_returns_the_hosted_hooks_for_bash() -> None:
|
|
25
35
|
all_entries = select_applicable_entries(BASH_TOOL_NAME)
|
|
26
36
|
all_script_paths = [each_entry.script_relative_path for each_entry in all_entries]
|
|
27
|
-
assert all_script_paths ==
|
|
28
|
-
"blocking/gh_pr_author_restore.py",
|
|
29
|
-
"observability/test_failure_recorder.py",
|
|
30
|
-
]
|
|
37
|
+
assert all_script_paths == _ALL_EXPECTED_ROSTER_PATHS
|
|
31
38
|
|
|
32
39
|
|
|
33
40
|
def test_select_applicable_entries_returns_none_for_a_write_tool() -> None:
|
|
@@ -48,27 +55,58 @@ def test_dispatch_runs_every_hosted_hook_in_roster_order(
|
|
|
48
55
|
|
|
49
56
|
dispatch('{"tool_name": "Bash"}', BASH_TOOL_NAME)
|
|
50
57
|
|
|
51
|
-
assert all_call_paths == [
|
|
58
|
+
assert all_call_paths == [Path(each_path).name for each_path in _ALL_EXPECTED_ROSTER_PATHS]
|
|
52
59
|
|
|
53
60
|
|
|
54
|
-
def
|
|
55
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
61
|
+
def test_dispatch_forwards_hosted_hook_additional_context_as_one_payload(
|
|
62
|
+
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
56
63
|
) -> None:
|
|
57
|
-
|
|
64
|
+
context_by_hook = {
|
|
65
|
+
"test_failure_recorder.py": "",
|
|
66
|
+
"pr_done_reminder.py": json.dumps(
|
|
67
|
+
{"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "PR #42"}}
|
|
68
|
+
),
|
|
69
|
+
}
|
|
58
70
|
|
|
59
71
|
def _fake_run_hook(script_path: str, payload_text: str) -> HostedHookRun:
|
|
60
72
|
del payload_text
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
return HostedHookRun(
|
|
74
|
+
captured_stdout=context_by_hook[Path(script_path).name], did_crash=False
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
monkeypatch.setattr("bash_post_call_dispatcher.run_hook_capturing_output", _fake_run_hook)
|
|
78
|
+
|
|
79
|
+
dispatch('{"tool_name": "Bash"}', BASH_TOOL_NAME)
|
|
80
|
+
|
|
81
|
+
emitted = json.loads(capsys.readouterr().out)
|
|
82
|
+
assert emitted == {
|
|
83
|
+
"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "PR #42"}
|
|
84
|
+
}
|
|
85
|
+
assert "decision" not in emitted
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_dispatch_writes_nothing_when_no_hosted_hook_printed_context(
|
|
89
|
+
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
90
|
+
) -> None:
|
|
91
|
+
def _fake_run_hook(script_path: str, payload_text: str) -> HostedHookRun:
|
|
92
|
+
del script_path, payload_text
|
|
65
93
|
return HostedHookRun(captured_stdout="", did_crash=False)
|
|
66
94
|
|
|
67
95
|
monkeypatch.setattr("bash_post_call_dispatcher.run_hook_capturing_output", _fake_run_hook)
|
|
68
96
|
|
|
69
97
|
dispatch('{"tool_name": "Bash"}', BASH_TOOL_NAME)
|
|
70
98
|
|
|
71
|
-
assert
|
|
99
|
+
assert capsys.readouterr().out == ""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@pytest.mark.parametrize(
|
|
103
|
+
"captured_stdout",
|
|
104
|
+
["", " ", "not json", "[]", '{"decision": "block"}', '{"hookSpecificOutput": {}}'],
|
|
105
|
+
)
|
|
106
|
+
def test_additional_context_from_hook_output_ignores_non_context_output(
|
|
107
|
+
captured_stdout: str,
|
|
108
|
+
) -> None:
|
|
109
|
+
assert additional_context_from_hook_output(captured_stdout) is None
|
|
72
110
|
|
|
73
111
|
|
|
74
112
|
def test_main_exits_zero_and_writes_nothing_to_stdout(
|
|
@@ -85,7 +123,7 @@ def test_main_exits_zero_and_writes_nothing_to_stdout(
|
|
|
85
123
|
|
|
86
124
|
|
|
87
125
|
def test_end_to_end_real_hosted_hooks_record_a_real_pytest_failure(tmp_path: Path) -> None:
|
|
88
|
-
"""
|
|
126
|
+
"""Drive the real recorder through the dispatcher."""
|
|
89
127
|
test_file = tmp_path / "test_orders.py"
|
|
90
128
|
test_file.write_text("def test_fulfill(): assert False\n")
|
|
91
129
|
session_id = "post-dispatcher-session"
|
|
@@ -222,7 +222,6 @@ def test_powershell_selects_the_shared_hooks_in_registration_order() -> None:
|
|
|
222
222
|
"blocking/cursor_cli_python_misfire_blocker.py",
|
|
223
223
|
"blocking/unscoped_search_blocker.py",
|
|
224
224
|
"blocking/pii_prevention_blocker.py",
|
|
225
|
-
"blocking/pr_description_writer_gate.py",
|
|
226
225
|
]
|
|
227
226
|
|
|
228
227
|
|
|
@@ -270,9 +269,7 @@ def test_dispatcher_denies_subshell_disguised_as_arithmetic_expansion() -> None:
|
|
|
270
269
|
the live Bash tool path.
|
|
271
270
|
"""
|
|
272
271
|
payload_text = _bash_payload("echo $((cd /tmp) && pwd)")
|
|
273
|
-
decision, _reason = _decision_from_stdout(
|
|
274
|
-
_run_process(_DISPATCHER_SCRIPT, payload_text).stdout
|
|
275
|
-
)
|
|
272
|
+
decision, _reason = _decision_from_stdout(_run_process(_DISPATCHER_SCRIPT, payload_text).stdout)
|
|
276
273
|
assert decision == "deny"
|
|
277
274
|
|
|
278
275
|
|
|
@@ -11,7 +11,7 @@ from code_rules_command_dispatch import ( # noqa: E402
|
|
|
11
11
|
check_unanchored_command_dispatch,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
GATE_PATH = "packages/claude-dev-env/hooks/blocking/
|
|
14
|
+
GATE_PATH = "packages/claude-dev-env/hooks/blocking/block_main_commit.py"
|
|
15
15
|
|
|
16
16
|
UNANCHORED_SOURCE = (
|
|
17
17
|
"import re\n"
|
|
@@ -84,7 +84,7 @@ def test_ignores_file_outside_hooks_blocking() -> None:
|
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
def test_ignores_test_file() -> None:
|
|
87
|
-
test_path = "packages/claude-dev-env/hooks/blocking/
|
|
87
|
+
test_path = "packages/claude-dev-env/hooks/blocking/test_block_main_commit.py"
|
|
88
88
|
assert check_unanchored_command_dispatch(UNANCHORED_SOURCE, test_path) == []
|
|
89
89
|
|
|
90
90
|
|
|
@@ -19,6 +19,10 @@ _BLOCKING_DIRECTORY = Path(__file__).resolve().parent
|
|
|
19
19
|
if str(_BLOCKING_DIRECTORY) not in sys.path:
|
|
20
20
|
sys.path.insert(0, str(_BLOCKING_DIRECTORY))
|
|
21
21
|
|
|
22
|
+
from hooks_constants.paired_test_coverage_constants import ( # noqa: E402
|
|
23
|
+
MAX_TEST_FILES_SCANNED,
|
|
24
|
+
)
|
|
25
|
+
|
|
22
26
|
from code_rules_paired_test import ( # noqa: E402
|
|
23
27
|
check_public_function_missing_paired_test,
|
|
24
28
|
check_test_file_omits_module_public_function,
|
|
@@ -26,9 +30,7 @@ from code_rules_paired_test import ( # noqa: E402
|
|
|
26
30
|
|
|
27
31
|
_TWO_PUBLIC_FUNCTIONS = "def alpha():\n return 1\n\n\ndef beta():\n return 2\n"
|
|
28
32
|
_THREE_PUBLIC_FUNCTIONS = (
|
|
29
|
-
"def alpha():\n return 1\n\n\n"
|
|
30
|
-
"def beta():\n return 2\n\n\n"
|
|
31
|
-
"def gamma():\n return 3\n"
|
|
33
|
+
"def alpha():\n return 1\n\n\ndef beta():\n return 2\n\n\ndef gamma():\n return 3\n"
|
|
32
34
|
)
|
|
33
35
|
|
|
34
36
|
|
|
@@ -145,6 +147,28 @@ def test_counts_coverage_across_sibling_test_files(
|
|
|
145
147
|
assert all_issues == []
|
|
146
148
|
|
|
147
149
|
|
|
150
|
+
def test_reads_the_stem_test_when_the_suite_exceeds_the_scan_cap(
|
|
151
|
+
neutral_package_directory: Path,
|
|
152
|
+
) -> None:
|
|
153
|
+
filler_body = "def test_nothing():\n assert True\n"
|
|
154
|
+
_write_test_file(
|
|
155
|
+
neutral_package_directory,
|
|
156
|
+
"test_a0000.py",
|
|
157
|
+
"from pkg.mod import alpha\n\ndef test_alpha():\n assert alpha() == 1\n",
|
|
158
|
+
)
|
|
159
|
+
for each_index in range(1, MAX_TEST_FILES_SCANNED + 1):
|
|
160
|
+
_write_test_file(neutral_package_directory, f"test_a{each_index:04d}.py", filler_body)
|
|
161
|
+
_write_test_file(
|
|
162
|
+
neutral_package_directory,
|
|
163
|
+
"test_mod.py",
|
|
164
|
+
"from pkg.mod import beta\n\ndef test_beta():\n assert beta() == 2\n",
|
|
165
|
+
)
|
|
166
|
+
all_issues = check_public_function_missing_paired_test(
|
|
167
|
+
_TWO_PUBLIC_FUNCTIONS, str(neutral_package_directory / "mod.py")
|
|
168
|
+
)
|
|
169
|
+
assert all_issues == []
|
|
170
|
+
|
|
171
|
+
|
|
148
172
|
def test_main_and_private_functions_are_never_required(
|
|
149
173
|
neutral_package_directory: Path,
|
|
150
174
|
) -> None:
|
|
@@ -183,8 +207,7 @@ def _write_module(package_directory: Path, body: str) -> None:
|
|
|
183
207
|
|
|
184
208
|
|
|
185
209
|
_SUITE_COVERS_ALPHA_BETA = (
|
|
186
|
-
"from pkg.mod import alpha, beta\n\n"
|
|
187
|
-
"def test_pair():\n assert alpha() == 1 and beta() == 2\n"
|
|
210
|
+
"from pkg.mod import alpha, beta\n\ndef test_pair():\n assert alpha() == 1 and beta() == 2\n"
|
|
188
211
|
)
|
|
189
212
|
|
|
190
213
|
|
|
@@ -193,9 +216,7 @@ def test_flags_module_public_function_when_test_suite_omits_it(
|
|
|
193
216
|
) -> None:
|
|
194
217
|
_write_module(neutral_package_directory, _THREE_PUBLIC_FUNCTIONS)
|
|
195
218
|
test_path = str(neutral_package_directory / "tests" / "test_mod.py")
|
|
196
|
-
all_issues = check_test_file_omits_module_public_function(
|
|
197
|
-
_SUITE_COVERS_ALPHA_BETA, test_path
|
|
198
|
-
)
|
|
219
|
+
all_issues = check_test_file_omits_module_public_function(_SUITE_COVERS_ALPHA_BETA, test_path)
|
|
199
220
|
assert len(all_issues) == 1
|
|
200
221
|
assert "gamma" in all_issues[0]
|
|
201
222
|
assert "mod.py" in all_issues[0]
|
|
@@ -231,9 +252,7 @@ def test_skips_test_file_with_no_paired_production_module(
|
|
|
231
252
|
neutral_package_directory: Path,
|
|
232
253
|
) -> None:
|
|
233
254
|
test_path = str(neutral_package_directory / "tests" / "test_mod.py")
|
|
234
|
-
all_issues = check_test_file_omits_module_public_function(
|
|
235
|
-
_SUITE_COVERS_ALPHA_BETA, test_path
|
|
236
|
-
)
|
|
255
|
+
all_issues = check_test_file_omits_module_public_function(_SUITE_COVERS_ALPHA_BETA, test_path)
|
|
237
256
|
assert all_issues == []
|
|
238
257
|
|
|
239
258
|
|
|
@@ -242,9 +261,7 @@ def test_ignores_a_non_stem_matched_written_file(
|
|
|
242
261
|
) -> None:
|
|
243
262
|
_write_module(neutral_package_directory, _THREE_PUBLIC_FUNCTIONS)
|
|
244
263
|
helper_path = str(neutral_package_directory / "helper.py")
|
|
245
|
-
all_issues = check_test_file_omits_module_public_function(
|
|
246
|
-
_SUITE_COVERS_ALPHA_BETA, helper_path
|
|
247
|
-
)
|
|
264
|
+
all_issues = check_test_file_omits_module_public_function(_SUITE_COVERS_ALPHA_BETA, helper_path)
|
|
248
265
|
assert all_issues == []
|
|
249
266
|
|
|
250
267
|
|
|
@@ -274,9 +291,7 @@ def test_judges_post_edit_content_over_stale_on_disk_test(
|
|
|
274
291
|
"from pkg.mod import alpha, beta, gamma\n\n"
|
|
275
292
|
"def test_all():\n assert alpha() and beta() and gamma()\n"
|
|
276
293
|
)
|
|
277
|
-
all_issues = check_test_file_omits_module_public_function(
|
|
278
|
-
post_edit_covers_all, test_path
|
|
279
|
-
)
|
|
294
|
+
all_issues = check_test_file_omits_module_public_function(post_edit_covers_all, test_path)
|
|
280
295
|
assert all_issues == []
|
|
281
296
|
|
|
282
297
|
|
|
@@ -290,9 +305,7 @@ def test_counts_coverage_across_sibling_test_files_on_test_write(
|
|
|
290
305
|
"from pkg.mod import gamma\n\ndef test_gamma():\n assert gamma() == 3\n",
|
|
291
306
|
)
|
|
292
307
|
test_path = str(neutral_package_directory / "tests" / "test_mod.py")
|
|
293
|
-
all_issues = check_test_file_omits_module_public_function(
|
|
294
|
-
_SUITE_COVERS_ALPHA_BETA, test_path
|
|
295
|
-
)
|
|
308
|
+
all_issues = check_test_file_omits_module_public_function(_SUITE_COVERS_ALPHA_BETA, test_path)
|
|
296
309
|
assert all_issues == []
|
|
297
310
|
|
|
298
311
|
|
|
@@ -301,9 +314,7 @@ def test_test_side_message_names_module_and_function_without_line_prefix(
|
|
|
301
314
|
) -> None:
|
|
302
315
|
_write_module(neutral_package_directory, _THREE_PUBLIC_FUNCTIONS)
|
|
303
316
|
test_path = str(neutral_package_directory / "tests" / "test_mod.py")
|
|
304
|
-
all_issues = check_test_file_omits_module_public_function(
|
|
305
|
-
_SUITE_COVERS_ALPHA_BETA, test_path
|
|
306
|
-
)
|
|
317
|
+
all_issues = check_test_file_omits_module_public_function(_SUITE_COVERS_ALPHA_BETA, test_path)
|
|
307
318
|
assert len(all_issues) == 1
|
|
308
319
|
assert not all_issues[0].startswith("Line ")
|
|
309
320
|
assert "mod.py" in all_issues[0]
|
|
@@ -315,9 +326,7 @@ def test_test_side_omission_blocks_on_any_test_file_edit(
|
|
|
315
326
|
) -> None:
|
|
316
327
|
_write_module(neutral_package_directory, _THREE_PUBLIC_FUNCTIONS)
|
|
317
328
|
test_path = str(neutral_package_directory / "tests" / "test_mod.py")
|
|
318
|
-
all_issues = check_test_file_omits_module_public_function(
|
|
319
|
-
_SUITE_COVERS_ALPHA_BETA, test_path
|
|
320
|
-
)
|
|
329
|
+
all_issues = check_test_file_omits_module_public_function(_SUITE_COVERS_ALPHA_BETA, test_path)
|
|
321
330
|
assert len(all_issues) == 1
|
|
322
331
|
assert "gamma" in all_issues[0]
|
|
323
332
|
|
|
@@ -333,7 +342,5 @@ def test_skips_when_paired_production_module_is_exempt(
|
|
|
333
342
|
"from pkg import alpha, beta, gamma\n\n"
|
|
334
343
|
"def test_all():\n assert alpha() and beta() and gamma()\n"
|
|
335
344
|
)
|
|
336
|
-
all_issues = check_test_file_omits_module_public_function(
|
|
337
|
-
suite_covers_all, beside_test_path
|
|
338
|
-
)
|
|
345
|
+
all_issues = check_test_file_omits_module_public_function(suite_covers_all, beside_test_path)
|
|
339
346
|
assert all_issues == []
|
|
@@ -17,8 +17,8 @@ import pathlib
|
|
|
17
17
|
import sys
|
|
18
18
|
|
|
19
19
|
_PACKAGE_ROOT = pathlib.Path(__file__).resolve().parents[2]
|
|
20
|
-
|
|
21
|
-
_PACKAGE_ROOT
|
|
20
|
+
_ARCHIVED_CONVERGE_MJS = (
|
|
21
|
+
_PACKAGE_ROOT.parents[1] / "skill-archive" / "autoconverge" / "workflow" / "converge.mjs"
|
|
22
22
|
)
|
|
23
23
|
|
|
24
24
|
_HOOK_DIRECTORY = pathlib.Path(__file__).parent
|
|
@@ -750,8 +750,8 @@ def should_still_flag_real_dispatch_alongside_task_substring_identifier() -> Non
|
|
|
750
750
|
assert "spawnVerifierAgent" in issues[0]
|
|
751
751
|
|
|
752
752
|
|
|
753
|
-
def
|
|
754
|
-
converge_source =
|
|
753
|
+
def should_not_flag_archived_converge_regression_fixture() -> None:
|
|
754
|
+
converge_source = _ARCHIVED_CONVERGE_MJS.read_text(encoding="utf-8")
|
|
755
755
|
issues = check_js_resume_task_enumeration_coverage(
|
|
756
756
|
converge_source, "skills/autoconverge/workflow/converge.mjs"
|
|
757
757
|
)
|
|
@@ -34,8 +34,8 @@ check_js_returns_object_schemaless_branch = (
|
|
|
34
34
|
|
|
35
35
|
_MJS_PATH = "skills/autoconverge/workflow/converge.mjs"
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
_HOOK_DIRECTORY.parents[
|
|
37
|
+
_ARCHIVED_CONVERGE_MJS = (
|
|
38
|
+
_HOOK_DIRECTORY.parents[3] / "skill-archive" / "autoconverge" / "workflow" / "converge.mjs"
|
|
39
39
|
)
|
|
40
40
|
|
|
41
41
|
|
|
@@ -161,7 +161,7 @@ def test_skips_python_files() -> None:
|
|
|
161
161
|
assert issues == []
|
|
162
162
|
|
|
163
163
|
|
|
164
|
-
def
|
|
165
|
-
|
|
166
|
-
issues = check_js_returns_object_schemaless_branch(
|
|
164
|
+
def test_archived_converge_regression_fixture_passes_the_check() -> None:
|
|
165
|
+
archived_source = _ARCHIVED_CONVERGE_MJS.read_text(encoding="utf-8")
|
|
166
|
+
issues = check_js_returns_object_schemaless_branch(archived_source, _MJS_PATH)
|
|
167
167
|
assert issues == []
|