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
@@ -0,0 +1,969 @@
1
+ """Behavioral tests for the headless codex review wrapper."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import subprocess
7
+ import sys
8
+ import time
9
+ from pathlib import Path
10
+ from typing import Callable
11
+
12
+ import pytest
13
+
14
+ SCRIPTS_DIRECTORY = Path(__file__).resolve().parent
15
+ if str(SCRIPTS_DIRECTORY) not in sys.path:
16
+ sys.path.insert(0, str(SCRIPTS_DIRECTORY))
17
+
18
+ import run_codex_review as wrapper # noqa: E402
19
+ from codex_review_scripts_constants.run_constants import ( # noqa: E402
20
+ ALL_SHAPE_PROBE_REQUIRED_FLAGS,
21
+ BASE_TARGET_FLAG,
22
+ CODEX_BINARY_NAME,
23
+ CODEX_HOME_ENV_VAR,
24
+ COMMIT_TARGET_FLAG,
25
+ CUSTOM_INSTRUCTIONS_PROMPT,
26
+ DEFAULT_TIMEOUT_SECONDS,
27
+ EXEC_SUBCOMMAND,
28
+ HELP_FLAG,
29
+ JSON_FLAG,
30
+ JSONL_CAPTURE_FILENAME,
31
+ MISSING_BINARY_EXIT_CODE,
32
+ MODEL_FLAG,
33
+ OUTCOME_CLASS_CODEX_DOWN,
34
+ OUTCOME_CLASS_COMPLETED,
35
+ REVIEW_SUBCOMMAND,
36
+ SUBPROCESS_DECODE_EXIT_CODE,
37
+ TIMEOUT_EXIT_CODE,
38
+ UNCOMMITTED_TARGET_FLAG,
39
+ UTF8_ENCODING,
40
+ VERSION_FLAG,
41
+ )
42
+
43
+ HELP_TEXT_WITH_TARGETS = (
44
+ "Usage: codex exec review [OPTIONS] [PROMPT]\n"
45
+ " --uncommitted\n"
46
+ " --base <BRANCH>\n"
47
+ " --commit <SHA>\n"
48
+ " --json\n"
49
+ )
50
+ VERSION_STDOUT = "codex-cli 0.144.3\n"
51
+ EXPECTED_BINARY_VERSION = "0.144.3"
52
+ AGENT_MESSAGE_BODY = "No issues found.\n\n```json\n[]\n```"
53
+ CARRIAGE_RETURN_LINE_ENDING = b"\r\n"
54
+ SUCCESS_JSONL = "\n".join(
55
+ [
56
+ json.dumps({"type": "thread.started", "thread_id": "thread-1"}),
57
+ json.dumps({"type": "turn.started"}),
58
+ json.dumps(
59
+ {
60
+ "type": "item.completed",
61
+ "item": {
62
+ "type": "agent_message",
63
+ "text": AGENT_MESSAGE_BODY,
64
+ },
65
+ }
66
+ ),
67
+ json.dumps(
68
+ {
69
+ "type": "turn.completed",
70
+ "usage": {
71
+ "input_tokens": 10,
72
+ "output_tokens": 5,
73
+ },
74
+ }
75
+ ),
76
+ ]
77
+ ) + "\n"
78
+
79
+
80
+ @pytest.fixture
81
+ def repository_directory(tmp_path: Path) -> Path:
82
+ existing_repository_directory = tmp_path / "repo"
83
+ existing_repository_directory.mkdir()
84
+ return existing_repository_directory
85
+
86
+
87
+ @pytest.fixture
88
+ def run_state_directory(tmp_path: Path) -> Path:
89
+ existing_run_state_directory = tmp_path / "run_state"
90
+ existing_run_state_directory.mkdir()
91
+ return existing_run_state_directory
92
+
93
+
94
+ class _SubprocessRecorder:
95
+ def __init__(
96
+ self,
97
+ behavior_for_invocation: Callable[
98
+ [list[str]], subprocess.CompletedProcess[str] | BaseException
99
+ ],
100
+ ) -> None:
101
+ self.behavior_for_invocation = behavior_for_invocation
102
+ self.all_invocations: list[list[str]] = []
103
+ self.all_keyword_arguments: list[dict[str, object]] = []
104
+
105
+ def __call__(
106
+ self,
107
+ invocation: list[str],
108
+ **keyword_arguments: object,
109
+ ) -> subprocess.CompletedProcess[str]:
110
+ self.all_invocations.append(list(invocation))
111
+ self.all_keyword_arguments.append(keyword_arguments)
112
+ behavior = self.behavior_for_invocation(list(invocation))
113
+ if isinstance(behavior, BaseException):
114
+ raise behavior
115
+ return behavior
116
+
117
+
118
+ def _completed(
119
+ all_arguments: list[str],
120
+ returncode: int,
121
+ stdout: str = "",
122
+ stderr: str = "",
123
+ ) -> subprocess.CompletedProcess[str]:
124
+ return subprocess.CompletedProcess(
125
+ args=all_arguments,
126
+ returncode=returncode,
127
+ stdout=stdout,
128
+ stderr=stderr,
129
+ )
130
+
131
+
132
+ def _is_version_probe(all_arguments: list[str]) -> bool:
133
+ return all_arguments == [CODEX_BINARY_NAME, VERSION_FLAG]
134
+
135
+
136
+ def _is_shape_probe(all_arguments: list[str]) -> bool:
137
+ return all_arguments == [
138
+ CODEX_BINARY_NAME,
139
+ EXEC_SUBCOMMAND,
140
+ REVIEW_SUBCOMMAND,
141
+ HELP_FLAG,
142
+ ]
143
+
144
+
145
+ def _is_review_run(all_arguments: list[str]) -> bool:
146
+ return (
147
+ len(all_arguments) >= 4
148
+ and all_arguments[0] == CODEX_BINARY_NAME
149
+ and all_arguments[1] == EXEC_SUBCOMMAND
150
+ and REVIEW_SUBCOMMAND in all_arguments
151
+ and JSON_FLAG in all_arguments
152
+ and HELP_FLAG not in all_arguments
153
+ and VERSION_FLAG not in all_arguments
154
+ )
155
+
156
+
157
+ def _install_recorder(
158
+ monkeypatch: pytest.MonkeyPatch,
159
+ behavior_for_invocation: Callable[
160
+ [list[str]], subprocess.CompletedProcess[str] | BaseException
161
+ ],
162
+ ) -> _SubprocessRecorder:
163
+ recorder = _SubprocessRecorder(behavior_for_invocation)
164
+ monkeypatch.setattr(wrapper, "codex_subprocess_runner", recorder)
165
+ monkeypatch.setattr(
166
+ wrapper, "_resolve_codex_command_prefix", lambda: [CODEX_BINARY_NAME]
167
+ )
168
+ return recorder
169
+
170
+
171
+ def _healthy_probe_then_review(
172
+ review_returncode: int = 0,
173
+ review_stdout: str = SUCCESS_JSONL,
174
+ review_stderr: str = "",
175
+ ) -> Callable[[list[str]], subprocess.CompletedProcess[str] | BaseException]:
176
+ def behavior(
177
+ all_arguments: list[str],
178
+ ) -> subprocess.CompletedProcess[str] | BaseException:
179
+ if _is_version_probe(all_arguments):
180
+ return _completed(all_arguments, 0, stdout=VERSION_STDOUT)
181
+ if _is_shape_probe(all_arguments):
182
+ return _completed(all_arguments, 0, stdout=HELP_TEXT_WITH_TARGETS)
183
+ if _is_review_run(all_arguments):
184
+ return _completed(
185
+ all_arguments,
186
+ review_returncode,
187
+ stdout=review_stdout,
188
+ stderr=review_stderr,
189
+ )
190
+ raise AssertionError(f"unexpected invocation: {all_arguments!r}")
191
+
192
+ return behavior
193
+
194
+
195
+ def _healthy_probe_then_failing_shape(
196
+ shape_stdout: str,
197
+ ) -> Callable[[list[str]], subprocess.CompletedProcess[str] | BaseException]:
198
+ def behavior(
199
+ all_arguments: list[str],
200
+ ) -> subprocess.CompletedProcess[str] | BaseException:
201
+ if _is_version_probe(all_arguments):
202
+ return _completed(all_arguments, 0, stdout=VERSION_STDOUT)
203
+ if _is_shape_probe(all_arguments):
204
+ return _completed(all_arguments, 0, stdout=shape_stdout)
205
+ raise AssertionError("review must not run after a failed shape probe")
206
+
207
+ return behavior
208
+
209
+
210
+ def _only_review_invocation(recorder: _SubprocessRecorder) -> list[str]:
211
+ return next(
212
+ each_invocation
213
+ for each_invocation in recorder.all_invocations
214
+ if _is_review_run(each_invocation)
215
+ )
216
+
217
+
218
+ def test_argv_assembly_for_uncommitted_target(
219
+ monkeypatch: pytest.MonkeyPatch,
220
+ repository_directory: Path,
221
+ run_state_directory: Path,
222
+ ) -> None:
223
+ recorder = _install_recorder(monkeypatch, _healthy_probe_then_review())
224
+
225
+ review_outcome = wrapper.run_codex_review(
226
+ repository_directory=repository_directory,
227
+ run_state_directory=run_state_directory,
228
+ is_uncommitted=True,
229
+ )
230
+
231
+ review_invocation = _only_review_invocation(recorder)
232
+ assert review_invocation == [
233
+ CODEX_BINARY_NAME,
234
+ EXEC_SUBCOMMAND,
235
+ REVIEW_SUBCOMMAND,
236
+ JSON_FLAG,
237
+ UNCOMMITTED_TARGET_FLAG,
238
+ ]
239
+ assert CUSTOM_INSTRUCTIONS_PROMPT not in review_invocation
240
+ assert review_outcome.outcome_class == OUTCOME_CLASS_COMPLETED
241
+
242
+
243
+ def test_argv_assembly_for_base_branch_target(
244
+ monkeypatch: pytest.MonkeyPatch,
245
+ repository_directory: Path,
246
+ run_state_directory: Path,
247
+ ) -> None:
248
+ recorder = _install_recorder(monkeypatch, _healthy_probe_then_review())
249
+ base_branch = "origin/main"
250
+
251
+ wrapper.run_codex_review(
252
+ repository_directory=repository_directory,
253
+ run_state_directory=run_state_directory,
254
+ base_branch=base_branch,
255
+ )
256
+
257
+ review_invocation = _only_review_invocation(recorder)
258
+ assert review_invocation == [
259
+ CODEX_BINARY_NAME,
260
+ EXEC_SUBCOMMAND,
261
+ REVIEW_SUBCOMMAND,
262
+ JSON_FLAG,
263
+ BASE_TARGET_FLAG,
264
+ base_branch,
265
+ ]
266
+ assert CUSTOM_INSTRUCTIONS_PROMPT not in review_invocation
267
+
268
+
269
+ def test_argv_assembly_for_commit_target(
270
+ monkeypatch: pytest.MonkeyPatch,
271
+ repository_directory: Path,
272
+ run_state_directory: Path,
273
+ ) -> None:
274
+ recorder = _install_recorder(monkeypatch, _healthy_probe_then_review())
275
+ commit_sha = "abc123def456"
276
+
277
+ wrapper.run_codex_review(
278
+ repository_directory=repository_directory,
279
+ run_state_directory=run_state_directory,
280
+ commit_sha=commit_sha,
281
+ )
282
+
283
+ review_invocation = _only_review_invocation(recorder)
284
+ assert review_invocation == [
285
+ CODEX_BINARY_NAME,
286
+ EXEC_SUBCOMMAND,
287
+ REVIEW_SUBCOMMAND,
288
+ JSON_FLAG,
289
+ COMMIT_TARGET_FLAG,
290
+ commit_sha,
291
+ ]
292
+ assert CUSTOM_INSTRUCTIONS_PROMPT not in review_invocation
293
+
294
+
295
+ def test_argv_assembly_for_prompt_target(
296
+ monkeypatch: pytest.MonkeyPatch,
297
+ repository_directory: Path,
298
+ run_state_directory: Path,
299
+ ) -> None:
300
+ recorder = _install_recorder(monkeypatch, _healthy_probe_then_review())
301
+
302
+ review_outcome = wrapper.run_codex_review(
303
+ repository_directory=repository_directory,
304
+ run_state_directory=run_state_directory,
305
+ is_prompt_target=True,
306
+ )
307
+
308
+ review_invocation = _only_review_invocation(recorder)
309
+ assert review_invocation == [
310
+ CODEX_BINARY_NAME,
311
+ EXEC_SUBCOMMAND,
312
+ REVIEW_SUBCOMMAND,
313
+ JSON_FLAG,
314
+ CUSTOM_INSTRUCTIONS_PROMPT,
315
+ ]
316
+ assert UNCOMMITTED_TARGET_FLAG not in review_invocation
317
+ assert BASE_TARGET_FLAG not in review_invocation
318
+ assert COMMIT_TARGET_FLAG not in review_invocation
319
+ assert review_outcome.outcome_class == OUTCOME_CLASS_COMPLETED
320
+
321
+
322
+ def test_shape_probe_missing_target_flags_returns_codex_down(
323
+ monkeypatch: pytest.MonkeyPatch,
324
+ repository_directory: Path,
325
+ run_state_directory: Path,
326
+ ) -> None:
327
+ recorder = _install_recorder(
328
+ monkeypatch,
329
+ _healthy_probe_then_failing_shape("Usage: codex exec review\n --legacy-only\n"),
330
+ )
331
+
332
+ review_outcome = wrapper.run_codex_review(
333
+ repository_directory=repository_directory,
334
+ run_state_directory=run_state_directory,
335
+ is_uncommitted=True,
336
+ )
337
+
338
+ assert review_outcome.outcome_class == OUTCOME_CLASS_CODEX_DOWN
339
+ assert review_outcome.binary_version == EXPECTED_BINARY_VERSION
340
+ assert review_outcome.agent_message == ""
341
+ assert review_outcome.jsonl_path is None
342
+ assert not any(
343
+ _is_review_run(each_invocation)
344
+ for each_invocation in recorder.all_invocations
345
+ )
346
+
347
+
348
+ def test_shape_probe_lookalike_flags_do_not_match_required_tokens(
349
+ monkeypatch: pytest.MonkeyPatch,
350
+ repository_directory: Path,
351
+ run_state_directory: Path,
352
+ ) -> None:
353
+ lookalike_help_text = (
354
+ "Usage: codex exec review [OPTIONS]\n"
355
+ " --baseline <BRANCH>\n"
356
+ " --commit-message <TEXT>\n"
357
+ " --uncommitted-only\n"
358
+ " --json-output\n"
359
+ )
360
+ recorder = _install_recorder(
361
+ monkeypatch, _healthy_probe_then_failing_shape(lookalike_help_text)
362
+ )
363
+
364
+ review_outcome = wrapper.run_codex_review(
365
+ repository_directory=repository_directory,
366
+ run_state_directory=run_state_directory,
367
+ is_uncommitted=True,
368
+ )
369
+
370
+ assert review_outcome.outcome_class == OUTCOME_CLASS_CODEX_DOWN
371
+ assert review_outcome.exit_code == 0
372
+ assert review_outcome.jsonl_path is None
373
+ assert not any(
374
+ _is_review_run(each_invocation)
375
+ for each_invocation in recorder.all_invocations
376
+ )
377
+ for each_required_flag in ALL_SHAPE_PROBE_REQUIRED_FLAGS:
378
+ assert each_required_flag in lookalike_help_text
379
+
380
+
381
+ def test_shape_probe_nonzero_help_returns_codex_down(
382
+ monkeypatch: pytest.MonkeyPatch,
383
+ repository_directory: Path,
384
+ run_state_directory: Path,
385
+ ) -> None:
386
+ def behavior(
387
+ all_arguments: list[str],
388
+ ) -> subprocess.CompletedProcess[str] | BaseException:
389
+ if _is_version_probe(all_arguments):
390
+ return _completed(all_arguments, 0, stdout=VERSION_STDOUT)
391
+ if _is_shape_probe(all_arguments):
392
+ return _completed(
393
+ all_arguments,
394
+ 2,
395
+ stderr="error: unrecognized subcommand 'review'\n",
396
+ )
397
+ raise AssertionError("review must not run after a failed shape probe")
398
+
399
+ _install_recorder(monkeypatch, behavior)
400
+
401
+ review_outcome = wrapper.run_codex_review(
402
+ repository_directory=repository_directory,
403
+ run_state_directory=run_state_directory,
404
+ is_uncommitted=True,
405
+ )
406
+
407
+ assert review_outcome.outcome_class == OUTCOME_CLASS_CODEX_DOWN
408
+ assert review_outcome.exit_code == 2
409
+
410
+
411
+ def test_missing_binary_returns_codex_down(
412
+ monkeypatch: pytest.MonkeyPatch,
413
+ repository_directory: Path,
414
+ run_state_directory: Path,
415
+ ) -> None:
416
+ def behavior(
417
+ all_arguments: list[str],
418
+ ) -> subprocess.CompletedProcess[str] | BaseException:
419
+ raise FileNotFoundError(all_arguments[0])
420
+
421
+ _install_recorder(monkeypatch, behavior)
422
+
423
+ review_outcome = wrapper.run_codex_review(
424
+ repository_directory=repository_directory,
425
+ run_state_directory=run_state_directory,
426
+ is_uncommitted=True,
427
+ )
428
+
429
+ assert review_outcome.outcome_class == OUTCOME_CLASS_CODEX_DOWN
430
+ assert review_outcome.exit_code == MISSING_BINARY_EXIT_CODE
431
+ assert review_outcome.jsonl_path is None
432
+
433
+
434
+ def test_jsonl_capture_and_agent_message_extraction(
435
+ monkeypatch: pytest.MonkeyPatch,
436
+ repository_directory: Path,
437
+ run_state_directory: Path,
438
+ ) -> None:
439
+ recorder = _install_recorder(monkeypatch, _healthy_probe_then_review())
440
+
441
+ review_outcome = wrapper.run_codex_review(
442
+ repository_directory=repository_directory,
443
+ run_state_directory=run_state_directory,
444
+ is_uncommitted=True,
445
+ )
446
+
447
+ expected_jsonl_path = run_state_directory / JSONL_CAPTURE_FILENAME
448
+ assert review_outcome.outcome_class == OUTCOME_CLASS_COMPLETED
449
+ assert review_outcome.exit_code == 0
450
+ assert review_outcome.jsonl_path == expected_jsonl_path
451
+ assert expected_jsonl_path.read_text(encoding=UTF8_ENCODING) == SUCCESS_JSONL
452
+ assert review_outcome.agent_message == AGENT_MESSAGE_BODY
453
+ assert review_outcome.binary_version == EXPECTED_BINARY_VERSION
454
+ review_keyword_arguments = next(
455
+ each_keyword_arguments
456
+ for each_invocation, each_keyword_arguments in zip(
457
+ recorder.all_invocations,
458
+ recorder.all_keyword_arguments,
459
+ strict=True,
460
+ )
461
+ if _is_review_run(each_invocation)
462
+ )
463
+ assert review_keyword_arguments["cwd"] == str(repository_directory)
464
+ assert review_keyword_arguments["timeout"] == DEFAULT_TIMEOUT_SECONDS
465
+ assert review_keyword_arguments["capture_output"] is True
466
+ assert review_keyword_arguments["text"] is True
467
+ assert review_keyword_arguments["encoding"] == UTF8_ENCODING
468
+ assert review_keyword_arguments["check"] is False
469
+
470
+
471
+ def test_jsonl_capture_preserves_stream_line_endings(
472
+ monkeypatch: pytest.MonkeyPatch,
473
+ repository_directory: Path,
474
+ run_state_directory: Path,
475
+ ) -> None:
476
+ _install_recorder(monkeypatch, _healthy_probe_then_review())
477
+
478
+ review_outcome = wrapper.run_codex_review(
479
+ repository_directory=repository_directory,
480
+ run_state_directory=run_state_directory,
481
+ is_uncommitted=True,
482
+ )
483
+
484
+ assert review_outcome.jsonl_path is not None
485
+ captured_bytes = review_outcome.jsonl_path.read_bytes()
486
+ assert CARRIAGE_RETURN_LINE_ENDING not in captured_bytes
487
+ assert captured_bytes == SUCCESS_JSONL.encode(UTF8_ENCODING)
488
+
489
+
490
+ def test_unicode_decode_error_returns_codex_down(
491
+ monkeypatch: pytest.MonkeyPatch,
492
+ repository_directory: Path,
493
+ run_state_directory: Path,
494
+ ) -> None:
495
+ def behavior(
496
+ all_arguments: list[str],
497
+ ) -> subprocess.CompletedProcess[str] | BaseException:
498
+ raise UnicodeDecodeError("utf-8", b"\xff", 0, 1, "invalid start byte")
499
+
500
+ _install_recorder(monkeypatch, behavior)
501
+
502
+ review_outcome = wrapper.run_codex_review(
503
+ repository_directory=repository_directory,
504
+ run_state_directory=run_state_directory,
505
+ is_uncommitted=True,
506
+ )
507
+
508
+ assert review_outcome.outcome_class == OUTCOME_CLASS_CODEX_DOWN
509
+ assert review_outcome.exit_code == SUBPROCESS_DECODE_EXIT_CODE
510
+ assert review_outcome.jsonl_path is None
511
+
512
+
513
+ def test_timeout_returns_codex_down(
514
+ monkeypatch: pytest.MonkeyPatch,
515
+ repository_directory: Path,
516
+ run_state_directory: Path,
517
+ ) -> None:
518
+ def behavior(
519
+ all_arguments: list[str],
520
+ ) -> subprocess.CompletedProcess[str] | BaseException:
521
+ if _is_version_probe(all_arguments):
522
+ return _completed(all_arguments, 0, stdout=VERSION_STDOUT)
523
+ if _is_shape_probe(all_arguments):
524
+ return _completed(all_arguments, 0, stdout=HELP_TEXT_WITH_TARGETS)
525
+ if _is_review_run(all_arguments):
526
+ return subprocess.TimeoutExpired(cmd=all_arguments, timeout=1)
527
+ raise AssertionError(f"unexpected invocation: {all_arguments!r}")
528
+
529
+ _install_recorder(monkeypatch, behavior)
530
+
531
+ review_outcome = wrapper.run_codex_review(
532
+ repository_directory=repository_directory,
533
+ run_state_directory=run_state_directory,
534
+ is_uncommitted=True,
535
+ timeout_seconds=1,
536
+ )
537
+
538
+ assert review_outcome.outcome_class == OUTCOME_CLASS_CODEX_DOWN
539
+ assert review_outcome.exit_code == TIMEOUT_EXIT_CODE
540
+
541
+
542
+ def test_codex_home_env_is_passed_through_when_set(
543
+ monkeypatch: pytest.MonkeyPatch,
544
+ tmp_path: Path,
545
+ repository_directory: Path,
546
+ run_state_directory: Path,
547
+ ) -> None:
548
+ codex_home_directory = tmp_path / "codex_home"
549
+ codex_home_directory.mkdir()
550
+ monkeypatch.setenv(CODEX_HOME_ENV_VAR, str(codex_home_directory))
551
+ recorder = _install_recorder(monkeypatch, _healthy_probe_then_review())
552
+
553
+ wrapper.run_codex_review(
554
+ repository_directory=repository_directory,
555
+ run_state_directory=run_state_directory,
556
+ is_uncommitted=True,
557
+ )
558
+
559
+ for each_keyword_arguments in recorder.all_keyword_arguments:
560
+ process_environment = each_keyword_arguments.get("env")
561
+ assert process_environment is not None
562
+ assert isinstance(process_environment, dict)
563
+ assert process_environment[CODEX_HOME_ENV_VAR] == str(codex_home_directory)
564
+
565
+
566
+ def test_rejects_multiple_targets(
567
+ monkeypatch: pytest.MonkeyPatch,
568
+ repository_directory: Path,
569
+ run_state_directory: Path,
570
+ ) -> None:
571
+ recorder = _install_recorder(monkeypatch, _healthy_probe_then_review())
572
+
573
+ with pytest.raises(ValueError):
574
+ wrapper.run_codex_review(
575
+ repository_directory=repository_directory,
576
+ run_state_directory=run_state_directory,
577
+ is_uncommitted=True,
578
+ base_branch="main",
579
+ )
580
+
581
+ assert recorder.all_invocations == []
582
+
583
+
584
+ def test_rejects_missing_target(
585
+ monkeypatch: pytest.MonkeyPatch,
586
+ repository_directory: Path,
587
+ run_state_directory: Path,
588
+ ) -> None:
589
+ recorder = _install_recorder(monkeypatch, _healthy_probe_then_review())
590
+
591
+ with pytest.raises(ValueError):
592
+ wrapper.run_codex_review(
593
+ repository_directory=repository_directory,
594
+ run_state_directory=run_state_directory,
595
+ )
596
+
597
+ assert recorder.all_invocations == []
598
+
599
+
600
+ def test_shape_probe_missing_json_flag_returns_codex_down(
601
+ monkeypatch: pytest.MonkeyPatch,
602
+ repository_directory: Path,
603
+ run_state_directory: Path,
604
+ ) -> None:
605
+ help_without_json = (
606
+ "Usage: codex exec review [OPTIONS]\n"
607
+ " --uncommitted\n"
608
+ " --base <BRANCH>\n"
609
+ " --commit <SHA>\n"
610
+ )
611
+ recorder = _install_recorder(
612
+ monkeypatch, _healthy_probe_then_failing_shape(help_without_json)
613
+ )
614
+
615
+ review_outcome = wrapper.run_codex_review(
616
+ repository_directory=repository_directory,
617
+ run_state_directory=run_state_directory,
618
+ is_uncommitted=True,
619
+ )
620
+
621
+ assert review_outcome.outcome_class == OUTCOME_CLASS_CODEX_DOWN
622
+ assert review_outcome.jsonl_path is None
623
+ assert not any(
624
+ _is_review_run(each_invocation)
625
+ for each_invocation in recorder.all_invocations
626
+ )
627
+ assert JSON_FLAG not in help_without_json
628
+ assert JSON_FLAG in ALL_SHAPE_PROBE_REQUIRED_FLAGS
629
+
630
+
631
+ def test_missing_repository_directory_raises_value_error(
632
+ monkeypatch: pytest.MonkeyPatch,
633
+ tmp_path: Path,
634
+ run_state_directory: Path,
635
+ ) -> None:
636
+ missing_repository_directory = tmp_path / "missing-repo"
637
+ recorder = _install_recorder(monkeypatch, _healthy_probe_then_review())
638
+
639
+ with pytest.raises(ValueError, match="repository_directory"):
640
+ wrapper.run_codex_review(
641
+ repository_directory=missing_repository_directory,
642
+ run_state_directory=run_state_directory,
643
+ is_uncommitted=True,
644
+ )
645
+
646
+ assert recorder.all_invocations == []
647
+
648
+
649
+ def test_missing_run_state_directory_raises_before_any_codex_process(
650
+ monkeypatch: pytest.MonkeyPatch,
651
+ tmp_path: Path,
652
+ repository_directory: Path,
653
+ ) -> None:
654
+ missing_run_state_directory = tmp_path / "missing-run-state"
655
+ recorder = _install_recorder(monkeypatch, _healthy_probe_then_review())
656
+
657
+ with pytest.raises(ValueError, match="run_state_directory"):
658
+ wrapper.run_codex_review(
659
+ repository_directory=repository_directory,
660
+ run_state_directory=missing_run_state_directory,
661
+ is_uncommitted=True,
662
+ )
663
+
664
+ assert recorder.all_invocations == []
665
+
666
+
667
+ def test_custom_instructions_prompt_demands_finding_bullet_format() -> None:
668
+ assert "- [P1]" in CUSTOM_INSTRUCTIONS_PROMPT
669
+ assert "Review comment:" in CUSTOM_INSTRUCTIONS_PROMPT
670
+ assert "fenced JSON" not in CUSTOM_INSTRUCTIONS_PROMPT
671
+ assert "JSON array" not in CUSTOM_INSTRUCTIONS_PROMPT
672
+
673
+
674
+ def test_model_pin_assembles_model_flag_before_review_subcommand(
675
+ monkeypatch: pytest.MonkeyPatch,
676
+ repository_directory: Path,
677
+ run_state_directory: Path,
678
+ ) -> None:
679
+ model_pin = "gpt-5-codex"
680
+ monkeypatch.setattr(wrapper, "CODEX_MODEL_PIN", model_pin)
681
+ recorder = _install_recorder(monkeypatch, _healthy_probe_then_review())
682
+
683
+ wrapper.run_codex_review(
684
+ repository_directory=repository_directory,
685
+ run_state_directory=run_state_directory,
686
+ is_uncommitted=True,
687
+ )
688
+
689
+ review_invocation = _only_review_invocation(recorder)
690
+ model_flag_index = review_invocation.index(MODEL_FLAG)
691
+ review_subcommand_index = review_invocation.index(REVIEW_SUBCOMMAND)
692
+ assert review_invocation[model_flag_index : model_flag_index + 2] == [
693
+ MODEL_FLAG,
694
+ model_pin,
695
+ ]
696
+ assert model_flag_index < review_subcommand_index
697
+
698
+
699
+ def test_review_exit_1_config_error_classifies_codex_down(
700
+ monkeypatch: pytest.MonkeyPatch,
701
+ repository_directory: Path,
702
+ run_state_directory: Path,
703
+ ) -> None:
704
+ config_error_stderr = (
705
+ "Error loading config.toml: unknown variant 'default', "
706
+ "expected 'fast' or 'flex' in 'service_tier'\n"
707
+ )
708
+ recorder = _install_recorder(
709
+ monkeypatch,
710
+ _healthy_probe_then_review(
711
+ review_returncode=1,
712
+ review_stdout="",
713
+ review_stderr=config_error_stderr,
714
+ ),
715
+ )
716
+
717
+ review_outcome = wrapper.run_codex_review(
718
+ repository_directory=repository_directory,
719
+ run_state_directory=run_state_directory,
720
+ is_uncommitted=True,
721
+ )
722
+
723
+ assert review_outcome.outcome_class == OUTCOME_CLASS_CODEX_DOWN
724
+ assert review_outcome.exit_code == 1
725
+ assert review_outcome.agent_message == config_error_stderr
726
+ assert review_outcome.jsonl_path is not None
727
+ assert review_outcome.jsonl_path.read_text(encoding=UTF8_ENCODING) == ""
728
+ assert any(
729
+ _is_review_run(each_invocation)
730
+ for each_invocation in recorder.all_invocations
731
+ )
732
+
733
+
734
+ def test_review_exit_2_argument_error_classifies_codex_down(
735
+ monkeypatch: pytest.MonkeyPatch,
736
+ repository_directory: Path,
737
+ run_state_directory: Path,
738
+ ) -> None:
739
+ argument_error_stderr = (
740
+ "error: the argument '--uncommitted' cannot be used with '[PROMPT]'\n"
741
+ "Usage: codex exec review --json --uncommitted [PROMPT]\n"
742
+ )
743
+ recorder = _install_recorder(
744
+ monkeypatch,
745
+ _healthy_probe_then_review(
746
+ review_returncode=2,
747
+ review_stdout="",
748
+ review_stderr=argument_error_stderr,
749
+ ),
750
+ )
751
+
752
+ review_outcome = wrapper.run_codex_review(
753
+ repository_directory=repository_directory,
754
+ run_state_directory=run_state_directory,
755
+ is_uncommitted=True,
756
+ )
757
+
758
+ assert review_outcome.outcome_class == OUTCOME_CLASS_CODEX_DOWN
759
+ assert review_outcome.exit_code == 2
760
+ assert review_outcome.agent_message == argument_error_stderr
761
+ assert review_outcome.jsonl_path is not None
762
+ assert review_outcome.jsonl_path.read_text(encoding=UTF8_ENCODING) == ""
763
+ assert any(
764
+ _is_review_run(each_invocation)
765
+ for each_invocation in recorder.all_invocations
766
+ )
767
+
768
+
769
+ def test_review_exit_0_stays_completed(
770
+ monkeypatch: pytest.MonkeyPatch,
771
+ repository_directory: Path,
772
+ run_state_directory: Path,
773
+ ) -> None:
774
+ _install_recorder(
775
+ monkeypatch,
776
+ _healthy_probe_then_review(review_returncode=0, review_stdout=SUCCESS_JSONL),
777
+ )
778
+
779
+ review_outcome = wrapper.run_codex_review(
780
+ repository_directory=repository_directory,
781
+ run_state_directory=run_state_directory,
782
+ is_uncommitted=True,
783
+ )
784
+
785
+ assert review_outcome.outcome_class == OUTCOME_CLASS_COMPLETED
786
+ assert review_outcome.exit_code == 0
787
+ assert review_outcome.agent_message == AGENT_MESSAGE_BODY
788
+
789
+
790
+ def test_resolve_prefix_wraps_windows_shim(monkeypatch: pytest.MonkeyPatch) -> None:
791
+ shim_path = r"C:\\Users\\dev\\AppData\\Roaming\\npm\\codex.CMD"
792
+ monkeypatch.setattr(wrapper.shutil, "which", lambda _name: shim_path)
793
+ monkeypatch.setattr(wrapper.os, "name", "nt")
794
+
795
+ assert wrapper._resolve_codex_command_prefix() == ["cmd", "/c", shim_path]
796
+
797
+
798
+ def test_resolve_prefix_uses_resolved_path_on_posix(
799
+ monkeypatch: pytest.MonkeyPatch,
800
+ ) -> None:
801
+ resolved_path = "/usr/local/bin/codex"
802
+ monkeypatch.setattr(wrapper.shutil, "which", lambda _name: resolved_path)
803
+ monkeypatch.setattr(wrapper.os, "name", "posix")
804
+
805
+ assert wrapper._resolve_codex_command_prefix() == [resolved_path]
806
+
807
+
808
+ def test_resolve_prefix_falls_back_to_bare_name_when_absent(
809
+ monkeypatch: pytest.MonkeyPatch,
810
+ ) -> None:
811
+ monkeypatch.setattr(wrapper.shutil, "which", lambda _name: None)
812
+
813
+ assert wrapper._resolve_codex_command_prefix() == [CODEX_BINARY_NAME]
814
+
815
+
816
+ def _is_process_running(process_identifier: int) -> bool:
817
+ if sys.platform == "win32":
818
+ process_listing = subprocess.run(
819
+ ["tasklist", "/FI", f"PID eq {process_identifier}"],
820
+ capture_output=True,
821
+ text=True,
822
+ check=False,
823
+ )
824
+ return str(process_identifier) in process_listing.stdout
825
+ liveness_probe = subprocess.run(
826
+ ["kill", "-0", str(process_identifier)],
827
+ capture_output=True,
828
+ check=False,
829
+ )
830
+ return liveness_probe.returncode == 0
831
+
832
+
833
+ def _wait_until_process_stops(
834
+ process_identifier: int, deadline_seconds: float
835
+ ) -> bool:
836
+ poll_interval_seconds = 0.5
837
+ deadline = time.monotonic() + deadline_seconds
838
+ while time.monotonic() < deadline:
839
+ if not _is_process_running(process_identifier):
840
+ return True
841
+ time.sleep(poll_interval_seconds)
842
+ return not _is_process_running(process_identifier)
843
+
844
+
845
+ def _grandchild_spawn_source(grandchild_pid_path: Path, lifetime_seconds: int) -> str:
846
+ """Python source that spawns a grandchild and records its PID before sleeping.
847
+
848
+ ::
849
+
850
+ Popen(grandchild) -> write grandchild.pid -> sleep grandchild holds pipe
851
+ """
852
+ return (
853
+ "import subprocess, sys, time, pathlib; "
854
+ "grandchild = subprocess.Popen([sys.executable, '-c', "
855
+ f"'import time; time.sleep({lifetime_seconds})']); "
856
+ f"pathlib.Path(r'{grandchild_pid_path}').write_text(str(grandchild.pid)); "
857
+ f"time.sleep({lifetime_seconds})"
858
+ )
859
+
860
+
861
+ def test_run_command_kills_grandchild_tree_on_timeout_without_hanging(
862
+ tmp_path: Path,
863
+ ) -> None:
864
+ """The timeout kills the grandchild that inherited the capture pipe.
865
+
866
+ ::
867
+
868
+ middle -> grandchild, timeout at 2s ok: grandchild PID stops running
869
+ kill only the middle child flag: grandchild lives on 30s
870
+
871
+ A direct-child-only kill leaves the grandchild alive on POSIX, so asserting
872
+ the recorded grandchild PID stops running guards the regression in CI.
873
+ """
874
+ grandchild_lifetime_seconds = 30
875
+ review_timeout_seconds = 2
876
+ wall_clock_ceiling_seconds = 20
877
+ grandchild_pid_path = tmp_path / "grandchild.pid"
878
+ grandchild_source = _grandchild_spawn_source(
879
+ grandchild_pid_path, grandchild_lifetime_seconds
880
+ )
881
+ start_time = time.monotonic()
882
+ with pytest.raises(subprocess.TimeoutExpired):
883
+ wrapper._run_command(
884
+ [sys.executable, "-c", grandchild_source],
885
+ working_directory=tmp_path,
886
+ timeout_seconds=review_timeout_seconds,
887
+ )
888
+ assert time.monotonic() - start_time < wall_clock_ceiling_seconds
889
+ grandchild_identifier = int(grandchild_pid_path.read_text())
890
+ assert _wait_until_process_stops(grandchild_identifier, wall_clock_ceiling_seconds)
891
+
892
+
893
+ def test_drain_joins_pipes_when_direct_kill_raises_process_lookup_error() -> None:
894
+ """Even when the process is already gone, drain still joins pipe readers."""
895
+ all_communicate_timeouts: list[float | None] = []
896
+
897
+ class _AlreadyDeadProcess:
898
+ def communicate(
899
+ self, timeout: float | None = None
900
+ ) -> tuple[str, str]:
901
+ all_communicate_timeouts.append(timeout)
902
+ if len(all_communicate_timeouts) == 1:
903
+ raise subprocess.TimeoutExpired(cmd="codex", timeout=timeout or 0)
904
+ return "", ""
905
+
906
+ def kill(self) -> None:
907
+ raise ProcessLookupError()
908
+
909
+ already_dead_process = _AlreadyDeadProcess()
910
+ wrapper._drain_process_after_tree_kill(
911
+ already_dead_process # type: ignore[arg-type] # duck-typed Popen stand-in
912
+ )
913
+ assert len(all_communicate_timeouts) == 2
914
+
915
+
916
+ def test_reap_process_with_grace_bounds_wait_when_child_survives() -> None:
917
+ """Cleanup kills a surviving child and waits with a grace timeout, not forever."""
918
+ all_wait_timeouts: list[float | None] = []
919
+ all_kill_calls: list[bool] = []
920
+
921
+ class _SurvivingProcess:
922
+ def poll(self) -> int | None:
923
+ return None
924
+
925
+ def kill(self) -> None:
926
+ all_kill_calls.append(True)
927
+
928
+ def wait(self, timeout: float | None = None) -> int:
929
+ all_wait_timeouts.append(timeout)
930
+ raise subprocess.TimeoutExpired(cmd="codex", timeout=timeout or 0)
931
+
932
+ surviving_process = _SurvivingProcess()
933
+ wrapper._reap_process_with_grace(
934
+ surviving_process # type: ignore[arg-type] # duck-typed Popen stand-in
935
+ )
936
+ assert all_kill_calls == [True]
937
+ assert all_wait_timeouts == [wrapper.PROCESS_TREE_KILL_TIMEOUT_SECONDS]
938
+
939
+
940
+ def test_run_command_surfaces_timeout_when_tree_kill_is_noop(
941
+ monkeypatch: pytest.MonkeyPatch,
942
+ tmp_path: Path,
943
+ ) -> None:
944
+ """An incomplete tree kill still raises TimeoutExpired inside a wall ceiling.
945
+
946
+ ::
947
+
948
+ terminate is a no-op, child sleeps 60s ok: TimeoutExpired < 25s wall
949
+ no post-kill drain / fallback kill flag: hang past ceiling on wait
950
+
951
+ The post-kill grace drain and direct-child kill fallback keep the caller
952
+ from blocking forever when taskkill/killpg leave the direct child alive.
953
+ """
954
+ review_timeout_seconds = 1
955
+ wall_clock_ceiling_seconds = 25
956
+ sleep_source = "import time; time.sleep(60)"
957
+
958
+ def leave_process_alive(_review_process: object) -> None:
959
+ return None
960
+
961
+ monkeypatch.setattr(wrapper, "terminate_process_tree", leave_process_alive)
962
+ start_time = time.monotonic()
963
+ with pytest.raises(subprocess.TimeoutExpired):
964
+ wrapper._run_command(
965
+ [sys.executable, "-c", sleep_source],
966
+ working_directory=tmp_path,
967
+ timeout_seconds=review_timeout_seconds,
968
+ )
969
+ assert time.monotonic() - start_time < wall_clock_ceiling_seconds