claude-dev-env 2.12.0 → 2.14.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 (399) hide show
  1. package/AGENTS.md +61 -0
  2. package/CLAUDE.md +1 -57
  3. package/_shared/AGENTS.md +15 -0
  4. package/_shared/CLAUDE.md +1 -15
  5. package/_shared/advisor/AGENTS.md +22 -0
  6. package/_shared/advisor/CLAUDE.md +1 -22
  7. package/_shared/pr-loop/AGENTS.md +27 -0
  8. package/_shared/pr-loop/CLAUDE.md +1 -27
  9. package/_shared/pr-loop/scripts/AGENTS.md +35 -0
  10. package/_shared/pr-loop/scripts/CLAUDE.md +1 -35
  11. package/_shared/pr-loop/scripts/code_rules_gate.py +2 -3
  12. package/_shared/pr-loop/scripts/code_rules_gate_parts/AGENTS.md +42 -0
  13. package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +1 -42
  14. package/_shared/pr-loop/scripts/pr_loop_shared_constants/AGENTS.md +26 -0
  15. package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -26
  16. package/_shared/pr-loop/scripts/test_code_rules_gate.py +0 -26
  17. package/_shared/pr-loop/scripts/tests/AGENTS.md +44 -0
  18. package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -44
  19. package/_shared/process-tree/AGENTS.md +41 -0
  20. package/_shared/process-tree/CLAUDE.md +1 -41
  21. package/agents/AGENTS.md +31 -0
  22. package/agents/CLAUDE.md +1 -32
  23. package/agents/clean-coder.md +6 -6
  24. package/agents/pr-description-writer.md +11 -81
  25. package/agents/test_agent_frontmatter.py +4 -4
  26. package/audit-rubrics/AGENTS.md +43 -0
  27. package/audit-rubrics/CLAUDE.md +1 -43
  28. package/audit-rubrics/category_rubrics/AGENTS.md +37 -0
  29. package/audit-rubrics/category_rubrics/CLAUDE.md +1 -37
  30. package/audit-rubrics/prompts/AGENTS.md +37 -0
  31. package/audit-rubrics/prompts/CLAUDE.md +1 -37
  32. package/bin/AGENTS.md +112 -0
  33. package/bin/CLAUDE.md +1 -112
  34. package/bin/ever-shipped-skills.mjs +1 -0
  35. package/bin/install-constants.mjs +6 -7
  36. package/bin/install.mjs +71 -13
  37. package/bin/install.prune.test.mjs +26 -0
  38. package/bin/install.settings-defaults.test.mjs +60 -33
  39. package/bin/install.test.mjs +36 -4
  40. package/commands/AGENTS.md +23 -0
  41. package/commands/CLAUDE.md +1 -23
  42. package/commands/hook-log-extract.md +3 -3
  43. package/commands/sr-loop.md +9 -5
  44. package/docs/AGENTS.md +32 -0
  45. package/docs/CLAUDE.md +1 -33
  46. package/docs/CODE_RULES.md +1 -1
  47. package/docs/imagegen.md +22 -0
  48. package/docs/references/AGENTS.md +17 -0
  49. package/docs/references/CLAUDE.md +1 -18
  50. package/hooks/AGENTS.md +31 -0
  51. package/hooks/CLAUDE.md +1 -31
  52. package/hooks/_gh_pr_author_swap_utils.py +14 -0
  53. package/hooks/advisory/AGENTS.md +16 -0
  54. package/hooks/advisory/CLAUDE.md +1 -16
  55. package/hooks/blocking/AGENTS.md +123 -0
  56. package/hooks/blocking/CLAUDE.md +1 -148
  57. package/hooks/blocking/claude_md_orphan_file_blocker_parts/AGENTS.md +28 -0
  58. package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +1 -28
  59. package/hooks/blocking/code_rules_banned_identifiers.py +1 -1
  60. package/hooks/blocking/code_rules_dead_module_constant.py +149 -37
  61. package/hooks/blocking/code_rules_probe_chains.py +6 -2
  62. package/hooks/blocking/config/AGENTS.md +10 -0
  63. package/hooks/blocking/config/CLAUDE.md +1 -22
  64. package/hooks/blocking/conventional_pr_title_gate.py +1 -2
  65. package/hooks/blocking/gh_pr_author_restore.py +7 -2
  66. package/hooks/blocking/intent_only_ending_blocker.py +11 -16
  67. package/hooks/blocking/inventory_intent_records/AGENTS.md +26 -0
  68. package/hooks/blocking/inventory_intent_records/CLAUDE.md +1 -26
  69. package/hooks/blocking/package_inventory_stale_blocker_parts/AGENTS.md +26 -0
  70. package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +1 -26
  71. package/hooks/blocking/pii_prevention_blocker_parts/AGENTS.md +24 -0
  72. package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +1 -24
  73. package/hooks/blocking/send_user_file_open_locally_blocker.py +1 -1
  74. package/hooks/blocking/stop_dispatcher.py +4 -4
  75. package/hooks/blocking/tdd_enforcer_parts/AGENTS.md +30 -0
  76. package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +1 -30
  77. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -3
  78. package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +1 -1
  79. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +171 -0
  80. package/hooks/blocking/test_code_rules_enforcer_magic_allowlist.py +1 -1
  81. package/hooks/blocking/test_gh_pr_author_restore.py +2 -1
  82. package/hooks/blocking/test_intent_only_ending_blocker.py +4 -0
  83. package/hooks/blocking/test_pre_tool_use_dispatcher.py +6 -7
  84. package/hooks/blocking/test_send_user_file_open_locally_blocker.py +4 -3
  85. package/hooks/blocking/test_shared_stdin_adoption.py +0 -2
  86. package/hooks/diagnostic/AGENTS.md +43 -0
  87. package/hooks/diagnostic/CLAUDE.md +1 -43
  88. package/hooks/diagnostic/hook_log_extractor.py +4 -38
  89. package/hooks/diagnostic/hook_log_stop_wrapper.py +6 -155
  90. package/hooks/diagnostic/migrations/AGENTS.md +16 -0
  91. package/hooks/diagnostic/migrations/CLAUDE.md +1 -16
  92. package/hooks/diagnostic/queries/AGENTS.md +19 -0
  93. package/hooks/diagnostic/queries/CLAUDE.md +1 -19
  94. package/hooks/diagnostic/test_hook_log_extractor.py +10 -23
  95. package/hooks/diagnostic/test_hook_log_stop_wrapper.py +3 -331
  96. package/hooks/git-hooks/AGENTS.md +31 -0
  97. package/hooks/git-hooks/CLAUDE.md +1 -31
  98. package/hooks/git-hooks/git_hooks_constants/AGENTS.md +21 -0
  99. package/hooks/git-hooks/git_hooks_constants/CLAUDE.md +1 -21
  100. package/hooks/git-hooks/git_hooks_constants/__init__.py +4 -12
  101. package/hooks/git-hooks/pre_push.py +49 -118
  102. package/hooks/git-hooks/test_pre_push.py +111 -137
  103. package/hooks/hooks.json +0 -35
  104. package/hooks/hooks_constants/AGENTS.md +104 -0
  105. package/hooks/hooks_constants/CLAUDE.md +1 -113
  106. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -11
  107. package/hooks/hooks_constants/convergence_branch_constants.py +0 -1
  108. package/hooks/hooks_constants/fable_spawn_gate_constants.py +3 -4
  109. package/hooks/hooks_constants/hook_log_extractor_constants.py +1 -2
  110. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -8
  111. package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +1 -6
  112. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -1
  113. package/hooks/hooks_constants/task_list_loop_starter_constants.py +4 -3
  114. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +1 -26
  115. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +0 -5
  116. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -1
  117. package/hooks/hooks_constants/working_style_prompt_constants.py +29 -20
  118. package/hooks/lifecycle/AGENTS.md +18 -0
  119. package/hooks/lifecycle/CLAUDE.md +1 -20
  120. package/hooks/observability/AGENTS.md +20 -0
  121. package/hooks/observability/CLAUDE.md +1 -20
  122. package/hooks/session/AGENTS.md +33 -0
  123. package/hooks/session/CLAUDE.md +1 -33
  124. package/hooks/session/gh_pr_author_session_cleanup.py +7 -2
  125. package/hooks/session/task_list_loop_starter.py +3 -3
  126. package/hooks/session/test_gh_pr_author_session_cleanup.py +2 -1
  127. package/hooks/session/test_task_list_loop_starter.py +10 -2
  128. package/hooks/session/test_working_style_prompt.py +9 -4
  129. package/hooks/validation/AGENTS.md +20 -0
  130. package/hooks/validation/CLAUDE.md +1 -20
  131. package/hooks/validators/AGENTS.md +51 -0
  132. package/hooks/validators/CLAUDE.md +1 -51
  133. package/hooks/validators/health_check.py +52 -19
  134. package/hooks/validators/python_style_checks.py +1 -1
  135. package/hooks/validators/test_health_check.py +112 -27
  136. package/hooks/workflow/AGENTS.md +16 -0
  137. package/hooks/workflow/CLAUDE.md +1 -16
  138. package/installable-surfaces.manifest.json +1 -0
  139. package/output-styles/AGENTS.md +15 -0
  140. package/output-styles/CLAUDE.md +1 -15
  141. package/package.json +2 -1
  142. package/rules/AGENTS.md +61 -0
  143. package/rules/CLAUDE.md +1 -63
  144. package/rules/code-standards.md +5 -9
  145. package/rules/doc-prose-cuts.md +1 -1
  146. package/rules/gh-cli-conventions.md +0 -1
  147. package/rules/git-workflow.md +1 -3
  148. package/scripts/AGENTS.md +62 -0
  149. package/scripts/CLAUDE.md +1 -63
  150. package/scripts/_code_review_test_support.py +0 -95
  151. package/scripts/codex_compat_materializer.py +4 -0
  152. package/scripts/dev_env_scripts_constants/AGENTS.md +21 -0
  153. package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -21
  154. package/scripts/dev_env_scripts_constants/code_review_constants.py +9 -98
  155. package/scripts/dev_env_scripts_constants/test_code_review_constants.py +1 -44
  156. package/scripts/invoke_code_review.py +8 -451
  157. package/scripts/sync_to_cursor/AGENTS.md +23 -0
  158. package/scripts/sync_to_cursor/CLAUDE.md +1 -23
  159. package/scripts/test_invoke_code_review.py +5 -61
  160. package/scripts/test_invoke_code_review_cli.py +1 -45
  161. package/scripts/test_invoke_code_review_contract.py +1 -118
  162. package/scripts/test_validate_instruction_pairs.py +90 -0
  163. package/scripts/test_verify_installable_package.py +5 -1
  164. package/scripts/tests/AGENTS.md +33 -0
  165. package/scripts/tests/CLAUDE.md +1 -33
  166. package/scripts/tests/test_codex_compat_materializer.py +36 -1
  167. package/scripts/validate_instruction_pairs.py +183 -0
  168. package/settings.json +1 -10
  169. package/skills/AGENTS.md +72 -0
  170. package/skills/CLAUDE.md +1 -64
  171. package/skills/_shared/AGENTS.md +44 -0
  172. package/skills/_shared/CLAUDE.md +1 -44
  173. package/skills/_shared/advisor/AGENTS.md +9 -0
  174. package/skills/_shared/advisor/CLAUDE.md +1 -9
  175. package/skills/_shared/pr-loop/AGENTS.md +50 -0
  176. package/skills/_shared/pr-loop/CLAUDE.md +1 -50
  177. package/skills/_shared/pr-loop/prompts/AGENTS.md +9 -0
  178. package/skills/_shared/pr-loop/prompts/CLAUDE.md +1 -9
  179. package/skills/_shared/pr-loop/scripts/AGENTS.md +35 -0
  180. package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -35
  181. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +24 -0
  182. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -24
  183. package/skills/anthropic-plan/AGENTS.md +34 -0
  184. package/skills/anthropic-plan/CLAUDE.md +1 -34
  185. package/skills/anthropic-plan/scripts/AGENTS.md +11 -0
  186. package/skills/anthropic-plan/scripts/CLAUDE.md +1 -11
  187. package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +16 -0
  188. package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/CLAUDE.md +1 -16
  189. package/skills/anthropic-plan/templates/AGENTS.md +13 -0
  190. package/skills/anthropic-plan/templates/CLAUDE.md +1 -13
  191. package/skills/anthropic-plan/workflow/AGENTS.md +14 -0
  192. package/skills/anthropic-plan/workflow/CLAUDE.md +1 -14
  193. package/skills/auditing-claude-config/AGENTS.md +21 -0
  194. package/skills/auditing-claude-config/CLAUDE.md +1 -21
  195. package/skills/autoconverge/AGENTS.md +36 -0
  196. package/skills/autoconverge/CLAUDE.md +1 -36
  197. package/skills/autoconverge/reference/AGENTS.md +16 -0
  198. package/skills/autoconverge/reference/CLAUDE.md +1 -16
  199. package/skills/autoconverge/reference/convergence.md +7 -7
  200. package/skills/autoconverge/reference/gotchas.md +3 -10
  201. package/skills/autoconverge/workflow/AGENTS.md +26 -0
  202. package/skills/autoconverge/workflow/CLAUDE.md +1 -26
  203. package/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +16 -0
  204. package/skills/autoconverge/workflow/autoconverge_report_constants/CLAUDE.md +1 -16
  205. package/skills/autoconverge/workflow/converge.contract.test.mjs +53 -1388
  206. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +4 -3
  207. package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +33 -493
  208. package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +15 -97
  209. package/skills/autoconverge/workflow/converge.mjs +201 -238
  210. package/skills/autoconverge/workflow/converge.precatch.test.mjs +1 -1
  211. package/skills/bugteam/AGENTS.md +30 -0
  212. package/skills/bugteam/CLAUDE.md +1 -30
  213. package/skills/bugteam/CONSTRAINTS.md +1 -1
  214. package/skills/bugteam/SKILL.md +9 -0
  215. package/skills/bugteam/reference/AGENTS.md +20 -0
  216. package/skills/bugteam/reference/CLAUDE.md +1 -20
  217. package/skills/bugteam/reference/audit-and-teammates.md +10 -6
  218. package/skills/bugteam/reference/obstacles/AGENTS.md +24 -0
  219. package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -24
  220. package/skills/bugteam/scripts/AGENTS.md +30 -0
  221. package/skills/bugteam/scripts/CLAUDE.md +1 -30
  222. package/skills/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +18 -0
  223. package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +1 -18
  224. package/skills/bugteam/test_skill_additions.py +9 -0
  225. package/skills/closeout/reference/handoff-prompt-template.md +1 -1
  226. package/skills/codex-review/AGENTS.md +46 -0
  227. package/skills/codex-review/CLAUDE.md +1 -46
  228. package/skills/codex-review/reference/AGENTS.md +15 -0
  229. package/skills/codex-review/reference/CLAUDE.md +1 -15
  230. package/skills/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +18 -0
  231. package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +1 -18
  232. package/skills/codex-review/test_skill_scaffold.py +3 -3
  233. package/skills/comments/SKILL.md +65 -0
  234. package/skills/copilot-review/AGENTS.md +18 -0
  235. package/skills/copilot-review/CLAUDE.md +1 -18
  236. package/skills/descriptions/SKILL.md +51 -0
  237. package/skills/emergencies/SKILL.md +42 -0
  238. package/skills/everything-search/AGENTS.md +17 -0
  239. package/skills/everything-search/CLAUDE.md +1 -17
  240. package/skills/fresh-branch/AGENTS.md +14 -0
  241. package/skills/fresh-branch/CLAUDE.md +1 -14
  242. package/skills/grok-spawn/AGENTS.md +28 -0
  243. package/skills/grok-spawn/CLAUDE.md +1 -28
  244. package/skills/orchestrator/SKILL.md +2 -4
  245. package/skills/plan-to-pr/SKILL.md +6 -7
  246. package/skills/plan-to-pr/reference/final-validation-tasks.md +1 -1
  247. package/skills/plan-to-pr/reference/process-inventory.md +2 -2
  248. package/skills/plan-to-pr/reference/review-loop.md +2 -2
  249. package/skills/plan-to-pr/reference/run-record.schema.json +1 -1
  250. package/skills/plan-to-pr/reference/task-seeds.md +1 -1
  251. package/skills/plan-to-pr/reference/task-ticket.md +3 -4
  252. package/skills/plan-to-pr/scripts/config/constants.py +1 -2
  253. package/skills/plan-to-pr/scripts/test_validate_protocol.py +1 -1
  254. package/skills/plan-to-pr/test_skill_contract.py +2 -2
  255. package/skills/pr-converge/AGENTS.md +32 -0
  256. package/skills/pr-converge/CLAUDE.md +1 -32
  257. package/skills/pr-converge/SKILL.md +13 -16
  258. package/skills/pr-converge/pr_converge_skill_constants/AGENTS.md +26 -0
  259. package/skills/pr-converge/pr_converge_skill_constants/CLAUDE.md +1 -26
  260. package/skills/pr-converge/reference/AGENTS.md +28 -0
  261. package/skills/pr-converge/reference/CLAUDE.md +1 -28
  262. package/skills/pr-converge/reference/obstacles/AGENTS.md +23 -0
  263. package/skills/pr-converge/reference/obstacles/CLAUDE.md +1 -23
  264. package/skills/pr-converge/reference/progress-checklist.md +6 -5
  265. package/skills/pr-converge/reference/state-schema.md +1 -27
  266. package/skills/pr-converge/scripts/AGENTS.md +46 -0
  267. package/skills/pr-converge/scripts/CLAUDE.md +1 -46
  268. package/skills/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +18 -0
  269. package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -18
  270. package/skills/pr-converge/workflows/AGENTS.md +16 -0
  271. package/skills/pr-converge/workflows/CLAUDE.md +1 -16
  272. package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
  273. package/skills/pr-loop-cloud-transport/reference/identity-and-hooks.md +3 -3
  274. package/skills/pr-loop-lifecycle/SKILL.md +1 -1
  275. package/skills/pr-loop-lifecycle/reference/teardown-publish-permissions.md +1 -1
  276. package/skills/prototype/SKILL.md +4 -4
  277. package/skills/prototype/reference/honest-limitations.md +5 -5
  278. package/skills/prototype/reference/promotion-tasks.md +2 -2
  279. package/skills/prototype/workflows/promotion.md +4 -6
  280. package/skills/rebase/AGENTS.md +32 -0
  281. package/skills/rebase/CLAUDE.md +1 -32
  282. package/skills/recall/AGENTS.md +30 -0
  283. package/skills/recall/CLAUDE.md +1 -30
  284. package/skills/remember/AGENTS.md +31 -0
  285. package/skills/remember/CLAUDE.md +1 -31
  286. package/skills/reviews/SKILL.md +85 -0
  287. package/skills/session-log/AGENTS.md +32 -0
  288. package/skills/session-log/CLAUDE.md +1 -32
  289. package/skills/session-tidy/AGENTS.md +36 -0
  290. package/skills/session-tidy/CLAUDE.md +1 -36
  291. package/skills/skill-builder/AGENTS.md +49 -0
  292. package/skills/skill-builder/CLAUDE.md +1 -49
  293. package/skills/skill-builder/references/AGENTS.md +25 -0
  294. package/skills/skill-builder/references/CLAUDE.md +1 -25
  295. package/skills/skill-builder/references/deterministic-elements.md +1 -1
  296. package/skills/skill-builder/templates/AGENTS.md +14 -0
  297. package/skills/skill-builder/templates/CLAUDE.md +1 -14
  298. package/skills/skill-builder/workflows/AGENTS.md +19 -0
  299. package/skills/skill-builder/workflows/CLAUDE.md +1 -19
  300. package/skills/small-cl/SKILL.md +52 -0
  301. package/skills/task-build/AGENTS.md +29 -0
  302. package/skills/task-build/CLAUDE.md +1 -29
  303. package/skills/update/AGENTS.md +38 -0
  304. package/skills/update/CLAUDE.md +1 -38
  305. package/system-prompts/AGENTS.md +17 -0
  306. package/system-prompts/CLAUDE.md +1 -17
  307. package/system-prompts/software-engineer.xml +3 -3
  308. package/agents/code-verifier.md +0 -74
  309. package/docs/PR_DESCRIPTION_GUIDE.md +0 -157
  310. package/docs/references/code-review-enforcement.md +0 -132
  311. package/hooks/blocking/code_review_enforcement_config_bootstrap.py +0 -53
  312. package/hooks/blocking/code_review_gate_deny.py +0 -74
  313. package/hooks/blocking/code_review_pr_create_gate.py +0 -198
  314. package/hooks/blocking/code_review_push_gate.py +0 -145
  315. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +0 -348
  316. package/hooks/blocking/code_review_stamp_store.py +0 -233
  317. package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +0 -7
  318. package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +0 -15
  319. package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +0 -212
  320. package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +0 -138
  321. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +0 -49
  322. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +0 -38
  323. package/hooks/blocking/code_verifier_spawn_preflight_gate.py +0 -483
  324. package/hooks/blocking/config/code_review_enforcement_constants.py +0 -143
  325. package/hooks/blocking/config/test_code_review_enforcement_constants.py +0 -166
  326. package/hooks/blocking/config/verified_commit_constants.py +0 -160
  327. package/hooks/blocking/config/verified_commit_context_constants.py +0 -21
  328. package/hooks/blocking/config/verified_commit_gate_output_constants.py +0 -14
  329. package/hooks/blocking/conftest.py +0 -123
  330. package/hooks/blocking/convergence_gate_blocker.py +0 -310
  331. package/hooks/blocking/pr_converge_bugteam_enforcer.py +0 -170
  332. package/hooks/blocking/pr_description_body_audit.py +0 -148
  333. package/hooks/blocking/pr_description_command_parser.py +0 -237
  334. package/hooks/blocking/pr_description_enforcer.py +0 -227
  335. package/hooks/blocking/pr_description_pr_number.py +0 -155
  336. package/hooks/blocking/pr_description_proof_of_work.py +0 -385
  337. package/hooks/blocking/pr_description_readability.py +0 -364
  338. package/hooks/blocking/reviewer_spawn_gate.py +0 -182
  339. package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +0 -62
  340. package/hooks/blocking/test_code_review_gate_deny.py +0 -54
  341. package/hooks/blocking/test_code_review_pr_create_gate.py +0 -199
  342. package/hooks/blocking/test_code_review_push_gate.py +0 -205
  343. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +0 -199
  344. package/hooks/blocking/test_code_review_stamp_store.py +0 -205
  345. package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +0 -710
  346. package/hooks/blocking/test_code_verifier_tools_contract.py +0 -28
  347. package/hooks/blocking/test_convergence_gate_blocker.py +0 -428
  348. package/hooks/blocking/test_pr_converge_bugteam_enforcer.py +0 -311
  349. package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +0 -184
  350. package/hooks/blocking/test_pr_description_enforcer.py +0 -221
  351. package/hooks/blocking/test_pr_description_enforcer_body_audit.py +0 -247
  352. package/hooks/blocking/test_pr_description_enforcer_body_rules.py +0 -493
  353. package/hooks/blocking/test_pr_description_enforcer_command_parser.py +0 -366
  354. package/hooks/blocking/test_pr_description_enforcer_pr_number.py +0 -159
  355. package/hooks/blocking/test_pr_description_enforcer_proof_gate.py +0 -175
  356. package/hooks/blocking/test_pr_description_enforcer_readability.py +0 -443
  357. package/hooks/blocking/test_pr_description_proof_of_work.py +0 -313
  358. package/hooks/blocking/test_reviewer_spawn_gate.py +0 -230
  359. package/hooks/blocking/test_verdict_directory_write_blocker.py +0 -804
  360. package/hooks/blocking/test_verification_verdict_store.py +0 -974
  361. package/hooks/blocking/test_verified_commit_config_bootstrap.py +0 -67
  362. package/hooks/blocking/test_verified_commit_docs_delta.py +0 -176
  363. package/hooks/blocking/test_verified_commit_gate.py +0 -581
  364. package/hooks/blocking/test_verified_commit_gate_additional_context.py +0 -134
  365. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +0 -131
  366. package/hooks/blocking/test_verifier_verdict_minter.py +0 -299
  367. package/hooks/blocking/tests/test_verified_commit_gate.py +0 -41
  368. package/hooks/blocking/verdict_directory_write_blocker.py +0 -687
  369. package/hooks/blocking/verification_verdict_store.py +0 -1039
  370. package/hooks/blocking/verified_commit_config_bootstrap.py +0 -63
  371. package/hooks/blocking/verified_commit_gate.py +0 -113
  372. package/hooks/blocking/verified_commit_gate_parts/CLAUDE.md +0 -28
  373. package/hooks/blocking/verified_commit_gate_parts/__init__.py +0 -1
  374. package/hooks/blocking/verified_commit_gate_parts/command_tokenization.py +0 -174
  375. package/hooks/blocking/verified_commit_gate_parts/deny_payload.py +0 -53
  376. package/hooks/blocking/verified_commit_gate_parts/deny_reason.py +0 -80
  377. package/hooks/blocking/verified_commit_gate_parts/directory_resolution.py +0 -170
  378. package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +0 -217
  379. package/hooks/blocking/verified_commit_gate_parts/tests/conftest.py +0 -10
  380. package/hooks/blocking/verified_commit_gate_parts/tests/test_command_tokenization.py +0 -94
  381. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_payload.py +0 -17
  382. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_reason.py +0 -38
  383. package/hooks/blocking/verified_commit_gate_parts/tests/test_directory_resolution.py +0 -71
  384. package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +0 -96
  385. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +0 -167
  386. package/hooks/blocking/verifier_verdict_minter.py +0 -280
  387. package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +0 -64
  388. package/hooks/hooks_constants/convergence_gate_blocker_constants.py +0 -53
  389. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_constants.py +0 -55
  390. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_state.py +0 -67
  391. package/hooks/hooks_constants/pr_description_enforcer_constants.py +0 -167
  392. package/hooks/hooks_constants/pr_description_proof_of_work_constants.py +0 -107
  393. package/hooks/hooks_constants/reviewer_spawn_gate_constants.py +0 -41
  394. package/hooks/hooks_constants/test_pr_description_enforcer_constants.py +0 -292
  395. package/hooks/lifecycle/pr_converge_bugteam_skill_tracker.py +0 -198
  396. package/hooks/lifecycle/test_pr_converge_bugteam_skill_tracker.py +0 -283
  397. package/rules/proof-of-work-pr-comments.md +0 -22
  398. package/rules/verified-commit-gate-skip.md +0 -4
  399. package/scripts/Show-Asset.ps1 +0 -106
@@ -715,8 +715,8 @@ def test_dispatcher_write_applies_both_groups() -> None:
715
715
  assert "blocking/plain_language_blocker.py" in all_write_script_paths, (
716
716
  "plain_language_blocker (Group B) must be in Write applicable set"
717
717
  )
718
- assert len(all_write_entries) == 21, (
719
- f"Write tool must apply to all 21 hosted hooks, got {len(all_write_entries)}"
718
+ assert len(all_write_entries) == 19, (
719
+ f"Write tool must apply to all 19 hosted hooks, got {len(all_write_entries)}"
720
720
  )
721
721
 
722
722
 
@@ -732,16 +732,16 @@ def test_dispatcher_edit_applies_both_groups() -> None:
732
732
  assert "advisory/refactor_guard.py" in all_edit_script_paths, (
733
733
  "refactor_guard is Edit-scoped and hosted, so it belongs in the Edit applicable set"
734
734
  )
735
- assert len(all_edit_entries) == 24, (
736
- f"expected 24 Edit entries, got {len(all_edit_entries)}"
735
+ assert len(all_edit_entries) == 22, (
736
+ f"expected 22 Edit entries, got {len(all_edit_entries)}"
737
737
  )
738
738
 
739
739
 
740
740
  def test_dispatcher_multi_edit_applies_only_group_b() -> None:
741
741
  """MultiEdit tool triggers only Group B (10 hooks), not Group A."""
742
742
  all_multi_edit_entries = _applicable_entries_for_tool(MULTI_EDIT_TOOL_NAME)
743
- assert len(all_multi_edit_entries) == 10, (
744
- f"MultiEdit tool must apply to exactly 10 Group-B hooks, got {len(all_multi_edit_entries)}"
743
+ assert len(all_multi_edit_entries) == 9, (
744
+ f"MultiEdit tool must apply to exactly 9 Group-B hooks, got {len(all_multi_edit_entries)}"
745
745
  )
746
746
 
747
747
 
@@ -969,7 +969,6 @@ def test_hosted_hook_set_covers_all_write_edit_blocking_hooks() -> None:
969
969
  "blocking/state_description_blocker.py",
970
970
  "blocking/subprocess_budget_completeness.py",
971
971
  "blocking/hook_prose_detector_consistency.py",
972
- "blocking/verified_commit_message_accuracy_blocker.py",
973
972
  "blocking/workflow_substitution_slot_blocker.py",
974
973
  "blocking/claude_md_orphan_file_blocker.py",
975
974
  "blocking/pytest_testpaths_orphan_blocker.py",
@@ -45,8 +45,9 @@ def test_allows_proactive_status() -> None:
45
45
  assert _should_block(PROACTIVE_STATUS) is False
46
46
 
47
47
 
48
- def test_corrective_message_points_to_show_asset() -> None:
49
- assert "Show-Asset.ps1" in CORRECTIVE_MESSAGE
48
+ def test_corrective_message_points_to_native_windows_app() -> None:
49
+ assert "native Windows app" in CORRECTIVE_MESSAGE
50
+ assert "Show-Asset.ps1" not in CORRECTIVE_MESSAGE
50
51
 
51
52
 
52
53
  def test_corrective_message_names_proactive_escape_hatch() -> None:
@@ -71,7 +72,7 @@ def test_main_blocks_normal_attach() -> None:
71
72
  output_text = _run_main_with_io(json.dumps(hook_input))
72
73
  output = json.loads(output_text)
73
74
  assert output["hookSpecificOutput"]["permissionDecision"] == "deny"
74
- assert "Show-Asset.ps1" in output["hookSpecificOutput"]["permissionDecisionReason"]
75
+ assert "native Windows app" in output["hookSpecificOutput"]["permissionDecisionReason"]
75
76
 
76
77
 
77
78
  def test_main_allows_proactive_attach() -> None:
@@ -30,8 +30,6 @@ _BLOCKING_DIRECTORY = Path(__file__).resolve().parent
30
30
  ALL_CONVERTED_HOOK_FILENAMES = (
31
31
  "open_questions_in_plans_blocker.py",
32
32
  "claude_md_orphan_file_blocker.py",
33
- "pr_converge_bugteam_enforcer.py",
34
- "verdict_directory_write_blocker.py",
35
33
  "package_inventory_stale_blocker.py",
36
34
  )
37
35
 
@@ -0,0 +1,43 @@
1
+ # hooks/diagnostic
2
+
3
+ Hooks and scripts that collect, store, and query hook-firing records. The pipeline reads session JSONL transcripts, extracts hook attachment records, and writes them as rows into a Neon (Postgres) `hook_events` table.
4
+
5
+ ## Subdirectories
6
+
7
+ | Directory | Role |
8
+ |---|---|
9
+ | `migrations/` | SQL migration files for the `hook_events` schema |
10
+ | `queries/` | Parameterized SQL queries for inspecting blocked commands |
11
+
12
+ ## Key files
13
+
14
+ | File | What it does |
15
+ |---|---|
16
+ | `hook_log_init.py` | One-time setup: creates the Neon schema (runs `schema.sql`), then verifies read-write parity with a sentinel round-trip |
17
+ | `hook_log_extractor.py` | Disabled CLI: exits 0 without reading transcripts or rewriting offset state; body remains for a re-enable path that ingests `hook_*` attachments into Neon |
18
+ | `hook_log_stop_wrapper.py` | Disabled Stop wrapper stub: exits 0; not on the Stop dispatcher roster |
19
+ | `schema.sql` | DDL for the `hook_events` table, `blocked_commands` view, and supporting indexes |
20
+ | `requirements-hook-logs.txt` | Runtime dependencies (`psycopg`) for the extractor |
21
+ | `requirements-hook-logs-dev.txt` | Dev/test dependencies |
22
+ | `test_hook_log_extractor.py` | Tests for the extractor |
23
+ | `test_hook_log_init.py` | Tests for the schema-init script |
24
+ | `test_hook_log_stop_wrapper.py` | Tests for the Stop wrapper |
25
+
26
+ ## Schema overview (`schema.sql`)
27
+
28
+ The `hook_events` table captures one row per hook firing:
29
+
30
+ - `hook_event`, `hook_name`, `hook_category` — what fired
31
+ - `outcome` — `allowed`, `blocked`, or `ask`
32
+ - `tool_name`, `command_excerpt` — what tool was called
33
+ - `session_id`, `git_branch`, `cwd` — context
34
+ - `duration_ms`, `exit_code` — timing and result
35
+ - `source_jsonl_path`, `source_line_number` — idempotency key
36
+
37
+ The `blocked_commands` view filters to `outcome = 'blocked'`.
38
+
39
+ ## Conventions
40
+
41
+ - Extractor and Stop wrapper mains are disabled and exit 0 with no work.
42
+ - Constants for the extractor (table name, offset state file, timeout) live in `hooks_constants/hook_log_extractor_constants.py`.
43
+ - Tests run with `python -m pytest diagnostic/test_hook_log_*.py`.
@@ -1,43 +1 @@
1
- # hooks/diagnostic
2
-
3
- Hooks and scripts that collect, store, and query hook-firing records. The pipeline reads session JSONL transcripts, extracts hook attachment records, and writes them as rows into a Neon (Postgres) `hook_events` table.
4
-
5
- ## Subdirectories
6
-
7
- | Directory | Role |
8
- |---|---|
9
- | `migrations/` | SQL migration files for the `hook_events` schema |
10
- | `queries/` | Parameterized SQL queries for inspecting blocked commands |
11
-
12
- ## Key files
13
-
14
- | File | What it does |
15
- |---|---|
16
- | `hook_log_init.py` | One-time setup: creates the Neon schema (runs `schema.sql`), then verifies read-write parity with a sentinel round-trip |
17
- | `hook_log_extractor.py` | Stop hook — reads per-session JSONL transcripts and ingests new `hook_*` attachment records into the `hook_events` table; idempotent via a UNIQUE constraint on `(source_jsonl_path, source_line_number)` |
18
- | `hook_log_stop_wrapper.py` | Thin wrapper that invokes `hook_log_extractor.py` from the Stop lifecycle event |
19
- | `schema.sql` | DDL for the `hook_events` table, `blocked_commands` view, and supporting indexes |
20
- | `requirements-hook-logs.txt` | Runtime dependencies (`psycopg`) for the extractor |
21
- | `requirements-hook-logs-dev.txt` | Dev/test dependencies |
22
- | `test_hook_log_extractor.py` | Tests for the extractor |
23
- | `test_hook_log_init.py` | Tests for the schema-init script |
24
- | `test_hook_log_stop_wrapper.py` | Tests for the Stop wrapper |
25
-
26
- ## Schema overview (`schema.sql`)
27
-
28
- The `hook_events` table captures one row per hook firing:
29
-
30
- - `hook_event`, `hook_name`, `hook_category` — what fired
31
- - `outcome` — `allowed`, `blocked`, or `ask`
32
- - `tool_name`, `command_excerpt` — what tool was called
33
- - `session_id`, `git_branch`, `cwd` — context
34
- - `duration_ms`, `exit_code` — timing and result
35
- - `source_jsonl_path`, `source_line_number` — idempotency key
36
-
37
- The `blocked_commands` view filters to `outcome = 'blocked'`.
38
-
39
- ## Conventions
40
-
41
- - The extractor exits 0 even when Neon is unreachable (offline-graceful); it logs to `OFFLINE_WARNING_LOG` and does not block session end.
42
- - Constants for the extractor (table name, offset state file, timeout) live in `hooks_constants/hook_log_extractor_constants.py`.
43
- - Tests run with `python -m pytest diagnostic/test_hook_log_*.py`.
1
+ @AGENTS.md
@@ -67,15 +67,10 @@ from hooks_constants.hook_log_extractor_constants import (
67
67
  CATEGORY_PATH_MINIMUM_PARTS,
68
68
  COMMAND_EXCERPT_MAX_CHARACTERS,
69
69
  CONNECT_TIMEOUT_SECONDS,
70
- DEFAULT_QUERY_FOR_SUMMARY,
71
70
  EMPTY_STRING,
72
71
  EXIT_CODE_EXTRACTOR_ENVIRONMENT_MISSING,
73
72
  EXIT_CODE_SUCCESS,
74
73
  EXIT_CODE_UNKNOWN_QUERY,
75
- FLAG_FULL_REBUILD,
76
- FLAG_INCREMENTAL,
77
- FLAG_QUERY,
78
- FLAG_SUMMARY,
79
74
  HOOK_CATEGORY_UNCATEGORIZED,
80
75
  HOOK_EVENTS_INSERT_SQL,
81
76
  HOOK_EVENTS_TRUNCATE_SQL,
@@ -94,10 +89,8 @@ from hooks_constants.hook_log_extractor_constants import (
94
89
  NEON_DATABASE_URL_ENVIRONMENT_VARIABLE,
95
90
  NEWLINE_JOINER,
96
91
  OFFLINE_WARNING_LOG,
97
- OFFSET_STATE_FILE,
98
92
  OFFSETS_JSON_INDENT,
99
93
  OUTCOME_BY_ATTACHMENT_TYPE,
100
- PROJECTS_TRANSCRIPT_ROOT,
101
94
  QUERIES_DIRECTORY_NAME,
102
95
  QUERY_NAME_PATTERN,
103
96
  QUERY_NO_ROWS_RETURNED_MESSAGE,
@@ -106,7 +99,7 @@ from hooks_constants.hook_log_extractor_constants import (
106
99
  STDERR_EXCERPT_MAX_CHARACTERS,
107
100
  STDOUT_EXCERPT_MAX_CHARACTERS,
108
101
  SUMMARY_COLUMN_HEADINGS,
109
- SUMMARY_NO_NEW_BLOCKS_MESSAGE,
102
+ SUMMARY_ZERO_BLOCKED_EVENTS_MESSAGE,
110
103
  SUMMARY_TABLE_COLUMN_GAP,
111
104
  TOP_BLOCKED_COMMAND_PREVIEW_MAX_CHARACTERS,
112
105
  TOP_BLOCKERS_LAST_24_HOURS_SQL,
@@ -787,7 +780,7 @@ def run_summary() -> int:
787
780
  except Exception:
788
781
  pass
789
782
  if not all_result_rows:
790
- print(SUMMARY_NO_NEW_BLOCKS_MESSAGE)
783
+ print(SUMMARY_ZERO_BLOCKED_EVENTS_MESSAGE)
791
784
  return EXIT_CODE_SUCCESS
792
785
  _print_summary_table(all_result_rows)
793
786
  return EXIT_CODE_SUCCESS
@@ -874,35 +867,8 @@ def run_query(named_query: str) -> int:
874
867
 
875
868
 
876
869
  def main() -> int:
877
- """Entry point for the hook-log extractor CLI.
878
-
879
- Supported flags:
880
-
881
- * ``--summary`` prints the top blockers of the last twenty-four hours.
882
- * ``--query <name>`` runs a pre-baked SQL file under ``queries/``.
883
- * ``--full-rebuild`` truncates ``hook_events`` and re-reads every
884
- JSONL from byte zero.
885
- * ``--incremental`` is a documented no-op; it selects the default
886
- byte-offset resumption path that the Stop hook also uses when no
887
- flags are passed.
888
- """
889
- all_cli_arguments = list(sys.argv[1:])
890
- if FLAG_SUMMARY in all_cli_arguments:
891
- return run_summary()
892
- if FLAG_QUERY in all_cli_arguments:
893
- flag_index = all_cli_arguments.index(FLAG_QUERY)
894
- if flag_index + 1 >= len(all_cli_arguments):
895
- return run_query(DEFAULT_QUERY_FOR_SUMMARY)
896
- return run_query(all_cli_arguments[flag_index + 1])
897
- is_full_rebuild_requested = FLAG_FULL_REBUILD in all_cli_arguments
898
- is_incremental_requested = FLAG_INCREMENTAL in all_cli_arguments
899
- if is_incremental_requested and is_full_rebuild_requested:
900
- is_full_rebuild_requested = False
901
- return run_full_extraction(
902
- transcripts_root=PROJECTS_TRANSCRIPT_ROOT,
903
- state_file_path=OFFSET_STATE_FILE,
904
- full_rebuild=is_full_rebuild_requested,
905
- )
870
+ """Disabled extractor entry point: exit success with no work."""
871
+ return EXIT_CODE_SUCCESS
906
872
 
907
873
 
908
874
  if __name__ == "__main__":
@@ -1,173 +1,24 @@
1
1
  #!/usr/bin/env python3
2
- """Stop-hook wrapper for hook_log_extractor: debounced, fire-and-forget.
2
+ """Disabled Stop-hook wrapper for the hook-log extractor.
3
3
 
4
- Runs after every assistant turn (Stop hook), so per-turn latency must
5
- stay near zero. The wrapper:
6
-
7
- 1. Reads the last-spawn timestamp; if it falls within the debounce
8
- window, exits 0 immediately without spawning anything (typical fast
9
- path: a small file read, well under 10ms).
10
- 2. Otherwise records the current timestamp, then launches the extractor
11
- as a fully detached background process (no stdio, separate process
12
- group on POSIX or CREATE_NO_WINDOW|CREATE_NEW_PROCESS_GROUP on
13
- Windows) and returns without waiting for it. CREATE_NO_WINDOW
14
- prevents a console flash on Windows even when the wrapper goes
15
- through ``bws run`` first; DETACHED_PROCESS would let the
16
- grandchild python.exe allocate a fresh console.
17
-
18
- Bitwarden injection: when both ``bws`` is on PATH and
19
- ``BWS_ACCESS_TOKEN`` is set, the extractor is launched via
20
- ``bws run --`` so the Neon URL never hits disk; otherwise it is
21
- launched directly. The extractor itself exits 0 when
22
- ``NEON_HOOK_LOGS_DATABASE_URL`` is unset, so missing dependencies
23
- cannot block session shutdown.
24
-
25
- This wrapper always exits 0 so the Stop hook never surfaces a failure.
4
+ The wrapper is off the Stop dispatcher roster and exits 0 without spawning
5
+ the extractor or writing debounce state. Git history holds the prior
6
+ debounce-and-spawn implementation if Neon-backed extraction returns.
26
7
  """
27
8
 
28
9
  from __future__ import annotations
29
10
 
30
- import os
31
- import shutil
32
- import subprocess
33
11
  import sys
34
- import time
35
12
  from pathlib import Path
36
13
 
37
14
  if str(Path(__file__).resolve().parent.parent) not in sys.path:
38
15
  sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
39
16
 
40
- from hooks_constants.hook_log_extractor_constants import (
41
- BWS_ACCESS_TOKEN_ENV_VAR,
42
- BWS_EXECUTABLE_NAME,
43
- BWS_RUN_SEPARATOR,
44
- BWS_RUN_SUBCOMMAND,
45
- EXIT_CODE_SUCCESS,
46
- FLAG_INCREMENTAL,
47
- STOP_WRAPPER_DEBOUNCE_SECONDS,
48
- STOP_WRAPPER_EXTRACTOR_SCRIPT_NAME,
49
- STOP_WRAPPER_LAST_RUN_TIMESTAMP_FILE,
50
- WINDOWS_CREATE_NEW_PROCESS_GROUP_FLAG,
51
- WINDOWS_CREATE_NO_WINDOW_FLAG,
52
- WINDOWS_OS_NAME,
53
- )
54
-
55
-
56
- def _extractor_script_path() -> str:
57
- return str(
58
- Path(__file__).resolve().parent / STOP_WRAPPER_EXTRACTOR_SCRIPT_NAME
59
- )
60
-
61
-
62
- def _last_run_timestamp_path() -> Path:
63
- return Path(STOP_WRAPPER_LAST_RUN_TIMESTAMP_FILE)
64
-
65
-
66
- def _is_within_debounce_window() -> bool:
67
- timestamp_path = _last_run_timestamp_path()
68
- if not timestamp_path.exists():
69
- return False
70
- try:
71
- previous_timestamp = float(timestamp_path.read_text().strip())
72
- except (OSError, ValueError):
73
- return False
74
- seconds_since_previous_spawn = time.time() - previous_timestamp
75
- if seconds_since_previous_spawn < 0:
76
- return False
77
- return seconds_since_previous_spawn < STOP_WRAPPER_DEBOUNCE_SECONDS
78
-
79
-
80
- def _record_current_timestamp() -> None:
81
- timestamp_path = _last_run_timestamp_path()
82
- timestamp_path.parent.mkdir(parents=True, exist_ok=True)
83
- timestamp_path.write_text(str(time.time()))
84
-
85
-
86
- def _clear_recorded_timestamp() -> None:
87
- """Remove the debounce timestamp regardless of which process wrote it.
88
-
89
- The unlink is unconditional: if process A writes a timestamp, process
90
- B observes it and debounces, then A's spawn fails, A's rollback here
91
- deletes the timestamp B already saw. The next Stop hook then spawns
92
- a fresh extractor instead of debouncing the remainder of the window.
93
- Consequence is benign because the extractor's own offset-file lock
94
- (LOCK_MAXIMUM_RETRY_COUNT in hook_log_extractor_constants) serializes
95
- concurrent extractor runs, so at most one extra extractor briefly
96
- waits on the lock. Scoping the rollback to A's own write would
97
- require a per-process sentinel and tighter atomicity than the
98
- benefit warrants for this Stop-hook fast path.
99
- """
100
- timestamp_path = _last_run_timestamp_path()
101
- timestamp_path.unlink(missing_ok=True)
102
-
103
-
104
- def _can_use_bws() -> bool:
105
- if not os.environ.get(BWS_ACCESS_TOKEN_ENV_VAR):
106
- return False
107
- return shutil.which(BWS_EXECUTABLE_NAME) is not None
108
-
109
-
110
- def _detached_spawn_keyword_arguments() -> dict[str, object]:
111
- spawn_arguments: dict[str, object] = {
112
- "stdin": subprocess.DEVNULL,
113
- "stdout": subprocess.DEVNULL,
114
- "stderr": subprocess.DEVNULL,
115
- "close_fds": True,
116
- }
117
- if os.name == WINDOWS_OS_NAME:
118
- spawn_arguments["creationflags"] = (
119
- WINDOWS_CREATE_NO_WINDOW_FLAG
120
- | WINDOWS_CREATE_NEW_PROCESS_GROUP_FLAG
121
- )
122
- startup_info = subprocess.STARTUPINFO()
123
- startup_info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
124
- startup_info.wShowWindow = subprocess.SW_HIDE
125
- spawn_arguments["startupinfo"] = startup_info
126
- else:
127
- spawn_arguments["start_new_session"] = True
128
- return spawn_arguments
129
-
130
-
131
- def _spawn_with_bws() -> None:
132
- subprocess.Popen(
133
- [
134
- BWS_EXECUTABLE_NAME,
135
- BWS_RUN_SUBCOMMAND,
136
- BWS_RUN_SEPARATOR,
137
- sys.executable,
138
- _extractor_script_path(),
139
- FLAG_INCREMENTAL,
140
- ],
141
- **_detached_spawn_keyword_arguments(),
142
- )
143
-
144
-
145
- def _spawn_without_bws() -> None:
146
- subprocess.Popen(
147
- [
148
- sys.executable,
149
- _extractor_script_path(),
150
- FLAG_INCREMENTAL,
151
- ],
152
- **_detached_spawn_keyword_arguments(),
153
- )
17
+ from hooks_constants.hook_log_extractor_constants import EXIT_CODE_SUCCESS
154
18
 
155
19
 
156
20
  def main() -> int:
157
- """Debounce, then fire-and-forget the extractor; always exit 0."""
158
- try:
159
- if _is_within_debounce_window():
160
- return EXIT_CODE_SUCCESS
161
- _record_current_timestamp()
162
- try:
163
- if _can_use_bws():
164
- _spawn_with_bws()
165
- else:
166
- _spawn_without_bws()
167
- except Exception:
168
- _clear_recorded_timestamp()
169
- except Exception:
170
- pass
21
+ """Exit success without spawning the extractor."""
171
22
  return EXIT_CODE_SUCCESS
172
23
 
173
24
 
@@ -0,0 +1,16 @@
1
+ # hooks/diagnostic/migrations
2
+
3
+ SQL migration files for the `hook_events` Neon schema. Each file applies a schema change to the `hook_events` table or its indexes.
4
+
5
+ ## Files
6
+
7
+ | File | What it does |
8
+ |---|---|
9
+ | `2026-04-25-drop-themes-hook-events.sql` | Drops the `themes` hook-events table variant |
10
+ | `README.md` | Notes on the migration approach |
11
+
12
+ ## Conventions
13
+
14
+ - Run migrations manually against the Neon database using `psql` or the Neon console.
15
+ - The baseline schema lives in `diagnostic/schema.sql`.
16
+ - File names follow `YYYY-MM-DD-<description>.sql` for chronological ordering.
@@ -1,16 +1 @@
1
- # hooks/diagnostic/migrations
2
-
3
- SQL migration files for the `hook_events` Neon schema. Each file applies a schema change to the `hook_events` table or its indexes.
4
-
5
- ## Files
6
-
7
- | File | What it does |
8
- |---|---|
9
- | `2026-04-25-drop-themes-hook-events.sql` | Drops the `themes` hook-events table variant |
10
- | `README.md` | Notes on the migration approach |
11
-
12
- ## Conventions
13
-
14
- - Run migrations manually against the Neon database using `psql` or the Neon console.
15
- - The baseline schema lives in `diagnostic/schema.sql`.
16
- - File names follow `YYYY-MM-DD-<description>.sql` for chronological ordering.
1
+ @AGENTS.md
@@ -0,0 +1,19 @@
1
+ # hooks/diagnostic/queries
2
+
3
+ Parameterized SQL queries for inspecting the `hook_events` Neon table. Run these directly against the Neon database to analyze hook-firing patterns.
4
+
5
+ ## Files
6
+
7
+ | File | What it returns |
8
+ |---|---|
9
+ | `block_details_for_hook.sql` | Full details for all blocked events matching a given hook name |
10
+ | `blocks_by_category.sql` | Count of blocks grouped by hook category |
11
+ | `blocks_by_tool.sql` | Count of blocks grouped by tool name |
12
+ | `blocks_last_7_days.sql` | All blocked events from the last 7 days |
13
+ | `top_blockers_last_24_hours.sql` | Hook names with the most blocks in the last 24 hours |
14
+ | `top_blockers_overall.sql` | Hook names with the most blocks across all time |
15
+
16
+ ## Conventions
17
+
18
+ - Queries target the `hook_events` table and `blocked_commands` view defined in `diagnostic/schema.sql`.
19
+ - Run with `psql $DATABASE_URL -f <query>.sql` or paste into the Neon console.
@@ -1,19 +1 @@
1
- # hooks/diagnostic/queries
2
-
3
- Parameterized SQL queries for inspecting the `hook_events` Neon table. Run these directly against the Neon database to analyze hook-firing patterns.
4
-
5
- ## Files
6
-
7
- | File | What it returns |
8
- |---|---|
9
- | `block_details_for_hook.sql` | Full details for all blocked events matching a given hook name |
10
- | `blocks_by_category.sql` | Count of blocks grouped by hook category |
11
- | `blocks_by_tool.sql` | Count of blocks grouped by tool name |
12
- | `blocks_last_7_days.sql` | All blocked events from the last 7 days |
13
- | `top_blockers_last_24_hours.sql` | Hook names with the most blocks in the last 24 hours |
14
- | `top_blockers_overall.sql` | Hook names with the most blocks across all time |
15
-
16
- ## Conventions
17
-
18
- - Queries target the `hook_events` table and `blocked_commands` view defined in `diagnostic/schema.sql`.
19
- - Run with `psql $DATABASE_URL -f <query>.sql` or paste into the Neon console.
1
+ @AGENTS.md
@@ -617,7 +617,7 @@ def test_tool_name_extracted_from_hook_name_prefix() -> None:
617
617
  assert hook_log_extractor.extract_tool_name("UserPromptSubmit") is None
618
618
 
619
619
 
620
- def test_run_summary_prints_no_new_blocks_when_cursor_empty(
620
+ def test_run_summary_prints_zero_blocked_events_when_cursor_empty(
621
621
  capsys: pytest.CaptureFixture[str],
622
622
  ) -> None:
623
623
  fake_cursor = MagicMock()
@@ -632,7 +632,7 @@ def test_run_summary_prints_no_new_blocks_when_cursor_empty(
632
632
 
633
633
  captured = capsys.readouterr()
634
634
  assert exit_code == 0
635
- assert "No new blocks since last run." in captured.out
635
+ assert "Blocked events recorded in the last 24 hours: 0." in captured.out
636
636
 
637
637
 
638
638
  def test_run_summary_prints_table_when_rows_returned(
@@ -805,32 +805,19 @@ def test_offline_fallback_still_exits_zero_when_warning_log_write_raises(
805
805
  assert exit_code == 0
806
806
 
807
807
 
808
- def test_main_accepts_incremental_flag_as_noop(
809
- tmp_path: Path,
808
+ def test_main_is_disabled_and_skips_extraction(
810
809
  monkeypatch: pytest.MonkeyPatch,
811
810
  ) -> None:
812
- """C8: ``--incremental`` must be recognized and route to default extraction."""
813
- captured_arguments: dict[str, object] = {}
814
-
815
- def _fake_run_full_extraction(
816
- transcripts_root: str,
817
- state_file_path: str,
818
- full_rebuild: bool,
819
- ) -> int:
820
- captured_arguments["transcripts_root"] = transcripts_root
821
- captured_arguments["state_file_path"] = state_file_path
822
- captured_arguments["full_rebuild"] = full_rebuild
823
- return 0
824
-
825
- monkeypatch.setattr(sys, "argv", ["hook_log_extractor.py", "--incremental"])
811
+ """Disabled main exits success without running extraction."""
812
+
813
+ def _fail_if_called(*_args: object, **_kwargs: object) -> int:
814
+ raise AssertionError("run_full_extraction must not run while disabled")
815
+
826
816
  monkeypatch.setattr(
827
- hook_log_extractor, "run_full_extraction", _fake_run_full_extraction
817
+ hook_log_extractor, "run_full_extraction", _fail_if_called
828
818
  )
829
819
 
830
- exit_code = hook_log_extractor.main()
831
-
832
- assert exit_code == 0
833
- assert captured_arguments["full_rebuild"] is False
820
+ assert hook_log_extractor.main() == 0
834
821
 
835
822
 
836
823
  def test_run_query_returns_nonzero_for_unknown_query(