pr-shepherd 0.24.0 → 0.25.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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -1
- package/bin/cli/args.mjs +0 -1
- package/bin/cli/formatters.mjs +0 -62
- package/bin/cli/help-command-pages.mjs +4 -10
- package/bin/cli/help-top-page.mjs +1 -1
- package/bin/cli-parser.mjs +28 -27
- package/bin/commands/commit-suggestion-instruction.mjs +3 -3
- package/bin/commands/resolve.mjs +0 -76
- package/bin/config.json +1 -2
- package/package.json +3 -3
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
- package/bin/commands/resolve-instructions.mjs +0 -62
package/README.md
CHANGED
|
@@ -97,10 +97,11 @@ pr-shepherd poll 42 # explicit poll command
|
|
|
97
97
|
### Resolve Review Items
|
|
98
98
|
|
|
99
99
|
```sh
|
|
100
|
-
pr-shepherd resolve 42 --fetch
|
|
101
100
|
pr-shepherd resolve 42 --reply-thread-ids PRRT_abc --message "Renamed the variable for clarity." --require-sha "$(git rev-parse HEAD)"
|
|
102
101
|
```
|
|
103
102
|
|
|
103
|
+
Use `pr-shepherd iterate 42` or `pr-shepherd 42` to fetch the next PR action. `resolve` requires at least one action flag and only applies explicit review-state mutations.
|
|
104
|
+
|
|
104
105
|
### Apply One Suggestion Thread
|
|
105
106
|
|
|
106
107
|
```sh
|
package/bin/cli/args.mjs
CHANGED
|
@@ -25,7 +25,6 @@ const FLAGS_WITH_VALUES = new Set([
|
|
|
25
25
|
// for PR-number detection — so removed flags don't silently cause their
|
|
26
26
|
// numeric value to be misidentified as the PR number.
|
|
27
27
|
const BOOLEAN_FLAGS = new Set([
|
|
28
|
-
"--fetch",
|
|
29
28
|
"--no-auto-mark-ready",
|
|
30
29
|
"--no-auto-cancel-actionable",
|
|
31
30
|
"--dry-run",
|
package/bin/cli/formatters.mjs
CHANGED
|
@@ -4,68 +4,6 @@ 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
|
-
import { renderThreadBullet, renderCommentBullet, renderReviewBullet, renderThreadResolutionStatusTag, buildFirstLookBullets, renderEditedCommentTag, } from "./list-formatters.mjs";
|
|
8
|
-
import { joinSections } from "../util/markdown.mjs";
|
|
9
|
-
export function formatFetchResult(result) {
|
|
10
|
-
const activeTotal = result.actionableThreads.length +
|
|
11
|
-
result.resolutionOnlyThreads.length +
|
|
12
|
-
result.actionableComments.length +
|
|
13
|
-
result.changesRequestedReviews.length +
|
|
14
|
-
result.reviewSummaries.length;
|
|
15
|
-
const firstLookTotal = result.firstLookThreads.length + result.firstLookComments.length;
|
|
16
|
-
const total = activeTotal + firstLookTotal;
|
|
17
|
-
const headingParts = [];
|
|
18
|
-
if (activeTotal > 0)
|
|
19
|
-
headingParts.push(`${activeTotal} actionable`);
|
|
20
|
-
if (firstLookTotal > 0)
|
|
21
|
-
headingParts.push(`${firstLookTotal} first-look`);
|
|
22
|
-
const headingSuffix = headingParts.length > 0 ? headingParts.join(", ") : "0 actionable";
|
|
23
|
-
const sections = [`# PR #${result.prNumber} — Resolve fetch (${headingSuffix})`];
|
|
24
|
-
if (result.actionableThreads.length > 0) {
|
|
25
|
-
sections.push(`## Actionable Review Threads (${result.actionableThreads.length})` +
|
|
26
|
-
(result.commitSuggestionsEnabled ? " [commit-suggestions: enabled]" : ""));
|
|
27
|
-
sections.push(result.actionableThreads
|
|
28
|
-
.map((t) => renderThreadBullet(t, { renderSuggestion: true }))
|
|
29
|
-
.join("\n\n"));
|
|
30
|
-
}
|
|
31
|
-
if (result.resolutionOnlyThreads.length > 0) {
|
|
32
|
-
sections.push(`## Review threads to resolve (${result.resolutionOnlyThreads.length})`);
|
|
33
|
-
sections.push(result.resolutionOnlyThreads
|
|
34
|
-
.map((t) => renderThreadBullet(t, { statusTag: renderThreadResolutionStatusTag(t) }))
|
|
35
|
-
.join("\n"));
|
|
36
|
-
}
|
|
37
|
-
if (result.actionableComments.length > 0) {
|
|
38
|
-
sections.push(`## Actionable PR Comments (${result.actionableComments.length})`);
|
|
39
|
-
sections.push(result.actionableComments
|
|
40
|
-
.map((c) => renderCommentBullet(c, { statusTag: renderEditedCommentTag(c) }))
|
|
41
|
-
.join("\n"));
|
|
42
|
-
}
|
|
43
|
-
if (result.changesRequestedReviews.length > 0) {
|
|
44
|
-
sections.push(`## Pending CHANGES_REQUESTED reviews (${result.changesRequestedReviews.length})`);
|
|
45
|
-
sections.push(result.changesRequestedReviews.map((r) => renderReviewBullet(r)).join("\n"));
|
|
46
|
-
}
|
|
47
|
-
if (result.reviewSummaries.length > 0) {
|
|
48
|
-
sections.push(`## Review summaries (${result.reviewSummaries.length})`);
|
|
49
|
-
sections.push(result.reviewSummaries.map((r) => renderReviewBullet(r, { includeBody: true })).join("\n"));
|
|
50
|
-
}
|
|
51
|
-
if (firstLookTotal > 0) {
|
|
52
|
-
sections.push(`## First-look items (${firstLookTotal}) — acknowledge status before acting`);
|
|
53
|
-
const resolutionOnlyIds = new Set(result.resolutionOnlyThreads.map((t) => t.id));
|
|
54
|
-
sections.push(buildFirstLookBullets(result.firstLookThreads, resolutionOnlyIds, result.firstLookComments).join("\n"));
|
|
55
|
-
}
|
|
56
|
-
sections.push("## Summary");
|
|
57
|
-
sections.push(total === 0
|
|
58
|
-
? "0 actionable, 0 first-look — all items seen"
|
|
59
|
-
: [
|
|
60
|
-
activeTotal > 0 ? `${activeTotal} actionable` : null,
|
|
61
|
-
firstLookTotal > 0 ? `${firstLookTotal} first-look` : null,
|
|
62
|
-
]
|
|
63
|
-
.filter(Boolean)
|
|
64
|
-
.join(", "));
|
|
65
|
-
sections.push("## Instructions");
|
|
66
|
-
sections.push(result.instructions.map((inst, i) => `${i + 1}. ${inst}`).join("\n"));
|
|
67
|
-
return joinSections(sections);
|
|
68
|
-
}
|
|
69
7
|
export function formatCommitSuggestionResult(result) {
|
|
70
8
|
const lines = [];
|
|
71
9
|
const range = result.startLine === result.endLine
|
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
export const COMMAND_USAGE = {
|
|
2
2
|
resolve: `pr-shepherd resolve
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Apply GitHub review-state mutations after fixes.
|
|
5
5
|
|
|
6
6
|
Usage:
|
|
7
|
-
pr-shepherd resolve [PR] [--fetch] [--format text|json]
|
|
8
7
|
pr-shepherd resolve [PR] --reply-thread-ids A,B --message MSG
|
|
9
8
|
pr-shepherd resolve [PR] --resolve-thread-ids A,B [--minimize-comment-ids X,Y]
|
|
10
9
|
[--dismiss-review-ids Q] [--message MSG]
|
|
11
10
|
[--require-sha SHA] [--format text|json]
|
|
12
11
|
|
|
13
|
-
Modes:
|
|
14
|
-
fetch Default when no mutation IDs are provided. Fetches review threads,
|
|
15
|
-
comments, first-look items, and instructions.
|
|
16
|
-
mutate Runs when any mutation ID flag is present. Resolves threads,
|
|
17
|
-
replies to human threads, resolves non-human/manual thread IDs,
|
|
18
|
-
minimizes non-human comments/review summaries, and dismisses non-human reviews.
|
|
19
|
-
|
|
20
12
|
Flags:
|
|
21
|
-
--fetch Force fetch mode.
|
|
22
13
|
--resolve-thread-ids <ids> Comma-separated review thread IDs to resolve.
|
|
23
14
|
Human-authored thread IDs are skipped; use --reply-thread-ids.
|
|
24
15
|
--reply-thread-ids <ids> Comma-separated human review thread IDs to reply to.
|
|
@@ -30,6 +21,9 @@ Flags:
|
|
|
30
21
|
--format text|json Output format. Default: text.
|
|
31
22
|
--help, -h Print this help and exit before GitHub I/O.
|
|
32
23
|
|
|
24
|
+
At least one non-empty action flag is required:
|
|
25
|
+
--reply-thread-ids, --resolve-thread-ids, --minimize-comment-ids, or --dismiss-review-ids.
|
|
26
|
+
|
|
33
27
|
PR may be a number or GitHub pull request URL. When omitted, the current branch PR is inferred.
|
|
34
28
|
Exit code: 0 on success; 1 on validation, lookup, or mutation failure.`,
|
|
35
29
|
"commit-suggestion": `pr-shepherd commit-suggestion
|
|
@@ -18,7 +18,7 @@ Commands:
|
|
|
18
18
|
[PR] Poll until non-WAIT or timeout. This is the default command.
|
|
19
19
|
iterate Run one iterate tick (single-tick alias).
|
|
20
20
|
poll Re-run iterate while the action is WAIT, then print the final tick.
|
|
21
|
-
resolve
|
|
21
|
+
resolve Apply review-state mutations (requires at least one action flag).
|
|
22
22
|
commit-suggestion Convert one GitHub suggestion thread into a patch and commit instructions.
|
|
23
23
|
mark-files-as-viewed Mark PR changed files as viewed in GitHub.
|
|
24
24
|
clean Remove pr-shepherd state files.
|
package/bin/cli-parser.mjs
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* pr-shepherd [PR] [--interval 45s] [--timeout 4m] [--format text|json] [--ready-delay Nm]
|
|
7
7
|
* [--stall-timeout <duration>] [--no-auto-mark-ready]
|
|
8
8
|
* [--no-auto-cancel-actionable]
|
|
9
|
-
* pr-shepherd resolve [PR] [--
|
|
9
|
+
* pr-shepherd resolve [PR] [--resolve-thread-ids A,B] [--minimize-comment-ids X,Y]
|
|
10
10
|
* [--reply-thread-ids A,B] [--dismiss-review-ids Q]
|
|
11
11
|
* [--message MSG] [--require-sha SHA]
|
|
12
12
|
* pr-shepherd commit-suggestion [PR] --thread-id ID --message MSG [--description DESC]
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
* pr-shepherd clean <pr|branch|current|repo|all> [value] [--dry-run] [--format text|json]
|
|
23
23
|
*/
|
|
24
24
|
import { readFileSync } from "node:fs";
|
|
25
|
-
import {
|
|
25
|
+
import { runResolveMutate } from "./commands/resolve.mjs";
|
|
26
26
|
import { runLogFile } from "./commands/log-file.mjs";
|
|
27
27
|
import { parseCommonArgs, getFlag, hasFlag, parseList } from "./cli/args.mjs";
|
|
28
28
|
import { isDefaultPollInvocation, validateDefaultPollArgs } from "./cli/default-poll.mjs";
|
|
29
29
|
import { USAGE, maybePrintHelp } from "./cli/help.mjs";
|
|
30
|
-
import {
|
|
30
|
+
import { formatMutateResult } from "./cli/formatters.mjs";
|
|
31
31
|
import { handleClean, handleCommitSuggestion, handleIterate, handleMarkFilesAsViewed, } from "./cli/handlers.mjs";
|
|
32
32
|
import { handlePoll } from "./cli/poll-handler.mjs";
|
|
33
33
|
import { setupLog } from "./log/setup.mjs";
|
|
@@ -130,30 +130,31 @@ async function handleResolve(args) {
|
|
|
130
130
|
const dismissReviewIds = parseList(getFlag(extra, "--dismiss-review-ids"));
|
|
131
131
|
const dismissMessage = getFlag(extra, "--message") ?? undefined;
|
|
132
132
|
const requireSha = getFlag(extra, "--require-sha") ?? undefined;
|
|
133
|
-
|
|
134
|
-
(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
dismissReviewIds.length === 0);
|
|
138
|
-
if (fetchMode) {
|
|
139
|
-
const result = await runResolveFetch({ ...globalOpts, prNumber });
|
|
140
|
-
process.stdout.write(globalOpts.format === "json"
|
|
141
|
-
? `${JSON.stringify(result, null, 2)}\n`
|
|
142
|
-
: `${formatFetchResult(result)}\n`);
|
|
133
|
+
if (hasFlag(extra, "--fetch")) {
|
|
134
|
+
process.stderr.write("pr-shepherd: resolve: --fetch has been removed; run pr-shepherd iterate or poll to fetch the next action.\n");
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
return;
|
|
143
137
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
dismissMessage,
|
|
153
|
-
requireSha,
|
|
154
|
-
});
|
|
155
|
-
process.stdout.write(globalOpts.format === "json"
|
|
156
|
-
? `${JSON.stringify(result, null, 2)}\n`
|
|
157
|
-
: `${formatMutateResult(result)}\n`);
|
|
138
|
+
const hasAction = resolveThreadIds.length > 0 ||
|
|
139
|
+
replyThreadIds.length > 0 ||
|
|
140
|
+
minimizeCommentIds.length > 0 ||
|
|
141
|
+
dismissReviewIds.length > 0;
|
|
142
|
+
if (!hasAction) {
|
|
143
|
+
process.stderr.write("pr-shepherd: resolve: an action flag is required (--reply-thread-ids, --resolve-thread-ids, --minimize-comment-ids, or --dismiss-review-ids).\n");
|
|
144
|
+
process.exitCode = 1;
|
|
145
|
+
return;
|
|
158
146
|
}
|
|
147
|
+
const result = await runResolveMutate({
|
|
148
|
+
...globalOpts,
|
|
149
|
+
prNumber,
|
|
150
|
+
resolveThreadIds,
|
|
151
|
+
replyThreadIds,
|
|
152
|
+
minimizeCommentIds,
|
|
153
|
+
dismissReviewIds,
|
|
154
|
+
dismissMessage,
|
|
155
|
+
requireSha,
|
|
156
|
+
});
|
|
157
|
+
process.stdout.write(globalOpts.format === "json"
|
|
158
|
+
? `${JSON.stringify(result, null, 2)}\n`
|
|
159
|
+
: `${formatMutateResult(result)}\n`);
|
|
159
160
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { buildPrShepherdCommand } from "../cli/runner.mjs";
|
|
2
2
|
/**
|
|
3
3
|
* Build the `commit-suggestion` instruction step for agent consumers.
|
|
4
|
-
*
|
|
5
|
-
* @param sectionName - The markdown section heading where suggestion threads appear
|
|
6
|
-
*
|
|
4
|
+
* Currently emitted by iterate `fix_code` for suggestion review threads.
|
|
5
|
+
* @param sectionName - The markdown section heading where suggestion threads appear,
|
|
6
|
+
* e.g. `"## Review threads"`.
|
|
7
7
|
* @param includeDriftHint - Whether to add the trailing note about drift on failed apply.
|
|
8
8
|
*/
|
|
9
9
|
export function buildCommitSuggestionInstruction(prNumber, sectionName, includeDriftHint) {
|
package/bin/commands/resolve.mjs
CHANGED
|
@@ -1,77 +1 @@
|
|
|
1
|
-
import { getRepoInfo, getCurrentPrNumber } from "../github/client.mjs";
|
|
2
|
-
import { fetchPrBatch } from "../github/batch.mjs";
|
|
3
|
-
import { loadConfig } from "../config/load.mjs";
|
|
4
|
-
import { classifyVisibleComments } from "../comments/visible-comments.mjs";
|
|
5
|
-
import { extractSuggestion } from "../suggestions/extract.mjs";
|
|
6
|
-
import { buildFetchInstructions } from "./resolve-instructions.mjs";
|
|
7
|
-
import { loadSeenMap, markSeen, classifyItem } from "../state/seen-comments.mjs";
|
|
8
|
-
import { threadTranscriptBody } from "../threads/transcript.mjs";
|
|
9
|
-
import { classifyThreadVisibility } from "../comments/thread-visibility.mjs";
|
|
10
|
-
import { classifyReviewsForDisplay } from "../comments/review-visibility.mjs";
|
|
11
|
-
import { markReviewInlineThreadMarkers } from "../comments/review-thread-markers.mjs";
|
|
12
|
-
import { normalizeBotUsernames } from "../comments/authors.mjs";
|
|
13
1
|
export { runResolveMutate } from "./resolve-mutate.mjs";
|
|
14
|
-
export async function runResolveFetch(opts) {
|
|
15
|
-
const repo = await getRepoInfo();
|
|
16
|
-
const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
|
|
17
|
-
if (prNumber === null) {
|
|
18
|
-
throw new Error("No open PR found for current branch. Pass a PR number explicitly.");
|
|
19
|
-
}
|
|
20
|
-
const { data } = await fetchPrBatch(prNumber, repo);
|
|
21
|
-
const stateKey = { owner: repo.owner, repo: repo.name, pr: prNumber };
|
|
22
|
-
const minimizedCommentCandidates = data.comments.filter((c) => c.isMinimized);
|
|
23
|
-
const seenMap = await loadSeenMap(stateKey);
|
|
24
|
-
const cfg = loadConfig();
|
|
25
|
-
const botUsernames = normalizeBotUsernames(cfg.botUsernames);
|
|
26
|
-
const threadVisibility = classifyThreadVisibility(data.reviewThreads, seenMap, botUsernames);
|
|
27
|
-
const unseenMinimizedComments = [];
|
|
28
|
-
const editedMinimizedComments = [];
|
|
29
|
-
for (const c of minimizedCommentCandidates) {
|
|
30
|
-
const cls = classifyItem(c.id, c.body, seenMap);
|
|
31
|
-
if (cls === "new")
|
|
32
|
-
unseenMinimizedComments.push(c);
|
|
33
|
-
else if (cls === "edited")
|
|
34
|
-
editedMinimizedComments.push(c);
|
|
35
|
-
}
|
|
36
|
-
const visibleCommentClassification = classifyVisibleComments(data.comments, seenMap, cfg.iterate?.minimizeComments, botUsernames);
|
|
37
|
-
const changesRequestedReviewVisibility = classifyReviewsForDisplay(data.changesRequestedReviews, seenMap);
|
|
38
|
-
const reviewSummaryVisibility = cfg.resolve.fetchReviewSummaries
|
|
39
|
-
? classifyReviewsForDisplay(data.reviewSummaries, seenMap)
|
|
40
|
-
: { visible: [], toMarkSeen: [] };
|
|
41
|
-
const actionableThreads = threadVisibility.activeThreads.map(({ isResolved: _r, isOutdated: _o, ...rest }) => {
|
|
42
|
-
const thread = rest;
|
|
43
|
-
const suggestion = extractSuggestion(rest);
|
|
44
|
-
if (suggestion)
|
|
45
|
-
thread.suggestion = suggestion;
|
|
46
|
-
return thread;
|
|
47
|
-
});
|
|
48
|
-
const firstLookComments = [
|
|
49
|
-
...unseenMinimizedComments.map((c) => ({ ...c, firstLookStatus: "minimized" })),
|
|
50
|
-
...editedMinimizedComments.map((c) => ({
|
|
51
|
-
...c,
|
|
52
|
-
firstLookStatus: "minimized",
|
|
53
|
-
edited: true,
|
|
54
|
-
})),
|
|
55
|
-
];
|
|
56
|
-
// Mark new and edited items as seen (best-effort — markSeen never throws).
|
|
57
|
-
await Promise.allSettled([
|
|
58
|
-
...threadVisibility.toMarkSeen.map((t) => markSeen(stateKey, t.id, threadTranscriptBody(t))),
|
|
59
|
-
...firstLookComments.map((c) => markSeen(stateKey, c.id, c.body)),
|
|
60
|
-
...visibleCommentClassification.toMarkSeen.map((c) => markSeen(stateKey, c.id, c.body)),
|
|
61
|
-
...changesRequestedReviewVisibility.toMarkSeen.map((r) => markSeen(stateKey, r.id, r.body)),
|
|
62
|
-
...reviewSummaryVisibility.toMarkSeen.map((r) => markSeen(stateKey, r.id, r.body)),
|
|
63
|
-
]);
|
|
64
|
-
await markReviewInlineThreadMarkers(stateKey, data.reviewThreads);
|
|
65
|
-
const result = {
|
|
66
|
-
prNumber,
|
|
67
|
-
actionableThreads,
|
|
68
|
-
resolutionOnlyThreads: threadVisibility.resolutionOnlyThreads,
|
|
69
|
-
firstLookThreads: threadVisibility.firstLookThreads,
|
|
70
|
-
actionableComments: visibleCommentClassification.actionable,
|
|
71
|
-
firstLookComments,
|
|
72
|
-
changesRequestedReviews: changesRequestedReviewVisibility.visible,
|
|
73
|
-
reviewSummaries: reviewSummaryVisibility.visible,
|
|
74
|
-
commitSuggestionsEnabled: cfg.actions.commitSuggestions,
|
|
75
|
-
};
|
|
76
|
-
return { ...result, instructions: buildFetchInstructions(prNumber, result) };
|
|
77
|
-
}
|
package/bin/config.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pr-shepherd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Jonathan Ong",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"@types/node": "^25.6.0",
|
|
29
29
|
"@vitest/coverage-v8": "^4.1.4",
|
|
30
30
|
"husky": "^9.1.7",
|
|
31
|
-
"knip": "^
|
|
32
|
-
"oxfmt": "^0.
|
|
31
|
+
"knip": "^6.14.1",
|
|
32
|
+
"oxfmt": "^0.50.0",
|
|
33
33
|
"oxlint": "^1.60.0",
|
|
34
34
|
"typescript": "^6.0.3",
|
|
35
35
|
"vitest": "^4.1.4"
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { buildPrShepherdCommand } from "../cli/runner.mjs";
|
|
2
|
-
import { SHEPHERD_JOURNAL_REFERENCE_GUIDANCE_THREADS_AND_COMMENTS_IN_ITEMS, buildShepherdJournalInstruction, } from "./shepherd-journal.mjs";
|
|
3
|
-
import { buildCommitSuggestionInstruction } from "./commit-suggestion-instruction.mjs";
|
|
4
|
-
/**
|
|
5
|
-
* Build the numbered triage/fix/resolve instruction steps for the agent to follow.
|
|
6
|
-
* Steps are conditionally emitted based on what the fetch returned (mirrors
|
|
7
|
-
* `buildFixInstructions` in `commands/iterate/render.mts`).
|
|
8
|
-
*/
|
|
9
|
-
export function buildFetchInstructions(prNumber, result) {
|
|
10
|
-
const { actionableThreads, resolutionOnlyThreads, firstLookThreads, actionableComments, firstLookComments, changesRequestedReviews, reviewSummaries, commitSuggestionsEnabled, } = result;
|
|
11
|
-
const firstLookTotal = firstLookThreads.length + firstLookComments.length;
|
|
12
|
-
const total = actionableThreads.length +
|
|
13
|
-
resolutionOnlyThreads.length +
|
|
14
|
-
actionableComments.length +
|
|
15
|
-
changesRequestedReviews.length +
|
|
16
|
-
reviewSummaries.length +
|
|
17
|
-
firstLookTotal;
|
|
18
|
-
if (total === 0) {
|
|
19
|
-
return ["No actionable items and no first-look items — end this invocation."];
|
|
20
|
-
}
|
|
21
|
-
const hasCodeItems = actionableThreads.length > 0 ||
|
|
22
|
-
actionableComments.length > 0 ||
|
|
23
|
-
changesRequestedReviews.length > 0;
|
|
24
|
-
const hasSuggestions = commitSuggestionsEnabled && actionableThreads.some((t) => t.suggestion != null);
|
|
25
|
-
const instructions = [];
|
|
26
|
-
instructions.push(`Classify every item listed above into exactly one of: Fixed / Actionable / Not relevant / Outdated / Acknowledge. Do not silently skip any item. Bot-authored review summaries (authors whose name contains \`[bot]\` or matches \`copilot-pull-request-reviewer\`, \`gemini-code-assist\`) default to Acknowledge with reason "bot summary — no actionable content" unless the body calls out an unaddressed issue.`);
|
|
27
|
-
if (firstLookTotal > 0) {
|
|
28
|
-
instructions.push(`Items in \`## First-look items\` are shown so you can acknowledge their current status before acting. If a first-look thread also appears under \`## Review threads to resolve\`, include its ID in \`--resolve-thread-ids\`; otherwise do not pass first-look-only IDs to mutation flags.`);
|
|
29
|
-
}
|
|
30
|
-
const editedTotal = actionableComments.filter((c) => c.edited).length +
|
|
31
|
-
firstLookThreads.filter((t) => t.edited).length +
|
|
32
|
-
firstLookComments.filter((c) => c.edited).length;
|
|
33
|
-
if (editedTotal > 0) {
|
|
34
|
-
instructions.push(`Actionable comments marked \`[edited since first look]\` and first-look bullets tagged \`, edited\` were updated by their author after you previously acknowledged them. Read the updated body before deciding whether any matching \`## Review threads to resolve\` item should be resolved.`);
|
|
35
|
-
}
|
|
36
|
-
if (hasSuggestions) {
|
|
37
|
-
instructions.push(buildCommitSuggestionInstruction(prNumber, "## Actionable Review Threads", true));
|
|
38
|
-
}
|
|
39
|
-
if (hasCodeItems) {
|
|
40
|
-
instructions.push(`Read and edit each file referenced under \`## Actionable Review Threads\`, \`## Actionable PR Comments\`, and \`## Pending CHANGES_REQUESTED reviews\` above. Reclassify each fixed item as Fixed. If an item is too complex to address, leave it as Actionable for the final report.`);
|
|
41
|
-
instructions.push(`If you applied code edits: commit them with a descriptive message, cancel any stale in-progress runs, then rebase and push per your repository's conventions.`);
|
|
42
|
-
}
|
|
43
|
-
if (resolutionOnlyThreads.length > 0) {
|
|
44
|
-
instructions.push(`Resolve each thread under \`## Review threads to resolve\` with \`--resolve-thread-ids\`. These threads are already outdated or minimized, so no code edit is required for them unless their body reveals separate work you choose to do.`);
|
|
45
|
-
}
|
|
46
|
-
const requireShaHint = hasCodeItems
|
|
47
|
-
? ` Include \`--require-sha $(git rev-parse HEAD)\` only when you pushed new commits.`
|
|
48
|
-
: "";
|
|
49
|
-
const dismissNote = changesRequestedReviews.length > 0
|
|
50
|
-
? ` For \`--dismiss-review-ids\`: \`--message\` is required with one specific sentence describing the fix or the reason for not acting (no boilerplate like "address review comments"); omit \`--message\` when not dismissing. Review-summary IDs (\`PRR_…\` from \`## Review summaries\`) go into \`--minimize-comment-ids\`, never \`--dismiss-review-ids\`.`
|
|
51
|
-
: reviewSummaries.length > 0
|
|
52
|
-
? ` Review-summary IDs (\`PRR_…\` from \`## Review summaries\`) go into \`--minimize-comment-ids\`.`
|
|
53
|
-
: "";
|
|
54
|
-
const resolveCommand = `${buildPrShepherdCommand(["resolve", String(prNumber)]).text} [--reply-thread-ids <ids>] [--resolve-thread-ids <ids>] [--minimize-comment-ids <ids>] [--dismiss-review-ids <ids>] [--message "<reason>"]`;
|
|
55
|
-
if (actionableThreads.length > 0 || resolutionOnlyThreads.length > 0) {
|
|
56
|
-
instructions.push(`Do not reply to your own thread comments. If the latest visible comment in a thread is your own prior Shepherd reply, leave that thread out of \`--reply-thread-ids\`.`);
|
|
57
|
-
}
|
|
58
|
-
instructions.push(`Run \`${resolveCommand}\` with only the non-empty flag subsets. Skip the command entirely if all ID lists are empty.${requireShaHint}${dismissNote}`);
|
|
59
|
-
instructions.push(buildShepherdJournalInstruction(prNumber, SHEPHERD_JOURNAL_REFERENCE_GUIDANCE_THREADS_AND_COMMENTS_IN_ITEMS));
|
|
60
|
-
instructions.push(`Report: echo the CLI's mutation output, then one line per Acknowledged item: \`Acknowledged <id> (@<author>): <reason>\`. If any fetched item was neither resolved nor acknowledged, stop and escalate: "<N> item(s) fetched but not acted on or acknowledged — need human direction before closing".`);
|
|
61
|
-
return instructions;
|
|
62
|
-
}
|