pr-shepherd 0.56.6 → 0.57.1

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 (421) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +150 -280
  3. package/bin/api.d.mts +25 -3
  4. package/bin/api.mjs +51 -13
  5. package/bin/checks/classify.d.mts +1 -0
  6. package/bin/checks/classify.mjs +1 -1
  7. package/bin/checks/conclusions.d.mts +5 -1
  8. package/bin/checks/conclusions.mjs +8 -2
  9. package/bin/checks/superseded.d.mts +8 -2
  10. package/bin/checks/superseded.mjs +24 -7
  11. package/bin/classify/loader.d.mts +2 -0
  12. package/bin/classify/loader.mjs +15 -0
  13. package/bin/classify/standard/author.d.mts +3 -0
  14. package/bin/classify/standard/author.mjs +10 -0
  15. package/bin/classify/standard/codecov-report.d.mts +4 -0
  16. package/bin/classify/standard/codecov-report.mjs +10 -0
  17. package/bin/classify/standard/coderabbit-notices.d.mts +8 -0
  18. package/bin/classify/standard/coderabbit-notices.mjs +38 -0
  19. package/bin/classify/standard/codex-notices.d.mts +8 -0
  20. package/bin/classify/standard/codex-notices.mjs +34 -0
  21. package/bin/classify/standard/dependabot-notices.d.mts +4 -0
  22. package/bin/classify/standard/dependabot-notices.mjs +15 -0
  23. package/bin/classify/standard/gemini-notices.d.mts +4 -0
  24. package/bin/classify/standard/gemini-notices.mjs +13 -0
  25. package/bin/classify/standard/index.d.mts +7 -0
  26. package/bin/classify/standard/index.mjs +21 -0
  27. package/bin/classify/standard/kilo-no-issues.d.mts +4 -0
  28. package/bin/classify/standard/kilo-no-issues.mjs +17 -0
  29. package/bin/classify/standard/sourcery-notices.d.mts +8 -0
  30. package/bin/classify/standard/sourcery-notices.mjs +20 -0
  31. package/bin/cli/api-usage-formatter.mjs +6 -1
  32. package/bin/cli/apply-merge-handler.d.mts +3 -0
  33. package/bin/cli/apply-merge-handler.mjs +73 -0
  34. package/bin/cli/arg-flags.d.mts +2 -0
  35. package/bin/cli/arg-flags.mjs +47 -0
  36. package/bin/cli/args.mjs +10 -41
  37. package/bin/cli/clean-handler.d.mts +1 -0
  38. package/bin/cli/clean-handler.mjs +71 -0
  39. package/bin/cli/default-poll.mjs +3 -0
  40. package/bin/cli/event-state-scope.d.mts +19 -0
  41. package/bin/cli/event-state-scope.mjs +60 -0
  42. package/bin/cli/fix-formatter.mjs +5 -11
  43. package/bin/cli/handlers.d.mts +0 -1
  44. package/bin/cli/handlers.mjs +8 -64
  45. package/bin/cli/help-command-pages.d.mts +62 -18
  46. package/bin/cli/help-command-pages.mjs +59 -15
  47. package/bin/cli/help-iterate-poll-pages.d.mts +2 -2
  48. package/bin/cli/help-iterate-poll-pages.mjs +6 -0
  49. package/bin/cli/help-log-file-page.d.mts +1 -1
  50. package/bin/cli/help-log-file-page.mjs +4 -1
  51. package/bin/cli/help-top-page.d.mts +1 -1
  52. package/bin/cli/help-top-page.mjs +5 -0
  53. package/bin/cli/help.d.mts +63 -19
  54. package/bin/cli/help.mjs +2 -0
  55. package/bin/cli/iterate-branch-segment.d.mts +2 -0
  56. package/bin/cli/iterate-branch-segment.mjs +12 -7
  57. package/bin/cli/iterate-flags.d.mts +7 -1
  58. package/bin/cli/iterate-flags.mjs +38 -0
  59. package/bin/cli/iterate-formatter.d.mts +6 -6
  60. package/bin/cli/iterate-formatter.mjs +56 -22
  61. package/bin/cli/iterate-instructions.mjs +45 -15
  62. package/bin/cli/iterate-lean.mjs +39 -14
  63. package/bin/cli/iterate-merge-formatter.d.mts +6 -0
  64. package/bin/cli/iterate-merge-formatter.mjs +24 -14
  65. package/bin/cli/iterate-verbose.mjs +3 -6
  66. package/bin/cli/list-formatters.mjs +3 -1
  67. package/bin/cli/mutate-formatter.mjs +10 -5
  68. package/bin/cli/next-check-format.d.mts +7 -0
  69. package/bin/cli/next-check-format.mjs +10 -0
  70. package/bin/cli/playbook-handler.d.mts +5 -0
  71. package/bin/cli/playbook-handler.mjs +40 -0
  72. package/bin/cli/poll-handler.mjs +7 -6
  73. package/bin/cli/poll-summary-formatter.mjs +11 -2
  74. package/bin/cli/poll-targets.mjs +2 -0
  75. package/bin/cli/resolve-validators.mjs +2 -2
  76. package/bin/cli/runner.mjs +6 -1
  77. package/bin/cli/stack-layer-view.d.mts +33 -0
  78. package/bin/cli/stack-layer-view.mjs +1 -0
  79. package/bin/cli/stack-overview.d.mts +10 -28
  80. package/bin/cli/stack-overview.mjs +39 -14
  81. package/bin/cli/transport-args.d.mts +5 -0
  82. package/bin/cli/transport-args.mjs +23 -0
  83. package/bin/cli/transport-formatter.d.mts +9 -0
  84. package/bin/cli/transport-formatter.mjs +13 -0
  85. package/bin/cli-parser.mjs +62 -7
  86. package/bin/commands/apply-merge-options.d.mts +4 -0
  87. package/bin/commands/apply-merge-options.mjs +34 -0
  88. package/bin/commands/apply-merge-output.d.mts +7 -0
  89. package/bin/commands/apply-merge-output.mjs +20 -0
  90. package/bin/commands/apply-merge-pull.d.mts +20 -0
  91. package/bin/commands/apply-merge-pull.mjs +15 -0
  92. package/bin/commands/apply-merge.d.mts +18 -0
  93. package/bin/commands/apply-merge.mjs +126 -0
  94. package/bin/commands/apply-queue-removal.mjs +7 -0
  95. package/bin/commands/check-annotations.mjs +28 -18
  96. package/bin/commands/check-execution-context.d.mts +5 -0
  97. package/bin/commands/check-execution-context.mjs +16 -1
  98. package/bin/commands/check-fingerprint.d.mts +26 -2
  99. package/bin/commands/check-fingerprint.mjs +64 -28
  100. package/bin/commands/check-terminal-report.mjs +11 -1
  101. package/bin/commands/check-unreported.d.mts +6 -2
  102. package/bin/commands/check-unreported.mjs +32 -12
  103. package/bin/commands/check-wait-detectors.d.mts +35 -0
  104. package/bin/commands/check-wait-detectors.mjs +107 -0
  105. package/bin/commands/check.d.mts +4 -2
  106. package/bin/commands/check.mjs +130 -40
  107. package/bin/commands/event-instructions.d.mts +11 -0
  108. package/bin/commands/event-instructions.mjs +27 -0
  109. package/bin/commands/event-mode.d.mts +14 -0
  110. package/bin/commands/event-mode.mjs +138 -0
  111. package/bin/commands/iterate/api-usage.mjs +4 -2
  112. package/bin/commands/iterate/base.mjs +4 -0
  113. package/bin/commands/iterate/check-blocker-gate.mjs +14 -2
  114. package/bin/commands/iterate/check-instructions.d.mts +9 -10
  115. package/bin/commands/iterate/check-instructions.mjs +23 -22
  116. package/bin/commands/iterate/classify.mjs +19 -5
  117. package/bin/commands/iterate/escalate.mjs +13 -3
  118. package/bin/commands/iterate/fix-code.mjs +26 -9
  119. package/bin/commands/iterate/fix-instruction-threads.d.mts +0 -8
  120. package/bin/commands/iterate/fix-instruction-threads.mjs +0 -16
  121. package/bin/commands/iterate/fix-loop-instruction.d.mts +9 -0
  122. package/bin/commands/iterate/fix-loop-instruction.mjs +35 -0
  123. package/bin/commands/iterate/helpers.mjs +15 -2
  124. package/bin/commands/iterate/index.mjs +57 -16
  125. package/bin/commands/iterate/mark-ready.mjs +50 -10
  126. package/bin/commands/iterate/merge-plan.d.mts +19 -0
  127. package/bin/commands/iterate/merge-plan.mjs +88 -0
  128. package/bin/commands/iterate/merge-state.mjs +3 -0
  129. package/bin/commands/iterate/merge.d.mts +4 -15
  130. package/bin/commands/iterate/merge.mjs +18 -51
  131. package/bin/commands/iterate/merged-base-pull-requests.d.mts +2 -6
  132. package/bin/commands/iterate/merged-base-pull-requests.mjs +15 -11
  133. package/bin/commands/iterate/native-stack-rebase.mjs +11 -0
  134. package/bin/commands/iterate/render.d.mts +8 -2
  135. package/bin/commands/iterate/render.mjs +28 -42
  136. package/bin/commands/iterate/stack-trunk-conflict.mjs +17 -1
  137. package/bin/commands/iterate/stale-ancestry.mjs +4 -1
  138. package/bin/commands/iterate/stall.d.mts +5 -1
  139. package/bin/commands/iterate/stall.mjs +19 -7
  140. package/bin/commands/iterate/thread-mutation-routing.d.mts +1 -0
  141. package/bin/commands/iterate/thread-mutation-routing.mjs +4 -2
  142. package/bin/commands/iterate/unreported-required.mjs +5 -2
  143. package/bin/commands/mark-files-as-viewed.mjs +7 -0
  144. package/bin/commands/next-check.d.mts +31 -0
  145. package/bin/commands/next-check.mjs +46 -0
  146. package/bin/commands/playbook.d.mts +13 -0
  147. package/bin/commands/playbook.mjs +48 -0
  148. package/bin/commands/poll-mode.d.mts +8 -0
  149. package/bin/commands/poll-mode.mjs +14 -0
  150. package/bin/commands/poll-progress.d.mts +1 -0
  151. package/bin/commands/poll-progress.mjs +6 -3
  152. package/bin/commands/poll-quota.d.mts +3 -2
  153. package/bin/commands/poll-quota.mjs +7 -3
  154. package/bin/commands/poll-summary-explicit-instructions.d.mts +2 -0
  155. package/bin/commands/poll-summary-explicit-instructions.mjs +10 -1
  156. package/bin/commands/poll-summary-instructions.mjs +16 -17
  157. package/bin/commands/poll-summary.mjs +42 -17
  158. package/bin/commands/poll.d.mts +0 -1
  159. package/bin/commands/poll.mjs +11 -19
  160. package/bin/commands/quota-selection.d.mts +2 -1
  161. package/bin/commands/quota-selection.mjs +12 -7
  162. package/bin/commands/resolve-mutate.mjs +33 -5
  163. package/bin/commands/rest-stack-merge-readiness.d.mts +4 -0
  164. package/bin/commands/rest-stack-merge-readiness.mjs +74 -0
  165. package/bin/commands/rule-auto-resolve.mjs +9 -3
  166. package/bin/commands/shepherd-journal.d.mts +5 -4
  167. package/bin/commands/shepherd-journal.mjs +9 -8
  168. package/bin/commands/stack-drain.d.mts +1 -9
  169. package/bin/commands/stack-drain.mjs +50 -37
  170. package/bin/commands/stack-handoffs.d.mts +10 -0
  171. package/bin/commands/stack-handoffs.mjs +25 -0
  172. package/bin/commands/stack-stall.d.mts +5 -0
  173. package/bin/commands/stack-stall.mjs +11 -0
  174. package/bin/commands/stack-work.d.mts +5 -0
  175. package/bin/commands/stack-work.mjs +32 -0
  176. package/bin/comments/existing-reply-scan.d.mts +17 -0
  177. package/bin/comments/existing-reply-scan.mjs +41 -0
  178. package/bin/comments/rate-limit.mjs +2 -2
  179. package/bin/comments/resolve.d.mts +7 -1
  180. package/bin/comments/resolve.mjs +108 -18
  181. package/bin/comments/rest-reply.d.mts +9 -0
  182. package/bin/comments/rest-reply.mjs +95 -0
  183. package/bin/comments/rest-review-mutations.d.mts +22 -0
  184. package/bin/comments/rest-review-mutations.mjs +125 -0
  185. package/bin/comments/review-visibility.d.mts +1 -1
  186. package/bin/comments/review-visibility.mjs +3 -2
  187. package/bin/comments/sha-poll.mjs +7 -3
  188. package/bin/comments/thread-visibility.mjs +10 -5
  189. package/bin/comments/uncertain-replies.d.mts +11 -0
  190. package/bin/comments/uncertain-replies.mjs +121 -0
  191. package/bin/comments/uncertain-reply-guards.d.mts +10 -0
  192. package/bin/comments/uncertain-reply-guards.mjs +94 -0
  193. package/bin/comments/visible-comments.d.mts +1 -1
  194. package/bin/comments/visible-comments.mjs +4 -2
  195. package/bin/config/load.d.mts +37 -1
  196. package/bin/config/load.mjs +73 -1
  197. package/bin/config.json +6 -2
  198. package/bin/exit-codes.d.mts +3 -0
  199. package/bin/exit-codes.mjs +8 -0
  200. package/bin/github/api-telemetry-aggregate.d.mts +15 -4
  201. package/bin/github/api-telemetry-aggregate.mjs +57 -18
  202. package/bin/github/api-telemetry.d.mts +4 -0
  203. package/bin/github/api-telemetry.mjs +22 -8
  204. package/bin/github/apply-review-preflight.d.mts +20 -0
  205. package/bin/github/apply-review-preflight.mjs +61 -0
  206. package/bin/github/base-behind-cache.d.mts +15 -0
  207. package/bin/github/base-behind-cache.mjs +18 -0
  208. package/bin/github/batch-parse-suites.d.mts +4 -2
  209. package/bin/github/batch-parse-suites.mjs +6 -1
  210. package/bin/github/batch-parser-helpers.mjs +40 -2
  211. package/bin/github/batch-parsers.mjs +1 -0
  212. package/bin/github/batch-raw-rules.d.mts +26 -0
  213. package/bin/github/batch-raw-types.d.mts +8 -6
  214. package/bin/github/batch-stack-evidence.d.mts +28 -0
  215. package/bin/github/batch-stack-evidence.mjs +62 -0
  216. package/bin/github/batch.d.mts +15 -2
  217. package/bin/github/batch.mjs +34 -17
  218. package/bin/github/captured-proxy-agent.d.mts +3 -0
  219. package/bin/github/captured-proxy-agent.mjs +8 -0
  220. package/bin/github/check-annotations-batch.d.mts +0 -5
  221. package/bin/github/check-annotations-batch.mjs +25 -4
  222. package/bin/github/check-annotations.mjs +3 -1
  223. package/bin/github/client.d.mts +8 -19
  224. package/bin/github/client.mjs +45 -13
  225. package/bin/github/fingerprint-fields.d.mts +8 -0
  226. package/bin/github/fingerprint-fields.mjs +6 -1
  227. package/bin/github/fingerprint.d.mts +4 -2
  228. package/bin/github/fingerprint.mjs +6 -38
  229. package/bin/github/github-fetch.d.mts +2 -0
  230. package/bin/github/github-fetch.mjs +32 -0
  231. package/bin/github/gql/base-behind.gql +3 -0
  232. package/bin/github/gql/batch-pr-page.gql +16 -0
  233. package/bin/github/gql/batch-pr.gql +57 -0
  234. package/bin/github/gql/commit-check-contexts.gql +17 -0
  235. package/bin/github/gql/get-pr-body.gql +1 -0
  236. package/bin/github/gql/poll-stack-summary.gql +1 -0
  237. package/bin/github/gql/poll-summary-check-contexts.gql +5 -0
  238. package/bin/github/gql/poll-summary-fragment.gql +65 -15
  239. package/bin/github/gql/stack-handoff-check-runs.gql +42 -0
  240. package/bin/github/graphql-http.mjs +9 -4
  241. package/bin/github/graphql-internal-retry.d.mts +2 -0
  242. package/bin/github/graphql-internal-retry.mjs +1 -1
  243. package/bin/github/graphql-quota-probe.d.mts +6 -0
  244. package/bin/github/graphql-quota-probe.mjs +56 -0
  245. package/bin/github/graphql-session-refusal.d.mts +8 -0
  246. package/bin/github/graphql-session-refusal.mjs +14 -0
  247. package/bin/github/http-request.d.mts +1 -1
  248. package/bin/github/http-request.mjs +22 -6
  249. package/bin/github/http-utils.mjs +6 -0
  250. package/bin/github/legacy-dispatcher.d.mts +6 -0
  251. package/bin/github/legacy-dispatcher.mjs +44 -0
  252. package/bin/github/merge-target-rules.d.mts +12 -3
  253. package/bin/github/merge-target-rules.mjs +71 -34
  254. package/bin/github/mutation-policy.d.mts +6 -0
  255. package/bin/github/mutation-policy.mjs +21 -0
  256. package/bin/github/poll-summary-annotation-probe.mjs +17 -10
  257. package/bin/github/poll-summary-check-blockers.d.mts +1 -1
  258. package/bin/github/poll-summary-check-blockers.mjs +2 -2
  259. package/bin/github/poll-summary-check-hydration.mjs +25 -10
  260. package/bin/github/poll-summary-checks.d.mts +2 -2
  261. package/bin/github/poll-summary-checks.mjs +9 -6
  262. package/bin/github/poll-summary-command.d.mts +2 -0
  263. package/bin/github/poll-summary-command.mjs +24 -0
  264. package/bin/github/poll-summary-fingerprint.d.mts +1 -1
  265. package/bin/github/poll-summary-fingerprint.mjs +4 -2
  266. package/bin/github/poll-summary-handoff-fields.d.mts +3 -0
  267. package/bin/github/poll-summary-handoff-fields.mjs +73 -0
  268. package/bin/github/poll-summary-projector.d.mts +1 -1
  269. package/bin/github/poll-summary-projector.mjs +17 -24
  270. package/bin/github/poll-summary-raw.d.mts +70 -10
  271. package/bin/github/poll-summary-readiness.d.mts +6 -1
  272. package/bin/github/poll-summary-readiness.mjs +13 -4
  273. package/bin/github/poll-summary-rest-queue.d.mts +3 -0
  274. package/bin/github/poll-summary-rest-queue.mjs +12 -0
  275. package/bin/github/poll-summary-review.d.mts +1 -1
  276. package/bin/github/poll-summary-review.mjs +35 -20
  277. package/bin/github/poll-summary-route.mjs +19 -3
  278. package/bin/github/poll-summary-stack-page.d.mts +11 -0
  279. package/bin/github/poll-summary-stack-page.mjs +27 -0
  280. package/bin/github/poll-summary-unreported.d.mts +3 -1
  281. package/bin/github/poll-summary-unreported.mjs +6 -3
  282. package/bin/github/poll-summary.d.mts +4 -1
  283. package/bin/github/poll-summary.mjs +47 -33
  284. package/bin/github/queries.d.mts +7 -3
  285. package/bin/github/queries.mjs +8 -4
  286. package/bin/github/rate-limit-sample.d.mts +7 -0
  287. package/bin/github/rate-limit-sample.mjs +12 -0
  288. package/bin/github/reply-recovery-read.d.mts +50 -0
  289. package/bin/github/reply-recovery-read.mjs +118 -0
  290. package/bin/github/reply-thread-transcripts.d.mts +0 -1
  291. package/bin/github/reply-thread-transcripts.mjs +24 -1
  292. package/bin/github/rest-annotation-read.d.mts +5 -0
  293. package/bin/github/rest-annotation-read.mjs +44 -0
  294. package/bin/github/rest-batch-read.d.mts +12 -0
  295. package/bin/github/rest-batch-read.mjs +57 -0
  296. package/bin/github/rest-batch-snapshot.d.mts +30 -0
  297. package/bin/github/rest-batch-snapshot.mjs +115 -0
  298. package/bin/github/rest-batch-unavailable.d.mts +5 -0
  299. package/bin/github/rest-batch-unavailable.mjs +16 -0
  300. package/bin/github/rest-check-read.d.mts +34 -0
  301. package/bin/github/rest-check-read.mjs +124 -0
  302. package/bin/github/rest-comment-authorship.d.mts +7 -0
  303. package/bin/github/rest-comment-authorship.mjs +11 -0
  304. package/bin/github/rest-conditional-scope.d.mts +20 -0
  305. package/bin/github/rest-conditional-scope.mjs +44 -0
  306. package/bin/github/rest-explicit-summary-read.d.mts +3 -0
  307. package/bin/github/rest-explicit-summary-read.mjs +8 -0
  308. package/bin/github/rest-feedback-read.d.mts +36 -0
  309. package/bin/github/rest-feedback-read.mjs +147 -0
  310. package/bin/github/rest-feedback-validation.d.mts +5 -0
  311. package/bin/github/rest-feedback-validation.mjs +28 -0
  312. package/bin/github/rest-http.d.mts +12 -0
  313. package/bin/github/rest-http.mjs +108 -23
  314. package/bin/github/rest-identities.d.mts +22 -0
  315. package/bin/github/rest-identities.mjs +134 -0
  316. package/bin/github/rest-merge-stack-guard.d.mts +14 -0
  317. package/bin/github/rest-merge-stack-guard.mjs +41 -0
  318. package/bin/github/rest-merge.d.mts +29 -0
  319. package/bin/github/rest-merge.mjs +56 -0
  320. package/bin/github/rest-merged-base-read.d.mts +9 -0
  321. package/bin/github/rest-merged-base-read.mjs +48 -0
  322. package/bin/github/rest-pr-core.d.mts +52 -0
  323. package/bin/github/rest-pr-core.mjs +60 -0
  324. package/bin/github/rest-reader-core.d.mts +16 -0
  325. package/bin/github/rest-reader-core.mjs +154 -0
  326. package/bin/github/rest-review-read.d.mts +11 -0
  327. package/bin/github/rest-review-read.mjs +47 -0
  328. package/bin/github/rest-rules-read.d.mts +9 -0
  329. package/bin/github/rest-rules-read.mjs +154 -0
  330. package/bin/github/rest-session-refusal.d.mts +5 -0
  331. package/bin/github/rest-session-refusal.mjs +21 -0
  332. package/bin/github/rest-snapshot-context.d.mts +10 -0
  333. package/bin/github/rest-snapshot-context.mjs +1 -0
  334. package/bin/github/rest-stack-read.d.mts +21 -0
  335. package/bin/github/rest-stack-read.mjs +47 -0
  336. package/bin/github/rest-stack-summary.d.mts +8 -0
  337. package/bin/github/rest-stack-summary.mjs +62 -0
  338. package/bin/github/rest-summary-read.d.mts +7 -0
  339. package/bin/github/rest-summary-read.mjs +112 -0
  340. package/bin/github/rest-terminal-batch.d.mts +14 -0
  341. package/bin/github/rest-terminal-batch.mjs +45 -0
  342. package/bin/github/rest-text.mjs +7 -3
  343. package/bin/github/rest-thread-read.d.mts +4 -0
  344. package/bin/github/rest-thread-read.mjs +35 -0
  345. package/bin/github/rest-viewer-read.d.mts +2 -0
  346. package/bin/github/rest-viewer-read.mjs +41 -0
  347. package/bin/github/stack-bottom.d.mts +3 -0
  348. package/bin/github/stack-bottom.mjs +14 -0
  349. package/bin/github/stack-handoff-context.d.mts +25 -0
  350. package/bin/github/stack-handoff-context.mjs +12 -0
  351. package/bin/github/stack-handoff-page.d.mts +17 -0
  352. package/bin/github/stack-handoff-page.mjs +306 -0
  353. package/bin/github/stack-handoff.d.mts +16 -0
  354. package/bin/github/stack-handoff.mjs +115 -0
  355. package/bin/github/stack-order.d.mts +6 -0
  356. package/bin/github/stack-order.mjs +13 -0
  357. package/bin/github/stack-read.d.mts +7 -5
  358. package/bin/github/stack-read.mjs +22 -24
  359. package/bin/github/stack-size-hint.d.mts +3 -0
  360. package/bin/github/stack-size-hint.mjs +14 -0
  361. package/bin/github/suggestion-thread.mjs +27 -1
  362. package/bin/github/thread-comments.mjs +9 -1
  363. package/bin/github/transport-evidence.d.mts +20 -0
  364. package/bin/github/transport-evidence.mjs +26 -0
  365. package/bin/github/transport-mode.d.mts +2 -0
  366. package/bin/github/transport-mode.mjs +5 -0
  367. package/bin/github/transport.d.mts +19 -0
  368. package/bin/github/transport.mjs +126 -0
  369. package/bin/github/unsupported-rest.d.mts +4 -0
  370. package/bin/github/unsupported-rest.mjs +7 -0
  371. package/bin/mcp/server.mjs +52 -3
  372. package/bin/merge-status/derive.d.mts +2 -0
  373. package/bin/merge-status/derive.mjs +17 -13
  374. package/bin/merge-status/required-check-requirements.d.mts +7 -0
  375. package/bin/merge-status/required-check-requirements.mjs +15 -0
  376. package/bin/merge-status/requirements-format.d.mts +5 -0
  377. package/bin/merge-status/requirements-format.mjs +41 -12
  378. package/bin/merge-status/requirements.mjs +18 -7
  379. package/bin/state/base.mjs +41 -0
  380. package/bin/state/durable-state.d.mts +6 -0
  381. package/bin/state/durable-state.mjs +41 -0
  382. package/bin/state/graphql-quota-policy.mjs +23 -16
  383. package/bin/state/merge-request.d.mts +25 -0
  384. package/bin/state/merge-request.mjs +145 -0
  385. package/bin/state/pr-fingerprint.d.mts +15 -0
  386. package/bin/state/pr-fingerprint.mjs +22 -7
  387. package/bin/state/rest-cache.d.mts +3 -0
  388. package/bin/state/rest-cache.mjs +1 -0
  389. package/bin/state/rest-snapshot-report.d.mts +21 -0
  390. package/bin/state/rest-snapshot-report.mjs +52 -0
  391. package/bin/state/seen-comments.d.mts +3 -0
  392. package/bin/state/seen-comments.mjs +18 -0
  393. package/bin/types/api-usage.d.mts +9 -1
  394. package/bin/types/escalate.d.mts +1 -1
  395. package/bin/types/github.d.mts +23 -9
  396. package/bin/types/iterate.d.mts +24 -2
  397. package/bin/types/merge-action.d.mts +1 -1
  398. package/bin/types/merge-requirements.d.mts +7 -7
  399. package/bin/types/next-check.d.mts +12 -0
  400. package/bin/types/next-check.mjs +1 -0
  401. package/bin/types/poll-summary.d.mts +21 -0
  402. package/bin/types/report.d.mts +13 -1
  403. package/bin/types/review-thread.d.mts +1 -1
  404. package/bin/types.d.mts +1 -0
  405. package/bin/types.mjs +1 -0
  406. package/package.json +4 -3
  407. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  408. package/plugins/pr-shepherd/.codex.mcp.json +1 -1
  409. package/plugins/pr-shepherd/.mcp.json +1 -1
  410. package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +1 -1
  411. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +14 -51
  412. package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +12 -16
  413. package/plugins/pr-shepherd/skills/pr-shepherd/references/cloud-event-loop.md +14 -0
  414. package/plugins/pr-shepherd/skills/pr-shepherd/references/create-pr.md +10 -0
  415. package/plugins/pr-shepherd/skills/pr-shepherd/references/fix-code-loop.md +10 -0
  416. package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +9 -0
  417. package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +3 -1
  418. package/bin/github/gql/pr-fingerprint.gql +0 -75
  419. package/plugins/pr-shepherd/skills/pr-shepherd/references/journal.md +0 -7
  420. package/plugins/pr-shepherd/skills/pr-shepherd/references/review-mutations.md +0 -9
  421. package/plugins/pr-shepherd/skills/pr-shepherd/references/stack-merge.md +0 -7
@@ -0,0 +1,41 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+ import { join, resolve } from "node:path";
4
+ import { getEffectiveCwd } from "../execution-context.mjs";
5
+ /**
6
+ * A cloud session can be recycled between ticks, and its temp dir with it. The git common
7
+ * directory lives with the repository checkout, so state stored there survives whatever the
8
+ * session does with `TMPDIR`. Only a read-only `git rev-parse` is ever invoked.
9
+ */
10
+ const durableScope = new AsyncLocalStorage();
11
+ const commonDirByCwd = new Map();
12
+ /** Run work whose state (seen markers, stall timers, reply records) must outlive a temp dir. */
13
+ export function runWithDurableState(work) {
14
+ return durableScope.run(true, work);
15
+ }
16
+ /** Cloud sessions are always durable; other callers opt in through `runWithDurableState`. */
17
+ export function durableStateRequested() {
18
+ return durableScope.getStore() === true || process.env["CLAUDE_CODE_REMOTE"] === "true";
19
+ }
20
+ /** `<git-common-dir>/pr-shepherd-state`, or null when not requested or outside a repository. */
21
+ export function resolveDurableStateBase() {
22
+ if (!durableStateRequested())
23
+ return null;
24
+ const cwd = getEffectiveCwd();
25
+ if (!commonDirByCwd.has(cwd))
26
+ commonDirByCwd.set(cwd, readCommonDir(cwd));
27
+ return commonDirByCwd.get(cwd) ?? null;
28
+ }
29
+ function readCommonDir(cwd) {
30
+ try {
31
+ const out = execFileSync("git", ["rev-parse", "--git-common-dir"], {
32
+ cwd,
33
+ encoding: "utf8",
34
+ stdio: ["ignore", "pipe", "ignore"],
35
+ }).trim();
36
+ return out ? join(resolve(cwd, out), "pr-shepherd-state") : null;
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
@@ -1,13 +1,25 @@
1
+ import { mostConservativeSample } from "../github/rate-limit-sample.mjs";
1
2
  export function evaluateGraphqlQuotaWarning(bands, sample, previous, observedAt = Date.now() / 1000) {
2
3
  const windowRolled = previous !== null && sample.resetAt > previous.resetAt && observedAt >= previous.resetAt;
4
+ // A sample whose own window already ended before the saved one is stale, whatever its
5
+ // credential, resource, or limit: keep the saved sample and do not re-arm from it.
6
+ const stale = previous !== null && sample.resetAt < previous.resetAt && observedAt >= sample.resetAt;
3
7
  const rearm = previous === null ||
4
- previous.resource !== sample.resource ||
5
- previous.limit !== sample.limit ||
6
- windowRolled ||
7
- fingerprintChanged(previous, sample);
8
- const effective = !rearm && previous !== null && usageRegressed(previous, sample)
9
- ? newerSavedSample(previous)
10
- : sample;
8
+ (!stale &&
9
+ (previous.resource !== sample.resource ||
10
+ previous.limit !== sample.limit ||
11
+ windowRolled ||
12
+ fingerprintChanged(previous, sample)));
13
+ // Same window: the lowest remaining and the highest used seen so far, so an older sample (or a
14
+ // proxy route whose X-RateLimit-Used disagrees) never lowers the warning level.
15
+ const effective = rearm || previous === null
16
+ ? sample
17
+ : stale
18
+ ? savedSample(previous)
19
+ : {
20
+ ...mostConservativeSample(savedSample(previous), sample),
21
+ resetAt: Math.max(previous.resetAt, sample.resetAt),
22
+ };
11
23
  const warned = new Set(rearm || previous === null ? [] : previous.warnedThresholds);
12
24
  const rearmEpoch = rearm ? (previous?.rearmEpoch ?? 0) + 1 : (previous?.rearmEpoch ?? 0);
13
25
  const crossed = bands.filter((band) => effective.remaining * 100 <= effective.limit * band.remainingPercent);
@@ -15,7 +27,9 @@ export function evaluateGraphqlQuotaWarning(bands, sample, previous, observedAt
15
27
  for (const band of crossed)
16
28
  warned.add(band.remainingPercent);
17
29
  const active = newCrossed.at(-1);
18
- const credentialFingerprint = effective.credentialFingerprint ?? previous?.credentialFingerprint;
30
+ const credentialFingerprint = stale
31
+ ? previous?.credentialFingerprint
32
+ : (sample.credentialFingerprint ?? previous?.credentialFingerprint);
19
33
  const state = {
20
34
  resource: effective.resource,
21
35
  limit: effective.limit,
@@ -49,14 +63,7 @@ function fingerprintChanged(previous, sample) {
49
63
  sample.credentialFingerprint !== undefined &&
50
64
  previous.credentialFingerprint !== sample.credentialFingerprint);
51
65
  }
52
- /** An older observation of the same window: lower used, or higher remaining when used is absent. */
53
- function usageRegressed(previous, sample) {
54
- if (sample.used !== undefined && previous.lastUsed !== undefined) {
55
- return sample.used < previous.lastUsed;
56
- }
57
- return sample.remaining > previous.lastRemaining;
58
- }
59
- function newerSavedSample(previous) {
66
+ function savedSample(previous) {
60
67
  return {
61
68
  resource: previous.resource,
62
69
  limit: previous.limit,
@@ -0,0 +1,25 @@
1
+ import type { RestMergeOptions, RestMergeResponse } from "../github/rest-merge.mts";
2
+ export interface MergeRequestRecord {
3
+ version: 1;
4
+ options: RestMergeOptions;
5
+ startedAtUnix: number;
6
+ uuid?: string;
7
+ response?: RestMergeResponse;
8
+ uncertain?: true;
9
+ /** Identifies the one consumable source of a permanently recorded successor. */
10
+ replacementToken?: string;
11
+ }
12
+ type Key = {
13
+ owner: string;
14
+ repo: string;
15
+ pr: number;
16
+ };
17
+ export declare function readMergeRequest(key: Key): Promise<MergeRequestRecord | null>;
18
+ /** Persist intent exclusively before contacting GitHub; another process cannot submit it twice. */
19
+ export declare function claimMergeRequest(key: Key, record: MergeRequestRecord): Promise<boolean>;
20
+ /** Consume one durable successor of a failed request or an enqueue guarded by an older head. */
21
+ export declare function replaceFailedMergeRequest(key: Key, previous: MergeRequestRecord, next: MergeRequestRecord): Promise<boolean>;
22
+ export declare function writeMergeRequest(key: Key, record: MergeRequestRecord): Promise<void>;
23
+ /** A delayed status poll can update only its own intent, never a later main request. */
24
+ export declare function writeMergeRequestStatus(key: Key, record: MergeRequestRecord): Promise<void>;
25
+ export {};
@@ -0,0 +1,145 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { link, mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ import { resolvePrStatePath } from "./base.mjs";
5
+ import { restMergeStackGuardKey, validateRestMergeStackGuard, } from "../github/rest-merge-stack-guard.mjs";
6
+ const path = (key) => resolvePrStatePath(key, "merge-async.json");
7
+ const generation = (record) => createHash("sha256")
8
+ .update(JSON.stringify([
9
+ record.options.requireSha,
10
+ record.options.mergeAction,
11
+ record.options.mergeMethod,
12
+ record.startedAtUnix,
13
+ record.replacementToken,
14
+ ...(record.options.expectedStack
15
+ ? [restMergeStackGuardKey(record.options.expectedStack)]
16
+ : []),
17
+ ]))
18
+ .digest("hex")
19
+ .slice(0, 24);
20
+ const statusPath = (key, record) => resolvePrStatePath(key, `merge-async-status-${generation(record)}.json`);
21
+ export async function readMergeRequest(key) {
22
+ let raw;
23
+ try {
24
+ raw = await readFile(path(key), "utf8");
25
+ }
26
+ catch (error) {
27
+ if (error.code === "ENOENT")
28
+ return null;
29
+ throw error;
30
+ }
31
+ const record = parseRecord(raw);
32
+ try {
33
+ const status = parseRecord(await readFile(statusPath(key, record), "utf8"));
34
+ if (generation(status) !== generation(record))
35
+ throw new Error("Asynchronous merge status does not match its persisted intent");
36
+ return status;
37
+ }
38
+ catch (error) {
39
+ if (error.code === "ENOENT")
40
+ return record;
41
+ throw error;
42
+ }
43
+ }
44
+ function parseRecord(raw) {
45
+ const record = JSON.parse(raw);
46
+ if (record?.version !== 1 ||
47
+ !/^[0-9a-f]{40}$/.test(record.options?.requireSha ?? "") ||
48
+ !["direct_merge", "merge_queue", "default"].includes(record.options.mergeAction) ||
49
+ !Number.isSafeInteger(record.startedAtUnix) ||
50
+ (record.uuid !== undefined && !/^[0-9a-f-]{36}$/i.test(record.uuid)) ||
51
+ (record.replacementToken !== undefined &&
52
+ !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(record.replacementToken))) {
53
+ throw new Error("Invalid asynchronous merge state; outcome must be reconciled before another request");
54
+ }
55
+ if (record.options.expectedStack !== undefined) {
56
+ validateRestMergeStackGuard(record.options.expectedStack);
57
+ if (record.options.expectedStack.prefix.at(-1)?.headRefOid !== record.options.requireSha)
58
+ throw new Error("Invalid asynchronous merge state: expectedStack head differs from guarded SHA");
59
+ }
60
+ return record;
61
+ }
62
+ /** Persist intent exclusively before contacting GitHub; another process cannot submit it twice. */
63
+ export async function claimMergeRequest(key, record) {
64
+ await mkdir(dirname(path(key)), { recursive: true });
65
+ try {
66
+ await writeFile(path(key), `${JSON.stringify(record)}\n`, { flag: "wx" });
67
+ return true;
68
+ }
69
+ catch (error) {
70
+ if (error.code === "EEXIST")
71
+ return false;
72
+ throw error;
73
+ }
74
+ }
75
+ /** Consume one durable successor of a failed request or an enqueue guarded by an older head. */
76
+ export async function replaceFailedMergeRequest(key, previous, next) {
77
+ if (previous.uncertain ||
78
+ (previous.response?.status !== "failed" &&
79
+ !(previous.response?.status === "enqueued" &&
80
+ previous.options.requireSha !== next.options.requireSha)))
81
+ return false;
82
+ const stem = resolvePrStatePath(key, `merge-async-successor-${generation(previous)}`);
83
+ const candidate = { ...next, replacementToken: randomUUID() };
84
+ const source = `${stem}-${candidate.replacementToken}.json`;
85
+ let created = false;
86
+ let published = false;
87
+ try {
88
+ await writeFile(source, `${JSON.stringify(candidate)}\n`, { flag: "wx" });
89
+ created = true;
90
+ if (JSON.stringify(await readMergeRequest(key)) !== JSON.stringify(previous))
91
+ return false;
92
+ let persisted = candidate;
93
+ try {
94
+ // Publish only complete bytes. This permanent marker must never be consumed or rewritten.
95
+ await link(source, `${stem}.json`);
96
+ published = true;
97
+ }
98
+ catch (error) {
99
+ if (error.code !== "EEXIST")
100
+ throw error;
101
+ persisted = parseRecord(await readFile(`${stem}.json`, "utf8"));
102
+ if (!persisted.replacementToken)
103
+ throw new Error("Legacy merge successor has no recovery token; reconcile its outcome");
104
+ if (persisted.options.requireSha !== next.options.requireSha ||
105
+ persisted.options.mergeAction !== next.options.mergeAction ||
106
+ persisted.options.mergeMethod !== next.options.mergeMethod ||
107
+ restMergeStackGuardKey(persisted.options.expectedStack) !==
108
+ restMergeStackGuardKey(next.options.expectedStack))
109
+ return false;
110
+ }
111
+ if (JSON.stringify(await readMergeRequest(key)) !== JSON.stringify(previous))
112
+ return false;
113
+ try {
114
+ // Never recreate this source: its absence means another caller already consumed the intent.
115
+ await rename(`${stem}-${persisted.replacementToken}.json`, path(key));
116
+ }
117
+ catch (error) {
118
+ if (error.code === "ENOENT")
119
+ return false;
120
+ throw error;
121
+ }
122
+ return true;
123
+ }
124
+ finally {
125
+ if (created && !published)
126
+ await unlink(source).catch(() => { });
127
+ }
128
+ }
129
+ export async function writeMergeRequest(key, record) {
130
+ await writeRecord(path(key), record);
131
+ }
132
+ /** A delayed status poll can update only its own intent, never a later main request. */
133
+ export async function writeMergeRequestStatus(key, record) {
134
+ await writeRecord(statusPath(key, record), record);
135
+ }
136
+ async function writeRecord(destination, record) {
137
+ const temporary = `${destination}.${randomUUID()}.tmp`;
138
+ try {
139
+ await writeFile(temporary, `${JSON.stringify(record)}\n`);
140
+ await rename(temporary, destination);
141
+ }
142
+ finally {
143
+ await unlink(temporary).catch(() => { });
144
+ }
145
+ }
@@ -6,6 +6,15 @@ export interface StoredPrFingerprint {
6
6
  inputDigest: string;
7
7
  fingerprint: PrFingerprint;
8
8
  report: ShepherdReport;
9
+ /**
10
+ * Conditional REST change detectors read before the GraphQL snapshot that produced
11
+ * `report`, and when that snapshot ran (unix ms). Dropped whenever the report is rewritten.
12
+ */
13
+ detectors?: StoredWaitDetectors;
14
+ }
15
+ export interface StoredWaitDetectors {
16
+ digest: string;
17
+ fullAt: number;
9
18
  }
10
19
  export declare function fingerprintInputDigest(config: PrShepherdConfig): string;
11
20
  export declare function loadPrFingerprint(key: {
@@ -18,3 +27,9 @@ export declare function storePrFingerprint(key: {
18
27
  repo: string;
19
28
  pr: number;
20
29
  }, fingerprint: PrFingerprint, report: ShepherdReport, config: PrShepherdConfig): Promise<void>;
30
+ /** Record the detector digest read before this tick's GraphQL snapshot beside its report. */
31
+ export declare function attachWaitDetectors(key: {
32
+ owner: string;
33
+ repo: string;
34
+ pr: number;
35
+ }, stored: StoredPrFingerprint, detectors: StoredWaitDetectors): Promise<void>;
@@ -5,7 +5,13 @@ import { dirname } from "node:path";
5
5
  import { resolvePrStatePath } from "./base.mjs";
6
6
  import { discoverRuleFiles } from "../classify/loader.mjs";
7
7
  import { getEffectiveCwd } from "../execution-context.mjs";
8
- const VERSION = 3;
8
+ const VERSION = 4;
9
+ let packageVersion;
10
+ /** Bundled standard rules change with the package, so their digest input is its version. */
11
+ function readPackageVersion() {
12
+ packageVersion ??= JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version;
13
+ return packageVersion;
14
+ }
9
15
  export function fingerprintInputDigest(config) {
10
16
  const hash = createHash("sha256");
11
17
  hash.update(JSON.stringify({
@@ -21,6 +27,9 @@ export function fingerprintInputDigest(config) {
21
27
  neverCancelRuns: config.actions.neverCancelRuns,
22
28
  workWhileQueued: config.actions.workWhileQueued,
23
29
  },
30
+ standardRules: (config.standardRules?.length ?? 0) > 0
31
+ ? { names: config.standardRules, version: readPackageVersion() }
32
+ : undefined,
24
33
  }));
25
34
  for (const file of discoverRuleFiles(getEffectiveCwd())) {
26
35
  hash.update("\0");
@@ -48,17 +57,23 @@ export async function loadPrFingerprint(key) {
48
57
  }
49
58
  }
50
59
  export async function storePrFingerprint(key, fingerprint, report, config) {
60
+ await writeStored(key, {
61
+ version: VERSION,
62
+ inputDigest: fingerprintInputDigest(config),
63
+ fingerprint,
64
+ report,
65
+ });
66
+ }
67
+ /** Record the detector digest read before this tick's GraphQL snapshot beside its report. */
68
+ export async function attachWaitDetectors(key, stored, detectors) {
69
+ await writeStored(key, { ...stored, detectors });
70
+ }
71
+ async function writeStored(key, payload) {
51
72
  const path = resolvePrStatePath(key, "fingerprint.json");
52
73
  let tmp;
53
74
  try {
54
75
  await mkdir(dirname(path), { recursive: true });
55
76
  tmp = `${path}.${randomUUID()}.tmp`;
56
- const payload = {
57
- version: VERSION,
58
- inputDigest: fingerprintInputDigest(config),
59
- fingerprint,
60
- report,
61
- };
62
77
  await writeFile(tmp, `${JSON.stringify(payload)}\n`, "utf8");
63
78
  await rename(tmp, path);
64
79
  tmp = undefined;
@@ -30,6 +30,8 @@ export interface EtagCacheEntry {
30
30
  body: unknown;
31
31
  storedAt: number;
32
32
  headSha?: string;
33
+ /** Pagination `Link` header; GitHub does not reliably repeat it on a 304. */
34
+ link?: string;
33
35
  }
34
36
  export interface DerivedCacheEntry<T = unknown> {
35
37
  kind: "derived";
@@ -43,6 +45,7 @@ export declare function storeEtagEntry(key: StateKey, name: string, fields: {
43
45
  etag: string;
44
46
  body: unknown;
45
47
  headSha?: string;
48
+ link?: string;
46
49
  }): Promise<void>;
47
50
  export declare function loadDerived<T = unknown>(key: StateKey, name: string): Promise<DerivedCacheEntry<T> | null>;
48
51
  export declare function storeDerived<T = unknown>(key: StateKey, name: string, value: T, headSha?: string): Promise<void>;
@@ -34,6 +34,7 @@ export async function storeEtagEntry(key, name, fields) {
34
34
  body: fields.body,
35
35
  storedAt: Date.now(),
36
36
  ...(fields.headSha !== undefined && { headSha: fields.headSha }),
37
+ ...(fields.link !== undefined && { link: fields.link }),
37
38
  });
38
39
  }
39
40
  export async function loadDerived(key, name) {
@@ -0,0 +1,21 @@
1
+ import type { PrShepherdConfig } from "../config/load.mts";
2
+ import type { ShepherdReport } from "../types.mts";
3
+ /**
4
+ * The last REST-transport report plus the digest of the conditional-read snapshot it was built
5
+ * from. The REST counterpart of `fingerprint.json`: when the next tick's reads are all 304 and
6
+ * hash to the same digest, the stored report is still current.
7
+ */
8
+ export interface StoredRestSnapshotReport {
9
+ version: number;
10
+ inputDigest: string;
11
+ snapshotDigest: string;
12
+ report: ShepherdReport;
13
+ }
14
+ type Key = {
15
+ owner: string;
16
+ repo: string;
17
+ pr: number;
18
+ };
19
+ export declare function loadRestSnapshotReport(key: Key): Promise<StoredRestSnapshotReport | null>;
20
+ export declare function storeRestSnapshotReport(key: Key, snapshotDigest: string, report: ShepherdReport, config: PrShepherdConfig): Promise<void>;
21
+ export {};
@@ -0,0 +1,52 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ import { resolvePrStatePath } from "./base.mjs";
5
+ import { fingerprintInputDigest } from "./pr-fingerprint.mjs";
6
+ const VERSION = 2;
7
+ export async function loadRestSnapshotReport(key) {
8
+ try {
9
+ const parsed = JSON.parse(await readFile(resolvePrStatePath(key, "rest-report.json"), "utf8"));
10
+ const record = parsed;
11
+ return record["version"] === VERSION &&
12
+ typeof record["inputDigest"] === "string" &&
13
+ typeof record["snapshotDigest"] === "string" &&
14
+ record["report"] !== null &&
15
+ typeof record["report"] === "object"
16
+ ? parsed
17
+ : null;
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ export async function storeRestSnapshotReport(key, snapshotDigest, report, config) {
24
+ const path = resolvePrStatePath(key, "rest-report.json");
25
+ let tmp;
26
+ try {
27
+ await mkdir(dirname(path), { recursive: true });
28
+ tmp = `${path}.${randomUUID()}.tmp`;
29
+ const payload = {
30
+ version: VERSION,
31
+ inputDigest: fingerprintInputDigest(config),
32
+ snapshotDigest,
33
+ report,
34
+ };
35
+ await writeFile(tmp, `${JSON.stringify(payload)}\n`, "utf8");
36
+ await rename(tmp, path);
37
+ tmp = undefined;
38
+ }
39
+ catch {
40
+ // An optimization only; a failed write just means the next tick rebuilds the report.
41
+ }
42
+ finally {
43
+ if (tmp !== undefined) {
44
+ try {
45
+ await unlink(tmp);
46
+ }
47
+ catch {
48
+ // best-effort cleanup
49
+ }
50
+ }
51
+ }
52
+ }
@@ -49,6 +49,9 @@ export declare function hasSeen(key: StateKey, id: string): Promise<boolean>;
49
49
  * All errors are silently swallowed — the marker is best-effort.
50
50
  */
51
51
  export declare function markSeen(key: StateKey, id: string, body: string): Promise<void>;
52
+ /** A definite permission denial suppresses generated retries until the feedback changes. */
53
+ export declare function markMutationDenied(key: StateKey, id: string, body?: string): Promise<void>;
54
+ export declare function mutationWasDenied(id: string, body: string, map: Map<string, SeenMarker>): boolean;
52
55
  export declare function markReviewInlineThreads(key: StateKey, reviewId: string, inlineThreadIds: readonly string[]): Promise<void>;
53
56
  /**
54
57
  * Write a marker after Shepherd successfully replies to a review thread.
@@ -105,6 +105,24 @@ export async function hasSeen(key, id) {
105
105
  export async function markSeen(key, id, body) {
106
106
  await writeSeenMarker(key, id, { bodyHash: hashBody(body) });
107
107
  }
108
+ /** A definite permission denial suppresses generated retries until the feedback changes. */
109
+ export async function markMutationDenied(key, id, body) {
110
+ let bodyHash = body === undefined ? undefined : hashBody(body);
111
+ if (bodyHash === undefined) {
112
+ try {
113
+ const marker = JSON.parse(await readFile(resolvePath(key, id), "utf8"));
114
+ bodyHash = marker.bodyHash;
115
+ }
116
+ catch {
117
+ return;
118
+ }
119
+ }
120
+ if (typeof bodyHash === "string")
121
+ await writeSeenMarker(key, id, { deniedMutationBodyHash: bodyHash });
122
+ }
123
+ export function mutationWasDenied(id, body, map) {
124
+ return map.get(id)?.deniedMutationBodyHash === hashBody(body);
125
+ }
108
126
  export async function markReviewInlineThreads(key, reviewId, inlineThreadIds) {
109
127
  await writeSeenMarker(key, reviewId, {
110
128
  inlineThreadIds: [...new Set(inlineThreadIds)].sort((a, b) => a.localeCompare(b)),
@@ -1,6 +1,9 @@
1
1
  export interface ApiResourceUsage {
2
2
  resource: string;
3
- requestCount: number;
3
+ /** Quota-consuming requests; excludes `notModified` responses. Omitted when 0. */
4
+ requestCount?: number;
5
+ /** Conditional reads answered 304 Not Modified (no quota consumed). Omitted when 0. */
6
+ notModified?: number;
4
7
  limit: number;
5
8
  used?: number;
6
9
  remaining: number;
@@ -29,6 +32,11 @@ export interface ApiUsage {
29
32
  credentialSources: string[];
30
33
  graphql?: GraphqlApiUsage;
31
34
  rest?: ApiResourceUsage[];
35
+ /**
36
+ * REST responses that carried no rate-limit headers, so no pool reported a charge (the Claude
37
+ * Code proxy's `ccr` routes). Omitted when 0.
38
+ */
39
+ unmeteredRestRequestCount?: number;
32
40
  }
33
41
  export interface GraphqlQuotaWarning {
34
42
  resource: "graphql" | "core" | "combined";
@@ -2,7 +2,7 @@ import type { AgentCheck, AgentComment, AgentThread } from "./report.mts";
2
2
  import type { ResolveCommand } from "./iterate.mts";
3
3
  import type { CheckStatus, Review } from "./github.mts";
4
4
  import type { MergeQueueRemovalStatus } from "./merge-requirements.mts";
5
- export type EscalateTrigger = "fix-thrash" | "base-branch-unknown" | "stall-timeout" | "required-checks-unreported" | "stall-state-unavailable" | "check-follow-up-unavailable" | "authorization-required" | "merge-queue-removed" | "merge-method-unavailable";
5
+ export type EscalateTrigger = "transport-unsupported" | "fix-thrash" | "base-branch-unknown" | "stall-timeout" | "required-checks-unreported" | "stall-state-unavailable" | "check-follow-up-unavailable" | "authorization-required" | "merge-queue-removed" | "merge-method-unavailable";
6
6
  export interface AgentStalledCheck {
7
7
  name: string;
8
8
  status: CheckStatus;
@@ -24,11 +24,11 @@ type RepositoryPermission = "NONE" | "READ" | "TRIAGE" | "WRITE" | "MAINTAIN" |
24
24
  /** Raw GitHub viewer fields used to gate remote actions. */
25
25
  export interface ViewerAuthorization {
26
26
  repositoryPermission: RepositoryPermission | null;
27
- viewerCanAdminister: boolean;
28
- viewerDidAuthor: boolean;
29
- viewerCanUpdate: boolean;
30
- viewerCanEnableAutoMerge: boolean;
31
- viewerCanEditFiles: boolean;
27
+ viewerCanAdminister?: boolean;
28
+ viewerDidAuthor?: boolean;
29
+ viewerCanUpdate?: boolean;
30
+ viewerCanEnableAutoMerge?: boolean;
31
+ viewerCanEditFiles?: boolean;
32
32
  headRepositoryPermission: RepositoryPermission | null;
33
33
  }
34
34
  /** Raw relationship between a comment author and the repository, as reported by GitHub. */
@@ -60,12 +60,17 @@ export interface CheckRun {
60
60
  workflowId?: string;
61
61
  /** True when GraphQL reported at least one CheckRun annotation. Omitted when false. */
62
62
  hasAnnotations?: boolean;
63
+ /**
64
+ * Every annotation on this run, when BatchPr's `annotations(first: 1)` page already held all
65
+ * of them. Internal: lets the check path skip `CheckRunAnnotationsBatch`; never rendered.
66
+ */
67
+ inlineAnnotations?: import("./check-annotations.mts").CheckAnnotation[];
63
68
  }
64
69
  export interface ReviewThread {
65
70
  id: string;
66
- isResolved: boolean;
67
- isOutdated: boolean;
68
- isMinimized: boolean;
71
+ isResolved?: boolean;
72
+ isOutdated?: boolean;
73
+ isMinimized?: boolean;
69
74
  viewerCanReply?: boolean;
70
75
  viewerCanResolve?: boolean;
71
76
  path: string | null;
@@ -98,7 +103,7 @@ export interface SuggestionBlock {
98
103
  }
99
104
  export interface PrComment {
100
105
  id: string;
101
- isMinimized: boolean;
106
+ isMinimized?: boolean;
102
107
  viewerCanMinimize?: boolean;
103
108
  author: string;
104
109
  authorType: AuthorType;
@@ -146,6 +151,13 @@ export interface MergeStatusResult {
146
151
  mergeRequirements?: MergeRequirements;
147
152
  }
148
153
  export interface BatchPrData extends BatchPrMergeFields {
154
+ /** Present when this snapshot was fetched through REST. */
155
+ transport?: "rest";
156
+ /** Fields unavailable from the selected transport; absence is never fabricated as false. */
157
+ transportUnavailable?: Array<{
158
+ field: string;
159
+ reason: string;
160
+ }>;
149
161
  nodeId: string;
150
162
  number: number;
151
163
  state: "OPEN" | "CLOSED" | "MERGED";
@@ -172,6 +184,8 @@ export interface BatchPrData extends BatchPrMergeFields {
172
184
  baseRefName: string;
173
185
  /** Base commit GitHub recorded for this PR (`PullRequest.baseRefOid`), not the branch's live tip. */
174
186
  baseRefOid?: string;
187
+ /** The base branch's live tip (`baseRef.target.oid`). GraphQL only; keys the BaseBehind cache. */
188
+ baseTipOid?: string;
175
189
  reviewRequests: Array<{
176
190
  login: string;
177
191
  }>;
@@ -6,6 +6,7 @@ import type { EscalateDetails } from "./escalate.mts";
6
6
  import type { MergeCommandPlan } from "./merge-action.mts";
7
7
  import type { ProtectedRun } from "./protected-run.mts";
8
8
  import type { ApiUsage, GraphqlQuotaWarning } from "./api-usage.mts";
9
+ import type { NextCheck } from "./next-check.mts";
9
10
  export type ShepherdAction = "wait" | "ready" | "fix_code" | "mark_ready" | "merge" | "cancel" | "escalate";
10
11
  export interface IterateResultSummary {
11
12
  passing: number;
@@ -15,6 +16,11 @@ export interface IterateResultSummary {
15
16
  superseded: number;
16
17
  }
17
18
  export interface IterateResultBase {
19
+ transport?: "rest";
20
+ transportUnavailable?: Array<{
21
+ field: string;
22
+ reason: string;
23
+ }>;
18
24
  pr: number;
19
25
  repo: string;
20
26
  status: ShepherdStatus;
@@ -58,6 +64,10 @@ export interface IterateResultBase {
58
64
  apiUsage?: ApiUsage;
59
65
  quotaWarning?: GraphqlQuotaWarning;
60
66
  fingerprintReused?: true;
67
+ /** Set to `event` when this result came from event mode: one tick, no sleeping. Omitted in poll mode. */
68
+ pollMode?: "event";
69
+ /** Event mode only: when to come back if no GitHub event arrives first. Omitted for terminal actions. */
70
+ nextCheck?: NextCheck;
61
71
  /** Classification-rule resolve/minimize applied during this fetch. Omitted when nothing happened. */
62
72
  ruleAutoResolve?: RuleAutoResolveReport;
63
73
  }
@@ -79,9 +89,9 @@ interface IterateResultCancel extends IterateResultBase {
79
89
  log: string;
80
90
  }
81
91
  export interface ResolveCommand {
82
- /** Argv for spawn-style execution. May contain a `$DISMISS_MESSAGE` placeholder. `$HEAD_SHA` is never in `argv` — `renderResolveCommand` (from `commands/iterate/render.mts`) appends `--require-sha "$HEAD_SHA"` when rendering if `requiresHeadSha` is true. Use `renderResolveCommand` to render as a command string; don't naive-join. */
92
+ /** Argv for spawn-style execution. May contain a `$DISMISS_MESSAGE` placeholder. `--require-sha` is never in `argv` — `renderResolveCommand` (from `commands/iterate/render.mts`) appends `--require-sha HEAD` when rendering if `requiresHeadSha` is true. Use `renderResolveCommand` to render as a command string; don't naive-join. */
83
93
  argv: string[];
84
- /** When true, `renderResolveCommand` appends `--require-sha "$HEAD_SHA"` to the rendered command. */
94
+ /** When true, `renderResolveCommand` appends `--require-sha HEAD` to the rendered command. */
85
95
  requiresHeadSha: boolean;
86
96
  /** Whether the model must substitute $DISMISS_MESSAGE with a specific description of the fix. */
87
97
  requiresDismissMessage: boolean;
@@ -175,8 +185,20 @@ export interface IterateCommandOptions extends GlobalOptions {
175
185
  * the public `IterateInput` in api.mts.
176
186
  */
177
187
  fingerprintCache?: boolean;
188
+ /**
189
+ * Internal. Event mode passes a sink that receives the CI-start stall deadline (oldest
190
+ * unstarted check age + stall timeout) so `nextCheck` can wake before it. Excluded from
191
+ * the public `IterateInput` in api.mts.
192
+ */
193
+ stallDeadlineSink?: {
194
+ ciStartDeadlineSeconds?: number;
195
+ };
178
196
  /** Shepherd through readiness and emit the exact merge/queue command when ready. */
179
197
  merge?: boolean;
198
+ /** Override `poll.mode` for this call. `auto` resolves to `event` when `CLAUDE_CODE_REMOTE=true`. */
199
+ pollMode?: import("../config/load.mts").PollMode;
200
+ /** Override `iterate.instructions` for this call. */
201
+ instructions?: import("../config/load.mts").InstructionStyle;
180
202
  /**
181
203
  * Internal. Defers quota warnings until an until-terminal poll breaks. Set only by
182
204
  * `runPollCore`; excluded from the public `IterateInput` in api.mts.