claude-dev-env 2.16.0 → 2.18.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 (557) 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/orchestrator/SKILL.md +6 -4
  28. package/.agents/skills/orchestrator-refresh/SKILL.md +9 -5
  29. package/.agents/skills/pr-cleanup/SKILL.md +99 -0
  30. package/.agents/skills/prototype/reference/honest-limitations.md +1 -1
  31. package/.agents/skills/prototype/reference/promotion-tasks.md +1 -1
  32. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +2 -2
  33. package/.agents/skills/prototype/workflows/promotion.md +2 -2
  34. package/.agents/skills/review-router/SKILL.md +51 -0
  35. package/.agents/skills/review-router/reference/route-policy.json +7 -0
  36. package/.agents/skills/review-router/reference/task-seeds.md +1 -0
  37. package/.agents/skills/review-router/scripts/conftest.py +4 -0
  38. package/.agents/skills/review-router/scripts/review_router.py +50 -0
  39. package/.agents/skills/review-router/scripts/review_router_cli.py +211 -0
  40. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +1 -0
  41. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  42. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +13 -0
  43. package/.agents/skills/review-router/scripts/test_review_router.py +23 -0
  44. package/.agents/skills/review-router/scripts/test_review_router_cli.py +47 -0
  45. package/.agents/skills/review-tier/SKILL.md +56 -0
  46. package/.agents/skills/review-tier/reference/task-seeds.md +1 -0
  47. package/.agents/skills/review-tier/reference/tier-policy.json +9 -0
  48. package/.agents/skills/review-tier/scripts/conftest.py +4 -0
  49. package/.agents/skills/review-tier/scripts/review_tier.py +332 -0
  50. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +1 -0
  51. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  52. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +29 -0
  53. package/.agents/skills/review-tier/scripts/test_generation.py +171 -0
  54. package/.agents/skills/review-tier/scripts/test_review_tier.py +33 -0
  55. package/.agents/skills/session-log/SKILL.md +1 -1
  56. package/.agents/skills/shared-extraction-audit/SKILL.md +160 -0
  57. package/.agents/skills/shared-extraction-audit/reference/examples.md +46 -0
  58. package/.agents/skills/shared-extraction-audit/reference/offense-taxonomy.md +100 -0
  59. package/.agents/skills/source-command-logifix/SKILL.md +29 -0
  60. package/.agents/skills/source-command-sr-loop/SKILL.md +58 -0
  61. package/.agents/skills/team-advisor/SKILL.md +5 -5
  62. package/.agents/skills-archived/AGENTS.md +45 -0
  63. package/.agents/{skills → skills-archived}/anthropic-plan/AGENTS.md +1 -1
  64. package/.agents/{skills → skills-archived}/bugteam/scripts/reflow_skill_md.py +3 -3
  65. package/.agents/{skills → skills-archived}/codex-review/test_skill_scaffold.py +1 -1
  66. package/.agents/{skills → skills-archived}/copilot-finding-triage/SKILL.md +1 -1
  67. package/.agents/{skills → skills-archived}/pr-converge/SKILL.md +6 -6
  68. package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/AGENTS.md +1 -1
  69. package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/constants.py +1 -1
  70. package/.agents/{skills → skills-archived}/pr-converge/reference/multi-pr-orchestration.md +1 -1
  71. package/.agents/{skills → skills-archived}/pr-converge/reference/per-tick.md +3 -3
  72. package/.agents/{skills → skills-archived}/pr-converge/scripts/reflow_skill_md.py +2 -2
  73. package/.agents/{skills → skills-archived}/pr-converge/test_portable_pacer_gate.py +6 -2
  74. package/.agents/{skills → skills-archived}/pr-converge/workflows/schedule-wakeup-loop.md +1 -1
  75. package/.agents/{skills → skills-archived}/split-pr/reference/proposal-format.md +1 -1
  76. package/AGENTS.md +34 -58
  77. package/_shared/AGENTS.md +2 -0
  78. package/_shared/advisor/AGENTS.md +5 -5
  79. package/_shared/advisor/advisor-protocol.md +54 -31
  80. package/_shared/advisor/reference/advisor-block.md +5 -1
  81. package/_shared/advisor/reference/consult-format.md +1 -1
  82. package/_shared/advisor/reference/identity.md +28 -0
  83. package/_shared/advisor/reference/lifecycle.md +8 -1
  84. package/_shared/advisor/reference/sol-rung.md +14 -9
  85. package/_shared/advisor/reference/spawn-walk-log.md +6 -5
  86. package/_shared/advisor/reference/third-party-bind.md +6 -7
  87. package/_shared/advisor/reference/warm-up.md +9 -2
  88. package/_shared/advisor/scripts/codex_sol_advisor.py +66 -11
  89. package/_shared/advisor/scripts/config/advisor_scripts_constants/advisor_route_constants.py +9 -0
  90. package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +16 -4
  91. package/_shared/advisor/scripts/config/advisor_scripts_constants/sol_advisor_constants.py +11 -10
  92. package/_shared/advisor/scripts/model_tier_run_validator.py +50 -23
  93. package/_shared/advisor/scripts/tests/test_codex_sol_advisor.py +108 -3
  94. package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +171 -82
  95. package/_shared/advisor/scripts/tests/test_tier_model_ids.py +43 -0
  96. package/_shared/advisor/scripts/tier_model_ids.py +75 -7
  97. package/_shared/pr-loop/audit-reply-template.md +4 -0
  98. package/_shared/pr-loop/fix-protocol.md +7 -6
  99. package/_shared/pr-loop/gh-payloads.md +8 -6
  100. package/_shared/pr-loop/precatch-rubric.md +6 -5
  101. package/_shared/pr-loop/scripts/AGENTS.md +15 -0
  102. package/_shared/pr-loop/scripts/_pr_converge_path_setup.py +38 -0
  103. package/_shared/pr-loop/scripts/check_bugbot_ci.py +279 -0
  104. package/_shared/pr-loop/scripts/check_convergence.py +923 -0
  105. package/_shared/pr-loop/scripts/check_convergence_availability.py +275 -0
  106. package/_shared/pr-loop/scripts/check_convergence_gates.py +279 -0
  107. package/_shared/pr-loop/scripts/check_convergence_thread_gates.py +219 -0
  108. package/_shared/pr-loop/scripts/check_pending_reviews.py +154 -0
  109. package/_shared/pr-loop/scripts/codex_down_classifier.py +98 -0
  110. package/_shared/pr-loop/scripts/codex_review_scripts_constants/.claude/CLAUDE.md +1 -0
  111. package/_shared/pr-loop/scripts/codex_review_scripts_constants/AGENTS.md +18 -0
  112. package/_shared/pr-loop/scripts/codex_review_scripts_constants/__init__.py +1 -0
  113. package/_shared/pr-loop/scripts/codex_review_scripts_constants/classifier_constants.py +35 -0
  114. package/_shared/pr-loop/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +82 -0
  115. package/_shared/pr-loop/scripts/codex_review_scripts_constants/findings_constants.py +18 -0
  116. package/_shared/pr-loop/scripts/codex_review_scripts_constants/run_constants.py +53 -0
  117. package/_shared/pr-loop/scripts/codex_usage_probe.py +570 -0
  118. package/_shared/pr-loop/scripts/conftest.py +43 -0
  119. package/_shared/pr-loop/scripts/fetch_copilot_reviews.py +134 -0
  120. package/_shared/pr-loop/scripts/fixtures/auth_failure_synthetic.txt +1 -0
  121. package/_shared/pr-loop/scripts/fixtures/config_load_failure_v0.125.0.txt +1 -0
  122. package/_shared/pr-loop/scripts/fixtures/freeform_findings_v0.144.3.txt +6 -0
  123. package/_shared/pr-loop/scripts/fixtures/model_rejection_v0.125.0.jsonl +5 -0
  124. package/_shared/pr-loop/scripts/fixtures/structured_findings.txt +13 -0
  125. package/_shared/pr-loop/scripts/fixtures/success_stream_v0.144.3.jsonl +6 -0
  126. package/_shared/pr-loop/scripts/fixtures/unknown_failure_synthetic.txt +1 -0
  127. package/_shared/pr-loop/scripts/fixtures/usage_limit_synthetic.txt +1 -0
  128. package/_shared/pr-loop/scripts/parse_codex_findings.py +207 -0
  129. package/_shared/pr-loop/scripts/post_fix_reply.py +168 -0
  130. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/.claude/CLAUDE.md +1 -0
  131. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/AGENTS.md +18 -0
  132. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/__init__.py +0 -0
  133. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +70 -0
  134. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/pr_converge_constants.py +118 -0
  135. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/reflow_skill_md_constants.py +13 -0
  136. package/_shared/pr-loop/scripts/pr_converge_skill_constants/.claude/CLAUDE.md +1 -0
  137. package/_shared/pr-loop/scripts/pr_converge_skill_constants/AGENTS.md +26 -0
  138. package/_shared/pr-loop/scripts/pr_converge_skill_constants/__init__.py +0 -0
  139. package/_shared/pr-loop/scripts/pr_converge_skill_constants/constants.py +69 -0
  140. package/_shared/pr-loop/scripts/run_codex_review.py +609 -0
  141. package/_shared/pr-loop/scripts/test__pr_converge_path_setup.py +30 -0
  142. package/_shared/pr-loop/scripts/test_check_bugbot_ci.py +312 -0
  143. package/_shared/pr-loop/scripts/test_check_convergence.py +438 -0
  144. package/_shared/pr-loop/scripts/test_check_convergence_availability.py +391 -0
  145. package/_shared/pr-loop/scripts/test_check_convergence_codex.py +517 -0
  146. package/_shared/pr-loop/scripts/test_check_convergence_contract.py +400 -0
  147. package/_shared/pr-loop/scripts/test_check_convergence_fixture.py +179 -0
  148. package/_shared/pr-loop/scripts/test_check_convergence_gates.py +84 -0
  149. package/_shared/pr-loop/scripts/test_check_convergence_thread_gates.py +99 -0
  150. package/_shared/pr-loop/scripts/test_codex_down_classifier.py +143 -0
  151. package/_shared/pr-loop/scripts/test_codex_usage_probe.py +683 -0
  152. package/_shared/pr-loop/scripts/test_parse_codex_findings.py +130 -0
  153. package/_shared/pr-loop/scripts/test_run_codex_review.py +969 -0
  154. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +1 -1
  155. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +1 -1
  156. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +1 -1
  157. package/_shared/pr-loop/state-schema.md +20 -28
  158. package/_shared/pr-loop/worker-spawn.md +4 -3
  159. package/_shared/process-tree/AGENTS.md +3 -3
  160. package/bin/ever-shipped-skills.mjs +29 -1
  161. package/bin/install-constants.mjs +15 -6
  162. package/bin/install.agents-home.test.mjs +13 -0
  163. package/bin/install.cursor-rules.test.mjs +3 -3
  164. package/bin/install.mjs +80 -20
  165. package/bin/install.profile-root.test.mjs +1 -1
  166. package/bin/install.prune.test.mjs +23 -204
  167. package/bin/install.test.mjs +8 -5
  168. package/bin/resolve-install-root.mjs +21 -0
  169. package/bin/resolve-package-managed-directory.mjs +4 -4
  170. package/commands/AGENTS.md +2 -0
  171. package/commands/sr-loop.md +3 -0
  172. package/docs/references/prose-style-enforcement.md +3 -7
  173. package/docs/references/team-advisor-skill.md +2 -2
  174. package/hooks/AGENTS.md +0 -3
  175. package/hooks/blocking/AGENTS.md +0 -2
  176. package/hooks/blocking/ask_user_question_shape_blocker.py +83 -0
  177. package/hooks/blocking/pre_tool_use_dispatcher.py +2 -19
  178. package/hooks/blocking/state_description_blocker.py +1 -1
  179. package/hooks/blocking/test_ask_user_question_shape.py +69 -0
  180. package/hooks/blocking/test_pre_tool_use_dispatcher.py +17 -58
  181. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +9 -104
  182. package/hooks/blocking/test_state_description_blocker.py +1 -0
  183. package/hooks/blocking/test_stop_dispatcher.py +1 -26
  184. package/hooks/hooks.json +1 -1
  185. package/hooks/hooks_constants/AGENTS.md +0 -3
  186. package/hooks/hooks_constants/ask_user_question_shape.py +1 -1
  187. package/hooks/hooks_constants/ask_user_question_shape_constants.py +73 -0
  188. package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +1 -2
  189. package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -1
  190. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -7
  191. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -1
  192. package/hooks/hooks_constants/test_ask_user_question_shape.py +1 -1
  193. package/hooks/hooks_constants/test_prose_metrics_parity.py +29 -107
  194. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -1
  195. package/hooks/hooks_constants/working_style_prompt_constants.py +9 -10
  196. package/hooks/session/AGENTS.md +4 -1
  197. package/hooks/session/test_working_style_prompt.py +8 -4
  198. package/hooks/session/working_style_prompt.py +4 -2
  199. package/package.json +1 -4
  200. package/rules/AGENTS.md +2 -5
  201. package/rules/asd-ste100-language.md +39 -0
  202. package/rules/ask-user-question-required.md +1 -1
  203. package/rules/long-horizon-autonomy.md +1 -1
  204. package/scripts/AGENTS.md +1 -0
  205. package/scripts/Show-Asset.ps1 +106 -0
  206. package/scripts/test_validate_instruction_pairs.py +30 -3
  207. package/scripts/tests/test_engine.py +4 -4
  208. package/scripts/tests/test_rules.py +5 -5
  209. package/system-prompts/AGENTS.md +8 -0
  210. package/system-prompts/software-engineer.xml +8 -3
  211. package/.agents/skills/grokify/evals/README.md +0 -72
  212. package/.agents/skills/grokify/evals/parse-payload.test.mjs +0 -171
  213. package/.agents/skills/grokify/evals/run-capability-evals.mjs +0 -545
  214. package/_shared/pr-loop/scripts/README.md +0 -21
  215. package/docs/imagegen.md +0 -22
  216. package/hooks/blocking/eli11_reply_enforcer.py +0 -436
  217. package/hooks/blocking/plain_language_blocker.py +0 -758
  218. package/hooks/blocking/test_eli11_reply_enforcer.py +0 -390
  219. package/hooks/blocking/test_plain_language_blocker.py +0 -683
  220. package/hooks/blocking/test_plain_language_blocker_allowlist.py +0 -228
  221. package/hooks/diagnostic/AGENTS.md +0 -75
  222. package/hooks/diagnostic/CLAUDE.md +0 -1
  223. package/hooks/diagnostic/hook_log_extractor.py +0 -875
  224. package/hooks/diagnostic/hook_log_init.py +0 -202
  225. package/hooks/diagnostic/hook_log_stop_wrapper.py +0 -26
  226. package/hooks/diagnostic/migrations/2026-04-25-drop-themes-hook-events.sql +0 -3
  227. package/hooks/diagnostic/migrations/AGENTS.md +0 -16
  228. package/hooks/diagnostic/migrations/CLAUDE.md +0 -1
  229. package/hooks/diagnostic/migrations/README.md +0 -76
  230. package/hooks/diagnostic/queries/AGENTS.md +0 -19
  231. package/hooks/diagnostic/queries/CLAUDE.md +0 -1
  232. package/hooks/diagnostic/queries/block_details_for_hook.sql +0 -26
  233. package/hooks/diagnostic/queries/blocks_by_category.sql +0 -10
  234. package/hooks/diagnostic/queries/blocks_by_tool.sql +0 -9
  235. package/hooks/diagnostic/queries/blocks_last_7_days.sql +0 -11
  236. package/hooks/diagnostic/queries/top_blockers_last_24_hours.sql +0 -12
  237. package/hooks/diagnostic/queries/top_blockers_overall.sql +0 -12
  238. package/hooks/diagnostic/requirements-hook-logs-dev.txt +0 -2
  239. package/hooks/diagnostic/requirements-hook-logs.txt +0 -1
  240. package/hooks/diagnostic/schema.sql +0 -51
  241. package/hooks/diagnostic/test_hook_log_extractor.py +0 -1518
  242. package/hooks/diagnostic/test_hook_log_init.py +0 -227
  243. package/hooks/diagnostic/test_hook_log_stop_wrapper.py +0 -17
  244. package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +0 -94
  245. package/hooks/hooks_constants/hook_log_extractor_constants.py +0 -233
  246. package/hooks/hooks_constants/plain_language_blocker_constants.py +0 -366
  247. package/hooks/hooks_constants/test_hook_log_extractor_constants.py +0 -123
  248. package/rules/doc-prose-cuts.md +0 -58
  249. package/rules/eli11-replies.md +0 -36
  250. package/rules/opus5-communication-contract.md +0 -45
  251. package/rules/plain-language.md +0 -9
  252. /package/.agents/{skills → skills-archived}/anthropic-plan/.claude/CLAUDE.md +0 -0
  253. /package/.agents/{skills → skills-archived}/anthropic-plan/SKILL.md +0 -0
  254. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/.claude/CLAUDE.md +0 -0
  255. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/AGENTS.md +0 -0
  256. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/.claude/CLAUDE.md +0 -0
  257. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +0 -0
  258. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/__init__.py +0 -0
  259. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/validate_packet_constants.py +0 -0
  260. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/test_validate_packet.py +0 -0
  261. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/validate_packet.py +0 -0
  262. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/.claude/CLAUDE.md +0 -0
  263. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/AGENTS.md +0 -0
  264. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/README.md +0 -0
  265. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/build-prompt.md +0 -0
  266. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/reuse-audit.md +0 -0
  267. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/source-map.md +0 -0
  268. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/visual-plan.template.html +0 -0
  269. /package/.agents/{skills → skills-archived}/anthropic-plan/test_skill_contract.py +0 -0
  270. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/.claude/CLAUDE.md +0 -0
  271. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/AGENTS.md +0 -0
  272. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/plan-packet.contract.test.mjs +0 -0
  273. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/plan-packet.mjs +0 -0
  274. /package/.agents/{skills → skills-archived}/auditing-claude-config/.claude/CLAUDE.md +0 -0
  275. /package/.agents/{skills → skills-archived}/auditing-claude-config/AGENTS.md +0 -0
  276. /package/.agents/{skills → skills-archived}/auditing-claude-config/SKILL.md +0 -0
  277. /package/.agents/{skills → skills-archived}/auditing-claude-config/reference/probe-hook.md +0 -0
  278. /package/.agents/{skills → skills-archived}/beat-sheet/SKILL.md +0 -0
  279. /package/.agents/{skills → skills-archived}/beat-sheet/reference/visual-beats.md +0 -0
  280. /package/.agents/{skills → skills-archived}/bugteam/.claude/CLAUDE.md +0 -0
  281. /package/.agents/{skills → skills-archived}/bugteam/AGENTS.md +0 -0
  282. /package/.agents/{skills → skills-archived}/bugteam/CONSTRAINTS.md +0 -0
  283. /package/.agents/{skills → skills-archived}/bugteam/EXAMPLES.md +0 -0
  284. /package/.agents/{skills → skills-archived}/bugteam/PROMPTS.md +0 -0
  285. /package/.agents/{skills → skills-archived}/bugteam/SKILL.md +0 -0
  286. /package/.agents/{skills → skills-archived}/bugteam/reference/.claude/CLAUDE.md +0 -0
  287. /package/.agents/{skills → skills-archived}/bugteam/reference/AGENTS.md +0 -0
  288. /package/.agents/{skills → skills-archived}/bugteam/reference/README.md +0 -0
  289. /package/.agents/{skills → skills-archived}/bugteam/reference/audit-and-teammates.md +0 -0
  290. /package/.agents/{skills → skills-archived}/bugteam/reference/copilot-gap-analysis.md +0 -0
  291. /package/.agents/{skills → skills-archived}/bugteam/reference/design-rationale.md +0 -0
  292. /package/.agents/{skills → skills-archived}/bugteam/reference/github-pr-reviews.md +0 -0
  293. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/.claude/CLAUDE.md +0 -0
  294. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/AGENTS.md +0 -0
  295. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-assign-ids.md +0 -0
  296. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-capture-excerpts.md +0 -0
  297. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-walk-categories.md +0 -0
  298. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-write-xml.md +0 -0
  299. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-append-summary.md +0 -0
  300. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-apply-fixes.md +0 -0
  301. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-git-add-commit.md +0 -0
  302. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-git-push.md +0 -0
  303. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-post-reply.md +0 -0
  304. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-publish-summary.md +0 -0
  305. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-py-compile.md +0 -0
  306. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-read-files.md +0 -0
  307. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-resolve-thread.md +0 -0
  308. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-test-suite.md +0 -0
  309. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-violation-count.md +0 -0
  310. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-write-xml.md +0 -0
  311. /package/.agents/{skills → skills-archived}/bugteam/reference/team-setup.md +0 -0
  312. /package/.agents/{skills → skills-archived}/bugteam/scripts/.claude/CLAUDE.md +0 -0
  313. /package/.agents/{skills → skills-archived}/bugteam/scripts/AGENTS.md +0 -0
  314. /package/.agents/{skills → skills-archived}/bugteam/scripts/README.md +0 -0
  315. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_code_rules_gate.py +0 -0
  316. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_fix_hookspath.py +0 -0
  317. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_preflight.py +0 -0
  318. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/.claude/CLAUDE.md +0 -0
  319. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +0 -0
  320. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/__init__.py +0 -0
  321. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/probe_code_rules_enforcer_check_constants.py +0 -0
  322. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/reflow_skill_md_constants.py +0 -0
  323. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/windows_safe_rmtree_constants.py +0 -0
  324. /package/.agents/{skills → skills-archived}/bugteam/scripts/probe_code_rules_enforcer_check.py +0 -0
  325. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_code_rules_gate.py +0 -0
  326. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -0
  327. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_preflight.py +0 -0
  328. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_probe_code_rules_enforcer_check.py +0 -0
  329. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_windows_safe_rmtree.py +0 -0
  330. /package/.agents/{skills → skills-archived}/bugteam/scripts/windows_safe_rmtree.py +0 -0
  331. /package/.agents/{skills → skills-archived}/bugteam/sources.md +0 -0
  332. /package/.agents/{skills → skills-archived}/bugteam/test_skill_additions.py +0 -0
  333. /package/.agents/{skills → skills-archived}/closeout/SKILL.md +0 -0
  334. /package/.agents/{skills → skills-archived}/closeout/reference/handoff-prompt-template.md +0 -0
  335. /package/.agents/{skills → skills-archived}/closeout/reference/pii-redaction-checklist.md +0 -0
  336. /package/.agents/{skills → skills-archived}/codex-review/.claude/CLAUDE.md +0 -0
  337. /package/.agents/{skills → skills-archived}/codex-review/AGENTS.md +0 -0
  338. /package/.agents/{skills → skills-archived}/codex-review/SKILL.md +0 -0
  339. /package/.agents/{skills → skills-archived}/codex-review/reference/.claude/CLAUDE.md +0 -0
  340. /package/.agents/{skills → skills-archived}/codex-review/reference/AGENTS.md +0 -0
  341. /package/.agents/{skills → skills-archived}/codex-review/reference/cli-contract.md +0 -0
  342. /package/.agents/{skills → skills-archived}/codex-review/reference/loop-integration.md +0 -0
  343. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_down_classifier.py +0 -0
  344. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/.claude/CLAUDE.md +0 -0
  345. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +0 -0
  346. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/__init__.py +0 -0
  347. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/classifier_constants.py +0 -0
  348. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +0 -0
  349. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/findings_constants.py +0 -0
  350. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/run_constants.py +0 -0
  351. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_usage_probe.py +0 -0
  352. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/auth_failure_synthetic.txt +0 -0
  353. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/config_load_failure_v0.125.0.txt +0 -0
  354. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/freeform_findings_v0.144.3.txt +0 -0
  355. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/model_rejection_v0.125.0.jsonl +0 -0
  356. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/structured_findings.txt +0 -0
  357. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/success_stream_v0.144.3.jsonl +0 -0
  358. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/unknown_failure_synthetic.txt +0 -0
  359. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/usage_limit_synthetic.txt +0 -0
  360. /package/.agents/{skills → skills-archived}/codex-review/scripts/parse_codex_findings.py +0 -0
  361. /package/.agents/{skills → skills-archived}/codex-review/scripts/run_codex_review.py +0 -0
  362. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_codex_down_classifier.py +0 -0
  363. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_codex_usage_probe.py +0 -0
  364. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_parse_codex_findings.py +0 -0
  365. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_run_codex_review.py +0 -0
  366. /package/.agents/{skills → skills-archived}/comments/SKILL.md +0 -0
  367. /package/.agents/{skills → skills-archived}/condensing-instructions/SKILL.md +0 -0
  368. /package/.agents/{skills → skills-archived}/copilot-finding-triage/reference/tier-rubric.md +0 -0
  369. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/conftest.py +0 -0
  370. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/__init__.py +0 -0
  371. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/__init__.py +0 -0
  372. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/notify_ntfy_constants.py +0 -0
  373. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/notify_ntfy.py +0 -0
  374. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/test_notify_ntfy.py +0 -0
  375. /package/.agents/{skills → skills-archived}/copilot-finding-triage/templates/notification.md +0 -0
  376. /package/.agents/{skills → skills-archived}/copilot-review/.claude/CLAUDE.md +0 -0
  377. /package/.agents/{skills → skills-archived}/copilot-review/AGENTS.md +0 -0
  378. /package/.agents/{skills → skills-archived}/copilot-review/SKILL.md +0 -0
  379. /package/.agents/{skills → skills-archived}/copilot-review/templates/subagent-prompt.md +0 -0
  380. /package/.agents/{skills → skills-archived}/descriptions/SKILL.md +0 -0
  381. /package/.agents/{skills → skills-archived}/emergencies/SKILL.md +0 -0
  382. /package/.agents/{skills → skills-archived}/grokify/SKILL.md +0 -0
  383. /package/.agents/{skills → skills-archived}/grokify/capability-claims.test.mjs +0 -0
  384. /package/.agents/{skills → skills-archived}/grokify/templates/handoff-template.md +0 -0
  385. /package/.agents/{skills → skills-archived}/plan-to-pr/SKILL.md +0 -0
  386. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/final-validation-tasks.md +0 -0
  387. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/model-routing.md +0 -0
  388. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/packet-contract.md +0 -0
  389. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/packet-schema.json +0 -0
  390. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/process-inventory.md +0 -0
  391. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/review-loop.md +0 -0
  392. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/run-record.schema.json +0 -0
  393. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/self-audit-tasks.md +0 -0
  394. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/task-seeds.md +0 -0
  395. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/task-ticket.md +0 -0
  396. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/config/__init__.py +0 -0
  397. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/config/constants.py +0 -0
  398. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/create_packet.py +0 -0
  399. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/load_skill_constants.py +0 -0
  400. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_create_packet.py +0 -0
  401. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_packet.py +0 -0
  402. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_protocol.py +0 -0
  403. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_run.py +0 -0
  404. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_packet.py +0 -0
  405. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_protocol.py +0 -0
  406. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_run.py +0 -0
  407. /package/.agents/{skills → skills-archived}/plan-to-pr/test_skill_contract.py +0 -0
  408. /package/.agents/{skills → skills-archived}/plan-to-pr/test_task_ticket_contract.py +0 -0
  409. /package/.agents/{skills → skills-archived}/pr-converge/.claude/CLAUDE.md +0 -0
  410. /package/.agents/{skills → skills-archived}/pr-converge/AGENTS.md +0 -0
  411. /package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/.claude/CLAUDE.md +0 -0
  412. /package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/__init__.py +0 -0
  413. /package/.agents/{skills → skills-archived}/pr-converge/reference/.claude/CLAUDE.md +0 -0
  414. /package/.agents/{skills → skills-archived}/pr-converge/reference/AGENTS.md +0 -0
  415. /package/.agents/{skills → skills-archived}/pr-converge/reference/convergence-gates.md +0 -0
  416. /package/.agents/{skills → skills-archived}/pr-converge/reference/examples.md +0 -0
  417. /package/.agents/{skills → skills-archived}/pr-converge/reference/fix-protocol.md +0 -0
  418. /package/.agents/{skills → skills-archived}/pr-converge/reference/ground-rules.md +0 -0
  419. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/.claude/CLAUDE.md +0 -0
  420. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/AGENTS.md +0 -0
  421. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-post-replies.md +0 -0
  422. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-publish-summary.md +0 -0
  423. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-push.md +0 -0
  424. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-read-filelines.md +0 -0
  425. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-reset-state.md +0 -0
  426. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-resolve-threads.md +0 -0
  427. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-spawn-clean-coder.md +0 -0
  428. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-stage-commit.md +0 -0
  429. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-trigger-bugbot.md +0 -0
  430. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-write-test.md +0 -0
  431. /package/.agents/{skills → skills-archived}/pr-converge/reference/progress-checklist.md +0 -0
  432. /package/.agents/{skills → skills-archived}/pr-converge/reference/state-schema.md +0 -0
  433. /package/.agents/{skills → skills-archived}/pr-converge/reference/stop-conditions.md +0 -0
  434. /package/.agents/{skills → skills-archived}/pr-converge/scripts/.claude/CLAUDE.md +0 -0
  435. /package/.agents/{skills → skills-archived}/pr-converge/scripts/AGENTS.md +0 -0
  436. /package/.agents/{skills → skills-archived}/pr-converge/scripts/README.md +0 -0
  437. /package/.agents/{skills → skills-archived}/pr-converge/scripts/_pr_converge_path_setup.py +0 -0
  438. /package/.agents/{skills → skills-archived}/pr-converge/scripts/caller-window-pid.ps1 +0 -0
  439. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_bugbot_ci.py +0 -0
  440. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence.py +0 -0
  441. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_availability.py +0 -0
  442. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_gates.py +0 -0
  443. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_thread_gates.py +0 -0
  444. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_pending_reviews.py +0 -0
  445. /package/.agents/{skills → skills-archived}/pr-converge/scripts/conftest.py +0 -0
  446. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue-caller.cmd +0 -0
  447. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue-stop-others.ps1 +0 -0
  448. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue.ahk +0 -0
  449. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue.cmd +0 -0
  450. /package/.agents/{skills → skills-archived}/pr-converge/scripts/fetch_copilot_reviews.py +0 -0
  451. /package/.agents/{skills → skills-archived}/pr-converge/scripts/post_fix_reply.py +0 -0
  452. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/.claude/CLAUDE.md +0 -0
  453. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +0 -0
  454. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/__init__.py +0 -0
  455. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +0 -0
  456. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/pr_converge_constants.py +0 -0
  457. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/reflow_skill_md_constants.py +0 -0
  458. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test__pr_converge_path_setup.py +0 -0
  459. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_bugbot_ci.py +0 -0
  460. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence.py +0 -0
  461. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_availability.py +0 -0
  462. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_codex.py +0 -0
  463. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_contract.py +0 -0
  464. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_fixture.py +0 -0
  465. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_gates.py +0 -0
  466. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_thread_gates.py +0 -0
  467. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_cursor_agents_continue.py +0 -0
  468. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_reflow_skill_md.py +0 -0
  469. /package/.agents/{skills → skills-archived}/pr-converge/test_step5_host_branch.py +0 -0
  470. /package/.agents/{skills → skills-archived}/pr-converge/workflows/.claude/CLAUDE.md +0 -0
  471. /package/.agents/{skills → skills-archived}/pr-converge/workflows/AGENTS.md +0 -0
  472. /package/.agents/{skills → skills-archived}/pr-fix-protocol/SKILL.md +0 -0
  473. /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/SKILL.md +0 -0
  474. /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/reference/identity-and-hooks.md +0 -0
  475. /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/reference/substitution-matrix.md +0 -0
  476. /package/.agents/{skills → skills-archived}/pr-loop-lifecycle/SKILL.md +0 -0
  477. /package/.agents/{skills → skills-archived}/pr-loop-lifecycle/reference/teardown-publish-permissions.md +0 -0
  478. /package/.agents/{skills → skills-archived}/recall/.claude/CLAUDE.md +0 -0
  479. /package/.agents/{skills → skills-archived}/recall/AGENTS.md +0 -0
  480. /package/.agents/{skills → skills-archived}/recall/SKILL.md +0 -0
  481. /package/.agents/{skills → skills-archived}/release-notes-html/SKILL.md +0 -0
  482. /package/.agents/{skills → skills-archived}/remember/.claude/CLAUDE.md +0 -0
  483. /package/.agents/{skills → skills-archived}/remember/AGENTS.md +0 -0
  484. /package/.agents/{skills → skills-archived}/remember/SKILL.md +0 -0
  485. /package/.agents/{skills → skills-archived}/reviewer-gates/SKILL.md +0 -0
  486. /package/.agents/{skills → skills-archived}/reviews/SKILL.md +0 -0
  487. /package/.agents/{skills → skills-archived}/show/SKILL.md +0 -0
  488. /package/.agents/{skills → skills-archived}/show/references/accessibility.md +0 -0
  489. /package/.agents/{skills → skills-archived}/show/references/art.md +0 -0
  490. /package/.agents/{skills → skills-archived}/show/references/charts.md +0 -0
  491. /package/.agents/{skills → skills-archived}/show/references/core-design.md +0 -0
  492. /package/.agents/{skills → skills-archived}/show/references/erds.md +0 -0
  493. /package/.agents/{skills → skills-archived}/show/references/flowcharts.md +0 -0
  494. /package/.agents/{skills → skills-archived}/show/references/host-and-html.md +0 -0
  495. /package/.agents/{skills → skills-archived}/show/references/illustrative-diagrams.md +0 -0
  496. /package/.agents/{skills → skills-archived}/show/references/interaction.md +0 -0
  497. /package/.agents/{skills → skills-archived}/show/references/mockups.md +0 -0
  498. /package/.agents/{skills → skills-archived}/show/references/quality-gates.md +0 -0
  499. /package/.agents/{skills → skills-archived}/show/references/structural-diagrams.md +0 -0
  500. /package/.agents/{skills → skills-archived}/show/references/subject-inventory.md +0 -0
  501. /package/.agents/{skills → skills-archived}/show/references/svg-contract.md +0 -0
  502. /package/.agents/{skills → skills-archived}/show/routing.yaml +0 -0
  503. /package/.agents/{skills → skills-archived}/show/samples/pr1262-v2.svg +0 -0
  504. /package/.agents/{skills → skills-archived}/show/scripts/README.md +0 -0
  505. /package/.agents/{skills → skills-archived}/show/scripts/validate-artifact.py +0 -0
  506. /package/.agents/{skills → skills-archived}/show/scripts/validate-package.py +0 -0
  507. /package/.agents/{skills → skills-archived}/show/templates/html-widget.html +0 -0
  508. /package/.agents/{skills → skills-archived}/show/templates/svg-base.svg +0 -0
  509. /package/.agents/{skills → skills-archived}/show/tests/fixtures/css-var.svg +0 -0
  510. /package/.agents/{skills → skills-archived}/show/tests/fixtures/dead-ref.svg +0 -0
  511. /package/.agents/{skills → skills-archived}/show/tests/fixtures/filled-glyph.svg +0 -0
  512. /package/.agents/{skills → skills-archived}/show/tests/fixtures/inherited-fill.svg +0 -0
  513. /package/.agents/{skills → skills-archived}/show/tests/fixtures/invalid.svg +0 -0
  514. /package/.agents/{skills → skills-archived}/show/tests/fixtures/large-canvas.svg +0 -0
  515. /package/.agents/{skills → skills-archived}/show/tests/fixtures/unfilled-connector.svg +0 -0
  516. /package/.agents/{skills → skills-archived}/show/tests/fixtures/valid.html +0 -0
  517. /package/.agents/{skills → skills-archived}/show/tests/test_validate-artifact.py +0 -0
  518. /package/.agents/{skills → skills-archived}/show/tests/test_validators.py +0 -0
  519. /package/.agents/{skills → skills-archived}/show/workflows/create-visual.md +0 -0
  520. /package/.agents/{skills → skills-archived}/show/workflows/review-visual.md +0 -0
  521. /package/.agents/{skills → skills-archived}/split-pr/SKILL.md +0 -0
  522. /package/.agents/{skills → skills-archived}/split-pr/reference/path-layers.md +0 -0
  523. /package/.agents/{skills → skills-archived}/split-pr/reference/split-further-loop.md +0 -0
  524. /package/.agents/{skills → skills-archived}/split-pr/reference/splitting-principles.md +0 -0
  525. /package/.agents/{skills → skills-archived}/split-pr/scripts/analyze_pr.py +0 -0
  526. /package/.agents/{skills → skills-archived}/split-pr/scripts/categorize_files.py +0 -0
  527. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/__init__.py +0 -0
  528. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/dependency_constants.py +0 -0
  529. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/git_operations_constants.py +0 -0
  530. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/packing_constants.py +0 -0
  531. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/plan_constants.py +0 -0
  532. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/split_pr_constants.py +0 -0
  533. /package/.agents/{skills → skills-archived}/split-pr/scripts/execute_split_slices.py +0 -0
  534. /package/.agents/{skills → skills-archived}/split-pr/scripts/pack_files_into_slices.py +0 -0
  535. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_dependency_graph.py +0 -0
  536. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_git_operations.py +0 -0
  537. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_layer_order.py +0 -0
  538. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_paginate.py +0 -0
  539. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_process_runner.py +0 -0
  540. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_script_types.py +0 -0
  541. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_title.py +0 -0
  542. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_analyze_pr.py +0 -0
  543. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_categorize_files.py +0 -0
  544. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_categorize_files_packing.py +0 -0
  545. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_execute_split_slices.py +0 -0
  546. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_dependency_graph.py +0 -0
  547. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_git_operations.py +0 -0
  548. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_layer_order.py +0 -0
  549. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_paginate.py +0 -0
  550. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_script_types.py +0 -0
  551. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_title.py +0 -0
  552. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_dependency_graph.py +0 -0
  553. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan.py +0 -0
  554. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan_contract.py +0 -0
  555. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan_path_normalization.py +0 -0
  556. /package/.agents/{skills → skills-archived}/split-pr/scripts/verify_dependency_graph.py +0 -0
  557. /package/.agents/{skills → skills-archived}/split-pr/scripts/verify_plan.py +0 -0
@@ -1,875 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Extract hook-firing records from per-session JSONL transcripts into Neon.
3
-
4
- Reads JSONL transcripts at ``PROJECTS_TRANSCRIPT_ROOT`` and ingests only
5
- ``attachment`` records whose inner ``attachment.type`` is one of the
6
- five variants enumerated in ``OUTCOME_BY_ATTACHMENT_TYPE``
7
- (``hook_success``, ``hook_blocking_error``, ``hook_non_blocking_error``,
8
- ``hook_system_message``, ``hook_additional_context``). Unknown
9
- ``hook_``-prefixed variants are skipped until
10
- ``OUTCOME_BY_ATTACHMENT_TYPE`` is extended to cover them. Each ingested
11
- record becomes one row in the ``hook_events`` table. Idempotence is
12
- enforced at the database layer via a UNIQUE constraint on
13
- ``(source_jsonl_path, source_line_number)`` combined with
14
- ``ON CONFLICT DO NOTHING``. Per-file byte offsets in
15
- ``OFFSET_STATE_FILE`` skip re-reading unchanged bytes.
16
-
17
- Offline graceful behavior: ``psycopg.OperationalError`` or any
18
- connect-time failure appends one ISO-8601 line to
19
- ``OFFLINE_WARNING_LOG`` and exits 0, so the Stop hook never blocks
20
- session end on a missing network.
21
- """
22
-
23
- from __future__ import annotations
24
-
25
- import contextlib
26
- import datetime
27
- import errno
28
- import glob
29
- import io
30
- import json
31
- import os
32
- import re
33
- import sys
34
- import tempfile
35
- import time
36
- from pathlib import Path
37
- from typing import IO, Iterator, Optional, Sequence
38
-
39
- if os.name == "nt":
40
- try:
41
- import msvcrt
42
- except ImportError:
43
- msvcrt = None
44
- fcntl = None
45
- else:
46
- try:
47
- import fcntl
48
- except ImportError:
49
- fcntl = None
50
- msvcrt = None
51
-
52
- if str(Path(__file__).resolve().parent.parent) not in sys.path:
53
- sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
54
-
55
- try:
56
- import psycopg
57
- except ImportError:
58
- psycopg = None
59
-
60
- from hooks_constants.hook_log_extractor_constants import (
61
- ATTACHMENT_TYPE_HOOK_ADDITIONAL_CONTEXT,
62
- ATTACHMENT_TYPE_HOOK_BLOCKING_ERROR,
63
- ATTACHMENT_TYPE_HOOK_SUCCESS,
64
- ATTACHMENT_TYPE_HOOK_SYSTEM_MESSAGE,
65
- ATTACHMENT_TYPE_PREFIX,
66
- BYTE_OFFSET_KEY,
67
- CATEGORY_PATH_MINIMUM_PARTS,
68
- COMMAND_EXCERPT_MAX_CHARACTERS,
69
- CONNECT_TIMEOUT_SECONDS,
70
- EMPTY_STRING,
71
- EXIT_CODE_EXTRACTOR_ENVIRONMENT_MISSING,
72
- EXIT_CODE_SUCCESS,
73
- EXIT_CODE_UNKNOWN_QUERY,
74
- HOOK_CATEGORY_UNCATEGORIZED,
75
- HOOK_EVENTS_INSERT_SQL,
76
- HOOK_EVENTS_TRUNCATE_SQL,
77
- HOOK_NAME_TOOL_SEPARATOR,
78
- HOOKS_DIRECTORY_TOKEN,
79
- INSERT_BATCH_SIZE,
80
- INVALID_QUERY_NAME_MESSAGE_PREFIX,
81
- JSONL_FILE_GLOB,
82
- KNOWN_HOOK_CATEGORIES,
83
- LEGACY_OFFSETS_FORMAT_WARNING_LABEL,
84
- LINE_NUMBER_KEY,
85
- LOCK_MAXIMUM_RETRY_COUNT,
86
- LOCK_RETRY_SLEEP_SECONDS,
87
- MISSING_NEON_DATABASE_URL_WARNING_LABEL,
88
- MISSING_PSYCOPG_WARNING_LABEL,
89
- NEON_DATABASE_URL_ENVIRONMENT_VARIABLE,
90
- NEWLINE_JOINER,
91
- OFFLINE_WARNING_LOG,
92
- OFFSETS_JSON_INDENT,
93
- OUTCOME_BY_ATTACHMENT_TYPE,
94
- QUERIES_DIRECTORY_NAME,
95
- QUERY_NAME_PATTERN,
96
- QUERY_NO_ROWS_RETURNED_MESSAGE,
97
- SCRIPT_PATH_PYTHON_PREFIXES,
98
- SQL_FILE_EXTENSION,
99
- STDERR_EXCERPT_MAX_CHARACTERS,
100
- STDOUT_EXCERPT_MAX_CHARACTERS,
101
- SUMMARY_COLUMN_HEADINGS,
102
- SUMMARY_ZERO_BLOCKED_EVENTS_MESSAGE,
103
- SUMMARY_TABLE_COLUMN_GAP,
104
- TOP_BLOCKED_COMMAND_PREVIEW_MAX_CHARACTERS,
105
- TOP_BLOCKERS_LAST_24_HOURS_SQL,
106
- TOP_LEVEL_ATTACHMENT_TYPE,
107
- UNKNOWN_QUERY_MESSAGE_PREFIX,
108
- )
109
-
110
-
111
- class MissingNeonDatabaseUrlError(RuntimeError):
112
- """Raised when the Neon connection URL environment variable is unset."""
113
-
114
-
115
- class MissingPsycopgDependencyError(RuntimeError):
116
- """Raised when the psycopg driver is not installed in the interpreter."""
117
-
118
-
119
- def derive_category(script_path: Optional[str]) -> str:
120
- """Return the category parent-directory name for a script path."""
121
- if not script_path:
122
- return HOOK_CATEGORY_UNCATEGORIZED
123
- normalized_path = script_path.replace("\\", "/")
124
- for each_prefix in SCRIPT_PATH_PYTHON_PREFIXES:
125
- if normalized_path.startswith(each_prefix):
126
- normalized_path = normalized_path[len(each_prefix) :]
127
- break
128
- hooks_directory_token_index = normalized_path.rfind(HOOKS_DIRECTORY_TOKEN)
129
- if hooks_directory_token_index == -1:
130
- return HOOK_CATEGORY_UNCATEGORIZED
131
- remainder_after_hooks_segment = normalized_path[
132
- hooks_directory_token_index + len(HOOKS_DIRECTORY_TOKEN) :
133
- ]
134
- all_remainder_parts = remainder_after_hooks_segment.split("/")
135
- if len(all_remainder_parts) < CATEGORY_PATH_MINIMUM_PARTS:
136
- return HOOK_CATEGORY_UNCATEGORIZED
137
- candidate_category = all_remainder_parts[0]
138
- if candidate_category in KNOWN_HOOK_CATEGORIES:
139
- return candidate_category
140
- return HOOK_CATEGORY_UNCATEGORIZED
141
-
142
-
143
- def derive_outcome(attachment_type: str) -> str:
144
- """Map an attachment.type value to its outcome label."""
145
- return OUTCOME_BY_ATTACHMENT_TYPE[attachment_type]
146
-
147
-
148
- def extract_script_path(attachment: dict[str, object]) -> Optional[str]:
149
- """Return the script path embedded in a hook attachment, if any."""
150
- attachment_type = attachment.get("type", EMPTY_STRING)
151
- if attachment_type == ATTACHMENT_TYPE_HOOK_SUCCESS:
152
- return _strip_python_prefix(attachment.get("command"))
153
- if attachment_type == ATTACHMENT_TYPE_HOOK_BLOCKING_ERROR:
154
- blocking_error_block = attachment.get("blockingError") or {}
155
- if isinstance(blocking_error_block, dict):
156
- return _strip_python_prefix(blocking_error_block.get("command"))
157
- return None
158
-
159
-
160
- def _strip_python_prefix(command_string: Optional[str]) -> Optional[str]:
161
- if not command_string:
162
- return None
163
- for each_prefix in SCRIPT_PATH_PYTHON_PREFIXES:
164
- if command_string.startswith(each_prefix):
165
- return command_string[len(each_prefix) :]
166
- return command_string
167
-
168
-
169
- def extract_tool_name(hook_name: Optional[str]) -> Optional[str]:
170
- """Return the tool name after the colon in a hook name, if present."""
171
- if not hook_name:
172
- return None
173
- if HOOK_NAME_TOOL_SEPARATOR not in hook_name:
174
- return None
175
- return hook_name.split(HOOK_NAME_TOOL_SEPARATOR, 1)[1]
176
-
177
-
178
- def truncate_command_excerpt(command_text: Optional[str]) -> Optional[str]:
179
- """Truncate a command string to the configured excerpt budget."""
180
- return _truncate_to_length(command_text, COMMAND_EXCERPT_MAX_CHARACTERS)
181
-
182
-
183
- def truncate_stdout_excerpt(stdout_text: Optional[str]) -> Optional[str]:
184
- """Truncate a stdout string to the configured excerpt budget."""
185
- return _truncate_to_length(stdout_text, STDOUT_EXCERPT_MAX_CHARACTERS)
186
-
187
-
188
- def truncate_stderr_excerpt(stderr_text: Optional[str]) -> Optional[str]:
189
- """Truncate a stderr string to the configured excerpt budget."""
190
- return _truncate_to_length(stderr_text, STDERR_EXCERPT_MAX_CHARACTERS)
191
-
192
-
193
- def _truncate_to_length(
194
- text_or_none: Optional[str], maximum_length: int
195
- ) -> Optional[str]:
196
- if text_or_none is None:
197
- return None
198
- if len(text_or_none) <= maximum_length:
199
- return text_or_none
200
- return text_or_none[:maximum_length]
201
-
202
-
203
- def _normalize_content_to_text(content_or_none: object) -> Optional[str]:
204
- if content_or_none is None:
205
- return None
206
- if isinstance(content_or_none, str):
207
- return content_or_none
208
- if isinstance(content_or_none, list):
209
- all_string_items = [
210
- each_entry for each_entry in content_or_none if isinstance(each_entry, str)
211
- ]
212
- return NEWLINE_JOINER.join(all_string_items) if all_string_items else None
213
- return str(content_or_none)
214
-
215
-
216
- def build_row_from_attachment(
217
- parsed_record: dict[str, object],
218
- source_jsonl_path: str,
219
- source_line_number: int,
220
- ) -> dict[str, object]:
221
- """Build a hook_events row dict from one parsed JSONL record."""
222
- attachment_block = parsed_record.get("attachment") or {}
223
- attachment_type = attachment_block.get("type", EMPTY_STRING)
224
- outcome_label = derive_outcome(attachment_type)
225
- script_path_or_none = extract_script_path(attachment_block)
226
- hook_category_label = derive_category(script_path_or_none)
227
- hook_name_string = attachment_block.get("hookName")
228
- hook_event_string = attachment_block.get("hookEvent", EMPTY_STRING)
229
- tool_use_id_or_none = attachment_block.get("toolUseID")
230
- tool_name_or_none = extract_tool_name(hook_name_string)
231
-
232
- command_text_or_none = attachment_block.get("command")
233
- stdout_text_or_none: Optional[str] = attachment_block.get("stdout")
234
- stderr_text_or_none: Optional[str] = attachment_block.get("stderr")
235
- exit_code_or_none = attachment_block.get("exitCode")
236
- duration_milliseconds_or_none = attachment_block.get("durationMs")
237
-
238
- if attachment_type == ATTACHMENT_TYPE_HOOK_BLOCKING_ERROR:
239
- blocking_error_block = attachment_block.get("blockingError") or {}
240
- if isinstance(blocking_error_block, dict):
241
- command_text_or_none = blocking_error_block.get("command")
242
- blocking_error_message = blocking_error_block.get("blockingError")
243
- if blocking_error_message:
244
- stderr_text_or_none = blocking_error_message
245
- elif attachment_type == ATTACHMENT_TYPE_HOOK_SYSTEM_MESSAGE:
246
- stdout_text_or_none = _normalize_content_to_text(
247
- attachment_block.get("content")
248
- )
249
- elif attachment_type == ATTACHMENT_TYPE_HOOK_ADDITIONAL_CONTEXT:
250
- stdout_text_or_none = _normalize_content_to_text(
251
- attachment_block.get("content")
252
- )
253
-
254
- return {
255
- "event_timestamp": parsed_record.get("timestamp"),
256
- "session_id": parsed_record.get("sessionId", EMPTY_STRING),
257
- "cwd": parsed_record.get("cwd"),
258
- "git_branch": parsed_record.get("gitBranch"),
259
- "hook_event": hook_event_string,
260
- "hook_name": hook_name_string or EMPTY_STRING,
261
- "hook_category": hook_category_label,
262
- "script_path": script_path_or_none,
263
- "tool_name": tool_name_or_none,
264
- "tool_use_id": tool_use_id_or_none,
265
- "outcome": outcome_label,
266
- "exit_code": exit_code_or_none,
267
- "duration_ms": duration_milliseconds_or_none,
268
- "command_excerpt": truncate_command_excerpt(command_text_or_none),
269
- "stdout_excerpt": truncate_stdout_excerpt(stdout_text_or_none),
270
- "stderr_excerpt": truncate_stderr_excerpt(stderr_text_or_none),
271
- "source_jsonl_path": source_jsonl_path,
272
- "source_line_number": source_line_number,
273
- }
274
-
275
-
276
- class AttachmentRecordIterator:
277
- """Iterates hook attachment records and tracks bytes actually consumed.
278
-
279
- ``final_line_number`` reflects the number of lines read from the file
280
- (including malformed and non-attachment lines), not just the line
281
- number of the last yielded record. ``final_byte_offset`` reflects the
282
- byte position after the last successfully-read line (or
283
- ``start_offset`` when the file did not exist). ``drained`` is True
284
- once iteration reached EOF. Callers persist ``final_byte_offset``
285
- and ``final_line_number`` whenever ``drained`` is True so resumption
286
- starts from the exact position after the last bytes the iterator
287
- consumed.
288
- """
289
-
290
- def __init__(
291
- self,
292
- jsonl_file_path: str,
293
- start_offset: int,
294
- start_line_number: int = 0,
295
- ) -> None:
296
- self._jsonl_file_path = jsonl_file_path
297
- self._start_offset = start_offset
298
- self._start_line_number = start_line_number
299
- self.final_line_number = start_line_number
300
- self.final_byte_offset = start_offset
301
- self.drained = False
302
-
303
- def __iter__(self) -> Iterator[tuple[dict[str, object], int, int]]:
304
- try:
305
- jsonl_file_handle = io.open(self._jsonl_file_path, "rb") # noqa: SIM115
306
- except (FileNotFoundError, OSError):
307
- self.final_line_number = self._start_line_number
308
- self.final_byte_offset = self._start_offset
309
- self.drained = True
310
- return
311
- with jsonl_file_handle:
312
- if self._start_offset > 0:
313
- jsonl_file_handle.seek(self._start_offset)
314
- current_line_number = self._start_line_number
315
- current_byte_offset = jsonl_file_handle.tell()
316
- self.final_byte_offset = current_byte_offset
317
- while True:
318
- raw_bytes = jsonl_file_handle.readline()
319
- if not raw_bytes:
320
- self.final_line_number = current_line_number
321
- self.final_byte_offset = current_byte_offset
322
- self.drained = True
323
- return
324
- current_line_number += 1
325
- current_byte_offset += len(raw_bytes)
326
- self.final_line_number = current_line_number
327
- self.final_byte_offset = current_byte_offset
328
- try:
329
- parsed_record = json.loads(raw_bytes.decode("utf-8"))
330
- except (UnicodeDecodeError, json.JSONDecodeError):
331
- continue
332
- if not isinstance(parsed_record, dict):
333
- continue
334
- if parsed_record.get("type") != TOP_LEVEL_ATTACHMENT_TYPE:
335
- continue
336
- attachment_block = parsed_record.get("attachment") or {}
337
- if not isinstance(attachment_block, dict):
338
- continue
339
- attachment_type = attachment_block.get("type", EMPTY_STRING)
340
- if not isinstance(attachment_type, str):
341
- continue
342
- if not attachment_type.startswith(ATTACHMENT_TYPE_PREFIX):
343
- continue
344
- if attachment_type not in OUTCOME_BY_ATTACHMENT_TYPE:
345
- continue
346
- yield parsed_record, current_line_number, current_byte_offset
347
-
348
-
349
- def iter_attachment_records_from_file(
350
- jsonl_file_path: str,
351
- start_offset: int,
352
- start_line_number: int = 0,
353
- ) -> AttachmentRecordIterator:
354
- """Return an iterator over hook attachment records in a JSONL file.
355
-
356
- The returned object supports iteration and exposes
357
- ``final_line_number`` after iteration completes. ``final_line_number``
358
- is the total number of lines consumed (malformed and non-attachment
359
- lines included), which differs from the line number of the last
360
- yielded record when non-attachment lines trail the last attachment.
361
- """
362
- return AttachmentRecordIterator(
363
- jsonl_file_path=jsonl_file_path,
364
- start_offset=start_offset,
365
- start_line_number=start_line_number,
366
- )
367
-
368
-
369
- def load_offsets(state_file_path: str) -> dict[str, dict[str, int]]:
370
- """Load per-file ``{byte_offset, line_number}`` entries from disk.
371
-
372
- Returns an empty dict when the state file is missing or contains
373
- malformed JSON. Legacy bare-integer entries trigger a single
374
- offline-warning line and are treated as invalid so the caller
375
- re-extracts from the start of each file.
376
- """
377
- if not os.path.exists(state_file_path):
378
- return {}
379
- try:
380
- with io.open(state_file_path, "r", encoding="utf-8") as state_file_handle:
381
- loaded_content = json.load(state_file_handle)
382
- except json.JSONDecodeError:
383
- return {}
384
- if not isinstance(loaded_content, dict):
385
- return {}
386
- migrated_offsets: dict[str, dict[str, int]] = {}
387
- has_legacy_entries = False
388
- for each_path, each_entry in loaded_content.items():
389
- path_string = str(each_path)
390
- if isinstance(each_entry, dict):
391
- byte_offset_value = each_entry.get(BYTE_OFFSET_KEY)
392
- line_number_value = each_entry.get(LINE_NUMBER_KEY)
393
- if isinstance(byte_offset_value, int) and isinstance(
394
- line_number_value, int
395
- ):
396
- migrated_offsets[path_string] = {
397
- BYTE_OFFSET_KEY: byte_offset_value,
398
- LINE_NUMBER_KEY: line_number_value,
399
- }
400
- continue
401
- has_legacy_entries = True
402
- if has_legacy_entries:
403
- _append_legacy_offsets_warning_line()
404
- return migrated_offsets
405
-
406
-
407
- def save_offsets(
408
- state_file_path: str,
409
- offset_by_jsonl_path: dict[str, dict[str, int]],
410
- ) -> None:
411
- """Persist per-file offset entries atomically via tempfile + os.replace."""
412
- state_file_parent = os.path.dirname(state_file_path)
413
- if state_file_parent:
414
- os.makedirs(state_file_parent, exist_ok=True)
415
- with tempfile.NamedTemporaryFile(
416
- mode="w",
417
- encoding="utf-8",
418
- dir=state_file_parent or None,
419
- delete=False,
420
- ) as temporary_file_handle:
421
- temporary_file_path = temporary_file_handle.name
422
- try:
423
- json.dump(
424
- offset_by_jsonl_path,
425
- temporary_file_handle,
426
- indent=OFFSETS_JSON_INDENT,
427
- sort_keys=True,
428
- )
429
- temporary_file_handle.flush()
430
- os.fsync(temporary_file_handle.fileno())
431
- except Exception:
432
- temporary_file_handle.close()
433
- try:
434
- os.unlink(temporary_file_path)
435
- except OSError:
436
- pass
437
- raise
438
- try:
439
- os.replace(temporary_file_path, state_file_path)
440
- except Exception:
441
- try:
442
- os.unlink(temporary_file_path)
443
- except OSError:
444
- pass
445
- raise
446
-
447
-
448
- @contextlib.contextmanager
449
- def _acquire_offsets_lock(state_file_path: str) -> Iterator[None]:
450
- """Hold a cross-platform advisory lock around an offsets read-modify-write.
451
-
452
- Serializes concurrent extractor runs so two Claude Code sessions
453
- closing at once cannot clobber each other's offset updates. Uses
454
- ``msvcrt.locking`` on Windows and ``fcntl.flock`` on POSIX; falls
455
- back to no locking on platforms where neither module is available.
456
-
457
- The sidecar path ``state_file_path + ".lock"`` is intentional,
458
- permanent infrastructure. It is a byte-range-lockable companion to
459
- the offsets file and is deliberately never unlinked. Attempting to
460
- unlink it after release would open a TOCTOU window on Windows,
461
- where another process may still hold it open. The stable sidecar
462
- is the safer choice; reused on every run, its presence in the
463
- state directory is expected and carries no other meaning.
464
- """
465
- lock_file_path = state_file_path + ".lock"
466
- lock_parent_directory = os.path.dirname(lock_file_path)
467
- if lock_parent_directory:
468
- os.makedirs(lock_parent_directory, exist_ok=True)
469
- with io.open(lock_file_path, "a+", encoding="utf-8") as lock_file_handle:
470
- _lock_file_handle_blocking(lock_file_handle)
471
- try:
472
- yield
473
- finally:
474
- _unlock_file_handle(lock_file_handle)
475
-
476
-
477
- def _lock_file_handle_blocking(lock_file_handle: IO[str]) -> None:
478
- """Acquire an exclusive byte-range lock with a bounded retry budget.
479
-
480
- Both the Windows (``msvcrt.locking``) and POSIX (``fcntl.flock``)
481
- branches deliberately fail fast: Windows uses ``LK_NBLCK`` instead
482
- of ``LK_LOCK`` so the kernel never blocks ~10s internally, and
483
- POSIX pairs ``LOCK_EX`` with ``LOCK_NB`` so ``EWOULDBLOCK`` bubbles
484
- up immediately. The Python ``time.sleep(LOCK_RETRY_SLEEP_SECONDS)``
485
- between attempts is the sole pacing mechanism, keeping the total
486
- retry budget within the intended ``LOCK_MAXIMUM_RETRY_COUNT *
487
- LOCK_RETRY_SLEEP_SECONDS`` window so the caller never exceeds the
488
- 30s Stop hook timeout under sustained contention.
489
- """
490
- if msvcrt is not None:
491
- lock_byte_count = 1
492
- for _each_attempt_index in range(LOCK_MAXIMUM_RETRY_COUNT):
493
- try:
494
- msvcrt.locking(
495
- lock_file_handle.fileno(), msvcrt.LK_NBLCK, lock_byte_count
496
- )
497
- return
498
- except OSError as lock_exception:
499
- if lock_exception.errno != errno.EACCES:
500
- raise
501
- time.sleep(LOCK_RETRY_SLEEP_SECONDS)
502
- raise OSError(
503
- errno.EACCES,
504
- "offsets lock retry budget exhausted",
505
- )
506
- if fcntl is not None:
507
- for _each_attempt_index in range(LOCK_MAXIMUM_RETRY_COUNT):
508
- try:
509
- fcntl.flock(
510
- lock_file_handle.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB
511
- )
512
- return
513
- except OSError as lock_exception:
514
- if lock_exception.errno not in (errno.EAGAIN, errno.EWOULDBLOCK):
515
- raise
516
- time.sleep(LOCK_RETRY_SLEEP_SECONDS)
517
- raise OSError(
518
- errno.EWOULDBLOCK,
519
- "offsets lock retry budget exhausted",
520
- )
521
-
522
-
523
- def _unlock_file_handle(lock_file_handle: IO[str]) -> None:
524
- if msvcrt is not None:
525
- lock_byte_count = 1
526
- try:
527
- msvcrt.locking(
528
- lock_file_handle.fileno(), msvcrt.LK_UNLCK, lock_byte_count
529
- )
530
- except OSError:
531
- return
532
- return
533
- if fcntl is not None:
534
- fcntl.flock(lock_file_handle.fileno(), fcntl.LOCK_UN)
535
-
536
-
537
- def is_operational_error(exception_instance: BaseException) -> bool:
538
- """Return True when an exception should trigger the offline fallback."""
539
- if isinstance(
540
- exception_instance,
541
- (MissingNeonDatabaseUrlError, MissingPsycopgDependencyError),
542
- ):
543
- return True
544
- class_name = type(exception_instance).__name__
545
- return class_name in {"OperationalError", "InterfaceError", "TimeoutError"}
546
-
547
-
548
- def connect_to_neon() -> object:
549
- """Open a psycopg connection using the Neon database URL env var.
550
-
551
- Raises ``MissingNeonDatabaseUrlError`` when the URL env var is unset
552
- and ``MissingPsycopgDependencyError`` when psycopg is not installed.
553
- Both are treated as offline by ``is_operational_error`` so the Stop
554
- hook never blocks session end on a missing environment.
555
- """
556
- if psycopg is None:
557
- raise MissingPsycopgDependencyError(MISSING_PSYCOPG_WARNING_LABEL)
558
- raw_database_url = os.environ.get(NEON_DATABASE_URL_ENVIRONMENT_VARIABLE)
559
- database_url = raw_database_url.strip() if raw_database_url is not None else None
560
- if not database_url:
561
- raise MissingNeonDatabaseUrlError(MISSING_NEON_DATABASE_URL_WARNING_LABEL)
562
- return psycopg.connect(database_url, connect_timeout=CONNECT_TIMEOUT_SECONDS)
563
-
564
-
565
- def insert_rows_batch(
566
- neon_connection: object,
567
- all_rows: Sequence[dict[str, object]],
568
- ) -> None:
569
- """Insert a batch of hook_events rows with ON CONFLICT DO NOTHING."""
570
- if not all_rows:
571
- return
572
- with neon_connection.cursor() as neon_cursor:
573
- neon_cursor.executemany(HOOK_EVENTS_INSERT_SQL, list(all_rows))
574
- neon_connection.commit()
575
-
576
-
577
- def _append_offline_warning_line(exception_instance: BaseException) -> None:
578
- """Append an offline-marker line to the warning log; swallow disk errors.
579
-
580
- The Stop hook contract requires that the offline-graceful path
581
- always exits with ``EXIT_CODE_EXTRACTOR_ENVIRONMENT_MISSING`` so
582
- session shutdown never stalls on a failed extractor. A read-only
583
- filesystem, missing parent path, or EACCES on the warning log file
584
- itself must not propagate to the caller.
585
- """
586
- try:
587
- warning_log_parent = os.path.dirname(OFFLINE_WARNING_LOG)
588
- if warning_log_parent:
589
- os.makedirs(warning_log_parent, exist_ok=True)
590
- timestamp_iso = datetime.datetime.now(datetime.timezone.utc).isoformat()
591
- exception_class_name = type(exception_instance).__name__
592
- warning_line_text = f"{timestamp_iso}\toffline\t{exception_class_name}"
593
- with io.open(OFFLINE_WARNING_LOG, "a", encoding="utf-8") as warning_log_handle:
594
- warning_log_handle.write(warning_line_text + "\n")
595
- except OSError:
596
- return
597
-
598
-
599
- def _append_legacy_offsets_warning_line() -> None:
600
- try:
601
- warning_log_parent = os.path.dirname(OFFLINE_WARNING_LOG)
602
- if warning_log_parent:
603
- os.makedirs(warning_log_parent, exist_ok=True)
604
- timestamp_iso = datetime.datetime.now(datetime.timezone.utc).isoformat()
605
- warning_line_text = (
606
- f"{timestamp_iso}\tmigration\t{LEGACY_OFFSETS_FORMAT_WARNING_LABEL}"
607
- )
608
- with io.open(OFFLINE_WARNING_LOG, "a", encoding="utf-8") as warning_log_handle:
609
- warning_log_handle.write(warning_line_text + "\n")
610
- except OSError:
611
- return
612
-
613
-
614
- def run_full_extraction(
615
- transcripts_root: str,
616
- state_file_path: str,
617
- full_rebuild: bool,
618
- ) -> int:
619
- """Execute one extraction pass (incremental by default).
620
-
621
- The offsets lock is held only around the initial offsets load and
622
- around each offsets write (where the latest on-disk offsets are
623
- re-read and merged with this process's pending updates via a
624
- per-file max). DB I/O and JSONL iteration run without the lock so
625
- concurrent Stop hooks do not serialize on each other's slow work.
626
-
627
- Returns process exit code (0 on success, 0 on offline fallback).
628
- """
629
- try:
630
- neon_connection = connect_to_neon()
631
- except Exception as connect_exception:
632
- if is_operational_error(connect_exception):
633
- _append_offline_warning_line(connect_exception)
634
- return EXIT_CODE_EXTRACTOR_ENVIRONMENT_MISSING
635
- raise
636
-
637
- try:
638
- starting_offset_by_jsonl_path = _load_starting_offsets(
639
- neon_connection=neon_connection,
640
- state_file_path=state_file_path,
641
- full_rebuild=full_rebuild,
642
- )
643
-
644
- all_jsonl_file_paths = _discover_jsonl_files(transcripts_root)
645
- for each_jsonl_file_path in all_jsonl_file_paths:
646
- previous_entry = starting_offset_by_jsonl_path.get(each_jsonl_file_path)
647
- start_offset = (
648
- previous_entry[BYTE_OFFSET_KEY]
649
- if previous_entry is not None
650
- else 0
651
- )
652
- start_line_number = (
653
- previous_entry[LINE_NUMBER_KEY]
654
- if previous_entry is not None
655
- else 0
656
- )
657
- batch_buffer: list[dict[str, object]] = []
658
- attachment_iterator = iter_attachment_records_from_file(
659
- each_jsonl_file_path,
660
- start_offset=start_offset,
661
- start_line_number=start_line_number,
662
- )
663
- for (
664
- parsed_record,
665
- line_number,
666
- byte_offset_after,
667
- ) in attachment_iterator:
668
- built_row = build_row_from_attachment(
669
- parsed_record=parsed_record,
670
- source_jsonl_path=each_jsonl_file_path,
671
- source_line_number=line_number,
672
- )
673
- batch_buffer.append(built_row)
674
- if len(batch_buffer) >= INSERT_BATCH_SIZE:
675
- insert_rows_batch(neon_connection, batch_buffer)
676
- batch_buffer.clear()
677
- _merge_and_save_offsets_under_lock(
678
- state_file_path=state_file_path,
679
- pending_updates={
680
- each_jsonl_file_path: {
681
- BYTE_OFFSET_KEY: byte_offset_after,
682
- LINE_NUMBER_KEY: attachment_iterator.final_line_number,
683
- },
684
- },
685
- )
686
- if batch_buffer:
687
- insert_rows_batch(neon_connection, batch_buffer)
688
- if attachment_iterator.drained:
689
- _merge_and_save_offsets_under_lock(
690
- state_file_path=state_file_path,
691
- pending_updates={
692
- each_jsonl_file_path: {
693
- BYTE_OFFSET_KEY: attachment_iterator.final_byte_offset,
694
- LINE_NUMBER_KEY: attachment_iterator.final_line_number,
695
- },
696
- },
697
- )
698
- finally:
699
- try:
700
- neon_connection.close()
701
- except Exception:
702
- pass
703
- return EXIT_CODE_SUCCESS
704
-
705
-
706
- def _load_starting_offsets(
707
- neon_connection: object,
708
- state_file_path: str,
709
- full_rebuild: bool,
710
- ) -> dict[str, dict[str, int]]:
711
- with _acquire_offsets_lock(state_file_path):
712
- if full_rebuild:
713
- with neon_connection.cursor() as neon_cursor:
714
- neon_cursor.execute(HOOK_EVENTS_TRUNCATE_SQL)
715
- neon_connection.commit()
716
- save_offsets(state_file_path, {})
717
- return {}
718
- return load_offsets(state_file_path)
719
-
720
-
721
- def _merge_and_save_offsets_under_lock(
722
- state_file_path: str,
723
- pending_updates: dict[str, dict[str, int]],
724
- ) -> None:
725
- with _acquire_offsets_lock(state_file_path):
726
- latest_on_disk_offsets = load_offsets(state_file_path)
727
- merged_offsets = _merge_offsets_taking_max(
728
- latest_on_disk_offsets, pending_updates
729
- )
730
- save_offsets(state_file_path, merged_offsets)
731
-
732
-
733
- def _merge_offsets_taking_max(
734
- disk_offsets: dict[str, dict[str, int]],
735
- pending_updates: dict[str, dict[str, int]],
736
- ) -> dict[str, dict[str, int]]:
737
- merged: dict[str, dict[str, int]] = dict(disk_offsets)
738
- for each_path, each_pending_entry in pending_updates.items():
739
- existing_entry = merged.get(each_path)
740
- if existing_entry is None:
741
- merged[each_path] = dict(each_pending_entry)
742
- continue
743
- merged[each_path] = {
744
- BYTE_OFFSET_KEY: max(
745
- existing_entry[BYTE_OFFSET_KEY],
746
- each_pending_entry[BYTE_OFFSET_KEY],
747
- ),
748
- LINE_NUMBER_KEY: max(
749
- existing_entry[LINE_NUMBER_KEY],
750
- each_pending_entry[LINE_NUMBER_KEY],
751
- ),
752
- }
753
- return merged
754
-
755
-
756
- def _discover_jsonl_files(transcripts_root: str) -> list[str]:
757
- recursive_glob_pattern = os.path.join(transcripts_root, "**", JSONL_FILE_GLOB)
758
- top_level_glob_pattern = os.path.join(transcripts_root, JSONL_FILE_GLOB)
759
- all_discovered_paths = set(glob.glob(recursive_glob_pattern, recursive=True))
760
- all_discovered_paths.update(glob.glob(top_level_glob_pattern))
761
- return sorted(all_discovered_paths)
762
-
763
-
764
- def run_summary() -> int:
765
- """Print the top-10 over-blockers summary and return exit code."""
766
- try:
767
- neon_connection = connect_to_neon()
768
- except Exception as connect_exception:
769
- if is_operational_error(connect_exception):
770
- _append_offline_warning_line(connect_exception)
771
- return EXIT_CODE_EXTRACTOR_ENVIRONMENT_MISSING
772
- raise
773
- try:
774
- with neon_connection.cursor() as neon_cursor:
775
- neon_cursor.execute(TOP_BLOCKERS_LAST_24_HOURS_SQL)
776
- all_result_rows = neon_cursor.fetchall()
777
- finally:
778
- try:
779
- neon_connection.close()
780
- except Exception:
781
- pass
782
- if not all_result_rows:
783
- print(SUMMARY_ZERO_BLOCKED_EVENTS_MESSAGE)
784
- return EXIT_CODE_SUCCESS
785
- _print_summary_table(all_result_rows)
786
- return EXIT_CODE_SUCCESS
787
-
788
-
789
- def _print_summary_table(all_result_rows: Sequence[tuple[object, ...]]) -> None:
790
- all_preview_rows: list[tuple[str, str, str, str]] = []
791
- for each_result_row in all_result_rows:
792
- (
793
- hook_name_string,
794
- hook_category_string,
795
- block_count_integer,
796
- top_command_preview,
797
- ) = each_result_row
798
- truncated_preview = (top_command_preview or EMPTY_STRING)[
799
- :TOP_BLOCKED_COMMAND_PREVIEW_MAX_CHARACTERS
800
- ]
801
- all_preview_rows.append(
802
- (
803
- str(hook_name_string),
804
- str(hook_category_string),
805
- str(block_count_integer),
806
- truncated_preview,
807
- ),
808
- )
809
- all_display_rows = [SUMMARY_COLUMN_HEADINGS, *all_preview_rows]
810
- all_column_widths = [
811
- max(len(each_row[each_column_index]) for each_row in all_display_rows)
812
- for each_column_index in range(len(SUMMARY_COLUMN_HEADINGS))
813
- ]
814
- for each_display_row in all_display_rows:
815
- formatted_columns = [
816
- each_cell.ljust(all_column_widths[each_column_index])
817
- for each_column_index, each_cell in enumerate(each_display_row)
818
- ]
819
- print(SUMMARY_TABLE_COLUMN_GAP.join(formatted_columns))
820
-
821
-
822
- def run_query(named_query: str) -> int:
823
- """Execute a pre-baked SQL file under ``queries/`` and print results."""
824
- if not re.fullmatch(QUERY_NAME_PATTERN, named_query):
825
- print(
826
- f"{INVALID_QUERY_NAME_MESSAGE_PREFIX}{named_query}",
827
- file=sys.stderr,
828
- )
829
- return EXIT_CODE_UNKNOWN_QUERY
830
- queries_directory = Path(__file__).resolve().parent / QUERIES_DIRECTORY_NAME
831
- query_file_path = queries_directory / f"{named_query}{SQL_FILE_EXTENSION}"
832
- if not query_file_path.exists():
833
- print(f"{UNKNOWN_QUERY_MESSAGE_PREFIX}{named_query}", file=sys.stderr)
834
- return EXIT_CODE_UNKNOWN_QUERY
835
- query_text = query_file_path.read_text(encoding="utf-8")
836
- try:
837
- neon_connection = connect_to_neon()
838
- except Exception as connect_exception:
839
- if is_operational_error(connect_exception):
840
- _append_offline_warning_line(connect_exception)
841
- return EXIT_CODE_EXTRACTOR_ENVIRONMENT_MISSING
842
- raise
843
- try:
844
- with neon_connection.cursor() as neon_cursor:
845
- neon_cursor.execute(query_text)
846
- all_result_rows = neon_cursor.fetchall()
847
- all_column_names = [
848
- each_description[0]
849
- for each_description in (neon_cursor.description or [])
850
- ]
851
- finally:
852
- try:
853
- neon_connection.close()
854
- except Exception:
855
- pass
856
- if not all_result_rows:
857
- print(QUERY_NO_ROWS_RETURNED_MESSAGE)
858
- return EXIT_CODE_SUCCESS
859
- print(SUMMARY_TABLE_COLUMN_GAP.join(all_column_names))
860
- for each_result_row in all_result_rows:
861
- print(
862
- SUMMARY_TABLE_COLUMN_GAP.join(
863
- str(each_cell) for each_cell in each_result_row
864
- )
865
- )
866
- return EXIT_CODE_SUCCESS
867
-
868
-
869
- def main() -> int:
870
- """Disabled extractor entry point: exit success with no work."""
871
- return EXIT_CODE_SUCCESS
872
-
873
-
874
- if __name__ == "__main__":
875
- sys.exit(main())