pr-shepherd 0.34.0 → 0.35.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 +3 -2
- package/.grok-plugin/marketplace.json +17 -0
- package/README.md +56 -39
- package/bin/api.d.mts +80 -0
- package/bin/api.mjs +236 -0
- package/bin/checks/classify.d.mts +41 -0
- package/bin/checks/startup-failures.d.mts +2 -0
- package/bin/checks/superseded.d.mts +21 -0
- package/bin/checks/triage.d.mts +4 -0
- package/bin/classify/apply.d.mts +18 -0
- package/bin/classify/apply.mjs +4 -0
- package/bin/classify/loader.d.mts +10 -0
- package/bin/classify/types.d.mts +35 -0
- package/bin/cli/args.d.mts +18 -0
- package/bin/cli/clean-formatter.d.mts +2 -0
- package/bin/cli/default-poll.d.mts +2 -0
- package/bin/cli/duration-flag.d.mts +2 -0
- package/bin/cli/duration.d.mts +13 -0
- package/bin/cli/fence.d.mts +1 -0
- package/bin/cli/fix-formatter-extra.d.mts +3 -0
- package/bin/cli/fix-formatter.d.mts +2 -0
- package/bin/cli/fix-formatter.mjs +6 -6
- package/bin/cli/formatters.d.mts +7 -0
- package/bin/cli/handlers.d.mts +4 -0
- package/bin/cli/handlers.mjs +11 -10
- package/bin/cli/help-command-pages.d.mts +231 -0
- package/bin/cli/help-command-pages.mjs +106 -1
- package/bin/cli/help-iterate-poll-pages.d.mts +4 -0
- package/bin/cli/help-iterate-poll-pages.mjs +4 -2
- package/bin/cli/help-log-file-page.d.mts +1 -0
- package/bin/cli/help-top-page.d.mts +1 -0
- package/bin/cli/help-top-page.mjs +14 -15
- package/bin/cli/help.d.mts +236 -0
- package/bin/cli/help.mjs +17 -0
- package/bin/cli/iterate-emitter.d.mts +8 -0
- package/bin/cli/iterate-flags.d.mts +11 -0
- package/bin/cli/iterate-formatter.d.mts +19 -0
- package/bin/cli/iterate-instructions.d.mts +6 -0
- package/bin/cli/iterate-lean.d.mts +12 -0
- package/bin/cli/journal-formatter.d.mts +2 -0
- package/bin/cli/journal-formatter.mjs +15 -0
- package/bin/cli/journal-handler.d.mts +1 -0
- package/bin/cli/journal-handler.mjs +7 -20
- package/bin/cli/list-formatters.d.mts +76 -0
- package/bin/cli/list-formatters.mjs +9 -9
- package/bin/cli/mark-files-as-viewed-flags.d.mts +11 -0
- package/bin/cli/mark-files-as-viewed-formatter.d.mts +2 -0
- package/bin/cli/mutate-formatter.d.mts +2 -0
- package/bin/cli/poll-handler.d.mts +1 -0
- package/bin/cli/poll-handler.mjs +2 -2
- package/bin/cli/resolve-validators.d.mts +3 -0
- package/bin/cli/resolve-validators.mjs +3 -3
- package/bin/cli/runner.d.mts +7 -0
- package/bin/cli/suggestion-renderer.d.mts +3 -0
- package/bin/cli/validate-default-args.d.mts +6 -0
- package/bin/cli-parser.d.mts +2 -0
- package/bin/cli-parser.mjs +72 -16
- package/bin/commands/check-annotations.d.mts +5 -0
- package/bin/commands/check-status.d.mts +3 -0
- package/bin/commands/check-terminal-report.d.mts +5 -0
- package/bin/commands/check.d.mts +6 -0
- package/bin/commands/check.mjs +2 -1
- package/bin/commands/clean.d.mts +21 -0
- package/bin/commands/commit-suggestion-instruction.d.mts +8 -0
- package/bin/commands/commit-suggestion-instruction.mjs +3 -3
- package/bin/commands/commit-suggestion.d.mts +8 -0
- package/bin/commands/commit-suggestion.mjs +11 -10
- package/bin/commands/iterate/check-instructions.d.mts +16 -0
- package/bin/commands/iterate/check-instructions.mjs +3 -3
- package/bin/commands/iterate/classify.d.mts +18 -0
- package/bin/commands/iterate/classify.mjs +4 -4
- package/bin/commands/iterate/escalate.d.mts +31 -0
- package/bin/commands/iterate/escalate.mjs +7 -4
- package/bin/commands/iterate/fix-code.d.mts +25 -0
- package/bin/commands/iterate/helpers.d.mts +13 -0
- package/bin/commands/iterate/helpers.mjs +4 -1
- package/bin/commands/iterate/index.d.mts +2 -0
- package/bin/commands/iterate/index.mjs +5 -2
- package/bin/commands/iterate/render.d.mts +5 -0
- package/bin/commands/iterate/render.mjs +8 -6
- package/bin/commands/iterate/reruns.d.mts +20 -0
- package/bin/commands/iterate/stall.d.mts +6 -0
- package/bin/commands/journal/index.d.mts +14 -0
- package/bin/commands/journal/index.mjs +1 -0
- package/bin/commands/journal/transform.d.mts +22 -0
- package/bin/commands/log-file.d.mts +5 -0
- package/bin/commands/mark-files-as-viewed.d.mts +26 -0
- package/bin/commands/mark-files-as-viewed.mjs +1 -0
- package/bin/commands/poll.d.mts +10 -0
- package/bin/commands/poll.mjs +1 -0
- package/bin/commands/ready-delay.d.mts +29 -0
- package/bin/commands/ready-mergeability.d.mts +15 -0
- package/bin/commands/resolve-mutate.d.mts +4 -0
- package/bin/commands/resolve-mutate.mjs +1 -0
- package/bin/commands/resolve.d.mts +4 -0
- package/bin/commands/shepherd-journal.d.mts +7 -0
- package/bin/commands/shepherd-journal.mjs +2 -2
- package/bin/comments/authors.d.mts +14 -0
- package/bin/comments/marker.d.mts +2 -0
- package/bin/comments/minimize-policy.d.mts +4 -0
- package/bin/comments/pending-ops.d.mts +15 -0
- package/bin/comments/rate-limit.d.mts +18 -0
- package/bin/comments/resolve.d.mts +34 -0
- package/bin/comments/resolve.mjs +1 -0
- package/bin/comments/review-thread-markers.d.mts +8 -0
- package/bin/comments/review-visibility.d.mts +28 -0
- package/bin/comments/sha-poll.d.mts +2 -0
- package/bin/comments/thread-visibility.d.mts +11 -0
- package/bin/comments/visible-comments.d.mts +11 -0
- package/bin/config/load.d.mts +60 -0
- package/bin/config/load.mjs +72 -1
- package/bin/config.json +0 -2
- package/bin/execution-context.d.mts +9 -0
- package/bin/execution-context.mjs +19 -0
- package/bin/exit-codes.d.mts +51 -0
- package/bin/github/activity.d.mts +3 -0
- package/bin/github/activity.mjs +7 -0
- package/bin/github/batch-parser-helpers.d.mts +22 -0
- package/bin/github/batch-parsers.d.mts +3 -0
- package/bin/github/batch-parsers.mjs +6 -0
- package/bin/github/batch-raw-types.d.mts +207 -0
- package/bin/github/batch-response.d.mts +4 -0
- package/bin/github/batch.d.mts +22 -0
- package/bin/github/branch-protection.d.mts +3 -0
- package/bin/github/check-annotations.d.mts +2 -0
- package/bin/github/client.d.mts +46 -0
- package/bin/github/client.mjs +7 -2
- package/bin/github/errors.d.mts +25 -0
- package/bin/github/gql/batch-pr.gql +5 -0
- package/bin/github/gql/review-thread-comments.gql +1 -0
- package/bin/github/graphql-http.d.mts +19 -0
- package/bin/github/graphql-response.d.mts +7 -0
- package/bin/github/http-auth.d.mts +4 -0
- package/bin/github/http-request.d.mts +7 -0
- package/bin/github/http-utils.d.mts +11 -0
- package/bin/github/http.d.mts +5 -0
- package/bin/github/pagination.d.mts +45 -0
- package/bin/github/queries.d.mts +24 -0
- package/bin/github/rest-http.d.mts +2 -0
- package/bin/github/thread-comments.d.mts +2 -0
- package/bin/index.d.mts +10 -0
- package/bin/index.mjs +1 -1
- package/bin/log/log-file.d.mts +28 -0
- package/bin/log/session.d.mts +31 -0
- package/bin/log/setup.d.mts +6 -0
- package/bin/mcp/index.d.mts +5 -0
- package/bin/mcp/index.mjs +8 -0
- package/bin/mcp/server.d.mts +8 -0
- package/bin/mcp/server.mjs +157 -0
- package/bin/mcp-stdio.d.mts +2 -0
- package/bin/mcp-stdio.mjs +7 -0
- package/bin/merge-status/derive.d.mts +19 -0
- package/bin/reporters/agent.d.mts +23 -0
- package/bin/reporters/agent.mjs +3 -0
- package/bin/state/base.d.mts +1 -0
- package/bin/state/bot-cr-seen.d.mts +51 -0
- package/bin/state/bot-cr-seen.mjs +1 -1
- package/bin/state/fix-attempts.d.mts +27 -0
- package/bin/state/iterate-stall.d.mts +27 -0
- package/bin/state/seen-comments.d.mts +62 -0
- package/bin/suggestions/extract.d.mts +8 -0
- package/bin/suggestions/parse.d.mts +48 -0
- package/bin/suggestions/patch.d.mts +14 -0
- package/bin/threads/transcript.d.mts +14 -0
- package/bin/threads/transcript.mjs +4 -0
- package/bin/types/activity.d.mts +30 -0
- package/bin/types/agent-thread.d.mts +9 -0
- package/bin/types/check-annotations.d.mts +14 -0
- package/bin/types/check-classification.d.mts +19 -0
- package/bin/types/github.d.mts +139 -0
- package/bin/types/iterate.d.mts +157 -0
- package/bin/types/protected-run.d.mts +6 -0
- package/bin/types/report.d.mts +176 -0
- package/bin/types/review-thread.d.mts +12 -0
- package/bin/types.d.mts +9 -0
- package/bin/util/markdown.d.mts +1 -0
- package/bin/util/path-segment.d.mts +2 -0
- package/bin/util/sleep.d.mts +1 -0
- package/bin/util/worktree.d.mts +9 -0
- package/bin/util/worktree.mjs +4 -1
- package/package.json +51 -37
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +3 -2
- package/plugins/pr-shepherd/.codex.mcp.json +8 -0
- package/plugins/pr-shepherd/.mcp.json +6 -0
- package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +5 -19
- package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +6 -15
- package/src/classify/types.mts +12 -0
package/bin/cli/poll-handler.mjs
CHANGED
|
@@ -14,12 +14,12 @@ export async function handlePoll(args) {
|
|
|
14
14
|
if (flags.readyDelaySuffix === null || flags.stallTimeoutSuffix === null)
|
|
15
15
|
return;
|
|
16
16
|
const intervalStr = getFlag(extra, "--interval");
|
|
17
|
-
const intervalSuffix = validateSecondsDurationFlag("pr-shepherd
|
|
17
|
+
const intervalSuffix = validateSecondsDurationFlag("pr-shepherd", "--interval", intervalStr, hasFlag(extra, "--interval"));
|
|
18
18
|
if (intervalSuffix === null)
|
|
19
19
|
return;
|
|
20
20
|
const intervalSeconds = parseDurationToSeconds(intervalSuffix ?? "", DEFAULT_POLL_INTERVAL_SECONDS);
|
|
21
21
|
const timeoutStr = getFlag(extra, "--timeout");
|
|
22
|
-
const timeoutSuffix = validateSecondsDurationFlag("pr-shepherd
|
|
22
|
+
const timeoutSuffix = validateSecondsDurationFlag("pr-shepherd", "--timeout", timeoutStr, hasFlag(extra, "--timeout"));
|
|
23
23
|
if (timeoutSuffix === null)
|
|
24
24
|
return;
|
|
25
25
|
const timeoutSeconds = parseDurationToSeconds(timeoutSuffix ?? "", DEFAULT_POLL_TIMEOUT_SECONDS);
|
|
@@ -2,7 +2,7 @@ import { EXIT } from "../exit-codes.mjs";
|
|
|
2
2
|
export function rejectPrrcMinimizeIds(ids) {
|
|
3
3
|
const prrcIds = ids.filter((id) => id.startsWith("PRRC_"));
|
|
4
4
|
if (prrcIds.length > 0) {
|
|
5
|
-
process.stderr.write(`pr-shepherd:
|
|
5
|
+
process.stderr.write(`pr-shepherd: apply review: --minimize-comment-ids contains thread comment IDs (PRRC_*): ${prrcIds.join(", ")}. Thread comments cannot be minimized individually — resolve the parent thread using --resolve-thread-ids with the PRRT_* thread ID instead.\n`);
|
|
6
6
|
process.exitCode = EXIT.DATAERR;
|
|
7
7
|
}
|
|
8
8
|
return prrcIds;
|
|
@@ -10,7 +10,7 @@ export function rejectPrrcMinimizeIds(ids) {
|
|
|
10
10
|
export function warnPrrcThreadIds(ids) {
|
|
11
11
|
const prrcIds = ids.filter((id) => id.startsWith("PRRC_"));
|
|
12
12
|
if (prrcIds.length > 0) {
|
|
13
|
-
process.stderr.write(`pr-shepherd:
|
|
13
|
+
process.stderr.write(`pr-shepherd: apply review: warning: --resolve-thread-ids contains comment IDs (PRRC_*) instead of thread IDs (PRRT_*): ${prrcIds.join(", ")}. The resolveReviewThread mutation requires PRRT_* thread IDs. Run a GraphQL query for pullRequest.reviewThreads to get the correct IDs.\n`);
|
|
14
14
|
}
|
|
15
15
|
return prrcIds;
|
|
16
16
|
}
|
|
@@ -19,7 +19,7 @@ export function validateRequireSha(sha) {
|
|
|
19
19
|
return true;
|
|
20
20
|
if (/^[0-9a-f]{40}$/.test(sha))
|
|
21
21
|
return true;
|
|
22
|
-
process.stderr.write(`pr-shepherd:
|
|
22
|
+
process.stderr.write(`pr-shepherd: apply review: --require-sha must be a full 40-character lowercase hex SHA, got "${sha}". Short SHAs will never match GitHub's headRefOid. Use $(git rev-parse HEAD) to get the full SHA.\n`);
|
|
23
23
|
process.exitCode = EXIT.DATAERR;
|
|
24
24
|
return false;
|
|
25
25
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate args for default-dispatch paths (e.g. `pr-shepherd [PR] [flags]`).
|
|
3
|
+
* Returns false when an unexpected token is found; calls onError with the
|
|
4
|
+
* offending arg so callers can print their own usage message.
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateDefaultArgs(args: string[], flagsWithValues: ReadonlySet<string>, booleanFlags: ReadonlySet<string>, onError: (arg: string) => void): boolean;
|
package/bin/cli-parser.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
1
2
|
/** CLI argument parsing and subcommand dispatch for pr-shepherd. See --help for usage. */
|
|
2
3
|
import { readFileSync } from "node:fs";
|
|
3
4
|
import { EXIT, errorToExitCode } from "./exit-codes.mjs";
|
|
@@ -5,7 +6,7 @@ import { runResolveMutate } from "./commands/resolve.mjs";
|
|
|
5
6
|
import { runLogFile } from "./commands/log-file.mjs";
|
|
6
7
|
import { parseCommonArgs, getFlag, hasFlag, parseList } from "./cli/args.mjs";
|
|
7
8
|
import { isDefaultPollInvocation, validateDefaultPollArgs } from "./cli/default-poll.mjs";
|
|
8
|
-
import { USAGE, maybePrintHelp } from "./cli/help.mjs";
|
|
9
|
+
import { USAGE, helpKeyForArgs, maybePrintHelp } from "./cli/help.mjs";
|
|
9
10
|
import { formatMutateResult } from "./cli/formatters.mjs";
|
|
10
11
|
import { handleClean, handleCommitSuggestion, handleIterate, handleMarkFilesAsViewed, } from "./cli/handlers.mjs";
|
|
11
12
|
import { handleJournal } from "./cli/journal-handler.mjs";
|
|
@@ -31,19 +32,33 @@ export async function main(argv) {
|
|
|
31
32
|
await handleLogFile(args.slice(1));
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
35
|
+
// The public command groups keep admin diagnostics out of the normal
|
|
36
|
+
// command namespace. Keep this before setupLog because log-file reads the
|
|
37
|
+
// log path and must not initialize/tee the log it is reporting.
|
|
38
|
+
if (subcommand === "admin" && args[1] === "log-file") {
|
|
39
|
+
await handleLogFile(args.slice(2), "admin log-file");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
34
42
|
// Short-circuit all --help/-h before any I/O or logging.
|
|
35
43
|
if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
|
|
36
44
|
if (isDefaultPollInvocation(subcommand)) {
|
|
37
|
-
process.stdout.write(`${USAGE.
|
|
45
|
+
process.stdout.write(`${USAGE.default}\n`);
|
|
38
46
|
}
|
|
39
47
|
else {
|
|
40
|
-
|
|
41
|
-
? subcommand
|
|
42
|
-
: "top";
|
|
43
|
-
process.stdout.write(`${USAGE[key]}\n`);
|
|
48
|
+
process.stdout.write(`${USAGE[helpKeyForArgs(args)]}\n`);
|
|
44
49
|
}
|
|
45
50
|
return;
|
|
46
51
|
}
|
|
52
|
+
const legacyReplacement = {
|
|
53
|
+
poll: "pr-shepherd [PR] (the default PR polling command)",
|
|
54
|
+
resolve: "apply review",
|
|
55
|
+
"mark-files-as-viewed": "apply files",
|
|
56
|
+
journal: "apply journal",
|
|
57
|
+
"commit-suggestion": "build-suggestion-patch",
|
|
58
|
+
};
|
|
59
|
+
if (subcommand !== undefined && legacyReplacement[subcommand] !== undefined) {
|
|
60
|
+
warnLegacyAlias(subcommand, legacyReplacement[subcommand]);
|
|
61
|
+
}
|
|
47
62
|
// Initialize the per-worktree log and install a stdout tee.
|
|
48
63
|
await setupLog(argv);
|
|
49
64
|
if (isDefaultPollInvocation(subcommand)) {
|
|
@@ -53,14 +68,23 @@ export async function main(argv) {
|
|
|
53
68
|
return;
|
|
54
69
|
}
|
|
55
70
|
switch (subcommand) {
|
|
71
|
+
case "apply":
|
|
72
|
+
await handleApply(args.slice(1));
|
|
73
|
+
break;
|
|
74
|
+
case "build-suggestion-patch":
|
|
75
|
+
await handleCommitSuggestion(args.slice(1));
|
|
76
|
+
break;
|
|
77
|
+
case "admin":
|
|
78
|
+
await handleAdmin(args.slice(1));
|
|
79
|
+
break;
|
|
56
80
|
case "resolve":
|
|
57
|
-
await handleResolve(args.slice(1));
|
|
81
|
+
await handleResolve(args.slice(1), "resolve");
|
|
58
82
|
break;
|
|
59
83
|
case "commit-suggestion":
|
|
60
|
-
await handleCommitSuggestion(args.slice(1));
|
|
84
|
+
await handleCommitSuggestion(args.slice(1), "commit-suggestion");
|
|
61
85
|
break;
|
|
62
86
|
case "mark-files-as-viewed":
|
|
63
|
-
await handleMarkFilesAsViewed(args.slice(1));
|
|
87
|
+
await handleMarkFilesAsViewed(args.slice(1), "mark-files-as-viewed");
|
|
64
88
|
break;
|
|
65
89
|
case "iterate":
|
|
66
90
|
await handleIterate(args.slice(1));
|
|
@@ -69,10 +93,10 @@ export async function main(argv) {
|
|
|
69
93
|
await handlePoll(args.slice(1));
|
|
70
94
|
break;
|
|
71
95
|
case "clean":
|
|
72
|
-
await handleClean(args.slice(1));
|
|
96
|
+
await handleClean(args.slice(1), "clean");
|
|
73
97
|
break;
|
|
74
98
|
case "journal":
|
|
75
|
-
await handleJournal(args.slice(1));
|
|
99
|
+
await handleJournal(args.slice(1), "journal");
|
|
76
100
|
break;
|
|
77
101
|
default:
|
|
78
102
|
process.stderr.write(`Unknown subcommand: ${subcommand ?? "(none)"}\n`);
|
|
@@ -81,6 +105,38 @@ export async function main(argv) {
|
|
|
81
105
|
return;
|
|
82
106
|
}
|
|
83
107
|
}
|
|
108
|
+
function warnLegacyAlias(alias, replacement) {
|
|
109
|
+
process.stderr.write(`pr-shepherd: ${alias} is deprecated; use ${replacement.startsWith("pr-shepherd") ? replacement : `pr-shepherd ${replacement}`} instead.\n`);
|
|
110
|
+
}
|
|
111
|
+
async function handleApply(args) {
|
|
112
|
+
const action = args[0];
|
|
113
|
+
switch (action) {
|
|
114
|
+
case "review":
|
|
115
|
+
await handleResolve(args.slice(1));
|
|
116
|
+
return;
|
|
117
|
+
case "files":
|
|
118
|
+
await handleMarkFilesAsViewed(args.slice(1));
|
|
119
|
+
return;
|
|
120
|
+
case "journal":
|
|
121
|
+
await handleJournal(args.slice(1));
|
|
122
|
+
return;
|
|
123
|
+
default:
|
|
124
|
+
process.stderr.write(`Unknown apply action: ${action ?? "(none)"}\n`);
|
|
125
|
+
process.stderr.write(`${USAGE.apply}\n`);
|
|
126
|
+
process.exitCode = EXIT.USAGE;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function handleAdmin(args) {
|
|
130
|
+
switch (args[0]) {
|
|
131
|
+
case "clean":
|
|
132
|
+
await handleClean(args.slice(1));
|
|
133
|
+
return;
|
|
134
|
+
default:
|
|
135
|
+
process.stderr.write(`Unknown admin command: ${args[0] ?? "(none)"}\n`);
|
|
136
|
+
process.stderr.write(`${USAGE.admin}\n`);
|
|
137
|
+
process.exitCode = EXIT.USAGE;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
84
140
|
function readVersion() {
|
|
85
141
|
const pkgUrl = new URL("../package.json", import.meta.url);
|
|
86
142
|
const pkg = JSON.parse(readFileSync(pkgUrl, "utf8"));
|
|
@@ -89,8 +145,8 @@ function readVersion() {
|
|
|
89
145
|
// ---------------------------------------------------------------------------
|
|
90
146
|
// Subcommand handlers
|
|
91
147
|
// ---------------------------------------------------------------------------
|
|
92
|
-
async function handleLogFile(args) {
|
|
93
|
-
if (maybePrintHelp(args,
|
|
148
|
+
async function handleLogFile(args, usageKey = "log-file") {
|
|
149
|
+
if (maybePrintHelp(args, usageKey))
|
|
94
150
|
return;
|
|
95
151
|
const jsonOut = args.some((a) => a === "--format=json") ||
|
|
96
152
|
(() => {
|
|
@@ -106,7 +162,7 @@ async function handleLogFile(args) {
|
|
|
106
162
|
process.exitCode = errorToExitCode(e);
|
|
107
163
|
}
|
|
108
164
|
}
|
|
109
|
-
async function handleResolve(args) {
|
|
165
|
+
async function handleResolve(args, command = "apply review") {
|
|
110
166
|
const { prNumber, global: globalOpts, extra } = parseCommonArgs(args);
|
|
111
167
|
const resolveThreadIds = parseList(getFlag(extra, "--resolve-thread-ids"));
|
|
112
168
|
const replyThreadIds = parseList(getFlag(extra, "--reply-thread-ids"));
|
|
@@ -120,7 +176,7 @@ async function handleResolve(args) {
|
|
|
120
176
|
if (rejectPrrcMinimizeIds(minimizeCommentIds).length > 0)
|
|
121
177
|
return;
|
|
122
178
|
if (hasFlag(extra, "--fetch")) {
|
|
123
|
-
process.stderr.write(
|
|
179
|
+
process.stderr.write(`pr-shepherd: ${command}: --fetch has been removed; run pr-shepherd iterate to fetch the next action.\n`);
|
|
124
180
|
process.exitCode = EXIT.USAGE;
|
|
125
181
|
return;
|
|
126
182
|
}
|
|
@@ -129,7 +185,7 @@ async function handleResolve(args) {
|
|
|
129
185
|
minimizeCommentIds.length > 0 ||
|
|
130
186
|
dismissReviewIds.length > 0;
|
|
131
187
|
if (!hasAction) {
|
|
132
|
-
process.stderr.write(
|
|
188
|
+
process.stderr.write(`pr-shepherd: ${command}: an action flag is required (--reply-thread-ids, --resolve-thread-ids, --minimize-comment-ids, or --dismiss-review-ids).\n`);
|
|
133
189
|
process.exitCode = EXIT.USAGE;
|
|
134
190
|
return;
|
|
135
191
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CheckAnnotation, TriagedCheck } from "../types.mts";
|
|
2
|
+
export declare function attachUnseenCheckAnnotations(checks: TriagedCheck[], seenMap: Map<string, {
|
|
3
|
+
seenAt: number;
|
|
4
|
+
}>, prNumber: number): Promise<TriagedCheck[]>;
|
|
5
|
+
export declare function annotationMarkerBody(a: CheckAnnotation): string;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { MergeStatusResult, ShepherdStatus } from "../types.mts";
|
|
2
|
+
import type { CiVerdict } from "../checks/classify.mts";
|
|
3
|
+
export declare function computeStatus(verdict: CiVerdict, unresolvedThreads: number, unresolvedComments: number, mergeStatus: MergeStatusResult, changesRequestedReviews: number): ShepherdStatus;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { BatchPrData, MergeStatusResult, ShepherdReport } from "../types.mts";
|
|
2
|
+
export declare function buildTerminalReport(prNumber: number, repo: {
|
|
3
|
+
owner: string;
|
|
4
|
+
name: string;
|
|
5
|
+
}, batchData: BatchPrData, mergeStatus: MergeStatusResult, status: "MERGED" | "CLOSED"): ShepherdReport;
|
package/bin/commands/check.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import { normalizeBotUsernames } from "../comments/authors.mjs";
|
|
|
20
20
|
import { discoverRuleFiles, loadRules } from "../classify/loader.mjs";
|
|
21
21
|
import { buildClassifyIndex, partitionBatch } from "../classify/apply.mjs";
|
|
22
22
|
import { EXIT, ShepherdError } from "../exit-codes.mjs";
|
|
23
|
+
import { getEffectiveCwd } from "../execution-context.mjs";
|
|
23
24
|
export async function runCheck(opts) {
|
|
24
25
|
const repo = await getRepoInfo();
|
|
25
26
|
const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
|
|
@@ -50,7 +51,7 @@ export async function runCheck(opts) {
|
|
|
50
51
|
const stateKey = { owner: repo.owner, repo: repo.name, pr: prNumber };
|
|
51
52
|
const seenMap = await loadSeenMap(stateKey);
|
|
52
53
|
const botUsernames = normalizeBotUsernames(config.botUsernames);
|
|
53
|
-
const ruleSet = await loadRules(discoverRuleFiles(
|
|
54
|
+
const ruleSet = await loadRules(discoverRuleFiles(getEffectiveCwd()));
|
|
54
55
|
const classifyIndex = buildClassifyIndex(ruleSet, batchData);
|
|
55
56
|
const partition = partitionBatch(classifyIndex, batchData);
|
|
56
57
|
const triaged = await attachUnseenCheckAnnotations(triagedBase, seenMap, prNumber);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type CleanVariant = "pr" | "branch" | "current" | "repo" | "all";
|
|
2
|
+
interface CleanOptions {
|
|
3
|
+
variant: CleanVariant;
|
|
4
|
+
/** PR number string (for "pr") or branch name (for "branch"). Defaults to current. */
|
|
5
|
+
value?: string;
|
|
6
|
+
dryRun?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface CleanResult {
|
|
9
|
+
ok: boolean;
|
|
10
|
+
variant: CleanVariant;
|
|
11
|
+
dryRun: boolean;
|
|
12
|
+
base: string;
|
|
13
|
+
target: string;
|
|
14
|
+
/** Paths removed (actual delete) or that would be removed (dry-run). */
|
|
15
|
+
deleted: string[];
|
|
16
|
+
/** Target path when it did not exist. */
|
|
17
|
+
skipped: string[];
|
|
18
|
+
error?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function runClean(opts: CleanOptions): Promise<CleanResult>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the `build-suggestion-patch` instruction step for agent consumers.
|
|
3
|
+
* Currently emitted by iterate `fix_code` for suggestion review threads.
|
|
4
|
+
* @param sectionName - The markdown section heading where suggestion threads appear,
|
|
5
|
+
* e.g. `"## Review threads"`.
|
|
6
|
+
* @param includeDriftHint - Whether to add the trailing note about drift on failed apply.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildCommitSuggestionInstruction(prNumber: number, sectionName: string, includeDriftHint: boolean): string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildPrShepherdCommand } from "../cli/runner.mjs";
|
|
2
2
|
/**
|
|
3
|
-
* Build the `
|
|
3
|
+
* Build the `build-suggestion-patch` instruction step for agent consumers.
|
|
4
4
|
* Currently emitted by iterate `fix_code` for suggestion review threads.
|
|
5
5
|
* @param sectionName - The markdown section heading where suggestion threads appear,
|
|
6
6
|
* e.g. `"## Review threads"`.
|
|
@@ -8,7 +8,7 @@ import { buildPrShepherdCommand } from "../cli/runner.mjs";
|
|
|
8
8
|
*/
|
|
9
9
|
export function buildCommitSuggestionInstruction(prNumber, sectionName, includeDriftHint) {
|
|
10
10
|
const command = buildPrShepherdCommand([
|
|
11
|
-
"
|
|
11
|
+
"build-suggestion-patch",
|
|
12
12
|
String(prNumber),
|
|
13
13
|
"--thread-id",
|
|
14
14
|
"<id>",
|
|
@@ -19,5 +19,5 @@ export function buildCommitSuggestionInstruction(prNumber, sectionName, includeD
|
|
|
19
19
|
const driftHint = includeDriftHint
|
|
20
20
|
? " If the patch fails to apply (drift since the suggestion was written), fall through to the manual fix step."
|
|
21
21
|
: " If the patch fails to apply, fall through to the manual-edit step.";
|
|
22
|
-
return `For each thread marked \`[suggestion]\` under \`${sectionName}\`: run \`${command}\` to retrieve the patch and suggested commit. The CLI does not mutate the working tree — apply the patch yourself (run \`git apply\` with the diff shown, or edit the file directly using the line range), then stage the listed file and run the suggested \`git commit\` from the \`## Instructions\` section. Human-authored thread IDs are replied to by the
|
|
22
|
+
return `For each thread marked \`[suggestion]\` under \`${sectionName}\`: run \`${command}\` to retrieve the patch and suggested commit. The CLI does not mutate the working tree — apply the patch yourself (run \`git apply\` with the diff shown, or edit the file directly using the line range), then stage the listed file and run the suggested \`git commit\` from the \`## Instructions\` section. Human-authored thread IDs are replied to by the apply command below; Shepherd does not auto-resolve them.${driftHint} Do not retry the same command.`;
|
|
23
23
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CommitSuggestionResult, GlobalOptions } from "../types.mts";
|
|
2
|
+
export interface CommitSuggestionOptions extends GlobalOptions {
|
|
3
|
+
threadId: string;
|
|
4
|
+
message: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
}
|
|
7
|
+
/** @deprecated Hidden implementation for `commit-suggestion`; use `build-suggestion-patch`. */
|
|
8
|
+
export declare function runCommitSuggestion(opts: CommitSuggestionOptions): Promise<CommitSuggestionResult>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execFile as execFileCb } from "node:child_process";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
3
4
|
import { promisify } from "node:util";
|
|
4
5
|
import { getRepoInfo, getCurrentPrNumber, getCurrentBranch } from "../github/client.mjs";
|
|
5
6
|
import { fetchPrBatch } from "../github/batch.mjs";
|
|
@@ -7,7 +8,9 @@ import { parseSuggestion, isCommittableSuggestion } from "../suggestions/parse.m
|
|
|
7
8
|
import { buildUnifiedDiff } from "../suggestions/patch.mjs";
|
|
8
9
|
import { EXIT, ShepherdError } from "../exit-codes.mjs";
|
|
9
10
|
import { buildPrShepherdCommand } from "../cli/runner.mjs";
|
|
11
|
+
import { getEffectiveCwd, getExecutionCwd } from "../execution-context.mjs";
|
|
10
12
|
const execFile = promisify(execFileCb);
|
|
13
|
+
/** @deprecated Hidden implementation for `commit-suggestion`; use `build-suggestion-patch`. */
|
|
11
14
|
export async function runCommitSuggestion(opts) {
|
|
12
15
|
if (!opts.threadId) {
|
|
13
16
|
throw new ShepherdError("--thread-id is required", EXIT.USAGE);
|
|
@@ -21,7 +24,9 @@ export async function runCommitSuggestion(opts) {
|
|
|
21
24
|
throw new ShepherdError("No open PR found for current branch. Pass a PR number explicitly.", EXIT.UNAVAILABLE);
|
|
22
25
|
}
|
|
23
26
|
const currentBranch = await getCurrentBranch();
|
|
24
|
-
const { stdout: localHeadOut } = await execFile("git", ["rev-parse", "HEAD"]
|
|
27
|
+
const { stdout: localHeadOut } = await execFile("git", ["rev-parse", "HEAD"], {
|
|
28
|
+
cwd: getExecutionCwd(),
|
|
29
|
+
});
|
|
25
30
|
const localHeadSha = localHeadOut.trim();
|
|
26
31
|
const { data } = await fetchPrBatch(prNumber, repo);
|
|
27
32
|
if (!data.headRepoWithOwner) {
|
|
@@ -53,14 +58,9 @@ export async function runCommitSuggestion(opts) {
|
|
|
53
58
|
}
|
|
54
59
|
// Validate the target file is clean before generating the patch, so the emitted
|
|
55
60
|
// `git add -- <file>` instruction cannot accidentally stage unrelated local edits.
|
|
56
|
-
const { stdout: fileStatus } = await execFile("git", [
|
|
57
|
-
"status",
|
|
58
|
-
"--porcelain",
|
|
59
|
-
"--",
|
|
60
|
-
thread.path,
|
|
61
|
-
]);
|
|
61
|
+
const { stdout: fileStatus } = await execFile("git", ["status", "--porcelain", "--", thread.path], { cwd: getExecutionCwd() });
|
|
62
62
|
if (fileStatus.trim() !== "") {
|
|
63
|
-
throw new ShepherdError(`${thread.path} has uncommitted changes. Commit or stash them before running
|
|
63
|
+
throw new ShepherdError(`${thread.path} has uncommitted changes. Commit or stash them before running build-suggestion-patch.`, EXIT.UNAVAILABLE);
|
|
64
64
|
}
|
|
65
65
|
const parsed = parseSuggestion(thread.body);
|
|
66
66
|
if (!parsed) {
|
|
@@ -73,7 +73,7 @@ export async function runCommitSuggestion(opts) {
|
|
|
73
73
|
const startLine = thread.startLine ?? thread.line;
|
|
74
74
|
const endLine = thread.line;
|
|
75
75
|
const filePath = thread.path;
|
|
76
|
-
const originalContent = await readFile(filePath, "utf8");
|
|
76
|
+
const originalContent = await readFile(resolve(getEffectiveCwd(), filePath), "utf8");
|
|
77
77
|
const patch = buildUnifiedDiff({
|
|
78
78
|
path: filePath,
|
|
79
79
|
originalContent,
|
|
@@ -94,7 +94,8 @@ export async function runCommitSuggestion(opts) {
|
|
|
94
94
|
...commitBodyArg.split("\n\n").map((p) => `-m ${sq(p)}`),
|
|
95
95
|
].join(" ");
|
|
96
96
|
const resolveCommand = buildPrShepherdCommand([
|
|
97
|
-
"
|
|
97
|
+
"apply",
|
|
98
|
+
"review",
|
|
98
99
|
String(prNumber),
|
|
99
100
|
"--resolve-thread-ids",
|
|
100
101
|
opts.threadId,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AgentCheck, ResolveCommand, Review } from "../../types.mts";
|
|
2
|
+
/** Build the stale-CR clause appended to the `## Changes-requested reviews` instruction. */
|
|
3
|
+
export declare function buildCrStaleClause(reviews: Review[]): string;
|
|
4
|
+
/**
|
|
5
|
+
* Build the optional behind-base push hint. Empty unless the branch is actually behind its base
|
|
6
|
+
* and the user configured a non-blank `iterate.behindBaseHint` — the CLI never prescribes
|
|
7
|
+
* rebase/merge mechanics itself (see "Keep skills and loop prompts minimal" in CLAUDE.md); this
|
|
8
|
+
* only echoes back the caller's own configured pointer. `hint` is trimmed and type-checked at the
|
|
9
|
+
* point of use (rather than at config load) so a malformed rc file value (non-string, or
|
|
10
|
+
* whitespace-only) degrades to "no hint" instead of rendering garbage into agent-facing text or
|
|
11
|
+
* discarding the rest of the user's config.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildBehindBaseHintInstruction(baseBranch: string, hint: string, isBehind: boolean): string[];
|
|
14
|
+
/** Build the `Run the apply review: command` instruction, including its optional substitution hint. */
|
|
15
|
+
export declare function buildResolveCommandInstruction(resolveCommand: ResolveCommand): string[];
|
|
16
|
+
export declare function buildFailingCheckInstructions(checks: AgentCheck[]): string[];
|
|
@@ -23,13 +23,13 @@ export function buildBehindBaseHintInstruction(baseBranch, hint, isBehind) {
|
|
|
23
23
|
return [];
|
|
24
24
|
return [`The branch is behind \`origin/${baseBranch}\` — ${trimmedHint} before pushing.`];
|
|
25
25
|
}
|
|
26
|
-
/** Build the `Run the
|
|
26
|
+
/** Build the `Run the apply review: command` instruction, including its optional substitution hint. */
|
|
27
27
|
export function buildResolveCommandInstruction(resolveCommand) {
|
|
28
28
|
if (!resolveCommand.hasMutations)
|
|
29
29
|
return [];
|
|
30
30
|
const instructions = [];
|
|
31
31
|
if ((resolveCommand.replyThreadIds?.length ?? 0) > 0) {
|
|
32
|
-
instructions.push(`Before running the \`
|
|
32
|
+
instructions.push(`Before running the \`apply review:\` command, remove any thread from \`--reply-thread-ids\` if the latest visible comment in that thread is your own prior Shepherd reply. Do not reply to your own comments.`);
|
|
33
33
|
}
|
|
34
34
|
const substituteParts = [];
|
|
35
35
|
if (resolveCommand.requiresHeadSha) {
|
|
@@ -39,7 +39,7 @@ export function buildResolveCommandInstruction(resolveCommand) {
|
|
|
39
39
|
substituteParts.push(`\`$DISMISS_MESSAGE\` with a one-sentence reply/description of what you changed`);
|
|
40
40
|
}
|
|
41
41
|
const substituteHint = substituteParts.length > 0 ? `, substituting ${substituteParts.join(" and ")}` : "";
|
|
42
|
-
instructions.push(`Run the \`
|
|
42
|
+
instructions.push(`Run the \`apply review:\` command shown above${substituteHint}.`);
|
|
43
43
|
return instructions;
|
|
44
44
|
}
|
|
45
45
|
export function buildFailingCheckInstructions(checks) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AgentThread, Review, ResolveCommand, AgentCheck, ReviewThread } from "../../types.mts";
|
|
2
|
+
import { type NormalizedBotUsernames } from "../../comments/authors.mts";
|
|
3
|
+
import type { MinimizeCommentsPolicy } from "../../config/load.mts";
|
|
4
|
+
export declare function classifyReviewSummaries(summaries: {
|
|
5
|
+
firstLook: Review[];
|
|
6
|
+
seen: Review[];
|
|
7
|
+
edited: Review[];
|
|
8
|
+
}, approvals: Review[], minimizeApprovals: boolean, minimizeComments?: MinimizeCommentsPolicy | undefined, botUsernames?: NormalizedBotUsernames, unresolvedThreads?: ReviewThread[], ruleAutoResolveIds?: string[]): {
|
|
9
|
+
minimizeIds: string[];
|
|
10
|
+
selfMinimizeIds: string[];
|
|
11
|
+
firstLookSummaries: Review[];
|
|
12
|
+
editedSummaries: Review[];
|
|
13
|
+
surfacedApprovals: Review[];
|
|
14
|
+
};
|
|
15
|
+
export declare function buildResolveCommand(threads: AgentThread[], resolutionOnlyThreads: ReviewThread[], allCommentIds: string[], reviews: Review[], checks: AgentCheck[], prNumber: number, botUsernames?: NormalizedBotUsernames, ruleAutoResolveThreadIds?: string[]): {
|
|
16
|
+
resolveCommand: ResolveCommand;
|
|
17
|
+
resolveOnlyCommand?: ResolveCommand;
|
|
18
|
+
};
|
|
@@ -17,7 +17,7 @@ export function classifyReviewSummaries(summaries, approvals, minimizeApprovals,
|
|
|
17
17
|
const eligible = (r) => shouldMinimizeAuthor(r.authorType, minimizeComments, r.author, botUsernames) &&
|
|
18
18
|
!blockedReviewIds.has(r.id);
|
|
19
19
|
// First-look summaries still need one tick to surface their body to the agent,
|
|
20
|
-
// so their minimize IDs ride in the agent-facing
|
|
20
|
+
// so their minimize IDs ride in the agent-facing apply command. Seen summaries
|
|
21
21
|
// (already surfaced in a prior tick) have no new content to show — the CLI
|
|
22
22
|
// self-minimizes them in-process (selfMinimizeIds) instead of routing a
|
|
23
23
|
// cosmetic-only mutation through fix_code (issue #313). Edited summaries are
|
|
@@ -80,7 +80,7 @@ export function buildResolveCommand(threads, resolutionOnlyThreads, allCommentId
|
|
|
80
80
|
if (hasMessageMutations && hasResolveOrMinimize) {
|
|
81
81
|
// Split: message-bearing mutations (replies + dismissals) ride in resolveArgv;
|
|
82
82
|
// resolve/minimize mutations go in resolveOnlyArgv so they can run without SHA or message.
|
|
83
|
-
const resolveArgv = buildPrShepherdCommand(["
|
|
83
|
+
const resolveArgv = buildPrShepherdCommand(["apply", "review", String(prNumber)]).argv;
|
|
84
84
|
if (replyThreadIds.length > 0) {
|
|
85
85
|
resolveArgv.push("--reply-thread-ids", replyThreadIds.join(","));
|
|
86
86
|
}
|
|
@@ -99,7 +99,7 @@ export function buildResolveCommand(threads, resolutionOnlyThreads, allCommentId
|
|
|
99
99
|
...(hasDismiss ? { dismissReviewIds } : undefined),
|
|
100
100
|
hasMutations: true,
|
|
101
101
|
};
|
|
102
|
-
const resolveOnlyArgv = buildPrShepherdCommand(["
|
|
102
|
+
const resolveOnlyArgv = buildPrShepherdCommand(["apply", "review", String(prNumber)]).argv;
|
|
103
103
|
if (resolveThreadIds.length > 0) {
|
|
104
104
|
resolveOnlyArgv.push("--resolve-thread-ids", resolveThreadIds.join(","));
|
|
105
105
|
}
|
|
@@ -116,7 +116,7 @@ export function buildResolveCommand(threads, resolutionOnlyThreads, allCommentId
|
|
|
116
116
|
return { resolveCommand, resolveOnlyCommand };
|
|
117
117
|
}
|
|
118
118
|
// Single command: all mutations combined (or only one category present).
|
|
119
|
-
const argv = buildPrShepherdCommand(["
|
|
119
|
+
const argv = buildPrShepherdCommand(["apply", "review", String(prNumber)]).argv;
|
|
120
120
|
if (replyThreadIds.length > 0) {
|
|
121
121
|
argv.push("--reply-thread-ids", replyThreadIds.join(","));
|
|
122
122
|
argv.push("--message", "$DISMISS_MESSAGE");
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { EscalateDetails, EscalateTrigger, ReviewThread } from "../../types.mts";
|
|
2
|
+
interface EscalateCheck {
|
|
3
|
+
triggers: EscalateTrigger[];
|
|
4
|
+
thrashHistory?: EscalateDetails["thrashHistory"];
|
|
5
|
+
}
|
|
6
|
+
export declare function checkEscalateTriggers(actionableThreads: ReviewThread[], threadAttempts: Record<string, number>): EscalateCheck;
|
|
7
|
+
interface BaseBranchLookup {
|
|
8
|
+
branch: string;
|
|
9
|
+
/** True when we could not confirm the branch name from GitHub. Callers must
|
|
10
|
+
* escalate rather than emitting a rebase against a potentially-wrong base. */
|
|
11
|
+
isFallback: boolean;
|
|
12
|
+
/** Populated when `isFallback`; one-line reason shown in escalate output. */
|
|
13
|
+
failureReason?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Validate the base branch name from the GraphQL batch (`report.baseBranch`)
|
|
17
|
+
* and fall back safely if it's missing/unsafe. The branch is interpolated into
|
|
18
|
+
* shell commands by `buildFixInstructions`, so we reject anything outside
|
|
19
|
+
* `[A-Za-z0-9._/-]` to prevent shell injection.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Render a seconds count as an approximate human duration: "8 seconds" below one minute,
|
|
23
|
+
* otherwise whole minutes ("166 minutes", "60 minutes") — matching the precision the
|
|
24
|
+
* generic stall timer has always reported, just adding a seconds tier for sub-minute ages
|
|
25
|
+
* instead of always flooring to "0 minutes".
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatDurationApprox(seconds: number): string;
|
|
28
|
+
export declare function validateBaseBranch(raw: string): BaseBranchLookup;
|
|
29
|
+
export declare function buildEscalateHumanMessage(escalate: Omit<EscalateDetails, "humanMessage">, pr: number): string;
|
|
30
|
+
export declare function buildEscalateSuggestion(triggers: EscalateTrigger[], detail?: string): string;
|
|
31
|
+
export {};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { loadConfig } from "../../config/load.mjs";
|
|
2
|
+
function renderEscalateAuthor(item) {
|
|
3
|
+
return [`@${item.author}`, item.authorType, item.authorAssociation].filter(Boolean).join(" · ");
|
|
4
|
+
}
|
|
2
5
|
export function checkEscalateTriggers(actionableThreads, threadAttempts) {
|
|
3
6
|
const triggers = [];
|
|
4
7
|
const maxAttempts = loadConfig().iterate.fixAttemptsPerThread;
|
|
@@ -85,21 +88,21 @@ export function buildEscalateHumanMessage(escalate, pr) {
|
|
|
85
88
|
lines.push("");
|
|
86
89
|
for (const t of escalate.unresolvedThreads) {
|
|
87
90
|
const loc = t.path ? `\`${t.path}:${t.line ?? "?"}\`` : "(no location)";
|
|
88
|
-
lines.push(`- thread \`${t.id}\` — ${loc} (
|
|
91
|
+
lines.push(`- thread \`${t.id}\` — ${loc} (${renderEscalateAuthor(t)}):`);
|
|
89
92
|
lines.push("");
|
|
90
93
|
for (const bodyLine of t.body.split("\n"))
|
|
91
94
|
lines.push(` > ${bodyLine}`);
|
|
92
95
|
lines.push("");
|
|
93
96
|
}
|
|
94
97
|
for (const r of escalate.changesRequestedReviews) {
|
|
95
|
-
lines.push(`- review \`${r.id}\` (
|
|
98
|
+
lines.push(`- review \`${r.id}\` (${renderEscalateAuthor(r)}):`);
|
|
96
99
|
lines.push("");
|
|
97
100
|
for (const bodyLine of r.body.split("\n"))
|
|
98
101
|
lines.push(` > ${bodyLine}`);
|
|
99
102
|
lines.push("");
|
|
100
103
|
}
|
|
101
104
|
for (const c of escalate.ambiguousComments) {
|
|
102
|
-
lines.push(`- comment \`${c.id}\` (
|
|
105
|
+
lines.push(`- comment \`${c.id}\` (${renderEscalateAuthor(c)}):`);
|
|
103
106
|
lines.push("");
|
|
104
107
|
for (const bodyLine of c.body.split("\n"))
|
|
105
108
|
lines.push(` > ${bodyLine}`);
|
|
@@ -137,7 +140,7 @@ export function buildEscalateSuggestion(triggers, detail) {
|
|
|
137
140
|
}
|
|
138
141
|
if (triggers.includes("bot-cr-not-dismissed")) {
|
|
139
142
|
const ids = detail ? ` (review IDs: ${detail})` : "";
|
|
140
|
-
return `Bot CHANGES_REQUESTED review(s) remained undismissed past the stall window${ids}. The agent likely dropped \`--dismiss-review-ids\` from a prior
|
|
143
|
+
return `Bot CHANGES_REQUESTED review(s) remained undismissed past the stall window${ids}. The agent likely dropped \`--dismiss-review-ids\` from a prior apply command. Dismiss the review(s) manually (or re-run \`pr-shepherd apply review\` with the IDs) to unblock the PR.`;
|
|
141
144
|
}
|
|
142
145
|
return "Ambiguous state — automated handling cannot proceed safely. Inspect the PR and act manually.";
|
|
143
146
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { IterateCommandOptions, IterateResult, IterateResultBase, Review, ShepherdReport } from "../../types.mts";
|
|
2
|
+
import type { NormalizedBotUsernames } from "../../comments/authors.mts";
|
|
3
|
+
interface HandleFixCodeContext {
|
|
4
|
+
base: IterateResultBase;
|
|
5
|
+
report: ShepherdReport;
|
|
6
|
+
opts: IterateCommandOptions;
|
|
7
|
+
headSha: string;
|
|
8
|
+
stallKey: {
|
|
9
|
+
owner: string;
|
|
10
|
+
repo: string;
|
|
11
|
+
pr: number;
|
|
12
|
+
};
|
|
13
|
+
prNumber: number;
|
|
14
|
+
stallTimeoutSeconds: number;
|
|
15
|
+
repoOwner: string;
|
|
16
|
+
repoName: string;
|
|
17
|
+
reviewSummaryIds: string[];
|
|
18
|
+
firstLookSummaries: Review[];
|
|
19
|
+
editedSummaries: Review[];
|
|
20
|
+
surfacedApprovals: Review[];
|
|
21
|
+
botUsernames: NormalizedBotUsernames;
|
|
22
|
+
ruleAutoResolveThreadIds?: string[];
|
|
23
|
+
}
|
|
24
|
+
export declare function handleFixCode(ctx: HandleFixCodeContext): Promise<IterateResult>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ActiveCheck, IterateResult, IterateResultBase, IterateResultSummary, RelevantCheck, ShepherdReport } from "../../types.mts";
|
|
2
|
+
export { buildAutoCancelRunIdsWithOptions, buildInProgressRunIds, buildRunProtection, } from "./reruns.mts";
|
|
3
|
+
export declare function buildSummary(report: ShepherdReport): IterateResultSummary;
|
|
4
|
+
/** Non-blocking check-name lists (ignored/superseded), omitted from the result when empty. */
|
|
5
|
+
export declare function buildSuppressedCheckFields(report: ShepherdReport): Pick<IterateResultBase, "ignoredNames" | "supersededNames">;
|
|
6
|
+
/** Build the `cancel` result for a merged/closed PR — caller has already updated ready-delay/stall state. */
|
|
7
|
+
export declare function buildTerminalCancelResult(report: ShepherdReport): IterateResult;
|
|
8
|
+
/** Build completed, non-skipped checks relevant to PR readiness. */
|
|
9
|
+
export declare function buildRelevantChecks(report: ShepherdReport): RelevantCheck[];
|
|
10
|
+
export declare function buildActiveChecks(report: ShepherdReport): ActiveCheck[];
|
|
11
|
+
export declare function tryCancelRun(runId: string, owner: string, repo: string): Promise<string | null>;
|
|
12
|
+
export declare function getCurrentHeadSha(): Promise<string | null>;
|
|
13
|
+
export declare function buildWaitLog(base: IterateResultBase): string;
|