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,758 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""PreToolUse hook guarding AskUserQuestion prose and .md writes.
|
|
3
|
-
|
|
4
|
-
Two checks run. The word check reaches for the everyday word over the formal
|
|
5
|
-
one: `use` over `utilize`, `start` over `initiate`, `enough` over `sufficient`.
|
|
6
|
-
It reads both guarded surfaces -- AskUserQuestion (its question and option
|
|
7
|
-
prose) and Write/Edit/MultiEdit targeting a .md file -- with code fences,
|
|
8
|
-
inline code, blockquotes, URLs, and file paths stripped first, so exact
|
|
9
|
-
identifiers and paths are never flagged.
|
|
10
|
-
|
|
11
|
-
The lean-block check reads the AskUserQuestion surface alone. AskUserQuestion
|
|
12
|
-
renders as one plain text block, so the question text and each option
|
|
13
|
-
description stay short and unformatted: a fenced block, a heading, a table row,
|
|
14
|
-
a bullet or numbered list marker, a second paragraph, or prose past the
|
|
15
|
-
sentence and word caps all belong in chat text before the call. Structure is
|
|
16
|
-
read at block level on the raw text, with line endings folded to one spelling
|
|
17
|
-
first; an inline code span naming a path, a flag, or a command weighs one word
|
|
18
|
-
against the length caps.
|
|
19
|
-
|
|
20
|
-
See the plain-language and ask-user-question-required rules for the full
|
|
21
|
-
guidance this hook enforces.
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
import json
|
|
25
|
-
import sys
|
|
26
|
-
from pathlib import Path
|
|
27
|
-
from typing import TextIO
|
|
28
|
-
|
|
29
|
-
_hooks_dir = str(Path(__file__).resolve().parent.parent)
|
|
30
|
-
if _hooks_dir not in sys.path:
|
|
31
|
-
sys.path.insert(0, _hooks_dir)
|
|
32
|
-
|
|
33
|
-
from blocking.code_rules_shared import is_ephemeral_path # noqa: E402
|
|
34
|
-
from blocking.config.prose_style_enforcement_constants import ( # noqa: E402
|
|
35
|
-
prose_style_enforcement_enabled_in_environment,
|
|
36
|
-
)
|
|
37
|
-
from hooks_constants.hook_block_logger import log_hook_block # noqa: E402
|
|
38
|
-
from hooks_constants.plain_language_blocker_constants import ( # noqa: E402
|
|
39
|
-
ALL_CHAT_DETAIL_MARKERS,
|
|
40
|
-
ALL_LINE_ENDING_REPLACEMENTS,
|
|
41
|
-
ALL_SOFTWARE_TERMS,
|
|
42
|
-
ALL_TERM_PATTERNS,
|
|
43
|
-
ALL_WRITE_EDIT_TOOL_NAMES,
|
|
44
|
-
ASK_USER_QUESTION_TOOL_NAME,
|
|
45
|
-
BLOCKQUOTE_LINE_PATTERN,
|
|
46
|
-
COUNTABLE_WORD_PATTERN,
|
|
47
|
-
DOT_CLAUDE_DIRECTORY_NAME,
|
|
48
|
-
FENCED_CODE_BLOCK_PATTERN,
|
|
49
|
-
FILE_PATH_PATTERN,
|
|
50
|
-
HEAVY_WORD_SWAP_LIST_SEPARATOR,
|
|
51
|
-
INLINE_CODE_PATTERN,
|
|
52
|
-
INLINE_CODE_PLACEHOLDER,
|
|
53
|
-
INLINE_CODE_SPAN_PATTERN,
|
|
54
|
-
LEAN_QUESTION_BLOCK_GUIDANCE,
|
|
55
|
-
LEAN_QUESTION_BLOCK_PREFIX,
|
|
56
|
-
LEAN_QUESTION_VIOLATION_SEPARATOR,
|
|
57
|
-
MARKDOWN_EXTENSION,
|
|
58
|
-
MAXIMUM_OPTION_DESCRIPTION_SENTENCE_COUNT,
|
|
59
|
-
MAXIMUM_OPTION_DESCRIPTION_WORD_COUNT,
|
|
60
|
-
MAXIMUM_QUESTION_SENTENCE_COUNT,
|
|
61
|
-
MAXIMUM_QUESTION_WORD_COUNT,
|
|
62
|
-
OPTION_DESCRIPTION_SURFACE_NAME,
|
|
63
|
-
PROJECT_ALLOWLIST_FILENAME,
|
|
64
|
-
PROJECT_ROOT_WALK_LIMIT,
|
|
65
|
-
QUESTION_SURFACE_NAME,
|
|
66
|
-
REPOSITORY_MARKER_NAME,
|
|
67
|
-
SENTENCE_BOUNDARY_PATTERN,
|
|
68
|
-
URL_PATTERN,
|
|
69
|
-
USER_FACING_LEAN_QUESTION_NOTICE,
|
|
70
|
-
USER_FACING_PLAIN_LANGUAGE_NOTICE,
|
|
71
|
-
)
|
|
72
|
-
from hooks_constants.prose_matcher_precision_constants import ( # noqa: E402
|
|
73
|
-
ADVISORY_CONTEXT_SNIPPET_MAX_CHARS,
|
|
74
|
-
MATCHER_ID_PLAIN_LANGUAGE_HEAVY_WORD,
|
|
75
|
-
MAXIMUM_ADVISORY_EMITS_PER_CALL,
|
|
76
|
-
)
|
|
77
|
-
from observability.prose_matcher_advisory import emit_advisory_candidate # noqa: E402
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def strip_non_prose_regions(text: str) -> str:
|
|
81
|
-
"""Return text with code, quotes, URLs, and file paths removed.
|
|
82
|
-
|
|
83
|
-
These regions carry exact identifiers and references that plain language
|
|
84
|
-
leaves untouched, so they must not contribute matches.
|
|
85
|
-
"""
|
|
86
|
-
without_fences = FENCED_CODE_BLOCK_PATTERN.sub("", text)
|
|
87
|
-
without_inline_code = INLINE_CODE_PATTERN.sub("", without_fences)
|
|
88
|
-
without_blockquotes = BLOCKQUOTE_LINE_PATTERN.sub("", without_inline_code)
|
|
89
|
-
without_urls = URL_PATTERN.sub("", without_blockquotes)
|
|
90
|
-
without_paths = FILE_PATH_PATTERN.sub("", without_urls)
|
|
91
|
-
return without_paths
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def find_banned_terms(
|
|
95
|
-
text: str, all_allowlisted_terms: frozenset[str] = frozenset()
|
|
96
|
-
) -> list[tuple[str, str]]:
|
|
97
|
-
"""Return each (matched term, suggested replacement) found in the prose.
|
|
98
|
-
|
|
99
|
-
Each term appears at most once, in first-seen order. Matching is
|
|
100
|
-
case-insensitive and respects word boundaries; multi-word phrases match as
|
|
101
|
-
whole units. Terms in the software-term allowlist and terms in the
|
|
102
|
-
caller-supplied per-project allowlist are exempt and never flagged.
|
|
103
|
-
|
|
104
|
-
Args:
|
|
105
|
-
text: The prose to scan.
|
|
106
|
-
all_allowlisted_terms: Lowercased project-vocabulary terms to exempt, on top
|
|
107
|
-
of the built-in software-term allowlist.
|
|
108
|
-
|
|
109
|
-
Returns:
|
|
110
|
-
The (matched term, suggested replacement) pairs, in first-seen order.
|
|
111
|
-
"""
|
|
112
|
-
prose_text = strip_non_prose_regions(text)
|
|
113
|
-
all_matches: list[tuple[str, str]] = []
|
|
114
|
-
seen_terms: set[str] = set()
|
|
115
|
-
for each_pattern, each_replacement in ALL_TERM_PATTERNS:
|
|
116
|
-
first_match = each_pattern.search(prose_text)
|
|
117
|
-
if first_match is None:
|
|
118
|
-
continue
|
|
119
|
-
normalized_term = first_match.group(0).lower()
|
|
120
|
-
if normalized_term in seen_terms:
|
|
121
|
-
continue
|
|
122
|
-
if normalized_term in ALL_SOFTWARE_TERMS:
|
|
123
|
-
continue
|
|
124
|
-
if normalized_term in all_allowlisted_terms:
|
|
125
|
-
continue
|
|
126
|
-
seen_terms.add(normalized_term)
|
|
127
|
-
all_matches.append((normalized_term, each_replacement))
|
|
128
|
-
return all_matches
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def _allowlist_start_directory(
|
|
132
|
-
tool_name: str, tool_input: dict, payload_by_key: dict[str, object]
|
|
133
|
-
) -> Path | None:
|
|
134
|
-
"""Return the directory to begin the project-allowlist search from.
|
|
135
|
-
|
|
136
|
-
A Write/Edit/MultiEdit on a file starts at that file's parent directory; an
|
|
137
|
-
AskUserQuestion (or a write with no path) starts at the session working
|
|
138
|
-
directory the payload carries.
|
|
139
|
-
|
|
140
|
-
Args:
|
|
141
|
-
tool_name: The intercepted tool's name.
|
|
142
|
-
tool_input: The intercepted tool's input payload.
|
|
143
|
-
payload_by_key: The full PreToolUse payload carrying ``cwd``.
|
|
144
|
-
|
|
145
|
-
Returns:
|
|
146
|
-
The starting directory, or None when neither a file path nor a working
|
|
147
|
-
directory is available.
|
|
148
|
-
"""
|
|
149
|
-
if tool_name in ALL_WRITE_EDIT_TOOL_NAMES:
|
|
150
|
-
file_path = tool_input.get("file_path", "")
|
|
151
|
-
if isinstance(file_path, str) and file_path:
|
|
152
|
-
return Path(file_path).parent
|
|
153
|
-
working_directory = payload_by_key.get("cwd", "")
|
|
154
|
-
if isinstance(working_directory, str) and working_directory:
|
|
155
|
-
return Path(working_directory)
|
|
156
|
-
return None
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def _find_project_allowlist_file(start_directory: Path) -> Path | None:
|
|
160
|
-
"""Walk ancestors from start_directory for a repo-scoped project allowlist file.
|
|
161
|
-
|
|
162
|
-
::
|
|
163
|
-
|
|
164
|
-
parent/ .claude/allow -> ignored (above the repo root)
|
|
165
|
-
parent/repo/ <- .git .claude/allow -> applied (repo root reached)
|
|
166
|
-
parent/repo/pkg/ .claude/allow -> applied (inside the repo tree)
|
|
167
|
-
|
|
168
|
-
The allowlist must live inside the repository so it is reviewed like any
|
|
169
|
-
other committed config. The walk checks each directory for the allowlist,
|
|
170
|
-
then for the ``.git`` repository marker; it accepts an allowlist at or below
|
|
171
|
-
the first ``.git``-bearing directory, stops at that repository root, and
|
|
172
|
-
never ascends past it. When no ``.git`` marker appears within the walk
|
|
173
|
-
limit, the directory is not inside a repository and no allowlist applies, so
|
|
174
|
-
a global file such as ``~/.claude/plain-language-allow.json`` never loosens
|
|
175
|
-
the gate for every project.
|
|
176
|
-
|
|
177
|
-
Args:
|
|
178
|
-
start_directory: The directory to begin the upward walk from.
|
|
179
|
-
|
|
180
|
-
Returns:
|
|
181
|
-
The nearest in-repository ``.claude/plain-language-allow.json`` at or
|
|
182
|
-
below the repository root, or None when the walk finds no repository
|
|
183
|
-
root or no allowlist within it.
|
|
184
|
-
"""
|
|
185
|
-
nearest_allowlist: Path | None = None
|
|
186
|
-
current_directory = start_directory
|
|
187
|
-
for _ in range(PROJECT_ROOT_WALK_LIMIT):
|
|
188
|
-
candidate = current_directory / DOT_CLAUDE_DIRECTORY_NAME / PROJECT_ALLOWLIST_FILENAME
|
|
189
|
-
if nearest_allowlist is None and candidate.is_file():
|
|
190
|
-
nearest_allowlist = candidate
|
|
191
|
-
if (current_directory / REPOSITORY_MARKER_NAME).exists():
|
|
192
|
-
return nearest_allowlist
|
|
193
|
-
if current_directory.parent == current_directory:
|
|
194
|
-
break
|
|
195
|
-
current_directory = current_directory.parent
|
|
196
|
-
return None
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def _parse_project_allowlist_file(allowlist_path: Path) -> frozenset[str]:
|
|
200
|
-
"""Read a JSON array of allowlist words into a lowercased term set.
|
|
201
|
-
|
|
202
|
-
Malformed JSON, an unreadable file, or any non-list shape yields an empty
|
|
203
|
-
set, so the hook falls back to the standard check rather than crashing.
|
|
204
|
-
|
|
205
|
-
Args:
|
|
206
|
-
allowlist_path: The allowlist file to read.
|
|
207
|
-
|
|
208
|
-
Returns:
|
|
209
|
-
The lowercased string entries, or an empty set on any read/parse fault.
|
|
210
|
-
"""
|
|
211
|
-
try:
|
|
212
|
-
raw_text = allowlist_path.read_text(encoding="utf-8")
|
|
213
|
-
parsed_entries = json.loads(raw_text)
|
|
214
|
-
except (OSError, ValueError):
|
|
215
|
-
return frozenset()
|
|
216
|
-
if not isinstance(parsed_entries, list):
|
|
217
|
-
return frozenset()
|
|
218
|
-
return frozenset(
|
|
219
|
-
each_entry.lower() for each_entry in parsed_entries if isinstance(each_entry, str)
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
def _load_project_allowlist(
|
|
224
|
-
tool_name: str, tool_input: dict, payload_by_key: dict[str, object]
|
|
225
|
-
) -> frozenset[str]:
|
|
226
|
-
"""Load the per-project domain-vocabulary allowlist for the write's project.
|
|
227
|
-
|
|
228
|
-
Args:
|
|
229
|
-
tool_name: The intercepted tool's name.
|
|
230
|
-
tool_input: The intercepted tool's input payload.
|
|
231
|
-
payload_by_key: The full PreToolUse payload carrying ``cwd``.
|
|
232
|
-
|
|
233
|
-
Returns:
|
|
234
|
-
The lowercased allowlist terms for the project the write targets, or an
|
|
235
|
-
empty set when no allowlist applies.
|
|
236
|
-
"""
|
|
237
|
-
start_directory = _allowlist_start_directory(tool_name, tool_input, payload_by_key)
|
|
238
|
-
if start_directory is None:
|
|
239
|
-
return frozenset()
|
|
240
|
-
allowlist_path = _find_project_allowlist_file(start_directory)
|
|
241
|
-
if allowlist_path is None:
|
|
242
|
-
return frozenset()
|
|
243
|
-
return _parse_project_allowlist_file(allowlist_path)
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
def build_block_reason(all_matches: list[tuple[str, str]]) -> str:
|
|
247
|
-
"""Return a legacy block-reason string naming each term and plain swap.
|
|
248
|
-
|
|
249
|
-
Heavy-word enforcement no longer hard-denies (OP-07D). Callers that still
|
|
250
|
-
need the named swap list for notices or tests use this helper. Lean-block
|
|
251
|
-
denials use ``build_lean_block_reason`` instead.
|
|
252
|
-
"""
|
|
253
|
-
swap_phrases = ", ".join(
|
|
254
|
-
f'use "{each_replacement}" instead of "{each_term}"'
|
|
255
|
-
for each_term, each_replacement in all_matches
|
|
256
|
-
)
|
|
257
|
-
return (
|
|
258
|
-
"BLOCKED: [PLAIN_LANGUAGE] Heavy words detected -- "
|
|
259
|
-
f"{swap_phrases}. Reach for the everyday word the reader understands "
|
|
260
|
-
"on the first pass."
|
|
261
|
-
)
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
def build_advisory_system_message(all_matches: list[tuple[str, str]]) -> str:
|
|
265
|
-
"""Return allow-path advisory text naming everyday swaps for heavy words.
|
|
266
|
-
|
|
267
|
-
::
|
|
268
|
-
|
|
269
|
-
build_advisory_system_message([("initiate", "start")])
|
|
270
|
-
-> names use "start" instead of "initiate" (no permission deny)
|
|
271
|
-
|
|
272
|
-
Args:
|
|
273
|
-
all_matches: ``(heavy_term, everyday_swap)`` pairs from the word scan.
|
|
274
|
-
|
|
275
|
-
Returns:
|
|
276
|
-
A systemMessage string for PreToolUse allow with advisory guidance.
|
|
277
|
-
"""
|
|
278
|
-
swap_phrases = HEAVY_WORD_SWAP_LIST_SEPARATOR.join(
|
|
279
|
-
f'use "{each_replacement}" instead of "{each_term}"'
|
|
280
|
-
for each_term, each_replacement in all_matches
|
|
281
|
-
)
|
|
282
|
-
return (
|
|
283
|
-
f"[PLAIN_LANGUAGE advisory] {swap_phrases}. "
|
|
284
|
-
"Reach for the everyday word the reader understands on the first pass. "
|
|
285
|
-
"This notice does not deny the write; lean-block structure denials still apply."
|
|
286
|
-
)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
def _normalize_line_endings(prose_text: str) -> str:
|
|
290
|
-
"""Return the prose with every line ending spelled as a bare line feed.
|
|
291
|
-
|
|
292
|
-
::
|
|
293
|
-
|
|
294
|
-
in: "Which gate?\\r\\n\\r\\nThe write gate reads it."
|
|
295
|
-
out: "Which gate?\\n\\nThe write gate reads it."
|
|
296
|
-
|
|
297
|
-
The structure markers read line boundaries, so a payload carrying carriage
|
|
298
|
-
returns is normalized once here rather than at each pattern.
|
|
299
|
-
|
|
300
|
-
Args:
|
|
301
|
-
prose_text: One question text or one option description.
|
|
302
|
-
|
|
303
|
-
Returns:
|
|
304
|
-
The prose with carriage returns folded into line feeds.
|
|
305
|
-
"""
|
|
306
|
-
normalized_text = prose_text
|
|
307
|
-
for each_line_ending, each_replacement in ALL_LINE_ENDING_REPLACEMENTS:
|
|
308
|
-
normalized_text = normalized_text.replace(each_line_ending, each_replacement)
|
|
309
|
-
return normalized_text
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
def _mask_inline_code(prose_text: str) -> str:
|
|
313
|
-
"""Return the prose with each inline code span collapsed to a single word.
|
|
314
|
-
|
|
315
|
-
::
|
|
316
|
-
|
|
317
|
-
in: "Does `git diff --cached` list it?"
|
|
318
|
-
out: "Does code list it?"
|
|
319
|
-
|
|
320
|
-
A span names a path, a flag, or a command the reader needs verbatim, so it
|
|
321
|
-
weighs one word against the length caps. Only the length counts read the
|
|
322
|
-
masked prose: the structure markers read the raw text, so a one-line closed
|
|
323
|
-
fence still reads as a fence rather than as a span.
|
|
324
|
-
|
|
325
|
-
Args:
|
|
326
|
-
prose_text: One question text or one option description.
|
|
327
|
-
|
|
328
|
-
Returns:
|
|
329
|
-
The prose with every single-line inline code span replaced.
|
|
330
|
-
"""
|
|
331
|
-
return INLINE_CODE_SPAN_PATTERN.sub(INLINE_CODE_PLACEHOLDER, prose_text)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
def _count_prose_sentences(prose_text: str) -> int:
|
|
335
|
-
"""Return how many sentences one piece of question-block prose carries.
|
|
336
|
-
|
|
337
|
-
::
|
|
338
|
-
|
|
339
|
-
in: "Which gate runs first? Both read the same lines."
|
|
340
|
-
out: 2
|
|
341
|
-
|
|
342
|
-
A sentence closes on `.`, `!`, or `?` followed by a capitalized word or the
|
|
343
|
-
end of the text, so an abbreviation mid-sentence closes nothing.
|
|
344
|
-
|
|
345
|
-
Args:
|
|
346
|
-
prose_text: One question text or one option description.
|
|
347
|
-
|
|
348
|
-
Returns:
|
|
349
|
-
The count of sentence closings the prose carries.
|
|
350
|
-
"""
|
|
351
|
-
return len(SENTENCE_BOUNDARY_PATTERN.findall(prose_text))
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
def _count_prose_words(prose_text: str) -> int:
|
|
355
|
-
"""Return how many reader-visible words one piece of prose carries.
|
|
356
|
-
|
|
357
|
-
Args:
|
|
358
|
-
prose_text: One question text or one option description.
|
|
359
|
-
|
|
360
|
-
Returns:
|
|
361
|
-
The count of whitespace-separated tokens carrying a letter or digit.
|
|
362
|
-
"""
|
|
363
|
-
return len(COUNTABLE_WORD_PATTERN.findall(prose_text))
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
def _find_chat_detail_markers(prose_text: str) -> list[str]:
|
|
367
|
-
"""Return the name of each chat-detail marker the prose carries.
|
|
368
|
-
|
|
369
|
-
::
|
|
370
|
-
|
|
371
|
-
ok: "Which gate should run first?" -> []
|
|
372
|
-
flag: "Which gate?\\n- write\\n- commit" -> ["a bullet or numbered
|
|
373
|
-
list marker"]
|
|
374
|
-
|
|
375
|
-
Args:
|
|
376
|
-
prose_text: One question text or one option description.
|
|
377
|
-
|
|
378
|
-
Returns:
|
|
379
|
-
One marker name per marker found, in the order the markers are tried.
|
|
380
|
-
"""
|
|
381
|
-
return [
|
|
382
|
-
each_marker_name
|
|
383
|
-
for each_pattern, each_marker_name in ALL_CHAT_DETAIL_MARKERS
|
|
384
|
-
if each_pattern.search(prose_text)
|
|
385
|
-
]
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
def _find_lean_block_violations(
|
|
389
|
-
prose_text: str,
|
|
390
|
-
surface_name: str,
|
|
391
|
-
maximum_sentence_count: int,
|
|
392
|
-
maximum_word_count: int,
|
|
393
|
-
) -> list[str]:
|
|
394
|
-
"""Return every lean-block rule one piece of question-block prose breaks.
|
|
395
|
-
|
|
396
|
-
Args:
|
|
397
|
-
prose_text: One question text or one option description.
|
|
398
|
-
surface_name: How the violation text names this piece of prose.
|
|
399
|
-
maximum_sentence_count: The sentence cap this piece answers to.
|
|
400
|
-
maximum_word_count: The word cap this piece answers to.
|
|
401
|
-
|
|
402
|
-
Returns:
|
|
403
|
-
One violation text per broken rule, empty when the prose is lean.
|
|
404
|
-
|
|
405
|
-
The structure markers read the line-ending-normalized prose; the sentence
|
|
406
|
-
and word counts read that prose with its inline code spans masked.
|
|
407
|
-
"""
|
|
408
|
-
normalized_text = _normalize_line_endings(prose_text)
|
|
409
|
-
masked_text = _mask_inline_code(normalized_text)
|
|
410
|
-
all_violations = [
|
|
411
|
-
f"{surface_name} carries {each_marker_name}"
|
|
412
|
-
for each_marker_name in _find_chat_detail_markers(normalized_text)
|
|
413
|
-
]
|
|
414
|
-
sentence_count = _count_prose_sentences(masked_text)
|
|
415
|
-
if sentence_count > maximum_sentence_count:
|
|
416
|
-
all_violations.append(
|
|
417
|
-
f"{surface_name} runs {sentence_count} sentences, over the "
|
|
418
|
-
f"{maximum_sentence_count}-sentence cap"
|
|
419
|
-
)
|
|
420
|
-
word_count = _count_prose_words(masked_text)
|
|
421
|
-
if word_count > maximum_word_count:
|
|
422
|
-
all_violations.append(
|
|
423
|
-
f"{surface_name} runs {word_count} words, over the "
|
|
424
|
-
f"{maximum_word_count}-word cap"
|
|
425
|
-
)
|
|
426
|
-
return all_violations
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
def _violations_for_one_question(question_by_key: dict) -> list[str]:
|
|
430
|
-
"""Return the lean-block violations one question entry carries.
|
|
431
|
-
|
|
432
|
-
Args:
|
|
433
|
-
question_by_key: One entry of the AskUserQuestion questions list.
|
|
434
|
-
|
|
435
|
-
Returns:
|
|
436
|
-
One violation text per broken rule across the question text and every
|
|
437
|
-
option description the entry carries.
|
|
438
|
-
"""
|
|
439
|
-
all_violations: list[str] = []
|
|
440
|
-
question_text = question_by_key.get("question", "")
|
|
441
|
-
if isinstance(question_text, str):
|
|
442
|
-
all_violations.extend(
|
|
443
|
-
_find_lean_block_violations(
|
|
444
|
-
question_text,
|
|
445
|
-
QUESTION_SURFACE_NAME,
|
|
446
|
-
MAXIMUM_QUESTION_SENTENCE_COUNT,
|
|
447
|
-
MAXIMUM_QUESTION_WORD_COUNT,
|
|
448
|
-
)
|
|
449
|
-
)
|
|
450
|
-
all_options = question_by_key.get("options", [])
|
|
451
|
-
if not isinstance(all_options, list):
|
|
452
|
-
return all_violations
|
|
453
|
-
for each_option in all_options:
|
|
454
|
-
if not isinstance(each_option, dict):
|
|
455
|
-
continue
|
|
456
|
-
option_description = each_option.get("description", "")
|
|
457
|
-
if not isinstance(option_description, str):
|
|
458
|
-
continue
|
|
459
|
-
all_violations.extend(
|
|
460
|
-
_find_lean_block_violations(
|
|
461
|
-
option_description,
|
|
462
|
-
OPTION_DESCRIPTION_SURFACE_NAME,
|
|
463
|
-
MAXIMUM_OPTION_DESCRIPTION_SENTENCE_COUNT,
|
|
464
|
-
MAXIMUM_OPTION_DESCRIPTION_WORD_COUNT,
|
|
465
|
-
)
|
|
466
|
-
)
|
|
467
|
-
return all_violations
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
def find_question_block_violations(tool_input: dict) -> list[str]:
|
|
471
|
-
"""Return every lean-block violation an AskUserQuestion payload carries.
|
|
472
|
-
|
|
473
|
-
::
|
|
474
|
-
|
|
475
|
-
ok: "Which gate should run first?" + "Runs on every write." -> []
|
|
476
|
-
flag: a question with a three-bullet plan under it
|
|
477
|
-
-> ["the question carries a bullet or numbered list marker"]
|
|
478
|
-
|
|
479
|
-
Each violation text appears once, so two options breaking the same cap read
|
|
480
|
-
as one line.
|
|
481
|
-
|
|
482
|
-
Args:
|
|
483
|
-
tool_input: The AskUserQuestion tool input carrying the questions list.
|
|
484
|
-
|
|
485
|
-
Returns:
|
|
486
|
-
One violation text per broken rule, in first-seen order, empty when the
|
|
487
|
-
whole question block is lean.
|
|
488
|
-
"""
|
|
489
|
-
all_questions = tool_input.get("questions", [])
|
|
490
|
-
if not isinstance(all_questions, list):
|
|
491
|
-
return []
|
|
492
|
-
all_violations: list[str] = []
|
|
493
|
-
for each_question in all_questions:
|
|
494
|
-
if isinstance(each_question, dict):
|
|
495
|
-
all_violations.extend(_violations_for_one_question(each_question))
|
|
496
|
-
return list(dict.fromkeys(all_violations))
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
def build_lean_block_reason(all_violations: list[str]) -> str:
|
|
500
|
-
"""Return a deny reason naming each chat detail and where the detail belongs.
|
|
501
|
-
|
|
502
|
-
Args:
|
|
503
|
-
all_violations: The violation texts the question block earned.
|
|
504
|
-
|
|
505
|
-
Returns:
|
|
506
|
-
The full deny reason the model rewrites its question block against.
|
|
507
|
-
"""
|
|
508
|
-
violation_list = LEAN_QUESTION_VIOLATION_SEPARATOR.join(all_violations)
|
|
509
|
-
return f"{LEAN_QUESTION_BLOCK_PREFIX}{violation_list}. {LEAN_QUESTION_BLOCK_GUIDANCE}"
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
def _collect_ask_user_question_prose(tool_input: dict) -> str:
|
|
513
|
-
all_questions = tool_input.get("questions", [])
|
|
514
|
-
if not isinstance(all_questions, list):
|
|
515
|
-
return ""
|
|
516
|
-
prose_segments: list[str] = []
|
|
517
|
-
for each_question in all_questions:
|
|
518
|
-
if not isinstance(each_question, dict):
|
|
519
|
-
continue
|
|
520
|
-
question_text = each_question.get("question", "")
|
|
521
|
-
if isinstance(question_text, str):
|
|
522
|
-
prose_segments.append(question_text)
|
|
523
|
-
all_options = each_question.get("options", [])
|
|
524
|
-
if isinstance(all_options, list):
|
|
525
|
-
for each_option in all_options:
|
|
526
|
-
if isinstance(each_option, dict):
|
|
527
|
-
option_label = each_option.get("label", "")
|
|
528
|
-
if isinstance(option_label, str):
|
|
529
|
-
prose_segments.append(option_label)
|
|
530
|
-
option_description = each_option.get("description", "")
|
|
531
|
-
if isinstance(option_description, str):
|
|
532
|
-
prose_segments.append(option_description)
|
|
533
|
-
return "\n".join(prose_segments)
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
def _collect_write_edit_markdown_prose(tool_name: str, tool_input: dict) -> str:
|
|
537
|
-
file_path = tool_input.get("file_path", "")
|
|
538
|
-
if not isinstance(file_path, str) or not file_path.lower().endswith(MARKDOWN_EXTENSION):
|
|
539
|
-
return ""
|
|
540
|
-
if tool_name == "Write":
|
|
541
|
-
content = tool_input.get("content", "")
|
|
542
|
-
return content if isinstance(content, str) else ""
|
|
543
|
-
if tool_name == "Edit":
|
|
544
|
-
new_string = tool_input.get("new_string", "")
|
|
545
|
-
return new_string if isinstance(new_string, str) else ""
|
|
546
|
-
all_edits = tool_input.get("edits", [])
|
|
547
|
-
if not isinstance(all_edits, list):
|
|
548
|
-
return ""
|
|
549
|
-
prose_segments: list[str] = []
|
|
550
|
-
for each_edit in all_edits:
|
|
551
|
-
if isinstance(each_edit, dict):
|
|
552
|
-
new_string = each_edit.get("new_string", "")
|
|
553
|
-
if isinstance(new_string, str):
|
|
554
|
-
prose_segments.append(new_string)
|
|
555
|
-
return "\n".join(prose_segments)
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
def _collect_prose_for_tool(tool_name: str, tool_input: dict) -> str:
|
|
559
|
-
if tool_name == ASK_USER_QUESTION_TOOL_NAME:
|
|
560
|
-
return _collect_ask_user_question_prose(tool_input)
|
|
561
|
-
if tool_name in ALL_WRITE_EDIT_TOOL_NAMES:
|
|
562
|
-
return _collect_write_edit_markdown_prose(tool_name, tool_input)
|
|
563
|
-
return ""
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
def _notice_for_deny_reason(deny_reason: str) -> str:
|
|
567
|
-
"""Return the short notice matching the check that produced a deny reason.
|
|
568
|
-
|
|
569
|
-
Args:
|
|
570
|
-
deny_reason: The permissionDecisionReason text for the denial.
|
|
571
|
-
|
|
572
|
-
Returns:
|
|
573
|
-
The lean-block notice for a question-block denial, the word-swap notice
|
|
574
|
-
otherwise.
|
|
575
|
-
"""
|
|
576
|
-
if deny_reason.startswith(LEAN_QUESTION_BLOCK_PREFIX):
|
|
577
|
-
return USER_FACING_LEAN_QUESTION_NOTICE
|
|
578
|
-
return USER_FACING_PLAIN_LANGUAGE_NOTICE
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
def build_deny_payload(deny_reason: str) -> dict[str, object]:
|
|
582
|
-
"""Build the full deny payload the hook writes for a deny-reason string.
|
|
583
|
-
|
|
584
|
-
The payload carries the core permission decision plus the user-facing notice
|
|
585
|
-
and output suppression, so a caller routing this hook through a dispatcher
|
|
586
|
-
reproduces the same deny shape the standalone hook writes.
|
|
587
|
-
|
|
588
|
-
Args:
|
|
589
|
-
deny_reason: The permissionDecisionReason text for the denial.
|
|
590
|
-
|
|
591
|
-
Returns:
|
|
592
|
-
The deny payload dictionary the hook serializes to stdout.
|
|
593
|
-
"""
|
|
594
|
-
log_hook_block(
|
|
595
|
-
calling_hook_name="plain_language_blocker.py",
|
|
596
|
-
hook_event="PreToolUse",
|
|
597
|
-
block_reason=deny_reason,
|
|
598
|
-
)
|
|
599
|
-
return {
|
|
600
|
-
"hookSpecificOutput": {
|
|
601
|
-
"hookEventName": "PreToolUse",
|
|
602
|
-
"permissionDecision": "deny",
|
|
603
|
-
"permissionDecisionReason": deny_reason,
|
|
604
|
-
},
|
|
605
|
-
"systemMessage": _notice_for_deny_reason(deny_reason),
|
|
606
|
-
"suppressOutput": True,
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
def _emit_deny(deny_reason: str, output_stream: TextIO) -> None:
|
|
611
|
-
output_stream.write(json.dumps(build_deny_payload(deny_reason)))
|
|
612
|
-
output_stream.flush()
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
def build_allow_advisory_payload(system_message: str) -> dict[str, object]:
|
|
616
|
-
"""Build an allow payload that still surfaces everyday-word guidance.
|
|
617
|
-
|
|
618
|
-
Args:
|
|
619
|
-
system_message: Advisory text naming heavy terms and plain swaps.
|
|
620
|
-
|
|
621
|
-
Returns:
|
|
622
|
-
A PreToolUse payload without permissionDecision deny.
|
|
623
|
-
"""
|
|
624
|
-
return {
|
|
625
|
-
"hookSpecificOutput": {
|
|
626
|
-
"hookEventName": "PreToolUse",
|
|
627
|
-
"permissionDecision": "allow",
|
|
628
|
-
},
|
|
629
|
-
"systemMessage": system_message,
|
|
630
|
-
"suppressOutput": False,
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
def _emit_allow_advisory(system_message: str, output_stream: TextIO) -> None:
|
|
635
|
-
output_stream.write(json.dumps(build_allow_advisory_payload(system_message)))
|
|
636
|
-
output_stream.flush()
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
def _emit_plain_language_advisory_candidates(
|
|
640
|
-
surface: str,
|
|
641
|
-
all_matches: list[tuple[str, str]],
|
|
642
|
-
prose_text: str,
|
|
643
|
-
) -> None:
|
|
644
|
-
"""Record privacy-safe advisory hits when enforcement is off (fail open)."""
|
|
645
|
-
try:
|
|
646
|
-
for each_match in all_matches[:MAXIMUM_ADVISORY_EMITS_PER_CALL]:
|
|
647
|
-
each_term = each_match[0]
|
|
648
|
-
emit_advisory_candidate(
|
|
649
|
-
MATCHER_ID_PLAIN_LANGUAGE_HEAVY_WORD,
|
|
650
|
-
surface,
|
|
651
|
-
f"{each_term}:{prose_text[:ADVISORY_CONTEXT_SNIPPET_MAX_CHARS]}",
|
|
652
|
-
)
|
|
653
|
-
except (ImportError, OSError, TypeError, ValueError):
|
|
654
|
-
return
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
def evaluate(payload_by_key: dict[str, object]) -> str | None:
|
|
658
|
-
"""Return a lean-block deny reason, or None when the payload is allowed.
|
|
659
|
-
|
|
660
|
-
Heavy-word matches never produce a deny reason (OP-07D). When prose-style
|
|
661
|
-
enforcement is on, main still emits an allow advisory naming everyday swaps
|
|
662
|
-
for AskUserQuestion and ``.md`` writes. When enforcement is off, matches
|
|
663
|
-
only record privacy-safe advisory candidates (OP-07B).
|
|
664
|
-
|
|
665
|
-
Args:
|
|
666
|
-
payload_by_key: The PreToolUse payload with tool_name and tool_input.
|
|
667
|
-
|
|
668
|
-
Returns:
|
|
669
|
-
The permissionDecisionReason text for a lean-block denial, or None when
|
|
670
|
-
the payload is allowed (with or without a heavy-word advisory).
|
|
671
|
-
"""
|
|
672
|
-
deny_reason, _advisory_system_message = evaluate_with_advisory(payload_by_key)
|
|
673
|
-
return deny_reason
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
def evaluate_with_advisory(
|
|
677
|
-
payload_by_key: dict[str, object],
|
|
678
|
-
all_allowlisted_terms: frozenset[str] | None = None,
|
|
679
|
-
) -> tuple[str | None, str | None]:
|
|
680
|
-
"""Return ``(deny_reason, advisory_system_message)`` for one PreToolUse payload.
|
|
681
|
-
|
|
682
|
-
::
|
|
683
|
-
|
|
684
|
-
lean chat detail in AskUserQuestion -> (lean deny reason, None)
|
|
685
|
-
heavy word, enforcement ON -> (None, advisory systemMessage)
|
|
686
|
-
heavy word, enforcement OFF -> (None, None) + privacy JSONL
|
|
687
|
-
clean payload -> (None, None)
|
|
688
|
-
|
|
689
|
-
Args:
|
|
690
|
-
payload_by_key: The PreToolUse payload with tool_name and tool_input.
|
|
691
|
-
all_allowlisted_terms: Optional project-vocabulary exemptions. When
|
|
692
|
-
omitted, the project allowlist file is loaded for the payload path.
|
|
693
|
-
|
|
694
|
-
Returns:
|
|
695
|
-
A pair of optional deny reason and optional allow-path advisory message.
|
|
696
|
-
"""
|
|
697
|
-
raw_tool_name = payload_by_key.get("tool_name", "")
|
|
698
|
-
raw_tool_input = payload_by_key.get("tool_input", {})
|
|
699
|
-
if not isinstance(raw_tool_name, str) or not isinstance(raw_tool_input, dict):
|
|
700
|
-
return None, None
|
|
701
|
-
|
|
702
|
-
if raw_tool_name in ALL_WRITE_EDIT_TOOL_NAMES:
|
|
703
|
-
target_file_path = raw_tool_input.get("file_path", "")
|
|
704
|
-
if isinstance(target_file_path, str) and is_ephemeral_path(
|
|
705
|
-
target_file_path, payload_by_key
|
|
706
|
-
):
|
|
707
|
-
return None, None
|
|
708
|
-
|
|
709
|
-
if raw_tool_name == ASK_USER_QUESTION_TOOL_NAME:
|
|
710
|
-
all_block_violations = find_question_block_violations(raw_tool_input)
|
|
711
|
-
if all_block_violations:
|
|
712
|
-
return build_lean_block_reason(all_block_violations), None
|
|
713
|
-
|
|
714
|
-
prose_text = _collect_prose_for_tool(raw_tool_name, raw_tool_input)
|
|
715
|
-
if not prose_text:
|
|
716
|
-
return None, None
|
|
717
|
-
|
|
718
|
-
resolved_allowlisted_terms = (
|
|
719
|
-
all_allowlisted_terms
|
|
720
|
-
if all_allowlisted_terms is not None
|
|
721
|
-
else _load_project_allowlist(raw_tool_name, raw_tool_input, payload_by_key)
|
|
722
|
-
)
|
|
723
|
-
all_matches = find_banned_terms(
|
|
724
|
-
prose_text, all_allowlisted_terms=resolved_allowlisted_terms
|
|
725
|
-
)
|
|
726
|
-
if not all_matches:
|
|
727
|
-
return None, None
|
|
728
|
-
|
|
729
|
-
if not prose_style_enforcement_enabled_in_environment():
|
|
730
|
-
_emit_plain_language_advisory_candidates(raw_tool_name, all_matches, prose_text)
|
|
731
|
-
return None, None
|
|
732
|
-
|
|
733
|
-
return None, build_advisory_system_message(all_matches)
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
def main() -> None:
|
|
737
|
-
try:
|
|
738
|
-
input_data = json.load(sys.stdin)
|
|
739
|
-
except json.JSONDecodeError:
|
|
740
|
-
sys.exit(0)
|
|
741
|
-
|
|
742
|
-
if not isinstance(input_data, dict):
|
|
743
|
-
sys.exit(0)
|
|
744
|
-
|
|
745
|
-
deny_reason, advisory_system_message = evaluate_with_advisory(input_data)
|
|
746
|
-
if deny_reason is not None:
|
|
747
|
-
_emit_deny(deny_reason, sys.stdout)
|
|
748
|
-
sys.exit(0)
|
|
749
|
-
|
|
750
|
-
if advisory_system_message is not None:
|
|
751
|
-
_emit_allow_advisory(advisory_system_message, sys.stdout)
|
|
752
|
-
sys.exit(0)
|
|
753
|
-
|
|
754
|
-
sys.exit(0)
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
if __name__ == "__main__":
|
|
758
|
-
main()
|