pr-shepherd 0.56.6 → 0.57.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (421) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +150 -280
  3. package/bin/api.d.mts +25 -3
  4. package/bin/api.mjs +51 -13
  5. package/bin/checks/classify.d.mts +1 -0
  6. package/bin/checks/classify.mjs +1 -1
  7. package/bin/checks/conclusions.d.mts +5 -1
  8. package/bin/checks/conclusions.mjs +8 -2
  9. package/bin/checks/superseded.d.mts +8 -2
  10. package/bin/checks/superseded.mjs +24 -7
  11. package/bin/classify/loader.d.mts +2 -0
  12. package/bin/classify/loader.mjs +15 -0
  13. package/bin/classify/standard/author.d.mts +3 -0
  14. package/bin/classify/standard/author.mjs +10 -0
  15. package/bin/classify/standard/codecov-report.d.mts +4 -0
  16. package/bin/classify/standard/codecov-report.mjs +10 -0
  17. package/bin/classify/standard/coderabbit-notices.d.mts +8 -0
  18. package/bin/classify/standard/coderabbit-notices.mjs +38 -0
  19. package/bin/classify/standard/codex-notices.d.mts +8 -0
  20. package/bin/classify/standard/codex-notices.mjs +34 -0
  21. package/bin/classify/standard/dependabot-notices.d.mts +4 -0
  22. package/bin/classify/standard/dependabot-notices.mjs +15 -0
  23. package/bin/classify/standard/gemini-notices.d.mts +4 -0
  24. package/bin/classify/standard/gemini-notices.mjs +13 -0
  25. package/bin/classify/standard/index.d.mts +7 -0
  26. package/bin/classify/standard/index.mjs +21 -0
  27. package/bin/classify/standard/kilo-no-issues.d.mts +4 -0
  28. package/bin/classify/standard/kilo-no-issues.mjs +17 -0
  29. package/bin/classify/standard/sourcery-notices.d.mts +8 -0
  30. package/bin/classify/standard/sourcery-notices.mjs +20 -0
  31. package/bin/cli/api-usage-formatter.mjs +6 -1
  32. package/bin/cli/apply-merge-handler.d.mts +3 -0
  33. package/bin/cli/apply-merge-handler.mjs +73 -0
  34. package/bin/cli/arg-flags.d.mts +2 -0
  35. package/bin/cli/arg-flags.mjs +47 -0
  36. package/bin/cli/args.mjs +10 -41
  37. package/bin/cli/clean-handler.d.mts +1 -0
  38. package/bin/cli/clean-handler.mjs +71 -0
  39. package/bin/cli/default-poll.mjs +3 -0
  40. package/bin/cli/event-state-scope.d.mts +19 -0
  41. package/bin/cli/event-state-scope.mjs +60 -0
  42. package/bin/cli/fix-formatter.mjs +5 -11
  43. package/bin/cli/handlers.d.mts +0 -1
  44. package/bin/cli/handlers.mjs +8 -64
  45. package/bin/cli/help-command-pages.d.mts +62 -18
  46. package/bin/cli/help-command-pages.mjs +59 -15
  47. package/bin/cli/help-iterate-poll-pages.d.mts +2 -2
  48. package/bin/cli/help-iterate-poll-pages.mjs +6 -0
  49. package/bin/cli/help-log-file-page.d.mts +1 -1
  50. package/bin/cli/help-log-file-page.mjs +4 -1
  51. package/bin/cli/help-top-page.d.mts +1 -1
  52. package/bin/cli/help-top-page.mjs +5 -0
  53. package/bin/cli/help.d.mts +63 -19
  54. package/bin/cli/help.mjs +2 -0
  55. package/bin/cli/iterate-branch-segment.d.mts +2 -0
  56. package/bin/cli/iterate-branch-segment.mjs +12 -7
  57. package/bin/cli/iterate-flags.d.mts +7 -1
  58. package/bin/cli/iterate-flags.mjs +38 -0
  59. package/bin/cli/iterate-formatter.d.mts +6 -6
  60. package/bin/cli/iterate-formatter.mjs +56 -22
  61. package/bin/cli/iterate-instructions.mjs +45 -15
  62. package/bin/cli/iterate-lean.mjs +39 -14
  63. package/bin/cli/iterate-merge-formatter.d.mts +6 -0
  64. package/bin/cli/iterate-merge-formatter.mjs +24 -14
  65. package/bin/cli/iterate-verbose.mjs +3 -6
  66. package/bin/cli/list-formatters.mjs +3 -1
  67. package/bin/cli/mutate-formatter.mjs +10 -5
  68. package/bin/cli/next-check-format.d.mts +7 -0
  69. package/bin/cli/next-check-format.mjs +10 -0
  70. package/bin/cli/playbook-handler.d.mts +5 -0
  71. package/bin/cli/playbook-handler.mjs +40 -0
  72. package/bin/cli/poll-handler.mjs +7 -6
  73. package/bin/cli/poll-summary-formatter.mjs +11 -2
  74. package/bin/cli/poll-targets.mjs +2 -0
  75. package/bin/cli/resolve-validators.mjs +2 -2
  76. package/bin/cli/runner.mjs +6 -1
  77. package/bin/cli/stack-layer-view.d.mts +33 -0
  78. package/bin/cli/stack-layer-view.mjs +1 -0
  79. package/bin/cli/stack-overview.d.mts +10 -28
  80. package/bin/cli/stack-overview.mjs +39 -14
  81. package/bin/cli/transport-args.d.mts +5 -0
  82. package/bin/cli/transport-args.mjs +23 -0
  83. package/bin/cli/transport-formatter.d.mts +9 -0
  84. package/bin/cli/transport-formatter.mjs +13 -0
  85. package/bin/cli-parser.mjs +62 -7
  86. package/bin/commands/apply-merge-options.d.mts +4 -0
  87. package/bin/commands/apply-merge-options.mjs +34 -0
  88. package/bin/commands/apply-merge-output.d.mts +7 -0
  89. package/bin/commands/apply-merge-output.mjs +20 -0
  90. package/bin/commands/apply-merge-pull.d.mts +20 -0
  91. package/bin/commands/apply-merge-pull.mjs +15 -0
  92. package/bin/commands/apply-merge.d.mts +18 -0
  93. package/bin/commands/apply-merge.mjs +126 -0
  94. package/bin/commands/apply-queue-removal.mjs +7 -0
  95. package/bin/commands/check-annotations.mjs +28 -18
  96. package/bin/commands/check-execution-context.d.mts +5 -0
  97. package/bin/commands/check-execution-context.mjs +16 -1
  98. package/bin/commands/check-fingerprint.d.mts +26 -2
  99. package/bin/commands/check-fingerprint.mjs +64 -28
  100. package/bin/commands/check-terminal-report.mjs +11 -1
  101. package/bin/commands/check-unreported.d.mts +6 -2
  102. package/bin/commands/check-unreported.mjs +32 -12
  103. package/bin/commands/check-wait-detectors.d.mts +35 -0
  104. package/bin/commands/check-wait-detectors.mjs +107 -0
  105. package/bin/commands/check.d.mts +4 -2
  106. package/bin/commands/check.mjs +130 -40
  107. package/bin/commands/event-instructions.d.mts +11 -0
  108. package/bin/commands/event-instructions.mjs +27 -0
  109. package/bin/commands/event-mode.d.mts +14 -0
  110. package/bin/commands/event-mode.mjs +138 -0
  111. package/bin/commands/iterate/api-usage.mjs +4 -2
  112. package/bin/commands/iterate/base.mjs +4 -0
  113. package/bin/commands/iterate/check-blocker-gate.mjs +14 -2
  114. package/bin/commands/iterate/check-instructions.d.mts +9 -10
  115. package/bin/commands/iterate/check-instructions.mjs +23 -22
  116. package/bin/commands/iterate/classify.mjs +19 -5
  117. package/bin/commands/iterate/escalate.mjs +13 -3
  118. package/bin/commands/iterate/fix-code.mjs +26 -9
  119. package/bin/commands/iterate/fix-instruction-threads.d.mts +0 -8
  120. package/bin/commands/iterate/fix-instruction-threads.mjs +0 -16
  121. package/bin/commands/iterate/fix-loop-instruction.d.mts +9 -0
  122. package/bin/commands/iterate/fix-loop-instruction.mjs +35 -0
  123. package/bin/commands/iterate/helpers.mjs +15 -2
  124. package/bin/commands/iterate/index.mjs +57 -16
  125. package/bin/commands/iterate/mark-ready.mjs +50 -10
  126. package/bin/commands/iterate/merge-plan.d.mts +19 -0
  127. package/bin/commands/iterate/merge-plan.mjs +88 -0
  128. package/bin/commands/iterate/merge-state.mjs +3 -0
  129. package/bin/commands/iterate/merge.d.mts +4 -15
  130. package/bin/commands/iterate/merge.mjs +18 -51
  131. package/bin/commands/iterate/merged-base-pull-requests.d.mts +2 -6
  132. package/bin/commands/iterate/merged-base-pull-requests.mjs +15 -11
  133. package/bin/commands/iterate/native-stack-rebase.mjs +11 -0
  134. package/bin/commands/iterate/render.d.mts +8 -2
  135. package/bin/commands/iterate/render.mjs +28 -42
  136. package/bin/commands/iterate/stack-trunk-conflict.mjs +17 -1
  137. package/bin/commands/iterate/stale-ancestry.mjs +4 -1
  138. package/bin/commands/iterate/stall.d.mts +5 -1
  139. package/bin/commands/iterate/stall.mjs +19 -7
  140. package/bin/commands/iterate/thread-mutation-routing.d.mts +1 -0
  141. package/bin/commands/iterate/thread-mutation-routing.mjs +4 -2
  142. package/bin/commands/iterate/unreported-required.mjs +5 -2
  143. package/bin/commands/mark-files-as-viewed.mjs +7 -0
  144. package/bin/commands/next-check.d.mts +31 -0
  145. package/bin/commands/next-check.mjs +46 -0
  146. package/bin/commands/playbook.d.mts +13 -0
  147. package/bin/commands/playbook.mjs +48 -0
  148. package/bin/commands/poll-mode.d.mts +8 -0
  149. package/bin/commands/poll-mode.mjs +14 -0
  150. package/bin/commands/poll-progress.d.mts +1 -0
  151. package/bin/commands/poll-progress.mjs +6 -3
  152. package/bin/commands/poll-quota.d.mts +3 -2
  153. package/bin/commands/poll-quota.mjs +7 -3
  154. package/bin/commands/poll-summary-explicit-instructions.d.mts +2 -0
  155. package/bin/commands/poll-summary-explicit-instructions.mjs +10 -1
  156. package/bin/commands/poll-summary-instructions.mjs +16 -17
  157. package/bin/commands/poll-summary.mjs +42 -17
  158. package/bin/commands/poll.d.mts +0 -1
  159. package/bin/commands/poll.mjs +11 -19
  160. package/bin/commands/quota-selection.d.mts +2 -1
  161. package/bin/commands/quota-selection.mjs +12 -7
  162. package/bin/commands/resolve-mutate.mjs +33 -5
  163. package/bin/commands/rest-stack-merge-readiness.d.mts +4 -0
  164. package/bin/commands/rest-stack-merge-readiness.mjs +74 -0
  165. package/bin/commands/rule-auto-resolve.mjs +9 -3
  166. package/bin/commands/shepherd-journal.d.mts +5 -4
  167. package/bin/commands/shepherd-journal.mjs +9 -8
  168. package/bin/commands/stack-drain.d.mts +1 -9
  169. package/bin/commands/stack-drain.mjs +50 -37
  170. package/bin/commands/stack-handoffs.d.mts +10 -0
  171. package/bin/commands/stack-handoffs.mjs +25 -0
  172. package/bin/commands/stack-stall.d.mts +5 -0
  173. package/bin/commands/stack-stall.mjs +11 -0
  174. package/bin/commands/stack-work.d.mts +5 -0
  175. package/bin/commands/stack-work.mjs +32 -0
  176. package/bin/comments/existing-reply-scan.d.mts +17 -0
  177. package/bin/comments/existing-reply-scan.mjs +41 -0
  178. package/bin/comments/rate-limit.mjs +2 -2
  179. package/bin/comments/resolve.d.mts +7 -1
  180. package/bin/comments/resolve.mjs +108 -18
  181. package/bin/comments/rest-reply.d.mts +9 -0
  182. package/bin/comments/rest-reply.mjs +95 -0
  183. package/bin/comments/rest-review-mutations.d.mts +22 -0
  184. package/bin/comments/rest-review-mutations.mjs +125 -0
  185. package/bin/comments/review-visibility.d.mts +1 -1
  186. package/bin/comments/review-visibility.mjs +3 -2
  187. package/bin/comments/sha-poll.mjs +7 -3
  188. package/bin/comments/thread-visibility.mjs +10 -5
  189. package/bin/comments/uncertain-replies.d.mts +11 -0
  190. package/bin/comments/uncertain-replies.mjs +121 -0
  191. package/bin/comments/uncertain-reply-guards.d.mts +10 -0
  192. package/bin/comments/uncertain-reply-guards.mjs +94 -0
  193. package/bin/comments/visible-comments.d.mts +1 -1
  194. package/bin/comments/visible-comments.mjs +4 -2
  195. package/bin/config/load.d.mts +37 -1
  196. package/bin/config/load.mjs +73 -1
  197. package/bin/config.json +6 -2
  198. package/bin/exit-codes.d.mts +3 -0
  199. package/bin/exit-codes.mjs +8 -0
  200. package/bin/github/api-telemetry-aggregate.d.mts +15 -4
  201. package/bin/github/api-telemetry-aggregate.mjs +57 -18
  202. package/bin/github/api-telemetry.d.mts +4 -0
  203. package/bin/github/api-telemetry.mjs +22 -8
  204. package/bin/github/apply-review-preflight.d.mts +20 -0
  205. package/bin/github/apply-review-preflight.mjs +61 -0
  206. package/bin/github/base-behind-cache.d.mts +15 -0
  207. package/bin/github/base-behind-cache.mjs +18 -0
  208. package/bin/github/batch-parse-suites.d.mts +4 -2
  209. package/bin/github/batch-parse-suites.mjs +6 -1
  210. package/bin/github/batch-parser-helpers.mjs +40 -2
  211. package/bin/github/batch-parsers.mjs +1 -0
  212. package/bin/github/batch-raw-rules.d.mts +26 -0
  213. package/bin/github/batch-raw-types.d.mts +8 -6
  214. package/bin/github/batch-stack-evidence.d.mts +28 -0
  215. package/bin/github/batch-stack-evidence.mjs +62 -0
  216. package/bin/github/batch.d.mts +15 -2
  217. package/bin/github/batch.mjs +34 -17
  218. package/bin/github/captured-proxy-agent.d.mts +3 -0
  219. package/bin/github/captured-proxy-agent.mjs +8 -0
  220. package/bin/github/check-annotations-batch.d.mts +0 -5
  221. package/bin/github/check-annotations-batch.mjs +25 -4
  222. package/bin/github/check-annotations.mjs +3 -1
  223. package/bin/github/client.d.mts +8 -19
  224. package/bin/github/client.mjs +45 -13
  225. package/bin/github/fingerprint-fields.d.mts +8 -0
  226. package/bin/github/fingerprint-fields.mjs +6 -1
  227. package/bin/github/fingerprint.d.mts +4 -2
  228. package/bin/github/fingerprint.mjs +6 -38
  229. package/bin/github/github-fetch.d.mts +2 -0
  230. package/bin/github/github-fetch.mjs +32 -0
  231. package/bin/github/gql/base-behind.gql +3 -0
  232. package/bin/github/gql/batch-pr-page.gql +16 -0
  233. package/bin/github/gql/batch-pr.gql +57 -0
  234. package/bin/github/gql/commit-check-contexts.gql +17 -0
  235. package/bin/github/gql/get-pr-body.gql +1 -0
  236. package/bin/github/gql/poll-stack-summary.gql +1 -0
  237. package/bin/github/gql/poll-summary-check-contexts.gql +5 -0
  238. package/bin/github/gql/poll-summary-fragment.gql +65 -15
  239. package/bin/github/gql/stack-handoff-check-runs.gql +42 -0
  240. package/bin/github/graphql-http.mjs +9 -4
  241. package/bin/github/graphql-internal-retry.d.mts +2 -0
  242. package/bin/github/graphql-internal-retry.mjs +1 -1
  243. package/bin/github/graphql-quota-probe.d.mts +6 -0
  244. package/bin/github/graphql-quota-probe.mjs +56 -0
  245. package/bin/github/graphql-session-refusal.d.mts +8 -0
  246. package/bin/github/graphql-session-refusal.mjs +14 -0
  247. package/bin/github/http-request.d.mts +1 -1
  248. package/bin/github/http-request.mjs +22 -6
  249. package/bin/github/http-utils.mjs +6 -0
  250. package/bin/github/legacy-dispatcher.d.mts +6 -0
  251. package/bin/github/legacy-dispatcher.mjs +44 -0
  252. package/bin/github/merge-target-rules.d.mts +12 -3
  253. package/bin/github/merge-target-rules.mjs +71 -34
  254. package/bin/github/mutation-policy.d.mts +6 -0
  255. package/bin/github/mutation-policy.mjs +21 -0
  256. package/bin/github/poll-summary-annotation-probe.mjs +17 -10
  257. package/bin/github/poll-summary-check-blockers.d.mts +1 -1
  258. package/bin/github/poll-summary-check-blockers.mjs +2 -2
  259. package/bin/github/poll-summary-check-hydration.mjs +25 -10
  260. package/bin/github/poll-summary-checks.d.mts +2 -2
  261. package/bin/github/poll-summary-checks.mjs +9 -6
  262. package/bin/github/poll-summary-command.d.mts +2 -0
  263. package/bin/github/poll-summary-command.mjs +24 -0
  264. package/bin/github/poll-summary-fingerprint.d.mts +1 -1
  265. package/bin/github/poll-summary-fingerprint.mjs +4 -2
  266. package/bin/github/poll-summary-handoff-fields.d.mts +3 -0
  267. package/bin/github/poll-summary-handoff-fields.mjs +73 -0
  268. package/bin/github/poll-summary-projector.d.mts +1 -1
  269. package/bin/github/poll-summary-projector.mjs +17 -24
  270. package/bin/github/poll-summary-raw.d.mts +70 -10
  271. package/bin/github/poll-summary-readiness.d.mts +6 -1
  272. package/bin/github/poll-summary-readiness.mjs +13 -4
  273. package/bin/github/poll-summary-rest-queue.d.mts +3 -0
  274. package/bin/github/poll-summary-rest-queue.mjs +12 -0
  275. package/bin/github/poll-summary-review.d.mts +1 -1
  276. package/bin/github/poll-summary-review.mjs +35 -20
  277. package/bin/github/poll-summary-route.mjs +19 -3
  278. package/bin/github/poll-summary-stack-page.d.mts +11 -0
  279. package/bin/github/poll-summary-stack-page.mjs +27 -0
  280. package/bin/github/poll-summary-unreported.d.mts +3 -1
  281. package/bin/github/poll-summary-unreported.mjs +6 -3
  282. package/bin/github/poll-summary.d.mts +4 -1
  283. package/bin/github/poll-summary.mjs +47 -33
  284. package/bin/github/queries.d.mts +7 -3
  285. package/bin/github/queries.mjs +8 -4
  286. package/bin/github/rate-limit-sample.d.mts +7 -0
  287. package/bin/github/rate-limit-sample.mjs +12 -0
  288. package/bin/github/reply-recovery-read.d.mts +50 -0
  289. package/bin/github/reply-recovery-read.mjs +118 -0
  290. package/bin/github/reply-thread-transcripts.d.mts +0 -1
  291. package/bin/github/reply-thread-transcripts.mjs +24 -1
  292. package/bin/github/rest-annotation-read.d.mts +5 -0
  293. package/bin/github/rest-annotation-read.mjs +44 -0
  294. package/bin/github/rest-batch-read.d.mts +12 -0
  295. package/bin/github/rest-batch-read.mjs +57 -0
  296. package/bin/github/rest-batch-snapshot.d.mts +30 -0
  297. package/bin/github/rest-batch-snapshot.mjs +115 -0
  298. package/bin/github/rest-batch-unavailable.d.mts +5 -0
  299. package/bin/github/rest-batch-unavailable.mjs +16 -0
  300. package/bin/github/rest-check-read.d.mts +34 -0
  301. package/bin/github/rest-check-read.mjs +124 -0
  302. package/bin/github/rest-comment-authorship.d.mts +7 -0
  303. package/bin/github/rest-comment-authorship.mjs +11 -0
  304. package/bin/github/rest-conditional-scope.d.mts +20 -0
  305. package/bin/github/rest-conditional-scope.mjs +44 -0
  306. package/bin/github/rest-explicit-summary-read.d.mts +3 -0
  307. package/bin/github/rest-explicit-summary-read.mjs +8 -0
  308. package/bin/github/rest-feedback-read.d.mts +36 -0
  309. package/bin/github/rest-feedback-read.mjs +147 -0
  310. package/bin/github/rest-feedback-validation.d.mts +5 -0
  311. package/bin/github/rest-feedback-validation.mjs +28 -0
  312. package/bin/github/rest-http.d.mts +12 -0
  313. package/bin/github/rest-http.mjs +108 -23
  314. package/bin/github/rest-identities.d.mts +22 -0
  315. package/bin/github/rest-identities.mjs +134 -0
  316. package/bin/github/rest-merge-stack-guard.d.mts +14 -0
  317. package/bin/github/rest-merge-stack-guard.mjs +41 -0
  318. package/bin/github/rest-merge.d.mts +29 -0
  319. package/bin/github/rest-merge.mjs +56 -0
  320. package/bin/github/rest-merged-base-read.d.mts +9 -0
  321. package/bin/github/rest-merged-base-read.mjs +48 -0
  322. package/bin/github/rest-pr-core.d.mts +52 -0
  323. package/bin/github/rest-pr-core.mjs +60 -0
  324. package/bin/github/rest-reader-core.d.mts +16 -0
  325. package/bin/github/rest-reader-core.mjs +154 -0
  326. package/bin/github/rest-review-read.d.mts +11 -0
  327. package/bin/github/rest-review-read.mjs +47 -0
  328. package/bin/github/rest-rules-read.d.mts +9 -0
  329. package/bin/github/rest-rules-read.mjs +154 -0
  330. package/bin/github/rest-session-refusal.d.mts +5 -0
  331. package/bin/github/rest-session-refusal.mjs +21 -0
  332. package/bin/github/rest-snapshot-context.d.mts +10 -0
  333. package/bin/github/rest-snapshot-context.mjs +1 -0
  334. package/bin/github/rest-stack-read.d.mts +21 -0
  335. package/bin/github/rest-stack-read.mjs +47 -0
  336. package/bin/github/rest-stack-summary.d.mts +8 -0
  337. package/bin/github/rest-stack-summary.mjs +62 -0
  338. package/bin/github/rest-summary-read.d.mts +7 -0
  339. package/bin/github/rest-summary-read.mjs +112 -0
  340. package/bin/github/rest-terminal-batch.d.mts +14 -0
  341. package/bin/github/rest-terminal-batch.mjs +45 -0
  342. package/bin/github/rest-text.mjs +7 -3
  343. package/bin/github/rest-thread-read.d.mts +4 -0
  344. package/bin/github/rest-thread-read.mjs +35 -0
  345. package/bin/github/rest-viewer-read.d.mts +2 -0
  346. package/bin/github/rest-viewer-read.mjs +41 -0
  347. package/bin/github/stack-bottom.d.mts +3 -0
  348. package/bin/github/stack-bottom.mjs +14 -0
  349. package/bin/github/stack-handoff-context.d.mts +25 -0
  350. package/bin/github/stack-handoff-context.mjs +12 -0
  351. package/bin/github/stack-handoff-page.d.mts +17 -0
  352. package/bin/github/stack-handoff-page.mjs +306 -0
  353. package/bin/github/stack-handoff.d.mts +16 -0
  354. package/bin/github/stack-handoff.mjs +115 -0
  355. package/bin/github/stack-order.d.mts +6 -0
  356. package/bin/github/stack-order.mjs +13 -0
  357. package/bin/github/stack-read.d.mts +7 -5
  358. package/bin/github/stack-read.mjs +22 -24
  359. package/bin/github/stack-size-hint.d.mts +3 -0
  360. package/bin/github/stack-size-hint.mjs +14 -0
  361. package/bin/github/suggestion-thread.mjs +27 -1
  362. package/bin/github/thread-comments.mjs +9 -1
  363. package/bin/github/transport-evidence.d.mts +20 -0
  364. package/bin/github/transport-evidence.mjs +26 -0
  365. package/bin/github/transport-mode.d.mts +2 -0
  366. package/bin/github/transport-mode.mjs +5 -0
  367. package/bin/github/transport.d.mts +19 -0
  368. package/bin/github/transport.mjs +126 -0
  369. package/bin/github/unsupported-rest.d.mts +4 -0
  370. package/bin/github/unsupported-rest.mjs +7 -0
  371. package/bin/mcp/server.mjs +52 -3
  372. package/bin/merge-status/derive.d.mts +2 -0
  373. package/bin/merge-status/derive.mjs +17 -13
  374. package/bin/merge-status/required-check-requirements.d.mts +7 -0
  375. package/bin/merge-status/required-check-requirements.mjs +15 -0
  376. package/bin/merge-status/requirements-format.d.mts +5 -0
  377. package/bin/merge-status/requirements-format.mjs +41 -12
  378. package/bin/merge-status/requirements.mjs +18 -7
  379. package/bin/state/base.mjs +41 -0
  380. package/bin/state/durable-state.d.mts +6 -0
  381. package/bin/state/durable-state.mjs +41 -0
  382. package/bin/state/graphql-quota-policy.mjs +23 -16
  383. package/bin/state/merge-request.d.mts +25 -0
  384. package/bin/state/merge-request.mjs +145 -0
  385. package/bin/state/pr-fingerprint.d.mts +15 -0
  386. package/bin/state/pr-fingerprint.mjs +22 -7
  387. package/bin/state/rest-cache.d.mts +3 -0
  388. package/bin/state/rest-cache.mjs +1 -0
  389. package/bin/state/rest-snapshot-report.d.mts +21 -0
  390. package/bin/state/rest-snapshot-report.mjs +52 -0
  391. package/bin/state/seen-comments.d.mts +3 -0
  392. package/bin/state/seen-comments.mjs +18 -0
  393. package/bin/types/api-usage.d.mts +9 -1
  394. package/bin/types/escalate.d.mts +1 -1
  395. package/bin/types/github.d.mts +23 -9
  396. package/bin/types/iterate.d.mts +24 -2
  397. package/bin/types/merge-action.d.mts +1 -1
  398. package/bin/types/merge-requirements.d.mts +7 -7
  399. package/bin/types/next-check.d.mts +12 -0
  400. package/bin/types/next-check.mjs +1 -0
  401. package/bin/types/poll-summary.d.mts +21 -0
  402. package/bin/types/report.d.mts +13 -1
  403. package/bin/types/review-thread.d.mts +1 -1
  404. package/bin/types.d.mts +1 -0
  405. package/bin/types.mjs +1 -0
  406. package/package.json +4 -3
  407. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  408. package/plugins/pr-shepherd/.codex.mcp.json +1 -1
  409. package/plugins/pr-shepherd/.mcp.json +1 -1
  410. package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +1 -1
  411. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +14 -51
  412. package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +12 -16
  413. package/plugins/pr-shepherd/skills/pr-shepherd/references/cloud-event-loop.md +14 -0
  414. package/plugins/pr-shepherd/skills/pr-shepherd/references/create-pr.md +10 -0
  415. package/plugins/pr-shepherd/skills/pr-shepherd/references/fix-code-loop.md +10 -0
  416. package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +9 -0
  417. package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +3 -1
  418. package/bin/github/gql/pr-fingerprint.gql +0 -75
  419. package/plugins/pr-shepherd/skills/pr-shepherd/references/journal.md +0 -7
  420. package/plugins/pr-shepherd/skills/pr-shepherd/references/review-mutations.md +0 -9
  421. package/plugins/pr-shepherd/skills/pr-shepherd/references/stack-merge.md +0 -7
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;
@@ -0,0 +1,10 @@
1
+ import { isBotItem } from "./author.mjs";
2
+ /** Codecov's coverage report comment. Coverage gates still report through their status checks. */
3
+ const rule = (item) => {
4
+ if (!isBotItem(item, ["codecov"], ["pr-comment"]))
5
+ return null;
6
+ if (!/^#{1,6}\s*\[?Codecov\b/i.test(item.body.trimStart()))
7
+ return null;
8
+ return { autoResolve: true, suppress: true, reason: "Codecov coverage report" };
9
+ };
10
+ export default rule;
@@ -0,0 +1,8 @@
1
+ import type { ClassifyRule } from "../types.mts";
2
+ /**
3
+ * CodeRabbit progress, rate-limit, review-skipped and draft notices, plus its "Action not
4
+ * completed" command replies. The summarize comment is later edited in place into the PR
5
+ * walkthrough, so the rule only hides it from agent output and never minimizes it.
6
+ */
7
+ declare const rule: ClassifyRule;
8
+ export default rule;
@@ -0,0 +1,38 @@
1
+ import { isBotItem } from "./author.mjs";
2
+ const NOTICE_ENVELOPE = /^<!-- This is an auto-generated comment: (rate limited|skip review|review in progress) by coderabbit.ai -->\n([\s\S]*?)\n<!-- end of auto-generated comment: \1 by coderabbit.ai -->$/;
3
+ const COMMAND_REPLY = /^<!-- This is an auto-generated reply by CodeRabbit -->\n<!-- CodeRabbit review command invocation: v2:[a-f0-9]+ -->\n<details>\n<summary>⚠️ Action not completed<\/summary>\n\n(?:Review rate limited\.|Review skipped: \d+ files exceed the limit of \d+\.)\n\n(?:> Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits\. This command is applicable only when automatic reviews are paused\.|---\n\nYour included review limit is currently reached under our \[Fair Usage Limits Policy\]\(https:\/\/docs.coderabbit.ai\/management\/plans#fair-usage-limits-policy\)\. This review may still proceed through usage-based billing if eligible\. Your next included review will be available in \d+ minutes?\.)\n\n<\/details>$/;
4
+ /** Strips CodeRabbit's footer and header blocks so only the notice envelope remains. */
5
+ function stripChrome(body) {
6
+ return body
7
+ .trim()
8
+ .replace(/\n<!-- tips_start -->[\s\S]*?<!-- tips_end -->$/, "")
9
+ .trim()
10
+ .replace(/\n<!-- autopilot:start -->[\s\S]*?<!-- autopilot:end -->$/, "")
11
+ .trim()
12
+ .replace(/^<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\s*/, "")
13
+ .replace(/^<!-- review_stack_entry_start -->[\s\S]*?<!-- review_stack_entry_end -->\s*/, "");
14
+ }
15
+ function isQuotedNotice(quoted) {
16
+ const onlyQuotedLines = quoted.split("\n").every((line) => !line.trim() || line.startsWith(">"));
17
+ if (!onlyQuotedLines)
18
+ return false;
19
+ return (quoted.includes("> ## Review limit reached\n") ||
20
+ quoted.includes("> ## Draft PR not reviewed\n") ||
21
+ quoted.includes("> ## Review skipped\n") ||
22
+ quoted.includes("> Currently processing new changes in this PR. This may take a few minutes, please wait..."));
23
+ }
24
+ /**
25
+ * CodeRabbit progress, rate-limit, review-skipped and draft notices, plus its "Action not
26
+ * completed" command replies. The summarize comment is later edited in place into the PR
27
+ * walkthrough, so the rule only hides it from agent output and never minimizes it.
28
+ */
29
+ const rule = (item) => {
30
+ if (!isBotItem(item, ["coderabbitai"], ["pr-comment"]))
31
+ return null;
32
+ const body = stripChrome(item.body);
33
+ const quoted = body.match(NOTICE_ENVELOPE)?.[2]?.trim();
34
+ if (!(quoted && isQuotedNotice(quoted)) && !COMMAND_REPLY.test(body))
35
+ return null;
36
+ return { suppress: true, reason: "CodeRabbit progress, review-limit, or skipped-review notice" };
37
+ };
38
+ export default rule;
@@ -0,0 +1,8 @@
1
+ import type { ClassifyRule } from "../types.mts";
2
+ /**
3
+ * Codex quota notices, the review-activity status table, and no-findings notices on conversation
4
+ * comments. Each pattern matches the whole comment, so a notice with any appended finding stays
5
+ * visible. Review summaries and inline comments are left for the normal review path.
6
+ */
7
+ declare const rule: ClassifyRule;
8
+ export default rule;
@@ -0,0 +1,34 @@
1
+ import { isBotItem } from "./author.mjs";
2
+ const QUOTA_NOTICES = [
3
+ /^You have reached your Codex usage limits for security reviews\. Please try again later\.$/,
4
+ /^You have reached your Codex usage limits for code reviews\. You can see your limits in the \[Codex usage dashboard\]\(https:\/\/chatgpt.com\/codex\/cloud\/settings\/usage\)\.(?:\nTo continue using code reviews, add credits to your account and enable them for code reviews in your \[settings\]\(https:\/\/chatgpt.com\/codex\/cloud\/settings\/code-review\)\.)?$/,
5
+ /^Codex usage limits have been reached for code reviews\. Please check with the admins of this repo to increase the limits by adding credits\.\nRepo admins can enable using credits for code reviews in their \[settings\]\(https:\/\/chatgpt.com\/codex\/cloud\/settings\/code-review\)\.$/,
6
+ /^Codex usage limits have been reached\. Please try again later\.$/,
7
+ ];
8
+ const ABOUT_BLOCK = /\n\n<details> <summary>ℹ️ About Codex (?:in GitHub|security reviews in GitHub)<\/summary>[\s\S]*?<\/details>$/;
9
+ const ACTIVITY_SUMMARY = /^<!-- codex-pull-request-review-summary -->\n(?:<!-- codex-security-review:v1 \{[^\n]*\} -->\n)?\n?## Codex Review Summary\n\nThis comment shows the latest Codex review activity on this pull request\.\n\n\| Review \| Status \| Commit \| Review trigger \|\n\| --- \| --- \| --- \| --- \|\n(?:\|[^\n]+\|\n?)+$/;
10
+ const CLEAN_REVIEW = /^Codex Review: Didn't find any major issues\. (?:Bravo\.|Breezy!|Hooray!|:\+1:)\n\n\*\*Reviewed commit:\*\* `[a-f0-9]+`$/;
11
+ const CLEAN_SECURITY = /^### 🛡️ Codex Security Review(?: · _Automatically triggered_)?\n\nSecurity review completed\. No security issues were found in this pull request\.\n\n\*\*Reviewed commit:\*\* `[a-f0-9]+`\n\n\[View security finding report\]\(https:\/\/chatgpt.com\/codex\/cloud\/tasks\/[a-zA-Z0-9_]+\)\n\n_Only the user who started this review can view the report in Codex\._$/;
12
+ /**
13
+ * Codex quota notices, the review-activity status table, and no-findings notices on conversation
14
+ * comments. Each pattern matches the whole comment, so a notice with any appended finding stays
15
+ * visible. Review summaries and inline comments are left for the normal review path.
16
+ */
17
+ const rule = (item) => {
18
+ if (!isBotItem(item, ["chatgpt-codex-connector"], ["pr-comment"]))
19
+ return null;
20
+ const body = item.body.trim();
21
+ if (QUOTA_NOTICES.some((notice) => notice.test(body))) {
22
+ return { autoResolve: true, suppress: true, reason: "Codex usage-limit notice" };
23
+ }
24
+ // Strip only the trailing About block; extra findings after it stay visible.
25
+ const content = body.replace(ABOUT_BLOCK, "").trim();
26
+ if (ACTIVITY_SUMMARY.test(content)) {
27
+ return { autoResolve: true, suppress: true, reason: "Codex review-activity table" };
28
+ }
29
+ if (CLEAN_REVIEW.test(content) || CLEAN_SECURITY.test(content)) {
30
+ return { autoResolve: true, suppress: true, reason: "Codex no-findings notice" };
31
+ }
32
+ return null;
33
+ };
34
+ export default rule;
@@ -0,0 +1,4 @@
1
+ import type { ClassifyRule } from "../types.mts";
2
+ /** Dependabot's replies that report no change to make. */
3
+ declare const rule: ClassifyRule;
4
+ export default rule;
@@ -0,0 +1,15 @@
1
+ import { isBotItem } from "./author.mjs";
2
+ const NOTICES = [
3
+ /^Looks like this PR is already up-to-date with [^\s!]+! If you'd still like to recreate it from scratch, overwriting any edits, you can request `@dependabot recreate`\.$/,
4
+ /^Looks like these dependencies are updatable in another way, so this is no longer needed\.$/,
5
+ ];
6
+ /** Dependabot's replies that report no change to make. */
7
+ const rule = (item) => {
8
+ if (!isBotItem(item, ["dependabot"], ["pr-comment"]))
9
+ return null;
10
+ const body = item.body.trim();
11
+ if (!NOTICES.some((notice) => notice.test(body)))
12
+ return null;
13
+ return { autoResolve: true, suppress: true, reason: "Dependabot no-op reply" };
14
+ };
15
+ export default rule;
@@ -0,0 +1,4 @@
1
+ import type { ClassifyRule } from "../types.mts";
2
+ /** Gemini Code Assist daily-quota and review-limit notices on conversation comments. A heading means a real review. */
3
+ declare const rule: ClassifyRule;
4
+ export default rule;
@@ -0,0 +1,13 @@
1
+ import { isBotItem } from "./author.mjs";
2
+ /** Gemini Code Assist daily-quota and review-limit notices on conversation comments. A heading means a real review. */
3
+ const rule = (item) => {
4
+ if (!isBotItem(item, ["gemini-code-assist"], ["pr-comment"]))
5
+ return null;
6
+ if (/^#{1,6}\s/m.test(item.body))
7
+ return null;
8
+ if (!/You have reached your daily quota limit|Review limit reached/i.test(item.body)) {
9
+ return null;
10
+ }
11
+ return { autoResolve: true, suppress: true, reason: "Gemini quota or review-limit notice" };
12
+ };
13
+ export default rule;
@@ -0,0 +1,7 @@
1
+ import type { ClassifyRule } from "../types.mts";
2
+ /**
3
+ * Bundled classification rules that `classify.standard` can enable by name.
4
+ * Each rule matches conversation comments (`pr-comment`) only.
5
+ */
6
+ export declare const STANDARD_RULES: Readonly<Record<string, ClassifyRule>>;
7
+ export declare const STANDARD_RULE_NAMES: readonly string[];
@@ -0,0 +1,21 @@
1
+ import coderabbitNotices from "./coderabbit-notices.mjs";
2
+ import codecovReport from "./codecov-report.mjs";
3
+ import codexNotices from "./codex-notices.mjs";
4
+ import dependabotNotices from "./dependabot-notices.mjs";
5
+ import geminiNotices from "./gemini-notices.mjs";
6
+ import kiloNoIssues from "./kilo-no-issues.mjs";
7
+ import sourceryNotices from "./sourcery-notices.mjs";
8
+ /**
9
+ * Bundled classification rules that `classify.standard` can enable by name.
10
+ * Each rule matches conversation comments (`pr-comment`) only.
11
+ */
12
+ export const STANDARD_RULES = {
13
+ "coderabbit-notices": coderabbitNotices,
14
+ "codecov-report": codecovReport,
15
+ "codex-notices": codexNotices,
16
+ "dependabot-notices": dependabotNotices,
17
+ "gemini-notices": geminiNotices,
18
+ "kilo-no-issues": kiloNoIssues,
19
+ "sourcery-notices": sourceryNotices,
20
+ };
21
+ export const STANDARD_RULE_NAMES = Object.keys(STANDARD_RULES);
@@ -0,0 +1,4 @@
1
+ import type { ClassifyRule } from "../types.mts";
2
+ /** Kilo Code conversation comment whose status line reads "No Issues Found" and that carries no findings. */
3
+ declare const rule: ClassifyRule;
4
+ export default rule;