@toolu/opencode 6.8.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 (331) hide show
  1. package/generated/GENERATED-NOTES.md +16 -0
  2. package/generated/agents/toolu--architect.md +52 -0
  3. package/generated/agents/toolu--deep-explore.md +65 -0
  4. package/generated/agents/toolu--implementer.md +50 -0
  5. package/generated/agents/toolu--quick-task.md +54 -0
  6. package/generated/agents/toolu--research-agent.md +101 -0
  7. package/generated/commands/toolu--commit.md +8 -0
  8. package/generated/commands/toolu--review-and-commit.md +8 -0
  9. package/generated/opencode.toolu.json +116 -0
  10. package/generated/skills/toolu--brainstorm/SKILL.md +57 -0
  11. package/generated/skills/toolu--brainstorm/references/design-questions.md +36 -0
  12. package/generated/skills/toolu--commit--skill/SKILL.md +11 -0
  13. package/generated/skills/toolu--debug/SKILL.md +54 -0
  14. package/generated/skills/toolu--deep-research/SKILL.md +51 -0
  15. package/generated/skills/toolu--execution/SKILL.md +77 -0
  16. package/generated/skills/toolu--orchestrator/SKILL.md +145 -0
  17. package/generated/skills/toolu--orchestrator/references/delegation-cost.md +104 -0
  18. package/generated/skills/toolu--orchestrator/references/model-routing.md +112 -0
  19. package/generated/skills/toolu--plan/SKILL.md +56 -0
  20. package/generated/skills/toolu--plan/references/ledger.md +18 -0
  21. package/generated/skills/toolu--plan-review/SKILL.md +48 -0
  22. package/generated/skills/toolu--review-and-commit--skill/SKILL.md +10 -0
  23. package/generated/skills/toolu--setup/SKILL.md +24 -0
  24. package/generated/skills/toolu--setup/scripts/setup.sh +206 -0
  25. package/generated/skills/toolu--spec/SKILL.md +67 -0
  26. package/generated/skills/toolu--spec-review/SKILL.md +50 -0
  27. package/generated/skills/toolu--test/SKILL.md +49 -0
  28. package/package.json +51 -0
  29. package/plugins/agent-browser/.claude-plugin/plugin.json +18 -0
  30. package/plugins/agent-browser/.codex-plugin/plugin.json +7 -0
  31. package/plugins/agent-browser/README.md +18 -0
  32. package/plugins/agent-browser/hooks/hooks.json +15 -0
  33. package/plugins/agent-browser/hooks/session-start.sh +42 -0
  34. package/plugins/agent-browser/skills/agent-browser/SKILL.md +77 -0
  35. package/plugins/agent-browser/skills/agent-browser/scripts/agent-browser.sh +80 -0
  36. package/plugins/ast-grep/.claude-plugin/plugin.json +18 -0
  37. package/plugins/ast-grep/.codex-plugin/plugin.json +7 -0
  38. package/plugins/ast-grep/README.md +21 -0
  39. package/plugins/ast-grep/hooks/hooks.json +15 -0
  40. package/plugins/ast-grep/hooks/post-tools.d/byte-savings.sh +67 -0
  41. package/plugins/ast-grep/hooks/pre-tools.d/search-nudge.sh +170 -0
  42. package/plugins/ast-grep/hooks/register.sh +68 -0
  43. package/plugins/ast-grep/scripts/byte-savings-report.sh +28 -0
  44. package/plugins/ast-grep/skills/ast-grep/SKILL.md +127 -0
  45. package/plugins/ast-grep/skills/ast-grep/references/ast-grep-advanced.md +44 -0
  46. package/plugins/ast-grep/skills/ast-grep/scripts/ast-grep.sh +103 -0
  47. package/plugins/context7/.claude-plugin/plugin.json +18 -0
  48. package/plugins/context7/.codex-plugin/plugin.json +7 -0
  49. package/plugins/context7/README.md +19 -0
  50. package/plugins/context7/hooks/hooks.json +15 -0
  51. package/plugins/context7/hooks/session-start.sh +42 -0
  52. package/plugins/context7/skills/context7/SKILL.md +64 -0
  53. package/plugins/context7/skills/context7/scripts/search.sh +151 -0
  54. package/plugins/exa-search/.claude-plugin/plugin.json +18 -0
  55. package/plugins/exa-search/.codex-plugin/plugin.json +7 -0
  56. package/plugins/exa-search/README.md +19 -0
  57. package/plugins/exa-search/hooks/hooks.json +15 -0
  58. package/plugins/exa-search/hooks/session-start.sh +34 -0
  59. package/plugins/exa-search/skills/exa-search/SKILL.md +91 -0
  60. package/plugins/exa-search/skills/exa-search/scripts/search.sh +221 -0
  61. package/plugins/jev/.claude-plugin/plugin.json +19 -0
  62. package/plugins/jev/.codex-plugin/plugin.json +7 -0
  63. package/plugins/jev/README.md +61 -0
  64. package/plugins/jev/hooks/hooks.json +25 -0
  65. package/plugins/jev/hooks/lib/common.sh +49 -0
  66. package/plugins/jev/hooks/session-start.sh +45 -0
  67. package/plugins/jev/hooks/user-prompt-submit.sh +41 -0
  68. package/plugins/jev/skills/jev/SKILL.md +81 -0
  69. package/plugins/jev/skills/jev/evals/README.md +98 -0
  70. package/plugins/jev/skills/jev/references/problem-solving.md +198 -0
  71. package/plugins/jev/skills/jev/scripts/jev.sh +405 -0
  72. package/plugins/jira/.claude-plugin/plugin.json +18 -0
  73. package/plugins/jira/.codex-plugin/plugin.json +7 -0
  74. package/plugins/jira/README.md +42 -0
  75. package/plugins/jira/hooks/hooks.json +15 -0
  76. package/plugins/jira/hooks/session-start.sh +34 -0
  77. package/plugins/jira/skills/jira/SKILL.md +123 -0
  78. package/plugins/jira/skills/jira/scripts/jira.sh +70 -0
  79. package/plugins/jira/skills/jira/scripts/lib/adf.sh +17 -0
  80. package/plugins/jira/skills/jira/scripts/lib/attachment.sh +94 -0
  81. package/plugins/jira/skills/jira/scripts/lib/board.sh +58 -0
  82. package/plugins/jira/skills/jira/scripts/lib/http.sh +184 -0
  83. package/plugins/jira/skills/jira/scripts/lib/issue.sh +159 -0
  84. package/plugins/jira/skills/jira/scripts/lib/paginate.sh +45 -0
  85. package/plugins/jira/skills/jira/scripts/lib/plan-parse.sh +70 -0
  86. package/plugins/jira/skills/jira/scripts/lib/plan-run.sh +115 -0
  87. package/plugins/jira/skills/jira/scripts/lib/plan-store.sh +168 -0
  88. package/plugins/jira/skills/jira/scripts/lib/plan.sh +108 -0
  89. package/plugins/jira/skills/jira/scripts/lib/project.sh +42 -0
  90. package/plugins/jira/skills/jira/scripts/lib/raw.sh +15 -0
  91. package/plugins/jira/skills/jira/scripts/lib/search.sh +41 -0
  92. package/plugins/jira/skills/jira/scripts/lib/sprint.sh +88 -0
  93. package/plugins/jira/skills/jira/scripts/lib/user.sh +45 -0
  94. package/plugins/jira/skills/jira/scripts/lib/worklog.sh +56 -0
  95. package/plugins/pr-babysit/.claude-plugin/plugin.json +25 -0
  96. package/plugins/pr-babysit/.codex-plugin/plugin.json +7 -0
  97. package/plugins/pr-babysit/README.md +38 -0
  98. package/plugins/pr-babysit/commands/babysit.md +13 -0
  99. package/plugins/pr-babysit/hooks/check-toolu.sh +20 -0
  100. package/plugins/pr-babysit/hooks/hooks.json +15 -0
  101. package/plugins/pr-babysit/scripts/babysit-tick.sh +133 -0
  102. package/plugins/pr-babysit/scripts/collect-pr.sh +224 -0
  103. package/plugins/pr-babysit/scripts/lib/common.sh +146 -0
  104. package/plugins/pr-babysit/scripts/lib/gh.sh +150 -0
  105. package/plugins/pr-babysit/scripts/lib/lock.sh +84 -0
  106. package/plugins/pr-babysit/scripts/lib/normalize.sh +98 -0
  107. package/plugins/pr-babysit/scripts/lib/state.sh +37 -0
  108. package/plugins/pr-babysit/scripts/parse-verdict.sh +171 -0
  109. package/plugins/pr-babysit/scripts/record.sh +69 -0
  110. package/plugins/pr-babysit/scripts/reduce-state.sh +245 -0
  111. package/plugins/pr-babysit/scripts/reply-thread.sh +90 -0
  112. package/plugins/pr-babysit/scripts/resolve-thread.sh +71 -0
  113. package/plugins/pr-babysit/skills/babysit/SKILL.md +31 -0
  114. package/plugins/pr-babysit/skills/babysit/references/helper.md +209 -0
  115. package/plugins/pr-babysit/workflows/babysit.md +722 -0
  116. package/plugins/python-quality/.claude-plugin/plugin.json +24 -0
  117. package/plugins/python-quality/.codex-plugin/plugin.json +6 -0
  118. package/plugins/python-quality/README.md +23 -0
  119. package/plugins/python-quality/hooks/check-toolu.sh +20 -0
  120. package/plugins/python-quality/hooks/concerns/00-preamble.sh +85 -0
  121. package/plugins/python-quality/hooks/concerns/10-size-file.sh +6 -0
  122. package/plugins/python-quality/hooks/concerns/20-tests.sh +37 -0
  123. package/plugins/python-quality/hooks/concerns/30-suppression.sh +33 -0
  124. package/plugins/python-quality/hooks/concerns/50-size-fn.sh +49 -0
  125. package/plugins/python-quality/hooks/concerns/70-no-mocks.sh +76 -0
  126. package/plugins/python-quality/hooks/concerns/90-docs.sh +52 -0
  127. package/plugins/python-quality/hooks/concerns/99-finalize.sh +36 -0
  128. package/plugins/python-quality/hooks/hooks.json +19 -0
  129. package/plugins/python-quality/hooks/register.sh +65 -0
  130. package/plugins/rust-quality/.claude-plugin/plugin.json +24 -0
  131. package/plugins/rust-quality/.codex-plugin/plugin.json +6 -0
  132. package/plugins/rust-quality/README.md +24 -0
  133. package/plugins/rust-quality/hooks/check-toolu.sh +20 -0
  134. package/plugins/rust-quality/hooks/concerns/00-preamble.sh +93 -0
  135. package/plugins/rust-quality/hooks/concerns/10-size-file.sh +10 -0
  136. package/plugins/rust-quality/hooks/concerns/20-tests.sh +84 -0
  137. package/plugins/rust-quality/hooks/concerns/30-suppression.sh +26 -0
  138. package/plugins/rust-quality/hooks/concerns/40-unsafe.sh +39 -0
  139. package/plugins/rust-quality/hooks/concerns/50-size-fn.sh +38 -0
  140. package/plugins/rust-quality/hooks/concerns/55-size-impl.sh +28 -0
  141. package/plugins/rust-quality/hooks/concerns/60-error-handling.sh +149 -0
  142. package/plugins/rust-quality/hooks/concerns/70-no-mocks.sh +139 -0
  143. package/plugins/rust-quality/hooks/concerns/90-docs.sh +32 -0
  144. package/plugins/rust-quality/hooks/concerns/99-finalize.sh +36 -0
  145. package/plugins/rust-quality/hooks/hooks.json +19 -0
  146. package/plugins/rust-quality/hooks/register.sh +65 -0
  147. package/plugins/statusline/.claude-plugin/plugin.json +19 -0
  148. package/plugins/statusline/.codex-plugin/plugin.json +7 -0
  149. package/plugins/statusline/README.md +108 -0
  150. package/plugins/statusline/commands/setup.md +30 -0
  151. package/plugins/statusline/hooks/hooks.json +15 -0
  152. package/plugins/statusline/hooks/session-start.sh +45 -0
  153. package/plugins/statusline/scripts/collect-status.sh +147 -0
  154. package/plugins/statusline/scripts/setup.sh +93 -0
  155. package/plugins/statusline/scripts/status.sh +55 -0
  156. package/plugins/statusline/skills/status/SKILL.md +13 -0
  157. package/plugins/statusline/statusline.sh +177 -0
  158. package/plugins/toolu/.claude-plugin/plugin.json +19 -0
  159. package/plugins/toolu/.codex-plugin/plugin.json +7 -0
  160. package/plugins/toolu/README.md +35 -0
  161. package/plugins/toolu/agents/.gitkeep +0 -0
  162. package/plugins/toolu/agents/architect.md +52 -0
  163. package/plugins/toolu/agents/deep-explore.md +65 -0
  164. package/plugins/toolu/agents/implementer.md +50 -0
  165. package/plugins/toolu/agents/quick-task.md +54 -0
  166. package/plugins/toolu/agents/research-agent.md +107 -0
  167. package/plugins/toolu/assets/agents/architect.toml +9 -0
  168. package/plugins/toolu/assets/agents/deep-explore.toml +9 -0
  169. package/plugins/toolu/assets/agents/implementer.toml +9 -0
  170. package/plugins/toolu/assets/agents/quick-task.toml +9 -0
  171. package/plugins/toolu/assets/agents/research-agent.toml +9 -0
  172. package/plugins/toolu/commands/.gitkeep +0 -0
  173. package/plugins/toolu/commands/commit.md +5 -0
  174. package/plugins/toolu/commands/review-and-commit.md +5 -0
  175. package/plugins/toolu/hooks/docs/gates.md +157 -0
  176. package/plugins/toolu/hooks/docs/model-routing.md +14 -0
  177. package/plugins/toolu/hooks/docs/post-compaction.md +3 -0
  178. package/plugins/toolu/hooks/docs/push-review.md +123 -0
  179. package/plugins/toolu/hooks/docs/session-start-python.md +3 -0
  180. package/plugins/toolu/hooks/docs/session-start-rust.md +4 -0
  181. package/plugins/toolu/hooks/docs/session-start-ts.md +3 -0
  182. package/plugins/toolu/hooks/docs/session-start.md +16 -0
  183. package/plugins/toolu/hooks/hooks.json +76 -0
  184. package/plugins/toolu/hooks/lib/config.sh +355 -0
  185. package/plugins/toolu/hooks/lib/detect.sh +929 -0
  186. package/plugins/toolu/hooks/lib/diff-sha.sh +32 -0
  187. package/plugins/toolu/hooks/lib/dispatch.sh +365 -0
  188. package/plugins/toolu/hooks/lib/docs-sync-config.sh +81 -0
  189. package/plugins/toolu/hooks/lib/edit-records.sh +157 -0
  190. package/plugins/toolu/hooks/lib/gate-file.sh +201 -0
  191. package/plugins/toolu/hooks/lib/gate-mode.sh +244 -0
  192. package/plugins/toolu/hooks/lib/host.sh +205 -0
  193. package/plugins/toolu/hooks/lib/permissions.sh +125 -0
  194. package/plugins/toolu/hooks/lib/plan-ledger-parse.sh +219 -0
  195. package/plugins/toolu/hooks/lib/plan-ledger-preflight.sh +137 -0
  196. package/plugins/toolu/hooks/lib/plan-ledger.sh +749 -0
  197. package/plugins/toolu/hooks/lib/push-waiver.sh +116 -0
  198. package/plugins/toolu/hooks/lib/quality-config.sh +184 -0
  199. package/plugins/toolu/hooks/lib/registry.sh +72 -0
  200. package/plugins/toolu/hooks/lib/state-sweeper.sh +194 -0
  201. package/plugins/toolu/hooks/lib/telemetry.sh +94 -0
  202. package/plugins/toolu/hooks/lib/verdict.sh +477 -0
  203. package/plugins/toolu/hooks/post-tools/mod.sh +42 -0
  204. package/plugins/toolu/hooks/post-tools/modules/gate-status.sh +140 -0
  205. package/plugins/toolu/hooks/post-tools/modules/push-waiver.sh +97 -0
  206. package/plugins/toolu/hooks/pre-compact.sh +18 -0
  207. package/plugins/toolu/hooks/pre-tools/agent-tier.sh +111 -0
  208. package/plugins/toolu/hooks/pre-tools/mod.sh +37 -0
  209. package/plugins/toolu/hooks/pre-tools/modules/bash-commands.sh +187 -0
  210. package/plugins/toolu/hooks/pre-tools/modules/code-edit-rules.sh +78 -0
  211. package/plugins/toolu/hooks/pre-tools/modules/commit-gate.sh +70 -0
  212. package/plugins/toolu/hooks/pre-tools/modules/docs-sync.sh +137 -0
  213. package/plugins/toolu/hooks/pre-tools/modules/mcp-blocker.sh +151 -0
  214. package/plugins/toolu/hooks/pre-tools/modules/plan-ledger.sh +201 -0
  215. package/plugins/toolu/hooks/pre-tools/modules/protected-files.sh +177 -0
  216. package/plugins/toolu/hooks/pre-tools/modules/push-review.sh +273 -0
  217. package/plugins/toolu/hooks/pre-tools/modules/quality-gate.sh +87 -0
  218. package/plugins/toolu/hooks/session-start.sh +379 -0
  219. package/plugins/toolu/hooks/user-prompt-submit.sh +197 -0
  220. package/plugins/toolu/scripts/context-budget.sh +111 -0
  221. package/plugins/toolu/scripts/debug-log.sh +98 -0
  222. package/plugins/toolu/scripts/debug-stack.sh +108 -0
  223. package/plugins/toolu/scripts/debug-testfail.sh +97 -0
  224. package/plugins/toolu/settings/.gitkeep +0 -0
  225. package/plugins/toolu/settings/README.md +127 -0
  226. package/plugins/toolu/settings/bash-allowlist.txt +12 -0
  227. package/plugins/toolu/settings/bash-denylist.txt +25 -0
  228. package/plugins/toolu/settings/code-edit-rules.json +67 -0
  229. package/plugins/toolu/settings/commit-prefixes.txt +12 -0
  230. package/plugins/toolu/settings/mcp-blocklist.txt +8 -0
  231. package/plugins/toolu/settings/permissions.fragment.json +36 -0
  232. package/plugins/toolu/settings/protected-files.txt +30 -0
  233. package/plugins/toolu/settings/rust-unsafe-exemptions.txt +5 -0
  234. package/plugins/toolu/settings/toolu.config.example.json +130 -0
  235. package/plugins/toolu/skills/.gitkeep +0 -0
  236. package/plugins/toolu/skills/brainstorm/SKILL.md +57 -0
  237. package/plugins/toolu/skills/brainstorm/references/design-questions.md +36 -0
  238. package/plugins/toolu/skills/commit/SKILL.md +11 -0
  239. package/plugins/toolu/skills/debug/SKILL.md +54 -0
  240. package/plugins/toolu/skills/debug/evals/README.md +17 -0
  241. package/plugins/toolu/skills/debug/evals/cases.json +24 -0
  242. package/plugins/toolu/skills/debug/evals/results.json +34 -0
  243. package/plugins/toolu/skills/deep-research/SKILL.md +51 -0
  244. package/plugins/toolu/skills/execution/SKILL.md +77 -0
  245. package/plugins/toolu/skills/orchestrator/SKILL.md +145 -0
  246. package/plugins/toolu/skills/orchestrator/references/delegation-cost.md +104 -0
  247. package/plugins/toolu/skills/orchestrator/references/model-routing.md +112 -0
  248. package/plugins/toolu/skills/plan/SKILL.md +56 -0
  249. package/plugins/toolu/skills/plan/references/ledger.md +18 -0
  250. package/plugins/toolu/skills/plan-review/SKILL.md +48 -0
  251. package/plugins/toolu/skills/review-and-commit/SKILL.md +10 -0
  252. package/plugins/toolu/skills/setup/SKILL.md +24 -0
  253. package/plugins/toolu/skills/setup/scripts/setup.sh +206 -0
  254. package/plugins/toolu/skills/spec/SKILL.md +67 -0
  255. package/plugins/toolu/skills/spec-review/SKILL.md +50 -0
  256. package/plugins/toolu/skills/test/SKILL.md +49 -0
  257. package/plugins/toolu/workflows/commit.md +21 -0
  258. package/plugins/toolu/workflows/host-mapping.md +22 -0
  259. package/plugins/toolu/workflows/review-and-commit.md +54 -0
  260. package/plugins/toolu/workflows/semantic-judgments.md +35 -0
  261. package/plugins/toolu-review/.claude-plugin/plugin.json +18 -0
  262. package/plugins/toolu-review/.codex-plugin/plugin.json +7 -0
  263. package/plugins/toolu-review/README.md +17 -0
  264. package/plugins/toolu-review/hooks/hooks.json +15 -0
  265. package/plugins/toolu-review/hooks/session-start.sh +34 -0
  266. package/plugins/toolu-review/skills/review/SKILL.md +90 -0
  267. package/plugins/toolu-review/skills/review/scripts/write-state.sh +189 -0
  268. package/plugins/ts-quality/.claude-plugin/plugin.json +24 -0
  269. package/plugins/ts-quality/.codex-plugin/plugin.json +6 -0
  270. package/plugins/ts-quality/README.md +24 -0
  271. package/plugins/ts-quality/hooks/check-toolu.sh +20 -0
  272. package/plugins/ts-quality/hooks/concerns/00-preamble.sh +123 -0
  273. package/plugins/ts-quality/hooks/concerns/10-imports.sh +33 -0
  274. package/plugins/ts-quality/hooks/concerns/15-type-as.sh +9 -0
  275. package/plugins/ts-quality/hooks/concerns/20-tests.sh +26 -0
  276. package/plugins/ts-quality/hooks/concerns/25-size-file.sh +40 -0
  277. package/plugins/ts-quality/hooks/concerns/30-size-fn.sh +53 -0
  278. package/plugins/ts-quality/hooks/concerns/35-react-hooks.sh +7 -0
  279. package/plugins/ts-quality/hooks/concerns/40-factory.sh +5 -0
  280. package/plugins/ts-quality/hooks/concerns/45-typeguard.sh +6 -0
  281. package/plugins/ts-quality/hooks/concerns/50-type-dup.sh +16 -0
  282. package/plugins/ts-quality/hooks/concerns/55-naming.sh +11 -0
  283. package/plugins/ts-quality/hooks/concerns/60-console.sh +6 -0
  284. package/plugins/ts-quality/hooks/concerns/65-suppression.sh +12 -0
  285. package/plugins/ts-quality/hooks/concerns/70-ui-confirm.sh +8 -0
  286. package/plugins/ts-quality/hooks/concerns/72-ui-radix.sh +12 -0
  287. package/plugins/ts-quality/hooks/concerns/74-react-props.sh +6 -0
  288. package/plugins/ts-quality/hooks/concerns/76-toast.sh +10 -0
  289. package/plugins/ts-quality/hooks/concerns/78-error-ast.sh +242 -0
  290. package/plugins/ts-quality/hooks/concerns/80-throw-literal.sh +23 -0
  291. package/plugins/ts-quality/hooks/concerns/85-no-mocks.sh +109 -0
  292. package/plugins/ts-quality/hooks/concerns/90-duplication.sh +30 -0
  293. package/plugins/ts-quality/hooks/concerns/92-docs.sh +36 -0
  294. package/plugins/ts-quality/hooks/concerns/94-handler.sh +15 -0
  295. package/plugins/ts-quality/hooks/concerns/99-finalize.sh +46 -0
  296. package/plugins/ts-quality/hooks/hooks.json +19 -0
  297. package/plugins/ts-quality/hooks/register.sh +65 -0
  298. package/src/adapter/__tests__/evaluate.test.ts +82 -0
  299. package/src/adapter/__tests__/permission-map.test.ts +91 -0
  300. package/src/adapter/evaluate.ts +54 -0
  301. package/src/adapter/permission-map.ts +177 -0
  302. package/src/bootstrap/__tests__/bootstrap.test.ts +148 -0
  303. package/src/bootstrap/entrypoint.ts +18 -0
  304. package/src/bootstrap/index.ts +5 -0
  305. package/src/bootstrap/readiness.ts +59 -0
  306. package/src/bootstrap/result.ts +21 -0
  307. package/src/bootstrap/runtime.ts +99 -0
  308. package/src/bootstrap/test-helpers.ts +25 -0
  309. package/src/host/__tests__/host.test.ts +37 -0
  310. package/src/host/detect.ts +46 -0
  311. package/src/host/index.ts +10 -0
  312. package/src/host/roots.ts +59 -0
  313. package/src/host/types.ts +14 -0
  314. package/src/inventory/__tests__/inventory.test.ts +69 -0
  315. package/src/inventory/index.ts +9 -0
  316. package/src/inventory/manifest.ts +46 -0
  317. package/src/inventory/scan.ts +66 -0
  318. package/src/inventory/selection.ts +92 -0
  319. package/src/inventory/types.ts +22 -0
  320. package/src/lifecycle/__tests__/lifecycle.test.ts +7 -0
  321. package/src/lifecycle/index.ts +2 -0
  322. package/src/lifecycle/table.ts +42 -0
  323. package/src/plugin/__tests__/plugin-stub.test.ts +6 -0
  324. package/src/plugin/plugin-stub.ts +7 -0
  325. package/src/plugin/toolu.ts +142 -0
  326. package/src/preflight/__tests__/preflight.test.ts +20 -0
  327. package/src/preflight/check.ts +75 -0
  328. package/src/preflight/index.ts +2 -0
  329. package/src/select/__tests__/select.test.ts +52 -0
  330. package/src/select/index.ts +2 -0
  331. package/src/select/resolve.ts +87 -0
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env bash
2
+ # project.sh — Jira project operations: list, get, versions, components. Sourced;
3
+ # depends on http.sh (jira_curl/jira_lean). All actions are read-only GETs.
4
+
5
+ _jira_project_path() { printf '/rest/api/%s/project' "${_JIRA_VER:-3}"; }
6
+
7
+ jira_project() {
8
+ local action="${1:-}"; [[ $# -gt 0 ]] && shift
9
+ case "$action" in
10
+ list) _jira_project_list "$@";;
11
+ get) _jira_project_get "$@";;
12
+ versions) _jira_project_versions "$@";;
13
+ components) _jira_project_components "$@";;
14
+ *) echo "Usage: jira project <list|get|versions|components> ..." >&2; return 1;;
15
+ esac
16
+ }
17
+
18
+ # list: GET the project collection (a top-level array), leaned to key/name/id.
19
+ _jira_project_list() {
20
+ jira_curl GET "$(_jira_project_path)" | jira_lean '{projects:[.[]|{key, name, id}]}'
21
+ }
22
+
23
+ # get <KEY>: GET one project's full detail.
24
+ _jira_project_get() {
25
+ local key="${1:-}"
26
+ [[ -z "$key" ]] && { echo "Usage: jira project get <KEY>" >&2; return 1; }
27
+ jira_curl GET "$(_jira_project_path)/$key" | jira_lean '.'
28
+ }
29
+
30
+ # versions <KEY>: GET the project's released/unreleased versions.
31
+ _jira_project_versions() {
32
+ local key="${1:-}"
33
+ [[ -z "$key" ]] && { echo "Usage: jira project versions <KEY>" >&2; return 1; }
34
+ jira_curl GET "$(_jira_project_path)/$key/versions" | jira_lean '.'
35
+ }
36
+
37
+ # components <KEY>: GET the project's components.
38
+ _jira_project_components() {
39
+ local key="${1:-}"
40
+ [[ -z "$key" ]] && { echo "Usage: jira project components <KEY>" >&2; return 1; }
41
+ jira_curl GET "$(_jira_project_path)/$key/components" | jira_lean '.'
42
+ }
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ # raw.sh — escape hatch: issue an arbitrary authenticated request to any Jira
3
+ # REST path, reaching endpoints the ergonomic families do not wrap. Sourced;
4
+ # depends on jira_curl/jira_lean from http.sh.
5
+
6
+ # jira_raw METHOD PATH [JSON_BODY]: METHOD in GET|POST|PUT|DELETE; PATH is
7
+ # relative to JIRA_BASE_URL. Prints the response body.
8
+ jira_raw() {
9
+ if [[ $# -lt 2 ]]; then
10
+ echo "Usage: jira raw <GET|POST|PUT|DELETE> <path> [json_body]" >&2
11
+ return 1
12
+ fi
13
+ local method="$1" path="$2" body="${3:-}"
14
+ jira_curl "$method" "$path" "$body" | jira_lean '.'
15
+ }
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+ # search.sh — JQL search. v3 (Cloud) POSTs to /rest/api/3/search/jql with
3
+ # nextPageToken paging; v2 (Server/DC) POSTs to /rest/api/2/search with startAt
4
+ # paging. Sourced; depends on http.sh and paginate.sh.
5
+
6
+ # jira_search -q <JQL> [-n MAX] [--all] [--fields f1,f2]
7
+ jira_search() {
8
+ local jql="" max=50 all="false" fields=""
9
+ while [[ $# -gt 0 ]]; do
10
+ case "$1" in
11
+ -q|--query) jql="$2"; shift 2;;
12
+ -n|--num) max="$2"; shift 2;;
13
+ --all) all="true"; shift;;
14
+ --fields) fields="$2"; shift 2;;
15
+ *) if [[ -z "$jql" ]]; then jql="$1"; shift
16
+ else echo "search: unknown option '$1'" >&2; return 1; fi;;
17
+ esac
18
+ done
19
+ [[ -z "$jql" ]] && { echo "Usage: jira search -q <JQL> [-n MAX] [--all] [--fields f1,f2]" >&2; return 1; }
20
+
21
+ local fields_json="null"
22
+ [[ -n "$fields" ]] && fields_json=$(jq -nc --arg f "$fields" '$f|split(",")')
23
+
24
+ local body
25
+ body=$(jq -nc --arg q "$jql" --argjson n "$max" --argjson f "$fields_json" \
26
+ '{jql:$q, maxResults:$n} | if $f then .fields=$f else . end')
27
+
28
+ local path mode
29
+ if [[ "${_JIRA_VER:-3}" == "2" ]]; then
30
+ path="/rest/api/2/search"; mode="offset"
31
+ else
32
+ path="/rest/api/3/search/jql"; mode="token"
33
+ fi
34
+
35
+ if [[ "$all" == "true" ]]; then
36
+ jira_paginate "$mode" POST "$path" issues "$body" | jira_lean '.'
37
+ else
38
+ jira_curl POST "$path" "$body" \
39
+ | jira_lean '{issues: [.issues[] | {key, summary: .fields.summary, status: .fields.status.name}]}'
40
+ fi
41
+ }
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env bash
2
+ # sprint.sh — Jira Agile sprint operations: list (per board), get, issues,
3
+ # create, move issues, start, complete. Sourced; depends on http.sh
4
+ # (jira_curl/jira_lean). Agile endpoints always use /rest/agile/1.0 regardless
5
+ # of API version; start/complete mutate the live sprint state.
6
+
7
+ # jira_sprint <list|get|issues|create|move|start|complete> ...: dispatch a
8
+ # sprint sub-action; unknown/missing prints usage to stderr and returns 1.
9
+ jira_sprint() {
10
+ local action="${1:-}"; [[ $# -gt 0 ]] && shift
11
+ case "$action" in
12
+ list) _jira_sprint_list "$@";;
13
+ get) _jira_sprint_get "$@";;
14
+ issues) _jira_sprint_issues "$@";;
15
+ create) _jira_sprint_create "$@";;
16
+ move) _jira_sprint_move "$@";;
17
+ start) _jira_sprint_start "$@";;
18
+ complete) _jira_sprint_complete "$@";;
19
+ *) echo "Usage: jira sprint <list|get|issues|create|move|start|complete> ..." >&2; return 1;;
20
+ esac
21
+ }
22
+
23
+ # _jira_sprint_list BOARD_ID: list the sprints on a board.
24
+ _jira_sprint_list() {
25
+ local board="${1:-}"
26
+ [[ -z "$board" ]] && { echo "Usage: jira sprint list <BOARD_ID>" >&2; return 1; }
27
+ jira_curl GET "/rest/agile/1.0/board/$board/sprint" \
28
+ | jira_lean '{values:[.values[]|{id,name,state}]}'
29
+ }
30
+
31
+ # _jira_sprint_get ID: fetch one sprint.
32
+ _jira_sprint_get() {
33
+ local id="${1:-}"
34
+ [[ -z "$id" ]] && { echo "Usage: jira sprint get <ID>" >&2; return 1; }
35
+ jira_curl GET "/rest/agile/1.0/sprint/$id" | jira_lean '.'
36
+ }
37
+
38
+ # _jira_sprint_issues ID: list the issues in a sprint.
39
+ _jira_sprint_issues() {
40
+ local id="${1:-}"
41
+ [[ -z "$id" ]] && { echo "Usage: jira sprint issues <ID>" >&2; return 1; }
42
+ jira_curl GET "/rest/agile/1.0/sprint/$id/issue" \
43
+ | jira_lean '{issues:[.issues[]|{key, summary:.fields.summary, status:.fields.status.name}]}'
44
+ }
45
+
46
+ # _jira_sprint_create BOARD_ID -n NAME: create a sprint on a board.
47
+ _jira_sprint_create() {
48
+ local board="${1:-}"; [[ $# -gt 0 ]] && shift
49
+ local name=""
50
+ while [[ $# -gt 0 ]]; do
51
+ case "$1" in
52
+ -n|--name) name="$2"; shift 2;;
53
+ *) echo "sprint create: unknown option '$1'" >&2; return 1;;
54
+ esac
55
+ done
56
+ [[ -z "$board" || -z "$name" ]] && {
57
+ echo "Usage: jira sprint create <BOARD_ID> -n <NAME>" >&2
58
+ return 1
59
+ }
60
+ local body
61
+ body=$(jq -nc --argjson b "$board" --arg n "$name" '{originBoardId:$b, name:$n}')
62
+ jira_curl POST "/rest/agile/1.0/sprint" "$body" | jira_lean '.'
63
+ }
64
+
65
+ # _jira_sprint_move SPRINT_ID KEY...: move one or more issues into a sprint.
66
+ _jira_sprint_move() {
67
+ local id="${1:-}"; [[ $# -gt 0 ]] && shift
68
+ [[ -z "$id" || $# -eq 0 ]] && { echo "Usage: jira sprint move <SPRINT_ID> <KEY...>" >&2; return 1; }
69
+ local body
70
+ body=$(jq -nc '{issues:$ARGS.positional}' --args "$@")
71
+ jira_curl POST "/rest/agile/1.0/sprint/$id/issue" "$body" | jira_lean '.'
72
+ }
73
+
74
+ # _jira_sprint_start ID: start a sprint (set state active).
75
+ _jira_sprint_start() {
76
+ local id="${1:-}"
77
+ [[ -z "$id" ]] && { echo "Usage: jira sprint start <ID>" >&2; return 1; }
78
+ jira_curl POST "/rest/agile/1.0/sprint/$id" '{"state":"active"}'
79
+ echo "started sprint $id"
80
+ }
81
+
82
+ # _jira_sprint_complete ID: complete a sprint (set state closed).
83
+ _jira_sprint_complete() {
84
+ local id="${1:-}"
85
+ [[ -z "$id" ]] && { echo "Usage: jira sprint complete <ID>" >&2; return 1; }
86
+ jira_curl POST "/rest/agile/1.0/sprint/$id" '{"state":"closed"}'
87
+ echo "completed sprint $id"
88
+ }
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ # user.sh — Jira user operations: whoami, search, get. Sourced; depends on
3
+ # http.sh (jira_curl/jira_lean). v3 keys users by accountId, v2 by username;
4
+ # the user search/lookup query param is version-gated accordingly.
5
+
6
+ jira_user() {
7
+ local action="${1:-}"; [[ $# -gt 0 ]] && shift
8
+ case "$action" in
9
+ whoami) _jira_user_whoami "$@";;
10
+ search) _jira_user_search "$@";;
11
+ get) _jira_user_get "$@";;
12
+ *) echo "Usage: jira user <whoami|search|get> ..." >&2; return 1;;
13
+ esac
14
+ }
15
+
16
+ # whoami: GET the current user, leaned to identity essentials.
17
+ _jira_user_whoami() {
18
+ jira_curl GET "/rest/api/${_JIRA_VER:-3}/myself" \
19
+ | jira_lean '{accountId, displayName, emailAddress}'
20
+ }
21
+
22
+ # search -q <QUERY>: find users; v3 keys on ?query, v2 on ?username.
23
+ _jira_user_search() {
24
+ local query=""
25
+ while [[ $# -gt 0 ]]; do
26
+ case "$1" in
27
+ -q|--query) query="$2"; shift 2;;
28
+ *) echo "user search: unknown option '$1'" >&2; return 1;;
29
+ esac
30
+ done
31
+ [[ -z "$query" ]] && { echo "Usage: jira user search -q <QUERY>" >&2; return 1; }
32
+ local param="query"
33
+ [[ "${_JIRA_VER:-3}" == "2" ]] && param="username"
34
+ jira_curl GET "/rest/api/${_JIRA_VER:-3}/user/search?${param}=$(jira_urlencode "$query")" \
35
+ | jira_lean '{users:[.[]|{accountId, displayName}]}'
36
+ }
37
+
38
+ # get <ACCOUNT_ID>: GET one user; v3 keys on ?accountId, v2 on ?username.
39
+ _jira_user_get() {
40
+ local id="${1:-}"
41
+ [[ -z "$id" ]] && { echo "Usage: jira user get <ACCOUNT_ID>" >&2; return 1; }
42
+ local param="accountId"
43
+ [[ "${_JIRA_VER:-3}" == "2" ]] && param="username"
44
+ jira_curl GET "/rest/api/${_JIRA_VER:-3}/user?${param}=$(jira_urlencode "$id")" | jira_lean '.'
45
+ }
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env bash
2
+ # worklog.sh — Jira worklog operations: add, list, delete. Sourced; depends on
3
+ # http.sh (jira_curl/jira_lean) and adf.sh (jira_text_to_adf). The add comment
4
+ # body is version-gated (ADF for v3, plain text for v2).
5
+
6
+ _jira_worklog_path() { printf '/rest/api/%s/issue' "${_JIRA_VER:-3}"; }
7
+
8
+ # jira_worklog <add|list|delete> ...: dispatch on the first arg.
9
+ jira_worklog() {
10
+ local action="${1:-}"; [[ $# -gt 0 ]] && shift
11
+ case "$action" in
12
+ add) _jira_worklog_add "$@";;
13
+ list) _jira_worklog_list "$@";;
14
+ delete) _jira_worklog_delete "$@";;
15
+ *) echo "Usage: jira worklog <add|list|delete> ..." >&2; return 1;;
16
+ esac
17
+ }
18
+
19
+ # _jira_worklog_add <KEY> -t <TIME> [-c COMMENT]: log time against an issue.
20
+ _jira_worklog_add() {
21
+ local key="${1:-}"; [[ $# -gt 0 ]] && shift
22
+ local time="" comment="" has_comment=""
23
+ while [[ $# -gt 0 ]]; do
24
+ case "$1" in
25
+ -t|--time) time="$2"; shift 2;;
26
+ -c|--comment) comment="$2"; has_comment=1; shift 2;;
27
+ *) echo "worklog add: unknown option '$1'" >&2; return 1;;
28
+ esac
29
+ done
30
+ [[ -z "$key" || -z "$time" ]] && {
31
+ echo "Usage: jira worklog add <KEY> -t <TIME> [-c COMMENT]" >&2
32
+ return 1
33
+ }
34
+ local body
35
+ body=$(jq -nc --arg ts "$time" '{timeSpent:$ts}')
36
+ if [[ -n "$has_comment" ]]; then
37
+ body=$(jq -c --argjson c "$(jira_text_to_adf "$comment")" '.comment=$c' <<<"$body")
38
+ fi
39
+ jira_curl POST "$(_jira_worklog_path)/$key/worklog" "$body" | jira_lean '.'
40
+ }
41
+
42
+ # _jira_worklog_list <KEY>: list worklog entries for an issue.
43
+ _jira_worklog_list() {
44
+ local key="${1:-}"
45
+ [[ -z "$key" ]] && { echo "Usage: jira worklog list <KEY>" >&2; return 1; }
46
+ jira_curl GET "$(_jira_worklog_path)/$key/worklog" \
47
+ | jira_lean '{worklogs:[.worklogs[]|{id, author:.author.displayName, timeSpent, started}]}'
48
+ }
49
+
50
+ # _jira_worklog_delete <KEY> <WORKLOG_ID>: remove one worklog entry.
51
+ _jira_worklog_delete() {
52
+ local key="${1:-}" wid="${2:-}"
53
+ [[ -z "$key" || -z "$wid" ]] && { echo "Usage: jira worklog delete <KEY> <WORKLOG_ID>" >&2; return 1; }
54
+ jira_curl DELETE "$(_jira_worklog_path)/$key/worklog/$wid"
55
+ echo "deleted worklog $wid"
56
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "pr-babysit",
3
+ "version": "6.8.0",
4
+ "description": "Babysit a PR until review threads, the review-bot verdict, and CI are clear, using Claude cron or a durable Codex goal with isolated worktrees.",
5
+ "author": {
6
+ "name": "Falconiere Barbosa",
7
+ "email": "hello@falconiere.io"
8
+ },
9
+ "homepage": "https://github.com/falconiere/toolu",
10
+ "repository": "https://github.com/falconiere/toolu.git",
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "claude-code",
14
+ "pr",
15
+ "review",
16
+ "ci",
17
+ "automation"
18
+ ],
19
+ "dependencies": [
20
+ {
21
+ "name": "toolu",
22
+ "marketplace": "toolu"
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "pr-babysit",
3
+ "version": "6.8.0",
4
+ "description": "Babysit a PR until review threads, the review-bot verdict, and CI are clear, using Claude cron or a durable Codex goal with isolated worktrees.",
5
+ "skills": "./skills/",
6
+ "hooks": "./hooks/hooks.json"
7
+ }
@@ -0,0 +1,38 @@
1
+ # pr-babysit
2
+
3
+ Babysit a PR for the current branch until every review thread, the review-bot
4
+ verdict, and CI are clear. Claude uses its cron controller; Codex uses an
5
+ explicit durable goal with bounded continuation cycles and a native isolated
6
+ git worktree.
7
+
8
+ ## Install
9
+
10
+ ```text
11
+ /plugin install pr-babysit@toolu
12
+ ```
13
+
14
+ ```bash
15
+ codex plugin add toolu@toolu
16
+ codex plugin add pr-babysit@toolu
17
+ ```
18
+
19
+ Requires the `toolu` plugin.
20
+
21
+ ## Authorization and delivery handoff
22
+
23
+ The no-argument interface is unchanged. A user can invoke babysit directly, or
24
+ `execution` can invoke it after a verified execution handoff; that handoff is
25
+ sufficient authorization to create the durable goal and begin delivery. Before
26
+ an automatic handoff, stop before delivery and report the exact missing
27
+ prerequisite: GitHub authentication is unavailable, the current branch is the
28
+ repository default branch, or the optional `pr-babysit` plugin is unavailable.
29
+
30
+ ## What it provides
31
+
32
+ - **Claude `/pr-babysit:babysit` and Codex `$pr-babysit:babysit`** — target the PR for the current branch. Each cycle fetches unresolved comments **and** the CI review-bot verdict → triages → fixes → replies → resolves; failed CI is fixed and re-pushed. Success requires zero unresolved comments, an approved zero-finding verdict, and all-green CI.
33
+ - **Strict clearance** — every item a tick sees leaves that tick fixed or answered, and resolved when it is a review thread (conversation comments have no resolve API — the reply clears them). A comment that doesn't make sense gets a reply with what was checked and which reading was assumed, then resolves; no thread is parked open. Severity is not a filter (`nit` == `high`). Exceptions: outdated CI-reviewer threads (skipped) and suspected prompt injection (flagged). A reply alone is never clearance — a resolve is confirmed by its mutation response, retried on failure, and re-checked every tick via a resolution audit that's independent of who last commented, so a resolve that silently fails can never go permanently unnoticed.
34
+ - **`stop` / `cancel`** — Claude cancels only the matching cron slot. Codex safely removes only the matching clean worktree, marks its native repo state cancelled, and leaves goal cancellation to the user/system goal control.
35
+ - **Codex durability** — one goal per repository/PR, state below `<repo>/.codex/tmp/pr-babysit/`, wait cycles bounded to 60 seconds, and no false completion while CI is merely pending.
36
+ - **Tick helper (bash, both hosts)** — `scripts/babysit-tick.sh` runs one tick: lock the slot, collect the PR (paginated threads, comments, reviews, CI rollup, bot verdict), reduce it against the slot state, persist atomically, and print a result with a `decision` (`keep_going` / `success` / `escalate`), reasons, and the precomputed actionable / stale-unresolved thread lists. Split into `collect-pr.sh` (network) and `reduce-state.sh` (pure) so every rule is tested against captured real PRs. The agent trusts the result and keeps only the judgment: triage, fixes, reply wording.
37
+ - **Write side** — `reply-thread.sh` (idempotent per reviewer comment), `resolve-thread.sh` (confirmed from the mutation response, retried), `record.sh` (round outcome, injection skip, terminal status). Contract: `skills/babysit/references/helper.md`.
38
+ - **`parse-verdict.sh`** — extracts the structured verdict from the CI review-bot comment.
@@ -0,0 +1,13 @@
1
+ # Babysit a PR
2
+
3
+ Read `${CLAUDE_PLUGIN_ROOT}/workflows/babysit.md` completely and execute its
4
+ Claude Code controller plus the shared strict-clearance workflow. Pass
5
+ `$ARGUMENTS` as the workflow input. Claude Code retains the cron-driven
6
+ continuation mechanism defined there.
7
+
8
+ Each tick is one command: `bash "${CLAUDE_PLUGIN_ROOT}/scripts/babysit-tick.sh"`
9
+ with `--state-file /tmp/pr-babysit-<slot>.json`; its result contract is
10
+ `${CLAUDE_PLUGIN_ROOT}/skills/babysit/references/helper.md`. Trust that
11
+ result — never write a polling script or controller of your own, and
12
+ never re-fetch with ad-hoc `gh` calls what it already reports — and act through
13
+ `reply-thread.sh`, `resolve-thread.sh` and `record.sh`.
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ # Codex lacks a plugin-dependency manifest field. Warn when this dependent
3
+ # plugin is installed without the toolu core, using Codex's authoritative list.
4
+ set -u
5
+
6
+ [ "${TOOLU_HOST_OVERRIDE:-}" != claude ] || exit 0
7
+ [ -n "${PLUGIN_ROOT:-}" ] || exit 0
8
+ command -v codex >/dev/null 2>&1 || exit 0
9
+ command -v jq >/dev/null 2>&1 || exit 0
10
+
11
+ installed=$(codex plugin list --json 2>/dev/null) || exit 0
12
+ jq -e 'any(.installed[]?; .pluginId == "toolu@toolu" and
13
+ ((has("installed") | not) or (.installed == true)) and
14
+ ((has("enabled") | not) or (.enabled == true)))' \
15
+ <<<"$installed" >/dev/null 2>&1 && exit 0
16
+
17
+ jq -n '{hookSpecificOutput:{
18
+ hookEventName:"SessionStart",
19
+ additionalContext:"WARN: this plugin requires the toolu core. Install it first with: codex plugin add toolu@toolu"
20
+ }}'
@@ -0,0 +1,15 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|resume|clear|compact",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/check-toolu.sh"
10
+ }
11
+ ]
12
+ }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env bash
2
+ # babysit-tick.sh — the one command a babysit tick runs, on either host.
3
+ #
4
+ # lock the slot → (validate prior state) → collect → reduce → persist → print
5
+ #
6
+ # Usage: babysit-tick.sh --repo <owner/repo> --pr <n> --state-file <path>
7
+ # [--snapshot-out <path>] [--snapshot-in <path>]
8
+ # [--page-size N] [--timeout SECONDS] [--now <iso8601>]
9
+ # --state-file the host's slot path, passed verbatim by the workflow
10
+ # (/tmp/pr-babysit-<slot>.json on Claude,
11
+ # <repo>/.codex/tmp/pr-babysit/<slot>.json on Codex)
12
+ # --snapshot-out full snapshot location (default: beside the state file)
13
+ # --snapshot-in skip collection and reduce a captured snapshot (tests,
14
+ # replay); its repo/number must match the arguments
15
+ # --now timestamp override for tests (default: UTC now)
16
+ #
17
+ # stdout: exactly one JSON document — the result (exit 0) or a structured
18
+ # error (non-zero). stderr: diagnostics only.
19
+ # Exit: 0 result · 2 usage · 3 structured error, prior state preserved
20
+ # (pr.lastError updated when a prior state exists) · 75 slot locked.
21
+ # Nothing here reads a session-specific environment variable for the repo,
22
+ # PR, home directory or plugin root: the root comes from this file's path.
23
+ set -euo pipefail
24
+
25
+ PB_SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)
26
+ # shellcheck source=lib/common.sh
27
+ . "$PB_SCRIPT_DIR/lib/common.sh"
28
+ # shellcheck source=lib/lock.sh
29
+ . "$PB_SCRIPT_DIR/lib/lock.sh"
30
+
31
+ repo=""; number=""; state_file=""; snapshot_out=""; snapshot_in=""; page_size=""; timeout=""; now=""
32
+ while [ $# -gt 0 ]; do
33
+ case "$1" in
34
+ --repo) repo="${2:-}"; shift 2 ;;
35
+ --pr) number="${2:-}"; shift 2 ;;
36
+ --state-file) state_file="${2:-}"; shift 2 ;;
37
+ --snapshot-out) snapshot_out="${2:-}"; shift 2 ;;
38
+ --snapshot-in) snapshot_in="${2:-}"; shift 2 ;;
39
+ --page-size) page_size="${2:-}"; shift 2 ;;
40
+ --timeout) timeout="${2:-}"; shift 2 ;;
41
+ --now) now="${2:-}"; shift 2 ;;
42
+ *) pb_fail usage "babysit-tick.sh: unknown argument: $1" ;;
43
+ esac
44
+ done
45
+ [[ "$repo" =~ ^[^/[:space:]]+/[^/[:space:]]+$ ]] || pb_fail usage "babysit-tick.sh: --repo <owner/repo> required"
46
+ [[ "$number" =~ ^[0-9]+$ ]] || pb_fail usage "babysit-tick.sh: --pr <n> required"
47
+ [ -n "$state_file" ] || pb_fail usage "babysit-tick.sh: --state-file <path> required"
48
+ [ -z "$page_size" ] || [[ "$page_size" =~ ^[0-9]+$ ]] || pb_fail usage "babysit-tick.sh: --page-size must be an integer"
49
+ [ -z "$timeout" ] || [[ "$timeout" =~ ^[0-9]+$ ]] || pb_fail usage "babysit-tick.sh: --timeout must be an integer"
50
+ [ -n "$now" ] || now=$(pb_now)
51
+ [[ "$now" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ ]] || pb_fail usage "babysit-tick.sh: --now must be YYYY-MM-DDTHH:MM:SSZ"
52
+ [ -n "$snapshot_out" ] || snapshot_out="${state_file%.json}.snapshot.json"
53
+
54
+ pb_require jq
55
+ [ -n "$snapshot_in" ] || pb_require gh
56
+ pb_init
57
+ pb_mktmpdir
58
+ mkdir -p "$(dirname "$state_file")" || pb_fail usage "babysit-tick.sh: cannot create $(dirname "$state_file")"
59
+ pb_lock_acquire "$state_file" || pb_lock_fail "$state_file"
60
+
61
+ # --- prior state: validate before touching the network ----------------------
62
+ # reduce-state.sh repeats these checks; running them first means a malformed
63
+ # or foreign state file fails in milliseconds and never costs API calls.
64
+ have_state=0
65
+ if [ -e "$state_file" ]; then
66
+ have_state=1
67
+ pb_json_valid "$state_file" || pb_fail state_malformed "babysit-tick.sh: state file is not valid JSON: $state_file" '{"source":"state"}'
68
+ jq -e '.version == 2' "$state_file" >/dev/null 2>&1 \
69
+ || pb_fail state_malformed "babysit-tick.sh: state file is not version 2: $state_file" "$(jq -c '{source:"state", version:(.version // null)}' "$state_file")"
70
+ jq -e --arg repo "$repo" --argjson number "$number" '.repo == $repo and .number == $number' "$state_file" >/dev/null 2>&1 \
71
+ || pb_fail slot_mismatch "babysit-tick.sh: state file belongs to $(jq -r '"\(.repo)#\(.number)"' "$state_file"), asked for $repo#$number" \
72
+ "$(jq -c --arg repo "$repo" --argjson number "$number" '{source:"state", state:{repo, number}, requested:{repo:$repo, number:$number}}' "$state_file")"
73
+ fi
74
+
75
+ # _record_error DOC — on a collection failure, keep the prior state but stamp
76
+ # pr.lastError so the next reader sees what happened and when.
77
+ _record_error() {
78
+ local doc="$1" entry
79
+ [ "$have_state" -eq 1 ] || return 0
80
+ # A structured error always carries errors[0]; anything else is recorded as
81
+ # an api_error with the raw text so the failure is never lost.
82
+ entry=$(jq -c '.errors[0] // empty' <<<"$doc" 2>/dev/null || true)
83
+ [ -n "$entry" ] || entry=$(jq -nc --arg m "$doc" '{code:"api_error", message:$m}')
84
+ pb_atomic_write_json "$state_file" jq -c --argjson e "$entry" --arg now "$now" \
85
+ '.pr.lastError = {code: ($e.code // "api_error"), message: ($e.message // ""), at: $now}' "$state_file" \
86
+ || echo "babysit-tick.sh: could not record lastError in $state_file" >&2
87
+ }
88
+
89
+ # _exit_with_doc DOC — print a structured error and exit with the code its
90
+ # errors[0].code maps to, so the exit code and the document never disagree.
91
+ _exit_with_doc() {
92
+ local doc="$1"
93
+ printf '%s\n' "$doc"
94
+ exit "$(pb_exit_code "$(jq -r '.errors[0].code // "api_error"' <<<"$doc")")"
95
+ }
96
+
97
+ # --- snapshot: collect, or take the one handed in ----------------------------
98
+ snap="$PB_TMPDIR/snapshot.json"
99
+ if [ -n "$snapshot_in" ]; then
100
+ [ -f "$snapshot_in" ] && pb_json_valid "$snapshot_in" || pb_fail invalid_json "babysit-tick.sh: --snapshot-in is not a JSON file: $snapshot_in" '{"source":"snapshot"}'
101
+ jq -e --arg repo "$repo" --argjson number "$number" '.repo == $repo and .number == $number' "$snapshot_in" >/dev/null 2>&1 \
102
+ || pb_fail slot_mismatch "babysit-tick.sh: --snapshot-in is for $(jq -r '"\(.repo)#\(.number)"' "$snapshot_in"), asked for $repo#$number" '{"source":"snapshot"}'
103
+ cp "$snapshot_in" "$snap"
104
+ else
105
+ collect_args=(--repo "$repo" --pr "$number" --out "$snap")
106
+ [ -z "$page_size" ] || collect_args+=(--page-size "$page_size")
107
+ [ -z "$timeout" ] || collect_args+=(--timeout "$timeout")
108
+ rc=0
109
+ bash "$PB_SCRIPT_DIR/collect-pr.sh" "${collect_args[@]}" >"$PB_TMPDIR/collect.out" || rc=$?
110
+ if [ "$rc" -ne 0 ]; then
111
+ doc=$(grep '^{' "$PB_TMPDIR/collect.out" | tail -n 1 || true)
112
+ [ -n "$doc" ] || doc=$(pb_error api_error "babysit-tick.sh: collect-pr.sh exited $rc without a structured error" '{"source":"collect"}')
113
+ _record_error "$doc"
114
+ _exit_with_doc "$doc"
115
+ fi
116
+ fi
117
+
118
+ # --- reduce -------------------------------------------------------------------
119
+ rc=0
120
+ bash "$PB_SCRIPT_DIR/reduce-state.sh" --snapshot "$snap" --state "$state_file" --now "$now" \
121
+ --state-out "$PB_TMPDIR/state.next.json" --result-out "$PB_TMPDIR/result.json" \
122
+ --state-path "$state_file" --snapshot-path "$snapshot_out" >"$PB_TMPDIR/reduce.out" || rc=$?
123
+ if [ "$rc" -ne 0 ]; then
124
+ doc=$(grep '^{' "$PB_TMPDIR/reduce.out" | tail -n 1 || true)
125
+ [ -n "$doc" ] || doc=$(pb_error invalid_json "babysit-tick.sh: reduce-state.sh exited $rc without a structured error" '{"source":"reduce"}')
126
+ _record_error "$doc"
127
+ _exit_with_doc "$doc"
128
+ fi
129
+
130
+ # --- persist: snapshot first (the state points at it), then state -----------
131
+ pb_atomic_write_json "$snapshot_out" cat "$snap" || pb_fail invalid_json "babysit-tick.sh: could not write $snapshot_out" '{"source":"snapshot_out"}'
132
+ pb_atomic_write_json "$state_file" cat "$PB_TMPDIR/state.next.json" || pb_fail invalid_json "babysit-tick.sh: could not write $state_file" '{"source":"state"}'
133
+ cat "$PB_TMPDIR/result.json"