pr-shepherd 0.60.0 → 1.0.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 (208) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +7 -5
  3. package/bin/api.d.mts +35 -24
  4. package/bin/api.mjs +68 -48
  5. package/bin/checks/classify.d.mts +1 -1
  6. package/bin/checks/classify.mjs +4 -4
  7. package/bin/checks/log-excerpt.mjs +1 -1
  8. package/bin/classify/standard/codecov-report.d.mts +1 -1
  9. package/bin/classify/standard/codecov-report.mjs +7 -2
  10. package/bin/classify/standard/coderabbit-notices.d.mts +4 -3
  11. package/bin/classify/standard/coderabbit-notices.mjs +29 -47
  12. package/bin/classify/standard/codex-notices.mjs +34 -7
  13. package/bin/classify/standard/dependabot-notices.mjs +5 -1
  14. package/bin/classify/standard/gemini-notices.d.mts +1 -1
  15. package/bin/classify/standard/gemini-notices.mjs +16 -5
  16. package/bin/classify/standard/kilo-no-issues.d.mts +1 -1
  17. package/bin/classify/standard/kilo-no-issues.mjs +37 -10
  18. package/bin/classify/standard/sourcery-notices.mjs +1 -1
  19. package/bin/cli/arg-flags.mjs +0 -3
  20. package/bin/cli/clean-handler.d.mts +1 -1
  21. package/bin/cli/clean-handler.mjs +5 -15
  22. package/bin/cli/default-poll.d.mts +2 -0
  23. package/bin/cli/default-poll.mjs +9 -6
  24. package/bin/cli/formatters.d.mts +2 -3
  25. package/bin/cli/formatters.mjs +0 -26
  26. package/bin/cli/handlers.d.mts +3 -3
  27. package/bin/cli/handlers.mjs +45 -35
  28. package/bin/cli/help-command-pages.d.mts +13 -158
  29. package/bin/cli/help-command-pages.mjs +12 -157
  30. package/bin/cli/help-iterate-poll-pages.d.mts +4 -3
  31. package/bin/cli/help-iterate-poll-pages.mjs +76 -36
  32. package/bin/cli/help-log-file-page.d.mts +1 -1
  33. package/bin/cli/help-log-file-page.mjs +3 -5
  34. package/bin/cli/help-top-page.d.mts +1 -1
  35. package/bin/cli/help-top-page.mjs +19 -20
  36. package/bin/cli/help.d.mts +14 -159
  37. package/bin/cli/iterate-checks-formatter.mjs +1 -1
  38. package/bin/cli/iterate-flags.d.mts +2 -4
  39. package/bin/cli/iterate-flags.mjs +4 -20
  40. package/bin/cli/iterate-formatter.d.mts +1 -1
  41. package/bin/cli/iterate-formatter.mjs +6 -6
  42. package/bin/cli/iterate-instructions.d.mts +1 -1
  43. package/bin/cli/iterate-instructions.mjs +5 -6
  44. package/bin/cli/iterate-lean.mjs +32 -40
  45. package/bin/cli/iterate-verbose.mjs +1 -1
  46. package/bin/cli/journal-formatter.mjs +0 -3
  47. package/bin/cli/journal-handler.d.mts +1 -1
  48. package/bin/cli/journal-handler.mjs +2 -1
  49. package/bin/cli/mutate-formatter.mjs +0 -9
  50. package/bin/cli/next-check-format.d.mts +0 -4
  51. package/bin/cli/next-check-format.mjs +0 -4
  52. package/bin/cli/poll-handler.d.mts +3 -0
  53. package/bin/cli/poll-handler.mjs +88 -47
  54. package/bin/cli/poll-summary-emitter.mjs +2 -2
  55. package/bin/cli/poll-summary-formatter.mjs +2 -2
  56. package/bin/cli/poll-targets.d.mts +2 -2
  57. package/bin/cli/poll-targets.mjs +15 -35
  58. package/bin/cli/removed-flags.d.mts +3 -0
  59. package/bin/cli/removed-flags.mjs +24 -0
  60. package/bin/cli/runner.d.mts +1 -1
  61. package/bin/cli/runner.mjs +2 -2
  62. package/bin/cli/stack-overview.d.mts +0 -1
  63. package/bin/cli/stack-overview.mjs +1 -2
  64. package/bin/cli/{fix-formatter-extra.d.mts → triage-formatter-extra.d.mts} +1 -2
  65. package/bin/cli/{fix-formatter-extra.mjs → triage-formatter-extra.mjs} +0 -6
  66. package/bin/cli/triage-formatter.d.mts +4 -0
  67. package/bin/cli/{fix-formatter.mjs → triage-formatter.mjs} +31 -42
  68. package/bin/cli/unknown-flags.d.mts +6 -0
  69. package/bin/cli/unknown-flags.mjs +29 -0
  70. package/bin/cli-parser.mjs +41 -62
  71. package/bin/commands/check-annotations.d.mts +1 -1
  72. package/bin/commands/check-annotations.mjs +1 -1
  73. package/bin/commands/check-review-budget.d.mts +1 -1
  74. package/bin/commands/check-review-budget.mjs +1 -1
  75. package/bin/commands/check-wait-detectors.d.mts +1 -1
  76. package/bin/commands/check-wait-detectors.mjs +1 -1
  77. package/bin/commands/check.d.mts +1 -1
  78. package/bin/commands/check.mjs +7 -7
  79. package/bin/commands/commit-suggestion-instruction.d.mts +2 -2
  80. package/bin/commands/commit-suggestion-instruction.mjs +3 -3
  81. package/bin/commands/iterate/api-usage.mjs +8 -8
  82. package/bin/commands/iterate/check-instructions.d.mts +4 -4
  83. package/bin/commands/iterate/check-instructions.mjs +8 -8
  84. package/bin/commands/iterate/classify.mjs +2 -2
  85. package/bin/commands/iterate/escalate.mjs +6 -6
  86. package/bin/commands/iterate/index.mjs +20 -23
  87. package/bin/commands/iterate/merge-plan.mjs +3 -3
  88. package/bin/commands/iterate/merge-state.mjs +4 -7
  89. package/bin/commands/iterate/native-stack-rebase.mjs +1 -1
  90. package/bin/commands/iterate/render.d.mts +1 -1
  91. package/bin/commands/iterate/render.mjs +2 -2
  92. package/bin/commands/iterate/stall.mjs +11 -9
  93. package/bin/commands/iterate/{fix-loop-instruction.mjs → triage-loop-instruction.mjs} +7 -3
  94. package/bin/commands/iterate/{fix-code.d.mts → triage.d.mts} +2 -2
  95. package/bin/commands/iterate/{fix-code.mjs → triage.mjs} +14 -23
  96. package/bin/commands/iterate/unreported-required.mjs +2 -5
  97. package/bin/commands/journal/index.d.mts +1 -3
  98. package/bin/commands/journal/index.mjs +1 -1
  99. package/bin/commands/mark-files-as-viewed.d.mts +1 -3
  100. package/bin/commands/mark-files-as-viewed.mjs +1 -1
  101. package/bin/commands/next-check.d.mts +2 -2
  102. package/bin/commands/next-check.mjs +4 -2
  103. package/bin/commands/poll-progress.mjs +1 -1
  104. package/bin/commands/poll-summary-explicit-instructions.mjs +1 -1
  105. package/bin/commands/poll-summary-instructions.mjs +7 -7
  106. package/bin/commands/poll-summary.mjs +4 -4
  107. package/bin/commands/poll.d.mts +1 -1
  108. package/bin/commands/poll.mjs +4 -4
  109. package/bin/commands/resolve-mutate.d.mts +1 -1
  110. package/bin/commands/resolve-mutate.mjs +1 -1
  111. package/bin/commands/rest-stack-merge-readiness.mjs +1 -1
  112. package/bin/commands/single-tick.d.mts +5 -0
  113. package/bin/commands/single-tick.mjs +7 -0
  114. package/bin/commands/stack-drain.mjs +1 -1
  115. package/bin/commands/stack-merge-flag.mjs +1 -1
  116. package/bin/commands/stack-stall.d.mts +2 -2
  117. package/bin/commands/stack-stall.mjs +2 -2
  118. package/bin/commands/stack-work.d.mts +2 -2
  119. package/bin/commands/stack-work.mjs +2 -2
  120. package/bin/commands/suggestion-patch-item.d.mts +0 -1
  121. package/bin/commands/suggestion-patch-item.mjs +0 -12
  122. package/bin/commands/suggestion-patches.d.mts +2 -2
  123. package/bin/commands/suggestion-patches.mjs +1 -1
  124. package/bin/commands/tick-instructions.d.mts +9 -0
  125. package/bin/commands/tick-instructions.mjs +23 -0
  126. package/bin/commands/tick.d.mts +15 -0
  127. package/bin/commands/{event-mode.mjs → tick.mjs} +39 -55
  128. package/bin/comments/minimize-policy.mjs +1 -3
  129. package/bin/comments/resolve.d.mts +0 -23
  130. package/bin/comments/resolve.mjs +0 -4
  131. package/bin/comments/review-output-budget.d.mts +1 -1
  132. package/bin/comments/review-output-budget.mjs +1 -1
  133. package/bin/comments/sha-poll.mjs +3 -1
  134. package/bin/config/default-bot-usernames.d.mts +2 -0
  135. package/bin/config/default-bot-usernames.mjs +15 -0
  136. package/bin/config/load.d.mts +4 -138
  137. package/bin/config/load.mjs +88 -443
  138. package/bin/config/merge-command-args.mjs +2 -2
  139. package/bin/config/merge-method.d.mts +1 -2
  140. package/bin/config/merge-method.mjs +1 -8
  141. package/bin/config/renames.d.mts +8 -0
  142. package/bin/config/renames.mjs +87 -0
  143. package/bin/config/resolve.d.mts +31 -0
  144. package/bin/config/resolve.mjs +89 -0
  145. package/bin/config/schema.d.mts +161 -0
  146. package/bin/config/schema.mjs +168 -0
  147. package/bin/exit-codes.d.mts +1 -1
  148. package/bin/exit-codes.mjs +3 -3
  149. package/bin/github/poll-summary-check-blockers.mjs +1 -1
  150. package/bin/github/poll-summary-command.mjs +8 -6
  151. package/bin/github/poll-summary-projector.mjs +2 -2
  152. package/bin/github/poll-summary-review.mjs +1 -1
  153. package/bin/github/poll-summary-route.mjs +9 -9
  154. package/bin/github/poll-summary-unreported.mjs +1 -1
  155. package/bin/github/poll-summary.mjs +1 -1
  156. package/bin/github/queries.d.mts +1 -1
  157. package/bin/github/queries.mjs +1 -1
  158. package/bin/mcp/server.mjs +48 -54
  159. package/bin/merge-status/derive.mjs +1 -1
  160. package/bin/state/durable-state.d.mts +1 -3
  161. package/bin/state/durable-state.mjs +2 -8
  162. package/bin/state/fix-attempts.d.mts +2 -2
  163. package/bin/state/fix-attempts.mjs +1 -1
  164. package/bin/state/merge-request.mjs +1 -1
  165. package/bin/state/pr-fingerprint.mjs +4 -11
  166. package/bin/state/seen-comments.d.mts +4 -5
  167. package/bin/state/seen-comments.mjs +8 -14
  168. package/bin/state/stack-stall.d.mts +1 -1
  169. package/bin/state/stack-stall.mjs +1 -1
  170. package/bin/state/stall-state-store.d.mts +1 -1
  171. package/bin/state/stall-state-store.mjs +1 -1
  172. package/bin/types/escalate.d.mts +2 -2
  173. package/bin/types/iterate.d.mts +12 -26
  174. package/bin/types/merge-queue.d.mts +2 -2
  175. package/bin/types/next-check.d.mts +1 -1
  176. package/bin/types/poll-summary.d.mts +3 -6
  177. package/bin/types/report.d.mts +2 -2
  178. package/bin/types/suggestion-patch.d.mts +1 -8
  179. package/package.json +2 -1
  180. package/plugins/pr-shepherd/.claude-plugin/plugin.json +1 -1
  181. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  182. package/plugins/pr-shepherd/.codex.mcp.json +1 -1
  183. package/plugins/pr-shepherd/.mcp.json +1 -1
  184. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +6 -6
  185. package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +1 -1
  186. package/plugins/pr-shepherd/skills/pr-shepherd/references/{cloud-event-loop.md → event-loop.md} +5 -5
  187. package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +3 -3
  188. package/plugins/pr-shepherd/skills/pr-shepherd/references/merge-queue-ejection.md +1 -1
  189. package/plugins/pr-shepherd/skills/pr-shepherd/references/suggestion-patches.md +1 -1
  190. package/plugins/pr-shepherd/skills/pr-shepherd/references/{fix-code-loop.md → triage-loop.md} +3 -3
  191. package/plugins/pr-shepherd/skills/reduce-pr-noise/SKILL.md +1 -1
  192. package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +2 -2
  193. package/plugins/pr-shepherd/skills/reduce-pr-noise/references/settings.md +11 -11
  194. package/pr-shepherd.schema.json +361 -0
  195. package/bin/cli/event-state-scope.d.mts +0 -19
  196. package/bin/cli/event-state-scope.mjs +0 -60
  197. package/bin/cli/fix-formatter.d.mts +0 -4
  198. package/bin/commands/commit-suggestion.d.mts +0 -8
  199. package/bin/commands/commit-suggestion.mjs +0 -26
  200. package/bin/commands/event-instructions.d.mts +0 -11
  201. package/bin/commands/event-instructions.mjs +0 -27
  202. package/bin/commands/event-mode.d.mts +0 -14
  203. package/bin/commands/poll-mode.d.mts +0 -8
  204. package/bin/commands/poll-mode.mjs +0 -14
  205. package/bin/config.json +0 -68
  206. package/bin/types/protected-run.d.mts +0 -6
  207. package/bin/types/protected-run.mjs +0 -1
  208. /package/bin/commands/iterate/{fix-loop-instruction.d.mts → triage-loop-instruction.d.mts} +0 -0
@@ -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.60.0",
4
+ "version": "1.0.0",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
7
7
  "email": "jonathanrichardong@gmail.com"
package/README.md CHANGED
@@ -13,7 +13,7 @@ Drive a pull request to merge-ready with a coding agent. It works with Claude Co
13
13
  ```text
14
14
  $ pr-shepherd 123
15
15
 
16
- # PR #123 [FIX_CODE]
16
+ # PR #123 [TRIAGE]
17
17
 
18
18
  **status** `UNRESOLVED_COMMENTS` · **repo** `owner/repo`
19
19
  **summary** 3 passing
@@ -96,7 +96,7 @@ These are offline estimates at 3.5 characters per token over recorded sessions,
96
96
 
97
97
  ### Clearer agent direction
98
98
 
99
- For a single PR, each tick returns exactly one action: `WAIT`, `READY`, `MARK_READY`, `FIX_CODE`, `MERGE`, `CANCEL`, or `ESCALATE`. A `--stack` poll can also return `SHEPHERD`, which starts one-PR sessions for owned layers. Every action except `CANCEL` comes with numbered `## Instructions` and exact commands, so the agent does not invent policy. In the [behavior evals](evals/README.md), Sonnet 5.5 at low effort scores **0.94** with the plugin and **0.82** without it on the 36 GitHub-gotcha cases, so it misses about two-thirds fewer graded checks (0.18 → 0.06). On the eight gotchas listed under [Context and correctness](#context-and-correctness) it goes from 0.50 to 0.92. Weighted toward those gotchas, the mean Δ over all 52 cases is **+0.14**, and no case has a regression whose interval excludes 0. See [docs/actions.md](docs/actions.md) for the output contract.
99
+ For a single PR, each tick returns exactly one action: `WAIT`, `READY`, `MARK_READY`, `TRIAGE`, `MERGE`, `CANCEL`, or `ESCALATE`. A `stack` poll can also return `SHEPHERD`, which starts one-PR sessions for owned layers. Every action except `CANCEL` comes with numbered `## Instructions` and exact commands, so the agent does not invent policy. In the [behavior evals](evals/README.md), Sonnet 5.5 at low effort scores **0.94** with the plugin and **0.82** without it on the 36 GitHub-gotcha cases, so it misses about two-thirds fewer graded checks (0.18 → 0.06). On the eight gotchas listed under [Context and correctness](#context-and-correctness) it goes from 0.50 to 0.92. Weighted toward those gotchas, the mean Δ over all 52 cases is **+0.14**, and no case has a regression whose interval excludes 0. See [docs/actions.md](docs/actions.md) for the output contract.
100
100
 
101
101
  ### Lower GitHub API quota use
102
102
 
@@ -139,8 +139,8 @@ See [docs/graphql-usage.md](docs/graphql-usage.md) and [docs/rest-usage.md](docs
139
139
  - The plugin ships [skills](docs/skills.md) for Claude Code, Codex, and Grok.
140
140
  - Cursor and other stdio clients use the [MCP server](docs/mcp.md).
141
141
  - Shell and CI use the [CLI](docs/cli-usage.md).
142
- - In Claude Code cloud sessions, Shepherd switches to the [REST transport](docs/rest.md) and [event mode](docs/cloud.md).
143
- - `--stack` reports every layer of a native GitHub stacked PR and shepherds the layers you own.
142
+ - In Claude Code cloud sessions, Shepherd switches to the [REST transport](docs/rest.md) and [single ticks](docs/cloud.md).
143
+ - `stack` reports every layer of a native GitHub stacked PR and shepherds the layers you own.
144
144
 
145
145
  ## Use cases
146
146
 
@@ -151,9 +151,10 @@ See [docs/graphql-usage.md](docs/graphql-usage.md) and [docs/rest-usage.md](docs
151
151
 
152
152
  1. **Install the MCP server and the CLI.** See [Install](#install) below.
153
153
  2. **Write your policy into a skill or agent instruction.** Examples: "skip non-blocking review comments", "reply instead of fixing nits", "never merge". Shepherd reports state and the next action. Your instructions decide how the agent treats each comment. The shipped `pr-shepherd` skill is a thin dispatcher you can wrap ([docs/skills.md](docs/skills.md)).
154
- 3. **Configure Shepherd.** In `.pr-shepherdrc.yml`, turn on the bundled classification rules. They drop known bot noise before it reaches the agent, which saves more tokens and tool calls. With the default `actions.autoMinimizeSuppressed: true`, several rules also minimize matching comments on GitHub and surface the mutation result without editing the PR body. See [docs/configuration.md](docs/configuration.md#classifystandard--default-).
154
+ 3. **Configure Shepherd.** In `.pr-shepherdrc.yml`, turn on the bundled classification rules. They drop known bot noise before it reaches the agent, which saves more tokens and tool calls. With the default `actions.auto-minimize-suppressed: true`, several rules also minimize matching comments on GitHub and surface the mutation result without editing the PR body. See [docs/configuration.md](docs/configuration.md#classifystandard--default-).
155
155
 
156
156
  ```yaml
157
+ version: 1
157
158
  classify:
158
159
  standard:
159
160
  all: true
@@ -212,6 +213,7 @@ Leave out the PR number to use the current branch.
212
213
  | Configuration and classification rules | [docs/configuration.md](docs/configuration.md), [examples/classification/](examples/classification/) |
213
214
  | Journal, library API | [docs/api.md](docs/api.md) |
214
215
  | Compared with Cursor, Copilot, CodeRabbit, … | [docs/comparison.md](docs/comparison.md) |
216
+ | Upgrading from 0.x | [docs/migrating-to-v1.md](docs/migrating-to-v1.md) |
215
217
 
216
218
  ## Harness ecosystem
217
219
 
package/bin/api.d.mts CHANGED
@@ -4,11 +4,20 @@ import { type ApplyMergeResult } from "./commands/apply-merge.mts";
4
4
  import type { RestMergeAction, RestMergeStackGuard } from "./github/rest-merge.mts";
5
5
  export type { RestMergeStackGuard } from "./github/rest-merge.mts";
6
6
  import type { MergeMethod } from "./config/merge-method.mts";
7
+ import { type CheckBlockerCommandResult } from "./commands/apply-check-blocker.mts";
7
8
  import { type JournalResult } from "./commands/journal/index.mts";
8
9
  import { type MarkFilesAsViewedResult } from "./commands/mark-files-as-viewed.mts";
9
10
  import { type ApplyQueueRemovalResult } from "./commands/apply-queue-removal.mts";
10
11
  import type { ResolveResult } from "./comments/resolve.mts";
11
- import type { BuildSuggestionPatchesResult, CommitSuggestionResult, IterateCommandOptions, IterateResult, PollSummaryResult } from "./types.mts";
12
+ import type { SuggestResult, IterateCommandOptions, IterateResult, PollSummaryResult } from "./types.mts";
13
+ export type { CancelReason, IterateResult, IterateResultTriage, IterateResultMerge, NextCheck, PollSummaryItem, PollSummaryResult, ShepherdAction, ShepherdMergeStatus, ShepherdStatus, StackNextAction, SuggestResult, SuggestionPatchResult, } from "./types.mts";
14
+ export type { ApplyMergeResult } from "./commands/apply-merge.mts";
15
+ export type { MarkFilesAsViewedResult } from "./commands/mark-files-as-viewed.mts";
16
+ export type { ApplyQueueRemovalResult } from "./commands/apply-queue-removal.mts";
17
+ export type { CheckBlockerCommandResult } from "./commands/apply-check-blocker.mts";
18
+ export type { JournalResult } from "./commands/journal/index.mts";
19
+ export type { ResolveResult } from "./comments/resolve.mts";
20
+ export type { ShepherdJournalExtraction } from "./journal/index.mts";
12
21
  import { type ShepherdJournalExtraction } from "./journal/index.mts";
13
22
  export interface CreatePrShepherdOptions {
14
23
  /** GitHub transport retained for this client; individual calls may override it. */
@@ -22,18 +31,17 @@ type IterateOptions = Omit<IterateCommandOptions, "format" | "prNumber" | "targe
22
31
  export type SingleIterateInput = IterateOptions & {
23
32
  pr?: PrReference;
24
33
  prs?: never;
25
- stack?: never;
26
34
  };
27
- export type AggregateIterateInput = IterateOptions & ({
35
+ /** One aggregate tick over several PRs from one repository. */
36
+ export type MultiIterateInput = IterateOptions & {
28
37
  prs: PrReference[];
29
38
  pr?: never;
30
- stack?: never;
31
- } | {
32
- stack: PrReference;
33
- pr?: never;
34
- prs?: never;
35
- });
36
- export type IterateInput = SingleIterateInput | AggregateIterateInput;
39
+ };
40
+ export type IterateInput = SingleIterateInput | MultiIterateInput;
41
+ /** One aggregate tick over the native stack containing `pr`. */
42
+ export type StackInput = IterateOptions & {
43
+ pr: PrReference;
44
+ };
37
45
  export interface ReviewMutationsOperation {
38
46
  type: "review_mutations";
39
47
  resolveThreadIds?: string[];
@@ -63,6 +71,14 @@ export interface AcknowledgeQueueRemovalOperation {
63
71
  queueCommitOid: string;
64
72
  removedAtUnix: number;
65
73
  }
74
+ export interface CheckBlockerOperation {
75
+ /** Record (`blockedBy`) or clear (`clear: true`) an external blocker for one failing check. */
76
+ type: "check_blocker";
77
+ check: string;
78
+ /** PR/issue URL, `owner/repo#N`, or `issue:owner/repo#N`. */
79
+ blockedBy?: string;
80
+ clear?: boolean;
81
+ }
66
82
  export interface MergeOperation {
67
83
  type: "merge";
68
84
  requireSha: string;
@@ -71,7 +87,7 @@ export interface MergeOperation {
71
87
  expectedStack?: RestMergeStackGuard;
72
88
  }
73
89
  /** Operations run in this exact list order after validation. */
74
- export type ApplyOperation = MergeOperation | ReviewMutationsOperation | MarkFilesViewedOperation | AppendJournalOperation | AcknowledgeQueueRemovalOperation;
90
+ export type ApplyOperation = MergeOperation | ReviewMutationsOperation | MarkFilesViewedOperation | AppendJournalOperation | AcknowledgeQueueRemovalOperation | CheckBlockerOperation;
75
91
  export interface ApplyInput {
76
92
  transport?: GithubTransport;
77
93
  /** PR shared by every operation in this ordered apply request. */
@@ -93,23 +109,19 @@ export type ApplyOperationResult = {
93
109
  } | {
94
110
  type: "acknowledge_queue_removal";
95
111
  result: ApplyQueueRemovalResult;
112
+ } | {
113
+ type: "check_blocker";
114
+ result: CheckBlockerCommandResult;
96
115
  };
97
116
  export interface ApplyResult {
98
117
  operations: ApplyOperationResult[];
99
118
  }
100
- export interface BuildSuggestionPatchInput {
101
- transport?: GithubTransport;
102
- pr?: PrReference;
103
- threadId: string;
104
- message: string;
105
- description?: string;
106
- }
107
119
  export interface SuggestionPatchInput {
108
120
  threadId: string;
109
121
  message: string;
110
122
  description?: string;
111
123
  }
112
- export interface BuildSuggestionPatchesInput {
124
+ export interface SuggestInput {
113
125
  transport?: GithubTransport;
114
126
  pr?: PrReference;
115
127
  suggestions: SuggestionPatchInput[];
@@ -120,13 +132,12 @@ export interface GetJournalInput {
120
132
  }
121
133
  export interface PrShepherd {
122
134
  iterate(input?: SingleIterateInput): Promise<IterateResult>;
123
- iterate(input: AggregateIterateInput): Promise<PollSummaryResult>;
135
+ iterate(input: MultiIterateInput): Promise<PollSummaryResult>;
124
136
  iterate(input: IterateInput): Promise<IterateResult | PollSummaryResult>;
137
+ stack(input: StackInput): Promise<PollSummaryResult>;
125
138
  apply(input: ApplyInput): Promise<ApplyResult>;
126
- buildSuggestionPatches(input: BuildSuggestionPatchesInput): Promise<BuildSuggestionPatchesResult>;
139
+ suggest(input: SuggestInput): Promise<SuggestResult>;
127
140
  getJournal(input: GetJournalInput): Promise<ShepherdJournalExtraction>;
128
- /** Compatibility adapter; prefer buildSuggestionPatches. */
129
- buildSuggestionPatch(input: BuildSuggestionPatchInput): Promise<CommitSuggestionResult>;
130
141
  }
131
142
  /** Raised before any API mutation when an input cannot be validated. */
132
143
  export declare class PrShepherdValidationError extends Error {
@@ -140,6 +151,6 @@ export declare class PartialApplyError extends Error {
140
151
  }
141
152
  /**
142
153
  * Creates the programmatic API. The returned object intentionally exposes only
143
- * read/plan, ordered apply, and suggestion-patch operations.
154
+ * read/plan (`iterate`, `stack`), ordered apply, and suggestion-patch (`suggest`) operations.
144
155
  */
145
156
  export declare function createPrShepherd(options?: CreatePrShepherdOptions): PrShepherd;
package/bin/api.mjs CHANGED
@@ -2,15 +2,17 @@
2
2
  import { resolve } from "node:path";
3
3
  import { createGithubTransportRunner } from "./github/transport.mjs";
4
4
  import { runApplyMerge, validateApplyMergeOptions, } from "./commands/apply-merge.mjs";
5
- import { runCommitSuggestion } from "./commands/commit-suggestion.mjs";
6
5
  import { runSuggestionPatches } from "./commands/suggestion-patches.mjs";
7
- import { runIterateForMode, runPollSummaryForMode } from "./commands/event-mode.mjs";
6
+ import { runIterateTick, runPollSummaryTick } from "./commands/tick.mjs";
7
+ import { applyCheckBlocker, } from "./commands/apply-check-blocker.mjs";
8
+ import { parseBlockedByRef } from "./commands/check-blocker-ref.mjs";
8
9
  import { runJournal } from "./commands/journal/index.mjs";
9
10
  import { validateJournalItem } from "./commands/journal/transform.mjs";
10
11
  import { runMarkFilesAsViewed, } from "./commands/mark-files-as-viewed.mjs";
11
12
  import { runResolveMutate } from "./commands/resolve-mutate.mjs";
12
13
  import { applyQueueRemovalAck, } from "./commands/apply-queue-removal.mjs";
13
14
  import { runWithExecutionCwd } from "./execution-context.mjs";
15
+ import { loadConfig } from "./config/load.mjs";
14
16
  import { parsePrReference, normalizeRepositoryIdentity, resolveParsedPrTarget, } from "./pr-reference.mjs";
15
17
  import { getPullRequestBody, getRepoInfo } from "./github/client.mjs";
16
18
  import { getLocalHeadSha } from "./commands/suggestion-patch-git.mjs";
@@ -36,28 +38,49 @@ export class PartialApplyError extends Error {
36
38
  }
37
39
  /**
38
40
  * Creates the programmatic API. The returned object intentionally exposes only
39
- * read/plan, ordered apply, and suggestion-patch operations.
41
+ * read/plan (`iterate`, `stack`), ordered apply, and suggestion-patch (`suggest`) operations.
40
42
  */
41
43
  export function createPrShepherd(options = {}) {
42
44
  const cwd = options.cwd === undefined ? undefined : resolve(options.cwd);
43
45
  const transportRunner = createGithubTransportRunner(options.transport);
44
46
  function runInClient(input, work) {
45
- return runWithExecutionCwd(cwd, () => transportRunner(work, input?.transport));
47
+ // Validate config first, as the CLI does, so a bad or pre-v1 file rejects before GitHub I/O.
48
+ return runWithExecutionCwd(cwd, async () => {
49
+ loadConfig();
50
+ return transportRunner(work, input?.transport);
51
+ });
46
52
  }
47
53
  function iterate(input = {}) {
48
54
  return runInClient(input, async () => {
49
- validateIterateSelectors(input);
50
- if ("prs" in input || "stack" in input) {
51
- const target = await resolveAggregateIterateInput(input);
52
- return runPollSummaryForMode(target);
55
+ if ("pr" in input && "prs" in input) {
56
+ throw new PrShepherdValidationError("iterate pr and prs selectors are mutually exclusive");
57
+ }
58
+ if ("prs" in input) {
59
+ const { prs, pr: _pr, ...options } = input;
60
+ const { numbers, repository } = await resolveSameRepositoryRefs(prs, "iterate.prs");
61
+ return runPollSummaryTick({ ...options, prNumbers: numbers, targetRepository: repository });
53
62
  }
54
- const { pr: _pr, ...iterateOptions } = input;
63
+ const { pr: _pr, prs: _prs, ...iterateOptions } = input;
55
64
  const target = resolvePrReference(input.pr);
56
- return runIterateForMode({ ...iterateOptions, ...target, format: "json" });
65
+ return runIterateTick({ ...iterateOptions, ...target, format: "json" });
57
66
  });
58
67
  }
59
68
  return Object.freeze({
60
69
  iterate,
70
+ stack(input) {
71
+ return runInClient(input ?? {}, async () => {
72
+ if (!input || input.pr === undefined) {
73
+ throw new PrShepherdValidationError("stack requires a PR reference");
74
+ }
75
+ const { pr, ...options } = input;
76
+ const { numbers, repository } = await resolveSameRepositoryRefs([pr], "stack.pr");
77
+ return runPollSummaryTick({
78
+ ...options,
79
+ stackPrNumber: numbers[0],
80
+ targetRepository: repository,
81
+ });
82
+ });
83
+ },
61
84
  getJournal(input) {
62
85
  return runInClient(input, async () => {
63
86
  const { prNumber, targetRepository } = resolvePrReference(input.pr);
@@ -127,6 +150,18 @@ export function createPrShepherd(options = {}) {
127
150
  results.push({ type: operation.type, result });
128
151
  break;
129
152
  }
153
+ case "check_blocker": {
154
+ const result = await applyCheckBlocker({
155
+ prNumber,
156
+ targetRepository,
157
+ checkName: operation.check,
158
+ ...(operation.clear
159
+ ? { clear: true }
160
+ : { blocker: parseBlockedByRef(operation.blockedBy) }),
161
+ });
162
+ results.push({ type: operation.type, result });
163
+ break;
164
+ }
130
165
  case "acknowledge_queue_removal": {
131
166
  const result = await applyQueueRemovalAck({
132
167
  prNumber,
@@ -149,16 +184,8 @@ export function createPrShepherd(options = {}) {
149
184
  return { operations: results };
150
185
  });
151
186
  },
152
- buildSuggestionPatch(input) {
153
- validateSuggestionPatchInput(input);
154
- const { pr: _pr, ...options } = input;
155
- return runInClient(input, async () => {
156
- const target = resolvePrReference(input.pr);
157
- return runCommitSuggestion({ ...options, ...target, format: "json" });
158
- });
159
- },
160
- buildSuggestionPatches(input) {
161
- validateSuggestionPatchesInput(input);
187
+ suggest(input) {
188
+ validateSuggestInput(input);
162
189
  const { pr: _pr, ...options } = input;
163
190
  return runInClient(input, async () => {
164
191
  const target = resolvePrReference(input.pr);
@@ -167,10 +194,9 @@ export function createPrShepherd(options = {}) {
167
194
  },
168
195
  });
169
196
  }
170
- async function resolveAggregateIterateInput(input) {
171
- const refs = "prs" in input ? input.prs : [input.stack];
172
- if (!refs || refs.length === 0) {
173
- throw new PrShepherdValidationError("iterate.prs must contain at least one PR reference");
197
+ async function resolveSameRepositoryRefs(refs, label) {
198
+ if (!Array.isArray(refs) || refs.length === 0) {
199
+ throw new PrShepherdValidationError(`${label} must contain at least one PR reference`);
174
200
  }
175
201
  const parsedRefs = refs.map((ref) => {
176
202
  const parsed = parsePrReference(ref);
@@ -197,16 +223,7 @@ async function resolveAggregateIterateInput(input) {
197
223
  if (!numbers.includes(prNumber))
198
224
  numbers.push(prNumber);
199
225
  }
200
- const { pr: _pr, prs: _prs, stack: _stack, ...options } = input;
201
- return "prs" in input
202
- ? { ...options, prNumbers: numbers, targetRepository: repository }
203
- : { ...options, stackPrNumber: numbers[0], targetRepository: repository };
204
- }
205
- function validateIterateSelectors(input) {
206
- const selectorCount = ["pr", "prs", "stack"].filter((key) => key in input).length;
207
- if (selectorCount > 1) {
208
- throw new PrShepherdValidationError("iterate pr, prs, and stack selectors are mutually exclusive");
209
- }
226
+ return { numbers, repository: repository };
210
227
  }
211
228
  function validateApplyInput(input) {
212
229
  if (!input || !Array.isArray(input.operations) || input.operations.length === 0) {
@@ -254,6 +271,21 @@ function validateOperation(operation) {
254
271
  throw new PrShepherdValidationError(validation.error);
255
272
  }
256
273
  return;
274
+ case "check_blocker":
275
+ if (typeof operation.check !== "string" || operation.check === "") {
276
+ throw new PrShepherdValidationError("check_blocker.check must be the exact check name");
277
+ }
278
+ if (operation.clear !== undefined && typeof operation.clear !== "boolean") {
279
+ throw new PrShepherdValidationError("check_blocker.clear must be a boolean");
280
+ }
281
+ if (operation.clear === true && operation.blockedBy !== undefined) {
282
+ throw new PrShepherdValidationError("check_blocker takes either blockedBy or clear, not both");
283
+ }
284
+ if (operation.clear !== true &&
285
+ (typeof operation.blockedBy !== "string" || !parseBlockedByRef(operation.blockedBy))) {
286
+ throw new PrShepherdValidationError("check_blocker.blockedBy must be a PR/issue URL, owner/repo#N, or issue:owner/repo#N");
287
+ }
288
+ return;
257
289
  case "acknowledge_queue_removal":
258
290
  if (typeof operation.requireSha !== "string" ||
259
291
  !/^[0-9a-f]{40}$/.test(operation.requireSha)) {
@@ -317,21 +349,9 @@ function validateMarkFilesViewed(operation) {
317
349
  }
318
350
  }
319
351
  }
320
- function validateSuggestionPatchInput(input) {
321
- if (!input || typeof input.threadId !== "string" || input.threadId.trim() === "") {
322
- throw new PrShepherdValidationError("buildSuggestionPatch.threadId is required");
323
- }
324
- if (typeof input.message !== "string" || input.message.trim() === "") {
325
- throw new PrShepherdValidationError("buildSuggestionPatch.message is required and must be non-empty");
326
- }
327
- if (input.description !== undefined && typeof input.description !== "string") {
328
- throw new PrShepherdValidationError("buildSuggestionPatch.description must be a string");
329
- }
330
- validatePrReference(input.pr);
331
- }
332
- function validateSuggestionPatchesInput(input) {
352
+ function validateSuggestInput(input) {
333
353
  if (!input || !Array.isArray(input.suggestions) || input.suggestions.length === 0) {
334
- throw new PrShepherdValidationError("buildSuggestionPatches.suggestions must be a non-empty array");
354
+ throw new PrShepherdValidationError("suggest.suggestions must be a non-empty array");
335
355
  }
336
356
  const seen = new Set();
337
357
  for (const suggestion of input.suggestions) {
@@ -34,7 +34,7 @@ export interface CiVerdict {
34
34
  anyFailing: boolean;
35
35
  /** Names of checks that were filtered out (triggered by non-PR events). */
36
36
  filteredNames: string[];
37
- /** Names of checks suppressed by the user's ignoreChecks config. */
37
+ /** Names of checks suppressed by the user's checks.ignore config. */
38
38
  ignoredNames: string[];
39
39
  /** Names of CANCELLED checks covered by another run of the same workflow and event. */
40
40
  supersededNames: string[];
@@ -24,11 +24,11 @@ import picomatch from "picomatch";
24
24
  export function classifyChecks(checks, opts = {}) {
25
25
  const config = loadConfig();
26
26
  const relevantEvents = new Set([
27
- ...config.checks.ciTriggerEvents,
27
+ ...config.checks["ci-trigger-events"],
28
28
  ...(opts.additionalRelevantEvents ?? []),
29
29
  ]);
30
- const isIgnored = buildMatcher(config.ignoreChecks ?? []);
31
- const isProtected = buildMatcher(config.actions.neverCancelRuns ?? []);
30
+ const isIgnored = buildMatcher(config.checks.ignore ?? []);
31
+ const isProtected = buildMatcher(config.checks["never-ignore"] ?? []);
32
32
  const protectedRunIds = buildProtectedRunIds(checks, isProtected);
33
33
  const supersededIndices = buildSupersededIndices(checks, opts.requiredContexts);
34
34
  return checks.map((c, index) => {
@@ -69,7 +69,7 @@ function checkMatchesProtection(check, isProtected) {
69
69
  }
70
70
  function classify(check, relevantEvents) {
71
71
  // Filter: runs from non-PR events don't count toward PR readiness.
72
- // event === null means a commit StatusContext — these are always relevant regardless of ciTriggerEvents.
72
+ // event === null means a commit StatusContext — these are always relevant regardless of checks.ci-trigger-events.
73
73
  if (check.event !== null && !relevantEvents.has(check.event)) {
74
74
  return { ...check, category: "filtered" };
75
75
  }
@@ -175,7 +175,7 @@ function truncateTail(text, keep) {
175
175
  return from === -1 ? tail : truncateLogExcerpt(`${head}${text.slice(from)}`);
176
176
  }
177
177
  function compileIgnoreLogLinePatterns() {
178
- return loadConfig().checks.ignoreLogLines.map((pattern) => new RegExp(pattern));
178
+ return loadConfig().checks["ignore-log-lines"].map((pattern) => new RegExp(pattern));
179
179
  }
180
180
  function isNoiseLine(line, patterns) {
181
181
  return patterns.some((re) => re.test(line));
@@ -1,4 +1,4 @@
1
1
  import type { ClassifyRule } from "../types.mts";
2
- /** Codecov's coverage report comment. Coverage gates still report through their status checks. */
2
+ /** Codecov's coverage report and first-run welcome. Coverage gates still report through checks. */
3
3
  declare const rule: ClassifyRule;
4
4
  export default rule;
@@ -1,9 +1,14 @@
1
1
  import { isBotItem } from "./author.mjs";
2
- /** Codecov's coverage report comment. Coverage gates still report through their status checks. */
2
+ const WELCOME = /^## Welcome to \[Codecov\]\(https:\/\/codecov\.io[^\s)]*\) :tada:\n\nOnce you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests\.\n\nThanks for integrating Codecov - We've got you covered :open_umbrella:$/;
3
+ /** Codecov's coverage report and first-run welcome. Coverage gates still report through checks. */
3
4
  const rule = (item) => {
4
5
  if (!isBotItem(item, ["codecov"], ["pr-comment"]))
5
6
  return null;
6
- if (!/^#{1,6}\s*\[?Codecov\b/i.test(item.body.trimStart()))
7
+ const body = item.body.trim();
8
+ if (WELCOME.test(body)) {
9
+ return { autoResolve: true, suppress: true, reason: "Codecov welcome notice" };
10
+ }
11
+ if (!/^#{1,6}\s*\[?Codecov\b/i.test(body))
7
12
  return null;
8
13
  return { autoResolve: true, suppress: true, reason: "Codecov coverage report" };
9
14
  };
@@ -1,8 +1,9 @@
1
1
  import type { ClassifyRule } from "../types.mts";
2
2
  /**
3
- * CodeRabbit progress, rate-limit, review-skipped and draft notices, plus its "Action not
4
- * completed" command replies. If the comment is later edited into a PR walkthrough, its new
5
- * body no longer matches and Shepherd surfaces the edited minimized comment to the agent.
3
+ * CodeRabbit progress, rate-limit, review-skipped and draft notices, its status-only command
4
+ * replies, and its zero-findings, skipped-autofix, and failed-edit status comments. If the
5
+ * comment is later edited into a PR walkthrough, its new body no longer matches and Shepherd
6
+ * surfaces the edited minimized comment to the agent.
6
7
  */
7
8
  declare const rule: ClassifyRule;
8
9
  export default rule;
@@ -1,54 +1,33 @@
1
1
  import { isBotItem } from "./author.mjs";
2
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
- const FINISHING_TOUCH_BLOCK = `<!-- finishing_touch_checkbox_start -->
5
-
6
- <details>
7
- <summary><strong>✨ Finishing Touches</strong></summary>
8
- <dl>
9
- <dd>
10
-
11
- <details open>
12
- <summary><strong>📝 Generate docstrings</strong></summary>
13
- <dl>
14
- <dd>
15
-
16
- - [ ] <!-- {"checkboxId":"3e1879ae-f29b-4d0d-8e06-d12b7ba33d98"} --> Commit to this branch
17
- - [ ] <!-- {"checkboxId":"7962f53c-55bc-4827-bfbf-6a18da830691"} --> Create a new PR
18
-
19
- </dd>
20
- </dl>
21
- </details>
22
- <details open>
23
- <summary><strong>🧪 Generate unit tests (beta)</strong></summary>
24
- <dl>
25
- <dd>
26
-
27
- - [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Commit to this branch
28
- - [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Create a new PR
29
-
30
- </dd>
31
- </dl>
32
- </details>
33
-
34
- </dd>
35
- </dl>
36
- </details>
37
-
38
- <!-- finishing_touch_checkbox_end -->`;
39
- const AUTOPILOT_BLOCK = /\n<!-- autopilot:start -->\n- \[ \] <!-- \{"checkboxId":"2708ad07-9f24-4260-9c11-7dc76a49f2e3"\} --> <strong title="Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts">Autofix<\/strong> · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts\n<!-- autopilot:end -->$/;
3
+ /**
4
+ * Command replies that only report a known status, optionally followed by one of CodeRabbit's
5
+ * fixed explanations. The invocation id varies between releases; any other text stays visible.
6
+ */
7
+ const COMMAND_REPLY = /^<!-- This is an auto-generated reply by CodeRabbit -->\n<!-- CodeRabbit review command invocation: [^\n]* -->\n<details>\n<summary>(?:⚠️ Action not completed|✅ Action performed)<\/summary>\n\n(?:Review rate limited\.|Review skipped: \d+ files exceed the limit of \d+\.|(?:Full review|Review) finished\.|Pull request base or head changed\.|Pull request is closed\.)\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\/[^\s)]*\)\. This review may still proceed through usage-based billing if eligible\. Your next included review will be available in \d+ minutes?\.)\n\n)?<\/details>$/;
8
+ /** Standalone status comments: zero-findings completion, skipped autofix, and a failed edit. */
9
+ const STATUS_COMMENTS = [
10
+ /^<!-- coderabbit-review-completion-marker -->\n\*\*Actionable comments posted: 0\*\*$/,
11
+ /^<!-- This is an auto-generated comment: autofix status by CodeRabbit -->\n> \[!NOTE\]\n> Autofix is a beta feature\.[^\n]*\n\nAutofix skipped\. No unresolved CodeRabbit review comments with fix instructions found\.\n<!-- autofix-run-id: [^\n]* -->$/,
12
+ /^> \[!CAUTION\]\n> Failed to replace \(edit\) comment\. This is likely due to insufficient permissions or the comment being deleted\.\n> \n> <details>\n> <summary>Error details<\/summary>\n> \n> ```\n> [^`\n]*\n> ```\n> \n> <\/details>$/,
13
+ ];
14
+ /** One "Finishing Touches" option group: a bold title and only checkbox lines with plain labels. */
15
+ const FINISHING_TOUCH_GROUP = "<details open>\\n<summary><strong>[^<\\n]+<\\/strong><\\/summary>\\n<dl>\\n<dd>\\n\\n(?:- \\[ \\] <!-- \\{[^\\n]*\\} --> [^<\\n]+\\n)+\\n<\\/dd>\\n<\\/dl>\\n<\\/details>\\n";
16
+ /** The finishing-touch checkbox UI, with any checkbox ids and any number of option groups. */
17
+ const FINISHING_TOUCH_BLOCK = new RegExp(`\\n\\n<!-- finishing_touch_checkbox_start -->\\n\\n<details>\\n<summary><strong>✨ Finishing Touches<\\/strong><\\/summary>\\n<dl>\\n<dd>\\n\\n(?:${FINISHING_TOUCH_GROUP})+\\n<\\/dd>\\n<\\/dl>\\n<\\/details>\\n\\n<!-- finishing_touch_checkbox_end -->$`);
18
+ /** The Autofix/Autopilot checkbox, with any checkbox id, one-word label, and description. */
19
+ const AUTOPILOT_BLOCK = /\n<!-- autopilot:start -->\n- \[ \] <!-- \{[^\n]*\} --> <strong title="[^"\n]*">\w+<\/strong> · [^<\n]+\n<!-- autopilot:end -->$/;
40
20
  /** Strips CodeRabbit's footer and header blocks so only the notice envelope remains. */
41
21
  function stripChrome(body) {
42
- const withoutFooter = body
22
+ return body
43
23
  .trim()
44
24
  .replace(/\n<!-- tips_start -->[\s\S]*?<!-- tips_end -->$/, "")
45
25
  .trim()
46
26
  .replace(AUTOPILOT_BLOCK, "")
47
- .trim();
48
- const withoutFinishingTouch = withoutFooter.endsWith(`\n\n${FINISHING_TOUCH_BLOCK}`)
49
- ? withoutFooter.slice(0, -FINISHING_TOUCH_BLOCK.length).trim()
50
- : withoutFooter;
51
- return withoutFinishingTouch
27
+ .trim()
28
+ .replace(FINISHING_TOUCH_BLOCK, "")
29
+ .trim()
30
+ .replace(/^<!-- coderabbit-ai-generated:\S* -->\s*/, "")
52
31
  .replace(/^<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\s*/, "")
53
32
  .replace(/^<!-- review_stack_entry_start -->[\s\S]*?<!-- review_stack_entry_end -->\s*/, "");
54
33
  }
@@ -57,21 +36,24 @@ function isQuotedNotice(quoted) {
57
36
  if (!onlyQuotedLines)
58
37
  return false;
59
38
  return (quoted.includes("> ## Review limit reached\n") ||
39
+ quoted.includes("> ## Rate limit exceeded\n") ||
60
40
  quoted.includes("> ## Draft PR not reviewed\n") ||
61
41
  quoted.includes("> ## Review skipped\n") ||
62
42
  quoted.includes("> Currently processing new changes in this PR. This may take a few minutes, please wait..."));
63
43
  }
64
44
  /**
65
- * CodeRabbit progress, rate-limit, review-skipped and draft notices, plus its "Action not
66
- * completed" command replies. If the comment is later edited into a PR walkthrough, its new
67
- * body no longer matches and Shepherd surfaces the edited minimized comment to the agent.
45
+ * CodeRabbit progress, rate-limit, review-skipped and draft notices, its status-only command
46
+ * replies, and its zero-findings, skipped-autofix, and failed-edit status comments. If the
47
+ * comment is later edited into a PR walkthrough, its new body no longer matches and Shepherd
48
+ * surfaces the edited minimized comment to the agent.
68
49
  */
69
50
  const rule = (item) => {
70
51
  if (!isBotItem(item, ["coderabbitai"], ["pr-comment"]))
71
52
  return null;
72
53
  const body = stripChrome(item.body);
73
54
  const quoted = body.match(NOTICE_ENVELOPE)?.[2]?.trim();
74
- if (!(quoted && isQuotedNotice(quoted)) && !COMMAND_REPLY.test(body))
55
+ const isStatus = COMMAND_REPLY.test(body) || STATUS_COMMENTS.some((notice) => notice.test(body));
56
+ if (!(quoted && isQuotedNotice(quoted)) && !isStatus)
75
57
  return null;
76
58
  return {
77
59
  autoResolve: true,