pr-shepherd 0.56.6 → 0.57.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 (420) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +44 -27
  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/coderabbit-review-summary.d.mts +8 -0
  20. package/bin/classify/standard/coderabbit-review-summary.mjs +37 -0
  21. package/bin/classify/standard/codex-notices.d.mts +7 -0
  22. package/bin/classify/standard/codex-notices.mjs +36 -0
  23. package/bin/classify/standard/codex-review-summary.d.mts +7 -0
  24. package/bin/classify/standard/codex-review-summary.mjs +20 -0
  25. package/bin/classify/standard/dependabot-notices.d.mts +4 -0
  26. package/bin/classify/standard/dependabot-notices.mjs +15 -0
  27. package/bin/classify/standard/gemini-notices.d.mts +4 -0
  28. package/bin/classify/standard/gemini-notices.mjs +13 -0
  29. package/bin/classify/standard/index.d.mts +4 -0
  30. package/bin/classify/standard/index.mjs +22 -0
  31. package/bin/classify/standard/kilo-no-issues.d.mts +4 -0
  32. package/bin/classify/standard/kilo-no-issues.mjs +17 -0
  33. package/bin/classify/standard/sourcery-notices.d.mts +8 -0
  34. package/bin/classify/standard/sourcery-notices.mjs +20 -0
  35. package/bin/cli/api-usage-formatter.mjs +6 -1
  36. package/bin/cli/apply-merge-handler.d.mts +3 -0
  37. package/bin/cli/apply-merge-handler.mjs +73 -0
  38. package/bin/cli/arg-flags.d.mts +2 -0
  39. package/bin/cli/arg-flags.mjs +47 -0
  40. package/bin/cli/args.mjs +10 -41
  41. package/bin/cli/clean-handler.d.mts +1 -0
  42. package/bin/cli/clean-handler.mjs +71 -0
  43. package/bin/cli/default-poll.mjs +3 -0
  44. package/bin/cli/event-state-scope.d.mts +19 -0
  45. package/bin/cli/event-state-scope.mjs +60 -0
  46. package/bin/cli/fix-formatter.mjs +5 -11
  47. package/bin/cli/handlers.d.mts +0 -1
  48. package/bin/cli/handlers.mjs +8 -64
  49. package/bin/cli/help-command-pages.d.mts +62 -18
  50. package/bin/cli/help-command-pages.mjs +59 -15
  51. package/bin/cli/help-iterate-poll-pages.d.mts +2 -2
  52. package/bin/cli/help-iterate-poll-pages.mjs +6 -0
  53. package/bin/cli/help-log-file-page.d.mts +1 -1
  54. package/bin/cli/help-log-file-page.mjs +4 -1
  55. package/bin/cli/help-top-page.d.mts +1 -1
  56. package/bin/cli/help-top-page.mjs +5 -0
  57. package/bin/cli/help.d.mts +63 -19
  58. package/bin/cli/help.mjs +2 -0
  59. package/bin/cli/iterate-branch-segment.d.mts +2 -0
  60. package/bin/cli/iterate-branch-segment.mjs +12 -7
  61. package/bin/cli/iterate-flags.d.mts +7 -1
  62. package/bin/cli/iterate-flags.mjs +38 -0
  63. package/bin/cli/iterate-formatter.d.mts +6 -6
  64. package/bin/cli/iterate-formatter.mjs +56 -22
  65. package/bin/cli/iterate-instructions.mjs +45 -15
  66. package/bin/cli/iterate-lean.mjs +39 -14
  67. package/bin/cli/iterate-merge-formatter.d.mts +6 -0
  68. package/bin/cli/iterate-merge-formatter.mjs +24 -14
  69. package/bin/cli/iterate-verbose.mjs +3 -6
  70. package/bin/cli/list-formatters.mjs +3 -1
  71. package/bin/cli/mutate-formatter.mjs +10 -5
  72. package/bin/cli/next-check-format.d.mts +7 -0
  73. package/bin/cli/next-check-format.mjs +10 -0
  74. package/bin/cli/playbook-handler.d.mts +5 -0
  75. package/bin/cli/playbook-handler.mjs +40 -0
  76. package/bin/cli/poll-handler.mjs +7 -6
  77. package/bin/cli/poll-summary-formatter.mjs +11 -2
  78. package/bin/cli/poll-targets.mjs +2 -0
  79. package/bin/cli/resolve-validators.mjs +2 -2
  80. package/bin/cli/runner.mjs +6 -1
  81. package/bin/cli/stack-layer-view.d.mts +33 -0
  82. package/bin/cli/stack-layer-view.mjs +1 -0
  83. package/bin/cli/stack-overview.d.mts +10 -28
  84. package/bin/cli/stack-overview.mjs +39 -14
  85. package/bin/cli/transport-args.d.mts +5 -0
  86. package/bin/cli/transport-args.mjs +23 -0
  87. package/bin/cli/transport-formatter.d.mts +9 -0
  88. package/bin/cli/transport-formatter.mjs +13 -0
  89. package/bin/cli-parser.mjs +62 -7
  90. package/bin/commands/apply-merge-options.d.mts +4 -0
  91. package/bin/commands/apply-merge-options.mjs +34 -0
  92. package/bin/commands/apply-merge-output.d.mts +7 -0
  93. package/bin/commands/apply-merge-output.mjs +20 -0
  94. package/bin/commands/apply-merge-pull.d.mts +20 -0
  95. package/bin/commands/apply-merge-pull.mjs +15 -0
  96. package/bin/commands/apply-merge.d.mts +18 -0
  97. package/bin/commands/apply-merge.mjs +126 -0
  98. package/bin/commands/apply-queue-removal.mjs +7 -0
  99. package/bin/commands/check-annotations.mjs +28 -18
  100. package/bin/commands/check-execution-context.d.mts +5 -0
  101. package/bin/commands/check-execution-context.mjs +16 -1
  102. package/bin/commands/check-fingerprint.d.mts +26 -2
  103. package/bin/commands/check-fingerprint.mjs +64 -28
  104. package/bin/commands/check-terminal-report.mjs +11 -1
  105. package/bin/commands/check-unreported.d.mts +6 -2
  106. package/bin/commands/check-unreported.mjs +32 -12
  107. package/bin/commands/check-wait-detectors.d.mts +35 -0
  108. package/bin/commands/check-wait-detectors.mjs +107 -0
  109. package/bin/commands/check.d.mts +4 -2
  110. package/bin/commands/check.mjs +130 -40
  111. package/bin/commands/event-instructions.d.mts +11 -0
  112. package/bin/commands/event-instructions.mjs +27 -0
  113. package/bin/commands/event-mode.d.mts +14 -0
  114. package/bin/commands/event-mode.mjs +138 -0
  115. package/bin/commands/iterate/api-usage.mjs +4 -2
  116. package/bin/commands/iterate/base.mjs +4 -0
  117. package/bin/commands/iterate/check-blocker-gate.mjs +14 -2
  118. package/bin/commands/iterate/check-instructions.d.mts +9 -10
  119. package/bin/commands/iterate/check-instructions.mjs +23 -22
  120. package/bin/commands/iterate/classify.mjs +19 -5
  121. package/bin/commands/iterate/escalate.mjs +13 -3
  122. package/bin/commands/iterate/fix-code.mjs +26 -9
  123. package/bin/commands/iterate/fix-instruction-threads.d.mts +0 -8
  124. package/bin/commands/iterate/fix-instruction-threads.mjs +0 -16
  125. package/bin/commands/iterate/fix-loop-instruction.d.mts +9 -0
  126. package/bin/commands/iterate/fix-loop-instruction.mjs +35 -0
  127. package/bin/commands/iterate/helpers.mjs +15 -2
  128. package/bin/commands/iterate/index.mjs +26 -4
  129. package/bin/commands/iterate/mark-ready.mjs +50 -10
  130. package/bin/commands/iterate/merge-plan.d.mts +19 -0
  131. package/bin/commands/iterate/merge-plan.mjs +88 -0
  132. package/bin/commands/iterate/merge-state.mjs +3 -0
  133. package/bin/commands/iterate/merge.d.mts +4 -15
  134. package/bin/commands/iterate/merge.mjs +18 -51
  135. package/bin/commands/iterate/merged-base-pull-requests.d.mts +2 -6
  136. package/bin/commands/iterate/merged-base-pull-requests.mjs +15 -11
  137. package/bin/commands/iterate/native-stack-rebase.mjs +11 -0
  138. package/bin/commands/iterate/render.d.mts +8 -2
  139. package/bin/commands/iterate/render.mjs +28 -42
  140. package/bin/commands/iterate/stack-trunk-conflict.mjs +17 -1
  141. package/bin/commands/iterate/stale-ancestry.mjs +4 -1
  142. package/bin/commands/iterate/stall.d.mts +5 -1
  143. package/bin/commands/iterate/stall.mjs +19 -7
  144. package/bin/commands/iterate/thread-mutation-routing.d.mts +1 -0
  145. package/bin/commands/iterate/thread-mutation-routing.mjs +4 -2
  146. package/bin/commands/iterate/unreported-required.mjs +5 -2
  147. package/bin/commands/mark-files-as-viewed.mjs +7 -0
  148. package/bin/commands/next-check.d.mts +31 -0
  149. package/bin/commands/next-check.mjs +46 -0
  150. package/bin/commands/playbook.d.mts +13 -0
  151. package/bin/commands/playbook.mjs +48 -0
  152. package/bin/commands/poll-mode.d.mts +8 -0
  153. package/bin/commands/poll-mode.mjs +14 -0
  154. package/bin/commands/poll-progress.d.mts +1 -0
  155. package/bin/commands/poll-progress.mjs +6 -3
  156. package/bin/commands/poll-quota.d.mts +3 -2
  157. package/bin/commands/poll-quota.mjs +7 -3
  158. package/bin/commands/poll-summary-explicit-instructions.d.mts +2 -0
  159. package/bin/commands/poll-summary-explicit-instructions.mjs +10 -1
  160. package/bin/commands/poll-summary-instructions.mjs +16 -17
  161. package/bin/commands/poll-summary.mjs +42 -17
  162. package/bin/commands/poll.d.mts +0 -1
  163. package/bin/commands/poll.mjs +11 -19
  164. package/bin/commands/quota-selection.d.mts +2 -1
  165. package/bin/commands/quota-selection.mjs +12 -7
  166. package/bin/commands/resolve-mutate.mjs +33 -5
  167. package/bin/commands/rest-stack-merge-readiness.d.mts +4 -0
  168. package/bin/commands/rest-stack-merge-readiness.mjs +74 -0
  169. package/bin/commands/rule-auto-resolve.mjs +9 -3
  170. package/bin/commands/shepherd-journal.d.mts +5 -4
  171. package/bin/commands/shepherd-journal.mjs +9 -8
  172. package/bin/commands/stack-drain.d.mts +1 -9
  173. package/bin/commands/stack-drain.mjs +50 -37
  174. package/bin/commands/stack-handoffs.d.mts +10 -0
  175. package/bin/commands/stack-handoffs.mjs +25 -0
  176. package/bin/commands/stack-stall.d.mts +5 -0
  177. package/bin/commands/stack-stall.mjs +11 -0
  178. package/bin/commands/stack-work.d.mts +5 -0
  179. package/bin/commands/stack-work.mjs +32 -0
  180. package/bin/comments/existing-reply-scan.d.mts +17 -0
  181. package/bin/comments/existing-reply-scan.mjs +41 -0
  182. package/bin/comments/rate-limit.mjs +2 -2
  183. package/bin/comments/resolve.d.mts +7 -1
  184. package/bin/comments/resolve.mjs +108 -18
  185. package/bin/comments/rest-reply.d.mts +9 -0
  186. package/bin/comments/rest-reply.mjs +95 -0
  187. package/bin/comments/rest-review-mutations.d.mts +22 -0
  188. package/bin/comments/rest-review-mutations.mjs +125 -0
  189. package/bin/comments/review-visibility.d.mts +1 -1
  190. package/bin/comments/review-visibility.mjs +3 -2
  191. package/bin/comments/sha-poll.mjs +7 -3
  192. package/bin/comments/thread-visibility.mjs +10 -5
  193. package/bin/comments/uncertain-replies.d.mts +11 -0
  194. package/bin/comments/uncertain-replies.mjs +121 -0
  195. package/bin/comments/uncertain-reply-guards.d.mts +10 -0
  196. package/bin/comments/uncertain-reply-guards.mjs +94 -0
  197. package/bin/comments/visible-comments.d.mts +1 -1
  198. package/bin/comments/visible-comments.mjs +4 -2
  199. package/bin/config/load.d.mts +37 -1
  200. package/bin/config/load.mjs +73 -1
  201. package/bin/config.json +6 -2
  202. package/bin/exit-codes.d.mts +3 -0
  203. package/bin/exit-codes.mjs +8 -0
  204. package/bin/github/api-telemetry-aggregate.d.mts +15 -4
  205. package/bin/github/api-telemetry-aggregate.mjs +57 -18
  206. package/bin/github/api-telemetry.d.mts +4 -0
  207. package/bin/github/api-telemetry.mjs +22 -8
  208. package/bin/github/apply-review-preflight.d.mts +20 -0
  209. package/bin/github/apply-review-preflight.mjs +61 -0
  210. package/bin/github/base-behind-cache.d.mts +15 -0
  211. package/bin/github/base-behind-cache.mjs +18 -0
  212. package/bin/github/batch-parse-suites.d.mts +4 -2
  213. package/bin/github/batch-parse-suites.mjs +6 -1
  214. package/bin/github/batch-parser-helpers.mjs +40 -2
  215. package/bin/github/batch-parsers.mjs +1 -0
  216. package/bin/github/batch-raw-rules.d.mts +26 -0
  217. package/bin/github/batch-raw-types.d.mts +8 -6
  218. package/bin/github/batch-stack-evidence.d.mts +28 -0
  219. package/bin/github/batch-stack-evidence.mjs +62 -0
  220. package/bin/github/batch.d.mts +15 -2
  221. package/bin/github/batch.mjs +34 -17
  222. package/bin/github/check-annotations-batch.d.mts +0 -5
  223. package/bin/github/check-annotations-batch.mjs +25 -4
  224. package/bin/github/check-annotations.mjs +3 -1
  225. package/bin/github/client.d.mts +8 -19
  226. package/bin/github/client.mjs +45 -13
  227. package/bin/github/fingerprint-fields.d.mts +8 -0
  228. package/bin/github/fingerprint-fields.mjs +6 -1
  229. package/bin/github/fingerprint.d.mts +4 -2
  230. package/bin/github/fingerprint.mjs +6 -38
  231. package/bin/github/github-fetch.d.mts +2 -0
  232. package/bin/github/github-fetch.mjs +19 -0
  233. package/bin/github/gql/base-behind.gql +3 -0
  234. package/bin/github/gql/batch-pr-page.gql +16 -0
  235. package/bin/github/gql/batch-pr.gql +57 -0
  236. package/bin/github/gql/commit-check-contexts.gql +17 -0
  237. package/bin/github/gql/get-pr-body.gql +1 -0
  238. package/bin/github/gql/poll-stack-summary.gql +1 -0
  239. package/bin/github/gql/poll-summary-check-contexts.gql +5 -0
  240. package/bin/github/gql/poll-summary-fragment.gql +65 -15
  241. package/bin/github/gql/stack-handoff-check-runs.gql +42 -0
  242. package/bin/github/graphql-http.mjs +9 -4
  243. package/bin/github/graphql-internal-retry.d.mts +2 -0
  244. package/bin/github/graphql-internal-retry.mjs +1 -1
  245. package/bin/github/graphql-quota-probe.d.mts +6 -0
  246. package/bin/github/graphql-quota-probe.mjs +56 -0
  247. package/bin/github/graphql-session-refusal.d.mts +8 -0
  248. package/bin/github/graphql-session-refusal.mjs +14 -0
  249. package/bin/github/http-request.d.mts +1 -1
  250. package/bin/github/http-request.mjs +22 -6
  251. package/bin/github/http-utils.mjs +6 -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.mjs +4 -1
  272. package/bin/github/poll-summary-rest-queue.d.mts +3 -0
  273. package/bin/github/poll-summary-rest-queue.mjs +12 -0
  274. package/bin/github/poll-summary-review.d.mts +1 -1
  275. package/bin/github/poll-summary-review.mjs +23 -11
  276. package/bin/github/poll-summary-route.mjs +19 -3
  277. package/bin/github/poll-summary-stack-page.d.mts +11 -0
  278. package/bin/github/poll-summary-stack-page.mjs +27 -0
  279. package/bin/github/poll-summary-unreported.d.mts +3 -1
  280. package/bin/github/poll-summary-unreported.mjs +6 -3
  281. package/bin/github/poll-summary.d.mts +4 -1
  282. package/bin/github/poll-summary.mjs +47 -33
  283. package/bin/github/queries.d.mts +7 -3
  284. package/bin/github/queries.mjs +8 -4
  285. package/bin/github/rate-limit-sample.d.mts +7 -0
  286. package/bin/github/rate-limit-sample.mjs +12 -0
  287. package/bin/github/reply-recovery-read.d.mts +50 -0
  288. package/bin/github/reply-recovery-read.mjs +118 -0
  289. package/bin/github/reply-thread-transcripts.d.mts +0 -1
  290. package/bin/github/reply-thread-transcripts.mjs +24 -1
  291. package/bin/github/rest-annotation-read.d.mts +5 -0
  292. package/bin/github/rest-annotation-read.mjs +44 -0
  293. package/bin/github/rest-batch-read.d.mts +12 -0
  294. package/bin/github/rest-batch-read.mjs +57 -0
  295. package/bin/github/rest-batch-snapshot.d.mts +30 -0
  296. package/bin/github/rest-batch-snapshot.mjs +115 -0
  297. package/bin/github/rest-batch-unavailable.d.mts +5 -0
  298. package/bin/github/rest-batch-unavailable.mjs +16 -0
  299. package/bin/github/rest-check-read.d.mts +34 -0
  300. package/bin/github/rest-check-read.mjs +124 -0
  301. package/bin/github/rest-comment-authorship.d.mts +7 -0
  302. package/bin/github/rest-comment-authorship.mjs +11 -0
  303. package/bin/github/rest-conditional-scope.d.mts +20 -0
  304. package/bin/github/rest-conditional-scope.mjs +44 -0
  305. package/bin/github/rest-explicit-summary-read.d.mts +3 -0
  306. package/bin/github/rest-explicit-summary-read.mjs +8 -0
  307. package/bin/github/rest-feedback-read.d.mts +36 -0
  308. package/bin/github/rest-feedback-read.mjs +147 -0
  309. package/bin/github/rest-feedback-validation.d.mts +5 -0
  310. package/bin/github/rest-feedback-validation.mjs +28 -0
  311. package/bin/github/rest-http.d.mts +12 -0
  312. package/bin/github/rest-http.mjs +108 -23
  313. package/bin/github/rest-identities.d.mts +22 -0
  314. package/bin/github/rest-identities.mjs +134 -0
  315. package/bin/github/rest-merge-stack-guard.d.mts +14 -0
  316. package/bin/github/rest-merge-stack-guard.mjs +41 -0
  317. package/bin/github/rest-merge.d.mts +29 -0
  318. package/bin/github/rest-merge.mjs +56 -0
  319. package/bin/github/rest-merged-base-read.d.mts +9 -0
  320. package/bin/github/rest-merged-base-read.mjs +48 -0
  321. package/bin/github/rest-pr-core.d.mts +52 -0
  322. package/bin/github/rest-pr-core.mjs +60 -0
  323. package/bin/github/rest-reader-core.d.mts +16 -0
  324. package/bin/github/rest-reader-core.mjs +154 -0
  325. package/bin/github/rest-review-read.d.mts +11 -0
  326. package/bin/github/rest-review-read.mjs +47 -0
  327. package/bin/github/rest-rules-read.d.mts +9 -0
  328. package/bin/github/rest-rules-read.mjs +154 -0
  329. package/bin/github/rest-session-refusal.d.mts +5 -0
  330. package/bin/github/rest-session-refusal.mjs +21 -0
  331. package/bin/github/rest-snapshot-context.d.mts +10 -0
  332. package/bin/github/rest-snapshot-context.mjs +1 -0
  333. package/bin/github/rest-stack-read.d.mts +21 -0
  334. package/bin/github/rest-stack-read.mjs +47 -0
  335. package/bin/github/rest-stack-summary.d.mts +8 -0
  336. package/bin/github/rest-stack-summary.mjs +62 -0
  337. package/bin/github/rest-summary-read.d.mts +7 -0
  338. package/bin/github/rest-summary-read.mjs +112 -0
  339. package/bin/github/rest-terminal-batch.d.mts +14 -0
  340. package/bin/github/rest-terminal-batch.mjs +45 -0
  341. package/bin/github/rest-text.mjs +7 -3
  342. package/bin/github/rest-thread-read.d.mts +4 -0
  343. package/bin/github/rest-thread-read.mjs +35 -0
  344. package/bin/github/rest-viewer-read.d.mts +2 -0
  345. package/bin/github/rest-viewer-read.mjs +41 -0
  346. package/bin/github/stack-bottom.d.mts +3 -0
  347. package/bin/github/stack-bottom.mjs +14 -0
  348. package/bin/github/stack-handoff-context.d.mts +25 -0
  349. package/bin/github/stack-handoff-context.mjs +12 -0
  350. package/bin/github/stack-handoff-page.d.mts +17 -0
  351. package/bin/github/stack-handoff-page.mjs +306 -0
  352. package/bin/github/stack-handoff.d.mts +16 -0
  353. package/bin/github/stack-handoff.mjs +115 -0
  354. package/bin/github/stack-order.d.mts +6 -0
  355. package/bin/github/stack-order.mjs +13 -0
  356. package/bin/github/stack-read.d.mts +7 -5
  357. package/bin/github/stack-read.mjs +22 -24
  358. package/bin/github/stack-size-hint.d.mts +3 -0
  359. package/bin/github/stack-size-hint.mjs +14 -0
  360. package/bin/github/suggestion-thread.mjs +27 -1
  361. package/bin/github/thread-comments.mjs +9 -1
  362. package/bin/github/transport-evidence.d.mts +20 -0
  363. package/bin/github/transport-evidence.mjs +26 -0
  364. package/bin/github/transport-mode.d.mts +2 -0
  365. package/bin/github/transport-mode.mjs +5 -0
  366. package/bin/github/transport.d.mts +19 -0
  367. package/bin/github/transport.mjs +126 -0
  368. package/bin/github/unsupported-rest.d.mts +4 -0
  369. package/bin/github/unsupported-rest.mjs +7 -0
  370. package/bin/mcp/server.mjs +52 -3
  371. package/bin/merge-status/derive.d.mts +2 -0
  372. package/bin/merge-status/derive.mjs +17 -13
  373. package/bin/merge-status/required-check-requirements.d.mts +7 -0
  374. package/bin/merge-status/required-check-requirements.mjs +15 -0
  375. package/bin/merge-status/requirements-format.d.mts +5 -0
  376. package/bin/merge-status/requirements-format.mjs +41 -12
  377. package/bin/merge-status/requirements.mjs +18 -7
  378. package/bin/state/base.mjs +41 -0
  379. package/bin/state/durable-state.d.mts +6 -0
  380. package/bin/state/durable-state.mjs +41 -0
  381. package/bin/state/graphql-quota-policy.mjs +23 -16
  382. package/bin/state/merge-request.d.mts +25 -0
  383. package/bin/state/merge-request.mjs +145 -0
  384. package/bin/state/pr-fingerprint.d.mts +15 -0
  385. package/bin/state/pr-fingerprint.mjs +22 -7
  386. package/bin/state/rest-cache.d.mts +3 -0
  387. package/bin/state/rest-cache.mjs +1 -0
  388. package/bin/state/rest-snapshot-report.d.mts +21 -0
  389. package/bin/state/rest-snapshot-report.mjs +52 -0
  390. package/bin/state/seen-comments.d.mts +3 -0
  391. package/bin/state/seen-comments.mjs +18 -0
  392. package/bin/types/api-usage.d.mts +9 -1
  393. package/bin/types/escalate.d.mts +1 -1
  394. package/bin/types/github.d.mts +23 -9
  395. package/bin/types/iterate.d.mts +24 -2
  396. package/bin/types/merge-action.d.mts +1 -1
  397. package/bin/types/merge-requirements.d.mts +7 -7
  398. package/bin/types/next-check.d.mts +12 -0
  399. package/bin/types/next-check.mjs +1 -0
  400. package/bin/types/poll-summary.d.mts +21 -0
  401. package/bin/types/report.d.mts +13 -1
  402. package/bin/types/review-thread.d.mts +1 -1
  403. package/bin/types.d.mts +1 -0
  404. package/bin/types.mjs +1 -0
  405. package/package.json +3 -2
  406. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  407. package/plugins/pr-shepherd/.codex.mcp.json +1 -1
  408. package/plugins/pr-shepherd/.mcp.json +1 -1
  409. package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +1 -1
  410. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +14 -51
  411. package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +12 -16
  412. package/plugins/pr-shepherd/skills/pr-shepherd/references/cloud-event-loop.md +14 -0
  413. package/plugins/pr-shepherd/skills/pr-shepherd/references/create-pr.md +10 -0
  414. package/plugins/pr-shepherd/skills/pr-shepherd/references/fix-code-loop.md +10 -0
  415. package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +9 -0
  416. package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +2 -0
  417. package/bin/github/gql/pr-fingerprint.gql +0 -75
  418. package/plugins/pr-shepherd/skills/pr-shepherd/references/journal.md +0 -7
  419. package/plugins/pr-shepherd/skills/pr-shepherd/references/review-mutations.md +0 -9
  420. package/plugins/pr-shepherd/skills/pr-shepherd/references/stack-merge.md +0 -7
@@ -0,0 +1,31 @@
1
+ import type { NextCheck, NextCheckReason } from "../types/next-check.mts";
2
+ /** A queued PR is rechecked about every five minutes, matching the poll cadence for queue waits. */
3
+ export declare const MERGE_QUEUE_RECHECK_SECONDS = 300;
4
+ /** Backstop for a missed GitHub event; events normally wake the session long before this. */
5
+ export declare const SAFETY_NET_SECONDS = 3000;
6
+ export interface NextCheckCandidate {
7
+ reason: NextCheckReason;
8
+ seconds: number;
9
+ }
10
+ /** The earliest candidate as a `NextCheck`, with `at` rounded up to a whole minute. */
11
+ export declare function earliestNextCheck(candidates: NextCheckCandidate[], nowMs?: number): NextCheck | undefined;
12
+ interface NextCheckFacts {
13
+ /** The action Shepherd returned for this tick. */
14
+ action: "wait" | "ready" | "mark_ready" | "fix_code" | "merge" | "cancel" | "escalate";
15
+ /** Ready-delay seconds left; only meaningful for `ready`. */
16
+ remainingSeconds?: number;
17
+ queued?: boolean;
18
+ /** A `merge` tick whose printed command is a REST merge request, which can stay `pending`. */
19
+ restMerge?: boolean;
20
+ /** A native-stack draft hold: repeating this one-PR session cannot advance it. */
21
+ stackDraftHold?: boolean;
22
+ /** Unix seconds when an unchanged state would trip the stall timeout. */
23
+ stallDeadlineSeconds?: number;
24
+ }
25
+ /**
26
+ * Deadlines for one PR, or none when the caller must not wait for one. `mark_ready` has none:
27
+ * its tick saw a draft, so only an immediate rerun can start the ready-delay timer. A REST
28
+ * merge can return `pending`, so it keeps a wake-up to resume the recorded request.
29
+ */
30
+ export declare function nextCheckCandidates(facts: NextCheckFacts, nowMs: number): NextCheckCandidate[];
31
+ export {};
@@ -0,0 +1,46 @@
1
+ /** A queued PR is rechecked about every five minutes, matching the poll cadence for queue waits. */
2
+ export const MERGE_QUEUE_RECHECK_SECONDS = 300;
3
+ /** Backstop for a missed GitHub event; events normally wake the session long before this. */
4
+ export const SAFETY_NET_SECONDS = 3000;
5
+ /** The earliest candidate as a `NextCheck`, with `at` rounded up to a whole minute. */
6
+ export function earliestNextCheck(candidates, nowMs = Date.now()) {
7
+ const first = [...candidates].sort((a, b) => a.seconds - b.seconds)[0];
8
+ if (!first)
9
+ return undefined;
10
+ const atMs = Math.ceil((nowMs + Math.max(0, first.seconds) * 1000) / 60_000) * 60_000;
11
+ return {
12
+ at: new Date(atMs).toISOString().replace(/\.\d{3}Z$/, "Z"),
13
+ inSeconds: Math.max(0, Math.round((atMs - nowMs) / 1000)),
14
+ reason: first.reason,
15
+ };
16
+ }
17
+ /**
18
+ * Deadlines for one PR, or none when the caller must not wait for one. `mark_ready` has none:
19
+ * its tick saw a draft, so only an immediate rerun can start the ready-delay timer. A REST
20
+ * merge can return `pending`, so it keeps a wake-up to resume the recorded request.
21
+ */
22
+ export function nextCheckCandidates(facts, nowMs) {
23
+ if (facts.action === "merge" && facts.restMerge) {
24
+ return [{ reason: "merge-pending", seconds: MERGE_QUEUE_RECHECK_SECONDS }];
25
+ }
26
+ const terminal = ["cancel", "escalate", "merge", "mark_ready"].includes(facts.action);
27
+ if (terminal || facts.stackDraftHold)
28
+ return [];
29
+ const candidates = [];
30
+ if (facts.action === "ready" && (facts.remainingSeconds ?? 0) > 0) {
31
+ candidates.push({ reason: "ready-delay", seconds: facts.remainingSeconds });
32
+ }
33
+ else if (facts.queued && facts.action === "wait") {
34
+ candidates.push({ reason: "merge-queue", seconds: MERGE_QUEUE_RECHECK_SECONDS });
35
+ }
36
+ else {
37
+ candidates.push({ reason: "safety-net", seconds: SAFETY_NET_SECONDS });
38
+ }
39
+ if (facts.stallDeadlineSeconds !== undefined) {
40
+ candidates.push({
41
+ reason: "stall-timeout",
42
+ seconds: facts.stallDeadlineSeconds - nowMs / 1000,
43
+ });
44
+ }
45
+ return candidates;
46
+ }
@@ -0,0 +1,13 @@
1
+ export type PlaybookResult = {
2
+ playbooks: string[];
3
+ } | {
4
+ name: string;
5
+ content: string;
6
+ };
7
+ /**
8
+ * Resolve a playbook by name (case-insensitive; spaces and hyphens are equivalent) or file
9
+ * slug. With no name, list every playbook name. Unknown names fail with the available names.
10
+ */
11
+ export declare function runPlaybook(name?: string): PlaybookResult;
12
+ /** Markdown projection shared by the CLI text format and the MCP content channel. */
13
+ export declare function formatPlaybookResult(result: PlaybookResult): string;
@@ -0,0 +1,48 @@
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import { EXIT, ShepherdError } from "../exit-codes.mjs";
3
+ /**
4
+ * Playbooks ship with the package under `plugins/pr-shepherd/skills/pr-shepherd/references/`
5
+ * (package.json `files` includes `plugins/**`), so a playbook pointer resolves
6
+ * with or without the skill installed. From `bin/commands/` or `src/commands/`, `../../` is
7
+ * the package root.
8
+ */
9
+ const REFERENCES_DIR = new URL("../../plugins/pr-shepherd/skills/pr-shepherd/references/", import.meta.url);
10
+ function normalize(value) {
11
+ return value.trim().toLowerCase().replace(/\s+/g, "-");
12
+ }
13
+ /** Every shipped playbook, sorted by name. */
14
+ function listPlaybooks() {
15
+ return readdirSync(REFERENCES_DIR)
16
+ .filter((file) => file.endsWith(".md"))
17
+ .map((file) => {
18
+ const content = readFileSync(new URL(file, REFERENCES_DIR), "utf8");
19
+ const slug = file.slice(0, -".md".length);
20
+ return {
21
+ name: /^# (.+)$/m.exec(content)?.[1]?.trim() ?? slug,
22
+ slug,
23
+ content,
24
+ };
25
+ })
26
+ .sort((a, b) => a.name.localeCompare(b.name));
27
+ }
28
+ /**
29
+ * Resolve a playbook by name (case-insensitive; spaces and hyphens are equivalent) or file
30
+ * slug. With no name, list every playbook name. Unknown names fail with the available names.
31
+ */
32
+ export function runPlaybook(name) {
33
+ const playbooks = listPlaybooks();
34
+ if (name === undefined || name.trim() === "")
35
+ return { playbooks: playbooks.map((p) => p.name) };
36
+ const wanted = normalize(name);
37
+ const match = playbooks.find((p) => normalize(p.name) === wanted || p.slug === wanted);
38
+ if (!match) {
39
+ throw new ShepherdError(`Unknown playbook ${JSON.stringify(name)}. Available: ${playbooks.map((p) => JSON.stringify(p.name)).join(", ")}`, EXIT.USAGE);
40
+ }
41
+ return { name: match.name, content: match.content };
42
+ }
43
+ /** Markdown projection shared by the CLI text format and the MCP content channel. */
44
+ export function formatPlaybookResult(result) {
45
+ return "playbooks" in result
46
+ ? result.playbooks.map((name) => `- ${name}`).join("\n")
47
+ : result.content.trimEnd();
48
+ }
@@ -0,0 +1,8 @@
1
+ import type { PollMode } from "../config/load.mts";
2
+ /** The mode a command actually runs in. `auto` has been resolved away. */
3
+ export type ResolvedPollMode = "poll" | "event";
4
+ /**
5
+ * Resolve `--poll-mode` / the `pollMode` option / `poll.mode`. An explicit request wins over
6
+ * config; `auto` becomes `event` only inside a cloud session. A missing value counts as `auto`.
7
+ */
8
+ export declare function resolvePollMode(requested: PollMode | undefined, configured: PollMode | undefined): ResolvedPollMode;
@@ -0,0 +1,14 @@
1
+ /** True when the process runs in a Claude Code cloud (remote) session. */
2
+ function isCloudSession() {
3
+ return process.env["CLAUDE_CODE_REMOTE"] === "true";
4
+ }
5
+ /**
6
+ * Resolve `--poll-mode` / the `pollMode` option / `poll.mode`. An explicit request wins over
7
+ * config; `auto` becomes `event` only inside a cloud session. A missing value counts as `auto`.
8
+ */
9
+ export function resolvePollMode(requested, configured) {
10
+ const mode = requested ?? configured ?? "auto";
11
+ if (mode === "auto")
12
+ return isCloudSession() ? "event" : "poll";
13
+ return mode;
14
+ }
@@ -12,4 +12,5 @@ export declare function writeWaitProgress(opts: {
12
12
  lastWaitSignature: string | null;
13
13
  }): string | null;
14
14
  export declare function writeDebounceProgress(tick: number, elapsedMs: number, remainingMs: number): void;
15
+ export declare function writeRuleAutoResolveProgress(result: IterateResult): void;
15
16
  export {};
@@ -40,9 +40,8 @@ export function writeWaitProgress(opts) {
40
40
  const sleepSeconds = Math.round(opts.sleepMs / 1000);
41
41
  if (!opts.quietStatus) {
42
42
  const reason = waitReason(opts.result);
43
- writeTickProgress(opts.tick, elapsedSeconds, opts.verbose
44
- ? `${reason} — sleeping ${sleepSeconds}s`
45
- : `${reason}; next tick in ${sleepSeconds}s`);
43
+ // The tick interval is the caller's own `--interval`; only verbose mode repeats it.
44
+ writeTickProgress(opts.tick, elapsedSeconds, opts.verbose ? `${reason} — sleeping ${sleepSeconds}s` : reason);
46
45
  return opts.lastWaitSignature;
47
46
  }
48
47
  const signature = waitSignature(opts.result);
@@ -56,3 +55,7 @@ export function writeDebounceProgress(tick, elapsedMs, remainingMs) {
56
55
  const remainingSeconds = Math.round(remainingMs / 1000);
57
56
  process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s] FIX_CODE — debounce ${remainingSeconds}s remaining\n`);
58
57
  }
58
+ export function writeRuleAutoResolveProgress(result) {
59
+ if (result.ruleAutoResolve?.summary)
60
+ process.stderr.write(`${result.ruleAutoResolve.summary}\n`);
61
+ }
@@ -1,11 +1,12 @@
1
1
  import type { GraphqlQuotaWarningBand } from "../config/load.mts";
2
2
  import { type RateLimitKind } from "../github/rate-limit-kind.mts";
3
- import type { ApiResourceUsage, GraphqlApiUsage, PollSummaryResult } from "../types.mts";
3
+ import type { ApiResourceUsage, GraphqlApiUsage, GraphqlQuotaWarning, PollSummaryResult } from "../types.mts";
4
+ export declare function carryQuotaWarning(pending: GraphqlQuotaWarning | undefined, current: GraphqlQuotaWarning | undefined, transport?: "graphql" | "rest"): GraphqlQuotaWarning | undefined;
4
5
  /** Slow the poll for whichever of GraphQL or REST core is in a tighter band. */
5
6
  export declare function quotaPollIntervalMs(bands: GraphqlQuotaWarningBand[], usage: {
6
7
  graphql?: Pick<GraphqlApiUsage, "remaining" | "limit">;
7
8
  rest?: Pick<ApiResourceUsage, "resource" | "remaining" | "limit">[];
8
- } | undefined, fallbackMs: number, maxMs: number): number;
9
+ } | undefined, fallbackMs: number, maxMs: number, transport?: "graphql" | "rest"): number;
9
10
  export interface RateLimitRetry {
10
11
  ms: number;
11
12
  resource: string;
@@ -3,7 +3,11 @@ import { GitHubRequestError } from "../github/errors.mjs";
3
3
  import { rateLimitKind } from "../github/rate-limit-kind.mjs";
4
4
  import { exhaustedPrimaryLimitDelayMs } from "./poll-rate-limit-delay.mjs";
5
5
  import { selectQuotaWarning } from "./quota-selection.mjs";
6
+ import { getGithubTransport } from "../github/transport.mjs";
6
7
  const GRAPHQL_RETRY_AFTER_DEFAULT_MS = 60_000;
8
+ export function carryQuotaWarning(pending, current, transport = getGithubTransport()) {
9
+ return current ?? (transport === "rest" && pending?.resource !== "core" ? undefined : pending);
10
+ }
7
11
  /** Sleep at least `--interval`, and at least the active crossed quota band. */
8
12
  function graphqlQuotaPollIntervalMs(bands, usage, fallbackMs, maxMs) {
9
13
  if (usage === undefined || usage.limit <= 0 || bands.length === 0) {
@@ -17,9 +21,9 @@ function graphqlQuotaPollIntervalMs(bands, usage, fallbackMs, maxMs) {
17
21
  return Math.min(Math.max(fallbackMs, bandMs), maxMs);
18
22
  }
19
23
  /** Slow the poll for whichever of GraphQL or REST core is in a tighter band. */
20
- export function quotaPollIntervalMs(bands, usage, fallbackMs, maxMs) {
24
+ export function quotaPollIntervalMs(bands, usage, fallbackMs, maxMs, transport) {
21
25
  const core = usage?.rest?.find((item) => item.resource === "core");
22
- return Math.max(graphqlQuotaPollIntervalMs(bands, usage?.graphql, fallbackMs, maxMs), graphqlQuotaPollIntervalMs(bands, core, fallbackMs, maxMs));
26
+ return Math.max(graphqlQuotaPollIntervalMs(bands, transport === "rest" ? undefined : usage?.graphql, fallbackMs, maxMs), graphqlQuotaPollIntervalMs(bands, core, fallbackMs, maxMs));
23
27
  }
24
28
  /**
25
29
  * Retry delay for `--until-terminal` when GitHub exhausts a primary quota or
@@ -88,5 +92,5 @@ export async function aggregateQuotaWarning(result, bands, intervalSeconds) {
88
92
  return selectQuotaWarning({ owner, repo }, bands.map((band) => ({
89
93
  ...band,
90
94
  pollIntervalMinutes: Math.max(band.pollIntervalMinutes, intervalSeconds / 60),
91
- })), usage, true);
95
+ })), usage, true, getGithubTransport());
92
96
  }
@@ -1,2 +1,4 @@
1
1
  import type { PollSummaryResult } from "../types.mts";
2
+ /** Every selected row already returned `CANCEL` or `ESCALATE`: no autonomous work remains. */
3
+ export declare function isHandedOffSelection(result: PollSummaryResult): boolean;
2
4
  export declare function explicitInstructions(result: PollSummaryResult): string[];
@@ -1,4 +1,9 @@
1
1
  import { buildQuotaAwareContinuation } from "../quota-warning.mjs";
2
+ /** Every selected row already returned `CANCEL` or `ESCALATE`: no autonomous work remains. */
3
+ export function isHandedOffSelection(result) {
4
+ return (result.prs.length > 0 &&
5
+ result.prs.every((item) => item.action === "cancel" || item.action === "escalate"));
6
+ }
2
7
  export function explicitInstructions(result) {
3
8
  if (result.reason === "all_terminal")
4
9
  return ["1. Stop — every selected PR is terminal."];
@@ -15,7 +20,11 @@ export function explicitInstructions(result) {
15
20
  "2. Follow each selected one-PR poll's `## Instructions` until it returns `CANCEL` or `ESCALATE`.",
16
21
  "3. Run this aggregate poll again after selected work completes; one row's `ESCALATE` does not stop work on other rows.",
17
22
  ];
18
- if (result.quotaWarning) {
23
+ if (isHandedOffSelection(result)) {
24
+ instructions[2] =
25
+ "3. Stop after reporting each `ESCALATE` handoff; every selected row is `CANCEL` or `ESCALATE`, so do not run this aggregate poll again.";
26
+ }
27
+ else if (result.quotaWarning) {
19
28
  instructions[2] = buildQuotaAwareContinuation(result.quotaWarning, "3. After selected work completes,");
20
29
  }
21
30
  return instructions;
@@ -2,7 +2,7 @@
2
2
  import { buildQuotaAwareContinuation } from "../quota-warning.mjs";
3
3
  import { explicitInstructions } from "./poll-summary-explicit-instructions.mjs";
4
4
  import { appendAutonomousInstructions, appendHumanHandoffInstructions, findHumanHandoffs, idleWaitPlan, isStackLayerReady, planPrefixDrain, retargetWaitPlan, stackPosition, } from "./stack-drain.mjs";
5
- import { appendMarkReadyInstructions, splitStackWork } from "./stack-work.mjs";
5
+ import { appendMarkReadyInstructions, appendStackRerunInstruction, splitStackWork, } from "./stack-work.mjs";
6
6
  /** Keep aggregate JSON, Markdown, and MCP instructions on one projection. */
7
7
  export function withPollSummaryInstructions(result, mergeRequested) {
8
8
  return planPollSummary(result, mergeRequested).result;
@@ -85,6 +85,8 @@ function planStack(result, mergeRequested) {
85
85
  const runnableCandidates = work.sessions.filter((item) => item.pollCommand);
86
86
  const missingCommands = work.sessions.filter((item) => !item.pollCommand);
87
87
  const agentWork = runnableCandidates.length > 0 || work.markReady.length > 0;
88
+ // Only owned work defers a human handoff: rerunning cannot advance another author's layer.
89
+ const ownedWork = [...runnableCandidates, ...work.markReady].some((item) => item.owned);
88
90
  const drain = planPrefixDrain(result, mergeRequested);
89
91
  if (drain)
90
92
  return drain;
@@ -93,13 +95,13 @@ function planStack(result, mergeRequested) {
93
95
  const instructions = [];
94
96
  appendAutonomousInstructions(instructions, runnableCandidates);
95
97
  appendMarkReadyInstructions(instructions, work.markReady);
96
- const stop = !agentWork;
98
+ const stop = !ownedWork;
97
99
  appendHumanHandoffInstructions(instructions, handoffs, stop);
98
100
  for (const item of missingCommands) {
99
101
  instructions.push(`${instructions.length + 1}. PR #${item.pr} needs a one-PR session, but Shepherd could not produce its command. Ask for direction.`);
100
102
  }
101
- if (agentWork) {
102
- instructions.push(`${instructions.length + 1}. After the listed one-PR sessions, rerun this same \`--stack\` selector. Stop for the human handoff only when no autonomous shepherding remains.`);
103
+ if (ownedWork) {
104
+ appendStackRerunInstruction(instructions, [...runnableCandidates, ...work.markReady], "After the listed one-PR sessions, rerun this same `--stack` selector. Stop for the human handoff only when no autonomous shepherding remains.");
103
105
  }
104
106
  return { action: stop ? "escalate" : "shepherd", stackMergeable: false, instructions };
105
107
  }
@@ -116,20 +118,20 @@ function planStack(result, mergeRequested) {
116
118
  appendAutonomousInstructions(instructions, runnableCandidates);
117
119
  appendMarkReadyInstructions(instructions, work.markReady);
118
120
  for (const item of missingCommands) {
119
- instructions.push(`${instructions.length + 1}. PR #${item.pr} needs a one-PR session, but Shepherd could not produce its command. ${agentWork ? "After autonomous shepherding, ask" : "Stop and ask"} for direction.`);
121
+ instructions.push(`${instructions.length + 1}. PR #${item.pr} needs a one-PR session, but Shepherd could not produce its command. ${ownedWork ? "After autonomous shepherding, ask" : "Stop and ask"} for direction.`);
120
122
  }
121
- if (agentWork) {
122
- instructions.push(`${instructions.length + 1}. After the listed one-PR sessions, rerun this same \`--stack\` selector. Stop for the human handoff only when no autonomous shepherding remains.`);
123
+ if (ownedWork) {
124
+ appendStackRerunInstruction(instructions, [...runnableCandidates, ...work.markReady], "After the listed one-PR sessions, rerun this same `--stack` selector. Stop for the human handoff only when no autonomous shepherding remains.");
123
125
  }
124
- return { action: agentWork ? "shepherd" : "escalate", stackMergeable: false, instructions };
126
+ return { action: ownedWork ? "shepherd" : "escalate", stackMergeable: false, instructions };
125
127
  }
126
128
  if (!agentWork)
127
129
  return idleWaitPlan(work.idle);
128
130
  const instructions = [];
129
131
  appendAutonomousInstructions(instructions, work.sessions);
130
132
  appendMarkReadyInstructions(instructions, work.markReady);
131
- instructions.push(`${instructions.length + 1}. After the selected one-PR sessions, rerun this same \`--stack\` selector.`);
132
- return { action: "shepherd", stackMergeable: false, instructions };
133
+ const action = appendStackRerunInstruction(instructions, [...work.sessions, ...work.markReady], "After the selected one-PR sessions, rerun this same `--stack` selector.");
134
+ return { action, stackMergeable: false, instructions };
133
135
  }
134
136
  if (open.some((item) => item.isInMergeQueue)) {
135
137
  return {
@@ -150,13 +152,10 @@ function planStack(result, mergeRequested) {
150
152
  }
151
153
  if (!mergeRequested) {
152
154
  const instructions = [];
153
- appendAutonomousInstructions(instructions, open.filter((item) => item.action !== "cancel"));
154
- instructions.push(`${instructions.length + 1}. After the selected one-PR sessions, rerun this same \`--stack\` selector.`);
155
- return {
156
- action: "shepherd",
157
- stackMergeable,
158
- instructions,
159
- };
155
+ const sessions = open.filter((item) => item.action !== "cancel");
156
+ appendAutonomousInstructions(instructions, sessions);
157
+ const action = appendStackRerunInstruction(instructions, sessions, "After the selected one-PR sessions, rerun this same `--stack` selector.");
158
+ return { action, stackMergeable, instructions };
160
159
  }
161
160
  return retargetWaitPlan(open[0]);
162
161
  }
@@ -2,10 +2,12 @@
2
2
  import { loadConfig } from "../config/load.mjs";
3
3
  import { ShepherdError } from "../exit-codes.mjs";
4
4
  import { getRepoInfo } from "../github/client.mjs";
5
+ import { getGithubTransport } from "../github/transport.mjs";
5
6
  import { withApiTelemetryScope, summarizeApiTelemetry } from "../github/api-telemetry.mjs";
6
7
  import { fetchPollSummary } from "../github/poll-summary.mjs";
8
+ import { writeStackHandoffs } from "../github/stack-handoff.mjs";
7
9
  import { sleep } from "../util/sleep.mjs";
8
- import { aggregateQuotaWarning, quotaPollIntervalMs } from "./poll-quota.mjs";
10
+ import { aggregateQuotaWarning, carryQuotaWarning, quotaPollIntervalMs } from "./poll-quota.mjs";
9
11
  import { aggregateCancelFromPulls, aggregateRateLimitTargets } from "./poll-rate-limit-cancel.mjs";
10
12
  import { createUntilTerminalRateLimitRetry } from "./poll-rate-limit-wait.mjs";
11
13
  import { planPollSummary, withPollSummaryInstructions } from "./poll-summary-instructions.mjs";
@@ -14,12 +16,38 @@ import { applyStackStallGuard } from "./stack-stall.mjs";
14
16
  const MAX_TIMER_MS = 2 ** 31 - 1;
15
17
  const TIMER_DRIFT_TOLERANCE_MS = 500;
16
18
  export function runPollSummary(opts) {
17
- return withApiTelemetryScope(async () => attachUsage(await runPollSummaryCore(opts), opts.merge));
19
+ return withApiTelemetryScope(async () => {
20
+ const tick = await readPollSummaryTick(opts);
21
+ await handOffRoutedLayers(tick);
22
+ return attachUsage(tick.result, opts.merge);
23
+ });
18
24
  }
19
25
  export function runAggregatePoll(opts) {
20
26
  return withApiTelemetryScope(() => runAggregatePollCore(opts));
21
27
  }
22
28
  async function runPollSummaryCore(opts) {
29
+ return (await readPollSummaryTick(opts)).result;
30
+ }
31
+ /**
32
+ * The stack tick that routes owned layers to their one-PR sessions hands each
33
+ * routed layer this tick's read of it, so that session's first read is free.
34
+ * Every other tick clears earlier handoffs: a newer read supersedes them.
35
+ */
36
+ async function handOffRoutedLayers(tick) {
37
+ const { result, handoffSource } = tick;
38
+ if (!handoffSource)
39
+ return;
40
+ const routing = result.nextAction === "shepherd" && result.reason !== "waiting";
41
+ const routed = routing
42
+ ? result.prs
43
+ .filter((item) => item.state === "OPEN" &&
44
+ item.pollCommand !== undefined &&
45
+ !["cancel", "wait", "escalate"].includes(item.action))
46
+ .map((item) => item.pr)
47
+ : [];
48
+ await writeStackHandoffs(handoffSource, routed, tick.repo);
49
+ }
50
+ async function readPollSummaryTick(opts) {
23
51
  const repo = opts.targetRepository ?? (await getRepoInfo());
24
52
  const fetched = await fetchPollSummary(opts, repo);
25
53
  const allTerminal = fetched.prs.every((item) => item.action === "cancel");
@@ -33,7 +61,8 @@ async function runPollSummaryCore(opts) {
33
61
  ...(fetched.stackAncestry?.length && { stackAncestry: fetched.stackAncestry }),
34
62
  ...(fetched.allowedMergeMethods && { allowedMergeMethods: fetched.allowedMergeMethods }),
35
63
  }, opts.merge === true);
36
- return applyStackStallGuard(planned, repo, opts.stallTimeoutSeconds ?? loadConfig().iterate.stallTimeoutMinutes * 60);
64
+ const result = await applyStackStallGuard(planned, repo, opts.stallTimeoutSeconds ?? loadConfig().iterate.stallTimeoutMinutes * 60);
65
+ return { result, repo, ...(fetched.handoffSource && { handoffSource: fetched.handoffSource }) };
37
66
  }
38
67
  async function runAggregatePollCore(opts) {
39
68
  const intervalMs = Math.min(opts.intervalSeconds * 1000, MAX_TIMER_MS);
@@ -44,16 +73,19 @@ async function runAggregatePollCore(opts) {
44
73
  let tick = 0;
45
74
  let debounceUntil = null;
46
75
  let last;
76
+ let lastTick;
47
77
  let lastStatusSignature = null;
48
78
  const rateLimitRetry = createUntilTerminalRateLimitRetry();
49
79
  let pendingQuotaWarning;
50
80
  while (true) {
51
81
  tick += 1;
52
82
  try {
53
- last = await runPollSummaryCore(opts);
83
+ lastTick = await readPollSummaryTick(opts);
84
+ last = lastTick.result;
54
85
  rateLimitRetry.reset();
55
86
  }
56
87
  catch (error) {
88
+ pendingQuotaWarning = carryQuotaWarning(pendingQuotaWarning, undefined);
57
89
  if (last?.selection.kind === "stack" && isMissingStack(error)) {
58
90
  const explicit = await runPollSummaryCore({
59
91
  ...opts,
@@ -62,8 +94,7 @@ async function runAggregatePollCore(opts) {
62
94
  });
63
95
  if (explicit.prs.every((item) => item.state === "MERGED")) {
64
96
  const warning = await aggregateQuotaWarning(explicit, quotaBands, opts.intervalSeconds);
65
- if (warning)
66
- pendingQuotaWarning = warning;
97
+ pendingQuotaWarning = carryQuotaWarning(pendingQuotaWarning, warning);
67
98
  return attachUsage({
68
99
  ...explicit,
69
100
  selection: last.selection,
@@ -110,8 +141,7 @@ async function runAggregatePollCore(opts) {
110
141
  ? last.nextAction === "shepherd" && last.reason !== "waiting"
111
142
  : last.prs.some((item) => item.action === "fix_code");
112
143
  const warning = await aggregateQuotaWarning(last, quotaBands, opts.intervalSeconds);
113
- if (warning)
114
- pendingQuotaWarning = warning;
144
+ pendingQuotaWarning = carryQuotaWarning(pendingQuotaWarning, warning);
115
145
  if (allTerminal) {
116
146
  return attachUsage({
117
147
  ...last,
@@ -127,19 +157,14 @@ async function runAggregatePollCore(opts) {
127
157
  }, opts.merge);
128
158
  }
129
159
  if (hasFix) {
130
- if (debounceMs === 0)
131
- return attachUsage({
132
- ...last,
133
- reason: "actionable",
134
- ...(pendingQuotaWarning && { quotaWarning: pendingQuotaWarning }),
135
- }, opts.merge);
136
- debounceUntil ??= Date.now() + debounceMs;
137
- if (Date.now() >= debounceUntil)
160
+ if (debounceMs === 0 || Date.now() >= (debounceUntil ??= Date.now() + debounceMs)) {
161
+ await handOffRoutedLayers(lastTick);
138
162
  return attachUsage({
139
163
  ...last,
140
164
  reason: "actionable",
141
165
  ...(pendingQuotaWarning && { quotaWarning: pendingQuotaWarning }),
142
166
  }, opts.merge);
167
+ }
143
168
  }
144
169
  else {
145
170
  debounceUntil = null;
@@ -150,7 +175,7 @@ async function runAggregatePollCore(opts) {
150
175
  const elapsedMs = Date.now() - start;
151
176
  const sleepMs = debounceUntil
152
177
  ? Math.min(intervalMs, Math.max(debounceUntil - Date.now(), 0))
153
- : quotaPollIntervalMs(quotaBands, summarizeApiTelemetry(), intervalMs, MAX_TIMER_MS);
178
+ : quotaPollIntervalMs(quotaBands, summarizeApiTelemetry(), intervalMs, MAX_TIMER_MS, getGithubTransport());
154
179
  if (!opts.untilTerminal && debounceUntil === null) {
155
180
  const remainingMs = timeoutMs - elapsedMs;
156
181
  if (remainingMs <= 0 || remainingMs + TIMER_DRIFT_TOLERANCE_MS < sleepMs) {
@@ -7,5 +7,4 @@ export interface PollCommandOptions extends IterateCommandOptions {
7
7
  quietStatus?: boolean;
8
8
  untilTerminal?: boolean;
9
9
  }
10
- /** @deprecated Hidden implementation for the legacy `poll` alias. */
11
10
  export declare function runPoll(opts: PollCommandOptions): Promise<IterateResult>;
@@ -2,18 +2,14 @@ import { runIterate } from "./iterate/index.mjs";
2
2
  import { sleep } from "../util/sleep.mjs";
3
3
  import { withPollApiUsage } from "./poll-run.mjs";
4
4
  import { loadConfig } from "../config/load.mjs";
5
- import { quotaPollIntervalMs } from "./poll-quota.mjs";
5
+ import { carryQuotaWarning, quotaPollIntervalMs } from "./poll-quota.mjs";
6
6
  import { onePrCancelFromPulls, onePrRateLimitTargets } from "./poll-rate-limit-cancel.mjs";
7
7
  import { createUntilTerminalRateLimitRetry } from "./poll-rate-limit-wait.mjs";
8
- import { writeDebounceProgress, writeWaitProgress } from "./poll-progress.mjs";
9
- function noteRuleAutoResolve(result) {
10
- if (result.ruleAutoResolve?.summary)
11
- process.stderr.write(`${result.ruleAutoResolve.summary}\n`);
12
- }
8
+ import { writeDebounceProgress, writeRuleAutoResolveProgress, writeWaitProgress, } from "./poll-progress.mjs";
9
+ import { getGithubTransport } from "../github/transport.mjs";
13
10
  const DEFAULT_POLL_DEBOUNCE_SECONDS = 60;
14
11
  const MAX_TIMER_MS = 2 ** 31 - 1;
15
12
  const TIMER_DRIFT_TOLERANCE_MS = 500;
16
- /** @deprecated Hidden implementation for the legacy `poll` alias. */
17
13
  export function runPoll(opts) {
18
14
  return withPollApiUsage(() => runPollCore(opts), opts.untilTerminal === true, opts.intervalSeconds / 60);
19
15
  }
@@ -32,7 +28,6 @@ async function runPollCore(opts) {
32
28
  const untilTerminal = untilTerminalOpt === true;
33
29
  let lastWaitSignature = null;
34
30
  let pendingQuotaWarning;
35
- // Pin the PR resolved by the first tick; branch inference only matches OPEN PRs.
36
31
  let prNumber = opts.prNumber;
37
32
  let debounceUntil = null;
38
33
  const rateLimitRetry = createUntilTerminalRateLimitRetry();
@@ -42,7 +37,6 @@ async function runPollCore(opts) {
42
37
  const remainingBefore = untilTerminal
43
38
  ? Number.POSITIVE_INFINITY
44
39
  : timeoutMs - (Date.now() - start);
45
- // Cache only internal continuation ticks; returned ticks must fetch BatchPr.
46
40
  const allowCache = debounceUntil === null && remainingBefore + TIMER_DRIFT_TOLERANCE_MS >= intervalMs;
47
41
  const iterateTick = (fingerprintCache) => runIterate({
48
42
  ...iterateOpts,
@@ -75,14 +69,12 @@ async function runPollCore(opts) {
75
69
  };
76
70
  lastResult = await runTick(allowCache);
77
71
  prNumber ??= lastResult.pr;
78
- if (lastResult.quotaWarning !== undefined)
79
- pendingQuotaWarning = lastResult.quotaWarning;
72
+ pendingQuotaWarning = carryQuotaWarning(pendingQuotaWarning, lastResult.quotaWarning);
80
73
  const refreshIfReturning = async () => {
81
74
  if (lastResult?.fingerprintReused !== true)
82
75
  return;
83
76
  lastResult = await runTick(false);
84
- if (lastResult.quotaWarning !== undefined)
85
- pendingQuotaWarning = lastResult.quotaWarning;
77
+ pendingQuotaWarning = carryQuotaWarning(pendingQuotaWarning, lastResult.quotaWarning);
86
78
  };
87
79
  if (untilTerminal &&
88
80
  pendingQuotaWarning !== undefined &&
@@ -92,7 +84,7 @@ async function runPollCore(opts) {
92
84
  if (lastResult.action === "fix_code" && debounceSeconds > 0 && !pastDebounce) {
93
85
  debounceUntil ??= Date.now() + debounceMs;
94
86
  const remainingMs = Math.max(debounceUntil - Date.now(), 0);
95
- noteRuleAutoResolve(lastResult);
87
+ writeRuleAutoResolveProgress(lastResult);
96
88
  writeDebounceProgress(tick, Date.now() - start, remainingMs);
97
89
  await sleep(Math.min(intervalMs, remainingMs));
98
90
  continue;
@@ -110,7 +102,7 @@ async function runPollCore(opts) {
110
102
  if (pendingQuotaWarning === undefined)
111
103
  debounceUntil = null;
112
104
  const elapsedMs = Date.now() - start;
113
- const sleepMs = quotaPollIntervalMs(quotaBands, lastResult.apiUsage, intervalMs, MAX_TIMER_MS);
105
+ const sleepMs = quotaPollIntervalMs(quotaBands, lastResult.apiUsage, intervalMs, MAX_TIMER_MS, getGithubTransport());
114
106
  if (!untilTerminal) {
115
107
  const remainingMs = timeoutMs - elapsedMs;
116
108
  if (remainingMs <= 0 || remainingMs + TIMER_DRIFT_TOLERANCE_MS < sleepMs) {
@@ -127,7 +119,7 @@ async function runPollCore(opts) {
127
119
  verbose,
128
120
  lastWaitSignature,
129
121
  });
130
- noteRuleAutoResolve(lastResult);
122
+ writeRuleAutoResolveProgress(lastResult);
131
123
  await sleep(sleepMs);
132
124
  continue;
133
125
  }
@@ -136,7 +128,7 @@ async function runPollCore(opts) {
136
128
  !pastDebounce) {
137
129
  debounceUntil = null;
138
130
  const elapsedMs = Date.now() - start;
139
- const sleepMs = quotaPollIntervalMs(quotaBands, lastResult.apiUsage, intervalMs, MAX_TIMER_MS);
131
+ const sleepMs = quotaPollIntervalMs(quotaBands, lastResult.apiUsage, intervalMs, MAX_TIMER_MS, getGithubTransport());
140
132
  if (!untilTerminal) {
141
133
  const remainingMs = timeoutMs - elapsedMs;
142
134
  if (remainingMs <= 0 || remainingMs + TIMER_DRIFT_TOLERANCE_MS < sleepMs) {
@@ -144,12 +136,12 @@ async function runPollCore(opts) {
144
136
  break;
145
137
  }
146
138
  }
147
- noteRuleAutoResolve(lastResult);
139
+ writeRuleAutoResolveProgress(lastResult);
148
140
  await sleep(sleepMs);
149
141
  continue;
150
142
  }
151
143
  if (lastResult.action === "fix_code" && debounceSeconds > 0 && !pastDebounce) {
152
- noteRuleAutoResolve(lastResult);
144
+ writeRuleAutoResolveProgress(lastResult);
153
145
  debounceUntil ??= Date.now() + debounceMs;
154
146
  const remainingMs = debounceUntil - Date.now();
155
147
  if (remainingMs > 0) {
@@ -1,7 +1,8 @@
1
1
  import type { GraphqlQuotaWarningBand } from "../config/load.mts";
2
2
  import type { ApiUsage, GraphqlQuotaWarning } from "../types.mts";
3
+ import type { GithubTransport } from "../github/transport.mts";
3
4
  /** Claim GraphQL and REST core warnings separately, then present one result. */
4
5
  export declare function selectQuotaWarning(key: {
5
6
  owner: string;
6
7
  repo: string;
7
- }, bands: GraphqlQuotaWarningBand[], usage: ApiUsage, persist: boolean): Promise<GraphqlQuotaWarning | undefined>;
8
+ }, bands: GraphqlQuotaWarningBand[], usage: ApiUsage, persist: boolean, transport?: Exclude<GithubTransport, "auto">): Promise<GraphqlQuotaWarning | undefined>;