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.
Files changed (535) hide show
  1. package/.agents/agents/AGENTS.md +6 -0
  2. package/.agents/skills/AGENTS.md +34 -35
  3. package/.agents/skills/_shared/pr-loop/AGENTS.md +22 -15
  4. package/.agents/skills/_shared/pr-loop/portable-driver.md +6 -7
  5. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +2 -2
  6. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +1 -1
  7. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +25 -16
  8. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +1 -5
  9. package/.agents/skills/autoconverge/reference/convergence-gates.md +31 -0
  10. package/.agents/skills/autoconverge/reference/convergence.md +8 -10
  11. package/.agents/skills/autoconverge/reference/gotchas.md +1 -1
  12. package/.agents/skills/autoconverge/reference/stop-conditions.md +1 -1
  13. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +1 -1
  14. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +1 -1
  15. package/.agents/skills/autoconverge/workflow/converge.mjs +3 -3
  16. package/.agents/skills/cert-classification-rule/SKILL.md +224 -0
  17. package/.agents/skills/cert-classification-rule/scripts/resort_cert_folders.py +158 -0
  18. package/.agents/skills/eli5/SKILL.md +128 -0
  19. package/.agents/skills/eli5/reference/task-seeds.md +11 -0
  20. package/.agents/skills/fresh-branch/scripts/test_create_fresh_branch.py +2 -3
  21. package/.agents/skills/name-by-capability-audit/SKILL.md +67 -0
  22. package/.agents/skills/name-by-capability-audit/reference/fetch-commands.md +29 -0
  23. package/.agents/skills/name-by-capability-audit/reference/offense-examples.md +32 -0
  24. package/.agents/skills/name-by-capability-audit/reference/report-template.md +29 -0
  25. package/.agents/skills/name-by-capability-audit/reference/rule-checklist.md +38 -0
  26. package/.agents/skills/name-by-capability-audit/reference/task-seeds.md +9 -0
  27. package/.agents/skills/pr-cleanup/SKILL.md +99 -0
  28. package/.agents/skills/prototype/reference/honest-limitations.md +1 -1
  29. package/.agents/skills/prototype/reference/promotion-tasks.md +1 -1
  30. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +2 -2
  31. package/.agents/skills/prototype/workflows/promotion.md +2 -2
  32. package/.agents/skills/review-router/SKILL.md +51 -0
  33. package/.agents/skills/review-router/reference/route-policy.json +7 -0
  34. package/.agents/skills/review-router/reference/task-seeds.md +1 -0
  35. package/.agents/skills/review-router/scripts/conftest.py +4 -0
  36. package/.agents/skills/review-router/scripts/review_router.py +50 -0
  37. package/.agents/skills/review-router/scripts/review_router_cli.py +211 -0
  38. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +1 -0
  39. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  40. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +13 -0
  41. package/.agents/skills/review-router/scripts/test_review_router.py +23 -0
  42. package/.agents/skills/review-router/scripts/test_review_router_cli.py +47 -0
  43. package/.agents/skills/review-tier/SKILL.md +56 -0
  44. package/.agents/skills/review-tier/reference/task-seeds.md +1 -0
  45. package/.agents/skills/review-tier/reference/tier-policy.json +9 -0
  46. package/.agents/skills/review-tier/scripts/conftest.py +4 -0
  47. package/.agents/skills/review-tier/scripts/review_tier.py +332 -0
  48. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +1 -0
  49. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  50. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +29 -0
  51. package/.agents/skills/review-tier/scripts/test_generation.py +171 -0
  52. package/.agents/skills/review-tier/scripts/test_review_tier.py +33 -0
  53. package/.agents/skills/session-log/SKILL.md +1 -1
  54. package/.agents/skills/shared-extraction-audit/SKILL.md +160 -0
  55. package/.agents/skills/shared-extraction-audit/reference/examples.md +46 -0
  56. package/.agents/skills/shared-extraction-audit/reference/offense-taxonomy.md +100 -0
  57. package/.agents/skills/source-command-logifix/SKILL.md +29 -0
  58. package/.agents/skills/source-command-sr-loop/SKILL.md +58 -0
  59. package/.agents/skills-archived/AGENTS.md +45 -0
  60. package/.agents/{skills → skills-archived}/anthropic-plan/AGENTS.md +1 -1
  61. package/.agents/{skills → skills-archived}/bugteam/scripts/reflow_skill_md.py +3 -3
  62. package/.agents/{skills → skills-archived}/codex-review/test_skill_scaffold.py +1 -1
  63. package/.agents/{skills → skills-archived}/copilot-finding-triage/SKILL.md +1 -1
  64. package/.agents/{skills → skills-archived}/pr-converge/SKILL.md +6 -6
  65. package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/AGENTS.md +1 -1
  66. package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/constants.py +1 -1
  67. package/.agents/{skills → skills-archived}/pr-converge/reference/multi-pr-orchestration.md +1 -1
  68. package/.agents/{skills → skills-archived}/pr-converge/reference/per-tick.md +3 -3
  69. package/.agents/{skills → skills-archived}/pr-converge/scripts/reflow_skill_md.py +2 -2
  70. package/.agents/{skills → skills-archived}/pr-converge/test_portable_pacer_gate.py +6 -2
  71. package/.agents/{skills → skills-archived}/pr-converge/workflows/schedule-wakeup-loop.md +1 -1
  72. package/.agents/{skills → skills-archived}/split-pr/reference/proposal-format.md +1 -1
  73. package/AGENTS.md +34 -58
  74. package/_shared/AGENTS.md +2 -0
  75. package/_shared/advisor/reference/sol-rung.md +2 -2
  76. package/_shared/advisor/reference/third-party-bind.md +1 -1
  77. package/_shared/pr-loop/audit-reply-template.md +4 -0
  78. package/_shared/pr-loop/fix-protocol.md +7 -6
  79. package/_shared/pr-loop/gh-payloads.md +8 -6
  80. package/_shared/pr-loop/precatch-rubric.md +6 -5
  81. package/_shared/pr-loop/scripts/AGENTS.md +15 -0
  82. package/_shared/pr-loop/scripts/_pr_converge_path_setup.py +38 -0
  83. package/_shared/pr-loop/scripts/check_bugbot_ci.py +279 -0
  84. package/_shared/pr-loop/scripts/check_convergence.py +923 -0
  85. package/_shared/pr-loop/scripts/check_convergence_availability.py +275 -0
  86. package/_shared/pr-loop/scripts/check_convergence_gates.py +279 -0
  87. package/_shared/pr-loop/scripts/check_convergence_thread_gates.py +219 -0
  88. package/_shared/pr-loop/scripts/check_pending_reviews.py +154 -0
  89. package/_shared/pr-loop/scripts/codex_down_classifier.py +98 -0
  90. package/_shared/pr-loop/scripts/codex_review_scripts_constants/.claude/CLAUDE.md +1 -0
  91. package/_shared/pr-loop/scripts/codex_review_scripts_constants/AGENTS.md +18 -0
  92. package/_shared/pr-loop/scripts/codex_review_scripts_constants/__init__.py +1 -0
  93. package/_shared/pr-loop/scripts/codex_review_scripts_constants/classifier_constants.py +35 -0
  94. package/_shared/pr-loop/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +82 -0
  95. package/_shared/pr-loop/scripts/codex_review_scripts_constants/findings_constants.py +18 -0
  96. package/_shared/pr-loop/scripts/codex_review_scripts_constants/run_constants.py +53 -0
  97. package/_shared/pr-loop/scripts/codex_usage_probe.py +570 -0
  98. package/_shared/pr-loop/scripts/conftest.py +43 -0
  99. package/_shared/pr-loop/scripts/fetch_copilot_reviews.py +134 -0
  100. package/_shared/pr-loop/scripts/fixtures/auth_failure_synthetic.txt +1 -0
  101. package/_shared/pr-loop/scripts/fixtures/config_load_failure_v0.125.0.txt +1 -0
  102. package/_shared/pr-loop/scripts/fixtures/freeform_findings_v0.144.3.txt +6 -0
  103. package/_shared/pr-loop/scripts/fixtures/model_rejection_v0.125.0.jsonl +5 -0
  104. package/_shared/pr-loop/scripts/fixtures/structured_findings.txt +13 -0
  105. package/_shared/pr-loop/scripts/fixtures/success_stream_v0.144.3.jsonl +6 -0
  106. package/_shared/pr-loop/scripts/fixtures/unknown_failure_synthetic.txt +1 -0
  107. package/_shared/pr-loop/scripts/fixtures/usage_limit_synthetic.txt +1 -0
  108. package/_shared/pr-loop/scripts/parse_codex_findings.py +207 -0
  109. package/_shared/pr-loop/scripts/post_fix_reply.py +168 -0
  110. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/.claude/CLAUDE.md +1 -0
  111. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/AGENTS.md +18 -0
  112. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/__init__.py +0 -0
  113. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +70 -0
  114. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/pr_converge_constants.py +118 -0
  115. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/reflow_skill_md_constants.py +13 -0
  116. package/_shared/pr-loop/scripts/pr_converge_skill_constants/.claude/CLAUDE.md +1 -0
  117. package/_shared/pr-loop/scripts/pr_converge_skill_constants/AGENTS.md +26 -0
  118. package/_shared/pr-loop/scripts/pr_converge_skill_constants/__init__.py +0 -0
  119. package/_shared/pr-loop/scripts/pr_converge_skill_constants/constants.py +69 -0
  120. package/_shared/pr-loop/scripts/run_codex_review.py +609 -0
  121. package/_shared/pr-loop/scripts/test__pr_converge_path_setup.py +30 -0
  122. package/_shared/pr-loop/scripts/test_check_bugbot_ci.py +312 -0
  123. package/_shared/pr-loop/scripts/test_check_convergence.py +438 -0
  124. package/_shared/pr-loop/scripts/test_check_convergence_availability.py +391 -0
  125. package/_shared/pr-loop/scripts/test_check_convergence_codex.py +517 -0
  126. package/_shared/pr-loop/scripts/test_check_convergence_contract.py +400 -0
  127. package/_shared/pr-loop/scripts/test_check_convergence_fixture.py +179 -0
  128. package/_shared/pr-loop/scripts/test_check_convergence_gates.py +84 -0
  129. package/_shared/pr-loop/scripts/test_check_convergence_thread_gates.py +99 -0
  130. package/_shared/pr-loop/scripts/test_codex_down_classifier.py +143 -0
  131. package/_shared/pr-loop/scripts/test_codex_usage_probe.py +683 -0
  132. package/_shared/pr-loop/scripts/test_parse_codex_findings.py +130 -0
  133. package/_shared/pr-loop/scripts/test_run_codex_review.py +969 -0
  134. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +1 -1
  135. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +1 -1
  136. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +1 -1
  137. package/_shared/pr-loop/state-schema.md +20 -28
  138. package/_shared/process-tree/AGENTS.md +3 -3
  139. package/bin/ever-shipped-skills.mjs +29 -1
  140. package/bin/install-constants.mjs +15 -6
  141. package/bin/install.agents-home.test.mjs +13 -0
  142. package/bin/install.cursor-rules.test.mjs +3 -3
  143. package/bin/install.mjs +80 -20
  144. package/bin/install.profile-root.test.mjs +1 -1
  145. package/bin/install.prune.test.mjs +23 -204
  146. package/bin/install.test.mjs +8 -5
  147. package/bin/resolve-install-root.mjs +21 -0
  148. package/bin/resolve-package-managed-directory.mjs +4 -4
  149. package/commands/AGENTS.md +2 -0
  150. package/commands/sr-loop.md +3 -0
  151. package/docs/references/prose-style-enforcement.md +3 -7
  152. package/hooks/AGENTS.md +0 -3
  153. package/hooks/blocking/AGENTS.md +0 -2
  154. package/hooks/blocking/ask_user_question_shape_blocker.py +83 -0
  155. package/hooks/blocking/pre_tool_use_dispatcher.py +2 -19
  156. package/hooks/blocking/state_description_blocker.py +1 -1
  157. package/hooks/blocking/test_ask_user_question_shape.py +69 -0
  158. package/hooks/blocking/test_pre_tool_use_dispatcher.py +17 -58
  159. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +9 -104
  160. package/hooks/blocking/test_state_description_blocker.py +1 -0
  161. package/hooks/blocking/test_stop_dispatcher.py +1 -26
  162. package/hooks/hooks.json +1 -1
  163. package/hooks/hooks_constants/AGENTS.md +0 -3
  164. package/hooks/hooks_constants/ask_user_question_shape.py +1 -1
  165. package/hooks/hooks_constants/ask_user_question_shape_constants.py +73 -0
  166. package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +1 -2
  167. package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -1
  168. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -7
  169. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -1
  170. package/hooks/hooks_constants/test_ask_user_question_shape.py +1 -1
  171. package/hooks/hooks_constants/test_prose_metrics_parity.py +29 -107
  172. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -1
  173. package/hooks/hooks_constants/working_style_prompt_constants.py +9 -10
  174. package/hooks/session/AGENTS.md +4 -1
  175. package/hooks/session/test_working_style_prompt.py +8 -4
  176. package/hooks/session/working_style_prompt.py +4 -2
  177. package/package.json +1 -4
  178. package/rules/AGENTS.md +2 -5
  179. package/rules/asd-ste100-language.md +39 -0
  180. package/rules/ask-user-question-required.md +1 -1
  181. package/rules/long-horizon-autonomy.md +1 -1
  182. package/scripts/AGENTS.md +1 -0
  183. package/scripts/Show-Asset.ps1 +106 -0
  184. package/scripts/test_validate_instruction_pairs.py +30 -3
  185. package/scripts/tests/test_engine.py +4 -4
  186. package/scripts/tests/test_rules.py +5 -5
  187. package/system-prompts/AGENTS.md +8 -0
  188. package/system-prompts/software-engineer.xml +8 -3
  189. package/.agents/skills/grokify/evals/README.md +0 -72
  190. package/.agents/skills/grokify/evals/parse-payload.test.mjs +0 -171
  191. package/.agents/skills/grokify/evals/run-capability-evals.mjs +0 -545
  192. package/_shared/pr-loop/scripts/README.md +0 -21
  193. package/docs/imagegen.md +0 -22
  194. package/hooks/blocking/eli11_reply_enforcer.py +0 -436
  195. package/hooks/blocking/plain_language_blocker.py +0 -758
  196. package/hooks/blocking/test_eli11_reply_enforcer.py +0 -390
  197. package/hooks/blocking/test_plain_language_blocker.py +0 -683
  198. package/hooks/blocking/test_plain_language_blocker_allowlist.py +0 -228
  199. package/hooks/diagnostic/AGENTS.md +0 -75
  200. package/hooks/diagnostic/CLAUDE.md +0 -1
  201. package/hooks/diagnostic/hook_log_extractor.py +0 -875
  202. package/hooks/diagnostic/hook_log_init.py +0 -202
  203. package/hooks/diagnostic/hook_log_stop_wrapper.py +0 -26
  204. package/hooks/diagnostic/migrations/2026-04-25-drop-themes-hook-events.sql +0 -3
  205. package/hooks/diagnostic/migrations/AGENTS.md +0 -16
  206. package/hooks/diagnostic/migrations/CLAUDE.md +0 -1
  207. package/hooks/diagnostic/migrations/README.md +0 -76
  208. package/hooks/diagnostic/queries/AGENTS.md +0 -19
  209. package/hooks/diagnostic/queries/CLAUDE.md +0 -1
  210. package/hooks/diagnostic/queries/block_details_for_hook.sql +0 -26
  211. package/hooks/diagnostic/queries/blocks_by_category.sql +0 -10
  212. package/hooks/diagnostic/queries/blocks_by_tool.sql +0 -9
  213. package/hooks/diagnostic/queries/blocks_last_7_days.sql +0 -11
  214. package/hooks/diagnostic/queries/top_blockers_last_24_hours.sql +0 -12
  215. package/hooks/diagnostic/queries/top_blockers_overall.sql +0 -12
  216. package/hooks/diagnostic/requirements-hook-logs-dev.txt +0 -2
  217. package/hooks/diagnostic/requirements-hook-logs.txt +0 -1
  218. package/hooks/diagnostic/schema.sql +0 -51
  219. package/hooks/diagnostic/test_hook_log_extractor.py +0 -1518
  220. package/hooks/diagnostic/test_hook_log_init.py +0 -227
  221. package/hooks/diagnostic/test_hook_log_stop_wrapper.py +0 -17
  222. package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +0 -94
  223. package/hooks/hooks_constants/hook_log_extractor_constants.py +0 -233
  224. package/hooks/hooks_constants/plain_language_blocker_constants.py +0 -366
  225. package/hooks/hooks_constants/test_hook_log_extractor_constants.py +0 -123
  226. package/rules/doc-prose-cuts.md +0 -58
  227. package/rules/eli11-replies.md +0 -36
  228. package/rules/opus5-communication-contract.md +0 -45
  229. package/rules/plain-language.md +0 -9
  230. /package/.agents/{skills → skills-archived}/anthropic-plan/.claude/CLAUDE.md +0 -0
  231. /package/.agents/{skills → skills-archived}/anthropic-plan/SKILL.md +0 -0
  232. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/.claude/CLAUDE.md +0 -0
  233. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/AGENTS.md +0 -0
  234. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/.claude/CLAUDE.md +0 -0
  235. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +0 -0
  236. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/__init__.py +0 -0
  237. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/validate_packet_constants.py +0 -0
  238. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/test_validate_packet.py +0 -0
  239. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/validate_packet.py +0 -0
  240. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/.claude/CLAUDE.md +0 -0
  241. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/AGENTS.md +0 -0
  242. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/README.md +0 -0
  243. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/build-prompt.md +0 -0
  244. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/reuse-audit.md +0 -0
  245. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/source-map.md +0 -0
  246. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/visual-plan.template.html +0 -0
  247. /package/.agents/{skills → skills-archived}/anthropic-plan/test_skill_contract.py +0 -0
  248. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/.claude/CLAUDE.md +0 -0
  249. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/AGENTS.md +0 -0
  250. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/plan-packet.contract.test.mjs +0 -0
  251. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/plan-packet.mjs +0 -0
  252. /package/.agents/{skills → skills-archived}/auditing-claude-config/.claude/CLAUDE.md +0 -0
  253. /package/.agents/{skills → skills-archived}/auditing-claude-config/AGENTS.md +0 -0
  254. /package/.agents/{skills → skills-archived}/auditing-claude-config/SKILL.md +0 -0
  255. /package/.agents/{skills → skills-archived}/auditing-claude-config/reference/probe-hook.md +0 -0
  256. /package/.agents/{skills → skills-archived}/beat-sheet/SKILL.md +0 -0
  257. /package/.agents/{skills → skills-archived}/beat-sheet/reference/visual-beats.md +0 -0
  258. /package/.agents/{skills → skills-archived}/bugteam/.claude/CLAUDE.md +0 -0
  259. /package/.agents/{skills → skills-archived}/bugteam/AGENTS.md +0 -0
  260. /package/.agents/{skills → skills-archived}/bugteam/CONSTRAINTS.md +0 -0
  261. /package/.agents/{skills → skills-archived}/bugteam/EXAMPLES.md +0 -0
  262. /package/.agents/{skills → skills-archived}/bugteam/PROMPTS.md +0 -0
  263. /package/.agents/{skills → skills-archived}/bugteam/SKILL.md +0 -0
  264. /package/.agents/{skills → skills-archived}/bugteam/reference/.claude/CLAUDE.md +0 -0
  265. /package/.agents/{skills → skills-archived}/bugteam/reference/AGENTS.md +0 -0
  266. /package/.agents/{skills → skills-archived}/bugteam/reference/README.md +0 -0
  267. /package/.agents/{skills → skills-archived}/bugteam/reference/audit-and-teammates.md +0 -0
  268. /package/.agents/{skills → skills-archived}/bugteam/reference/copilot-gap-analysis.md +0 -0
  269. /package/.agents/{skills → skills-archived}/bugteam/reference/design-rationale.md +0 -0
  270. /package/.agents/{skills → skills-archived}/bugteam/reference/github-pr-reviews.md +0 -0
  271. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/.claude/CLAUDE.md +0 -0
  272. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/AGENTS.md +0 -0
  273. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-assign-ids.md +0 -0
  274. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-capture-excerpts.md +0 -0
  275. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-walk-categories.md +0 -0
  276. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-write-xml.md +0 -0
  277. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-append-summary.md +0 -0
  278. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-apply-fixes.md +0 -0
  279. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-git-add-commit.md +0 -0
  280. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-git-push.md +0 -0
  281. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-post-reply.md +0 -0
  282. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-publish-summary.md +0 -0
  283. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-py-compile.md +0 -0
  284. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-read-files.md +0 -0
  285. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-resolve-thread.md +0 -0
  286. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-test-suite.md +0 -0
  287. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-violation-count.md +0 -0
  288. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-write-xml.md +0 -0
  289. /package/.agents/{skills → skills-archived}/bugteam/reference/team-setup.md +0 -0
  290. /package/.agents/{skills → skills-archived}/bugteam/scripts/.claude/CLAUDE.md +0 -0
  291. /package/.agents/{skills → skills-archived}/bugteam/scripts/AGENTS.md +0 -0
  292. /package/.agents/{skills → skills-archived}/bugteam/scripts/README.md +0 -0
  293. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_code_rules_gate.py +0 -0
  294. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_fix_hookspath.py +0 -0
  295. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_preflight.py +0 -0
  296. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/.claude/CLAUDE.md +0 -0
  297. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +0 -0
  298. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/__init__.py +0 -0
  299. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/probe_code_rules_enforcer_check_constants.py +0 -0
  300. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/reflow_skill_md_constants.py +0 -0
  301. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/windows_safe_rmtree_constants.py +0 -0
  302. /package/.agents/{skills → skills-archived}/bugteam/scripts/probe_code_rules_enforcer_check.py +0 -0
  303. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_code_rules_gate.py +0 -0
  304. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -0
  305. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_preflight.py +0 -0
  306. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_probe_code_rules_enforcer_check.py +0 -0
  307. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_windows_safe_rmtree.py +0 -0
  308. /package/.agents/{skills → skills-archived}/bugteam/scripts/windows_safe_rmtree.py +0 -0
  309. /package/.agents/{skills → skills-archived}/bugteam/sources.md +0 -0
  310. /package/.agents/{skills → skills-archived}/bugteam/test_skill_additions.py +0 -0
  311. /package/.agents/{skills → skills-archived}/closeout/SKILL.md +0 -0
  312. /package/.agents/{skills → skills-archived}/closeout/reference/handoff-prompt-template.md +0 -0
  313. /package/.agents/{skills → skills-archived}/closeout/reference/pii-redaction-checklist.md +0 -0
  314. /package/.agents/{skills → skills-archived}/codex-review/.claude/CLAUDE.md +0 -0
  315. /package/.agents/{skills → skills-archived}/codex-review/AGENTS.md +0 -0
  316. /package/.agents/{skills → skills-archived}/codex-review/SKILL.md +0 -0
  317. /package/.agents/{skills → skills-archived}/codex-review/reference/.claude/CLAUDE.md +0 -0
  318. /package/.agents/{skills → skills-archived}/codex-review/reference/AGENTS.md +0 -0
  319. /package/.agents/{skills → skills-archived}/codex-review/reference/cli-contract.md +0 -0
  320. /package/.agents/{skills → skills-archived}/codex-review/reference/loop-integration.md +0 -0
  321. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_down_classifier.py +0 -0
  322. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/.claude/CLAUDE.md +0 -0
  323. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +0 -0
  324. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/__init__.py +0 -0
  325. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/classifier_constants.py +0 -0
  326. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +0 -0
  327. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/findings_constants.py +0 -0
  328. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/run_constants.py +0 -0
  329. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_usage_probe.py +0 -0
  330. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/auth_failure_synthetic.txt +0 -0
  331. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/config_load_failure_v0.125.0.txt +0 -0
  332. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/freeform_findings_v0.144.3.txt +0 -0
  333. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/model_rejection_v0.125.0.jsonl +0 -0
  334. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/structured_findings.txt +0 -0
  335. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/success_stream_v0.144.3.jsonl +0 -0
  336. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/unknown_failure_synthetic.txt +0 -0
  337. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/usage_limit_synthetic.txt +0 -0
  338. /package/.agents/{skills → skills-archived}/codex-review/scripts/parse_codex_findings.py +0 -0
  339. /package/.agents/{skills → skills-archived}/codex-review/scripts/run_codex_review.py +0 -0
  340. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_codex_down_classifier.py +0 -0
  341. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_codex_usage_probe.py +0 -0
  342. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_parse_codex_findings.py +0 -0
  343. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_run_codex_review.py +0 -0
  344. /package/.agents/{skills → skills-archived}/comments/SKILL.md +0 -0
  345. /package/.agents/{skills → skills-archived}/condensing-instructions/SKILL.md +0 -0
  346. /package/.agents/{skills → skills-archived}/copilot-finding-triage/reference/tier-rubric.md +0 -0
  347. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/conftest.py +0 -0
  348. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/__init__.py +0 -0
  349. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/__init__.py +0 -0
  350. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/notify_ntfy_constants.py +0 -0
  351. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/notify_ntfy.py +0 -0
  352. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/test_notify_ntfy.py +0 -0
  353. /package/.agents/{skills → skills-archived}/copilot-finding-triage/templates/notification.md +0 -0
  354. /package/.agents/{skills → skills-archived}/copilot-review/.claude/CLAUDE.md +0 -0
  355. /package/.agents/{skills → skills-archived}/copilot-review/AGENTS.md +0 -0
  356. /package/.agents/{skills → skills-archived}/copilot-review/SKILL.md +0 -0
  357. /package/.agents/{skills → skills-archived}/copilot-review/templates/subagent-prompt.md +0 -0
  358. /package/.agents/{skills → skills-archived}/descriptions/SKILL.md +0 -0
  359. /package/.agents/{skills → skills-archived}/emergencies/SKILL.md +0 -0
  360. /package/.agents/{skills → skills-archived}/grokify/SKILL.md +0 -0
  361. /package/.agents/{skills → skills-archived}/grokify/capability-claims.test.mjs +0 -0
  362. /package/.agents/{skills → skills-archived}/grokify/templates/handoff-template.md +0 -0
  363. /package/.agents/{skills → skills-archived}/plan-to-pr/SKILL.md +0 -0
  364. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/final-validation-tasks.md +0 -0
  365. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/model-routing.md +0 -0
  366. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/packet-contract.md +0 -0
  367. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/packet-schema.json +0 -0
  368. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/process-inventory.md +0 -0
  369. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/review-loop.md +0 -0
  370. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/run-record.schema.json +0 -0
  371. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/self-audit-tasks.md +0 -0
  372. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/task-seeds.md +0 -0
  373. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/task-ticket.md +0 -0
  374. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/config/__init__.py +0 -0
  375. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/config/constants.py +0 -0
  376. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/create_packet.py +0 -0
  377. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/load_skill_constants.py +0 -0
  378. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_create_packet.py +0 -0
  379. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_packet.py +0 -0
  380. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_protocol.py +0 -0
  381. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_run.py +0 -0
  382. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_packet.py +0 -0
  383. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_protocol.py +0 -0
  384. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_run.py +0 -0
  385. /package/.agents/{skills → skills-archived}/plan-to-pr/test_skill_contract.py +0 -0
  386. /package/.agents/{skills → skills-archived}/plan-to-pr/test_task_ticket_contract.py +0 -0
  387. /package/.agents/{skills → skills-archived}/pr-converge/.claude/CLAUDE.md +0 -0
  388. /package/.agents/{skills → skills-archived}/pr-converge/AGENTS.md +0 -0
  389. /package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/.claude/CLAUDE.md +0 -0
  390. /package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/__init__.py +0 -0
  391. /package/.agents/{skills → skills-archived}/pr-converge/reference/.claude/CLAUDE.md +0 -0
  392. /package/.agents/{skills → skills-archived}/pr-converge/reference/AGENTS.md +0 -0
  393. /package/.agents/{skills → skills-archived}/pr-converge/reference/convergence-gates.md +0 -0
  394. /package/.agents/{skills → skills-archived}/pr-converge/reference/examples.md +0 -0
  395. /package/.agents/{skills → skills-archived}/pr-converge/reference/fix-protocol.md +0 -0
  396. /package/.agents/{skills → skills-archived}/pr-converge/reference/ground-rules.md +0 -0
  397. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/.claude/CLAUDE.md +0 -0
  398. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/AGENTS.md +0 -0
  399. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-post-replies.md +0 -0
  400. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-publish-summary.md +0 -0
  401. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-push.md +0 -0
  402. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-read-filelines.md +0 -0
  403. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-reset-state.md +0 -0
  404. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-resolve-threads.md +0 -0
  405. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-spawn-clean-coder.md +0 -0
  406. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-stage-commit.md +0 -0
  407. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-trigger-bugbot.md +0 -0
  408. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-write-test.md +0 -0
  409. /package/.agents/{skills → skills-archived}/pr-converge/reference/progress-checklist.md +0 -0
  410. /package/.agents/{skills → skills-archived}/pr-converge/reference/state-schema.md +0 -0
  411. /package/.agents/{skills → skills-archived}/pr-converge/reference/stop-conditions.md +0 -0
  412. /package/.agents/{skills → skills-archived}/pr-converge/scripts/.claude/CLAUDE.md +0 -0
  413. /package/.agents/{skills → skills-archived}/pr-converge/scripts/AGENTS.md +0 -0
  414. /package/.agents/{skills → skills-archived}/pr-converge/scripts/README.md +0 -0
  415. /package/.agents/{skills → skills-archived}/pr-converge/scripts/_pr_converge_path_setup.py +0 -0
  416. /package/.agents/{skills → skills-archived}/pr-converge/scripts/caller-window-pid.ps1 +0 -0
  417. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_bugbot_ci.py +0 -0
  418. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence.py +0 -0
  419. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_availability.py +0 -0
  420. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_gates.py +0 -0
  421. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_thread_gates.py +0 -0
  422. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_pending_reviews.py +0 -0
  423. /package/.agents/{skills → skills-archived}/pr-converge/scripts/conftest.py +0 -0
  424. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue-caller.cmd +0 -0
  425. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue-stop-others.ps1 +0 -0
  426. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue.ahk +0 -0
  427. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue.cmd +0 -0
  428. /package/.agents/{skills → skills-archived}/pr-converge/scripts/fetch_copilot_reviews.py +0 -0
  429. /package/.agents/{skills → skills-archived}/pr-converge/scripts/post_fix_reply.py +0 -0
  430. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/.claude/CLAUDE.md +0 -0
  431. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +0 -0
  432. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/__init__.py +0 -0
  433. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +0 -0
  434. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/pr_converge_constants.py +0 -0
  435. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/reflow_skill_md_constants.py +0 -0
  436. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test__pr_converge_path_setup.py +0 -0
  437. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_bugbot_ci.py +0 -0
  438. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence.py +0 -0
  439. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_availability.py +0 -0
  440. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_codex.py +0 -0
  441. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_contract.py +0 -0
  442. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_fixture.py +0 -0
  443. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_gates.py +0 -0
  444. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_thread_gates.py +0 -0
  445. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_cursor_agents_continue.py +0 -0
  446. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_reflow_skill_md.py +0 -0
  447. /package/.agents/{skills → skills-archived}/pr-converge/test_step5_host_branch.py +0 -0
  448. /package/.agents/{skills → skills-archived}/pr-converge/workflows/.claude/CLAUDE.md +0 -0
  449. /package/.agents/{skills → skills-archived}/pr-converge/workflows/AGENTS.md +0 -0
  450. /package/.agents/{skills → skills-archived}/pr-fix-protocol/SKILL.md +0 -0
  451. /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/SKILL.md +0 -0
  452. /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/reference/identity-and-hooks.md +0 -0
  453. /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/reference/substitution-matrix.md +0 -0
  454. /package/.agents/{skills → skills-archived}/pr-loop-lifecycle/SKILL.md +0 -0
  455. /package/.agents/{skills → skills-archived}/pr-loop-lifecycle/reference/teardown-publish-permissions.md +0 -0
  456. /package/.agents/{skills → skills-archived}/recall/.claude/CLAUDE.md +0 -0
  457. /package/.agents/{skills → skills-archived}/recall/AGENTS.md +0 -0
  458. /package/.agents/{skills → skills-archived}/recall/SKILL.md +0 -0
  459. /package/.agents/{skills → skills-archived}/release-notes-html/SKILL.md +0 -0
  460. /package/.agents/{skills → skills-archived}/remember/.claude/CLAUDE.md +0 -0
  461. /package/.agents/{skills → skills-archived}/remember/AGENTS.md +0 -0
  462. /package/.agents/{skills → skills-archived}/remember/SKILL.md +0 -0
  463. /package/.agents/{skills → skills-archived}/reviewer-gates/SKILL.md +0 -0
  464. /package/.agents/{skills → skills-archived}/reviews/SKILL.md +0 -0
  465. /package/.agents/{skills → skills-archived}/show/SKILL.md +0 -0
  466. /package/.agents/{skills → skills-archived}/show/references/accessibility.md +0 -0
  467. /package/.agents/{skills → skills-archived}/show/references/art.md +0 -0
  468. /package/.agents/{skills → skills-archived}/show/references/charts.md +0 -0
  469. /package/.agents/{skills → skills-archived}/show/references/core-design.md +0 -0
  470. /package/.agents/{skills → skills-archived}/show/references/erds.md +0 -0
  471. /package/.agents/{skills → skills-archived}/show/references/flowcharts.md +0 -0
  472. /package/.agents/{skills → skills-archived}/show/references/host-and-html.md +0 -0
  473. /package/.agents/{skills → skills-archived}/show/references/illustrative-diagrams.md +0 -0
  474. /package/.agents/{skills → skills-archived}/show/references/interaction.md +0 -0
  475. /package/.agents/{skills → skills-archived}/show/references/mockups.md +0 -0
  476. /package/.agents/{skills → skills-archived}/show/references/quality-gates.md +0 -0
  477. /package/.agents/{skills → skills-archived}/show/references/structural-diagrams.md +0 -0
  478. /package/.agents/{skills → skills-archived}/show/references/subject-inventory.md +0 -0
  479. /package/.agents/{skills → skills-archived}/show/references/svg-contract.md +0 -0
  480. /package/.agents/{skills → skills-archived}/show/routing.yaml +0 -0
  481. /package/.agents/{skills → skills-archived}/show/samples/pr1262-v2.svg +0 -0
  482. /package/.agents/{skills → skills-archived}/show/scripts/README.md +0 -0
  483. /package/.agents/{skills → skills-archived}/show/scripts/validate-artifact.py +0 -0
  484. /package/.agents/{skills → skills-archived}/show/scripts/validate-package.py +0 -0
  485. /package/.agents/{skills → skills-archived}/show/templates/html-widget.html +0 -0
  486. /package/.agents/{skills → skills-archived}/show/templates/svg-base.svg +0 -0
  487. /package/.agents/{skills → skills-archived}/show/tests/fixtures/css-var.svg +0 -0
  488. /package/.agents/{skills → skills-archived}/show/tests/fixtures/dead-ref.svg +0 -0
  489. /package/.agents/{skills → skills-archived}/show/tests/fixtures/filled-glyph.svg +0 -0
  490. /package/.agents/{skills → skills-archived}/show/tests/fixtures/inherited-fill.svg +0 -0
  491. /package/.agents/{skills → skills-archived}/show/tests/fixtures/invalid.svg +0 -0
  492. /package/.agents/{skills → skills-archived}/show/tests/fixtures/large-canvas.svg +0 -0
  493. /package/.agents/{skills → skills-archived}/show/tests/fixtures/unfilled-connector.svg +0 -0
  494. /package/.agents/{skills → skills-archived}/show/tests/fixtures/valid.html +0 -0
  495. /package/.agents/{skills → skills-archived}/show/tests/test_validate-artifact.py +0 -0
  496. /package/.agents/{skills → skills-archived}/show/tests/test_validators.py +0 -0
  497. /package/.agents/{skills → skills-archived}/show/workflows/create-visual.md +0 -0
  498. /package/.agents/{skills → skills-archived}/show/workflows/review-visual.md +0 -0
  499. /package/.agents/{skills → skills-archived}/split-pr/SKILL.md +0 -0
  500. /package/.agents/{skills → skills-archived}/split-pr/reference/path-layers.md +0 -0
  501. /package/.agents/{skills → skills-archived}/split-pr/reference/split-further-loop.md +0 -0
  502. /package/.agents/{skills → skills-archived}/split-pr/reference/splitting-principles.md +0 -0
  503. /package/.agents/{skills → skills-archived}/split-pr/scripts/analyze_pr.py +0 -0
  504. /package/.agents/{skills → skills-archived}/split-pr/scripts/categorize_files.py +0 -0
  505. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/__init__.py +0 -0
  506. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/dependency_constants.py +0 -0
  507. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/git_operations_constants.py +0 -0
  508. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/packing_constants.py +0 -0
  509. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/plan_constants.py +0 -0
  510. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/split_pr_constants.py +0 -0
  511. /package/.agents/{skills → skills-archived}/split-pr/scripts/execute_split_slices.py +0 -0
  512. /package/.agents/{skills → skills-archived}/split-pr/scripts/pack_files_into_slices.py +0 -0
  513. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_dependency_graph.py +0 -0
  514. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_git_operations.py +0 -0
  515. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_layer_order.py +0 -0
  516. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_paginate.py +0 -0
  517. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_process_runner.py +0 -0
  518. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_script_types.py +0 -0
  519. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_title.py +0 -0
  520. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_analyze_pr.py +0 -0
  521. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_categorize_files.py +0 -0
  522. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_categorize_files_packing.py +0 -0
  523. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_execute_split_slices.py +0 -0
  524. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_dependency_graph.py +0 -0
  525. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_git_operations.py +0 -0
  526. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_layer_order.py +0 -0
  527. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_paginate.py +0 -0
  528. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_script_types.py +0 -0
  529. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_title.py +0 -0
  530. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_dependency_graph.py +0 -0
  531. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan.py +0 -0
  532. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan_contract.py +0 -0
  533. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan_path_normalization.py +0 -0
  534. /package/.agents/{skills → skills-archived}/split-pr/scripts/verify_dependency_graph.py +0 -0
  535. /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