pr-shepherd 0.41.0 → 0.42.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 (43) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +7 -7
  3. package/bin/api.d.mts +12 -1
  4. package/bin/api.mjs +34 -1
  5. package/bin/cli/formatters.d.mts +2 -1
  6. package/bin/cli/formatters.mjs +33 -0
  7. package/bin/cli/handlers.d.mts +1 -0
  8. package/bin/cli/handlers.mjs +20 -1
  9. package/bin/cli/help-command-pages.d.mts +20 -4
  10. package/bin/cli/help-command-pages.mjs +20 -4
  11. package/bin/cli/help-top-page.d.mts +1 -1
  12. package/bin/cli/help-top-page.mjs +3 -3
  13. package/bin/cli/help.d.mts +21 -5
  14. package/bin/cli/suggestion-patch-flags.d.mts +11 -0
  15. package/bin/cli/suggestion-patch-flags.mjs +83 -0
  16. package/bin/cli-parser.mjs +6 -2
  17. package/bin/commands/commit-suggestion-instruction.d.mts +1 -1
  18. package/bin/commands/commit-suggestion-instruction.mjs +3 -3
  19. package/bin/commands/commit-suggestion.d.mts +1 -1
  20. package/bin/commands/commit-suggestion.mjs +19 -138
  21. package/bin/commands/suggestion-patch-git.d.mts +6 -0
  22. package/bin/commands/suggestion-patch-git.mjs +54 -0
  23. package/bin/commands/suggestion-patch-item.d.mts +15 -0
  24. package/bin/commands/suggestion-patch-item.mjs +106 -0
  25. package/bin/commands/suggestion-patches.d.mts +6 -0
  26. package/bin/commands/suggestion-patches.mjs +108 -0
  27. package/bin/github/gql/{commit-suggestion-thread.gql → suggestion-threads.gql} +2 -2
  28. package/bin/github/queries.d.mts +1 -1
  29. package/bin/github/queries.mjs +1 -1
  30. package/bin/github/suggestion-thread.d.mts +3 -3
  31. package/bin/github/suggestion-thread.mjs +8 -5
  32. package/bin/mcp/server.d.mts +1 -1
  33. package/bin/mcp/server.mjs +23 -3
  34. package/bin/types/report.d.mts +0 -19
  35. package/bin/types/suggestion-patch.d.mts +30 -0
  36. package/bin/types/suggestion-patch.mjs +1 -0
  37. package/bin/types.d.mts +1 -0
  38. package/bin/types.mjs +1 -0
  39. package/package.json +1 -1
  40. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  41. package/plugins/pr-shepherd/.codex.mcp.json +1 -1
  42. package/plugins/pr-shepherd/.mcp.json +1 -1
  43. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +8 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
3
  "description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
4
- "version": "0.41.0",
4
+ "version": "0.42.0",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
7
7
  "email": "jonathanrichardong@gmail.com"
package/README.md CHANGED
@@ -9,14 +9,14 @@ An agent finishing a PR should think about code, not reconstruct GitHub state or
9
9
  ## What it does
10
10
 
11
11
  1. **Gather all context for a PR** in one invocation: review threads, comments, replies, summaries, CI, mergeability, merge requirements, first-look / outdated / edited items, and author provenance.
12
- 2. **Provide deterministic actions for the agent**: exactly one of `WAIT`, `MARK_READY`, `FIX_CODE`, `CANCEL`, or `ESCALATE`, plus numbered `## Instructions` and explicit `apply` / `build_suggestion_patch` mutations. The agent still decides whether a comment or CI failure needs a code change. Shepherd does not classify signal vs noise and does not mutate git.
12
+ 2. **Provide deterministic actions for the agent**: exactly one of `WAIT`, `MARK_READY`, `FIX_CODE`, `CANCEL`, or `ESCALATE`, plus numbered `## Instructions` and explicit `apply` / `build_suggestion_patches` operations. The agent still decides whether a comment or CI failure needs a code change. Shepherd does not classify signal vs noise and does not mutate git.
13
13
 
14
14
  Highlights:
15
15
 
16
16
  - Batched GraphQL reads and writes (plus REST where GraphQL cannot) so one poll replaces a tool-call fan-out. MCP `iterate` is one tick and the client owns recurrence; `--debounce` is a poll-dispatcher settle window, not an MCP tool.
17
17
  - CI summaries include failed checks, and the failed job/step plus a log excerpt when triage can fetch them. Job and log details are omitted for `STARTUP_FAILURE` and `CANCELLED`; agents may still inspect logs.
18
18
  - Handles GitHub comment types (comments, threads, replies) and their states, including first-look, outdated, resolved, minimized, and edited.
19
- - `apply` batches resolve / reply / minimize / dismiss. `build_suggestion_patch` emits a unified diff in output, not a patch file, and does not mutate git.
19
+ - `apply` batches resolve / reply / minimize / dismiss. `build_suggestion_patches` validates and returns ordered diffs without mutating git.
20
20
  - `BEHIND` is mergeability information, not a rebase or a guarantee that the next push is at the default-branch tip. The agent can update the branch before pushing.
21
21
 
22
22
  Full reference: [docs/README.md](docs/README.md). Feature matrix: [docs/features.md](docs/features.md).
@@ -25,7 +25,7 @@ Full reference: [docs/README.md](docs/README.md). Feature matrix: [docs/features
25
25
 
26
26
  `pr-shepherd` moves deterministic PR orchestration into a local MCP server, with a CLI for shells and CI. Both interfaces fetch the same GitHub state, emit raw-enough context, and return a numbered plan for the calling agent to follow.
27
27
 
28
- The MCP server exposes three tools: `iterate`, `apply`, and `build_suggestion_patch`. `apply` accepts ordered review mutations, file-view mutations, and journal entries. Direct MCP calls require a repository-qualified `pr`: a GitHub PR URL or `owner/repo#N`, matching the repository where the server started. The CLI and programmatic API also retain bare-number and current-branch PR discovery. The shipped skills are thin dispatchers for those tools.
28
+ The MCP server exposes canonical `iterate`, `apply`, and `build_suggestion_patches` tools. `apply` accepts ordered review mutations, file-view mutations, and journal entries; the deprecated singular suggestion tool remains temporarily as an adapter. Direct MCP calls require a repository-qualified `pr`: a GitHub PR URL or `owner/repo#N`, matching the repository where the server started. The CLI and programmatic API also retain bare-number and current-branch PR discovery. The shipped skills are thin dispatchers for those tools.
29
29
 
30
30
  Each tick returns exactly one action:
31
31
 
@@ -88,7 +88,7 @@ This system is opinionated and works best with PRs that use required status chec
88
88
  - Every review thread/comment/review summary is surfaced at least once, even if already outdated, resolved, or minimized; edited items re-surface through seen markers.
89
89
  - Draft PRs can be marked ready automatically when clean; disable with `actions.autoMarkReady: false` or `--no-auto-mark-ready`.
90
90
  - The CLI never performs git mutations. It emits instructions; the caller commits, rebases, pushes, and handles repository hooks.
91
- - `build_suggestion_patch` turns one GitHub suggestion thread into a patch and commit metadata, but never edits the working tree or git history.
91
+ - `build_suggestion_patches` turns one or more ordered GitHub suggestion threads into checked patches and commit metadata, but never edits the working tree or git history. Local HEAD may be ahead when the live PR head is its ancestor.
92
92
 
93
93
  ## Usage
94
94
 
@@ -115,7 +115,7 @@ Grok:
115
115
  /pr-shepherd 42
116
116
  ```
117
117
 
118
- MCP clients call `iterate` once per tick, then use `apply` for review/file/journal mutations and `build_suggestion_patch` for an anchored suggestion. Every direct MCP call supplies the same repository-qualified PR reference. `iterate` returns the same structured action data as the CLI, including its review mutation arguments. The client owns recurrence, so this works consistently in Codex, Claude Code, Grok, and any other stdio MCP client.
118
+ MCP clients call `iterate` once per tick, then use `apply` for review/file/journal mutations and `build_suggestion_patches` for anchored suggestions. Every direct MCP call supplies the same repository-qualified PR reference. `iterate` returns the same structured action data as the CLI, including its review mutation arguments. The client owns recurrence, so this works consistently in Codex, Claude Code, Grok, and any other stdio MCP client.
119
119
 
120
120
  The CLI remains useful for shell workflows. Its canonical polling form is:
121
121
 
@@ -131,7 +131,7 @@ pr-shepherd iterate 42 # single tick
131
131
 
132
132
  ### Apply Review, File, And Journal Changes
133
133
 
134
- Use `apply` with ordered operations to reply/resolve/minimize/dismiss review items, mark changed files viewed, or append an idempotent Shepherd Journal item. Use `build_suggestion_patch` to turn one review suggestion into a validated patch and commit metadata; it never changes the worktree or git history.
134
+ Use `apply` with ordered operations to reply/resolve/minimize/dismiss review items, mark changed files viewed, or append an idempotent Shepherd Journal item. Use `build_suggestion_patches` to turn ordered review suggestions into checked patches and commit metadata; it never changes the worktree or git history.
135
135
 
136
136
  ### Extract Shepherd Journal Entries
137
137
 
@@ -276,7 +276,7 @@ Ready-to-use examples for common patterns are in [`examples/classification/`](ex
276
276
 
277
277
  ## CLI aliases
278
278
 
279
- `poll`, `resolve`, `commit-suggestion`, `mark-files-as-viewed`, `journal`, `clean`, and `log-file` are CLI aliases. Prefer default polling/`iterate` in a shell and the MCP `iterate`, `apply`, and `build_suggestion_patch` tools in an agent client.
279
+ `poll`, `resolve`, `build-suggestion-patch`, `commit-suggestion`, `mark-files-as-viewed`, `journal`, `clean`, and `log-file` are CLI aliases or deprecated adapters. Prefer default polling/`iterate` in a shell and the MCP `iterate`, `apply`, and `build_suggestion_patches` tools in an agent client.
280
280
 
281
281
  ## Requirements
282
282
 
package/bin/api.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type JournalResult } from "./commands/journal/index.mts";
2
2
  import { type MarkFilesAsViewedResult } from "./commands/mark-files-as-viewed.mts";
3
3
  import type { ResolveResult } from "./comments/resolve.mts";
4
- import type { CommitSuggestionResult, IterateCommandOptions, IterateResult } from "./types.mts";
4
+ import type { BuildSuggestionPatchesResult, CommitSuggestionResult, IterateCommandOptions, IterateResult } from "./types.mts";
5
5
  export interface CreatePrShepherdOptions {
6
6
  /** Working directory used for git, config, and classification-rule lookups. */
7
7
  cwd?: string;
@@ -58,9 +58,20 @@ export interface BuildSuggestionPatchInput {
58
58
  message: string;
59
59
  description?: string;
60
60
  }
61
+ export interface SuggestionPatchInput {
62
+ threadId: string;
63
+ message: string;
64
+ description?: string;
65
+ }
66
+ export interface BuildSuggestionPatchesInput {
67
+ pr?: PrReference;
68
+ suggestions: SuggestionPatchInput[];
69
+ }
61
70
  export interface PrShepherd {
62
71
  iterate(input?: IterateInput): Promise<IterateResult>;
63
72
  apply(input: ApplyInput): Promise<ApplyResult>;
73
+ buildSuggestionPatches(input: BuildSuggestionPatchesInput): Promise<BuildSuggestionPatchesResult>;
74
+ /** Compatibility adapter; prefer buildSuggestionPatches. */
64
75
  buildSuggestionPatch(input: BuildSuggestionPatchInput): Promise<CommitSuggestionResult>;
65
76
  }
66
77
  /** Raised before any API mutation when an input cannot be validated. */
package/bin/api.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable max-lines */
2
2
  import { resolve } from "node:path";
3
3
  import { runCommitSuggestion } from "./commands/commit-suggestion.mjs";
4
+ import { runSuggestionPatches } from "./commands/suggestion-patches.mjs";
4
5
  import { runIterate } from "./commands/iterate/index.mjs";
5
6
  import { runJournal } from "./commands/journal/index.mjs";
6
7
  import { validateJournalItem } from "./commands/journal/transform.mjs";
@@ -101,6 +102,14 @@ export function createPrShepherd(options = {}) {
101
102
  return runCommitSuggestion({ ...options, prNumber, format: "json" });
102
103
  });
103
104
  },
105
+ buildSuggestionPatches(input) {
106
+ validateSuggestionPatchesInput(input);
107
+ const { pr: _pr, ...options } = input;
108
+ return runWithExecutionCwd(cwd, async () => {
109
+ const prNumber = await resolvePrReference(input.pr);
110
+ return runSuggestionPatches({ ...options, prNumber, format: "json" });
111
+ });
112
+ },
104
113
  });
105
114
  }
106
115
  function validateApplyInput(input) {
@@ -184,7 +193,7 @@ function validateMarkFilesViewed(operation) {
184
193
  }
185
194
  }
186
195
  function validateSuggestionPatchInput(input) {
187
- if (!input || typeof input.threadId !== "string" || input.threadId === "") {
196
+ if (!input || typeof input.threadId !== "string" || input.threadId.trim() === "") {
188
197
  throw new PrShepherdValidationError("buildSuggestionPatch.threadId is required");
189
198
  }
190
199
  if (typeof input.message !== "string" || input.message.trim() === "") {
@@ -195,6 +204,30 @@ function validateSuggestionPatchInput(input) {
195
204
  }
196
205
  validatePrReference(input.pr);
197
206
  }
207
+ function validateSuggestionPatchesInput(input) {
208
+ if (!input || !Array.isArray(input.suggestions) || input.suggestions.length === 0) {
209
+ throw new PrShepherdValidationError("buildSuggestionPatches.suggestions must be a non-empty array");
210
+ }
211
+ const seen = new Set();
212
+ for (const suggestion of input.suggestions) {
213
+ if (!suggestion ||
214
+ typeof suggestion.threadId !== "string" ||
215
+ suggestion.threadId.trim() === "") {
216
+ throw new PrShepherdValidationError("each suggestion.threadId is required");
217
+ }
218
+ if (typeof suggestion.message !== "string" || suggestion.message.trim() === "") {
219
+ throw new PrShepherdValidationError("each suggestion.message must be non-empty");
220
+ }
221
+ if (suggestion.description !== undefined && typeof suggestion.description !== "string") {
222
+ throw new PrShepherdValidationError("each suggestion.description must be a string");
223
+ }
224
+ if (seen.has(suggestion.threadId)) {
225
+ throw new PrShepherdValidationError(`duplicate suggestion thread ID: ${suggestion.threadId}`);
226
+ }
227
+ seen.add(suggestion.threadId);
228
+ }
229
+ validatePrReference(input.pr);
230
+ }
198
231
  function validatePrReference(pr) {
199
232
  const parsed = parsePrReference(pr);
200
233
  if (parsed !== null)
@@ -3,5 +3,6 @@ export { projectIterateLean, projectIterateVerbose } from "./iterate-lean.mts";
3
3
  export { formatCleanResult } from "./clean-formatter.mts";
4
4
  export { formatMarkFilesAsViewedResult } from "./mark-files-as-viewed-formatter.mts";
5
5
  export { formatMutateResult } from "./mutate-formatter.mts";
6
- import type { CommitSuggestionResult } from "../types.mts";
6
+ import type { BuildSuggestionPatchesResult, CommitSuggestionResult } from "../types.mts";
7
+ export declare function formatSuggestionPatchesResult(result: BuildSuggestionPatchesResult): string;
7
8
  export declare function formatCommitSuggestionResult(result: CommitSuggestionResult): string;
@@ -4,6 +4,14 @@ export { formatCleanResult } from "./clean-formatter.mjs";
4
4
  export { formatMarkFilesAsViewedResult } from "./mark-files-as-viewed-formatter.mjs";
5
5
  export { formatMutateResult } from "./mutate-formatter.mjs";
6
6
  import { safeFence } from "./fence.mjs";
7
+ export function formatSuggestionPatchesResult(result) {
8
+ const lines = [`Suggestion patches for PR #${result.pr}:`, ` repo: ${result.repo}`];
9
+ result.patches.forEach((patch, index) => {
10
+ lines.push("", ...formatPatch(patch, `## Patch ${index + 1}`).split("\n"));
11
+ });
12
+ appendInstructions(lines, result.postActionInstructions);
13
+ return lines.join("\n");
14
+ }
7
15
  export function formatCommitSuggestionResult(result) {
8
16
  const lines = [];
9
17
  const range = result.startLine === result.endLine
@@ -30,3 +38,28 @@ export function formatCommitSuggestionResult(result) {
30
38
  }
31
39
  return lines.join("\n");
32
40
  }
41
+ function formatPatch(patch, heading) {
42
+ const range = patch.startLine === patch.endLine
43
+ ? `line ${patch.startLine}`
44
+ : `lines ${patch.startLine}–${patch.endLine}`;
45
+ const lines = [
46
+ heading,
47
+ "",
48
+ `Suggestion from @${patch.author} — thread ${patch.threadId}:`,
49
+ ` ${patch.path} (${range})`,
50
+ ];
51
+ if (patch.patch) {
52
+ const fence = safeFence(patch.patch);
53
+ lines.push("", `${fence}diff`, patch.patch.trimEnd(), fence);
54
+ }
55
+ lines.push("", "### Suggested commit message", "", patch.commitMessage, "", patch.commitBody);
56
+ return lines.join("\n");
57
+ }
58
+ function appendInstructions(lines, instructions) {
59
+ if (instructions.length === 0)
60
+ return;
61
+ lines.push("", "## Instructions", "");
62
+ instructions.forEach((instruction, index) => {
63
+ lines.push(`${index + 1}. ${instruction}`);
64
+ });
65
+ }
@@ -1,4 +1,5 @@
1
1
  export declare function handleClean(args: string[], command?: "admin clean" | "clean"): Promise<void>;
2
2
  export declare function handleCommitSuggestion(args: string[], command?: "build-suggestion-patch" | "commit-suggestion"): Promise<void>;
3
+ export declare function handleSuggestionPatches(args: string[]): Promise<void>;
3
4
  export declare function handleIterate(args: string[]): Promise<void>;
4
5
  export declare function handleMarkFilesAsViewed(args: string[], command?: "apply files" | "mark-files-as-viewed"): Promise<void>;
@@ -1,4 +1,5 @@
1
1
  import { runCommitSuggestion } from "../commands/commit-suggestion.mjs";
2
+ import { runSuggestionPatches } from "../commands/suggestion-patches.mjs";
2
3
  import { runMarkFilesAsViewed } from "../commands/mark-files-as-viewed.mjs";
3
4
  import { runIterate } from "../commands/iterate/index.mjs";
4
5
  import { runClean } from "../commands/clean.mjs";
@@ -6,7 +7,8 @@ import { loadConfig } from "../config/load.mjs";
6
7
  import { EXIT } from "../exit-codes.mjs";
7
8
  import { parseCommonArgs, getFlag } from "./args.mjs";
8
9
  import { USAGE } from "./help.mjs";
9
- import { formatCommitSuggestionResult, formatCleanResult, formatMarkFilesAsViewedResult, } from "./formatters.mjs";
10
+ import { formatCommitSuggestionResult, formatSuggestionPatchesResult, formatCleanResult, formatMarkFilesAsViewedResult, } from "./formatters.mjs";
11
+ import { parseSuggestionPatchGroups } from "./suggestion-patch-flags.mjs";
10
12
  import { parseIterateFlags } from "./iterate-flags.mjs";
11
13
  import { emitIterateResult } from "./iterate-emitter.mjs";
12
14
  import { parseMarkFilesAsViewedArgs } from "./mark-files-as-viewed-flags.mjs";
@@ -92,6 +94,23 @@ export async function handleCommitSuggestion(args, command = "build-suggestion-p
92
94
  ? `${JSON.stringify(result, null, 2)}\n`
93
95
  : `${formatCommitSuggestionResult(result)}\n`);
94
96
  }
97
+ export async function handleSuggestionPatches(args) {
98
+ const { prNumber, global: globalOpts, extra } = parseCommonArgs(args);
99
+ const parsed = parseSuggestionPatchGroups(extra);
100
+ if (!parsed.ok) {
101
+ process.stderr.write(`${parsed.error}\n${USAGE["build-suggestion-patches"]}\n`);
102
+ process.exitCode = EXIT.USAGE;
103
+ return;
104
+ }
105
+ const result = await runSuggestionPatches({
106
+ ...globalOpts,
107
+ prNumber,
108
+ suggestions: parsed.suggestions,
109
+ });
110
+ process.stdout.write(globalOpts.format === "json"
111
+ ? `${JSON.stringify(result, null, 2)}\n`
112
+ : `${formatSuggestionPatchesResult(result)}\n`);
113
+ }
95
114
  export async function handleIterate(args) {
96
115
  const { prNumber, global: globalOpts, extra } = parseCommonArgs(args);
97
116
  const cfg = loadConfig();
@@ -61,11 +61,27 @@ PR may be a number or GitHub pull request URL. An item must start with '- ' foll
61
61
  Use --file to read an item from a file, or --file - to read it from stdin. Exactly one item source
62
62
  is required. --dry-run previews the resulting body without writing it.
63
63
  --help, -h Print this help and exit before GitHub I/O.`;
64
- readonly "build-suggestion-patch": `pr-shepherd build-suggestion-patch
64
+ readonly "build-suggestion-patches": `pr-shepherd build-suggestion-patches
65
65
 
66
- Build a patch and commit instructions for one GitHub review thread containing a suggestion block.
66
+ Build an ordered list of patches and commit instructions from GitHub review suggestions.
67
67
  The command does not edit files or mutate git history.
68
68
 
69
+ Usage:
70
+ pr-shepherd build-suggestion-patches [PR]
71
+ --thread-id ID --message MSG [--description DESC]
72
+ [--thread-id ID --message MSG [--description DESC] ...]
73
+ [--format text|json]
74
+
75
+ Each --thread-id starts a suggestion group. Its following --message and optional --description
76
+ belong to that suggestion. Groups are validated and returned in command-line order.
77
+
78
+ The current branch must match the PR head ref. Local HEAD may equal or descend from the PR head;
79
+ every generated patch is dry-run in order with git apply --check before output.
80
+ --help, -h Print this help and exit before GitHub, git, config, or log I/O.`;
81
+ readonly "build-suggestion-patch": `pr-shepherd build-suggestion-patch
82
+
83
+ Deprecated one-suggestion adapter. Use build-suggestion-patches.
84
+
69
85
  Usage:
70
86
  pr-shepherd build-suggestion-patch [PR] --thread-id ID --message MSG
71
87
  [--description DESC] [--format text|json]
@@ -76,7 +92,7 @@ Flags:
76
92
  --description <text> Optional longer commit body.
77
93
  --format text|json Output format. Default: text.
78
94
 
79
- The current branch must match the PR head ref and local HEAD must match the PR head SHA.
95
+ The current branch must match the PR head ref. Local HEAD may equal or descend from the PR head.
80
96
  --help, -h Print this help and exit before GitHub, git, config, or log I/O.`;
81
97
  readonly admin: `pr-shepherd admin
82
98
 
@@ -149,7 +165,7 @@ Flags:
149
165
  --help, -h Print this help and exit before GitHub, git, config, or log I/O.
150
166
 
151
167
  Preconditions:
152
- The current branch must match the PR head ref, and local HEAD must match the PR head SHA.
168
+ The current branch must match the PR head ref. Local HEAD may equal or descend from the PR head.
153
169
 
154
170
  Exit codes:
155
171
  0 suggestion patch and instructions produced
@@ -64,11 +64,27 @@ PR may be a number or GitHub pull request URL. An item must start with '- ' foll
64
64
  Use --file to read an item from a file, or --file - to read it from stdin. Exactly one item source
65
65
  is required. --dry-run previews the resulting body without writing it.
66
66
  --help, -h Print this help and exit before GitHub I/O.`,
67
- "build-suggestion-patch": `pr-shepherd build-suggestion-patch
67
+ "build-suggestion-patches": `pr-shepherd build-suggestion-patches
68
68
 
69
- Build a patch and commit instructions for one GitHub review thread containing a suggestion block.
69
+ Build an ordered list of patches and commit instructions from GitHub review suggestions.
70
70
  The command does not edit files or mutate git history.
71
71
 
72
+ Usage:
73
+ pr-shepherd build-suggestion-patches [PR]
74
+ --thread-id ID --message MSG [--description DESC]
75
+ [--thread-id ID --message MSG [--description DESC] ...]
76
+ [--format text|json]
77
+
78
+ Each --thread-id starts a suggestion group. Its following --message and optional --description
79
+ belong to that suggestion. Groups are validated and returned in command-line order.
80
+
81
+ The current branch must match the PR head ref. Local HEAD may equal or descend from the PR head;
82
+ every generated patch is dry-run in order with git apply --check before output.
83
+ --help, -h Print this help and exit before GitHub, git, config, or log I/O.`,
84
+ "build-suggestion-patch": `pr-shepherd build-suggestion-patch
85
+
86
+ Deprecated one-suggestion adapter. Use build-suggestion-patches.
87
+
72
88
  Usage:
73
89
  pr-shepherd build-suggestion-patch [PR] --thread-id ID --message MSG
74
90
  [--description DESC] [--format text|json]
@@ -79,7 +95,7 @@ Flags:
79
95
  --description <text> Optional longer commit body.
80
96
  --format text|json Output format. Default: text.
81
97
 
82
- The current branch must match the PR head ref and local HEAD must match the PR head SHA.
98
+ The current branch must match the PR head ref. Local HEAD may equal or descend from the PR head.
83
99
  --help, -h Print this help and exit before GitHub, git, config, or log I/O.`,
84
100
  admin: `pr-shepherd admin
85
101
 
@@ -152,7 +168,7 @@ Flags:
152
168
  --help, -h Print this help and exit before GitHub, git, config, or log I/O.
153
169
 
154
170
  Preconditions:
155
- The current branch must match the PR head ref, and local HEAD must match the PR head SHA.
171
+ The current branch must match the PR head ref. Local HEAD may equal or descend from the PR head.
156
172
 
157
173
  Exit codes:
158
174
  0 suggestion patch and instructions produced
@@ -1 +1 @@
1
- export declare const TOP_USAGE = "pr-shepherd\n\nAutonomous PR CI monitor and review-comment resolver for agentic coding tools.\n\nUsage:\n pr-shepherd --version | -v\n pr-shepherd --help | -h\n pr-shepherd [PR] [poll-flags] [iterate-flags]\n pr-shepherd iterate [PR] [iterate-flags]\n pr-shepherd apply review [PR] [review-flags]\n pr-shepherd apply files [PR] [files...] [--tests] [--match REGEX]\n pr-shepherd apply journal [PR] <item> [--dry-run] [--format text|json]\n pr-shepherd build-suggestion-patch [PR] --thread-id ID --message MSG [flags]\n pr-shepherd admin clean <pr|branch|current|repo|all> [value] [flags]\n pr-shepherd admin log-file [--format text|json]\n\nCommands:\n [PR] Poll until non-WAIT or timeout. This is the default command.\n iterate Run one iterate tick (single-tick alias).\n apply review Apply review-state mutations after fixes.\n apply files Mark changed files as viewed in GitHub.\n apply journal Append a list item to the Shepherd Journal details block of a PR body.\n build-suggestion-patch\n Convert one GitHub suggestion thread into a patch and commit instructions.\n admin clean Remove pr-shepherd state files.\n admin log-file Print the per-worktree debug log path.\n\nPR argument:\n PR may be a number such as 42 or a GitHub pull request URL.\n When omitted, pr-shepherd infers the current branch's pull request.\n\nCommon flags:\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields and detailed poll-tick lines.\n --help, -h Print help and exit before any GitHub, git, config, or log I/O.\n\nIterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Do not cancel in-progress runs before actionable fixes.\n\nPolling flags:\n --interval <duration> Delay between WAIT ticks. Bare number = seconds. Default: 60s.\n --timeout <duration> Poll wall-clock cap for WAIT ticks. Bare number = seconds. Default: 4.5m.\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.\n --quiet-status During WAIT polling, print only changed status snapshots.\n --until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.\n\nClean variants:\n pr [number] Remove state for one PR. Defaults to current branch PR.\n branch [name] Remove state for a branch's PR. Defaults to current branch.\n current Alias for branch against the current branch.\n repo Remove all state for the current repository.\n all Remove all pr-shepherd state.\n\nExit codes: 0 done, 10-19 PR state, 64-78 shepherd failed (sysexits.h).\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\nSee docs/exit-codes.md for the full sysexits.h error-code table.\n\nDuration examples: 30s, 4.5m, 1h. A bare number uses each flag's default unit (see above); decimals are allowed with an explicit unit (4.5m).\n\nRun 'pr-shepherd <command> --help' for command-specific details.";
1
+ export declare const TOP_USAGE = "pr-shepherd\n\nAutonomous PR CI monitor and review-comment resolver for agentic coding tools.\n\nUsage:\n pr-shepherd --version | -v\n pr-shepherd --help | -h\n pr-shepherd [PR] [poll-flags] [iterate-flags]\n pr-shepherd iterate [PR] [iterate-flags]\n pr-shepherd apply review [PR] [review-flags]\n pr-shepherd apply files [PR] [files...] [--tests] [--match REGEX]\n pr-shepherd apply journal [PR] <item> [--dry-run] [--format text|json]\n pr-shepherd build-suggestion-patches [PR] --thread-id ID --message MSG [groups...]\n pr-shepherd admin clean <pr|branch|current|repo|all> [value] [flags]\n pr-shepherd admin log-file [--format text|json]\n\nCommands:\n [PR] Poll until non-WAIT or timeout. This is the default command.\n iterate Run one iterate tick (single-tick alias).\n apply review Apply review-state mutations after fixes.\n apply files Mark changed files as viewed in GitHub.\n apply journal Append a list item to the Shepherd Journal details block of a PR body.\n build-suggestion-patches\n Convert ordered GitHub suggestion threads into patches and commit instructions.\n admin clean Remove pr-shepherd state files.\n admin log-file Print the per-worktree debug log path.\n\nPR argument:\n PR may be a number such as 42 or a GitHub pull request URL.\n When omitted, pr-shepherd infers the current branch's pull request.\n\nCommon flags:\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields and detailed poll-tick lines.\n --help, -h Print help and exit before any GitHub, git, config, or log I/O.\n\nIterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Do not cancel in-progress runs before actionable fixes.\n\nPolling flags:\n --interval <duration> Delay between WAIT ticks. Bare number = seconds. Default: 60s.\n --timeout <duration> Poll wall-clock cap for WAIT ticks. Bare number = seconds. Default: 4.5m.\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.\n --quiet-status During WAIT polling, print only changed status snapshots.\n --until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.\n\nClean variants:\n pr [number] Remove state for one PR. Defaults to current branch PR.\n branch [name] Remove state for a branch's PR. Defaults to current branch.\n current Alias for branch against the current branch.\n repo Remove all state for the current repository.\n all Remove all pr-shepherd state.\n\nExit codes: 0 done, 10-19 PR state, 64-78 shepherd failed (sysexits.h).\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\nSee docs/exit-codes.md for the full sysexits.h error-code table.\n\nDuration examples: 30s, 4.5m, 1h. A bare number uses each flag's default unit (see above); decimals are allowed with an explicit unit (4.5m).\n\nRun 'pr-shepherd <command> --help' for command-specific details.";
@@ -10,7 +10,7 @@ Usage:
10
10
  pr-shepherd apply review [PR] [review-flags]
11
11
  pr-shepherd apply files [PR] [files...] [--tests] [--match REGEX]
12
12
  pr-shepherd apply journal [PR] <item> [--dry-run] [--format text|json]
13
- pr-shepherd build-suggestion-patch [PR] --thread-id ID --message MSG [flags]
13
+ pr-shepherd build-suggestion-patches [PR] --thread-id ID --message MSG [groups...]
14
14
  pr-shepherd admin clean <pr|branch|current|repo|all> [value] [flags]
15
15
  pr-shepherd admin log-file [--format text|json]
16
16
 
@@ -20,8 +20,8 @@ Commands:
20
20
  apply review Apply review-state mutations after fixes.
21
21
  apply files Mark changed files as viewed in GitHub.
22
22
  apply journal Append a list item to the Shepherd Journal details block of a PR body.
23
- build-suggestion-patch
24
- Convert one GitHub suggestion thread into a patch and commit instructions.
23
+ build-suggestion-patches
24
+ Convert ordered GitHub suggestion threads into patches and commit instructions.
25
25
  admin clean Remove pr-shepherd state files.
26
26
  admin log-file Print the per-worktree debug log path.
27
27
 
@@ -61,11 +61,27 @@ PR may be a number or GitHub pull request URL. An item must start with '- ' foll
61
61
  Use --file to read an item from a file, or --file - to read it from stdin. Exactly one item source
62
62
  is required. --dry-run previews the resulting body without writing it.
63
63
  --help, -h Print this help and exit before GitHub I/O.`;
64
- readonly "build-suggestion-patch": `pr-shepherd build-suggestion-patch
64
+ readonly "build-suggestion-patches": `pr-shepherd build-suggestion-patches
65
65
 
66
- Build a patch and commit instructions for one GitHub review thread containing a suggestion block.
66
+ Build an ordered list of patches and commit instructions from GitHub review suggestions.
67
67
  The command does not edit files or mutate git history.
68
68
 
69
+ Usage:
70
+ pr-shepherd build-suggestion-patches [PR]
71
+ --thread-id ID --message MSG [--description DESC]
72
+ [--thread-id ID --message MSG [--description DESC] ...]
73
+ [--format text|json]
74
+
75
+ Each --thread-id starts a suggestion group. Its following --message and optional --description
76
+ belong to that suggestion. Groups are validated and returned in command-line order.
77
+
78
+ The current branch must match the PR head ref. Local HEAD may equal or descend from the PR head;
79
+ every generated patch is dry-run in order with git apply --check before output.
80
+ --help, -h Print this help and exit before GitHub, git, config, or log I/O.`;
81
+ readonly "build-suggestion-patch": `pr-shepherd build-suggestion-patch
82
+
83
+ Deprecated one-suggestion adapter. Use build-suggestion-patches.
84
+
69
85
  Usage:
70
86
  pr-shepherd build-suggestion-patch [PR] --thread-id ID --message MSG
71
87
  [--description DESC] [--format text|json]
@@ -76,7 +92,7 @@ Flags:
76
92
  --description <text> Optional longer commit body.
77
93
  --format text|json Output format. Default: text.
78
94
 
79
- The current branch must match the PR head ref and local HEAD must match the PR head SHA.
95
+ The current branch must match the PR head ref. Local HEAD may equal or descend from the PR head.
80
96
  --help, -h Print this help and exit before GitHub, git, config, or log I/O.`;
81
97
  readonly admin: `pr-shepherd admin
82
98
 
@@ -149,7 +165,7 @@ Flags:
149
165
  --help, -h Print this help and exit before GitHub, git, config, or log I/O.
150
166
 
151
167
  Preconditions:
152
- The current branch must match the PR head ref, and local HEAD must match the PR head SHA.
168
+ The current branch must match the PR head ref. Local HEAD may equal or descend from the PR head.
153
169
 
154
170
  Exit codes:
155
171
  0 suggestion patch and instructions produced
@@ -228,7 +244,7 @@ Flags:
228
244
 
229
245
  Exit code: 0 on success (including no-change no-op); nonzero on failure (sysexits.h — see docs/exit-codes.md).`;
230
246
  readonly "log-file": "pr-shepherd log-file\n\nPrint the per-worktree append-only debug log path for the current repository.\nThe log is created by the first non-help pr-shepherd command that initializes logging.\n\nUsage:\n pr-shepherd log-file [--format text|json]\n\nFlags:\n --format text|json Print a raw path or {\"path\": \"...\"} JSON. Default: text.\n --help, -h Print this help and exit before logging setup.\n\nEnvironment:\n PR_SHEPHERD_LOG_DISABLED=1 disables logging.\n PR_SHEPHERD_STATE_DIR overrides the base state directory.\n\nExit code: 0 on success; 1 if repository identity cannot be resolved.";
231
- readonly top: "pr-shepherd\n\nAutonomous PR CI monitor and review-comment resolver for agentic coding tools.\n\nUsage:\n pr-shepherd --version | -v\n pr-shepherd --help | -h\n pr-shepherd [PR] [poll-flags] [iterate-flags]\n pr-shepherd iterate [PR] [iterate-flags]\n pr-shepherd apply review [PR] [review-flags]\n pr-shepherd apply files [PR] [files...] [--tests] [--match REGEX]\n pr-shepherd apply journal [PR] <item> [--dry-run] [--format text|json]\n pr-shepherd build-suggestion-patch [PR] --thread-id ID --message MSG [flags]\n pr-shepherd admin clean <pr|branch|current|repo|all> [value] [flags]\n pr-shepherd admin log-file [--format text|json]\n\nCommands:\n [PR] Poll until non-WAIT or timeout. This is the default command.\n iterate Run one iterate tick (single-tick alias).\n apply review Apply review-state mutations after fixes.\n apply files Mark changed files as viewed in GitHub.\n apply journal Append a list item to the Shepherd Journal details block of a PR body.\n build-suggestion-patch\n Convert one GitHub suggestion thread into a patch and commit instructions.\n admin clean Remove pr-shepherd state files.\n admin log-file Print the per-worktree debug log path.\n\nPR argument:\n PR may be a number such as 42 or a GitHub pull request URL.\n When omitted, pr-shepherd infers the current branch's pull request.\n\nCommon flags:\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields and detailed poll-tick lines.\n --help, -h Print help and exit before any GitHub, git, config, or log I/O.\n\nIterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Do not cancel in-progress runs before actionable fixes.\n\nPolling flags:\n --interval <duration> Delay between WAIT ticks. Bare number = seconds. Default: 60s.\n --timeout <duration> Poll wall-clock cap for WAIT ticks. Bare number = seconds. Default: 4.5m.\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.\n --quiet-status During WAIT polling, print only changed status snapshots.\n --until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.\n\nClean variants:\n pr [number] Remove state for one PR. Defaults to current branch PR.\n branch [name] Remove state for a branch's PR. Defaults to current branch.\n current Alias for branch against the current branch.\n repo Remove all state for the current repository.\n all Remove all pr-shepherd state.\n\nExit codes: 0 done, 10-19 PR state, 64-78 shepherd failed (sysexits.h).\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\nSee docs/exit-codes.md for the full sysexits.h error-code table.\n\nDuration examples: 30s, 4.5m, 1h. A bare number uses each flag's default unit (see above); decimals are allowed with an explicit unit (4.5m).\n\nRun 'pr-shepherd <command> --help' for command-specific details.";
247
+ readonly top: "pr-shepherd\n\nAutonomous PR CI monitor and review-comment resolver for agentic coding tools.\n\nUsage:\n pr-shepherd --version | -v\n pr-shepherd --help | -h\n pr-shepherd [PR] [poll-flags] [iterate-flags]\n pr-shepherd iterate [PR] [iterate-flags]\n pr-shepherd apply review [PR] [review-flags]\n pr-shepherd apply files [PR] [files...] [--tests] [--match REGEX]\n pr-shepherd apply journal [PR] <item> [--dry-run] [--format text|json]\n pr-shepherd build-suggestion-patches [PR] --thread-id ID --message MSG [groups...]\n pr-shepherd admin clean <pr|branch|current|repo|all> [value] [flags]\n pr-shepherd admin log-file [--format text|json]\n\nCommands:\n [PR] Poll until non-WAIT or timeout. This is the default command.\n iterate Run one iterate tick (single-tick alias).\n apply review Apply review-state mutations after fixes.\n apply files Mark changed files as viewed in GitHub.\n apply journal Append a list item to the Shepherd Journal details block of a PR body.\n build-suggestion-patches\n Convert ordered GitHub suggestion threads into patches and commit instructions.\n admin clean Remove pr-shepherd state files.\n admin log-file Print the per-worktree debug log path.\n\nPR argument:\n PR may be a number such as 42 or a GitHub pull request URL.\n When omitted, pr-shepherd infers the current branch's pull request.\n\nCommon flags:\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields and detailed poll-tick lines.\n --help, -h Print help and exit before any GitHub, git, config, or log I/O.\n\nIterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Do not cancel in-progress runs before actionable fixes.\n\nPolling flags:\n --interval <duration> Delay between WAIT ticks. Bare number = seconds. Default: 60s.\n --timeout <duration> Poll wall-clock cap for WAIT ticks. Bare number = seconds. Default: 4.5m.\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.\n --quiet-status During WAIT polling, print only changed status snapshots.\n --until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.\n\nClean variants:\n pr [number] Remove state for one PR. Defaults to current branch PR.\n branch [name] Remove state for a branch's PR. Defaults to current branch.\n current Alias for branch against the current branch.\n repo Remove all state for the current repository.\n all Remove all pr-shepherd state.\n\nExit codes: 0 done, 10-19 PR state, 64-78 shepherd failed (sysexits.h).\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\nSee docs/exit-codes.md for the full sysexits.h error-code table.\n\nDuration examples: 30s, 4.5m, 1h. A bare number uses each flag's default unit (see above); decimals are allowed with an explicit unit (4.5m).\n\nRun 'pr-shepherd <command> --help' for command-specific details.";
232
248
  };
233
249
  /** Resolve help keys for nested public commands before any command I/O. */
234
250
  export declare function helpKeyForArgs(args: string[]): keyof typeof USAGE;
@@ -0,0 +1,11 @@
1
+ import type { SuggestionPatchRequest } from "../commands/suggestion-patch-item.mts";
2
+ type ParseFailure = {
3
+ ok: false;
4
+ error: string;
5
+ };
6
+ type ParseResult = {
7
+ ok: true;
8
+ suggestions: SuggestionPatchRequest[];
9
+ } | ParseFailure;
10
+ export declare function parseSuggestionPatchGroups(args: readonly string[]): ParseResult;
11
+ export {};
@@ -0,0 +1,83 @@
1
+ export function parseSuggestionPatchGroups(args) {
2
+ const parsed = parseFlags(args);
3
+ if (!parsed.ok)
4
+ return parsed;
5
+ return groupFlags(parsed.flags);
6
+ }
7
+ function parseFlags(args) {
8
+ const flags = [];
9
+ for (let index = 0; index < args.length;) {
10
+ const parsed = parseFlag(args, index);
11
+ if (!parsed)
12
+ return { ok: false, error: `Unknown argument: ${args[index]}` };
13
+ flags.push(parsed);
14
+ index += parsed.consumed;
15
+ }
16
+ return { ok: true, flags };
17
+ }
18
+ function groupFlags(flags) {
19
+ const suggestions = [];
20
+ let current = null;
21
+ for (const flag of flags) {
22
+ if (flag.name === "--thread-id") {
23
+ const finalized = finalize(current);
24
+ if (!finalized.ok)
25
+ return finalized;
26
+ if (finalized.suggestion)
27
+ suggestions.push(finalized.suggestion);
28
+ current = { threadId: flag.value };
29
+ continue;
30
+ }
31
+ const updated = setMetadata(current, flag);
32
+ if (!updated.ok)
33
+ return updated;
34
+ current = updated.current;
35
+ }
36
+ const finalized = finalize(current);
37
+ if (!finalized.ok)
38
+ return finalized;
39
+ if (finalized.suggestion)
40
+ suggestions.push(finalized.suggestion);
41
+ if (suggestions.length === 0)
42
+ return { ok: false, error: "At least one --thread-id is required." };
43
+ return { ok: true, suggestions };
44
+ }
45
+ function setMetadata(current, flag) {
46
+ if (!current)
47
+ return { ok: false, error: `${flag.name} must follow --thread-id.` };
48
+ const property = flag.name === "--message" ? "message" : "description";
49
+ if (current[property] !== undefined) {
50
+ return { ok: false, error: `${flag.name} may appear only once per suggestion.` };
51
+ }
52
+ return { ok: true, current: { ...current, [property]: flag.value } };
53
+ }
54
+ function parseFlag(args, index) {
55
+ const arg = args[index];
56
+ for (const name of ["--thread-id", "--message", "--description"]) {
57
+ if (arg.startsWith(`${name}=`)) {
58
+ return { name, value: arg.slice(name.length + 1), consumed: 1 };
59
+ }
60
+ if (arg === name && index + 1 < args.length) {
61
+ return { name, value: args[index + 1], consumed: 2 };
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+ function finalize(current) {
67
+ if (!current)
68
+ return { ok: true, suggestion: null };
69
+ if (!current.threadId || current.threadId.trim() === "") {
70
+ return { ok: false, error: "--thread-id must be non-empty." };
71
+ }
72
+ if (!current.message || current.message.trim() === "") {
73
+ return { ok: false, error: `--message is required for thread ${current.threadId}.` };
74
+ }
75
+ return {
76
+ ok: true,
77
+ suggestion: {
78
+ threadId: current.threadId,
79
+ message: current.message,
80
+ ...(current.description !== undefined && { description: current.description }),
81
+ },
82
+ };
83
+ }
@@ -8,7 +8,7 @@ import { parseCommonArgs, getFlag, hasFlag, parseList } from "./cli/args.mjs";
8
8
  import { isDefaultPollInvocation, validateDefaultPollArgs } from "./cli/default-poll.mjs";
9
9
  import { USAGE, helpKeyForArgs, maybePrintHelp } from "./cli/help.mjs";
10
10
  import { formatMutateResult } from "./cli/formatters.mjs";
11
- import { handleClean, handleCommitSuggestion, handleIterate, handleMarkFilesAsViewed, } from "./cli/handlers.mjs";
11
+ import { handleClean, handleCommitSuggestion, handleSuggestionPatches, handleIterate, handleMarkFilesAsViewed, } from "./cli/handlers.mjs";
12
12
  import { handleJournal } from "./cli/journal-handler.mjs";
13
13
  import { handlePoll } from "./cli/poll-handler.mjs";
14
14
  import { warnPrrcThreadIds, validateRequireSha, rejectPrrcMinimizeIds, } from "./cli/resolve-validators.mjs";
@@ -54,7 +54,8 @@ export async function main(argv) {
54
54
  resolve: "apply review",
55
55
  "mark-files-as-viewed": "apply files",
56
56
  journal: "apply journal",
57
- "commit-suggestion": "build-suggestion-patch",
57
+ "build-suggestion-patch": "build-suggestion-patches",
58
+ "commit-suggestion": "build-suggestion-patches",
58
59
  };
59
60
  if (subcommand !== undefined && legacyReplacement[subcommand] !== undefined) {
60
61
  warnLegacyAlias(subcommand, legacyReplacement[subcommand]);
@@ -71,6 +72,9 @@ export async function main(argv) {
71
72
  case "apply":
72
73
  await handleApply(args.slice(1));
73
74
  break;
75
+ case "build-suggestion-patches":
76
+ await handleSuggestionPatches(args.slice(1));
77
+ break;
74
78
  case "build-suggestion-patch":
75
79
  await handleCommitSuggestion(args.slice(1));
76
80
  break;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Build the `build-suggestion-patch` instruction step for agent consumers.
2
+ * Build the `build-suggestion-patches` instruction step for agent consumers.
3
3
  * Currently emitted by iterate `fix_code` for suggestion review threads. The CLI keeps
4
4
  * only the trigger and the concrete command; refusal/drift handling is invariant across
5
5
  * every invocation, so it lives in the pr-shepherd skill's "Suggestion patches" playbook
@@ -1,6 +1,6 @@
1
1
  import { buildPrShepherdCommand } from "../cli/runner.mjs";
2
2
  /**
3
- * Build the `build-suggestion-patch` instruction step for agent consumers.
3
+ * Build the `build-suggestion-patches` instruction step for agent consumers.
4
4
  * Currently emitted by iterate `fix_code` for suggestion review threads. The CLI keeps
5
5
  * only the trigger and the concrete command; refusal/drift handling is invariant across
6
6
  * every invocation, so it lives in the pr-shepherd skill's "Suggestion patches" playbook
@@ -11,7 +11,7 @@ import { buildPrShepherdCommand } from "../cli/runner.mjs";
11
11
  */
12
12
  export function buildCommitSuggestionInstruction(prNumber, sectionName) {
13
13
  const command = buildPrShepherdCommand([
14
- "build-suggestion-patch",
14
+ "build-suggestion-patches",
15
15
  String(prNumber),
16
16
  "--thread-id",
17
17
  "<id>",
@@ -19,5 +19,5 @@ export function buildCommitSuggestionInstruction(prNumber, sectionName) {
19
19
  "<one-sentence headline>",
20
20
  "--format=json",
21
21
  ]).text;
22
- return `For each thread marked \`[suggestion]\` under \`${sectionName}\`, run \`${command}\` and apply the returned patch. See "Suggestion patches" in the pr-shepherd skill for refusals and drift.`;
22
+ return `For all threads marked \`[suggestion]\` under \`${sectionName}\`, run one \`${command}\` command, repeating the \`--thread-id <id> --message <one-sentence headline>\` group in displayed order, then apply the returned patches in order. See "Suggestion patches" in the pr-shepherd skill for refusals and drift.`;
23
23
  }