pr-shepherd 0.34.0 → 0.36.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 +80 -43
- 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/checks/triage.mjs +11 -3
- 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/args.mjs +1 -0
- package/bin/cli/clean-formatter.d.mts +2 -0
- package/bin/cli/default-poll.d.mts +2 -0
- package/bin/cli/default-poll.mjs +1 -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 +14 -7
- 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 +16 -16
- 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-formatter.mjs +11 -2
- package/bin/cli/iterate-instructions.d.mts +6 -0
- package/bin/cli/iterate-lean.d.mts +12 -0
- package/bin/cli/iterate-lean.mjs +1 -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 +9 -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 +7 -0
- package/bin/commands/check.mjs +28 -23
- 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 +20 -13
- 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/fix-code.mjs +1 -1
- package/bin/commands/iterate/helpers.d.mts +14 -0
- package/bin/commands/iterate/helpers.mjs +19 -7
- package/bin/commands/iterate/index.d.mts +2 -0
- package/bin/commands/iterate/index.mjs +8 -12
- 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 +12 -0
- package/bin/commands/poll.mjs +52 -17
- package/bin/commands/ready-delay.d.mts +29 -0
- package/bin/commands/ready-delay.mjs +3 -13
- 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 +129 -16
- 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-page-helpers.d.mts +45 -0
- package/bin/github/batch-page-helpers.mjs +63 -0
- package/bin/github/batch-page.d.mts +14 -0
- package/bin/github/batch-page.mjs +62 -0
- package/bin/github/batch-parse-suites.d.mts +4 -0
- package/bin/github/batch-parse-suites.mjs +25 -0
- package/bin/github/batch-parser-helpers.d.mts +22 -0
- package/bin/github/batch-parsers-rules.d.mts +6 -0
- package/bin/github/batch-parsers-rules.mjs +122 -0
- package/bin/github/batch-parsers.d.mts +3 -0
- package/bin/github/batch-parsers.mjs +12 -0
- package/bin/github/batch-raw-rules.d.mts +59 -0
- package/bin/github/batch-raw-rules.mjs +1 -0
- package/bin/github/batch-raw-types.d.mts +216 -0
- package/bin/github/batch-response.d.mts +4 -0
- package/bin/github/batch.d.mts +24 -0
- package/bin/github/batch.mjs +14 -120
- 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-page.gql +189 -0
- package/bin/github/gql/batch-pr.gql +79 -21
- package/bin/github/gql/commit-suggestion-thread.gql +40 -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 +6 -0
- package/bin/github/http.mjs +2 -1
- package/bin/github/pagination.d.mts +46 -0
- package/bin/github/pagination.mjs +3 -2
- package/bin/github/queries.d.mts +28 -0
- package/bin/github/queries.mjs +4 -0
- package/bin/github/rest-http.d.mts +7 -0
- package/bin/github/rest-http.mjs +25 -86
- package/bin/github/rest-text.d.mts +1 -0
- package/bin/github/rest-text.mjs +88 -0
- package/bin/github/suggestion-thread.d.mts +9 -0
- package/bin/github/suggestion-thread.mjs +45 -0
- package/bin/github/thread-comments.d.mts +2 -0
- package/bin/github/thread-comments.mjs +12 -8
- 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/merge-status/derive.mjs +2 -0
- package/bin/merge-status/requirements-format.d.mts +3 -0
- package/bin/merge-status/requirements-format.mjs +88 -0
- package/bin/merge-status/requirements.d.mts +2 -0
- package/bin/merge-status/requirements.mjs +51 -0
- package/bin/reporters/agent.d.mts +23 -0
- package/bin/reporters/agent.mjs +3 -0
- package/bin/state/base.d.mts +10 -0
- package/bin/state/base.mjs +23 -0
- package/bin/state/bot-cr-seen.d.mts +51 -0
- package/bin/state/bot-cr-seen.mjs +4 -14
- package/bin/state/fix-attempts.d.mts +27 -0
- package/bin/state/fix-attempts.mjs +3 -13
- package/bin/state/iterate-stall.d.mts +27 -0
- package/bin/state/iterate-stall.mjs +3 -13
- package/bin/state/seen-comments.d.mts +62 -0
- package/bin/state/seen-comments.mjs +6 -13
- 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 +136 -0
- package/bin/types/iterate.d.mts +156 -0
- package/bin/types/merge-requirements.d.mts +82 -0
- package/bin/types/merge-requirements.mjs +2 -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 +10 -0
- package/bin/types.mjs +1 -0
- package/bin/util/markdown.d.mts +1 -0
- package/bin/util/path-segment.d.mts +4 -0
- package/bin/util/path-segment.mjs +2 -0
- package/bin/util/pool.d.mts +2 -0
- package/bin/util/pool.mjs +18 -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
|
@@ -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,13 +1,16 @@
|
|
|
1
1
|
import { execFile as execFileCb } from "node:child_process";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { relative, resolve } from "node:path";
|
|
3
4
|
import { promisify } from "node:util";
|
|
4
5
|
import { getRepoInfo, getCurrentPrNumber, getCurrentBranch } from "../github/client.mjs";
|
|
5
|
-
import {
|
|
6
|
+
import { fetchSuggestionThread } from "../github/suggestion-thread.mjs";
|
|
6
7
|
import { parseSuggestion, isCommittableSuggestion } from "../suggestions/parse.mjs";
|
|
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,9 +24,11 @@ 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
|
-
const
|
|
31
|
+
const data = await fetchSuggestionThread(prNumber, repo, opts.threadId);
|
|
27
32
|
if (!data.headRepoWithOwner) {
|
|
28
33
|
throw new ShepherdError(`PR #${prNumber} head repository is unavailable (fork may have been deleted).`, EXIT.UNAVAILABLE);
|
|
29
34
|
}
|
|
@@ -35,7 +40,7 @@ export async function runCommitSuggestion(opts) {
|
|
|
35
40
|
throw new ShepherdError(`Local HEAD ${localHeadSha} does not match PR head ${data.headRefOid}. ` +
|
|
36
41
|
`Pull/rebase "${data.headRefName}" to the latest PR head and try again.`, EXIT.UNAVAILABLE);
|
|
37
42
|
}
|
|
38
|
-
const thread = data.
|
|
43
|
+
const thread = data.thread;
|
|
39
44
|
if (!thread) {
|
|
40
45
|
throw new ShepherdError(`Thread ${opts.threadId} not found on PR #${prNumber}.`, EXIT.UNAVAILABLE);
|
|
41
46
|
}
|
|
@@ -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,13 @@ 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
|
|
76
|
+
const cwd = getEffectiveCwd();
|
|
77
|
+
const resolvedPath = resolve(cwd, filePath);
|
|
78
|
+
const rel = relative(cwd, resolvedPath);
|
|
79
|
+
if (rel.startsWith("..") || rel === "") {
|
|
80
|
+
throw new ShepherdError(`Thread ${opts.threadId} path escapes the working tree.`, EXIT.UNAVAILABLE);
|
|
81
|
+
}
|
|
82
|
+
const originalContent = await readFile(resolvedPath, "utf8");
|
|
77
83
|
const patch = buildUnifiedDiff({
|
|
78
84
|
path: filePath,
|
|
79
85
|
originalContent,
|
|
@@ -94,7 +100,8 @@ export async function runCommitSuggestion(opts) {
|
|
|
94
100
|
...commitBodyArg.split("\n\n").map((p) => `-m ${sq(p)}`),
|
|
95
101
|
].join(" ");
|
|
96
102
|
const resolveCommand = buildPrShepherdCommand([
|
|
97
|
-
"
|
|
103
|
+
"apply",
|
|
104
|
+
"review",
|
|
98
105
|
String(prNumber),
|
|
99
106
|
"--resolve-thread-ids",
|
|
100
107
|
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 {};
|
|
@@ -166,7 +166,7 @@ export async function handleFixCode(ctx) {
|
|
|
166
166
|
cancelled,
|
|
167
167
|
};
|
|
168
168
|
const result = await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, prospectiveResult, report, reviewSummaryIds);
|
|
169
|
-
if (result.action === "fix_code") {
|
|
169
|
+
if (result.action === "fix_code" && opts.persistSeen !== false) {
|
|
170
170
|
await Promise.allSettled(result.fix.checks.flatMap((ch) => (ch.annotations ?? []).map((a) => markSeen(stallKey, a.id, annotationMarkerBody(a)))));
|
|
171
171
|
}
|
|
172
172
|
return result;
|
|
@@ -0,0 +1,14 @@
|
|
|
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;
|
|
14
|
+
export declare function blockedCancelNote(base: IterateResultBase): string;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { execFile as execFileCb } from "node:child_process";
|
|
2
2
|
import { promisify } from "node:util";
|
|
3
3
|
import { rest } from "../../github/http.mjs";
|
|
4
|
+
import { getExecutionCwd } from "../../execution-context.mjs";
|
|
5
|
+
import { blockedReasonFromRequirements } from "../../merge-status/requirements-format.mjs";
|
|
4
6
|
const execFile = promisify(execFileCb);
|
|
5
7
|
export { buildAutoCancelRunIdsWithOptions, buildInProgressRunIds, buildRunProtection, } from "./reruns.mjs";
|
|
6
8
|
export function buildSummary(report) {
|
|
@@ -39,6 +41,7 @@ export function buildTerminalCancelResult(report) {
|
|
|
39
41
|
summary: buildSummary(report),
|
|
40
42
|
baseBranch: report.baseBranch,
|
|
41
43
|
branchProtection: report.branchProtection,
|
|
44
|
+
mergeRequirements: report.mergeStatus.mergeRequirements,
|
|
42
45
|
checks: buildRelevantChecks(report),
|
|
43
46
|
inProgressChecks: buildActiveChecks(report),
|
|
44
47
|
...buildSuppressedCheckFields(report),
|
|
@@ -114,7 +117,9 @@ export async function tryCancelRun(runId, owner, repo) {
|
|
|
114
117
|
}
|
|
115
118
|
export async function getCurrentHeadSha() {
|
|
116
119
|
try {
|
|
117
|
-
const { stdout } = await execFile("git", ["rev-parse", "HEAD"]
|
|
120
|
+
const { stdout } = await execFile("git", ["rev-parse", "HEAD"], {
|
|
121
|
+
cwd: getExecutionCwd(),
|
|
122
|
+
});
|
|
118
123
|
return stdout.trim();
|
|
119
124
|
}
|
|
120
125
|
catch {
|
|
@@ -131,14 +136,15 @@ export function buildWaitLog(base) {
|
|
|
131
136
|
.join(", ")}`);
|
|
132
137
|
}
|
|
133
138
|
switch (base.mergeStatus) {
|
|
134
|
-
case "BLOCKED":
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
else
|
|
139
|
+
case "BLOCKED": {
|
|
140
|
+
const fromRules = blockedReasonFromRequirements(base.mergeRequirements);
|
|
141
|
+
if (fromRules)
|
|
142
|
+
parts.push(fromRules);
|
|
143
|
+
else if (!base.blockingBotReviewInProgress) {
|
|
140
144
|
parts.push("awaiting human review or branch protection");
|
|
145
|
+
}
|
|
141
146
|
break;
|
|
147
|
+
}
|
|
142
148
|
case "BEHIND":
|
|
143
149
|
parts.push("branch is behind base");
|
|
144
150
|
break;
|
|
@@ -154,3 +160,9 @@ export function buildWaitLog(base) {
|
|
|
154
160
|
}
|
|
155
161
|
return parts.join(" — ");
|
|
156
162
|
}
|
|
163
|
+
export function blockedCancelNote(base) {
|
|
164
|
+
if (base.mergeStatus !== "BLOCKED")
|
|
165
|
+
return "has been ready for review";
|
|
166
|
+
const fromRules = blockedReasonFromRequirements(base.mergeRequirements);
|
|
167
|
+
return fromRules ? `is ${fromRules}` : "is awaiting human review or branch protection resolution";
|
|
168
|
+
}
|
|
@@ -5,7 +5,7 @@ import { graphql } from "../../github/http.mjs";
|
|
|
5
5
|
import { MARK_PR_READY_MUTATION } from "../../github/queries.mjs";
|
|
6
6
|
import { loadConfig } from "../../config/load.mjs";
|
|
7
7
|
import { EXIT, ShepherdError } from "../../exit-codes.mjs";
|
|
8
|
-
import { getCurrentHeadSha, buildSummary, buildRelevantChecks, buildActiveChecks, buildWaitLog, buildSuppressedCheckFields, buildTerminalCancelResult, } from "./helpers.mjs";
|
|
8
|
+
import { getCurrentHeadSha, buildSummary, buildRelevantChecks, buildActiveChecks, buildWaitLog, buildSuppressedCheckFields, buildTerminalCancelResult, blockedCancelNote, } from "./helpers.mjs";
|
|
9
9
|
import { classifyReviewSummaries } from "./classify.mjs";
|
|
10
10
|
import { applyStallGuard } from "./stall.mjs";
|
|
11
11
|
import { clearStallState } from "../../state/iterate-stall.mjs";
|
|
@@ -25,7 +25,10 @@ export async function runIterate(opts) {
|
|
|
25
25
|
const report = await runCheck({
|
|
26
26
|
...opts,
|
|
27
27
|
prNumber,
|
|
28
|
-
|
|
28
|
+
// Outdated Shepherd-visible threads are always resolved by iterate. The
|
|
29
|
+
// old actions.autoResolveOutdated switch is retained only as an ignored
|
|
30
|
+
// compatibility key in the config loader.
|
|
31
|
+
autoResolve: true,
|
|
29
32
|
autoMinimizeSuppressed: config.actions.autoMinimizeSuppressed,
|
|
30
33
|
});
|
|
31
34
|
const [repoOwner, repoName] = report.repo.split("/");
|
|
@@ -48,7 +51,7 @@ export async function runIterate(opts) {
|
|
|
48
51
|
// GitHub can still return a null/error/rate-limit result per ID without
|
|
49
52
|
// throwing (autoMinimizeComments reports this via `errors`, not a rejection);
|
|
50
53
|
// any ID it did not confirm minimized falls back into the agent-facing set so
|
|
51
|
-
// the
|
|
54
|
+
// the apply command remains a working fallback instead of silently dropping it.
|
|
52
55
|
let reviewSummaryIds = minimizeIds;
|
|
53
56
|
if (selfMinimizeIds.length > 0) {
|
|
54
57
|
const { minimized } = await autoMinimizeComments(selfMinimizeIds);
|
|
@@ -87,6 +90,7 @@ export async function runIterate(opts) {
|
|
|
87
90
|
summary: buildSummary(report),
|
|
88
91
|
baseBranch: report.baseBranch,
|
|
89
92
|
branchProtection: report.branchProtection,
|
|
93
|
+
mergeRequirements: report.mergeStatus.mergeRequirements,
|
|
90
94
|
checks: buildRelevantChecks(report),
|
|
91
95
|
inProgressChecks: buildActiveChecks(report),
|
|
92
96
|
...buildSuppressedCheckFields(report),
|
|
@@ -94,15 +98,7 @@ export async function runIterate(opts) {
|
|
|
94
98
|
};
|
|
95
99
|
if (readyState.shouldCancel) {
|
|
96
100
|
await clearStallState(stallKey);
|
|
97
|
-
|
|
98
|
-
if (base.mergeStatus !== "BLOCKED")
|
|
99
|
-
cancelNote = "has been ready for review";
|
|
100
|
-
else if (base.reviewDecision === "REVIEW_REQUIRED")
|
|
101
|
-
cancelNote = "is awaiting human review";
|
|
102
|
-
else if (base.reviewDecision === "APPROVED")
|
|
103
|
-
cancelNote = "is awaiting additional approvals";
|
|
104
|
-
else
|
|
105
|
-
cancelNote = "is awaiting human review or branch protection resolution";
|
|
101
|
+
const cancelNote = blockedCancelNote(base);
|
|
106
102
|
return {
|
|
107
103
|
...base,
|
|
108
104
|
action: "cancel",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AgentThread, AgentComment, AgentCheck, Review, ResolveCommand, FirstLookThread, FirstLookComment, ReviewThread } from "../../types.mts";
|
|
2
|
+
/** Render a resolve command as a shell snippet. Appends `--require-sha "$HEAD_SHA"` when set. */
|
|
3
|
+
export declare function renderResolveCommand(rc: ResolveCommand): string;
|
|
4
|
+
export declare function buildFixInstructions(threads: AgentThread[], actionableComments: AgentComment[], checks: AgentCheck[], changesRequestedReviews: Review[], baseBranch: string, resolveCommand: ResolveCommand, hasConflicts: boolean, prNumber: number, cancelledCount: number, firstLookThreads?: FirstLookThread[], firstLookComments?: FirstLookComment[], firstLookSummaries?: Review[], editedSummaries?: Review[], inProgressRunIds?: string[], resolutionOnlyThreads?: ReviewThread[], resolveOnlyCommand?: ResolveCommand, behindBaseHint?: string, // iterate.behindBaseHint — see buildBehindBaseHintInstruction
|
|
5
|
+
isBehind?: boolean): string[];
|
|
@@ -32,14 +32,16 @@ isBehind = false) {
|
|
|
32
32
|
if (changesRequestedReviews.length > 0)
|
|
33
33
|
actionableSections.push("`## Changes-requested reviews`");
|
|
34
34
|
const sectionRef = actionableSections.length > 0 ? `under ${actionableSections.join(", ")}` : "above";
|
|
35
|
-
const resolveClause = resolveCommand.hasMutations
|
|
35
|
+
const resolveClause = resolveCommand.hasMutations
|
|
36
|
+
? ", then run the `apply review:` command"
|
|
37
|
+
: "";
|
|
36
38
|
if (hasConflicts) {
|
|
37
39
|
// Conflicts make push mandatory regardless of whether code edits are needed.
|
|
38
40
|
instructions.push(`The branch has merge conflicts that must be resolved before merging (see \`**branch**\` above). Apply any code edits for items ${sectionRef}, then commit and push${resolveClause}.`);
|
|
39
41
|
}
|
|
40
42
|
else {
|
|
41
43
|
const skipClause = resolveCommand.hasMutations
|
|
42
|
-
? "skip the commit/push and run the `
|
|
44
|
+
? "skip the commit/push and run the `apply review:` command"
|
|
43
45
|
: "no push is needed";
|
|
44
46
|
instructions.push(`Decide for each item ${sectionRef} whether a code change is warranted. **If any code changes are needed:** apply edits, commit, push${resolveClause}. **If no code changes are needed:** ${skipClause}.`);
|
|
45
47
|
}
|
|
@@ -59,12 +61,12 @@ isBehind = false) {
|
|
|
59
61
|
// is only accurate when threads are present.
|
|
60
62
|
const filesRef = threads.length > 0 ? "each file referenced above" : "the relevant files";
|
|
61
63
|
const suggestionFallback = hasSuggestions
|
|
62
|
-
? ` When applying a \`[suggestion]\` thread manually (e.g. after a failed \`
|
|
64
|
+
? ` When applying a \`[suggestion]\` thread manually (e.g. after a failed \`build-suggestion-patch\` run), replace the exact line range shown in the heading (\`path:startLine-endLine\`) with the replacement shown in its \`Replaces lines …\` block verbatim — an empty replacement deletes those lines, a single blank line replaces the range with one blank line.`
|
|
63
65
|
: "";
|
|
64
66
|
instructions.push(`Apply code fixes: read and edit ${filesRef}.${suggestionFallback}`);
|
|
65
67
|
}
|
|
66
68
|
if (resolutionOnlyThreads.length > 0) {
|
|
67
|
-
instructions.push(`Review the threads under \`## Review threads to resolve\`. Human-authored threads are replied to by the \`
|
|
69
|
+
instructions.push(`Review the threads under \`## Review threads to resolve\`. Human-authored threads are replied to by the \`apply review:\` command shown below; Shepherd does not resolve them. Bot/non-human threads are included in \`--resolve-thread-ids\`.`);
|
|
68
70
|
}
|
|
69
71
|
instructions.push(...buildFailingCheckInstructions(checks));
|
|
70
72
|
if (checks.some((c) => (c.annotations?.length ?? 0) > 0)) {
|
|
@@ -74,7 +76,7 @@ isBehind = false) {
|
|
|
74
76
|
const staleClause = buildCrStaleClause(changesRequestedReviews);
|
|
75
77
|
instructions.push(`For each bullet under \`## Changes-requested reviews\` above: read the review body and apply the requested changes.${staleClause}`);
|
|
76
78
|
if ((resolveCommand.dismissReviewIds?.length ?? 0) > 0)
|
|
77
|
-
instructions.push(`Pass every ID listed in \`--dismiss-review-ids\` to the \`
|
|
79
|
+
instructions.push(`Pass every ID listed in \`--dismiss-review-ids\` to the \`apply review:\` command verbatim — these are bot/non-human CR reviews that the agent (not the author) must dismiss. Dropping an ID leaves the PR in \`CHANGES_REQUESTED\` state; the next tick re-surfaces it as \`[pending dismissal]\` and an unattended bot CR escalates after \`iterate.stallTimeoutMinutes\`.`);
|
|
78
80
|
}
|
|
79
81
|
if (resolveOnlyCommand?.hasMutations)
|
|
80
82
|
instructions.push(`Run the \`resolve-only:\` command shown above — no substitutions needed.`);
|
|
@@ -84,7 +86,7 @@ isBehind = false) {
|
|
|
84
86
|
}
|
|
85
87
|
const firstLookTotal = firstLookThreads.length + firstLookComments.length;
|
|
86
88
|
if (firstLookTotal > 0) {
|
|
87
|
-
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\`, its ID is already included in the \`
|
|
89
|
+
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\`, its ID is already included in the \`apply review:\` command; otherwise do not pass first-look-only IDs to mutation flags.`);
|
|
88
90
|
}
|
|
89
91
|
if (firstLookSummaries.length > 0)
|
|
90
92
|
instructions.push(SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ShepherdReport } from "../../types.mts";
|
|
2
|
+
import type { ProtectedRun } from "../../types/protected-run.mts";
|
|
3
|
+
type FailingCheck = ShepherdReport["checks"]["failing"][number];
|
|
4
|
+
type InProgressCheck = ShepherdReport["checks"]["inProgress"][number];
|
|
5
|
+
type CancellableCheck = FailingCheck | InProgressCheck;
|
|
6
|
+
interface RunProtection {
|
|
7
|
+
protectedRunIds: Set<string>;
|
|
8
|
+
protectedRuns: ProtectedRun[];
|
|
9
|
+
}
|
|
10
|
+
interface BuildRunIdOptions {
|
|
11
|
+
protectedRunIds?: Set<string>;
|
|
12
|
+
}
|
|
13
|
+
export declare function buildAutoCancelRunIdsWithOptions(report: ShepherdReport, opts?: BuildRunIdOptions): string[];
|
|
14
|
+
interface BuildInProgressRunIdOptions {
|
|
15
|
+
suppressProtectedFreshReruns?: boolean;
|
|
16
|
+
protectedRunIds?: Set<string>;
|
|
17
|
+
}
|
|
18
|
+
export declare function buildInProgressRunIds(report: ShepherdReport, cancelledSet: Set<string>, opts?: BuildInProgressRunIdOptions): string[];
|
|
19
|
+
export declare function buildRunProtection(checks: CancellableCheck[], patterns?: string[]): RunProtection;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IterateResult, IterateResultBase, ShepherdReport } from "../../types.mts";
|
|
2
|
+
export declare function applyStallGuard(stallKey: {
|
|
3
|
+
owner: string;
|
|
4
|
+
repo: string;
|
|
5
|
+
pr: number;
|
|
6
|
+
}, stallTimeoutSeconds: number, headSha: string, base: IterateResultBase, prNumber: number, prospectiveResult: IterateResult, report: ShepherdReport, reviewSummaryIds: string[]): Promise<IterateResult>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface RunJournalOptions {
|
|
2
|
+
prNumber: number | undefined;
|
|
3
|
+
rawItem: string;
|
|
4
|
+
dryRun: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface JournalResult {
|
|
7
|
+
prNumber: number;
|
|
8
|
+
mutated: boolean;
|
|
9
|
+
sectionExisted: boolean;
|
|
10
|
+
dryRun: boolean;
|
|
11
|
+
previewBody?: string;
|
|
12
|
+
}
|
|
13
|
+
/** @deprecated Hidden implementation for standalone `journal`; use `apply journal`. */
|
|
14
|
+
export declare function runJournal(opts: RunJournalOptions): Promise<JournalResult>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getRepoInfo, getPullRequestBody, updatePullRequestBody, getCurrentPrNumber, } from "../../github/client.mjs";
|
|
2
2
|
import { validateJournalItem, appendJournalItem } from "./transform.mjs";
|
|
3
|
+
/** @deprecated Hidden implementation for standalone `journal`; use `apply journal`. */
|
|
3
4
|
export async function runJournal(opts) {
|
|
4
5
|
const validation = validateJournalItem(opts.rawItem);
|
|
5
6
|
if (!validation.ok) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface AppendResult {
|
|
2
|
+
body: string;
|
|
3
|
+
mutated: boolean;
|
|
4
|
+
sectionExisted: boolean;
|
|
5
|
+
}
|
|
6
|
+
type ValidationOk = {
|
|
7
|
+
ok: true;
|
|
8
|
+
item: string;
|
|
9
|
+
};
|
|
10
|
+
type ValidationError = {
|
|
11
|
+
ok: false;
|
|
12
|
+
error: string;
|
|
13
|
+
};
|
|
14
|
+
export type ValidationResult = ValidationOk | ValidationError;
|
|
15
|
+
/** Validates that the input is a properly formed markdown list item. */
|
|
16
|
+
export declare function validateJournalItem(input: string): ValidationResult;
|
|
17
|
+
/**
|
|
18
|
+
* Appends a validated list item to the ## Shepherd Journal section of a PR body.
|
|
19
|
+
* Creates the section at the end if absent. Skips if the exact item is already present (idempotent).
|
|
20
|
+
*/
|
|
21
|
+
export declare function appendJournalItem(body: string, item: string): AppendResult;
|
|
22
|
+
export {};
|