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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
3
  "description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
4
- "version": "0.56.5",
4
+ "version": "0.57.0",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
7
7
  "email": "jonathanrichardong@gmail.com"
package/README.md CHANGED
@@ -15,7 +15,7 @@ An agent finishing a PR should think about code, not reconstruct GitHub state or
15
15
 
16
16
  Highlights:
17
17
 
18
- - Batched GraphQL reads and writes (plus REST where GraphQL cannot) so one poll replaces a tool-call fan-out. MCP `iterate` is one tick and the client owns recurrence; `--debounce` is a poll-dispatcher settle window, not an MCP tool.
18
+ - GraphQL by default, with a supported REST transport for Claude Code cloud sessions and narrowly defined automatic fallback. REST may expose less context; missing fields remain unknown. MCP `iterate` is one tick and the client owns recurrence; `--debounce` is a poll-dispatcher settle window, not an MCP tool.
19
19
  - CI summaries include failed checks, and the failed job/step plus a log excerpt when triage can fetch them. Job and log details are omitted for `STARTUP_FAILURE` and `CANCELLED`; agents may still inspect logs.
20
20
  - Handles GitHub comment types (comments, threads, replies) and their states, including first-look, outdated, resolved, minimized, and edited.
21
21
  - `apply` batches resolve / reply / minimize / dismiss. `build_suggestion_patches` validates and returns ordered diffs without mutating git.
@@ -34,8 +34,8 @@ Each tick returns exactly one action:
34
34
  - `WAIT` — no immediate action; continue with the next poll.
35
35
  - `MARK_READY` — the CLI converted an eligible draft PR to ready; continue polling.
36
36
  - `FIX_CODE` — agent work is required; complete it, push when needed, then continue polling. Push access to the PR head branch is a usage precondition.
37
- - `MERGE` — run the emitted head-pinned auto-merge or queue command. Ordinary merges include a plain-merge fallback; queue merges include a GraphQL enqueue fallback. GitHub is authoritative for the result and reports any authorization failure.
38
- - `CANCEL` — stop polling this pull request because it merged, closed, or completed its ready-delay. Continue any remaining pull requests or issues from the original request.
37
+ - `MERGE` — run the emitted head-pinned merge or queue command for the selected transport. GitHub is authoritative for the result and reports any authorization failure. Queue operations can use REST.
38
+ - `CANCEL` — stop polling this pull request because it merged, closed, or completed its ready-delay. It prints no instructions; continue any remaining pull requests or issues from the original request.
39
39
  - `ESCALATE` — stop polling until a human provides direction. Native stacks reach this only after their autonomous one-PR sessions are exhausted.
40
40
 
41
41
  Native-stack summaries additionally use stack-level `SHEPHERD`: run the listed one-PR sessions,
@@ -48,14 +48,12 @@ Example shape:
48
48
 
49
49
  # PR #123 [FIX_CODE]
50
50
 
51
- **status** `UNRESOLVED_COMMENTS` · **merge** `CLEAN` · **state** `OPEN` · **repo** `owner/repo`
51
+ **status** `UNRESOLVED_COMMENTS` · **repo** `owner/repo`
52
52
  **summary** 3 passing
53
- Approvals: None [Not Required]
54
- Conversations Resolved: No [Not Required]
55
53
 
56
54
  ## Review threads
57
55
 
58
- ### `threadId=PRRT_kwDOSGizTs58XB1L` — `src/commands/iterate/index.mts:42` (@alice · User · MEMBER)
56
+ ### `threadId=PRRT_kwDOSGizTs58XB1L` — `src/commands/iterate/index.mts:42` (@alice · MEMBER)
59
57
 
60
58
  > The variable name is misleading.
61
59
 
@@ -64,21 +62,14 @@ Conversations Resolved: No [Not Required]
64
62
  - `24697658766` — `CI › lint / typecheck / test (22.x)` [conclusion: FAILURE]
65
63
  > oxfmt
66
64
 
67
- ## Post-fix actions
68
-
69
- - base: `main`
70
- - apply review: `pr-shepherd apply review 123 --reply-thread-ids PRRT_kwDOSGizTs58XB1L --message "$DISMISS_MESSAGE" --require-sha "$HEAD_SHA"`
71
-
72
65
  ## Instructions
73
66
 
74
- 1. Review each item under `## Review threads` and `## Failing checks` and decide whether it needs a code change.
75
- 2. Apply every warranted review fix in each file referenced above.
76
- 3. Triage `## Failing checks`. Playbook: "CI failure triage".
77
- 4. If you changed code, commit any remaining changes and push to the PR head branch. If you did not, do not commit.
78
- 5. If you did not change code, replace `$HEAD_SHA` with `$(git rev-parse HEAD)` (it must equal the remote PR head). If you did, use the pushed SHA.
79
- 6. Replace `$DISMISS_MESSAGE` with one sentence describing what changed.
80
- 7. Run the `apply review:` command above. Playbook: "Review-mutation mechanics".
81
- 8. `[FIX_CODE]` is non-terminal. Iterate immediately with the same options.
67
+ 1. Fix each warranted item.
68
+ 2. Triage `## Failing checks`. Playbook: "CI failure triage".
69
+ 3. Commit and push any changes.
70
+ 4. Journal key decisions or rejections with URLs or IDs: `pr-shepherd apply journal 123 '- <decision>'`
71
+ 5. `$DISMISS_MESSAGE`: one sentence on what changed. Run even if no code changed: `pr-shepherd apply review 123 --reply-thread-ids PRRT_kwDOSGizTs58XB1L --message "$DISMISS_MESSAGE" --require-sha HEAD`
72
+ 6. Rerun Shepherd now.
82
73
  ```
83
74
 
84
75
  See [docs/actions.md](docs/actions.md) for the complete output contract and [docs/escalations.md](docs/escalations.md) for the exact finite human-handoff boundary. Iterate/poll PR outcomes use exit codes `0` and `10`–`16`; command and GitHub failures use `sysexits.h` codes — [docs/exit-codes.md](docs/exit-codes.md).
@@ -94,7 +85,7 @@ This system is opinionated and works best with PRs that use required status chec
94
85
  - Draft PRs can be marked ready automatically when clean; disable with `actions.autoMarkReady: false` or `--no-auto-mark-ready`.
95
86
  - With `--merge`, actionable review threads/comments/reviews/summaries are held back (`WAIT`, with raw deferred-work counts) while a PR sits in the merge queue, since a Shepherd-initiated push would eject it; set `actions.workWhileQueued: true` to act on them immediately instead. Failing checks and merge conflicts are never deferred.
96
87
  - The CLI never performs git mutations itself — it only emits commit/push instructions for the agent to run. Push access to the PR head is a usage precondition; GitHub viewer fields do not create a separate push-authorization handoff.
97
- - Generated iterate mutations and automatic actions are capability-aware and omit unauthorized commands. Explicit `apply` operations forward the caller's requested IDs without iterate's author or capability policy and surface GitHub's result.
88
+ - Generated iterate mutations use capability fields when the transport supplies them. With REST, capability is unknown and otherwise eligible mutations are attempted; GitHub's response is authoritative. Explicit `apply` operations forward the caller's requested IDs and surface GitHub's result.
98
89
  - `build_suggestion_patches` turns one or more ordered GitHub suggestion threads into checked patches and commit metadata, but never edits the working tree or git history. Local HEAD may be ahead when the live PR head is its ancestor.
99
90
 
100
91
  ## Usage
@@ -135,6 +126,9 @@ pr-shepherd 42 --debounce 5m # wait 5m after first FIX_CODE or stack S
135
126
  pr-shepherd 42 --ready-delay 15m
136
127
  pr-shepherd 42 --merge # request head-pinned auto-merge/queue; GitHub reports the result
137
128
  pr-shepherd iterate 42 # single tick
129
+ pr-shepherd 42 --instructions playbook # fold invariant fix_code steps into a playbook pointer (default: inline)
130
+ pr-shepherd 42 --poll-mode event # one tick, no sleeping; prints nextCheck (default under CLAUDE_CODE_REMOTE=true)
131
+ pr-shepherd playbook "Fix-code loop" # print a bundled skill playbook (no name lists them)
138
132
  pr-shepherd owner/repo#42 # poll a PR in an explicit repository
139
133
  pr-shepherd https://github.com/owner/repo/pull/42
140
134
  pr-shepherd 42 43 44 # summarize an explicit same-repository set
@@ -144,13 +138,16 @@ pr-shepherd --stack 43 # summarize every PR in a native GitHub s
144
138
  A user-supplied `--merge` authorizes the agent to run the emitted merge/enqueue commands for the
145
139
  selected PRs or stack without another conversational confirmation. Host permission checks still apply.
146
140
 
147
- Multi-PR and `--stack` polling use compact, read-only GraphQL summaries. They return when work is
141
+ Multi-PR and `--stack` polling use compact, read-only summaries from the selected transport.
142
+ GraphQL mode uses GraphQL summaries; REST mode preserves unavailable fields as unknown. They return when work is
148
143
  needed, every selected PR is complete, the bounded timeout expires, or `--until-terminal` crosses a
149
144
  configured GraphQL quota-warning band. Explicit PR sets give each actionable row an exact single-PR
150
145
  `pollCommand`, so independent rows can proceed before the next aggregate poll.
151
146
 
152
147
  Native-stack rows are ordered bottom-to-top. `--stack` never performs a mutation itself. Every
153
- layer that still has work gets its own one-PR session on the same tick, including a clean draft
148
+ row marks ownership by comparing its author with the authenticated viewer; REST reads `/user`
149
+ for that identity without inferring viewer permissions. Sessions act only on rows marked `owned`. Every
150
+ owned layer that still has work gets its own one-PR session on the same tick, including a clean draft
154
151
  whose session marks it ready. Layers do not wait for a lower layer's READY receipt, so their
155
152
  ready-delays overlap. With automatic mark-ready disabled, the instructions ask the agent to mark
156
153
  a clean draft ready after its probe. A queued stack, or one whose remaining layers can only wait,
@@ -161,14 +158,20 @@ until then, `SHEPHERD` remains the immediate action and lists the human blockers
161
158
 
162
159
  With `--stack --merge`, the highest open layer whose open lower layers all have current READY
163
160
  receipts, and whose bottom open layer GitHub has retargeted onto the stack base, returns `MERGE`
164
- with `gh stack merge <that PR number> --yes` and the allowed method flag (`--squash` unless config or the repository selects another). That lands the named layer and every
161
+ with the transport-specific stack merge command and allowed method. GraphQL mode uses
162
+ `gh stack merge <that PR number> --yes`; REST mode uses the SHA-pinned
163
+ `pr-shepherd apply merge` command, using the trunk's known queue policy even when the named layer
164
+ targets its parent branch. The command binds the observed stack, trunk, and ordered prefix with
165
+ `--expected-stack` and rejects a changed stack before submission. Unknown REST queue policy uses
166
+ `default` without a merge method. Known queue requests omit the configured direct merge method.
167
+ That lands the named layer and every
165
168
  unmerged layer below it. When the base uses a merge queue, the same command queues the prefix
166
169
  together and GitHub evaluates each layer from the bottom; a failure ejects that layer and those
167
170
  above it. For a `failed_checks` removal on the layer's first ejection, the one-PR session emits a head-, queue-commit-, and timestamp-pinned local acknowledgment command; run it only when the failure does not reproduce after updating from the latest base, the head did not change, and no source changes or other blockers remain. Fresh source checks and a new READY receipt then let the aggregate selector recover the eligible prefix. Manual or stale removals cannot use this path. Layers above the prefix keep their one-PR sessions. After the merge, GitHub retargets
168
171
  the next layer, so the rerun continues until the stack returns `CANCEL`. API and MCP aggregate
169
172
  calls perform one summary tick and leave recurrence to the caller.
170
173
 
171
- Polling defaults can be set under `poll` in `.pr-shepherdrc.yml`: `intervalSeconds` (built-in 60 for one PR), `stackIntervalFactor` (built-in 2, so `--stack` and multi-PR polls wait 120s), `timeoutSeconds`, `debounceSeconds`, and `quietStatus`. Explicit `--interval` overrides either period for that invocation and is not multiplied. Other explicit flags override configuration, including `--no-quiet-status` when a shared config enables quiet output. Quiet status remains off by default. Quota-warning bands stay multiples of `intervalSeconds`; the dispatcher sleeps the slower of the effective interval and the active band, so a default stack waits 120s until a tighter band is slower than that.
174
+ Polling defaults can be set under `poll` in `.pr-shepherdrc.yml`: `intervalSeconds` (built-in 60 for one PR), `stackIntervalFactor` (built-in 2, so `--stack` and multi-PR polls wait 120s), `timeoutSeconds`, `debounceSeconds`, `quietStatus`, and `reconcileSeconds` (built-in 900; the longest an idle GraphQL wait replays its last snapshot while REST change detectors are unchanged). Explicit `--interval` overrides either period for that invocation and is not multiplied. Other explicit flags override configuration, including `--no-quiet-status` when a shared config enables quiet output. Quiet status remains off by default. Quota-warning bands stay multiples of `intervalSeconds`; the dispatcher sleeps the slower of the effective interval and the active band, so a default stack waits 120s until a tighter band is slower than that.
172
175
 
173
176
  ### Apply Review And Journal Changes, Or Select Files
174
177
 
@@ -193,7 +196,8 @@ containers and ignores journal-shaped examples hidden in Markdown constructs. Th
193
196
  including append and reconciliation helpers, is documented in [docs/api.md](docs/api.md).
194
197
 
195
198
  MCP clients can call `extract_journal({ body: prBody })` for the same pure result without writing a
196
- file, or `get_journal({ pr: "owner/repo#123" })` to fetch a PR body through GraphQL and extract it.
199
+ file, or `get_journal({ pr: "owner/repo#123" })` to fetch a PR body through the selected
200
+ transport and extract it.
197
201
  Both return the typed extraction JSON in `structuredContent` and `content`; neither mutates the PR.
198
202
 
199
203
  For shell automation that already has a PR body, use the equivalent local-only command:
@@ -209,6 +213,8 @@ Unsupported platforms fail closed with that same exit code.
209
213
 
210
214
  ### Clean Local State
211
215
 
216
+ In Claude Code cloud sessions, [event mode](docs/cloud.md) runs one tick per event or wake-up instead of sleeping, and the default state directory moves into the repository's common directory.
217
+
212
218
  `pr-shepherd` stores seen markers, fix-attempt counters, stall fingerprints, ready-delay markers, and logs under `$PR_SHEPHERD_STATE_DIR` (default: `pr-shepherd-state` in the per-user temp dir; see [configuration](docs/configuration.md#environment-variables)).
213
219
 
214
220
  ```sh
@@ -314,6 +320,17 @@ For guided noise reduction, use the plugin's `reduce-pr-noise` skill. It loads f
314
320
 
315
321
  ### Classification rules
316
322
 
323
+ For common vendor bot noise (CodeRabbit and Gemini rate-limit notices, Codex usage-limit and review boilerplate, Codecov reports, and more), enable the bundled rules instead of writing your own:
324
+
325
+ ```yaml
326
+ # .pr-shepherdrc.yml
327
+ classify:
328
+ standard:
329
+ all: true
330
+ ```
331
+
332
+ See [`classify.standard`](docs/configuration.md#classifystandard--default-) for the rule list and per-rule overrides. Write custom rules for repository-specific policy.
333
+
317
334
  Drop `.ts` / `.mts` / `.mjs` / `.js` files under `.pr-shepherd/classification/` to suppress and/or auto-resolve specific bot comments — useful for silencing repetitive noise like rate-limit notices from `gemini-code-assist` or "Reviews paused" from `coderabbitai`.
318
335
 
319
336
  ```ts
@@ -328,7 +345,7 @@ const rule: ClassifyRule = (item) => {
328
345
  export default rule;
329
346
  ```
330
347
 
331
- `suppress: true` hides the item from agent output. `autoResolve: true` queues it for the minimize/resolve mutation. `reason` is an optional note. When both flags apply together and `actions.autoMinimizeSuppressed` is `true` (the default), Shepherd resolves the thread or minimizes the comment or review summary during `iterate` only when GitHub reports the exact per-object capability. Confirmed successes are recorded on `threads.autoResolved` and `comments.autoMinimized`, printed once as `## Classification auto-resolve` before `## Instructions` (the same `ruleAutoResolve` object in JSON), and appended as one Shepherd Journal list item attributed to the token login. Distinct rule reasons are included. Failed IDs stay on the generated `apply review` command, and each failure is an extra bullet in that section. A journal write failure is reported the same way and does not undo the GitHub resolve or minimize. `actions.autoMinimizeSuppressed: false` leaves the IDs on that command and does not mutate, journal, or print the line. Denied or unverifiable items return to the normal first-look/edit visibility gate and produce no mutation recommendation.
348
+ `suppress: true` hides the item from agent output. `autoResolve: true` queues it for the minimize/resolve mutation. `reason` is an optional note. When both flags apply together and `actions.autoMinimizeSuppressed` is `true` (the default), Shepherd attempts supported operations and relies on GitHub's response when capability is unknown. Confirmed successes are recorded on `threads.autoResolved` and `comments.autoMinimized`, printed once as `## Classification auto-resolve` before `## Instructions` (the same `ruleAutoResolve` object in JSON), and appended as one Shepherd Journal list item attributed to the token login. Distinct rule reasons are included. Failed IDs stay on the generated `apply review` command, and each failure is an extra bullet in that section. A journal write failure is reported the same way and does not undo the GitHub resolve or minimize. `actions.autoMinimizeSuppressed: false` leaves the IDs on that command and does not mutate, journal, or print the line. Denied review mutations are surfaced once then skipped; REST comment minimization is unsupported and is surfaced as a one-look skip. See [transport behavior](docs/rest.md).
332
349
 
333
350
  TypeScript rules are loaded by the runtime's native TypeScript support; keep them to erasable syntax such as type annotations and `import type`. Runtime TypeScript features that need transpilation, such as enums, namespaces, parameter properties, and decorators, are not supported. Use `.mts` for portable ESM rules across Node, Bun, and Deno.
334
351
 
package/bin/api.d.mts CHANGED
@@ -1,3 +1,9 @@
1
+ import { type GithubTransport } from "./github/transport.mts";
2
+ export type { GithubTransport } from "./github/transport.mts";
3
+ import { type ApplyMergeResult } from "./commands/apply-merge.mts";
4
+ import type { RestMergeAction, RestMergeStackGuard } from "./github/rest-merge.mts";
5
+ export type { RestMergeStackGuard } from "./github/rest-merge.mts";
6
+ import type { MergeMethod } from "./config/merge-method.mts";
1
7
  import { type JournalResult } from "./commands/journal/index.mts";
2
8
  import { type MarkFilesAsViewedResult } from "./commands/mark-files-as-viewed.mts";
3
9
  import { type ApplyQueueRemovalResult } from "./commands/apply-queue-removal.mts";
@@ -5,12 +11,14 @@ import type { ResolveResult } from "./comments/resolve.mts";
5
11
  import type { BuildSuggestionPatchesResult, CommitSuggestionResult, IterateCommandOptions, IterateResult, PollSummaryResult } from "./types.mts";
6
12
  import { type ShepherdJournalExtraction } from "./journal/index.mts";
7
13
  export interface CreatePrShepherdOptions {
14
+ /** GitHub transport retained for this client; individual calls may override it. */
15
+ transport?: GithubTransport;
8
16
  /** Working directory used for git, config, and classification-rule lookups. */
9
17
  cwd?: string;
10
18
  }
11
19
  /** A positive PR number, GitHub pull-request URL, or owner/repo#number reference. */
12
20
  export type PrReference = number | string;
13
- type IterateOptions = Omit<IterateCommandOptions, "format" | "prNumber" | "targetRepository" | "persistSeen" | "fingerprintCache" | "deferQuotaWarning" | "quotaWarningMinimumPollIntervalMinutes">;
21
+ type IterateOptions = Omit<IterateCommandOptions, "format" | "prNumber" | "targetRepository" | "persistSeen" | "fingerprintCache" | "stallDeadlineSink" | "deferQuotaWarning" | "quotaWarningMinimumPollIntervalMinutes">;
14
22
  export type SingleIterateInput = IterateOptions & {
15
23
  pr?: PrReference;
16
24
  prs?: never;
@@ -34,6 +42,7 @@ export interface ReviewMutationsOperation {
34
42
  dismissReviewIds?: string[];
35
43
  /** Required when replying to a thread or dismissing a review. */
36
44
  message?: string;
45
+ /** `HEAD` (this checkout's commit) or a full SHA the PR head must match before mutating. */
37
46
  requireSha?: string;
38
47
  }
39
48
  export interface MarkFilesViewedOperation {
@@ -54,14 +63,25 @@ export interface AcknowledgeQueueRemovalOperation {
54
63
  queueCommitOid: string;
55
64
  removedAtUnix: number;
56
65
  }
66
+ export interface MergeOperation {
67
+ type: "merge";
68
+ requireSha: string;
69
+ mergeAction: RestMergeAction;
70
+ mergeMethod?: MergeMethod;
71
+ expectedStack?: RestMergeStackGuard;
72
+ }
57
73
  /** Operations run in this exact list order after validation. */
58
- export type ApplyOperation = ReviewMutationsOperation | MarkFilesViewedOperation | AppendJournalOperation | AcknowledgeQueueRemovalOperation;
74
+ export type ApplyOperation = MergeOperation | ReviewMutationsOperation | MarkFilesViewedOperation | AppendJournalOperation | AcknowledgeQueueRemovalOperation;
59
75
  export interface ApplyInput {
76
+ transport?: GithubTransport;
60
77
  /** PR shared by every operation in this ordered apply request. */
61
78
  pr?: PrReference;
62
79
  operations: ApplyOperation[];
63
80
  }
64
81
  export type ApplyOperationResult = {
82
+ type: "merge";
83
+ result: ApplyMergeResult;
84
+ } | {
65
85
  type: "review_mutations";
66
86
  result: ResolveResult;
67
87
  } | {
@@ -78,6 +98,7 @@ export interface ApplyResult {
78
98
  operations: ApplyOperationResult[];
79
99
  }
80
100
  export interface BuildSuggestionPatchInput {
101
+ transport?: GithubTransport;
81
102
  pr?: PrReference;
82
103
  threadId: string;
83
104
  message: string;
@@ -89,10 +110,12 @@ export interface SuggestionPatchInput {
89
110
  description?: string;
90
111
  }
91
112
  export interface BuildSuggestionPatchesInput {
113
+ transport?: GithubTransport;
92
114
  pr?: PrReference;
93
115
  suggestions: SuggestionPatchInput[];
94
116
  }
95
117
  export interface GetJournalInput {
118
+ transport?: GithubTransport;
96
119
  pr: PrReference;
97
120
  }
98
121
  export interface PrShepherd {
@@ -120,4 +143,3 @@ export declare class PartialApplyError extends Error {
120
143
  * read/plan, ordered apply, and suggestion-patch operations.
121
144
  */
122
145
  export declare function createPrShepherd(options?: CreatePrShepherdOptions): PrShepherd;
123
- export {};
package/bin/api.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  /* eslint-disable max-lines */
2
2
  import { resolve } from "node:path";
3
+ import { createGithubTransportRunner } from "./github/transport.mjs";
4
+ import { runApplyMerge, validateApplyMergeOptions, } from "./commands/apply-merge.mjs";
3
5
  import { runCommitSuggestion } from "./commands/commit-suggestion.mjs";
4
6
  import { runSuggestionPatches } from "./commands/suggestion-patches.mjs";
5
- import { runIterate } from "./commands/iterate/index.mjs";
6
- import { runPollSummary } from "./commands/poll-summary.mjs";
7
+ import { runIterateForMode, runPollSummaryForMode } from "./commands/event-mode.mjs";
7
8
  import { runJournal } from "./commands/journal/index.mjs";
8
9
  import { validateJournalItem } from "./commands/journal/transform.mjs";
9
10
  import { runMarkFilesAsViewed, } from "./commands/mark-files-as-viewed.mjs";
@@ -12,6 +13,7 @@ import { applyQueueRemovalAck, } from "./commands/apply-queue-removal.mjs";
12
13
  import { runWithExecutionCwd } from "./execution-context.mjs";
13
14
  import { parsePrReference, normalizeRepositoryIdentity, resolveParsedPrTarget, } from "./pr-reference.mjs";
14
15
  import { getPullRequestBody, getRepoInfo } from "./github/client.mjs";
16
+ import { getLocalHeadSha } from "./commands/suggestion-patch-git.mjs";
15
17
  import { extractShepherdJournal } from "./journal/index.mjs";
16
18
  /** Raised before any API mutation when an input cannot be validated. */
17
19
  export class PrShepherdValidationError extends Error {
@@ -38,22 +40,26 @@ export class PartialApplyError extends Error {
38
40
  */
39
41
  export function createPrShepherd(options = {}) {
40
42
  const cwd = options.cwd === undefined ? undefined : resolve(options.cwd);
43
+ const transportRunner = createGithubTransportRunner(options.transport);
44
+ function runInClient(input, work) {
45
+ return runWithExecutionCwd(cwd, () => transportRunner(work, input?.transport));
46
+ }
41
47
  function iterate(input = {}) {
42
- return runWithExecutionCwd(cwd, async () => {
48
+ return runInClient(input, async () => {
43
49
  validateIterateSelectors(input);
44
50
  if ("prs" in input || "stack" in input) {
45
51
  const target = await resolveAggregateIterateInput(input);
46
- return runPollSummary(target);
52
+ return runPollSummaryForMode(target);
47
53
  }
48
54
  const { pr: _pr, ...iterateOptions } = input;
49
55
  const target = resolvePrReference(input.pr);
50
- return runIterate({ ...iterateOptions, ...target, format: "json" });
56
+ return runIterateForMode({ ...iterateOptions, ...target, format: "json" });
51
57
  });
52
58
  }
53
59
  return Object.freeze({
54
60
  iterate,
55
61
  getJournal(input) {
56
- return runWithExecutionCwd(cwd, async () => {
62
+ return runInClient(input, async () => {
57
63
  const { prNumber, targetRepository } = resolvePrReference(input.pr);
58
64
  if (!prNumber)
59
65
  throw new PrShepherdValidationError("getJournal requires a PR reference");
@@ -63,7 +69,7 @@ export function createPrShepherd(options = {}) {
63
69
  });
64
70
  },
65
71
  apply(input) {
66
- return runWithExecutionCwd(cwd, async () => {
72
+ return runInClient(input, async () => {
67
73
  validateApplyInput(input);
68
74
  const { prNumber, targetRepository } = resolvePrReference(input.pr);
69
75
  const results = [];
@@ -71,16 +77,32 @@ export function createPrShepherd(options = {}) {
71
77
  const operation = input.operations[index];
72
78
  try {
73
79
  switch (operation.type) {
80
+ case "merge": {
81
+ const result = await runApplyMerge({
82
+ prNumber,
83
+ targetRepository,
84
+ requireSha: operation.requireSha,
85
+ mergeAction: operation.mergeAction,
86
+ mergeMethod: operation.mergeMethod,
87
+ expectedStack: operation.expectedStack,
88
+ });
89
+ results.push({ type: operation.type, result });
90
+ break;
91
+ }
74
92
  case "review_mutations": {
75
93
  const { type: _type, message, ...options } = operation;
76
94
  const result = await runResolveMutate({
77
95
  ...options,
96
+ // `HEAD` names this checkout's commit, the same form the printed CLI command uses.
97
+ requireSha: options.requireSha === "HEAD" ? await getLocalHeadSha() : options.requireSha,
78
98
  prNumber,
79
99
  targetRepository,
80
100
  dismissMessage: message,
81
101
  format: "json",
82
102
  });
83
103
  results.push({ type: operation.type, result });
104
+ if (result.sessionRefusal)
105
+ return { operations: results };
84
106
  break;
85
107
  }
86
108
  case "mark_files_viewed": {
@@ -130,7 +152,7 @@ export function createPrShepherd(options = {}) {
130
152
  buildSuggestionPatch(input) {
131
153
  validateSuggestionPatchInput(input);
132
154
  const { pr: _pr, ...options } = input;
133
- return runWithExecutionCwd(cwd, async () => {
155
+ return runInClient(input, async () => {
134
156
  const target = resolvePrReference(input.pr);
135
157
  return runCommitSuggestion({ ...options, ...target, format: "json" });
136
158
  });
@@ -138,7 +160,7 @@ export function createPrShepherd(options = {}) {
138
160
  buildSuggestionPatches(input) {
139
161
  validateSuggestionPatchesInput(input);
140
162
  const { pr: _pr, ...options } = input;
141
- return runWithExecutionCwd(cwd, async () => {
163
+ return runInClient(input, async () => {
142
164
  const target = resolvePrReference(input.pr);
143
165
  return runSuggestionPatches({ ...options, ...target, format: "json" });
144
166
  });
@@ -190,15 +212,29 @@ function validateApplyInput(input) {
190
212
  if (!input || !Array.isArray(input.operations) || input.operations.length === 0) {
191
213
  throw new PrShepherdValidationError("apply requires a non-empty operations array");
192
214
  }
193
- for (const operation of input.operations)
215
+ const parsed = validatePrReference(input.pr);
216
+ for (const operation of input.operations) {
194
217
  validateOperation(operation);
195
- validatePrReference(input.pr);
218
+ if (operation.type === "merge" &&
219
+ operation.expectedStack !== undefined &&
220
+ parsed.number !== undefined &&
221
+ operation.expectedStack.prefix.at(-1)?.pr !== parsed.number)
222
+ throw new PrShepherdValidationError("expectedStack prefix must end at the requested PR");
223
+ }
196
224
  }
197
225
  function validateOperation(operation) {
198
226
  if (!operation || typeof operation !== "object") {
199
227
  throw new PrShepherdValidationError("apply operation must be an object");
200
228
  }
201
229
  switch (operation.type) {
230
+ case "merge":
231
+ try {
232
+ validateApplyMergeOptions(operation);
233
+ }
234
+ catch (error) {
235
+ throw new PrShepherdValidationError(error instanceof Error ? error.message : String(error));
236
+ }
237
+ return;
202
238
  case "review_mutations":
203
239
  validateReviewMutations(operation);
204
240
  return;
@@ -254,8 +290,10 @@ function validateReviewMutations(operation) {
254
290
  if (operation.message !== undefined && typeof operation.message !== "string") {
255
291
  throw new PrShepherdValidationError("review_mutations.message must be a string");
256
292
  }
257
- if (operation.requireSha !== undefined && !/^[0-9a-f]{40}$/.test(operation.requireSha)) {
258
- throw new PrShepherdValidationError("review_mutations.requireSha must be a full 40-character lowercase hex SHA");
293
+ if (operation.requireSha !== undefined &&
294
+ operation.requireSha !== "HEAD" &&
295
+ !/^[0-9a-f]{40}$/.test(operation.requireSha)) {
296
+ throw new PrShepherdValidationError("review_mutations.requireSha must be HEAD or a full 40-character lowercase hex SHA");
259
297
  }
260
298
  }
261
299
  function validateMarkFilesViewed(operation) {
@@ -21,6 +21,7 @@ import type { CheckRun, ClassifiedCheck } from "../types.mts";
21
21
  */
22
22
  export declare function classifyChecks(checks: CheckRun[], opts?: {
23
23
  additionalRelevantEvents?: string[];
24
+ requiredContexts?: readonly string[];
24
25
  }): ClassifiedCheck[];
25
26
  export interface CiVerdict {
26
27
  /** True when all relevant (non-filtered, non-skipped, non-ignored) checks passed. */
@@ -30,7 +30,7 @@ export function classifyChecks(checks, opts = {}) {
30
30
  const isIgnored = buildMatcher(config.ignoreChecks ?? []);
31
31
  const isProtected = buildMatcher(config.actions.neverCancelRuns ?? []);
32
32
  const protectedRunIds = buildProtectedRunIds(checks, isProtected);
33
- const supersededIndices = buildSupersededIndices(checks);
33
+ const supersededIndices = buildSupersededIndices(checks, opts.requiredContexts);
34
34
  return checks.map((c, index) => {
35
35
  if (isIgnored(c.name) && !isProtectedCheck(c, protectedRunIds)) {
36
36
  return { ...c, category: "ignored" };
@@ -4,8 +4,12 @@ import type { CheckConclusion, ViewerAuthorization } from "../types.mts";
4
4
  * (`actions: write`, which rides with push access). The rerun runs against the base
5
5
  * repo where the run lives, so `repositoryPermission` — not `headRepositoryPermission`,
6
6
  * which covers fork push access — is the right field to gate on.
7
+ *
8
+ * REST exposes no viewer capability fields, so on a REST snapshot an unknown role does not deny
9
+ * the rerun: the recommended `gh run rerun` is attempted and GitHub's response decides. A role
10
+ * GitHub did report still gates the rerun.
7
11
  */
8
- export declare function canRerunWorkflows(auth: ViewerAuthorization | undefined): boolean;
12
+ export declare function canRerunWorkflows(auth: ViewerAuthorization | undefined, transport?: "rest"): boolean;
9
13
  /** Failing-check rows for formatter/instructions — excludes annotation-only carriers. */
10
14
  export declare function isFailingAgentCheck(check: {
11
15
  conclusion: CheckConclusion;
@@ -6,10 +6,16 @@ const RERUN_CAPABLE_PERMISSIONS = new Set(["WRITE", "MAINTAIN", "ADMIN"]);
6
6
  * (`actions: write`, which rides with push access). The rerun runs against the base
7
7
  * repo where the run lives, so `repositoryPermission` — not `headRepositoryPermission`,
8
8
  * which covers fork push access — is the right field to gate on.
9
+ *
10
+ * REST exposes no viewer capability fields, so on a REST snapshot an unknown role does not deny
11
+ * the rerun: the recommended `gh run rerun` is attempted and GitHub's response decides. A role
12
+ * GitHub did report still gates the rerun.
9
13
  */
10
- export function canRerunWorkflows(auth) {
14
+ export function canRerunWorkflows(auth, transport) {
11
15
  const permission = auth?.repositoryPermission;
12
- return permission != null && RERUN_CAPABLE_PERMISSIONS.has(permission);
16
+ if (permission === undefined)
17
+ return transport === "rest";
18
+ return permission !== null && RERUN_CAPABLE_PERMISSIONS.has(permission);
13
19
  }
14
20
  /** True for conclusions that belong under `## Failing checks` (not success/skipped/neutral). */
15
21
  function isFailingCheckConclusion(conclusion) {
@@ -3,6 +3,12 @@ import type { CheckRun } from "../types.mts";
3
3
  /**
4
4
  * A later-created run supersedes an older cancellation as before. GitHub can start
5
5
  * check jobs out of run-ID order, so a lower-ID run can also cover a cancellation
6
- * when its matching successful job actually started and completed later.
6
+ * when its matching successful job actually started and completed later. Cancelled
7
+ * wrappers can report completion before start; compare both observed boundaries
8
+ * without treating that reversed interval as a successful execution.
9
+ *
10
+ * The lower-ID form never covers a required context: GitHub's required gate reads the
11
+ * latest (highest-ID) run, so that cancellation still fails it (github/gh-stack#466).
12
+ * Neither does a newer run that lacks the context, for the same reason.
7
13
  */
8
- export declare function buildSupersededIndices(checks: CheckRun[]): Set<number>;
14
+ export declare function buildSupersededIndices(checks: CheckRun[], requiredContexts?: readonly string[]): Set<number>;
@@ -17,7 +17,7 @@ function numericRunId(check) {
17
17
  const id = Number(check.runId);
18
18
  return Number.isSafeInteger(id) ? id : undefined;
19
19
  }
20
- function validTimes(check) {
20
+ function validTimes(check, requireOrdered = true) {
21
21
  const { startedAtUnix: start, completedAtUnix: completion } = check;
22
22
  return (typeof start === "number" &&
23
23
  Number.isFinite(start) &&
@@ -25,14 +25,21 @@ function validTimes(check) {
25
25
  typeof completion === "number" &&
26
26
  Number.isFinite(completion) &&
27
27
  completion > 0 &&
28
- completion >= start);
28
+ (!requireOrdered || completion >= start));
29
29
  }
30
30
  /**
31
31
  * A later-created run supersedes an older cancellation as before. GitHub can start
32
32
  * check jobs out of run-ID order, so a lower-ID run can also cover a cancellation
33
- * when its matching successful job actually started and completed later.
33
+ * when its matching successful job actually started and completed later. Cancelled
34
+ * wrappers can report completion before start; compare both observed boundaries
35
+ * without treating that reversed interval as a successful execution.
36
+ *
37
+ * The lower-ID form never covers a required context: GitHub's required gate reads the
38
+ * latest (highest-ID) run, so that cancellation still fails it (github/gh-stack#466).
39
+ * Neither does a newer run that lacks the context, for the same reason.
34
40
  */
35
- export function buildSupersededIndices(checks) {
41
+ export function buildSupersededIndices(checks, requiredContexts = []) {
42
+ const required = new Set(requiredContexts.map((context) => context.trim()));
36
43
  const maxRunIdByGroup = new Map();
37
44
  const runIds = checks.map(numericRunId);
38
45
  checks.forEach((check, index) => {
@@ -52,14 +59,18 @@ export function buildSupersededIndices(checks) {
52
59
  const runId = runIds[index];
53
60
  if (group === undefined || runId === undefined)
54
61
  return;
62
+ const isRequired = required.has(cancelled.name.trim());
55
63
  if (maxRunIdByGroup.get(group) > runId) {
56
- superseded.add(index);
64
+ // A required context is covered only by a newer run of the same context.
65
+ if (!isRequired || hasNewerSameName(checks, runIds, cancelled, group, runId))
66
+ superseded.add(index);
57
67
  return;
58
68
  }
59
- if (cancelled.status !== "COMPLETED" ||
69
+ if (isRequired ||
70
+ cancelled.status !== "COMPLETED" ||
60
71
  !cancelled.workflowId ||
61
72
  cancelled.event === null ||
62
- !validTimes(cancelled))
73
+ !validTimes(cancelled, false))
63
74
  return;
64
75
  const covered = checks.some((success, candidateIndex) => {
65
76
  const candidateRunId = runIds[candidateIndex];
@@ -81,3 +92,9 @@ export function buildSupersededIndices(checks) {
81
92
  });
82
93
  return superseded;
83
94
  }
95
+ function hasNewerSameName(checks, runIds, cancelled, group, runId) {
96
+ return checks.some((check, index) => runIds[index] !== undefined &&
97
+ runIds[index] > runId &&
98
+ check.name.trim() === cancelled.name.trim() &&
99
+ groupKeyOf(check) === group);
100
+ }
@@ -6,5 +6,7 @@ export interface LoadedRule {
6
6
  }
7
7
  export declare function discoverRuleFiles(cwd: string): string[];
8
8
  export declare function loadRules(files: string[]): Promise<LoadedRule[]>;
9
+ /** Local `.pr-shepherd/classification/` rules followed by the enabled bundled rules. */
10
+ export declare function loadClassifyRules(cwd: string, standardRuleNames?: readonly string[]): Promise<LoadedRule[]>;
9
11
  /** Reset the rule cache — for use in tests. */
10
12
  export declare function _resetRuleCache(): void;
@@ -10,6 +10,7 @@ import { readdirSync, statSync } from "node:fs";
10
10
  import { join, dirname, extname, basename } from "node:path";
11
11
  import { homedir } from "node:os";
12
12
  import { pathToFileURL } from "node:url";
13
+ import { STANDARD_RULES } from "./standard/index.mjs";
13
14
  const CLASSIFICATION_DIR = ".pr-shepherd/classification";
14
15
  const VALID_EXTENSIONS = new Set([".ts", ".mts", ".mjs", ".js"]);
15
16
  export function discoverRuleFiles(cwd) {
@@ -60,6 +61,20 @@ export async function loadRules(files) {
60
61
  ruleCache.set(cacheKey, rules);
61
62
  return rules;
62
63
  }
64
+ /** Bundled rules enabled by `classify.standard`, named `standard:<rule>`. */
65
+ function standardRules(names) {
66
+ return names.flatMap((name) => {
67
+ const rule = STANDARD_RULES[name];
68
+ return rule === undefined
69
+ ? []
70
+ : [{ name: `standard:${name}`, file: `pr-shepherd:standard/${name}`, rule }];
71
+ });
72
+ }
73
+ /** Local `.pr-shepherd/classification/` rules followed by the enabled bundled rules. */
74
+ export async function loadClassifyRules(cwd, standardRuleNames = []) {
75
+ const local = await loadRules(discoverRuleFiles(cwd));
76
+ return [...local, ...standardRules(standardRuleNames)];
77
+ }
63
78
  /** Reset the rule cache — for use in tests. */
64
79
  export function _resetRuleCache() {
65
80
  ruleCache.clear();
@@ -0,0 +1,3 @@
1
+ import type { ClassifyItem, ClassifyItemKind } from "../types.mts";
2
+ /** True when GitHub reports a bot author whose normalized login is one of `logins`. */
3
+ export declare function isBotItem(item: ClassifyItem, logins: readonly string[], kinds: readonly ClassifyItemKind[]): boolean;
@@ -0,0 +1,10 @@
1
+ /** Lowercased login without the REST `[bot]` suffix, so GraphQL and REST logins compare equal. */
2
+ function normalizeLogin(login) {
3
+ return login.toLowerCase().replace(/\[bot\]$/, "");
4
+ }
5
+ /** True when GitHub reports a bot author whose normalized login is one of `logins`. */
6
+ export function isBotItem(item, logins, kinds) {
7
+ return (item.authorType === "Bot" &&
8
+ kinds.includes(item.kind) &&
9
+ logins.includes(normalizeLogin(item.author)));
10
+ }
@@ -0,0 +1,4 @@
1
+ import type { ClassifyRule } from "../types.mts";
2
+ /** Codecov's coverage report comment. Coverage gates still report through their status checks. */
3
+ declare const rule: ClassifyRule;
4
+ export default rule;