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
@@ -1,28 +0,0 @@
1
- """Contract: code-verifier cannot message a warm session-advisor."""
2
-
3
- from pathlib import Path
4
-
5
- _PACKAGE_ROOT = Path(__file__).resolve().parents[2]
6
- _CODE_VERIFIER_AGENT_PATH = _PACKAGE_ROOT / "agents" / "code-verifier.md"
7
- _FRONTMATTER_OPEN = "---\n"
8
- _FRONTMATTER_CLOSE = "\n---\n"
9
- _TOOLS_PREFIX = "tools:"
10
- _SEND_MESSAGE_TOOL_NAME = "SendMessage"
11
-
12
-
13
- def test_code_verifier_tools_exclude_sendmessage() -> None:
14
- """tools frontmatter must not list SendMessage (warm-advisor path closed)."""
15
- agent_text = _CODE_VERIFIER_AGENT_PATH.read_text(encoding="utf-8")
16
- assert agent_text.startswith(_FRONTMATTER_OPEN), "missing frontmatter open"
17
- frontmatter_end = agent_text.find(_FRONTMATTER_CLOSE, len(_FRONTMATTER_OPEN))
18
- assert frontmatter_end > 0, "missing frontmatter close"
19
- frontmatter_body = agent_text[len(_FRONTMATTER_OPEN) : frontmatter_end]
20
- all_tools_lines = [
21
- each_line
22
- for each_line in frontmatter_body.splitlines()
23
- if each_line.startswith(_TOOLS_PREFIX)
24
- ]
25
- assert len(all_tools_lines) == 1, all_tools_lines
26
- tools_value = all_tools_lines[0].removeprefix(_TOOLS_PREFIX).strip()
27
- all_tool_names = {each_name.strip() for each_name in tools_value.split(",")}
28
- assert _SEND_MESSAGE_TOOL_NAME not in all_tool_names, all_tool_names
@@ -1,428 +0,0 @@
1
- """Unit tests for convergence-gate-blocker PreToolUse hook."""
2
-
3
- import importlib.util
4
- import io
5
- import json
6
- import pathlib
7
- import re
8
- import subprocess
9
- import sys
10
-
11
- import pytest
12
-
13
- _HOOK_DIR = pathlib.Path(__file__).parent
14
- if str(_HOOK_DIR) not in sys.path:
15
- sys.path.insert(0, str(_HOOK_DIR))
16
-
17
- _GH_PR_READY_PATTERN = re.compile(r"\bgh\s+pr\s+ready\b(?![^&|;\n]*--undo)")
18
-
19
- hook_spec = importlib.util.spec_from_file_location(
20
- "convergence_gate_blocker",
21
- _HOOK_DIR / "convergence_gate_blocker.py",
22
- )
23
- assert hook_spec is not None
24
- assert hook_spec.loader is not None
25
- hook_module = importlib.util.module_from_spec(hook_spec)
26
- hook_spec.loader.exec_module(hook_module)
27
- _resolve_pr_number = hook_module._resolve_pr_number
28
-
29
-
30
- def test_matches_gh_pr_ready_with_number() -> None:
31
- assert _GH_PR_READY_PATTERN.search("gh pr ready 418")
32
-
33
-
34
- def test_matches_gh_pr_ready_without_number() -> None:
35
- assert _GH_PR_READY_PATTERN.search("gh pr ready")
36
-
37
-
38
- def test_matches_gh_pr_ready_with_flags() -> None:
39
- assert not _GH_PR_READY_PATTERN.search("gh pr ready --undo")
40
-
41
-
42
- def test_does_not_match_gh_pr_create() -> None:
43
- assert not _GH_PR_READY_PATTERN.search("gh pr create --title T")
44
-
45
-
46
- def test_does_not_match_gh_pr_view() -> None:
47
- assert not _GH_PR_READY_PATTERN.search("gh pr view 418")
48
-
49
-
50
- def test_does_not_match_gh_issue_close() -> None:
51
- assert not _GH_PR_READY_PATTERN.search("gh issue close 42")
52
-
53
-
54
- def test_extracts_pr_number_from_command() -> None:
55
- assert _resolve_pr_number("gh pr ready 418", None, None, None) == 418
56
-
57
-
58
- def test_extracts_pr_number_when_repo_flag_precedes_number() -> None:
59
- assert (
60
- _resolve_pr_number(
61
- "gh pr ready --repo sample-owner/target-repo 161",
62
- None,
63
- "sample-owner",
64
- "target-repo",
65
- )
66
- == 161
67
- )
68
-
69
-
70
- def test_extracts_pr_number_with_flags() -> None:
71
- assert _resolve_pr_number("gh pr ready 99 --undo", None, None, None) == 99
72
-
73
-
74
- def test_returns_none_when_no_number_and_no_repo() -> None:
75
- assert _resolve_pr_number("gh pr ready", "/nonexistent/path", None, None) is None
76
-
77
-
78
- def test_number_resolution_binds_gh_view_to_named_repo(
79
- monkeypatch: pytest.MonkeyPatch,
80
- ) -> None:
81
- captured_commands: list[list[str]] = []
82
-
83
- def fake_run(all_arguments: list[str], **_kwargs: object) -> subprocess.CompletedProcess[str]:
84
- captured_commands.append(all_arguments)
85
- return subprocess.CompletedProcess(args=all_arguments, returncode=0, stdout="500\n", stderr="")
86
-
87
- monkeypatch.setattr(hook_module.subprocess, "run", fake_run)
88
- assert _resolve_pr_number("gh pr ready", None, "flag-owner", "flag-repo") == 500
89
- assert captured_commands
90
- for each_command in captured_commands:
91
- assert "--repo" in each_command
92
- assert "flag-owner/flag-repo" in each_command
93
-
94
-
95
- def test_matches_gh_pr_ready_in_compound_command() -> None:
96
- assert not _GH_PR_READY_PATTERN.search("gh pr ready --undo && gh pr create")
97
-
98
-
99
- def test_run_convergence_check_forwards_cwd_to_subprocess(
100
- monkeypatch: pytest.MonkeyPatch,
101
- ) -> None:
102
- captured_cwd: list[object] = []
103
-
104
- def fake_run(*_run_args: object, **run_keywords: object) -> subprocess.CompletedProcess[str]:
105
- captured_cwd.append(run_keywords.get("cwd"))
106
- return subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr="")
107
-
108
- monkeypatch.setattr(hook_module.subprocess, "run", fake_run)
109
- hook_module._run_convergence_check(
110
- "check_convergence.py", "owner", "repo", 783, "C:/worktrees/pr-783"
111
- )
112
- assert captured_cwd == ["C:/worktrees/pr-783"]
113
-
114
-
115
- def test_run_convergence_check_forwards_none_cwd_as_none(
116
- monkeypatch: pytest.MonkeyPatch,
117
- ) -> None:
118
- captured_cwd: list[object] = []
119
-
120
- def fake_run(*_run_args: object, **run_keywords: object) -> subprocess.CompletedProcess[str]:
121
- captured_cwd.append(run_keywords.get("cwd"))
122
- return subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr="")
123
-
124
- monkeypatch.setattr(hook_module.subprocess, "run", fake_run)
125
- hook_module._run_convergence_check("check_convergence.py", "owner", "repo", 783, None)
126
- assert captured_cwd == [None]
127
-
128
-
129
- def test_main_reads_cwd_from_top_level_payload(
130
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
131
- ) -> None:
132
- convergence_script = (
133
- tmp_path / ".claude" / "skills" / "pr-converge" / "scripts" / "check_convergence.py"
134
- )
135
- convergence_script.parent.mkdir(parents=True)
136
- convergence_script.write_text("")
137
- monkeypatch.setattr(hook_module.Path, "home", classmethod(lambda _cls: tmp_path))
138
-
139
- worktree_path = str(tmp_path / "worktrees" / "pr-783")
140
- monkeypatch.setattr(hook_module, "_resolve_owner_repo", lambda _cwd: ("jl-cmd", "repo"))
141
-
142
- captured_cwd: list[object] = []
143
-
144
- def fake_check(
145
- _script: str, _owner: str, _repo: str, _pr_number: int, cwd: str | None
146
- ) -> subprocess.CompletedProcess[str]:
147
- captured_cwd.append(cwd)
148
- return subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr="")
149
-
150
- monkeypatch.setattr(hook_module, "_run_convergence_check", fake_check)
151
-
152
- payload = {
153
- "tool_name": "Bash",
154
- "cwd": worktree_path,
155
- "tool_input": {"command": "gh pr ready 783", "cwd": "C:/wrong/session/dir"},
156
- }
157
- monkeypatch.setattr(sys, "stdin", io.StringIO(json.dumps(payload)))
158
-
159
- with pytest.raises(SystemExit):
160
- hook_module.main()
161
-
162
- assert captured_cwd == [worktree_path]
163
-
164
-
165
- def _capture_convergence_identity(
166
- monkeypatch: pytest.MonkeyPatch,
167
- tmp_path: pathlib.Path,
168
- command: str,
169
- cwd_owner_repo: tuple[str, str],
170
- ) -> tuple[str, str, int]:
171
- convergence_script = (
172
- tmp_path / ".claude" / "skills" / "pr-converge" / "scripts" / "check_convergence.py"
173
- )
174
- convergence_script.parent.mkdir(parents=True)
175
- convergence_script.write_text("")
176
- monkeypatch.setattr(hook_module.Path, "home", classmethod(lambda _cls: tmp_path))
177
- monkeypatch.setattr(hook_module, "_resolve_owner_repo", lambda _cwd: cwd_owner_repo)
178
-
179
- captured_identity: list[tuple[str, str, int]] = []
180
-
181
- def fake_check(
182
- _script: str, owner: str, repo: str, pr_number: int, _cwd: str | None
183
- ) -> subprocess.CompletedProcess[str]:
184
- captured_identity.append((owner, repo, pr_number))
185
- return subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr="")
186
-
187
- monkeypatch.setattr(hook_module, "_run_convergence_check", fake_check)
188
-
189
- payload = {
190
- "tool_name": "Bash",
191
- "cwd": str(tmp_path / "worktree"),
192
- "tool_input": {"command": command},
193
- }
194
- monkeypatch.setattr(sys, "stdin", io.StringIO(json.dumps(payload)))
195
- with pytest.raises(SystemExit):
196
- hook_module.main()
197
- return captured_identity[0]
198
-
199
-
200
- def test_repo_flag_overrides_cwd_repo(
201
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
202
- ) -> None:
203
- identity = _capture_convergence_identity(
204
- monkeypatch,
205
- tmp_path,
206
- "gh pr ready 161 --repo sample-owner/target-repo",
207
- ("cwd-owner", "cwd-repo"),
208
- )
209
- assert identity == ("sample-owner", "target-repo", 161)
210
-
211
-
212
- def test_pr_url_yields_repo_and_number(
213
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
214
- ) -> None:
215
- identity = _capture_convergence_identity(
216
- monkeypatch,
217
- tmp_path,
218
- "gh pr ready https://github.com/sample-owner/target-repo/pull/161",
219
- ("cwd-owner", "cwd-repo"),
220
- )
221
- assert identity == ("sample-owner", "target-repo", 161)
222
-
223
-
224
- def test_bare_pr_ready_resolves_repo_from_cwd(
225
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
226
- ) -> None:
227
- identity = _capture_convergence_identity(
228
- monkeypatch,
229
- tmp_path,
230
- "gh pr ready 418",
231
- ("cwd-owner", "cwd-repo"),
232
- )
233
- assert identity == ("cwd-owner", "cwd-repo", 418)
234
-
235
-
236
- def test_parse_repo_flag_space_form() -> None:
237
- assert hook_module._parse_repo_flag("gh pr ready 161 --repo sample-owner/target-repo") == (
238
- "sample-owner",
239
- "target-repo",
240
- )
241
-
242
-
243
- def test_parse_repo_flag_equals_form() -> None:
244
- assert hook_module._parse_repo_flag("gh pr ready 161 --repo=sample-owner/target-repo") == (
245
- "sample-owner",
246
- "target-repo",
247
- )
248
-
249
-
250
- def test_parse_repo_flag_short_alias() -> None:
251
- assert hook_module._parse_repo_flag("gh pr ready 161 -R sample-owner/target-repo") == (
252
- "sample-owner",
253
- "target-repo",
254
- )
255
-
256
-
257
- def test_parse_repo_flag_short_alias_attached_value() -> None:
258
- assert hook_module._parse_repo_flag("gh pr ready 161 -Rsample-owner/target-repo") == (
259
- "sample-owner",
260
- "target-repo",
261
- )
262
-
263
-
264
- def test_parse_repo_flag_absent_returns_none() -> None:
265
- assert hook_module._parse_repo_flag("gh pr ready 418") is None
266
-
267
-
268
- def test_parse_pr_url_yields_owner_repo_number() -> None:
269
- assert hook_module._parse_pr_url(
270
- "gh pr ready https://github.com/sample-owner/target-repo/pull/161"
271
- ) == ("sample-owner", "target-repo", 161)
272
-
273
-
274
- def test_parse_pr_url_absent_returns_none() -> None:
275
- assert hook_module._parse_pr_url("gh pr ready 418") is None
276
-
277
-
278
- def test_ready_segment_clips_at_command_separator() -> None:
279
- assert (
280
- hook_module._ready_command_segment(
281
- "gh pr ready 161 && gh pr comment 999 --repo other-owner/other-repo"
282
- )
283
- == "gh pr ready 161 "
284
- )
285
-
286
-
287
- def test_ready_segment_keeps_repo_flag_on_a_continued_line() -> None:
288
- segment = hook_module._ready_command_segment(
289
- "gh pr ready 161 \\\n --repo target-owner/target-repo"
290
- )
291
- assert "--repo" in segment
292
- assert "target-owner/target-repo" in segment
293
-
294
-
295
- def test_continued_repo_flag_binds_the_gate_to_the_named_repo(
296
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
297
- ) -> None:
298
- identity = _capture_convergence_identity(
299
- monkeypatch,
300
- tmp_path,
301
- "gh pr ready 161 \\\n --repo target-owner/target-repo",
302
- ("cwd-owner", "cwd-repo"),
303
- )
304
- assert identity == ("target-owner", "target-repo", 161)
305
-
306
-
307
- def test_chained_repo_flag_does_not_misbind_the_gate(
308
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
309
- ) -> None:
310
- identity = _capture_convergence_identity(
311
- monkeypatch,
312
- tmp_path,
313
- "gh pr ready 161 && gh pr comment 999 --repo other-owner/other-repo --body-file note.md",
314
- ("cwd-owner", "cwd-repo"),
315
- )
316
- assert identity == ("cwd-owner", "cwd-repo", 161)
317
-
318
-
319
- def test_chained_pr_url_does_not_misbind_the_gate(
320
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
321
- ) -> None:
322
- identity = _capture_convergence_identity(
323
- monkeypatch,
324
- tmp_path,
325
- "gh pr ready 161 && echo https://github.com/other-owner/other-repo/pull/5",
326
- ("cwd-owner", "cwd-repo"),
327
- )
328
- assert identity == ("cwd-owner", "cwd-repo", 161)
329
-
330
-
331
- def test_ready_segment_skips_a_leading_undo_invocation() -> None:
332
- assert (
333
- hook_module._ready_command_segment("gh pr ready --undo && gh pr ready 161")
334
- == "gh pr ready 161"
335
- )
336
-
337
-
338
- def test_parse_repo_flag_full_url_form() -> None:
339
- assert hook_module._parse_repo_flag(
340
- "gh pr ready 161 --repo https://github.com/other-owner/other-repo"
341
- ) == ("other-owner", "other-repo")
342
-
343
-
344
- def test_parse_repo_flag_short_alias_full_url_form() -> None:
345
- assert hook_module._parse_repo_flag(
346
- "gh pr ready 161 -R https://github.com/other-owner/other-repo"
347
- ) == ("other-owner", "other-repo")
348
-
349
-
350
- def test_parse_repo_flag_ssh_form() -> None:
351
- assert hook_module._parse_repo_flag(
352
- "gh pr ready 161 --repo git@github.com:other-owner/other-repo"
353
- ) == ("other-owner", "other-repo")
354
-
355
-
356
- def test_parse_repo_flag_ssh_form_strips_git_suffix() -> None:
357
- assert hook_module._parse_repo_flag(
358
- "gh pr ready 161 --repo git@github.com:other-owner/other-repo.git"
359
- ) == ("other-owner", "other-repo")
360
-
361
-
362
- def test_parse_repo_flag_url_form_strips_git_suffix() -> None:
363
- assert hook_module._parse_repo_flag(
364
- "gh pr ready 161 --repo https://github.com/other-owner/other-repo.git"
365
- ) == ("other-owner", "other-repo")
366
-
367
-
368
- def test_parse_pr_url_accepts_enterprise_host() -> None:
369
- assert hook_module._parse_pr_url(
370
- "gh pr ready https://github.example.com/other-owner/other-repo/pull/161"
371
- ) == ("other-owner", "other-repo", 161)
372
-
373
-
374
- def test_background_operator_clips_chained_repo_flag(
375
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
376
- ) -> None:
377
- identity = _capture_convergence_identity(
378
- monkeypatch,
379
- tmp_path,
380
- "gh pr ready 161 & gh pr comment 999 --repo other-owner/other-repo",
381
- ("cwd-owner", "cwd-repo"),
382
- )
383
- assert identity == ("cwd-owner", "cwd-repo", 161)
384
-
385
-
386
- def test_trailing_redirect_keeps_earlier_repo_flag(
387
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
388
- ) -> None:
389
- identity = _capture_convergence_identity(
390
- monkeypatch,
391
- tmp_path,
392
- "gh pr ready 161 --repo other-owner/other-repo 2>&1",
393
- ("cwd-owner", "cwd-repo"),
394
- )
395
- assert identity == ("other-owner", "other-repo", 161)
396
-
397
-
398
- def test_stderr_redirect_dup_keeps_repo_flag() -> None:
399
- segment = hook_module._ready_command_segment(
400
- "gh pr ready 161 >&2 --repo other-owner/other-repo"
401
- )
402
- assert hook_module._parse_repo_flag(segment) == ("other-owner", "other-repo")
403
-
404
-
405
- def test_stderr_to_stdout_redirect_keeps_repo_flag() -> None:
406
- segment = hook_module._ready_command_segment(
407
- "gh pr ready 2>&1 --repo other-owner/other-repo"
408
- )
409
- assert hook_module._parse_repo_flag(segment) == ("other-owner", "other-repo")
410
-
411
-
412
- def test_append_all_output_redirect_keeps_repo_flag() -> None:
413
- segment = hook_module._ready_command_segment(
414
- "gh pr ready 161 &> log --repo other-owner/other-repo"
415
- )
416
- assert hook_module._parse_repo_flag(segment) == ("other-owner", "other-repo")
417
-
418
-
419
- def test_stderr_redirect_binds_repo_end_to_end(
420
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
421
- ) -> None:
422
- identity = _capture_convergence_identity(
423
- monkeypatch,
424
- tmp_path,
425
- "gh pr ready 161 >&2 --repo other-owner/other-repo",
426
- ("cwd-owner", "cwd-repo"),
427
- )
428
- assert identity == ("other-owner", "other-repo", 161)