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,26 @@
|
|
|
1
|
+
import { type ResolveRateLimitStop } from "../comments/rate-limit.mts";
|
|
2
|
+
import type { GlobalOptions } from "../types.mts";
|
|
3
|
+
export interface MarkFilesAsViewedOptions extends GlobalOptions {
|
|
4
|
+
prNumber?: number;
|
|
5
|
+
files: string[];
|
|
6
|
+
tests?: boolean;
|
|
7
|
+
matchPatterns?: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface MarkFilesAsViewedResult {
|
|
10
|
+
repo: string;
|
|
11
|
+
prNumber: number;
|
|
12
|
+
pullRequestId: string;
|
|
13
|
+
requestedPaths: string[];
|
|
14
|
+
testSelector: boolean;
|
|
15
|
+
matchPatterns: string[];
|
|
16
|
+
matchedPaths: string[];
|
|
17
|
+
markedPaths: string[];
|
|
18
|
+
alreadyViewedPaths: string[];
|
|
19
|
+
missingPaths: string[];
|
|
20
|
+
unmatchedSelectors: string[];
|
|
21
|
+
errors: string[];
|
|
22
|
+
rateLimit?: ResolveRateLimitStop;
|
|
23
|
+
unmarkedPaths?: string[];
|
|
24
|
+
}
|
|
25
|
+
/** @deprecated Hidden implementation for `mark-files-as-viewed`; use `apply files`. */
|
|
26
|
+
export declare function runMarkFilesAsViewed(opts: MarkFilesAsViewedOptions): Promise<MarkFilesAsViewedResult>;
|
|
@@ -23,6 +23,7 @@ const FILES_QUERY = `query PullRequestFiles($owner: String!, $repo: String!, $pr
|
|
|
23
23
|
}`;
|
|
24
24
|
const TEST_FILE_RE = /(^|\/)(tests?|__tests__|spec)(\/|$)|\.(test|spec)\.[cm]?[jt]sx?$|_tests?\.rs$|(^|\/)tests?\.rs$/i;
|
|
25
25
|
const BULK_CHUNK_SIZE = 10;
|
|
26
|
+
/** @deprecated Hidden implementation for `mark-files-as-viewed`; use `apply files`. */
|
|
26
27
|
export async function runMarkFilesAsViewed(opts) {
|
|
27
28
|
const repo = await getRepoInfo();
|
|
28
29
|
const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IterateCommandOptions, IterateResult } from "../types.mts";
|
|
2
|
+
interface PollCommandOptions extends IterateCommandOptions {
|
|
3
|
+
intervalSeconds: number;
|
|
4
|
+
timeoutSeconds: number;
|
|
5
|
+
/** Settle window after first FIX_CODE before returning. Default 60. 0 disables. */
|
|
6
|
+
debounceSeconds?: number;
|
|
7
|
+
quietStatus?: boolean;
|
|
8
|
+
untilTerminal?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** @deprecated Hidden implementation for the legacy `poll` alias. */
|
|
11
|
+
export declare function runPoll(opts: PollCommandOptions): Promise<IterateResult>;
|
|
12
|
+
export {};
|
package/bin/commands/poll.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { runIterate } from "./iterate/index.mjs";
|
|
2
2
|
import { sleep } from "../util/sleep.mjs";
|
|
3
|
+
const DEFAULT_POLL_DEBOUNCE_SECONDS = 60;
|
|
3
4
|
function writeTickProgress(tick, elapsedSeconds, sleepSeconds, verbose) {
|
|
4
5
|
if (verbose) {
|
|
5
6
|
process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s] WAIT — sleeping ${sleepSeconds}s\n`);
|
|
@@ -54,10 +55,18 @@ function writeWaitProgress(opts) {
|
|
|
54
55
|
}
|
|
55
56
|
return signature;
|
|
56
57
|
}
|
|
58
|
+
function writeDebounceProgress(tick, elapsedMs, remainingMs) {
|
|
59
|
+
const elapsedSeconds = Math.round(elapsedMs / 1000);
|
|
60
|
+
const remainingSeconds = Math.round(remainingMs / 1000);
|
|
61
|
+
process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s] FIX_CODE — debounce ${remainingSeconds}s remaining\n`);
|
|
62
|
+
}
|
|
63
|
+
/** @deprecated Hidden implementation for the legacy `poll` alias. */
|
|
57
64
|
export async function runPoll(opts) {
|
|
58
|
-
const { intervalSeconds, timeoutSeconds, quietStatus: quietStatusOpt, untilTerminal: untilTerminalOpt, ...iterateOpts } = opts;
|
|
65
|
+
const { intervalSeconds, timeoutSeconds, debounceSeconds: debounceSecondsOpt, quietStatus: quietStatusOpt, untilTerminal: untilTerminalOpt, ...iterateOpts } = opts;
|
|
59
66
|
const intervalMs = Math.min(intervalSeconds * 1000, MAX_TIMER_MS);
|
|
60
67
|
const timeoutMs = Math.min(timeoutSeconds * 1000, MAX_TIMER_MS);
|
|
68
|
+
const debounceSeconds = debounceSecondsOpt ?? DEFAULT_POLL_DEBOUNCE_SECONDS;
|
|
69
|
+
const debounceMs = Math.min(debounceSeconds * 1000, MAX_TIMER_MS);
|
|
61
70
|
const start = Date.now();
|
|
62
71
|
let tick = 0;
|
|
63
72
|
let lastResult;
|
|
@@ -72,27 +81,30 @@ export async function runPoll(opts) {
|
|
|
72
81
|
// terminal CANCEL/merged result. Pin the PR resolved by the first tick so later ticks target it
|
|
73
82
|
// directly and never re-run branch discovery.
|
|
74
83
|
let prNumber = opts.prNumber;
|
|
84
|
+
let debounceUntil = null;
|
|
75
85
|
while (true) {
|
|
76
86
|
tick += 1;
|
|
77
|
-
|
|
87
|
+
const pastDebounce = debounceUntil !== null && Date.now() >= debounceUntil;
|
|
88
|
+
lastResult = await runIterate({
|
|
89
|
+
...iterateOpts,
|
|
90
|
+
prNumber,
|
|
91
|
+
persistSeen: debounceSeconds === 0 || pastDebounce,
|
|
92
|
+
});
|
|
78
93
|
prNumber ??= lastResult.pr;
|
|
79
|
-
if (lastResult.action
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
const nextSleepMs = intervalMs;
|
|
91
|
-
if (lastResult.action === "wait") {
|
|
94
|
+
if (lastResult.action === "wait" && !pastDebounce) {
|
|
95
|
+
debounceUntil = null;
|
|
96
|
+
const elapsedMs = Date.now() - start;
|
|
97
|
+
if (!untilTerminal) {
|
|
98
|
+
const remainingMs = timeoutMs - elapsedMs;
|
|
99
|
+
if (remainingMs <= 0)
|
|
100
|
+
break;
|
|
101
|
+
if (remainingMs + TIMER_DRIFT_TOLERANCE_MS < intervalMs)
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
92
104
|
lastWaitSignature = writeWaitProgress({
|
|
93
105
|
tick,
|
|
94
106
|
elapsedMs,
|
|
95
|
-
sleepMs:
|
|
107
|
+
sleepMs: intervalMs,
|
|
96
108
|
result: lastResult,
|
|
97
109
|
quietStatus,
|
|
98
110
|
verbose,
|
|
@@ -100,8 +112,31 @@ export async function runPoll(opts) {
|
|
|
100
112
|
});
|
|
101
113
|
if (!quietStatus && !verbose)
|
|
102
114
|
dotsPrinted = true;
|
|
115
|
+
await sleep(intervalMs);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (untilTerminal && lastResult.action === "mark_ready" && !pastDebounce) {
|
|
119
|
+
debounceUntil = null;
|
|
120
|
+
await sleep(intervalMs);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (lastResult.action === "fix_code" && debounceSeconds > 0 && !pastDebounce) {
|
|
124
|
+
debounceUntil ??= Date.now() + debounceMs;
|
|
125
|
+
const remainingMs = debounceUntil - Date.now();
|
|
126
|
+
if (remainingMs > 0) {
|
|
127
|
+
if (dotsPrinted) {
|
|
128
|
+
process.stderr.write("\n");
|
|
129
|
+
dotsPrinted = false;
|
|
130
|
+
}
|
|
131
|
+
writeDebounceProgress(tick, Date.now() - start, remainingMs);
|
|
132
|
+
await sleep(Math.min(intervalMs, remainingMs));
|
|
133
|
+
}
|
|
134
|
+
// Always continue so a tick that *started* before debounceUntil but *finished*
|
|
135
|
+
// after it cannot return with persistSeen: false. The next loop iteration
|
|
136
|
+
// sees pastDebounce and persists.
|
|
137
|
+
continue;
|
|
103
138
|
}
|
|
104
|
-
|
|
139
|
+
break;
|
|
105
140
|
}
|
|
106
141
|
if (dotsPrinted)
|
|
107
142
|
process.stderr.write("\n");
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ready-delay state machine for the shepherd iterate loop.
|
|
3
|
+
*
|
|
4
|
+
* When all READY conditions hold, shepherd writes a `ready-since.txt` marker
|
|
5
|
+
* to the state dir. The loop continues until the PR has been READY for
|
|
6
|
+
* `readyDelaySeconds` consecutively. Any not-READY result resets the timer.
|
|
7
|
+
*/
|
|
8
|
+
interface ReadyDelayState {
|
|
9
|
+
isReady: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* When true, the loop should cancel itself — the PR has been READY for
|
|
12
|
+
* longer than the configured ready-delay.
|
|
13
|
+
*/
|
|
14
|
+
shouldCancel: boolean;
|
|
15
|
+
/** How many seconds remain in the ready-delay. */
|
|
16
|
+
remainingSeconds: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Update the ready-delay state machine and return the current decision.
|
|
20
|
+
*
|
|
21
|
+
* Call this at the end of each sweep iteration:
|
|
22
|
+
* - If `isReady == true`: start or continue the ready timer.
|
|
23
|
+
* - If `isReady == false`: reset the timer.
|
|
24
|
+
*
|
|
25
|
+
* When `shouldCancel == true`, the formatter tells loop-capable agents to cancel
|
|
26
|
+
* the loop and tells one-shot agents to stop.
|
|
27
|
+
*/
|
|
28
|
+
export declare function updateReadyDelay(prNumber: number, isReady: boolean, readyDelaySeconds: number, owner: string, repo: string): Promise<ReadyDelayState>;
|
|
29
|
+
export {};
|
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
* `readyDelaySeconds` consecutively. Any not-READY result resets the timer.
|
|
7
7
|
*/
|
|
8
8
|
import { readFile, writeFile, mkdir, unlink } from "node:fs/promises";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { resolveStateBase } from "../state/base.mjs";
|
|
9
|
+
import { dirname } from "node:path";
|
|
10
|
+
import { resolvePrStatePath } from "../state/base.mjs";
|
|
12
11
|
/**
|
|
13
12
|
* Update the ready-delay state machine and return the current decision.
|
|
14
13
|
*
|
|
@@ -57,16 +56,7 @@ export async function updateReadyDelay(prNumber, isReady, readyDelaySeconds, own
|
|
|
57
56
|
// Helpers
|
|
58
57
|
// ---------------------------------------------------------------------------
|
|
59
58
|
function readySincePath(pr, owner, repo) {
|
|
60
|
-
|
|
61
|
-
["owner", owner],
|
|
62
|
-
["repo", repo],
|
|
63
|
-
]) {
|
|
64
|
-
if (!SAFE_SEGMENT.test(value)) {
|
|
65
|
-
throw new Error(`Invalid path segment "${field}": ${value}`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
const base = resolveStateBase();
|
|
69
|
-
return join(base, `${owner}-${repo}`, String(pr), "ready-since.txt");
|
|
59
|
+
return resolvePrStatePath({ owner, repo, pr }, "ready-since.txt");
|
|
70
60
|
}
|
|
71
61
|
async function safeUnlink(path) {
|
|
72
62
|
try {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type RepoInfo } from "../github/client.mts";
|
|
2
|
+
import type { CiVerdict } from "../checks/classify.mts";
|
|
3
|
+
import type { BatchPrData, MergeStatusResult, ShepherdStatus } from "../types.mts";
|
|
4
|
+
interface ReadyMergeabilityRefresh {
|
|
5
|
+
batchData: BatchPrData;
|
|
6
|
+
mergeStatus: MergeStatusResult;
|
|
7
|
+
status: ShepherdStatus;
|
|
8
|
+
}
|
|
9
|
+
export declare function refreshUnknownMergeability(prNumber: number, repo: RepoInfo, batchData: BatchPrData): Promise<{
|
|
10
|
+
batchData: BatchPrData;
|
|
11
|
+
didRefresh: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function refreshReadyMergeability(prNumber: number, repo: RepoInfo, batchData: BatchPrData, verdict: CiVerdict, unresolvedThreads: number, unresolvedComments: number, changesRequestedCount: number): Promise<ReadyMergeabilityRefresh>;
|
|
14
|
+
export declare function isBlockedByFilteredCheck(mergeStatus: MergeStatusResult, verdict: CiVerdict): boolean;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ResolveOptions } from "../types.mts";
|
|
2
|
+
import type { ResolveCommandOptions } from "./resolve.mts";
|
|
3
|
+
/** @deprecated Hidden implementation for `resolve`; use `apply review`. */
|
|
4
|
+
export declare function runResolveMutate(opts: ResolveCommandOptions & ResolveOptions): Promise<import("../comments/resolve.mts").ResolveResult>;
|
|
@@ -7,6 +7,7 @@ import { markReplySeen } from "../state/seen-comments.mjs";
|
|
|
7
7
|
import { threadTranscriptBody } from "../threads/transcript.mjs";
|
|
8
8
|
import { addPrShepherdMarker } from "../comments/marker.mjs";
|
|
9
9
|
import { EXIT, ShepherdError } from "../exit-codes.mjs";
|
|
10
|
+
/** @deprecated Hidden implementation for `resolve`; use `apply review`. */
|
|
10
11
|
export async function runResolveMutate(opts) {
|
|
11
12
|
const repo = await getRepoInfo();
|
|
12
13
|
const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const SHEPHERD_JOURNAL_SECTION = "## Shepherd Journal";
|
|
2
|
+
export declare const SHEPHERD_JOURNAL_SECTION_PATTERN: RegExp;
|
|
3
|
+
export declare const SHEPHERD_JOURNAL_APPEND_HINT = "If this section already exists, append your entries under it instead of creating a duplicate heading.";
|
|
4
|
+
export declare const SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE = "Review the bodies shown under `## Review summaries (first look)` \u2014 you are seeing these for the first time. Eligible non-human IDs, when present, are already included in `--minimize-comment-ids` in the `apply review:` or `resolve-only:` command above; if any warrants a Shepherd Journal note, append it before applying review mutations.";
|
|
5
|
+
export declare function buildShepherdJournalInstruction(prNumber: number, itemReferenceGuidance: string): string;
|
|
6
|
+
export declare const SHEPHERD_JOURNAL_REFERENCE_GUIDANCE_THREADS_AND_COMMENTS_IN_ITEM_HEADINGS = "For threads and comments, use the markdown link shown in its heading above; for reviews, reference the review ID.";
|
|
7
|
+
export declare const SHEPHERD_JOURNAL_REFERENCE_GUIDANCE_THREADS_AND_COMMENTS_IN_ITEMS = "For threads and comments, use the markdown link shown in each item's bullet above; for reviews, reference the review ID.";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export const SHEPHERD_JOURNAL_SECTION = "## Shepherd Journal";
|
|
2
2
|
export const SHEPHERD_JOURNAL_SECTION_PATTERN = /^##\s+Shepherd\s+Journal$/;
|
|
3
3
|
export const SHEPHERD_JOURNAL_APPEND_HINT = "If this section already exists, append your entries under it instead of creating a duplicate heading.";
|
|
4
|
-
export const SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE = "Review the bodies shown under `## Review summaries (first look)` — you are seeing these for the first time. Eligible non-human IDs, when present, are already included in `--minimize-comment-ids` in the
|
|
4
|
+
export const SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE = "Review the bodies shown under `## Review summaries (first look)` — you are seeing these for the first time. Eligible non-human IDs, when present, are already included in `--minimize-comment-ids` in the `apply review:` or `resolve-only:` command above; if any warrants a Shepherd Journal note, append it before applying review mutations.";
|
|
5
5
|
export function buildShepherdJournalInstruction(prNumber, itemReferenceGuidance) {
|
|
6
6
|
return [
|
|
7
|
-
`For any large decisions or rejections you made this iteration, run \`pr-shepherd journal ${prNumber} '- <decision>'\` to append an entry to the \`${SHEPHERD_JOURNAL_SECTION}\` section.`,
|
|
7
|
+
`For any large decisions or rejections you made this iteration, run \`pr-shepherd apply journal ${prNumber} '- <decision>'\` to append an entry to the \`${SHEPHERD_JOURNAL_SECTION}\` section.`,
|
|
8
8
|
itemReferenceGuidance,
|
|
9
9
|
`The command is idempotent — re-running with the same text is a no-op.`,
|
|
10
10
|
].join(" ");
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AuthorType } from "../types.mts";
|
|
2
|
+
export type NormalizedBotUsernames = ReadonlySet<string>;
|
|
3
|
+
export declare function normalizeBotUsernames(botUsernames?: readonly string[] | undefined | null): NormalizedBotUsernames;
|
|
4
|
+
export declare function normalizeAuthorType(typeName: string | undefined | null, login: string | undefined | null): AuthorType;
|
|
5
|
+
export declare function isHumanAuthor(author: {
|
|
6
|
+
author?: string;
|
|
7
|
+
login?: string;
|
|
8
|
+
authorType?: AuthorType;
|
|
9
|
+
}): boolean;
|
|
10
|
+
export declare function isConfiguredBotAuthor(author: {
|
|
11
|
+
author?: string;
|
|
12
|
+
login?: string;
|
|
13
|
+
authorType?: AuthorType;
|
|
14
|
+
}, botUsernames?: NormalizedBotUsernames): boolean;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { MinimizeCommentsPolicy } from "../config/load.mts";
|
|
2
|
+
import type { AuthorType } from "../types.mts";
|
|
3
|
+
import { type NormalizedBotUsernames } from "./authors.mts";
|
|
4
|
+
export declare function shouldMinimizeAuthor(authorType: AuthorType | undefined, policy: MinimizeCommentsPolicy | undefined, author?: string, botUsernames?: NormalizedBotUsernames): boolean;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ResolveResult } from "./resolve.mts";
|
|
2
|
+
export type ResolveMutationOp = {
|
|
3
|
+
kind: "p";
|
|
4
|
+
id: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: "r";
|
|
7
|
+
id: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: "m";
|
|
10
|
+
id: string;
|
|
11
|
+
} | {
|
|
12
|
+
kind: "d";
|
|
13
|
+
id: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function setPendingOps(result: ResolveResult, ops: ResolveMutationOp[]): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ResolveRateLimitStop {
|
|
2
|
+
message: string;
|
|
3
|
+
retryAfterSeconds?: number;
|
|
4
|
+
limit?: number;
|
|
5
|
+
remaining?: number;
|
|
6
|
+
resetAt?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function rateLimitFromError(err: unknown, fallbackMessage: string): ResolveRateLimitStop | null;
|
|
9
|
+
export declare function rateLimitFromGraphQlResult(messages: string[], meta: {
|
|
10
|
+
rateLimit?: {
|
|
11
|
+
remaining?: unknown;
|
|
12
|
+
limit?: unknown;
|
|
13
|
+
resetAt?: unknown;
|
|
14
|
+
};
|
|
15
|
+
retryAfterSeconds?: unknown;
|
|
16
|
+
stopOnZeroRemaining?: boolean;
|
|
17
|
+
}): ResolveRateLimitStop | undefined;
|
|
18
|
+
export declare function isRateLimitMessage(message: string): boolean;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type RepoInfo } from "../github/client.mts";
|
|
2
|
+
import type { ResolveOptions } from "../types.mts";
|
|
3
|
+
import { type ResolveRateLimitStop } from "./rate-limit.mts";
|
|
4
|
+
export interface ResolveResult {
|
|
5
|
+
repliedThreads: string[];
|
|
6
|
+
resolvedThreads: string[];
|
|
7
|
+
minimizedComments: string[];
|
|
8
|
+
dismissedReviews: string[];
|
|
9
|
+
errors: string[];
|
|
10
|
+
skippedDismissals?: string[];
|
|
11
|
+
skippedHumanResolves?: string[];
|
|
12
|
+
skippedHumanMinimizes?: string[];
|
|
13
|
+
skippedHumanDismissals?: string[];
|
|
14
|
+
skippedNonHumanReplies?: string[];
|
|
15
|
+
rateLimit?: ResolveRateLimitStop;
|
|
16
|
+
unrepliedThreads?: string[];
|
|
17
|
+
unresolvedThreads?: string[];
|
|
18
|
+
unminimizedComments?: string[];
|
|
19
|
+
undismissedReviews?: string[];
|
|
20
|
+
}
|
|
21
|
+
export declare function applyResolveOptions(pr: number, repo: RepoInfo, opts: ResolveOptions): Promise<ResolveResult>;
|
|
22
|
+
/** @deprecated Compatibility alias; use `autoResolveThreads`. */
|
|
23
|
+
export declare function autoResolveOutdated(threadIds: string[]): Promise<{
|
|
24
|
+
resolved: string[];
|
|
25
|
+
errors: string[];
|
|
26
|
+
}>;
|
|
27
|
+
export declare function autoResolveThreads(threadIds: string[]): Promise<{
|
|
28
|
+
resolved: string[];
|
|
29
|
+
errors: string[];
|
|
30
|
+
}>;
|
|
31
|
+
export declare function autoMinimizeComments(minimizeIds: string[]): Promise<{
|
|
32
|
+
minimized: string[];
|
|
33
|
+
errors: string[];
|
|
34
|
+
}>;
|
package/bin/comments/resolve.mjs
CHANGED
|
@@ -56,6 +56,7 @@ export async function applyResolveOptions(pr, repo, opts) {
|
|
|
56
56
|
await bulkApply(replyThreadIds, resolveThreadIds, minimizeCommentIds, filteredDismissReviewIds, opts.dismissMessage ?? "", result);
|
|
57
57
|
return result;
|
|
58
58
|
}
|
|
59
|
+
/** @deprecated Compatibility alias; use `autoResolveThreads`. */
|
|
59
60
|
export async function autoResolveOutdated(threadIds) {
|
|
60
61
|
return autoResolveThreads(threadIds);
|
|
61
62
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type SeenMarker } from "../state/seen-comments.mts";
|
|
2
|
+
import { type NormalizedBotUsernames } from "./authors.mts";
|
|
3
|
+
import type { Review } from "../types.mts";
|
|
4
|
+
interface ReviewVisibility {
|
|
5
|
+
visible: Review[];
|
|
6
|
+
toMarkSeen: Review[];
|
|
7
|
+
}
|
|
8
|
+
export declare function classifyReviewsForDisplay(reviews: Review[], seenMap: Map<string, SeenMarker>): ReviewVisibility;
|
|
9
|
+
/**
|
|
10
|
+
* Bot CHANGES_REQUESTED reviews are different from every other surfaceable
|
|
11
|
+
* item: the agent — not the author — is responsible for dismissing them via
|
|
12
|
+
* `--dismiss-review-ids` after a fix push. If the agent forgets, the review
|
|
13
|
+
* stays in `CHANGES_REQUESTED` state and silently blocks the PR.
|
|
14
|
+
*
|
|
15
|
+
* The standard seen-gate (`classifyReviewsForDisplay`) suppresses items with
|
|
16
|
+
* unchanged bodies, which would also drop the bot CR ID from the
|
|
17
|
+
* `--dismiss-review-ids` flag — making the bug irrecoverable. This function
|
|
18
|
+
* keeps every bot CR in the visible set on every tick, using the seen-map
|
|
19
|
+
* only to pick the render form:
|
|
20
|
+
*
|
|
21
|
+
* - `new` / `edited` → full bullet (caller renders body normally).
|
|
22
|
+
* - `unchanged` → flagged `staleBotCr: true` so the formatter emits a terse
|
|
23
|
+
* one-line reminder.
|
|
24
|
+
*
|
|
25
|
+
* Human-authored CR reviews continue to flow through the standard seen-gate.
|
|
26
|
+
*/
|
|
27
|
+
export declare function classifyChangesRequestedReviewsForDisplay(reviews: Review[], seenMap: Map<string, SeenMarker>, botUsernames: NormalizedBotUsernames): ReviewVisibility;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type SeenMarker } from "../state/seen-comments.mts";
|
|
2
|
+
import { type NormalizedBotUsernames } from "./authors.mts";
|
|
3
|
+
import type { FirstLookThread, ReviewThread } from "../types.mts";
|
|
4
|
+
interface ThreadVisibility {
|
|
5
|
+
activeThreads: ReviewThread[];
|
|
6
|
+
resolutionOnlyThreads: ReviewThread[];
|
|
7
|
+
firstLookThreads: FirstLookThread[];
|
|
8
|
+
toMarkSeen: ReviewThread[];
|
|
9
|
+
}
|
|
10
|
+
export declare function classifyThreadVisibility(threads: ReviewThread[], seenMap: Map<string, SeenMarker>, botUsernames?: NormalizedBotUsernames): ThreadVisibility;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type SeenMarker } from "../state/seen-comments.mts";
|
|
2
|
+
import type { MinimizeCommentsPolicy } from "../config/load.mts";
|
|
3
|
+
import type { ActionableComment, PrComment } from "../types.mts";
|
|
4
|
+
import type { NormalizedBotUsernames } from "./authors.mts";
|
|
5
|
+
interface VisibleCommentClassification {
|
|
6
|
+
actionable: ActionableComment[];
|
|
7
|
+
minimizeIds: string[];
|
|
8
|
+
toMarkSeen: ActionableComment[];
|
|
9
|
+
}
|
|
10
|
+
export declare function classifyVisibleComments(comments: PrComment[], seenMap: Map<string, SeenMarker>, minimizeComments: MinimizeCommentsPolicy | undefined, botUsernames?: NormalizedBotUsernames): VisibleCommentClassification;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
declare const MINIMIZE_COMMENTS_POLICIES: readonly ["all", "bots", "users", "none"];
|
|
2
|
+
export type MinimizeCommentsPolicy = (typeof MINIMIZE_COMMENTS_POLICIES)[number];
|
|
3
|
+
interface PrShepherdConfig {
|
|
4
|
+
/** Optional user classification configuration; preserved for rule consumers. */
|
|
5
|
+
classify?: unknown;
|
|
6
|
+
/** GitHub logins that should be treated as bots even when GitHub reports User/Unknown. */
|
|
7
|
+
botUsernames: string[];
|
|
8
|
+
/** Case-insensitive glob patterns for check/status context names Shepherd should ignore. */
|
|
9
|
+
ignoreChecks: string[];
|
|
10
|
+
iterate: {
|
|
11
|
+
fixAttemptsPerThread: number;
|
|
12
|
+
stallTimeoutMinutes: number;
|
|
13
|
+
/**
|
|
14
|
+
* When `true`, APPROVED-state reviews are also eligible for minimization — defaults to `false`
|
|
15
|
+
* so approvals stay visible. `minimizeComments` still filters by GitHub author type.
|
|
16
|
+
*/
|
|
17
|
+
minimizeApprovals: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Which GitHub author classes should be auto-minimized for minimizable PR comments and review
|
|
20
|
+
* summaries. Items excluded by this policy are still surfaced once (and after edits) via seen
|
|
21
|
+
* markers so they do not repeat forever.
|
|
22
|
+
*/
|
|
23
|
+
minimizeComments: MinimizeCommentsPolicy;
|
|
24
|
+
/**
|
|
25
|
+
* One-liner hint appended to the `fix_code` push instruction when the branch is behind its
|
|
26
|
+
* base — e.g. "rebase --force-with-lease" or "see .agents/skills/git-and-prs.md". Empty
|
|
27
|
+
* (default) omits the hint entirely; the CLI never prescribes rebase/merge mechanics itself.
|
|
28
|
+
*/
|
|
29
|
+
behindBaseHint: string;
|
|
30
|
+
};
|
|
31
|
+
watch: {
|
|
32
|
+
readyDelayMinutes: number;
|
|
33
|
+
};
|
|
34
|
+
resolve: {
|
|
35
|
+
shaPoll: {
|
|
36
|
+
intervalMs: number;
|
|
37
|
+
maxAttempts: number;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
checks: {
|
|
41
|
+
ciTriggerEvents: string[];
|
|
42
|
+
};
|
|
43
|
+
mergeStatus: {
|
|
44
|
+
blockingReviewerLogins: string[];
|
|
45
|
+
};
|
|
46
|
+
actions: {
|
|
47
|
+
autoMinimizeSuppressed: boolean;
|
|
48
|
+
autoMarkReady: boolean;
|
|
49
|
+
/** Case-insensitive glob patterns for workflow/check names Shepherd must not cancel. */
|
|
50
|
+
neverCancelRuns: string[];
|
|
51
|
+
/** @deprecated Accepted for compatibility, ignored by the loader. */
|
|
52
|
+
autoResolveOutdated?: boolean;
|
|
53
|
+
/** @deprecated Accepted for compatibility, ignored by the loader. */
|
|
54
|
+
commitSuggestions?: boolean;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export declare function loadConfig(): PrShepherdConfig;
|
|
58
|
+
/** Reset the config cache — for use in tests that change directories. */
|
|
59
|
+
export declare function _resetConfigCache(): void;
|
|
60
|
+
export {};
|