claude-dev-env 2.16.0 → 2.17.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/AGENTS.md +6 -0
- package/.agents/skills/AGENTS.md +34 -35
- package/.agents/skills/_shared/pr-loop/AGENTS.md +22 -15
- package/.agents/skills/_shared/pr-loop/portable-driver.md +6 -7
- package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +2 -2
- package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +1 -1
- package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +25 -16
- package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +1 -5
- package/.agents/skills/autoconverge/reference/convergence-gates.md +31 -0
- package/.agents/skills/autoconverge/reference/convergence.md +8 -10
- package/.agents/skills/autoconverge/reference/gotchas.md +1 -1
- package/.agents/skills/autoconverge/reference/stop-conditions.md +1 -1
- package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +1 -1
- package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +1 -1
- package/.agents/skills/autoconverge/workflow/converge.mjs +3 -3
- package/.agents/skills/cert-classification-rule/SKILL.md +224 -0
- package/.agents/skills/cert-classification-rule/scripts/resort_cert_folders.py +158 -0
- package/.agents/skills/eli5/SKILL.md +128 -0
- package/.agents/skills/eli5/reference/task-seeds.md +11 -0
- package/.agents/skills/fresh-branch/scripts/test_create_fresh_branch.py +2 -3
- package/.agents/skills/name-by-capability-audit/SKILL.md +67 -0
- package/.agents/skills/name-by-capability-audit/reference/fetch-commands.md +29 -0
- package/.agents/skills/name-by-capability-audit/reference/offense-examples.md +32 -0
- package/.agents/skills/name-by-capability-audit/reference/report-template.md +29 -0
- package/.agents/skills/name-by-capability-audit/reference/rule-checklist.md +38 -0
- package/.agents/skills/name-by-capability-audit/reference/task-seeds.md +9 -0
- package/.agents/skills/pr-cleanup/SKILL.md +99 -0
- package/.agents/skills/prototype/reference/honest-limitations.md +1 -1
- package/.agents/skills/prototype/reference/promotion-tasks.md +1 -1
- package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +2 -2
- package/.agents/skills/prototype/workflows/promotion.md +2 -2
- package/.agents/skills/review-router/SKILL.md +51 -0
- package/.agents/skills/review-router/reference/route-policy.json +7 -0
- package/.agents/skills/review-router/reference/task-seeds.md +1 -0
- package/.agents/skills/review-router/scripts/conftest.py +4 -0
- package/.agents/skills/review-router/scripts/review_router.py +50 -0
- package/.agents/skills/review-router/scripts/review_router_cli.py +211 -0
- package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +1 -0
- 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 +13 -0
- package/.agents/skills/review-router/scripts/test_review_router.py +23 -0
- package/.agents/skills/review-router/scripts/test_review_router_cli.py +47 -0
- package/.agents/skills/review-tier/SKILL.md +56 -0
- package/.agents/skills/review-tier/reference/task-seeds.md +1 -0
- package/.agents/skills/review-tier/reference/tier-policy.json +9 -0
- package/.agents/skills/review-tier/scripts/conftest.py +4 -0
- package/.agents/skills/review-tier/scripts/review_tier.py +332 -0
- package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +1 -0
- 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 +29 -0
- package/.agents/skills/review-tier/scripts/test_generation.py +171 -0
- package/.agents/skills/review-tier/scripts/test_review_tier.py +33 -0
- package/.agents/skills/session-log/SKILL.md +1 -1
- package/.agents/skills/shared-extraction-audit/SKILL.md +160 -0
- package/.agents/skills/shared-extraction-audit/reference/examples.md +46 -0
- package/.agents/skills/shared-extraction-audit/reference/offense-taxonomy.md +100 -0
- package/.agents/skills/source-command-logifix/SKILL.md +29 -0
- package/.agents/skills/source-command-sr-loop/SKILL.md +58 -0
- package/.agents/skills-archived/AGENTS.md +45 -0
- package/.agents/{skills → skills-archived}/anthropic-plan/AGENTS.md +1 -1
- package/.agents/{skills → skills-archived}/bugteam/scripts/reflow_skill_md.py +3 -3
- package/.agents/{skills → skills-archived}/codex-review/test_skill_scaffold.py +1 -1
- package/.agents/{skills → skills-archived}/copilot-finding-triage/SKILL.md +1 -1
- package/.agents/{skills → skills-archived}/pr-converge/SKILL.md +6 -6
- package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/AGENTS.md +1 -1
- package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/constants.py +1 -1
- package/.agents/{skills → skills-archived}/pr-converge/reference/multi-pr-orchestration.md +1 -1
- package/.agents/{skills → skills-archived}/pr-converge/reference/per-tick.md +3 -3
- package/.agents/{skills → skills-archived}/pr-converge/scripts/reflow_skill_md.py +2 -2
- package/.agents/{skills → skills-archived}/pr-converge/test_portable_pacer_gate.py +6 -2
- package/.agents/{skills → skills-archived}/pr-converge/workflows/schedule-wakeup-loop.md +1 -1
- package/.agents/{skills → skills-archived}/split-pr/reference/proposal-format.md +1 -1
- package/AGENTS.md +34 -58
- package/_shared/AGENTS.md +2 -0
- package/_shared/advisor/reference/sol-rung.md +2 -2
- package/_shared/advisor/reference/third-party-bind.md +1 -1
- package/_shared/pr-loop/audit-reply-template.md +4 -0
- package/_shared/pr-loop/fix-protocol.md +7 -6
- package/_shared/pr-loop/gh-payloads.md +8 -6
- package/_shared/pr-loop/precatch-rubric.md +6 -5
- package/_shared/pr-loop/scripts/AGENTS.md +15 -0
- package/_shared/pr-loop/scripts/_pr_converge_path_setup.py +38 -0
- package/_shared/pr-loop/scripts/check_bugbot_ci.py +279 -0
- package/_shared/pr-loop/scripts/check_convergence.py +923 -0
- package/_shared/pr-loop/scripts/check_convergence_availability.py +275 -0
- package/_shared/pr-loop/scripts/check_convergence_gates.py +279 -0
- package/_shared/pr-loop/scripts/check_convergence_thread_gates.py +219 -0
- package/_shared/pr-loop/scripts/check_pending_reviews.py +154 -0
- package/_shared/pr-loop/scripts/codex_down_classifier.py +98 -0
- package/_shared/pr-loop/scripts/codex_review_scripts_constants/.claude/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/codex_review_scripts_constants/AGENTS.md +18 -0
- package/_shared/pr-loop/scripts/codex_review_scripts_constants/__init__.py +1 -0
- package/_shared/pr-loop/scripts/codex_review_scripts_constants/classifier_constants.py +35 -0
- package/_shared/pr-loop/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +82 -0
- package/_shared/pr-loop/scripts/codex_review_scripts_constants/findings_constants.py +18 -0
- package/_shared/pr-loop/scripts/codex_review_scripts_constants/run_constants.py +53 -0
- package/_shared/pr-loop/scripts/codex_usage_probe.py +570 -0
- package/_shared/pr-loop/scripts/conftest.py +43 -0
- package/_shared/pr-loop/scripts/fetch_copilot_reviews.py +134 -0
- package/_shared/pr-loop/scripts/fixtures/auth_failure_synthetic.txt +1 -0
- package/_shared/pr-loop/scripts/fixtures/config_load_failure_v0.125.0.txt +1 -0
- package/_shared/pr-loop/scripts/fixtures/freeform_findings_v0.144.3.txt +6 -0
- package/_shared/pr-loop/scripts/fixtures/model_rejection_v0.125.0.jsonl +5 -0
- package/_shared/pr-loop/scripts/fixtures/structured_findings.txt +13 -0
- package/_shared/pr-loop/scripts/fixtures/success_stream_v0.144.3.jsonl +6 -0
- package/_shared/pr-loop/scripts/fixtures/unknown_failure_synthetic.txt +1 -0
- package/_shared/pr-loop/scripts/fixtures/usage_limit_synthetic.txt +1 -0
- package/_shared/pr-loop/scripts/parse_codex_findings.py +207 -0
- package/_shared/pr-loop/scripts/post_fix_reply.py +168 -0
- package/_shared/pr-loop/scripts/pr_converge_scripts_constants/.claude/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/pr_converge_scripts_constants/AGENTS.md +18 -0
- package/_shared/pr-loop/scripts/pr_converge_scripts_constants/__init__.py +0 -0
- package/_shared/pr-loop/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +70 -0
- package/_shared/pr-loop/scripts/pr_converge_scripts_constants/pr_converge_constants.py +118 -0
- package/_shared/pr-loop/scripts/pr_converge_scripts_constants/reflow_skill_md_constants.py +13 -0
- package/_shared/pr-loop/scripts/pr_converge_skill_constants/.claude/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/pr_converge_skill_constants/AGENTS.md +26 -0
- package/_shared/pr-loop/scripts/pr_converge_skill_constants/__init__.py +0 -0
- package/_shared/pr-loop/scripts/pr_converge_skill_constants/constants.py +69 -0
- package/_shared/pr-loop/scripts/run_codex_review.py +609 -0
- package/_shared/pr-loop/scripts/test__pr_converge_path_setup.py +30 -0
- package/_shared/pr-loop/scripts/test_check_bugbot_ci.py +312 -0
- package/_shared/pr-loop/scripts/test_check_convergence.py +438 -0
- package/_shared/pr-loop/scripts/test_check_convergence_availability.py +391 -0
- package/_shared/pr-loop/scripts/test_check_convergence_codex.py +517 -0
- package/_shared/pr-loop/scripts/test_check_convergence_contract.py +400 -0
- package/_shared/pr-loop/scripts/test_check_convergence_fixture.py +179 -0
- package/_shared/pr-loop/scripts/test_check_convergence_gates.py +84 -0
- package/_shared/pr-loop/scripts/test_check_convergence_thread_gates.py +99 -0
- package/_shared/pr-loop/scripts/test_codex_down_classifier.py +143 -0
- package/_shared/pr-loop/scripts/test_codex_usage_probe.py +683 -0
- package/_shared/pr-loop/scripts/test_parse_codex_findings.py +130 -0
- package/_shared/pr-loop/scripts/test_run_codex_review.py +969 -0
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +1 -1
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +1 -1
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +1 -1
- package/_shared/pr-loop/state-schema.md +20 -28
- package/_shared/process-tree/AGENTS.md +3 -3
- package/bin/ever-shipped-skills.mjs +29 -1
- package/bin/install-constants.mjs +15 -6
- package/bin/install.agents-home.test.mjs +13 -0
- package/bin/install.cursor-rules.test.mjs +3 -3
- package/bin/install.mjs +80 -20
- package/bin/install.profile-root.test.mjs +1 -1
- package/bin/install.prune.test.mjs +23 -204
- package/bin/install.test.mjs +8 -5
- package/bin/resolve-install-root.mjs +21 -0
- package/bin/resolve-package-managed-directory.mjs +4 -4
- package/commands/AGENTS.md +2 -0
- package/commands/sr-loop.md +3 -0
- package/docs/references/prose-style-enforcement.md +3 -7
- package/hooks/AGENTS.md +0 -3
- package/hooks/blocking/AGENTS.md +0 -2
- package/hooks/blocking/ask_user_question_shape_blocker.py +83 -0
- package/hooks/blocking/pre_tool_use_dispatcher.py +2 -19
- package/hooks/blocking/state_description_blocker.py +1 -1
- package/hooks/blocking/test_ask_user_question_shape.py +69 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +17 -58
- package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +9 -104
- package/hooks/blocking/test_state_description_blocker.py +1 -0
- package/hooks/blocking/test_stop_dispatcher.py +1 -26
- package/hooks/hooks.json +1 -1
- package/hooks/hooks_constants/AGENTS.md +0 -3
- package/hooks/hooks_constants/ask_user_question_shape.py +1 -1
- package/hooks/hooks_constants/ask_user_question_shape_constants.py +73 -0
- package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +1 -2
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -1
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -7
- package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -1
- package/hooks/hooks_constants/test_ask_user_question_shape.py +1 -1
- package/hooks/hooks_constants/test_prose_metrics_parity.py +29 -107
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -1
- package/hooks/hooks_constants/working_style_prompt_constants.py +9 -10
- package/hooks/session/AGENTS.md +4 -1
- package/hooks/session/test_working_style_prompt.py +8 -4
- package/hooks/session/working_style_prompt.py +4 -2
- package/package.json +1 -4
- package/rules/AGENTS.md +2 -5
- package/rules/asd-ste100-language.md +39 -0
- package/rules/ask-user-question-required.md +1 -1
- package/rules/long-horizon-autonomy.md +1 -1
- package/scripts/AGENTS.md +1 -0
- package/scripts/Show-Asset.ps1 +106 -0
- package/scripts/test_validate_instruction_pairs.py +30 -3
- package/scripts/tests/test_engine.py +4 -4
- package/scripts/tests/test_rules.py +5 -5
- package/system-prompts/AGENTS.md +8 -0
- package/system-prompts/software-engineer.xml +8 -3
- package/.agents/skills/grokify/evals/README.md +0 -72
- package/.agents/skills/grokify/evals/parse-payload.test.mjs +0 -171
- package/.agents/skills/grokify/evals/run-capability-evals.mjs +0 -545
- package/_shared/pr-loop/scripts/README.md +0 -21
- package/docs/imagegen.md +0 -22
- package/hooks/blocking/eli11_reply_enforcer.py +0 -436
- package/hooks/blocking/plain_language_blocker.py +0 -758
- package/hooks/blocking/test_eli11_reply_enforcer.py +0 -390
- package/hooks/blocking/test_plain_language_blocker.py +0 -683
- package/hooks/blocking/test_plain_language_blocker_allowlist.py +0 -228
- package/hooks/diagnostic/AGENTS.md +0 -75
- package/hooks/diagnostic/CLAUDE.md +0 -1
- package/hooks/diagnostic/hook_log_extractor.py +0 -875
- package/hooks/diagnostic/hook_log_init.py +0 -202
- package/hooks/diagnostic/hook_log_stop_wrapper.py +0 -26
- package/hooks/diagnostic/migrations/2026-04-25-drop-themes-hook-events.sql +0 -3
- package/hooks/diagnostic/migrations/AGENTS.md +0 -16
- package/hooks/diagnostic/migrations/CLAUDE.md +0 -1
- package/hooks/diagnostic/migrations/README.md +0 -76
- package/hooks/diagnostic/queries/AGENTS.md +0 -19
- package/hooks/diagnostic/queries/CLAUDE.md +0 -1
- package/hooks/diagnostic/queries/block_details_for_hook.sql +0 -26
- package/hooks/diagnostic/queries/blocks_by_category.sql +0 -10
- package/hooks/diagnostic/queries/blocks_by_tool.sql +0 -9
- package/hooks/diagnostic/queries/blocks_last_7_days.sql +0 -11
- package/hooks/diagnostic/queries/top_blockers_last_24_hours.sql +0 -12
- package/hooks/diagnostic/queries/top_blockers_overall.sql +0 -12
- package/hooks/diagnostic/requirements-hook-logs-dev.txt +0 -2
- package/hooks/diagnostic/requirements-hook-logs.txt +0 -1
- package/hooks/diagnostic/schema.sql +0 -51
- package/hooks/diagnostic/test_hook_log_extractor.py +0 -1518
- package/hooks/diagnostic/test_hook_log_init.py +0 -227
- package/hooks/diagnostic/test_hook_log_stop_wrapper.py +0 -17
- package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +0 -94
- package/hooks/hooks_constants/hook_log_extractor_constants.py +0 -233
- package/hooks/hooks_constants/plain_language_blocker_constants.py +0 -366
- package/hooks/hooks_constants/test_hook_log_extractor_constants.py +0 -123
- package/rules/doc-prose-cuts.md +0 -58
- package/rules/eli11-replies.md +0 -36
- package/rules/opus5-communication-contract.md +0 -45
- package/rules/plain-language.md +0 -9
- /package/.agents/{skills → skills-archived}/anthropic-plan/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/__init__.py +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/validate_packet_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/test_validate_packet.py +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/validate_packet.py +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/templates/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/templates/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/templates/README.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/templates/build-prompt.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/templates/reuse-audit.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/templates/source-map.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/templates/visual-plan.template.html +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/test_skill_contract.py +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/plan-packet.contract.test.mjs +0 -0
- /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/plan-packet.mjs +0 -0
- /package/.agents/{skills → skills-archived}/auditing-claude-config/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/auditing-claude-config/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/auditing-claude-config/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/auditing-claude-config/reference/probe-hook.md +0 -0
- /package/.agents/{skills → skills-archived}/beat-sheet/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/beat-sheet/reference/visual-beats.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/CONSTRAINTS.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/EXAMPLES.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/PROMPTS.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/README.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/audit-and-teammates.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/copilot-gap-analysis.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/design-rationale.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/github-pr-reviews.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-assign-ids.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-capture-excerpts.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-walk-categories.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-write-xml.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-append-summary.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-apply-fixes.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-git-add-commit.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-git-push.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-post-reply.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-publish-summary.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-py-compile.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-read-files.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-resolve-thread.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-test-suite.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-violation-count.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-write-xml.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/reference/team-setup.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/README.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_code_rules_gate.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_fix_hookspath.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_preflight.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/__init__.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/probe_code_rules_enforcer_check_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/reflow_skill_md_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/windows_safe_rmtree_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/probe_code_rules_enforcer_check.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_code_rules_gate.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_preflight.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/test_probe_code_rules_enforcer_check.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/test_windows_safe_rmtree.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/scripts/windows_safe_rmtree.py +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/sources.md +0 -0
- /package/.agents/{skills → skills-archived}/bugteam/test_skill_additions.py +0 -0
- /package/.agents/{skills → skills-archived}/closeout/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/closeout/reference/handoff-prompt-template.md +0 -0
- /package/.agents/{skills → skills-archived}/closeout/reference/pii-redaction-checklist.md +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/reference/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/reference/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/reference/cli-contract.md +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/reference/loop-integration.md +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_down_classifier.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/__init__.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/classifier_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/findings_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/run_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_usage_probe.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/auth_failure_synthetic.txt +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/config_load_failure_v0.125.0.txt +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/freeform_findings_v0.144.3.txt +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/model_rejection_v0.125.0.jsonl +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/structured_findings.txt +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/success_stream_v0.144.3.jsonl +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/unknown_failure_synthetic.txt +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/usage_limit_synthetic.txt +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/parse_codex_findings.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/run_codex_review.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/test_codex_down_classifier.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/test_codex_usage_probe.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/test_parse_codex_findings.py +0 -0
- /package/.agents/{skills → skills-archived}/codex-review/scripts/test_run_codex_review.py +0 -0
- /package/.agents/{skills → skills-archived}/comments/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/condensing-instructions/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/copilot-finding-triage/reference/tier-rubric.md +0 -0
- /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/conftest.py +0 -0
- /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/__init__.py +0 -0
- /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/__init__.py +0 -0
- /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/notify_ntfy_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/notify_ntfy.py +0 -0
- /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/test_notify_ntfy.py +0 -0
- /package/.agents/{skills → skills-archived}/copilot-finding-triage/templates/notification.md +0 -0
- /package/.agents/{skills → skills-archived}/copilot-review/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/copilot-review/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/copilot-review/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/copilot-review/templates/subagent-prompt.md +0 -0
- /package/.agents/{skills → skills-archived}/descriptions/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/emergencies/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/grokify/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/grokify/capability-claims.test.mjs +0 -0
- /package/.agents/{skills → skills-archived}/grokify/templates/handoff-template.md +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/reference/final-validation-tasks.md +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/reference/model-routing.md +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/reference/packet-contract.md +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/reference/packet-schema.json +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/reference/process-inventory.md +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/reference/review-loop.md +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/reference/run-record.schema.json +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/reference/self-audit-tasks.md +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/reference/task-seeds.md +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/reference/task-ticket.md +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/config/__init__.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/config/constants.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/create_packet.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/load_skill_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_create_packet.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_packet.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_protocol.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_run.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_packet.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_protocol.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_run.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/test_skill_contract.py +0 -0
- /package/.agents/{skills → skills-archived}/plan-to-pr/test_task_ticket_contract.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/__init__.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/convergence-gates.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/examples.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/fix-protocol.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/ground-rules.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-post-replies.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-publish-summary.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-push.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-read-filelines.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-reset-state.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-resolve-threads.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-spawn-clean-coder.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-stage-commit.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-trigger-bugbot.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-write-test.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/progress-checklist.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/state-schema.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/reference/stop-conditions.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/README.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/_pr_converge_path_setup.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/caller-window-pid.ps1 +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_bugbot_ci.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_availability.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_gates.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_thread_gates.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_pending_reviews.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/conftest.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue-caller.cmd +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue-stop-others.ps1 +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue.ahk +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue.cmd +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/fetch_copilot_reviews.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/post_fix_reply.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/__init__.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/pr_converge_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/reflow_skill_md_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/test__pr_converge_path_setup.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_bugbot_ci.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_availability.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_codex.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_contract.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_fixture.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_gates.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_thread_gates.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_cursor_agents_continue.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_reflow_skill_md.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/test_step5_host_branch.py +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/workflows/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-converge/workflows/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-fix-protocol/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/reference/identity-and-hooks.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/reference/substitution-matrix.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-loop-lifecycle/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/pr-loop-lifecycle/reference/teardown-publish-permissions.md +0 -0
- /package/.agents/{skills → skills-archived}/recall/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/recall/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/recall/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/release-notes-html/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/remember/.claude/CLAUDE.md +0 -0
- /package/.agents/{skills → skills-archived}/remember/AGENTS.md +0 -0
- /package/.agents/{skills → skills-archived}/remember/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/reviewer-gates/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/reviews/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/show/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/accessibility.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/art.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/charts.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/core-design.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/erds.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/flowcharts.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/host-and-html.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/illustrative-diagrams.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/interaction.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/mockups.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/quality-gates.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/structural-diagrams.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/subject-inventory.md +0 -0
- /package/.agents/{skills → skills-archived}/show/references/svg-contract.md +0 -0
- /package/.agents/{skills → skills-archived}/show/routing.yaml +0 -0
- /package/.agents/{skills → skills-archived}/show/samples/pr1262-v2.svg +0 -0
- /package/.agents/{skills → skills-archived}/show/scripts/README.md +0 -0
- /package/.agents/{skills → skills-archived}/show/scripts/validate-artifact.py +0 -0
- /package/.agents/{skills → skills-archived}/show/scripts/validate-package.py +0 -0
- /package/.agents/{skills → skills-archived}/show/templates/html-widget.html +0 -0
- /package/.agents/{skills → skills-archived}/show/templates/svg-base.svg +0 -0
- /package/.agents/{skills → skills-archived}/show/tests/fixtures/css-var.svg +0 -0
- /package/.agents/{skills → skills-archived}/show/tests/fixtures/dead-ref.svg +0 -0
- /package/.agents/{skills → skills-archived}/show/tests/fixtures/filled-glyph.svg +0 -0
- /package/.agents/{skills → skills-archived}/show/tests/fixtures/inherited-fill.svg +0 -0
- /package/.agents/{skills → skills-archived}/show/tests/fixtures/invalid.svg +0 -0
- /package/.agents/{skills → skills-archived}/show/tests/fixtures/large-canvas.svg +0 -0
- /package/.agents/{skills → skills-archived}/show/tests/fixtures/unfilled-connector.svg +0 -0
- /package/.agents/{skills → skills-archived}/show/tests/fixtures/valid.html +0 -0
- /package/.agents/{skills → skills-archived}/show/tests/test_validate-artifact.py +0 -0
- /package/.agents/{skills → skills-archived}/show/tests/test_validators.py +0 -0
- /package/.agents/{skills → skills-archived}/show/workflows/create-visual.md +0 -0
- /package/.agents/{skills → skills-archived}/show/workflows/review-visual.md +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/SKILL.md +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/reference/path-layers.md +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/reference/split-further-loop.md +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/reference/splitting-principles.md +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/analyze_pr.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/categorize_files.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/config/__init__.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/config/dependency_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/config/git_operations_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/config/packing_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/config/plan_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/config/split_pr_constants.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/execute_split_slices.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/pack_files_into_slices.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_dependency_graph.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_git_operations.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_layer_order.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_paginate.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_process_runner.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_script_types.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_title.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_analyze_pr.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_categorize_files.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_categorize_files_packing.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_execute_split_slices.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_dependency_graph.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_git_operations.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_layer_order.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_paginate.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_script_types.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_title.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_dependency_graph.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan_contract.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan_path_normalization.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/verify_dependency_graph.py +0 -0
- /package/.agents/{skills → skills-archived}/split-pr/scripts/verify_plan.py +0 -0
|
@@ -1,683 +0,0 @@
|
|
|
1
|
-
"""Tests for the plain_language_blocker PreToolUse hook.
|
|
2
|
-
|
|
3
|
-
Covers the shared prose scanner (fenced code, inline code, blockquotes, URLs,
|
|
4
|
-
file paths), the word-boundary guard, multi-word phrase matching, case
|
|
5
|
-
insensitivity, the term -> replacement block message, both registered
|
|
6
|
-
PreToolUse surfaces (AskUserQuestion and Write|Edit on .md targets), and the
|
|
7
|
-
lean-question-block check that keeps chat detail out of an AskUserQuestion call.
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
import importlib.util
|
|
11
|
-
import json
|
|
12
|
-
import os
|
|
13
|
-
import subprocess
|
|
14
|
-
import sys
|
|
15
|
-
from pathlib import Path
|
|
16
|
-
from unittest.mock import patch
|
|
17
|
-
|
|
18
|
-
import pytest
|
|
19
|
-
|
|
20
|
-
HOOK_SCRIPT_PATH = Path(__file__).parent / "plain_language_blocker.py"
|
|
21
|
-
_HOOKS_DIR = str(Path(__file__).resolve().parent)
|
|
22
|
-
_HOOKS_ROOT = str(Path(__file__).resolve().parent.parent)
|
|
23
|
-
if _HOOKS_DIR not in sys.path:
|
|
24
|
-
sys.path.insert(0, _HOOKS_DIR)
|
|
25
|
-
if _HOOKS_ROOT not in sys.path:
|
|
26
|
-
sys.path.insert(0, _HOOKS_ROOT)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _load_hook_module() -> object:
|
|
30
|
-
module_spec = importlib.util.spec_from_file_location(
|
|
31
|
-
"plain_language_blocker_under_test", HOOK_SCRIPT_PATH
|
|
32
|
-
)
|
|
33
|
-
assert module_spec is not None and module_spec.loader is not None
|
|
34
|
-
loaded_module = importlib.util.module_from_spec(module_spec)
|
|
35
|
-
module_spec.loader.exec_module(loaded_module)
|
|
36
|
-
return loaded_module
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
hook_module = _load_hook_module()
|
|
40
|
-
find_banned_terms = hook_module.find_banned_terms
|
|
41
|
-
strip_non_prose_regions = hook_module.strip_non_prose_regions
|
|
42
|
-
build_block_reason = hook_module.build_block_reason
|
|
43
|
-
find_question_block_violations = hook_module.find_question_block_violations
|
|
44
|
-
build_lean_block_reason = hook_module.build_lean_block_reason
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def evaluate(payload_by_key: dict[str, object]) -> str | None:
|
|
48
|
-
"""Call evaluate with opinionated prose enforcement enabled."""
|
|
49
|
-
with patch.dict(os.environ, {"CLAUDE_PROSE_STYLE_ENFORCEMENT": "1"}):
|
|
50
|
-
return hook_module.evaluate(payload_by_key)
|
|
51
|
-
|
|
52
|
-
from pre_tool_use_dispatcher import NativeHook, run_native_hook # noqa: E402
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def _run_hook_with_payload(
|
|
56
|
-
payload: dict, *, is_prose_style_enabled: bool = True
|
|
57
|
-
) -> subprocess.CompletedProcess[str]:
|
|
58
|
-
environment_by_key = os.environ.copy()
|
|
59
|
-
if is_prose_style_enabled:
|
|
60
|
-
environment_by_key["CLAUDE_PROSE_STYLE_ENFORCEMENT"] = "1"
|
|
61
|
-
else:
|
|
62
|
-
environment_by_key.pop("CLAUDE_PROSE_STYLE_ENFORCEMENT", None)
|
|
63
|
-
return subprocess.run(
|
|
64
|
-
[sys.executable, str(HOOK_SCRIPT_PATH)],
|
|
65
|
-
input=json.dumps(payload),
|
|
66
|
-
capture_output=True,
|
|
67
|
-
text=True,
|
|
68
|
-
check=False,
|
|
69
|
-
env=environment_by_key,
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def _decision_from(completed: subprocess.CompletedProcess[str]) -> str | None:
|
|
74
|
-
if not completed.stdout:
|
|
75
|
-
return None
|
|
76
|
-
parsed = json.loads(completed.stdout)
|
|
77
|
-
return parsed.get("hookSpecificOutput", {}).get("permissionDecision")
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def _system_message_from(completed: subprocess.CompletedProcess[str]) -> str | None:
|
|
81
|
-
if not completed.stdout:
|
|
82
|
-
return None
|
|
83
|
-
parsed = json.loads(completed.stdout)
|
|
84
|
-
system_message = parsed.get("systemMessage")
|
|
85
|
-
return system_message if isinstance(system_message, str) else None
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def test_canonical_hook_script_exists_at_expected_path() -> None:
|
|
89
|
-
assert HOOK_SCRIPT_PATH.is_file()
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def test_heavy_word_scan_is_default_off(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
93
|
-
monkeypatch.delenv("CLAUDE_PROSE_STYLE_ENFORCEMENT", raising=False)
|
|
94
|
-
payload = {
|
|
95
|
-
"tool_name": "Write",
|
|
96
|
-
"tool_input": {
|
|
97
|
-
"file_path": "notes.md",
|
|
98
|
-
"content": "We initiate the worker pool at boot.",
|
|
99
|
-
},
|
|
100
|
-
}
|
|
101
|
-
assert hook_module.evaluate(payload) is None
|
|
102
|
-
completed = _run_hook_with_payload(payload, is_prose_style_enabled=False)
|
|
103
|
-
assert _decision_from(completed) is None
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def test_default_off_emits_privacy_safe_advisory_candidates(
|
|
107
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
108
|
-
) -> None:
|
|
109
|
-
monkeypatch.delenv("CLAUDE_PROSE_STYLE_ENFORCEMENT", raising=False)
|
|
110
|
-
all_emitted: list[tuple[str, str, str]] = []
|
|
111
|
-
|
|
112
|
-
def _record_advisory(
|
|
113
|
-
matcher_id: str, surface: str, context_text: str, **_kwargs: object
|
|
114
|
-
) -> dict[str, object]:
|
|
115
|
-
all_emitted.append((matcher_id, surface, context_text))
|
|
116
|
-
return {}
|
|
117
|
-
|
|
118
|
-
monkeypatch.setattr(hook_module, "emit_advisory_candidate", _record_advisory)
|
|
119
|
-
payload = {
|
|
120
|
-
"tool_name": "Write",
|
|
121
|
-
"tool_input": {
|
|
122
|
-
"file_path": "notes.md",
|
|
123
|
-
"content": "We initiate the worker pool at boot.",
|
|
124
|
-
},
|
|
125
|
-
}
|
|
126
|
-
assert hook_module.evaluate(payload) is None
|
|
127
|
-
assert all_emitted
|
|
128
|
-
matcher_id, surface, context_text = all_emitted[0]
|
|
129
|
-
assert matcher_id == "plain_language_heavy_word"
|
|
130
|
-
assert surface == "Write"
|
|
131
|
-
assert "initiate" in context_text
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def test_heavy_word_scan_opt_in_allows_with_system_message_swaps() -> None:
|
|
135
|
-
payload = {
|
|
136
|
-
"tool_name": "Write",
|
|
137
|
-
"tool_input": {
|
|
138
|
-
"file_path": "notes.md",
|
|
139
|
-
"content": "We initiate the worker pool at boot.",
|
|
140
|
-
},
|
|
141
|
-
}
|
|
142
|
-
assert evaluate(payload) is None
|
|
143
|
-
with patch.dict(os.environ, {"CLAUDE_PROSE_STYLE_ENFORCEMENT": "1"}):
|
|
144
|
-
deny_reason, advisory_message = hook_module.evaluate_with_advisory(payload)
|
|
145
|
-
assert deny_reason is None
|
|
146
|
-
assert advisory_message is not None
|
|
147
|
-
assert 'use "start" instead of "initiate"' in advisory_message
|
|
148
|
-
completed = _run_hook_with_payload(payload, is_prose_style_enabled=True)
|
|
149
|
-
assert _decision_from(completed) == "allow"
|
|
150
|
-
system_message = _system_message_from(completed)
|
|
151
|
-
assert system_message is not None
|
|
152
|
-
assert 'use "start" instead of "initiate"' in system_message
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
def test_bare_prose_banned_term_is_detected() -> None:
|
|
156
|
-
matched = find_banned_terms("We initiate the worker pool at boot.")
|
|
157
|
-
assert any(each_term == "initiate" for each_term, _replacement in matched)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def test_banned_term_inside_fenced_code_is_exempt() -> None:
|
|
161
|
-
prose = "Start the pool at boot.\n\n```python\nutilize(pool)\n```\n"
|
|
162
|
-
assert find_banned_terms(prose) == []
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def test_banned_term_inside_inline_code_is_exempt() -> None:
|
|
166
|
-
prose = "Call the `utilize` helper from the legacy module to migrate."
|
|
167
|
-
assert find_banned_terms(prose) == []
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def test_banned_term_inside_blockquote_is_exempt() -> None:
|
|
171
|
-
prose = "> The old guide said to utilize the pool.\n\nUse the pool directly now."
|
|
172
|
-
assert find_banned_terms(prose) == []
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def test_banned_term_inside_url_is_exempt() -> None:
|
|
176
|
-
prose = "See https://example.com/initiate-flow for the original write-up."
|
|
177
|
-
assert find_banned_terms(prose) == []
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
def test_banned_term_inside_file_path_is_exempt() -> None:
|
|
181
|
-
prose = "Edit src/utilize_helpers/initiate.py to wire the new path."
|
|
182
|
-
assert find_banned_terms(prose) == []
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
def test_word_boundary_guard_does_not_match_substring() -> None:
|
|
186
|
-
assert find_banned_terms("The reinitialize routine reruns the seed.") == []
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
def test_case_insensitive_match() -> None:
|
|
190
|
-
matched_lower = find_banned_terms("utilize the cache.")
|
|
191
|
-
matched_upper = find_banned_terms("Utilize the cache.")
|
|
192
|
-
assert any(term == "utilize" for term, _ in matched_lower)
|
|
193
|
-
assert any(term == "utilize" for term, _ in matched_upper)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
def test_multi_word_phrase_matches_as_unit() -> None:
|
|
197
|
-
matched = find_banned_terms("Run the migration prior to the deploy step.")
|
|
198
|
-
assert any(term == "prior to" for term, _ in matched)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
def test_strip_non_prose_regions_removes_code_and_paths() -> None:
|
|
202
|
-
prose = "Use `utilize` and src/initiate.py and https://x.test/utilize here."
|
|
203
|
-
stripped = strip_non_prose_regions(prose)
|
|
204
|
-
assert "utilize" not in stripped
|
|
205
|
-
assert "initiate" not in stripped
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
def test_block_reason_names_term_and_replacement() -> None:
|
|
209
|
-
reason = build_block_reason([("initiate", "start")])
|
|
210
|
-
assert "initiate" in reason
|
|
211
|
-
assert "start" in reason
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def test_ask_user_question_with_banned_term_is_advisory_allow() -> None:
|
|
215
|
-
payload = {
|
|
216
|
-
"tool_name": "AskUserQuestion",
|
|
217
|
-
"tool_input": {
|
|
218
|
-
"questions": [
|
|
219
|
-
{
|
|
220
|
-
"question": "Should we utilize the new allocator now?",
|
|
221
|
-
"header": "Allocator",
|
|
222
|
-
"options": [{"label": "Yes", "description": "Switch now."}],
|
|
223
|
-
}
|
|
224
|
-
]
|
|
225
|
-
},
|
|
226
|
-
}
|
|
227
|
-
completed = _run_hook_with_payload(payload)
|
|
228
|
-
assert _decision_from(completed) == "allow"
|
|
229
|
-
system_message = _system_message_from(completed)
|
|
230
|
-
assert system_message is not None
|
|
231
|
-
assert "utilize" in system_message
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
def test_ask_user_question_banned_term_in_option_label_is_advisory_allow() -> None:
|
|
235
|
-
payload = {
|
|
236
|
-
"tool_name": "AskUserQuestion",
|
|
237
|
-
"tool_input": {
|
|
238
|
-
"questions": [
|
|
239
|
-
{
|
|
240
|
-
"question": "Which path should we take?",
|
|
241
|
-
"header": "Path",
|
|
242
|
-
"options": [{"label": "Utilize the cache", "description": "Go fast."}],
|
|
243
|
-
}
|
|
244
|
-
]
|
|
245
|
-
},
|
|
246
|
-
}
|
|
247
|
-
completed = _run_hook_with_payload(payload)
|
|
248
|
-
assert _decision_from(completed) == "allow"
|
|
249
|
-
system_message = _system_message_from(completed)
|
|
250
|
-
assert system_message is not None
|
|
251
|
-
assert "utilize" in system_message
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
def test_clean_ask_user_question_passes_through() -> None:
|
|
255
|
-
payload = {
|
|
256
|
-
"tool_name": "AskUserQuestion",
|
|
257
|
-
"tool_input": {
|
|
258
|
-
"questions": [
|
|
259
|
-
{
|
|
260
|
-
"question": "Should we switch the allocator now?",
|
|
261
|
-
"header": "Allocator",
|
|
262
|
-
"options": [{"label": "Yes", "description": "Switch now."}],
|
|
263
|
-
}
|
|
264
|
-
]
|
|
265
|
-
},
|
|
266
|
-
}
|
|
267
|
-
completed = _run_hook_with_payload(payload)
|
|
268
|
-
assert _decision_from(completed) is None
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
def test_write_markdown_with_banned_term_is_advisory_allow(tmp_path: Path) -> None:
|
|
272
|
-
target = tmp_path / "notes.md"
|
|
273
|
-
payload = {
|
|
274
|
-
"tool_name": "Write",
|
|
275
|
-
"tool_input": {
|
|
276
|
-
"file_path": str(target),
|
|
277
|
-
"content": "This guide explains how to utilize the new cache layer.",
|
|
278
|
-
},
|
|
279
|
-
}
|
|
280
|
-
completed = _run_hook_with_payload(payload)
|
|
281
|
-
assert _decision_from(completed) == "allow"
|
|
282
|
-
system_message = _system_message_from(completed)
|
|
283
|
-
assert system_message is not None
|
|
284
|
-
assert "utilize" in system_message
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
def test_write_markdown_at_ephemeral_path_is_allowed(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
288
|
-
monkeypatch.delenv("CLAUDE_CODE_RULES_DISABLE_EPHEMERAL_EXEMPT", raising=False)
|
|
289
|
-
payload = {
|
|
290
|
-
"tool_name": "Write",
|
|
291
|
-
"tool_input": {
|
|
292
|
-
"file_path": "/tmp/notes.md",
|
|
293
|
-
"content": "This guide explains how to utilize the new cache layer.",
|
|
294
|
-
},
|
|
295
|
-
}
|
|
296
|
-
completed = _run_hook_with_payload(payload)
|
|
297
|
-
assert _decision_from(completed) is None
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
def test_write_non_markdown_is_ignored(tmp_path: Path) -> None:
|
|
301
|
-
target = tmp_path / "notes.txt"
|
|
302
|
-
payload = {
|
|
303
|
-
"tool_name": "Write",
|
|
304
|
-
"tool_input": {
|
|
305
|
-
"file_path": str(target),
|
|
306
|
-
"content": "This guide explains how to utilize the new cache layer.",
|
|
307
|
-
},
|
|
308
|
-
}
|
|
309
|
-
completed = _run_hook_with_payload(payload)
|
|
310
|
-
assert _decision_from(completed) is None
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
def test_edit_markdown_clean_content_passes_through(tmp_path: Path) -> None:
|
|
314
|
-
target = tmp_path / "notes.md"
|
|
315
|
-
payload = {
|
|
316
|
-
"tool_name": "Edit",
|
|
317
|
-
"tool_input": {
|
|
318
|
-
"file_path": str(target),
|
|
319
|
-
"new_string": "This guide explains how to use the new cache layer.",
|
|
320
|
-
},
|
|
321
|
-
}
|
|
322
|
-
completed = _run_hook_with_payload(payload)
|
|
323
|
-
assert _decision_from(completed) is None
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
def test_multiedit_markdown_with_banned_term_is_advisory_allow(tmp_path: Path) -> None:
|
|
327
|
-
target = tmp_path / "notes.md"
|
|
328
|
-
payload = {
|
|
329
|
-
"tool_name": "MultiEdit",
|
|
330
|
-
"tool_input": {
|
|
331
|
-
"file_path": str(target),
|
|
332
|
-
"edits": [
|
|
333
|
-
{"old_string": "intro", "new_string": "This section reads cleanly."},
|
|
334
|
-
{"old_string": "body", "new_string": "Then we utilize the new cache."},
|
|
335
|
-
],
|
|
336
|
-
},
|
|
337
|
-
}
|
|
338
|
-
completed = _run_hook_with_payload(payload)
|
|
339
|
-
assert _decision_from(completed) == "allow"
|
|
340
|
-
system_message = _system_message_from(completed)
|
|
341
|
-
assert system_message is not None
|
|
342
|
-
assert "utilize" in system_message
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
def test_other_tool_is_ignored() -> None:
|
|
346
|
-
payload = {"tool_name": "Bash", "tool_input": {"command": "echo utilize"}}
|
|
347
|
-
completed = _run_hook_with_payload(payload)
|
|
348
|
-
assert _decision_from(completed) is None
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
def test_software_allowlisted_term_is_not_flagged() -> None:
|
|
352
|
-
assert find_banned_terms("Run this command to start the worker.") == []
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
def test_non_allowlisted_formal_term_still_flagged() -> None:
|
|
356
|
-
matched = find_banned_terms("Please utilize the cache now.")
|
|
357
|
-
assert any(term == "utilize" for term, _ in matched)
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
def test_prose_slash_token_is_not_stripped_as_path() -> None:
|
|
361
|
-
assert "client/server" in strip_non_prose_regions("Use a client/server split here.")
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
def test_real_file_path_is_still_stripped() -> None:
|
|
365
|
-
assert "initiate" not in strip_non_prose_regions("Edit src/initiate.py to wire it.")
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
def test_native_dispatch_path_logs_the_block(tmp_path: Path) -> None:
|
|
369
|
-
"""A lean-block deny routed through the dispatcher's native path logs once.
|
|
370
|
-
|
|
371
|
-
On the Write|Edit|MultiEdit surface this hook runs only through
|
|
372
|
-
pre_tool_use_dispatcher's native path, which calls evaluate() and
|
|
373
|
-
build_deny_payload() — never _emit_deny() or main(). Heavy words no longer
|
|
374
|
-
deny (OP-07D), so this probe uses AskUserQuestion lean-block chat detail.
|
|
375
|
-
The log call still lives on build_deny_payload.
|
|
376
|
-
"""
|
|
377
|
-
deny_payload = {
|
|
378
|
-
"tool_name": "AskUserQuestion",
|
|
379
|
-
"tool_input": {
|
|
380
|
-
"questions": [
|
|
381
|
-
{
|
|
382
|
-
"question": "Which gate should run first?\n\nPlan the whole stack.",
|
|
383
|
-
"header": "Gate",
|
|
384
|
-
"options": [{"label": "Write", "description": "Runs on write."}],
|
|
385
|
-
}
|
|
386
|
-
]
|
|
387
|
-
},
|
|
388
|
-
}
|
|
389
|
-
native_hook = NativeHook(
|
|
390
|
-
evaluate=hook_module.evaluate,
|
|
391
|
-
build_deny_payload=hook_module.build_deny_payload,
|
|
392
|
-
)
|
|
393
|
-
|
|
394
|
-
with patch.object(Path, "home", return_value=tmp_path):
|
|
395
|
-
hosted_result = run_native_hook(
|
|
396
|
-
native_hook, deny_payload, is_blocking=True
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
assert hosted_result.captured_stdout
|
|
400
|
-
log_path = tmp_path / ".claude" / "logs" / "hook-blocks.log"
|
|
401
|
-
all_records = log_path.read_text(encoding="utf-8").strip().splitlines()
|
|
402
|
-
assert len(all_records) == 1
|
|
403
|
-
logged_record = json.loads(all_records[0])
|
|
404
|
-
assert logged_record["hook"] == "plain_language_blocker.py"
|
|
405
|
-
assert logged_record["event"] == "PreToolUse"
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
LEAN_QUESTION = "Which gate should run first?"
|
|
409
|
-
LEAN_DESCRIPTION = "Runs on every write."
|
|
410
|
-
QUESTION_AT_THE_WORD_CAP = (
|
|
411
|
-
"Should we keep the gate on the write path where it reads the whole file "
|
|
412
|
-
"each time, or move it to the commit path where it reads only the lines "
|
|
413
|
-
"that a change has already staged for the current review?"
|
|
414
|
-
)
|
|
415
|
-
QUESTION_OVER_THE_WORD_CAP = (
|
|
416
|
-
"Should we keep the gate on the write path where it reads the whole file "
|
|
417
|
-
"each time it runs, or move the gate to the commit path where it reads "
|
|
418
|
-
"only the staged lines and skips the files that no one on the team ever "
|
|
419
|
-
"touched?"
|
|
420
|
-
)
|
|
421
|
-
DESCRIPTION_AT_THE_WORD_CAP = (
|
|
422
|
-
"Runs the gate at commit time so it reads only the lines a change staged."
|
|
423
|
-
)
|
|
424
|
-
DESCRIPTION_OVER_THE_WORD_CAP = (
|
|
425
|
-
"Runs the gate at commit time so it reads only the lines the change "
|
|
426
|
-
"actually staged for review."
|
|
427
|
-
)
|
|
428
|
-
QUESTION_WITH_INLINE_CODE_AT_THE_WORD_CAP = (
|
|
429
|
-
"Should we keep the gate on the write path where it reads the whole file "
|
|
430
|
-
"each time, or move it to the commit path where it reads only what "
|
|
431
|
-
"`git diff --cached --name-only HEAD` lists for the current review before "
|
|
432
|
-
"the team sees it?"
|
|
433
|
-
)
|
|
434
|
-
QUESTION_WITH_INLINE_CODE_OVER_THE_WORD_CAP = (
|
|
435
|
-
"Should we keep the gate on the write path where it reads the whole file "
|
|
436
|
-
"each time, or move it to the commit path where it reads only what "
|
|
437
|
-
"`git diff --cached --name-only HEAD` lists for the current review before "
|
|
438
|
-
"the whole team sees it?"
|
|
439
|
-
)
|
|
440
|
-
DESCRIPTION_WITH_INLINE_CODE_AT_THE_WORD_CAP = (
|
|
441
|
-
"Runs the gate at commit time so it reads only what "
|
|
442
|
-
"`git diff --cached --name-only HEAD` lists for review."
|
|
443
|
-
)
|
|
444
|
-
DESCRIPTION_WITH_INLINE_CODE_OVER_THE_WORD_CAP = (
|
|
445
|
-
"Runs the gate at commit time so it reads only what "
|
|
446
|
-
"`git diff --cached --name-only HEAD` lists for the review."
|
|
447
|
-
)
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
def _ask_payload(question_text: str, all_descriptions: list[str]) -> dict[str, object]:
|
|
451
|
-
return {
|
|
452
|
-
"tool_name": "AskUserQuestion",
|
|
453
|
-
"tool_input": {
|
|
454
|
-
"questions": [
|
|
455
|
-
{
|
|
456
|
-
"question": question_text,
|
|
457
|
-
"header": "Gate",
|
|
458
|
-
"options": [
|
|
459
|
-
{"label": f"Gate {each_index}", "description": each_description}
|
|
460
|
-
for each_index, each_description in enumerate(all_descriptions)
|
|
461
|
-
],
|
|
462
|
-
}
|
|
463
|
-
]
|
|
464
|
-
},
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
def test_question_carrying_a_list_marker_plan_is_denied() -> None:
|
|
469
|
-
plan_question = f"{LEAN_QUESTION}\n- Split the file\n- Wire the gate\n- Run the suite"
|
|
470
|
-
|
|
471
|
-
deny_reason = evaluate(_ask_payload(plan_question, [LEAN_DESCRIPTION]))
|
|
472
|
-
|
|
473
|
-
assert deny_reason is not None
|
|
474
|
-
assert "a bullet or numbered list marker" in deny_reason
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
def test_question_carrying_a_fenced_block_is_denied() -> None:
|
|
478
|
-
fenced_question = f"{LEAN_QUESTION}\n```\nrun_gate()\n```"
|
|
479
|
-
|
|
480
|
-
deny_reason = evaluate(_ask_payload(fenced_question, [LEAN_DESCRIPTION]))
|
|
481
|
-
|
|
482
|
-
assert deny_reason is not None
|
|
483
|
-
assert "a fenced code block" in deny_reason
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
def test_question_carrying_a_second_paragraph_is_denied() -> None:
|
|
487
|
-
two_paragraph_question = f"{LEAN_QUESTION}\n\nThe write gate reads every file."
|
|
488
|
-
|
|
489
|
-
deny_reason = evaluate(_ask_payload(two_paragraph_question, [LEAN_DESCRIPTION]))
|
|
490
|
-
|
|
491
|
-
assert deny_reason is not None
|
|
492
|
-
assert "more than one paragraph" in deny_reason
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
def test_question_carrying_a_one_line_fence_is_denied() -> None:
|
|
496
|
-
one_line_fence_question = f"{LEAN_QUESTION}\n```print(1)```"
|
|
497
|
-
|
|
498
|
-
deny_reason = evaluate(_ask_payload(one_line_fence_question, [LEAN_DESCRIPTION]))
|
|
499
|
-
|
|
500
|
-
assert deny_reason is not None
|
|
501
|
-
assert "a fenced code block" in deny_reason
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
def test_question_carrying_a_carriage_return_paragraph_break_is_denied() -> None:
|
|
505
|
-
crlf_question = f"{LEAN_QUESTION}\r\n\r\nThe write gate reads every file."
|
|
506
|
-
|
|
507
|
-
deny_reason = evaluate(_ask_payload(crlf_question, [LEAN_DESCRIPTION]))
|
|
508
|
-
|
|
509
|
-
assert deny_reason is not None
|
|
510
|
-
assert "more than one paragraph" in deny_reason
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
def test_question_carrying_a_heading_is_denied() -> None:
|
|
514
|
-
headed_question = f"{LEAN_QUESTION}\n## The write gate\nIt reads every file."
|
|
515
|
-
|
|
516
|
-
deny_reason = evaluate(_ask_payload(headed_question, [LEAN_DESCRIPTION]))
|
|
517
|
-
|
|
518
|
-
assert deny_reason is not None
|
|
519
|
-
assert "a heading" in deny_reason
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
def test_option_description_carrying_a_table_row_is_denied() -> None:
|
|
523
|
-
deny_reason = evaluate(_ask_payload(LEAN_QUESTION, ["| gate | 12 ms |"]))
|
|
524
|
-
|
|
525
|
-
assert deny_reason is not None
|
|
526
|
-
assert "a table row" in deny_reason
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
def test_question_over_the_word_cap_is_denied() -> None:
|
|
530
|
-
deny_reason = evaluate(_ask_payload(QUESTION_OVER_THE_WORD_CAP, [LEAN_DESCRIPTION]))
|
|
531
|
-
|
|
532
|
-
assert deny_reason is not None
|
|
533
|
-
assert "46 words, over the 40-word cap" in deny_reason
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
def test_question_over_the_sentence_cap_is_denied() -> None:
|
|
537
|
-
three_sentence_question = (
|
|
538
|
-
"Which gate runs first? The write gate reads every file. "
|
|
539
|
-
"The commit gate reads staged lines only."
|
|
540
|
-
)
|
|
541
|
-
|
|
542
|
-
deny_reason = evaluate(_ask_payload(three_sentence_question, [LEAN_DESCRIPTION]))
|
|
543
|
-
|
|
544
|
-
assert deny_reason is not None
|
|
545
|
-
assert "3 sentences, over the 2-sentence cap" in deny_reason
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
def test_option_description_over_the_word_cap_is_denied() -> None:
|
|
549
|
-
deny_reason = evaluate(_ask_payload(LEAN_QUESTION, [DESCRIPTION_OVER_THE_WORD_CAP]))
|
|
550
|
-
|
|
551
|
-
assert deny_reason is not None
|
|
552
|
-
assert "18 words, over the 15-word cap" in deny_reason
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
def test_option_description_over_the_sentence_cap_is_denied() -> None:
|
|
556
|
-
deny_reason = evaluate(_ask_payload(LEAN_QUESTION, ["Runs on write. Reads every file."]))
|
|
557
|
-
|
|
558
|
-
assert deny_reason is not None
|
|
559
|
-
assert "2 sentences, over the 1-sentence cap" in deny_reason
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
def test_lean_question_with_three_short_options_is_allowed() -> None:
|
|
563
|
-
all_descriptions = [
|
|
564
|
-
LEAN_DESCRIPTION,
|
|
565
|
-
"Runs at commit time.",
|
|
566
|
-
"Runs in both places.",
|
|
567
|
-
]
|
|
568
|
-
|
|
569
|
-
assert evaluate(_ask_payload(LEAN_QUESTION, all_descriptions)) is None
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
def test_two_sentence_question_at_the_sentence_cap_is_allowed() -> None:
|
|
573
|
-
two_sentence_question = f"{LEAN_QUESTION} Both gates read the same lines."
|
|
574
|
-
|
|
575
|
-
assert evaluate(_ask_payload(two_sentence_question, [LEAN_DESCRIPTION])) is None
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
def test_question_at_the_word_cap_is_allowed() -> None:
|
|
579
|
-
assert evaluate(_ask_payload(QUESTION_AT_THE_WORD_CAP, [LEAN_DESCRIPTION])) is None
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
def test_option_description_at_the_word_cap_is_allowed() -> None:
|
|
583
|
-
assert evaluate(_ask_payload(LEAN_QUESTION, [DESCRIPTION_AT_THE_WORD_CAP])) is None
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
def test_question_with_an_inline_code_span_is_allowed() -> None:
|
|
587
|
-
question_text = QUESTION_WITH_INLINE_CODE_AT_THE_WORD_CAP
|
|
588
|
-
|
|
589
|
-
assert len(question_text.split()) > 40
|
|
590
|
-
assert evaluate(_ask_payload(question_text, [LEAN_DESCRIPTION])) is None
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
def test_inline_code_span_counts_as_one_word() -> None:
|
|
594
|
-
deny_reason = evaluate(
|
|
595
|
-
_ask_payload(QUESTION_WITH_INLINE_CODE_OVER_THE_WORD_CAP, [LEAN_DESCRIPTION])
|
|
596
|
-
)
|
|
597
|
-
|
|
598
|
-
assert deny_reason is not None
|
|
599
|
-
assert "41 words, over the 40-word cap" in deny_reason
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
def test_option_description_with_an_inline_code_span_is_allowed() -> None:
|
|
603
|
-
option_description = DESCRIPTION_WITH_INLINE_CODE_AT_THE_WORD_CAP
|
|
604
|
-
|
|
605
|
-
assert len(option_description.split()) > 15
|
|
606
|
-
assert evaluate(_ask_payload(LEAN_QUESTION, [option_description])) is None
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
def test_inline_code_span_counts_as_one_word_in_an_option_description() -> None:
|
|
610
|
-
deny_reason = evaluate(
|
|
611
|
-
_ask_payload(LEAN_QUESTION, [DESCRIPTION_WITH_INLINE_CODE_OVER_THE_WORD_CAP])
|
|
612
|
-
)
|
|
613
|
-
|
|
614
|
-
assert deny_reason is not None
|
|
615
|
-
assert "16 words, over the 15-word cap" in deny_reason
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
def test_markdown_write_keeps_its_bullets_and_tables(tmp_path: Path) -> None:
|
|
619
|
-
bulleted_prose = "- Split the file\n- Wire the gate\n\n| gate | 12 ms |\n"
|
|
620
|
-
target_path = str(tmp_path / "notes.md")
|
|
621
|
-
heavy_payload = {
|
|
622
|
-
"tool_name": "Write",
|
|
623
|
-
"tool_input": {
|
|
624
|
-
"file_path": target_path,
|
|
625
|
-
"content": f"{bulleted_prose}Then utilize the cache.",
|
|
626
|
-
},
|
|
627
|
-
}
|
|
628
|
-
clean_payload = {
|
|
629
|
-
"tool_name": "Write",
|
|
630
|
-
"tool_input": {"file_path": target_path, "content": bulleted_prose},
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
assert evaluate(heavy_payload) is None
|
|
634
|
-
with patch.dict(os.environ, {"CLAUDE_PROSE_STYLE_ENFORCEMENT": "1"}):
|
|
635
|
-
_deny_reason, advisory_message = hook_module.evaluate_with_advisory(
|
|
636
|
-
heavy_payload
|
|
637
|
-
)
|
|
638
|
-
assert _deny_reason is None
|
|
639
|
-
assert advisory_message is not None
|
|
640
|
-
assert "utilize" in advisory_message
|
|
641
|
-
assert evaluate(clean_payload) is None
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
def test_find_question_block_violations_reports_one_line_per_repeated_fault() -> None:
|
|
645
|
-
tool_input = {
|
|
646
|
-
"questions": [
|
|
647
|
-
{
|
|
648
|
-
"question": LEAN_QUESTION,
|
|
649
|
-
"options": [
|
|
650
|
-
{"label": "Write", "description": DESCRIPTION_OVER_THE_WORD_CAP},
|
|
651
|
-
{"label": "Commit", "description": DESCRIPTION_OVER_THE_WORD_CAP},
|
|
652
|
-
],
|
|
653
|
-
}
|
|
654
|
-
]
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
all_violations = find_question_block_violations(tool_input)
|
|
658
|
-
|
|
659
|
-
assert all_violations == [
|
|
660
|
-
"an option description runs 18 words, over the 15-word cap"
|
|
661
|
-
]
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
def test_lean_block_reason_names_the_fault_and_the_fix() -> None:
|
|
665
|
-
reason = build_lean_block_reason(["the question carries a table row"])
|
|
666
|
-
|
|
667
|
-
assert "the question carries a table row" in reason
|
|
668
|
-
assert "in chat text" in reason
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
def test_each_denial_carries_its_own_user_notice() -> None:
|
|
672
|
-
plan_question = f"{LEAN_QUESTION}\n- Split the file\n- Wire the gate"
|
|
673
|
-
lean_denial = _run_hook_with_payload(_ask_payload(plan_question, [LEAN_DESCRIPTION]))
|
|
674
|
-
heavy_denial = _run_hook_with_payload(
|
|
675
|
-
_ask_payload("Should we utilize the cache?", [LEAN_DESCRIPTION])
|
|
676
|
-
)
|
|
677
|
-
|
|
678
|
-
lean_notice = json.loads(lean_denial.stdout)["systemMessage"]
|
|
679
|
-
heavy_notice = json.loads(heavy_denial.stdout)["systemMessage"]
|
|
680
|
-
|
|
681
|
-
assert "question" in lean_notice.lower()
|
|
682
|
-
assert "word" in heavy_notice.lower()
|
|
683
|
-
assert lean_notice != heavy_notice
|