pr-shepherd 0.56.5 → 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 (423) 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 +40 -19
  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.d.mts +6 -5
  138. package/bin/commands/iterate/native-stack-rebase.mjs +24 -7
  139. package/bin/commands/iterate/render.d.mts +8 -2
  140. package/bin/commands/iterate/render.mjs +28 -42
  141. package/bin/commands/iterate/stack-trunk-conflict.d.mts +4 -2
  142. package/bin/commands/iterate/stack-trunk-conflict.mjs +44 -19
  143. package/bin/commands/iterate/stale-ancestry.mjs +4 -1
  144. package/bin/commands/iterate/stall.d.mts +5 -1
  145. package/bin/commands/iterate/stall.mjs +19 -7
  146. package/bin/commands/iterate/thread-mutation-routing.d.mts +1 -0
  147. package/bin/commands/iterate/thread-mutation-routing.mjs +4 -2
  148. package/bin/commands/iterate/unreported-required.mjs +5 -2
  149. package/bin/commands/mark-files-as-viewed.mjs +7 -0
  150. package/bin/commands/next-check.d.mts +31 -0
  151. package/bin/commands/next-check.mjs +46 -0
  152. package/bin/commands/playbook.d.mts +13 -0
  153. package/bin/commands/playbook.mjs +48 -0
  154. package/bin/commands/poll-mode.d.mts +8 -0
  155. package/bin/commands/poll-mode.mjs +14 -0
  156. package/bin/commands/poll-progress.d.mts +1 -0
  157. package/bin/commands/poll-progress.mjs +6 -3
  158. package/bin/commands/poll-quota.d.mts +3 -2
  159. package/bin/commands/poll-quota.mjs +7 -3
  160. package/bin/commands/poll-summary-explicit-instructions.d.mts +2 -0
  161. package/bin/commands/poll-summary-explicit-instructions.mjs +10 -1
  162. package/bin/commands/poll-summary-instructions.mjs +16 -17
  163. package/bin/commands/poll-summary.mjs +42 -17
  164. package/bin/commands/poll.d.mts +0 -1
  165. package/bin/commands/poll.mjs +11 -19
  166. package/bin/commands/quota-selection.d.mts +2 -1
  167. package/bin/commands/quota-selection.mjs +12 -7
  168. package/bin/commands/resolve-mutate.mjs +33 -5
  169. package/bin/commands/rest-stack-merge-readiness.d.mts +4 -0
  170. package/bin/commands/rest-stack-merge-readiness.mjs +74 -0
  171. package/bin/commands/rule-auto-resolve.mjs +9 -3
  172. package/bin/commands/shepherd-journal.d.mts +5 -4
  173. package/bin/commands/shepherd-journal.mjs +9 -8
  174. package/bin/commands/stack-drain.d.mts +1 -9
  175. package/bin/commands/stack-drain.mjs +50 -37
  176. package/bin/commands/stack-handoffs.d.mts +10 -0
  177. package/bin/commands/stack-handoffs.mjs +25 -0
  178. package/bin/commands/stack-stall.d.mts +5 -0
  179. package/bin/commands/stack-stall.mjs +11 -0
  180. package/bin/commands/stack-work.d.mts +5 -0
  181. package/bin/commands/stack-work.mjs +32 -0
  182. package/bin/comments/existing-reply-scan.d.mts +17 -0
  183. package/bin/comments/existing-reply-scan.mjs +41 -0
  184. package/bin/comments/rate-limit.mjs +2 -2
  185. package/bin/comments/resolve.d.mts +7 -1
  186. package/bin/comments/resolve.mjs +108 -18
  187. package/bin/comments/rest-reply.d.mts +9 -0
  188. package/bin/comments/rest-reply.mjs +95 -0
  189. package/bin/comments/rest-review-mutations.d.mts +22 -0
  190. package/bin/comments/rest-review-mutations.mjs +125 -0
  191. package/bin/comments/review-visibility.d.mts +1 -1
  192. package/bin/comments/review-visibility.mjs +3 -2
  193. package/bin/comments/sha-poll.mjs +7 -3
  194. package/bin/comments/thread-visibility.mjs +10 -5
  195. package/bin/comments/uncertain-replies.d.mts +11 -0
  196. package/bin/comments/uncertain-replies.mjs +121 -0
  197. package/bin/comments/uncertain-reply-guards.d.mts +10 -0
  198. package/bin/comments/uncertain-reply-guards.mjs +94 -0
  199. package/bin/comments/visible-comments.d.mts +1 -1
  200. package/bin/comments/visible-comments.mjs +4 -2
  201. package/bin/config/load.d.mts +37 -1
  202. package/bin/config/load.mjs +73 -1
  203. package/bin/config.json +6 -2
  204. package/bin/exit-codes.d.mts +3 -0
  205. package/bin/exit-codes.mjs +8 -0
  206. package/bin/github/api-telemetry-aggregate.d.mts +15 -4
  207. package/bin/github/api-telemetry-aggregate.mjs +57 -18
  208. package/bin/github/api-telemetry.d.mts +4 -0
  209. package/bin/github/api-telemetry.mjs +22 -8
  210. package/bin/github/apply-review-preflight.d.mts +20 -0
  211. package/bin/github/apply-review-preflight.mjs +61 -0
  212. package/bin/github/base-behind-cache.d.mts +15 -0
  213. package/bin/github/base-behind-cache.mjs +18 -0
  214. package/bin/github/batch-parse-suites.d.mts +4 -2
  215. package/bin/github/batch-parse-suites.mjs +6 -1
  216. package/bin/github/batch-parser-helpers.mjs +40 -2
  217. package/bin/github/batch-parsers.mjs +1 -0
  218. package/bin/github/batch-raw-rules.d.mts +26 -0
  219. package/bin/github/batch-raw-types.d.mts +8 -6
  220. package/bin/github/batch-stack-evidence.d.mts +28 -0
  221. package/bin/github/batch-stack-evidence.mjs +62 -0
  222. package/bin/github/batch.d.mts +15 -2
  223. package/bin/github/batch.mjs +34 -17
  224. package/bin/github/check-annotations-batch.d.mts +0 -5
  225. package/bin/github/check-annotations-batch.mjs +25 -4
  226. package/bin/github/check-annotations.mjs +3 -1
  227. package/bin/github/client.d.mts +8 -19
  228. package/bin/github/client.mjs +45 -13
  229. package/bin/github/fingerprint-fields.d.mts +8 -0
  230. package/bin/github/fingerprint-fields.mjs +6 -1
  231. package/bin/github/fingerprint.d.mts +4 -2
  232. package/bin/github/fingerprint.mjs +6 -38
  233. package/bin/github/github-fetch.d.mts +2 -0
  234. package/bin/github/github-fetch.mjs +19 -0
  235. package/bin/github/gql/base-behind.gql +3 -0
  236. package/bin/github/gql/batch-pr-page.gql +16 -0
  237. package/bin/github/gql/batch-pr.gql +57 -0
  238. package/bin/github/gql/commit-check-contexts.gql +17 -0
  239. package/bin/github/gql/get-pr-body.gql +1 -0
  240. package/bin/github/gql/poll-stack-summary.gql +1 -0
  241. package/bin/github/gql/poll-summary-check-contexts.gql +5 -0
  242. package/bin/github/gql/poll-summary-fragment.gql +65 -15
  243. package/bin/github/gql/stack-handoff-check-runs.gql +42 -0
  244. package/bin/github/gql/upper-layer-conflict-target.gql +0 -15
  245. package/bin/github/graphql-http.mjs +9 -4
  246. package/bin/github/graphql-internal-retry.d.mts +2 -0
  247. package/bin/github/graphql-internal-retry.mjs +1 -1
  248. package/bin/github/graphql-quota-probe.d.mts +6 -0
  249. package/bin/github/graphql-quota-probe.mjs +56 -0
  250. package/bin/github/graphql-session-refusal.d.mts +8 -0
  251. package/bin/github/graphql-session-refusal.mjs +14 -0
  252. package/bin/github/http-request.d.mts +1 -1
  253. package/bin/github/http-request.mjs +22 -6
  254. package/bin/github/http-utils.mjs +6 -0
  255. package/bin/github/merge-target-rules.d.mts +16 -3
  256. package/bin/github/merge-target-rules.mjs +79 -44
  257. package/bin/github/mutation-policy.d.mts +6 -0
  258. package/bin/github/mutation-policy.mjs +21 -0
  259. package/bin/github/poll-summary-annotation-probe.mjs +17 -10
  260. package/bin/github/poll-summary-check-blockers.d.mts +1 -1
  261. package/bin/github/poll-summary-check-blockers.mjs +2 -2
  262. package/bin/github/poll-summary-check-hydration.mjs +25 -10
  263. package/bin/github/poll-summary-checks.d.mts +2 -2
  264. package/bin/github/poll-summary-checks.mjs +9 -6
  265. package/bin/github/poll-summary-command.d.mts +2 -0
  266. package/bin/github/poll-summary-command.mjs +24 -0
  267. package/bin/github/poll-summary-fingerprint.d.mts +1 -1
  268. package/bin/github/poll-summary-fingerprint.mjs +4 -2
  269. package/bin/github/poll-summary-handoff-fields.d.mts +3 -0
  270. package/bin/github/poll-summary-handoff-fields.mjs +73 -0
  271. package/bin/github/poll-summary-projector.d.mts +1 -1
  272. package/bin/github/poll-summary-projector.mjs +17 -24
  273. package/bin/github/poll-summary-raw.d.mts +70 -10
  274. package/bin/github/poll-summary-readiness.mjs +4 -1
  275. package/bin/github/poll-summary-rest-queue.d.mts +3 -0
  276. package/bin/github/poll-summary-rest-queue.mjs +12 -0
  277. package/bin/github/poll-summary-review.d.mts +1 -1
  278. package/bin/github/poll-summary-review.mjs +23 -11
  279. package/bin/github/poll-summary-route.mjs +19 -3
  280. package/bin/github/poll-summary-stack-page.d.mts +11 -0
  281. package/bin/github/poll-summary-stack-page.mjs +27 -0
  282. package/bin/github/poll-summary-unreported.d.mts +6 -3
  283. package/bin/github/poll-summary-unreported.mjs +15 -7
  284. package/bin/github/poll-summary.d.mts +4 -1
  285. package/bin/github/poll-summary.mjs +48 -34
  286. package/bin/github/queries.d.mts +9 -5
  287. package/bin/github/queries.mjs +10 -6
  288. package/bin/github/rate-limit-sample.d.mts +7 -0
  289. package/bin/github/rate-limit-sample.mjs +12 -0
  290. package/bin/github/reply-recovery-read.d.mts +50 -0
  291. package/bin/github/reply-recovery-read.mjs +118 -0
  292. package/bin/github/reply-thread-transcripts.d.mts +0 -1
  293. package/bin/github/reply-thread-transcripts.mjs +24 -1
  294. package/bin/github/rest-annotation-read.d.mts +5 -0
  295. package/bin/github/rest-annotation-read.mjs +44 -0
  296. package/bin/github/rest-batch-read.d.mts +12 -0
  297. package/bin/github/rest-batch-read.mjs +57 -0
  298. package/bin/github/rest-batch-snapshot.d.mts +30 -0
  299. package/bin/github/rest-batch-snapshot.mjs +115 -0
  300. package/bin/github/rest-batch-unavailable.d.mts +5 -0
  301. package/bin/github/rest-batch-unavailable.mjs +16 -0
  302. package/bin/github/rest-check-read.d.mts +34 -0
  303. package/bin/github/rest-check-read.mjs +124 -0
  304. package/bin/github/rest-comment-authorship.d.mts +7 -0
  305. package/bin/github/rest-comment-authorship.mjs +11 -0
  306. package/bin/github/rest-conditional-scope.d.mts +20 -0
  307. package/bin/github/rest-conditional-scope.mjs +44 -0
  308. package/bin/github/rest-explicit-summary-read.d.mts +3 -0
  309. package/bin/github/rest-explicit-summary-read.mjs +8 -0
  310. package/bin/github/rest-feedback-read.d.mts +36 -0
  311. package/bin/github/rest-feedback-read.mjs +147 -0
  312. package/bin/github/rest-feedback-validation.d.mts +5 -0
  313. package/bin/github/rest-feedback-validation.mjs +28 -0
  314. package/bin/github/rest-http.d.mts +12 -0
  315. package/bin/github/rest-http.mjs +108 -23
  316. package/bin/github/rest-identities.d.mts +22 -0
  317. package/bin/github/rest-identities.mjs +134 -0
  318. package/bin/github/rest-merge-stack-guard.d.mts +14 -0
  319. package/bin/github/rest-merge-stack-guard.mjs +41 -0
  320. package/bin/github/rest-merge.d.mts +29 -0
  321. package/bin/github/rest-merge.mjs +56 -0
  322. package/bin/github/rest-merged-base-read.d.mts +9 -0
  323. package/bin/github/rest-merged-base-read.mjs +48 -0
  324. package/bin/github/rest-pr-core.d.mts +52 -0
  325. package/bin/github/rest-pr-core.mjs +60 -0
  326. package/bin/github/rest-reader-core.d.mts +16 -0
  327. package/bin/github/rest-reader-core.mjs +154 -0
  328. package/bin/github/rest-review-read.d.mts +11 -0
  329. package/bin/github/rest-review-read.mjs +47 -0
  330. package/bin/github/rest-rules-read.d.mts +9 -0
  331. package/bin/github/rest-rules-read.mjs +154 -0
  332. package/bin/github/rest-session-refusal.d.mts +5 -0
  333. package/bin/github/rest-session-refusal.mjs +21 -0
  334. package/bin/github/rest-snapshot-context.d.mts +10 -0
  335. package/bin/github/rest-snapshot-context.mjs +1 -0
  336. package/bin/github/rest-stack-read.d.mts +21 -0
  337. package/bin/github/rest-stack-read.mjs +47 -0
  338. package/bin/github/rest-stack-summary.d.mts +8 -0
  339. package/bin/github/rest-stack-summary.mjs +62 -0
  340. package/bin/github/rest-summary-read.d.mts +7 -0
  341. package/bin/github/rest-summary-read.mjs +112 -0
  342. package/bin/github/rest-terminal-batch.d.mts +14 -0
  343. package/bin/github/rest-terminal-batch.mjs +45 -0
  344. package/bin/github/rest-text.mjs +7 -3
  345. package/bin/github/rest-thread-read.d.mts +4 -0
  346. package/bin/github/rest-thread-read.mjs +35 -0
  347. package/bin/github/rest-viewer-read.d.mts +2 -0
  348. package/bin/github/rest-viewer-read.mjs +41 -0
  349. package/bin/github/stack-bottom.d.mts +3 -0
  350. package/bin/github/stack-bottom.mjs +14 -0
  351. package/bin/github/stack-handoff-context.d.mts +25 -0
  352. package/bin/github/stack-handoff-context.mjs +12 -0
  353. package/bin/github/stack-handoff-page.d.mts +17 -0
  354. package/bin/github/stack-handoff-page.mjs +306 -0
  355. package/bin/github/stack-handoff.d.mts +16 -0
  356. package/bin/github/stack-handoff.mjs +115 -0
  357. package/bin/github/stack-order.d.mts +6 -0
  358. package/bin/github/stack-order.mjs +13 -0
  359. package/bin/github/stack-read.d.mts +7 -3
  360. package/bin/github/stack-read.mjs +22 -11
  361. package/bin/github/stack-size-hint.d.mts +3 -0
  362. package/bin/github/stack-size-hint.mjs +14 -0
  363. package/bin/github/suggestion-thread.mjs +27 -1
  364. package/bin/github/thread-comments.mjs +9 -1
  365. package/bin/github/transport-evidence.d.mts +20 -0
  366. package/bin/github/transport-evidence.mjs +26 -0
  367. package/bin/github/transport-mode.d.mts +2 -0
  368. package/bin/github/transport-mode.mjs +5 -0
  369. package/bin/github/transport.d.mts +19 -0
  370. package/bin/github/transport.mjs +126 -0
  371. package/bin/github/unsupported-rest.d.mts +4 -0
  372. package/bin/github/unsupported-rest.mjs +7 -0
  373. package/bin/mcp/server.mjs +52 -3
  374. package/bin/merge-status/derive.d.mts +2 -0
  375. package/bin/merge-status/derive.mjs +17 -13
  376. package/bin/merge-status/required-check-requirements.d.mts +7 -0
  377. package/bin/merge-status/required-check-requirements.mjs +15 -0
  378. package/bin/merge-status/requirements-format.d.mts +5 -0
  379. package/bin/merge-status/requirements-format.mjs +41 -12
  380. package/bin/merge-status/requirements.mjs +18 -7
  381. package/bin/state/base.mjs +41 -0
  382. package/bin/state/durable-state.d.mts +6 -0
  383. package/bin/state/durable-state.mjs +41 -0
  384. package/bin/state/graphql-quota-policy.mjs +23 -16
  385. package/bin/state/merge-request.d.mts +25 -0
  386. package/bin/state/merge-request.mjs +145 -0
  387. package/bin/state/pr-fingerprint.d.mts +15 -0
  388. package/bin/state/pr-fingerprint.mjs +22 -7
  389. package/bin/state/rest-cache.d.mts +3 -0
  390. package/bin/state/rest-cache.mjs +1 -0
  391. package/bin/state/rest-snapshot-report.d.mts +21 -0
  392. package/bin/state/rest-snapshot-report.mjs +52 -0
  393. package/bin/state/seen-comments.d.mts +3 -0
  394. package/bin/state/seen-comments.mjs +18 -0
  395. package/bin/types/api-usage.d.mts +9 -1
  396. package/bin/types/escalate.d.mts +1 -1
  397. package/bin/types/github.d.mts +23 -9
  398. package/bin/types/iterate.d.mts +24 -2
  399. package/bin/types/merge-action.d.mts +1 -1
  400. package/bin/types/merge-requirements.d.mts +7 -7
  401. package/bin/types/next-check.d.mts +12 -0
  402. package/bin/types/next-check.mjs +1 -0
  403. package/bin/types/poll-summary.d.mts +21 -0
  404. package/bin/types/report.d.mts +13 -1
  405. package/bin/types/review-thread.d.mts +1 -1
  406. package/bin/types.d.mts +1 -0
  407. package/bin/types.mjs +1 -0
  408. package/package.json +3 -2
  409. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  410. package/plugins/pr-shepherd/.codex.mcp.json +1 -1
  411. package/plugins/pr-shepherd/.mcp.json +1 -1
  412. package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +1 -1
  413. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +14 -51
  414. package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +12 -16
  415. package/plugins/pr-shepherd/skills/pr-shepherd/references/cloud-event-loop.md +14 -0
  416. package/plugins/pr-shepherd/skills/pr-shepherd/references/create-pr.md +10 -0
  417. package/plugins/pr-shepherd/skills/pr-shepherd/references/fix-code-loop.md +10 -0
  418. package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +9 -0
  419. package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +2 -0
  420. package/bin/github/gql/pr-fingerprint.gql +0 -75
  421. package/plugins/pr-shepherd/skills/pr-shepherd/references/journal.md +0 -7
  422. package/plugins/pr-shepherd/skills/pr-shepherd/references/review-mutations.md +0 -9
  423. package/plugins/pr-shepherd/skills/pr-shepherd/references/stack-merge.md +0 -7
@@ -0,0 +1,88 @@
1
+ import { loadConfig } from "../../config/load.mjs";
2
+ import { findMergeStrategies } from "../../config/merge-command-args.mjs";
3
+ import { chooseMergeMethod, configuredMergeMethod, } from "../../config/merge-method.mjs";
4
+ import { formatPrUrl } from "../../pr-reference.mjs";
5
+ import { buildPrShepherdCommand } from "../../cli/runner.mjs";
6
+ import { getGithubTransport } from "../../github/transport.mjs";
7
+ const ENQUEUE_MUTATION = "mutation EnqueuePullRequest($pullRequestId: ID!, $expectedHeadOid: GitObjectID!) { enqueuePullRequest(input: { pullRequestId: $pullRequestId, expectedHeadOid: $expectedHeadOid }) { mergeQueueEntry { id } } }";
8
+ export function buildMergeCommandPlan(input) {
9
+ if (input.transport === "rest" || getGithubTransport() === "rest") {
10
+ const configured = configuredMergeMethod(loadConfig().merge ?? {});
11
+ if (!input.queue && input.queueKnown !== true && configured)
12
+ return {
13
+ unavailable: "REST cannot verify merge-queue policy and cannot apply a configured direct merge method to an automatic merge request. Refresh branch policy before merging.",
14
+ };
15
+ let method;
16
+ if (!input.queue && input.queueKnown === true) {
17
+ const decision = chooseMergeMethod({
18
+ allowed: input.allowedMergeMethods,
19
+ configured,
20
+ fallback: "merge",
21
+ });
22
+ if ("unavailable" in decision)
23
+ return decision;
24
+ method = decision.method;
25
+ }
26
+ return {
27
+ mode: "rest",
28
+ command: buildPrShepherdCommand([
29
+ "apply",
30
+ "merge",
31
+ formatPrUrl(input.repo, input.pr),
32
+ "--require-sha",
33
+ input.headSha,
34
+ "--merge-action",
35
+ input.queue ? "merge_queue" : input.queueKnown ? "direct_merge" : "default",
36
+ ...(method ? ["--method", method] : []),
37
+ ...(input.expectedStack ? ["--expected-stack", JSON.stringify(input.expectedStack)] : []),
38
+ "--transport",
39
+ "rest",
40
+ ]),
41
+ };
42
+ }
43
+ const base = [
44
+ "gh",
45
+ "pr",
46
+ "merge",
47
+ String(input.pr),
48
+ "--repo",
49
+ input.repo,
50
+ "--match-head-commit",
51
+ input.headSha,
52
+ ];
53
+ if (input.queue) {
54
+ return {
55
+ mode: "queue",
56
+ command: { argv: base },
57
+ queueApiFallbackCommand: {
58
+ argv: [
59
+ "gh",
60
+ "api",
61
+ "graphql",
62
+ "-f",
63
+ `query=${ENQUEUE_MUTATION}`,
64
+ "-f",
65
+ `pullRequestId=${input.nodeId}`,
66
+ "-f",
67
+ `expectedHeadOid=${input.headSha}`,
68
+ ],
69
+ },
70
+ };
71
+ }
72
+ const mergeConfig = loadConfig().merge;
73
+ const configuredArgs = mergeConfig?.commandArgs ?? [];
74
+ const decision = chooseMergeMethod({
75
+ allowed: input.allowedMergeMethods,
76
+ configured: configuredMergeMethod(mergeConfig ?? {}),
77
+ fallback: "merge",
78
+ });
79
+ if ("unavailable" in decision)
80
+ return decision;
81
+ const hasStrategy = findMergeStrategies(configuredArgs).length > 0;
82
+ const commandArgs = hasStrategy ? configuredArgs : [...configuredArgs, `--${decision.method}`];
83
+ return {
84
+ mode: "auto",
85
+ command: { argv: [...base, "--auto", ...commandArgs] },
86
+ fallbackCommand: { argv: [...base, ...commandArgs] },
87
+ };
88
+ }
@@ -51,10 +51,13 @@ export function buildReadyMergeOutcome(enabled, readyElapsed, base, report) {
51
51
  report.mergeStatus.mergeRequirements?.mergeQueue?.enabled);
52
52
  const plan = buildMergeCommandPlan({
53
53
  pr: report.pr,
54
+ ...(report.transport && { transport: report.transport }),
54
55
  repo: report.repo,
55
56
  nodeId: report.nodeId,
56
57
  headSha: report.headSha ?? "unknown",
57
58
  queue,
59
+ queueKnown: (report.transport !== "rest" || report.branchProtection === null) &&
60
+ !(report.transportUnavailable ?? []).some(({ field }) => ["branchProtection", "branchRules"].includes(field)),
58
61
  ...(report.allowedMergeMethods && { allowedMergeMethods: report.allowedMergeMethods }),
59
62
  });
60
63
  if ("unavailable" in plan)
@@ -1,29 +1,18 @@
1
- import { type MergeMethod } from "../../config/merge-method.mts";
1
+ export { buildMergeCommandPlan } from "./merge-plan.mts";
2
2
  import type { AgentCheck, IterateResult, IterateResultBase, MergeCommandPlan, ShepherdReport } from "../../types.mts";
3
- interface MergePlanInput {
4
- pr: number;
5
- repo: string;
6
- nodeId: string;
7
- headSha: string;
8
- queue: boolean;
9
- /** Omitted when the batch did not select repository merge settings. */
10
- allowedMergeMethods?: readonly MergeMethod[];
11
- }
12
- export interface MergeMethodUnavailable {
3
+ interface QueueRecoveryUnavailable {
13
4
  unavailable: string;
14
5
  }
15
- export declare function buildMergeCommandPlan(input: MergePlanInput): MergeCommandPlan | MergeMethodUnavailable;
16
6
  export declare function renderMergeCommand(command: {
17
7
  argv: string[];
18
8
  }): string;
19
9
  /** Offer a guarded queue command; the caller decides whether the failure warrants a PR fix. */
20
- export declare function buildRemovedQueueRecovery(report: ShepherdReport, checks: AgentCheck[], merge: boolean | undefined): MergeCommandPlan | undefined;
10
+ export declare function buildRemovedQueueRecovery(report: ShepherdReport, checks: AgentCheck[], merge: boolean | undefined): MergeCommandPlan | QueueRecoveryUnavailable | undefined;
21
11
  /** Bind a caller's disposition to one removed stack queue commit, without submitting a merge. */
22
12
  export declare function buildStackQueueRemovalAcknowledgment(report: ShepherdReport, checks: AgentCheck[]): {
23
13
  argv: string[];
24
- } | undefined;
14
+ } | QueueRecoveryUnavailable | undefined;
25
15
  export declare function unavailableMergeResult(base: IterateResultBase, report: {
26
16
  pr: number;
27
17
  repo: string;
28
18
  }, unavailable: string): IterateResult;
29
- export {};
@@ -1,59 +1,11 @@
1
- import { loadConfig } from "../../config/load.mjs";
2
- import { findMergeStrategies } from "../../config/merge-command-args.mjs";
3
- import { chooseMergeMethod, configuredMergeMethod, } from "../../config/merge-method.mjs";
1
+ import { buildMergeCommandPlan } from "./merge-plan.mjs";
2
+ export { buildMergeCommandPlan } from "./merge-plan.mjs";
4
3
  import { formatPrUrl } from "../../pr-reference.mjs";
5
4
  import { renderShellCommand, buildPrShepherdCommand } from "../../cli/runner.mjs";
6
5
  import { hasQueueRecoveryEvidence } from "./check-evidence.mjs";
7
6
  import { isCiQueueRemovalReason } from "../../state/queue-removal-ack.mjs";
8
7
  import { buildEscalateHumanMessage } from "./escalate.mjs";
9
- const ENQUEUE_MUTATION = "mutation EnqueuePullRequest($pullRequestId: ID!, $expectedHeadOid: GitObjectID!) { enqueuePullRequest(input: { pullRequestId: $pullRequestId, expectedHeadOid: $expectedHeadOid }) { mergeQueueEntry { id } } }";
10
- export function buildMergeCommandPlan(input) {
11
- const base = [
12
- "gh",
13
- "pr",
14
- "merge",
15
- String(input.pr),
16
- "--repo",
17
- input.repo,
18
- "--match-head-commit",
19
- input.headSha,
20
- ];
21
- if (input.queue) {
22
- return {
23
- mode: "queue",
24
- command: { argv: base },
25
- queueApiFallbackCommand: {
26
- argv: [
27
- "gh",
28
- "api",
29
- "graphql",
30
- "-f",
31
- `query=${ENQUEUE_MUTATION}`,
32
- "-f",
33
- `pullRequestId=${input.nodeId}`,
34
- "-f",
35
- `expectedHeadOid=${input.headSha}`,
36
- ],
37
- },
38
- };
39
- }
40
- const mergeConfig = loadConfig().merge;
41
- const configuredArgs = mergeConfig?.commandArgs ?? [];
42
- const decision = chooseMergeMethod({
43
- allowed: input.allowedMergeMethods,
44
- configured: configuredMergeMethod(mergeConfig ?? {}),
45
- fallback: "merge",
46
- });
47
- if ("unavailable" in decision)
48
- return decision;
49
- const hasStrategy = findMergeStrategies(configuredArgs).length > 0;
50
- const commandArgs = hasStrategy ? configuredArgs : [...configuredArgs, `--${decision.method}`];
51
- return {
52
- mode: "auto",
53
- command: { argv: [...base, "--auto", ...commandArgs] },
54
- fallbackCommand: { argv: [...base, ...commandArgs] },
55
- };
56
- }
8
+ import { getGithubTransport } from "../../github/transport.mjs";
57
9
  export function renderMergeCommand(command) {
58
10
  return renderShellCommand(command.argv);
59
11
  }
@@ -63,6 +15,11 @@ export function buildRemovedQueueRecovery(report, checks, merge) {
63
15
  return undefined;
64
16
  if (!removedQueueRecoveryAvailable(report, checks, merge))
65
17
  return undefined;
18
+ // REST cannot prove that a recorded enqueue was removed or that no later enqueue exists.
19
+ // Never offer a resume command as if it could authorize a fresh same-head submission.
20
+ const unsupported = unsupportedRestQueueRecovery(report);
21
+ if (unsupported)
22
+ return unsupported;
66
23
  const plan = buildMergeCommandPlan({
67
24
  pr: report.pr,
68
25
  repo: report.repo,
@@ -79,6 +36,9 @@ export function buildStackQueueRemovalAcknowledgment(report, checks) {
79
36
  // This records a local disposition, without enqueueing; aggregate child sessions omit --merge.
80
37
  if (!removedQueueRecoveryAvailable(report, checks, true))
81
38
  return undefined;
39
+ const unsupported = unsupportedRestQueueRecovery(report);
40
+ if (unsupported)
41
+ return unsupported;
82
42
  const removal = report.mergeQueue.latestRemoval;
83
43
  return buildPrShepherdCommand([
84
44
  "apply",
@@ -92,6 +52,13 @@ export function buildStackQueueRemovalAcknowledgment(report, checks) {
92
52
  String(removal.createdAtUnix),
93
53
  ]);
94
54
  }
55
+ function unsupportedRestQueueRecovery(report) {
56
+ if (report.transport !== "rest" && getGithubTransport() !== "rest")
57
+ return undefined;
58
+ return {
59
+ unavailable: "Queue recovery is transport-unsupported: REST cannot verify current merge-queue removal evidence. Continue the printed fix steps; requeue and removal acknowledgment require verified current removal evidence.",
60
+ };
61
+ }
95
62
  function removedQueueRecoveryAvailable(report, checks, merge) {
96
63
  const queue = report.mergeQueue;
97
64
  const removedCommit = queue?.latestRemoval?.beforeCommitOid;
@@ -1,8 +1,4 @@
1
1
  import type { MergedBasePullRequest } from "../../types.mts";
2
+ import { type MergedBaseLookupInput } from "../../github/rest-merged-base-read.mts";
2
3
  /** A bounded, best-effort lookup; matching OIDs and repository prevent branch-name reuse false positives. */
3
- export declare function lookupMergedBasePullRequests(input: {
4
- owner: string;
5
- repo: string;
6
- baseRefName: string;
7
- baseRefOid: string;
8
- }): Promise<MergedBasePullRequest[]>;
4
+ export declare function lookupMergedBasePullRequests(input: MergedBaseLookupInput): Promise<MergedBasePullRequest[]>;
@@ -1,22 +1,26 @@
1
1
  import { graphql } from "../../github/client.mjs";
2
2
  import { MERGED_BASE_PULL_REQUESTS_QUERY } from "../../github/queries.mjs";
3
3
  import { pollRateLimitRetryAfterMs } from "../poll-quota.mjs";
4
+ import { githubOperation, isGithubReadFallbackError } from "../../github/transport.mjs";
5
+ import { readRestMergedBasePullRequests, } from "../../github/rest-merged-base-read.mjs";
4
6
  /** A bounded, best-effort lookup; matching OIDs and repository prevent branch-name reuse false positives. */
5
7
  export async function lookupMergedBasePullRequests(input) {
6
8
  try {
7
- const { data } = await graphql(MERGED_BASE_PULL_REQUESTS_QUERY, {
8
- owner: input.owner,
9
- repo: input.repo,
10
- branch: input.baseRefName,
11
- });
12
- return (data.repository?.pullRequests.nodes ?? []).filter((candidate) => candidate !== null &&
13
- candidate.state === "MERGED" &&
14
- candidate.headRefName === input.baseRefName &&
15
- candidate.headRefOid === input.baseRefOid &&
16
- candidate.headRepository?.nameWithOwner === `${input.owner}/${input.repo}`);
9
+ return await githubOperation("MergedBasePullRequests", async () => {
10
+ const { data } = await graphql(MERGED_BASE_PULL_REQUESTS_QUERY, {
11
+ owner: input.owner,
12
+ repo: input.repo,
13
+ branch: input.baseRefName,
14
+ });
15
+ return (data.repository?.pullRequests.nodes ?? []).filter((candidate) => candidate !== null &&
16
+ candidate.state === "MERGED" &&
17
+ candidate.headRefName === input.baseRefName &&
18
+ candidate.headRefOid === input.baseRefOid &&
19
+ candidate.headRepository?.nameWithOwner === `${input.owner}/${input.repo}`);
20
+ }, () => readRestMergedBasePullRequests(input));
17
21
  }
18
22
  catch (error) {
19
- if (pollRateLimitRetryAfterMs(error) !== null)
23
+ if (pollRateLimitRetryAfterMs(error) !== null || isGithubReadFallbackError(error))
20
24
  throw error;
21
25
  process.stderr.write(`pr-shepherd: merged-base PR lookup unavailable (ignored): ${error instanceof Error ? error.message : String(error)}\n`);
22
26
  return [];
@@ -22,10 +22,11 @@ export declare function buildNativeStackRebaseInstruction(repo: string, stackNum
22
22
  /**
23
23
  * The stack-aware branch update for a native stack layer (a conflict, or a behind branch whose
24
24
  * workflow keeps failing); undefined outside a stack.
25
- * A layer whose PR targets the stack's trunk (`stack.baseRefName`) is the bottom open layer —
26
- * position 1, or a higher layer GitHub retargeted after every layer below it merged. Any other
27
- * layer is an upper layer whose parent is its own PR base branch. `trunkConflict` means that
28
- * upper layer already contains its parent, so the rebase starts at the bottom open layer.
25
+ * The first open layer in validated stack order is the bottom open layer, even when GitHub
26
+ * retains its merged parent's branch as its PR base. Without that topology, a PR base matching
27
+ * the stack trunk also identifies a bottom layer. Other layers rebase from their PR base.
28
+ * `trunkConflict` means an upper layer already contains its parent, so the rebase starts
29
+ * at the bottom open layer.
29
30
  */
30
31
  export declare function buildNativeStackLayerRebase(repo: string, pr: {
31
32
  number: number;
@@ -33,7 +34,7 @@ export declare function buildNativeStackLayerRebase(repo: string, pr: {
33
34
  }, stack: StackStatus | undefined, trunkConflict?: {
34
35
  trunk: string;
35
36
  bottomPr?: number;
36
- }): string | undefined;
37
+ }, stackBottomPr?: number): string | undefined;
37
38
  /** Point at the conflicts, and at the stack rebase when the PR is a native stack layer. */
38
39
  export declare function buildConflictInstruction(stackRebase: string | undefined): string;
39
40
  /**
@@ -1,4 +1,5 @@
1
1
  import { playbookPointer } from "../playbook-pointer.mjs";
2
+ import { getGithubTransport } from "../../github/transport.mjs";
2
3
  /**
3
4
  * One gh-stack rebase step. A native stack layer must not be rebased or merged from its base
4
5
  * branch alone: that rewrites one branch and strands every layer above it.
@@ -8,6 +9,14 @@ import { playbookPointer } from "../playbook-pointer.mjs";
8
9
  * stack number before a PR number) and checks each local layer against its PR head.
9
10
  */
10
11
  export function buildNativeStackRebaseInstruction(repo, stackNumber, start) {
12
+ if (getGithubTransport() === "rest") {
13
+ const boundary = "parentBranch" in start
14
+ ? `parent branch \`${start.parentBranch}\``
15
+ : "trunk" in start
16
+ ? `trunk \`${start.trunk}\``
17
+ : `bottom open layer PR #${start.bottomPr}`;
18
+ return `Use the repository's stack-update procedure for native stack #${stackNumber} in \`${repo}\`, starting at ${boundary} and preserving the ordered parent boundaries of every affected upper layer. Read membership with REST-backed Shepherd \`--stack\` output and update and push affected branches using the caller's git workflow. ${playbookPointer("Branch update")}`;
19
+ }
11
20
  const [checkout, command] = "parentBranch" in start
12
21
  ? [
13
22
  `check out the parent stack branch \`${start.parentBranch}\``,
@@ -15,26 +24,32 @@ export function buildNativeStackRebaseInstruction(repo, stackNumber, start) {
15
24
  ]
16
25
  : "bottomPr" in start
17
26
  ? [`check out the head branch of PR #${start.bottomPr}`, "gh stack rebase"]
18
- : [`check out the bottom open layer whose base is \`${start.trunk}\``, "gh stack rebase"];
27
+ : [
28
+ `check out the bottom open layer of the stack to rebase onto \`${start.trunk}\``,
29
+ "gh stack rebase",
30
+ ];
19
31
  const prepare = `if \`gh stack\` does not track stack #${stackNumber} locally, import it with \`gh stack checkout ${stackNumber}\``;
20
32
  return `From a clean checkout of \`${repo}\`, ${prepare}. Then ${checkout} and run \`${command}\`. ${playbookPointer("Branch update")}`;
21
33
  }
22
34
  /**
23
35
  * The stack-aware branch update for a native stack layer (a conflict, or a behind branch whose
24
36
  * workflow keeps failing); undefined outside a stack.
25
- * A layer whose PR targets the stack's trunk (`stack.baseRefName`) is the bottom open layer —
26
- * position 1, or a higher layer GitHub retargeted after every layer below it merged. Any other
27
- * layer is an upper layer whose parent is its own PR base branch. `trunkConflict` means that
28
- * upper layer already contains its parent, so the rebase starts at the bottom open layer.
37
+ * The first open layer in validated stack order is the bottom open layer, even when GitHub
38
+ * retains its merged parent's branch as its PR base. Without that topology, a PR base matching
39
+ * the stack trunk also identifies a bottom layer. Other layers rebase from their PR base.
40
+ * `trunkConflict` means an upper layer already contains its parent, so the rebase starts
41
+ * at the bottom open layer.
29
42
  */
30
- export function buildNativeStackLayerRebase(repo, pr, stack, trunkConflict) {
43
+ export function buildNativeStackLayerRebase(repo, pr, stack, trunkConflict, stackBottomPr) {
31
44
  if (!stack)
32
45
  return undefined;
33
46
  const start = trunkConflict
34
47
  ? trunkConflict.bottomPr !== undefined
35
48
  ? { bottomPr: trunkConflict.bottomPr }
36
49
  : { trunk: trunkConflict.trunk }
37
- : pr.baseBranch === stack.baseRefName
50
+ : (stackBottomPr !== undefined
51
+ ? pr.number === stackBottomPr
52
+ : pr.baseBranch === stack.baseRefName)
38
53
  ? { bottomPr: pr.number }
39
54
  : { parentBranch: pr.baseBranch };
40
55
  return buildNativeStackRebaseInstruction(repo, stack.number, start);
@@ -49,6 +64,8 @@ export function buildConflictInstruction(stackRebase) {
49
64
  * After a merge-queue ejection update, push only when the update or a fix changed the head.
50
65
  */
51
66
  export function buildBranchPushInstruction(stackRebase, hasConflicts, mutationSuffix, ejectionUpdate = false) {
67
+ if (stackRebase && getGithubTransport() === "rest")
68
+ return `If the base update or a fix changed any heads, commit the remaining changes and push every affected stack branch using the repository's stack-update procedure${mutationSuffix}. If no heads changed, do not push.`;
52
69
  if (ejectionUpdate) {
53
70
  const push = stackRebase
54
71
  ? "commit any remaining changes on the PR head branch and push the rewritten stack with `gh stack push`"
@@ -1,7 +1,13 @@
1
1
  import type { AgentThread, AgentComment, AgentCheck, Review, ResolveCommand, FirstLookThread, FirstLookComment, ReviewThread } from "../../types.mts";
2
2
  import { type QueueEjectionRecovery } from "./queue-recovery-instructions.mts";
3
- /** Render a resolve command as a shell snippet. Appends `--require-sha "$HEAD_SHA"` when set. */
3
+ import type { InstructionStyle } from "../../config/load.mts";
4
+ /**
5
+ * Render a resolve command as a shell snippet. Appends `--require-sha HEAD` when set: run after
6
+ * the push step, the local HEAD is the pushed PR head, and `apply review` rejects the mutations
7
+ * if it is not. `apply review` reads that HEAD itself with a read-only `git rev-parse HEAD`.
8
+ */
4
9
  export declare function renderResolveCommand(rc: ResolveCommand): string;
5
10
  export declare function buildFixInstructions(threads: AgentThread[], actionableComments: AgentComment[], checks: AgentCheck[], changesRequestedReviews: Review[], baseBranch: string, resolveCommand: ResolveCommand, hasConflicts: boolean, prReference: string | number, _cancelledCount: number, firstLookThreads?: FirstLookThread[], firstLookComments?: FirstLookComment[], firstLookSummaries?: Review[], editedSummaries?: Review[], _inProgressRunIds?: string[], resolutionOnlyThreads?: ReviewThread[], resolveOnlyCommand?: ResolveCommand, behindBaseHint?: string, // iterate.behindBaseHint — see buildBehindBaseHintInstruction
6
11
  isBehind?: boolean, viewerCanUpdate?: boolean, hasExhaustedWorkflowRerun?: boolean, stackRebase?: string, // native stack layers rebase with gh-stack, not branch by branch
7
- queueEjection?: QueueEjectionRecovery): string[];
12
+ queueEjection?: QueueEjectionRecovery, // current merge-queue removal with failing queue checks
13
+ instructionStyle?: InstructionStyle): string[];
@@ -1,23 +1,28 @@
1
1
  import { renderShellCommand } from "../../cli/runner.mjs";
2
2
  import { buildFailingCheckInstructions, buildCrStaleClause, buildBehindBaseHintInstruction, buildRepeatedWorkflowBranchRecoveryInstructions, buildResolveCommandInstruction, buildFixCompletionInstruction, } from "./check-instructions.mjs";
3
- import { SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE, buildShepherdJournalInstruction, } from "../shepherd-journal.mjs";
3
+ import { SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE } from "../shepherd-journal.mjs";
4
4
  import { isFailingAgentCheck } from "../../checks/conclusions.mjs";
5
5
  import { buildCommitSuggestionInstruction } from "../commit-suggestion-instruction.mjs";
6
- import { partitionFixThreads, reviewSectionRefs } from "./fix-instruction-threads.mjs";
6
+ import { partitionFixThreads } from "./fix-instruction-threads.mjs";
7
7
  import { buildBranchPushInstruction, buildConflictInstruction } from "./native-stack-rebase.mjs";
8
8
  import { queueEjectionSteps } from "./queue-recovery-instructions.mjs";
9
- /** Render a resolve command as a shell snippet. Appends `--require-sha "$HEAD_SHA"` when set. */
9
+ import { buildPushJournalSteps } from "./fix-loop-instruction.mjs";
10
+ /**
11
+ * Render a resolve command as a shell snippet. Appends `--require-sha HEAD` when set: run after
12
+ * the push step, the local HEAD is the pushed PR head, and `apply review` rejects the mutations
13
+ * if it is not. `apply review` reads that HEAD itself with a read-only `git rev-parse HEAD`.
14
+ */
10
15
  export function renderResolveCommand(rc) {
11
- const parts = [...rc.argv];
12
- if (rc.requiresHeadSha)
13
- parts.push("--require-sha", "$HEAD_SHA");
14
- return renderShellCommand(parts);
16
+ const command = renderShellCommand(rc.argv);
17
+ return rc.requiresHeadSha ? `${command} ${REQUIRE_HEAD_SHA}` : command;
15
18
  }
19
+ const REQUIRE_HEAD_SHA = "--require-sha HEAD";
16
20
  export function buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseBranch, resolveCommand, hasConflicts, prReference, _cancelledCount, firstLookThreads = [], firstLookComments = [], firstLookSummaries = [], editedSummaries = [], _inProgressRunIds = [], resolutionOnlyThreads = [], resolveOnlyCommand, behindBaseHint = "", // iterate.behindBaseHint — see buildBehindBaseHintInstruction
17
21
  isBehind = false, viewerCanUpdate = false, hasExhaustedWorkflowRerun = false, stackRebase, // native stack layers rebase with gh-stack, not branch by branch
18
- queueEjection) {
22
+ queueEjection, // current merge-queue removal with failing queue checks
23
+ instructionStyle = "inline") {
19
24
  const instructions = [];
20
- const { locatedThreads, unlocatedMutatedThreads, unlocatedThreads } = partitionFixThreads(threads, resolveCommand, resolveOnlyCommand);
25
+ const { locatedThreads, unlocatedThreads } = partitionFixThreads(threads, resolveCommand, resolveOnlyCommand);
21
26
  const failingChecks = checks.filter((c) => isFailingAgentCheck(c));
22
27
  const repeatedWorkflowBranchRecoveryInstructions = buildRepeatedWorkflowBranchRecoveryInstructions(baseBranch, hasExhaustedWorkflowRerun, { isBehind, hasConflicts }, stackRebase);
23
28
  const hasRepeatedWorkflowBranchRecovery = repeatedWorkflowBranchRecoveryInstructions.length > 0;
@@ -28,18 +33,8 @@ queueEjection) {
28
33
  changesRequestedReviews.length > 0 ||
29
34
  actionableComments.length > 0;
30
35
  // Start with interpretation. The agent decides what raw feedback warrants a code change.
31
- if (hasNonConflictHints) {
32
- const actionableSections = reviewSectionRefs({
33
- hasReviewThreads: locatedThreads.length > 0 || unlocatedMutatedThreads.length > 0,
34
- hasUnlocatedSkipThreads: unlocatedThreads.length > 0,
35
- hasActionableComments: actionableComments.length > 0,
36
- hasFailingChecks: failingChecks.length > 0,
37
- hasAnnotations,
38
- hasChangesRequested: changesRequestedReviews.length > 0,
39
- });
40
- const sectionRef = actionableSections.length > 0 ? `under ${actionableSections.join(", ")}` : "above";
41
- instructions.push(`Review each item ${sectionRef} and decide whether it needs a code change.`);
42
- }
36
+ if (hasNonConflictHints)
37
+ instructions.push("Fix each warranted item.");
43
38
  // A conflicting native stack layer follows the printed gh-stack route, so it omits the hint.
44
39
  const branchUpdateHint = buildBehindBaseHintInstruction(baseBranch, behindBaseHint, {
45
40
  isBehind,
@@ -66,41 +61,32 @@ queueEjection) {
66
61
  const hasSuggestions = locatedThreads.some((t) => t.suggestion);
67
62
  if (hasSuggestions)
68
63
  instructions.push(buildCommitSuggestionInstruction(prReference, "## Review threads"));
69
- if (locatedThreads.length > 0 || actionableComments.length > 0) {
70
- // Actionable comments carry no file/line location (unlike threads), so "referenced above"
71
- // is only accurate when threads are present.
72
- const filesRef = locatedThreads.length > 0 ? "each file referenced above" : "the relevant files";
73
- instructions.push(`Apply every warranted review fix in ${filesRef}.`);
74
- }
75
64
  if (resolutionOnlyThreads.length > 0)
76
65
  instructions.push("Review the threads under `## Review threads to resolve` before running the generated mutations.");
77
66
  instructions.push(...buildFailingCheckInstructions(failingChecks), ...repeatedWorkflowBranchRecoveryInstructions, ...queueEjectionSteps(queueEjection, stackRebase, branchRecovery));
78
67
  if (hasAnnotations)
79
68
  instructions.push("Inspect every referenced range under `## Check annotations` and apply any warranted change.");
80
- if (changesRequestedReviews.length > 0) {
81
- const staleClause = buildCrStaleClause(changesRequestedReviews);
82
- instructions.push(`Read every body under \`## Changes-requested reviews\` and apply any warranted change.${staleClause}`);
83
- }
69
+ // The first step already covers applying changes-requested bodies; only stale CRs add a step.
70
+ const staleClause = buildCrStaleClause(changesRequestedReviews);
71
+ if (staleClause)
72
+ instructions.push(staleClause);
84
73
  if (!hintWithConflictStep)
85
74
  instructions.push(...branchUpdateHint);
86
75
  const hasReviewMutations = resolveCommand.hasMutations || resolveOnlyCommand?.hasMutations === true;
87
76
  const mutationSuffix = hasReviewMutations ? " before review mutations" : "";
88
- if (branchRecovery || queueEjection) {
89
- instructions.push(buildBranchPushInstruction(stackRebase, hasConflicts, mutationSuffix, !branchRecovery));
90
- }
91
- else if (hasNonConflictHints) {
92
- instructions.push("If you changed code, commit any remaining changes and push to the PR head branch. If you did not, do not commit.");
93
- }
94
- if (viewerCanUpdate &&
77
+ const genericPush = !(branchRecovery || queueEjection) && hasNonConflictHints;
78
+ const wantsJournal = viewerCanUpdate &&
95
79
  (hasReviewMutations ||
96
80
  hasNonConflictHints ||
97
81
  firstLookTotal > 0 ||
98
82
  firstLookSummaries.length > 0 ||
99
- editedTotal > 0)) {
100
- instructions.push(buildShepherdJournalInstruction(prReference));
83
+ editedTotal > 0);
84
+ if (branchRecovery || queueEjection) {
85
+ instructions.push(buildBranchPushInstruction(stackRebase, hasConflicts, mutationSuffix, !branchRecovery));
101
86
  }
87
+ instructions.push(...buildPushJournalSteps(instructionStyle, prReference, genericPush, wantsJournal));
102
88
  if (resolveOnlyCommand?.hasMutations)
103
- instructions.push("Run the `resolve-only:` command shown above.");
104
- instructions.push(...buildResolveCommandInstruction(resolveCommand), buildFixCompletionInstruction());
89
+ instructions.push(`Run: \`${renderResolveCommand(resolveOnlyCommand)}\``);
90
+ instructions.push(...buildResolveCommandInstruction(resolveCommand, renderResolveCommand(resolveCommand)), buildFixCompletionInstruction());
105
91
  return instructions;
106
92
  }
@@ -1,7 +1,7 @@
1
- /** An upper layer already contains its parent, so the dirty state is against trunk. */
1
+ /** A bottom open layer, or an upper layer that already contains its parent, updates from trunk. */
2
2
  export interface UpperLayerTrunkConflict {
3
3
  trunk: string;
4
- /** Open layer whose PR base is the trunk. Omitted when that layer is not on this page. */
4
+ /** First open layer after a verified merged prefix. Omitted when topology is unavailable. */
5
5
  bottomPr?: number;
6
6
  }
7
7
  export declare function lookupUpperLayerTrunkConflict(input: {
@@ -10,4 +10,6 @@ export declare function lookupUpperLayerTrunkConflict(input: {
10
10
  pr: number;
11
11
  headRef: string;
12
12
  trunk: string;
13
+ /** Reuse the bottom layer already resolved for the report, avoiding a topology fetch. */
14
+ bottomPr?: number;
13
15
  }): Promise<UpperLayerTrunkConflict | undefined>;
@@ -1,25 +1,57 @@
1
1
  import { graphql } from "../../github/client.mjs";
2
+ import { readStackTopology, verifiedBottomOpenLayer } from "../../github/stack-read.mjs";
2
3
  import { UPPER_LAYER_CONFLICT_TARGET_QUERY } from "../../github/queries.mjs";
3
4
  import { pollRateLimitRetryAfterMs } from "../poll-quota.mjs";
4
- /** Lowest-position open layer whose base is the stack trunk, if this page contains one. */
5
- function selectBottomOpenLayer(nodes, trunk) {
6
- const onTrunk = nodes.flatMap((node) => {
7
- const pull = node?.pullRequest;
8
- if (!node || !pull || pull.state !== "OPEN" || pull.baseRefName !== trunk)
9
- return [];
10
- return [{ position: node.position, number: pull.number }];
11
- });
12
- onTrunk.sort((a, b) => a.position - b.position);
13
- return onTrunk[0]?.number;
14
- }
5
+ import { githubOperation } from "../../github/transport.mjs";
6
+ import { readRestPull } from "../../github/rest-pr-core.mjs";
7
+ import { readRestBehind } from "../../github/rest-check-read.mjs";
15
8
  export async function lookupUpperLayerTrunkConflict(input) {
9
+ let bottomPr = input.bottomPr;
10
+ if (bottomPr === undefined) {
11
+ let topology;
12
+ try {
13
+ topology = await readStackTopology(input.pr, { owner: input.owner, name: input.name });
14
+ }
15
+ catch (err) {
16
+ if (pollRateLimitRetryAfterMs(err) !== null)
17
+ throw err;
18
+ ignore(input.pr, err instanceof Error ? err.message : String(err));
19
+ }
20
+ if (topology) {
21
+ try {
22
+ bottomPr = verifiedBottomOpenLayer(topology.ordered, input.pr).number;
23
+ }
24
+ catch (err) {
25
+ ignore(input.pr, err instanceof Error ? err.message : String(err));
26
+ // A base comparison cannot prove that a closed or unverified predecessor merged.
27
+ return undefined;
28
+ }
29
+ }
30
+ }
31
+ // Merged lower layers need not cause GitHub to retarget this PR to trunk. Even when its
32
+ // head is behind that old parent branch, the first open layer rebases the stack onto trunk.
33
+ if (bottomPr === input.pr)
34
+ return { trunk: input.trunk, bottomPr };
16
35
  let data;
17
36
  try {
18
- ({ data } = await graphql(UPPER_LAYER_CONFLICT_TARGET_QUERY, {
37
+ ({ data } = await githubOperation("UpperLayerConflictTarget", () => graphql(UPPER_LAYER_CONFLICT_TARGET_QUERY, {
19
38
  owner: input.owner,
20
39
  name: input.name,
21
40
  number: input.pr,
22
41
  headRef: input.headRef,
42
+ }), async () => {
43
+ const repo = { owner: input.owner, name: input.name };
44
+ const pull = await readRestPull(input.pr, repo);
45
+ const behindBy = await readRestBehind(repo, pull.base.ref, input.headRef);
46
+ return {
47
+ data: {
48
+ repository: {
49
+ pullRequest: {
50
+ baseRef: { compare: { behindBy } },
51
+ },
52
+ },
53
+ },
54
+ };
23
55
  }));
24
56
  }
25
57
  catch (err) {
@@ -37,13 +69,6 @@ export async function lookupUpperLayerTrunkConflict(input) {
37
69
  // behindBy > 0: the layer is still behind its parent. Keep the parent rebase.
38
70
  if (behindBy !== 0)
39
71
  return undefined;
40
- const entries = pull.stack?.entries;
41
- const bottomPr = entries ? selectBottomOpenLayer(entries.nodes, input.trunk) : undefined;
42
- if (bottomPr === undefined) {
43
- ignore(input.pr, entries?.pageInfo.hasNextPage
44
- ? "stack entry page truncated before the trunk layer"
45
- : "bottom open layer not found");
46
- }
47
72
  return { trunk: input.trunk, ...(bottomPr !== undefined && { bottomPr }) };
48
73
  }
49
74
  function ignore(pr, message) {
@@ -1,5 +1,6 @@
1
1
  import { readStackTopology, stackAncestryGaps } from "../../github/stack-read.mjs";
2
2
  import { buildNativeStackRebaseInstruction } from "./native-stack-rebase.mjs";
3
+ import { getGithubTransport } from "../../github/transport.mjs";
3
4
  /**
4
5
  * Find a stale immediate parent boundary for one PR.
5
6
  *
@@ -36,6 +37,8 @@ function buildStaleNativeStackAncestryInstructions(repo, stackNumber, ancestry)
36
37
  buildNativeStackRebaseInstruction(`${repo.owner}/${repo.name}`, stackNumber, {
37
38
  parentBranch: ancestry.parentHeadRefName,
38
39
  }),
39
- "Push the rewritten stack with `gh stack push`.",
40
+ getGithubTransport() === "rest"
41
+ ? "Push every affected stack branch using the repository's stack-update procedure."
42
+ : "Push the rewritten stack with `gh stack push`.",
40
43
  ];
41
44
  }