pr-shepherd 0.48.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +12 -0
- package/bin/api.d.mts +19 -3
- package/bin/api.mjs +57 -8
- package/bin/classify/apply.d.mts +2 -0
- package/bin/classify/apply.mjs +1 -1
- package/bin/cli/default-poll.mjs +1 -0
- package/bin/cli/help-command-pages.d.mts +1 -1
- package/bin/cli/help-iterate-poll-pages.d.mts +1 -1
- package/bin/cli/help-iterate-poll-pages.mjs +10 -5
- package/bin/cli/help-top-page.d.mts +1 -1
- package/bin/cli/help-top-page.mjs +5 -3
- package/bin/cli/help.d.mts +2 -2
- package/bin/cli/poll-handler.mjs +25 -4
- package/bin/cli/poll-summary-emitter.d.mts +4 -0
- package/bin/cli/poll-summary-emitter.mjs +22 -0
- package/bin/cli/poll-summary-formatter.d.mts +2 -0
- package/bin/cli/poll-summary-formatter.mjs +96 -0
- package/bin/cli/poll-targets.d.mts +15 -0
- package/bin/cli/poll-targets.mjs +114 -0
- package/bin/cli/validate-default-args.mjs +1 -4
- package/bin/commands/poll-summary.d.mts +10 -0
- package/bin/commands/poll-summary.mjs +163 -0
- package/bin/commands/ready-delay.d.mts +3 -1
- package/bin/commands/ready-delay.mjs +3 -2
- package/bin/github/gql/poll-stack-summary.gql +33 -0
- package/bin/github/gql/poll-summary-fragment.gql +198 -0
- package/bin/github/poll-summary-checks.d.mts +3 -0
- package/bin/github/poll-summary-checks.mjs +61 -0
- package/bin/github/poll-summary-projector.d.mts +4 -0
- package/bin/github/poll-summary-projector.mjs +81 -0
- package/bin/github/poll-summary-raw.d.mts +134 -0
- package/bin/github/poll-summary-raw.mjs +1 -0
- package/bin/github/poll-summary-review.d.mts +4 -0
- package/bin/github/poll-summary-review.mjs +85 -0
- package/bin/github/poll-summary-route.d.mts +4 -0
- package/bin/github/poll-summary-route.mjs +47 -0
- package/bin/github/poll-summary.d.mts +7 -0
- package/bin/github/poll-summary.mjs +110 -0
- package/bin/github/queries.d.mts +4 -0
- package/bin/github/queries.mjs +4 -0
- package/bin/mcp/server.mjs +39 -6
- package/bin/pr-reference.d.mts +2 -0
- package/bin/pr-reference.mjs +4 -0
- package/bin/types/poll-summary.d.mts +82 -0
- package/bin/types/poll-summary.mjs +1 -0
- package/bin/types.d.mts +1 -0
- package/bin/types.mjs +1 -0
- package/package.json +1 -1
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
- package/plugins/pr-shepherd/.codex.mcp.json +1 -1
- package/plugins/pr-shepherd/.mcp.json +1 -1
- package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +2 -2
package/README.md
CHANGED
|
@@ -135,8 +135,20 @@ pr-shepherd 42 --merge # request head-pinned auto-merge/queue;
|
|
|
135
135
|
pr-shepherd iterate 42 # single tick
|
|
136
136
|
pr-shepherd owner/repo#42 # poll a PR in an explicit repository
|
|
137
137
|
pr-shepherd https://github.com/owner/repo/pull/42
|
|
138
|
+
pr-shepherd 42 43 44 # summarize an explicit same-repository set
|
|
139
|
+
pr-shepherd --stack 43 # summarize every PR in a native GitHub stack
|
|
138
140
|
```
|
|
139
141
|
|
|
142
|
+
Multi-PR and `--stack` polling use compact, read-only GraphQL summaries. They return when any row
|
|
143
|
+
needs agent work, all rows are terminal, the bounded timeout expires, or `--until-terminal` crosses
|
|
144
|
+
a configured GraphQL quota-warning band. Check counts use the same ignored, protected-run,
|
|
145
|
+
superseded-run, and event rules as singular iteration and include active merge-queue commit checks.
|
|
146
|
+
Bounded review/check overflow remains visible without permanently forcing work, and clean rows use
|
|
147
|
+
the configured ready-delay before becoming terminal. Each actionable row includes
|
|
148
|
+
an exact single-PR `pollCommand`; run independent actionable rows, then invoke the aggregate selector again.
|
|
149
|
+
Stack rows are ordered bottom-to-top. API and MCP aggregate calls perform one summary tick and leave
|
|
150
|
+
recurrence to the caller.
|
|
151
|
+
|
|
140
152
|
Polling defaults can be set under `poll` in `.pr-shepherdrc.yml`: `intervalSeconds`, `timeoutSeconds`, `debounceSeconds`, and `quietStatus`. Explicit flags override configuration, including `--no-quiet-status` when a shared config enables quiet output. Quiet status remains off by default.
|
|
141
153
|
|
|
142
154
|
### Apply Review And Journal Changes, Or Select Files
|
package/bin/api.d.mts
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
import { type JournalResult } from "./commands/journal/index.mts";
|
|
2
2
|
import { type MarkFilesAsViewedResult } from "./commands/mark-files-as-viewed.mts";
|
|
3
3
|
import type { ResolveResult } from "./comments/resolve.mts";
|
|
4
|
-
import type { BuildSuggestionPatchesResult, CommitSuggestionResult, IterateCommandOptions, IterateResult } from "./types.mts";
|
|
4
|
+
import type { BuildSuggestionPatchesResult, CommitSuggestionResult, IterateCommandOptions, IterateResult, PollSummaryResult } from "./types.mts";
|
|
5
5
|
export interface CreatePrShepherdOptions {
|
|
6
6
|
/** Working directory used for git, config, and classification-rule lookups. */
|
|
7
7
|
cwd?: string;
|
|
8
8
|
}
|
|
9
9
|
/** A positive PR number, GitHub pull-request URL, or owner/repo#number reference. */
|
|
10
10
|
export type PrReference = number | string;
|
|
11
|
-
|
|
11
|
+
type IterateOptions = Omit<IterateCommandOptions, "format" | "prNumber" | "targetRepository" | "persistSeen" | "fingerprintCache" | "deferQuotaWarning" | "quotaWarningMinimumPollIntervalMinutes">;
|
|
12
|
+
export type SingleIterateInput = IterateOptions & {
|
|
12
13
|
pr?: PrReference;
|
|
14
|
+
prs?: never;
|
|
15
|
+
stack?: never;
|
|
13
16
|
};
|
|
17
|
+
export type AggregateIterateInput = IterateOptions & ({
|
|
18
|
+
prs: PrReference[];
|
|
19
|
+
pr?: never;
|
|
20
|
+
stack?: never;
|
|
21
|
+
} | {
|
|
22
|
+
stack: PrReference;
|
|
23
|
+
pr?: never;
|
|
24
|
+
prs?: never;
|
|
25
|
+
});
|
|
26
|
+
export type IterateInput = SingleIterateInput | AggregateIterateInput;
|
|
14
27
|
export interface ReviewMutationsOperation {
|
|
15
28
|
type: "review_mutations";
|
|
16
29
|
resolveThreadIds?: string[];
|
|
@@ -69,7 +82,9 @@ export interface BuildSuggestionPatchesInput {
|
|
|
69
82
|
suggestions: SuggestionPatchInput[];
|
|
70
83
|
}
|
|
71
84
|
export interface PrShepherd {
|
|
72
|
-
iterate(input?:
|
|
85
|
+
iterate(input?: SingleIterateInput): Promise<IterateResult>;
|
|
86
|
+
iterate(input: AggregateIterateInput): Promise<PollSummaryResult>;
|
|
87
|
+
iterate(input: IterateInput): Promise<IterateResult | PollSummaryResult>;
|
|
73
88
|
apply(input: ApplyInput): Promise<ApplyResult>;
|
|
74
89
|
buildSuggestionPatches(input: BuildSuggestionPatchesInput): Promise<BuildSuggestionPatchesResult>;
|
|
75
90
|
/** Compatibility adapter; prefer buildSuggestionPatches. */
|
|
@@ -90,3 +105,4 @@ export declare class PartialApplyError extends Error {
|
|
|
90
105
|
* read/plan, ordered apply, and suggestion-patch operations.
|
|
91
106
|
*/
|
|
92
107
|
export declare function createPrShepherd(options?: CreatePrShepherdOptions): PrShepherd;
|
|
108
|
+
export {};
|
package/bin/api.mjs
CHANGED
|
@@ -3,12 +3,14 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { runCommitSuggestion } from "./commands/commit-suggestion.mjs";
|
|
4
4
|
import { runSuggestionPatches } from "./commands/suggestion-patches.mjs";
|
|
5
5
|
import { runIterate } from "./commands/iterate/index.mjs";
|
|
6
|
+
import { runPollSummary } from "./commands/poll-summary.mjs";
|
|
6
7
|
import { runJournal } from "./commands/journal/index.mjs";
|
|
7
8
|
import { validateJournalItem } from "./commands/journal/transform.mjs";
|
|
8
9
|
import { runMarkFilesAsViewed, } from "./commands/mark-files-as-viewed.mjs";
|
|
9
10
|
import { runResolveMutate } from "./commands/resolve-mutate.mjs";
|
|
10
11
|
import { runWithExecutionCwd } from "./execution-context.mjs";
|
|
11
|
-
import { parsePrReference, resolveParsedPrTarget, } from "./pr-reference.mjs";
|
|
12
|
+
import { parsePrReference, normalizeRepositoryIdentity, resolveParsedPrTarget, } from "./pr-reference.mjs";
|
|
13
|
+
import { getRepoInfo } from "./github/client.mjs";
|
|
12
14
|
/** Raised before any API mutation when an input cannot be validated. */
|
|
13
15
|
export class PrShepherdValidationError extends Error {
|
|
14
16
|
constructor(message) {
|
|
@@ -34,14 +36,20 @@ export class PartialApplyError extends Error {
|
|
|
34
36
|
*/
|
|
35
37
|
export function createPrShepherd(options = {}) {
|
|
36
38
|
const cwd = options.cwd === undefined ? undefined : resolve(options.cwd);
|
|
39
|
+
function iterate(input = {}) {
|
|
40
|
+
return runWithExecutionCwd(cwd, async () => {
|
|
41
|
+
validateIterateSelectors(input);
|
|
42
|
+
if ("prs" in input || "stack" in input) {
|
|
43
|
+
const target = await resolveAggregateIterateInput(input);
|
|
44
|
+
return runPollSummary(target);
|
|
45
|
+
}
|
|
46
|
+
const { pr: _pr, ...iterateOptions } = input;
|
|
47
|
+
const target = resolvePrReference(input.pr);
|
|
48
|
+
return runIterate({ ...iterateOptions, ...target, format: "json" });
|
|
49
|
+
});
|
|
50
|
+
}
|
|
37
51
|
return Object.freeze({
|
|
38
|
-
iterate
|
|
39
|
-
const { pr: _pr, ...options } = input;
|
|
40
|
-
return runWithExecutionCwd(cwd, async () => {
|
|
41
|
-
const target = resolvePrReference(input.pr);
|
|
42
|
-
return runIterate({ ...options, ...target, format: "json" });
|
|
43
|
-
});
|
|
44
|
-
},
|
|
52
|
+
iterate,
|
|
45
53
|
apply(input) {
|
|
46
54
|
return runWithExecutionCwd(cwd, async () => {
|
|
47
55
|
validateApplyInput(input);
|
|
@@ -114,6 +122,47 @@ export function createPrShepherd(options = {}) {
|
|
|
114
122
|
},
|
|
115
123
|
});
|
|
116
124
|
}
|
|
125
|
+
async function resolveAggregateIterateInput(input) {
|
|
126
|
+
const refs = "prs" in input ? input.prs : [input.stack];
|
|
127
|
+
if (!refs || refs.length === 0) {
|
|
128
|
+
throw new PrShepherdValidationError("iterate.prs must contain at least one PR reference");
|
|
129
|
+
}
|
|
130
|
+
const parsedRefs = refs.map((ref) => {
|
|
131
|
+
const parsed = parsePrReference(ref);
|
|
132
|
+
const prNumber = parsed?.number;
|
|
133
|
+
if (!parsed || !prNumber) {
|
|
134
|
+
throw new PrShepherdValidationError(`Invalid PR reference: ${String(ref)}`);
|
|
135
|
+
}
|
|
136
|
+
return { parsed, prNumber };
|
|
137
|
+
});
|
|
138
|
+
const checkout = parsedRefs.some(({ parsed }) => parsed?.repository === undefined)
|
|
139
|
+
? await getRepoInfo()
|
|
140
|
+
: undefined;
|
|
141
|
+
let repository;
|
|
142
|
+
const numbers = [];
|
|
143
|
+
for (const { parsed, prNumber } of parsedRefs) {
|
|
144
|
+
const target = resolveParsedPrTarget(parsed);
|
|
145
|
+
const nextRepository = target.targetRepository ?? checkout;
|
|
146
|
+
if (repository &&
|
|
147
|
+
normalizeRepositoryIdentity(`${repository.owner}/${repository.name}`) !==
|
|
148
|
+
normalizeRepositoryIdentity(`${nextRepository.owner}/${nextRepository.name}`)) {
|
|
149
|
+
throw new PrShepherdValidationError("aggregate iterate only supports PRs from one repository");
|
|
150
|
+
}
|
|
151
|
+
repository = nextRepository;
|
|
152
|
+
if (!numbers.includes(prNumber))
|
|
153
|
+
numbers.push(prNumber);
|
|
154
|
+
}
|
|
155
|
+
const { pr: _pr, prs: _prs, stack: _stack, ...options } = input;
|
|
156
|
+
return "prs" in input
|
|
157
|
+
? { ...options, prNumbers: numbers, targetRepository: repository }
|
|
158
|
+
: { ...options, stackPrNumber: numbers[0], targetRepository: repository };
|
|
159
|
+
}
|
|
160
|
+
function validateIterateSelectors(input) {
|
|
161
|
+
const selectorCount = ["pr", "prs", "stack"].filter((key) => key in input).length;
|
|
162
|
+
if (selectorCount > 1) {
|
|
163
|
+
throw new PrShepherdValidationError("iterate pr, prs, and stack selectors are mutually exclusive");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
117
166
|
function validateApplyInput(input) {
|
|
118
167
|
if (!input || !Array.isArray(input.operations) || input.operations.length === 0) {
|
|
119
168
|
throw new PrShepherdValidationError("apply requires a non-empty operations array");
|
package/bin/classify/apply.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BatchPrData } from "../types.mts";
|
|
2
|
+
import type { ClassifyItem, ClassifyAction } from "./types.mts";
|
|
2
3
|
import type { LoadedRule } from "./loader.mts";
|
|
3
4
|
export interface ClassifyIndex {
|
|
4
5
|
suppressedIds: Set<string>;
|
|
@@ -14,5 +15,6 @@ export interface BatchPartition {
|
|
|
14
15
|
/** COMMENTED review summary IDs — minimized without surfacing to the agent. */
|
|
15
16
|
ruleAutoResolveReviewSummaryIds: string[];
|
|
16
17
|
}
|
|
18
|
+
export declare function applyRules(rules: LoadedRule[], item: ClassifyItem): ClassifyAction;
|
|
17
19
|
export declare function buildClassifyIndex(rules: LoadedRule[], batch: BatchPrData): ClassifyIndex;
|
|
18
20
|
export declare function partitionBatch(index: ClassifyIndex, batch: BatchPrData): BatchPartition;
|
package/bin/classify/apply.mjs
CHANGED
package/bin/cli/default-poll.mjs
CHANGED
|
@@ -194,7 +194,7 @@ Flags:
|
|
|
194
194
|
PR may be a number or GitHub pull request URL. When omitted, the current branch PR is inferred.
|
|
195
195
|
Exit code: 0 on success; nonzero on failure (sysexits.h — see docs/exit-codes.md).`;
|
|
196
196
|
readonly iterate: "pr-shepherd iterate\n\nRun one iterate tick for a pull request. The no-subcommand form polls; use this subcommand for a single tick.\nThe output contains one action and an action-specific ## Instructions section.\n\nUsage:\n pr-shepherd iterate [PR] [iterate-flags]\n\nIterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Legacy no-op; workflow runs are never cancelled.\n --merge Shepherd through readiness, then emit a merge or merge-queue command.\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields.\n --help, -h Print this help and exit before GitHub, git, config, or log I/O.\n\nDurations accept s/m/h suffixes: 30s, 4.5m, 1h. A bare number is minutes; decimals are allowed only with an explicit unit (4.5m).\n\nActions:\n WAIT No immediate action; continue with the next poll.\n MARK_READY Draft PR was marked ready; continue with the next poll.\n FIX_CODE Agent action is required; follow the instructions, then continue polling.\n CANCEL Stop polling: merged/closed or ready-delay elapsed.\n ESCALATE Stop polling until a human provides direction.\n MERGE Run the emitted merge/queue command, then continue monitoring.\n\nExit codes:\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\n 15 MERGE\n A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
|
|
197
|
-
readonly poll: "pr-shepherd poll\n\nRun iterate repeatedly for
|
|
197
|
+
readonly poll: "pr-shepherd poll\n\nRun iterate repeatedly for one PR, or read compact summaries for an explicit PR set or native\nGitHub stack. Aggregate mode returns when any row needs work, every row is terminal, or timeout.\nPoll exits as soon as iterate returns MARK_READY, CANCEL, or ESCALATE, or when timeout\nreturns the last WAIT result. FIX_CODE starts a --debounce settle window (default:\npoll.debounceSeconds; built-in 1m): poll keeps\niterating at --interval, then runs one more tick after the window and returns that result.\nWith --until-terminal or --merge, poll also continues through MARK_READY.\n\nUsage:\n pr-shepherd poll [PR ...] [poll-flags] [iterate-flags]\n pr-shepherd poll --stack PR [poll-flags] [iterate-flags]\n\nPoll flags:\n --stack PR Select all entries in PR's native GitHub stack, bottom to top.\n --interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: poll.intervalSeconds (built-in 60s).\n --timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: poll.timeoutSeconds (built-in 4.5m).\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: poll.debounceSeconds (built-in 60s). 0 disables.\n --quiet-status Print only changed WAIT snapshots. Overrides poll.quietStatus.\n --no-quiet-status Print every WAIT snapshot. Overrides poll.quietStatus.\n --until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.\n\nForwarded iterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Legacy no-op; workflow runs are never cancelled.\n --merge Shepherd through readiness, then emit a merge or merge-queue command.\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields and detailed per-tick lines.\n --help, -h Print this help and exit before GitHub, git, config, or log I/O.\n\nDurations accept s/m/h suffixes: 30s, 4.5m, 1h. A bare number uses each flag's default unit (seconds\nfor --interval/--timeout/--debounce, minutes for --ready-delay/--stall-timeout); decimals are allowed only with\nan explicit unit (4.5m).\nEach WAIT tick writes an explicit still-running line to stderr by default; poll.quietStatus can change that default, --quiet-status/--no-quiet-status override it, and --verbose emits detailed per-tick lines.\nFIX_CODE debounce writes a remaining-seconds line to stderr. --timeout does not cut an in-flight debounce short.\nWith --until-terminal, --timeout is ignored for WAIT ticks and polling continues until FIX_CODE, MERGE, CANCEL, or ESCALATE. With --merge, --timeout still bounds WAIT ticks; it only continues through MARK_READY while polling remains within that timeout.\n\nExit codes: same as iterate (the final tick's action/reason decides the code).\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT (including a WAIT returned by --timeout)\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\n 15 MERGE\n A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
|
|
198
198
|
readonly clean: `pr-shepherd clean
|
|
199
199
|
|
|
200
200
|
Remove pr-shepherd state files from PR_SHEPHERD_STATE_DIR.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const ITERATE_USAGE = "pr-shepherd iterate\n\nRun one iterate tick for a pull request. The no-subcommand form polls; use this subcommand for a single tick.\nThe output contains one action and an action-specific ## Instructions section.\n\nUsage:\n pr-shepherd iterate [PR] [iterate-flags]\n\nIterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Legacy no-op; workflow runs are never cancelled.\n --merge Shepherd through readiness, then emit a merge or merge-queue command.\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields.\n --help, -h Print this help and exit before GitHub, git, config, or log I/O.\n\nDurations accept s/m/h suffixes: 30s, 4.5m, 1h. A bare number is minutes; decimals are allowed only with an explicit unit (4.5m).\n\nActions:\n WAIT No immediate action; continue with the next poll.\n MARK_READY Draft PR was marked ready; continue with the next poll.\n FIX_CODE Agent action is required; follow the instructions, then continue polling.\n CANCEL Stop polling: merged/closed or ready-delay elapsed.\n ESCALATE Stop polling until a human provides direction.\n MERGE Run the emitted merge/queue command, then continue monitoring.\n\nExit codes:\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\n 15 MERGE\n A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
|
|
2
|
-
export declare const POLL_USAGE = "pr-shepherd poll\n\nRun iterate repeatedly for
|
|
2
|
+
export declare const POLL_USAGE = "pr-shepherd poll\n\nRun iterate repeatedly for one PR, or read compact summaries for an explicit PR set or native\nGitHub stack. Aggregate mode returns when any row needs work, every row is terminal, or timeout.\nPoll exits as soon as iterate returns MARK_READY, CANCEL, or ESCALATE, or when timeout\nreturns the last WAIT result. FIX_CODE starts a --debounce settle window (default:\npoll.debounceSeconds; built-in 1m): poll keeps\niterating at --interval, then runs one more tick after the window and returns that result.\nWith --until-terminal or --merge, poll also continues through MARK_READY.\n\nUsage:\n pr-shepherd poll [PR ...] [poll-flags] [iterate-flags]\n pr-shepherd poll --stack PR [poll-flags] [iterate-flags]\n\nPoll flags:\n --stack PR Select all entries in PR's native GitHub stack, bottom to top.\n --interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: poll.intervalSeconds (built-in 60s).\n --timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: poll.timeoutSeconds (built-in 4.5m).\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: poll.debounceSeconds (built-in 60s). 0 disables.\n --quiet-status Print only changed WAIT snapshots. Overrides poll.quietStatus.\n --no-quiet-status Print every WAIT snapshot. Overrides poll.quietStatus.\n --until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.\n\nForwarded iterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Legacy no-op; workflow runs are never cancelled.\n --merge Shepherd through readiness, then emit a merge or merge-queue command.\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields and detailed per-tick lines.\n --help, -h Print this help and exit before GitHub, git, config, or log I/O.\n\nDurations accept s/m/h suffixes: 30s, 4.5m, 1h. A bare number uses each flag's default unit (seconds\nfor --interval/--timeout/--debounce, minutes for --ready-delay/--stall-timeout); decimals are allowed only with\nan explicit unit (4.5m).\nEach WAIT tick writes an explicit still-running line to stderr by default; poll.quietStatus can change that default, --quiet-status/--no-quiet-status override it, and --verbose emits detailed per-tick lines.\nFIX_CODE debounce writes a remaining-seconds line to stderr. --timeout does not cut an in-flight debounce short.\nWith --until-terminal, --timeout is ignored for WAIT ticks and polling continues until FIX_CODE, MERGE, CANCEL, or ESCALATE. With --merge, --timeout still bounds WAIT ticks; it only continues through MARK_READY while polling remains within that timeout.\n\nExit codes: same as iterate (the final tick's action/reason decides the code).\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT (including a WAIT returned by --timeout)\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\n 15 MERGE\n A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
|
|
3
3
|
/** Public help page for the default PR polling invocation. */
|
|
4
4
|
export declare const DEFAULT_USAGE: string;
|
|
@@ -37,17 +37,20 @@ Exit codes:
|
|
|
37
37
|
A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).`;
|
|
38
38
|
export const POLL_USAGE = `pr-shepherd poll
|
|
39
39
|
|
|
40
|
-
Run iterate repeatedly for
|
|
41
|
-
|
|
40
|
+
Run iterate repeatedly for one PR, or read compact summaries for an explicit PR set or native
|
|
41
|
+
GitHub stack. Aggregate mode returns when any row needs work, every row is terminal, or timeout.
|
|
42
42
|
Poll exits as soon as iterate returns MARK_READY, CANCEL, or ESCALATE, or when timeout
|
|
43
|
-
returns the last WAIT result. FIX_CODE starts a --debounce settle window (default
|
|
43
|
+
returns the last WAIT result. FIX_CODE starts a --debounce settle window (default:
|
|
44
|
+
poll.debounceSeconds; built-in 1m): poll keeps
|
|
44
45
|
iterating at --interval, then runs one more tick after the window and returns that result.
|
|
45
46
|
With --until-terminal or --merge, poll also continues through MARK_READY.
|
|
46
47
|
|
|
47
48
|
Usage:
|
|
48
|
-
pr-shepherd poll [PR] [poll-flags] [iterate-flags]
|
|
49
|
+
pr-shepherd poll [PR ...] [poll-flags] [iterate-flags]
|
|
50
|
+
pr-shepherd poll --stack PR [poll-flags] [iterate-flags]
|
|
49
51
|
|
|
50
52
|
Poll flags:
|
|
53
|
+
--stack PR Select all entries in PR's native GitHub stack, bottom to top.
|
|
51
54
|
--interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: poll.intervalSeconds (built-in 60s).
|
|
52
55
|
--timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: poll.timeoutSeconds (built-in 4.5m).
|
|
53
56
|
--debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: poll.debounceSeconds (built-in 60s). 0 disables.
|
|
@@ -82,4 +85,6 @@ Exit codes: same as iterate (the final tick's action/reason decides the code).
|
|
|
82
85
|
15 MERGE
|
|
83
86
|
A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).`;
|
|
84
87
|
/** Public help page for the default PR polling invocation. */
|
|
85
|
-
export const DEFAULT_USAGE = POLL_USAGE.replace(/^pr-shepherd poll$/m, "pr-shepherd [PR]")
|
|
88
|
+
export const DEFAULT_USAGE = POLL_USAGE.replace(/^pr-shepherd poll$/m, "pr-shepherd [PR]")
|
|
89
|
+
.replace(/^ {2}pr-shepherd poll \[PR \.\.\.\]/m, " pr-shepherd [PR ...]")
|
|
90
|
+
.replace(/^ {2}pr-shepherd poll --stack/m, " pr-shepherd --stack");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const TOP_USAGE = "pr-shepherd\n\nAutonomous PR CI monitor and review-comment resolver for agentic coding tools.\n\nUsage:\n pr-shepherd --version | -v\n pr-shepherd --help | -h\n pr-shepherd [PR] [poll-flags] [iterate-flags]\n pr-shepherd iterate [PR] [iterate-flags]\n pr-shepherd apply review [PR] [review-flags]\n pr-shepherd apply files [PR] [files...] [--tests] [--match REGEX]\n pr-shepherd apply journal [PR] <item> [--dry-run] [--format text|json]\n pr-shepherd journal extract --body-file <path>\n pr-shepherd build-suggestion-patches [PR] --thread-id ID --message MSG [groups...]\n pr-shepherd admin clean <pr|branch|current|repo|all> [value] [flags]\n pr-shepherd admin log-file [--format text|json]\n\nCommands:\n [PR]
|
|
1
|
+
export declare const TOP_USAGE = "pr-shepherd\n\nAutonomous PR CI monitor and review-comment resolver for agentic coding tools.\n\nUsage:\n pr-shepherd --version | -v\n pr-shepherd --help | -h\n pr-shepherd [PR ...] [poll-flags] [iterate-flags]\n pr-shepherd --stack PR [poll-flags] [iterate-flags]\n pr-shepherd iterate [PR] [iterate-flags]\n pr-shepherd apply review [PR] [review-flags]\n pr-shepherd apply files [PR] [files...] [--tests] [--match REGEX]\n pr-shepherd apply journal [PR] <item> [--dry-run] [--format text|json]\n pr-shepherd journal extract --body-file <path>\n pr-shepherd build-suggestion-patches [PR] --thread-id ID --message MSG [groups...]\n pr-shepherd admin clean <pr|branch|current|repo|all> [value] [flags]\n pr-shepherd admin log-file [--format text|json]\n\nCommands:\n [PR ...] Poll one PR, an explicit same-repository set, or a native stack.\n iterate Run one iterate tick (single-tick alias).\n apply review Apply review-state mutations after fixes.\n apply files Mark selected changed files as viewed.\n apply journal Append a list item to the Shepherd Journal details block of a PR body.\n journal extract Extract a validated Shepherd Journal from a local PR-body file as JSON.\n build-suggestion-patches\n Convert ordered GitHub suggestion threads into patches and commit instructions.\n admin clean Remove pr-shepherd state files.\n admin log-file Print the per-worktree debug log path.\n\nPR argument:\n PR may be a number, owner/repo#number, or a GitHub pull request URL.\n Multiple PRs must name one repository. --stack PR selects every entry in PR's native stack.\n When omitted, pr-shepherd infers the current branch's pull request.\n\nCommon flags:\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields and detailed poll-tick lines.\n --help, -h Print help and exit before any GitHub, git, config, or log I/O.\n\nIterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Legacy no-op; workflow runs are never cancelled.\n --merge Shepherd through readiness, then emit a merge or merge-queue command.\n\nPolling flags:\n --interval <duration> Delay between WAIT ticks. Bare number = seconds. Default: poll.intervalSeconds (built-in 60s).\n --timeout <duration> Poll wall-clock cap for WAIT ticks. Bare number = seconds. Default: poll.timeoutSeconds (built-in 4.5m).\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: poll.debounceSeconds (built-in 60s). 0 disables.\n --quiet-status Print only changed WAIT snapshots. Overrides poll.quietStatus.\n --no-quiet-status Print every WAIT snapshot. Overrides poll.quietStatus.\n --until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.\n\nClean variants:\n pr [number] Remove state for one PR. Defaults to current branch PR.\n branch [name] Remove state for a branch's PR. Defaults to current branch.\n current Alias for branch against the current branch.\n repo Remove all state for the current repository.\n all Remove all pr-shepherd state.\n\nExit codes: 0 done, 10-19 PR state, 64-78 shepherd failed (sysexits.h).\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\n 15 MERGE\nSee docs/exit-codes.md for the full sysexits.h error-code table.\n\nDuration examples: 30s, 4.5m, 1h. A bare number uses each flag's default unit (see above); decimals are allowed with an explicit unit (4.5m).\n\nRun 'pr-shepherd <command> --help' for command-specific details.";
|
|
@@ -5,7 +5,8 @@ Autonomous PR CI monitor and review-comment resolver for agentic coding tools.
|
|
|
5
5
|
Usage:
|
|
6
6
|
pr-shepherd --version | -v
|
|
7
7
|
pr-shepherd --help | -h
|
|
8
|
-
pr-shepherd [PR] [poll-flags] [iterate-flags]
|
|
8
|
+
pr-shepherd [PR ...] [poll-flags] [iterate-flags]
|
|
9
|
+
pr-shepherd --stack PR [poll-flags] [iterate-flags]
|
|
9
10
|
pr-shepherd iterate [PR] [iterate-flags]
|
|
10
11
|
pr-shepherd apply review [PR] [review-flags]
|
|
11
12
|
pr-shepherd apply files [PR] [files...] [--tests] [--match REGEX]
|
|
@@ -16,7 +17,7 @@ Usage:
|
|
|
16
17
|
pr-shepherd admin log-file [--format text|json]
|
|
17
18
|
|
|
18
19
|
Commands:
|
|
19
|
-
[PR]
|
|
20
|
+
[PR ...] Poll one PR, an explicit same-repository set, or a native stack.
|
|
20
21
|
iterate Run one iterate tick (single-tick alias).
|
|
21
22
|
apply review Apply review-state mutations after fixes.
|
|
22
23
|
apply files Mark selected changed files as viewed.
|
|
@@ -28,7 +29,8 @@ Commands:
|
|
|
28
29
|
admin log-file Print the per-worktree debug log path.
|
|
29
30
|
|
|
30
31
|
PR argument:
|
|
31
|
-
PR may be a number
|
|
32
|
+
PR may be a number, owner/repo#number, or a GitHub pull request URL.
|
|
33
|
+
Multiple PRs must name one repository. --stack PR selects every entry in PR's native stack.
|
|
32
34
|
When omitted, pr-shepherd infers the current branch's pull request.
|
|
33
35
|
|
|
34
36
|
Common flags:
|
package/bin/cli/help.d.mts
CHANGED
|
@@ -194,7 +194,7 @@ Flags:
|
|
|
194
194
|
PR may be a number or GitHub pull request URL. When omitted, the current branch PR is inferred.
|
|
195
195
|
Exit code: 0 on success; nonzero on failure (sysexits.h — see docs/exit-codes.md).`;
|
|
196
196
|
readonly iterate: "pr-shepherd iterate\n\nRun one iterate tick for a pull request. The no-subcommand form polls; use this subcommand for a single tick.\nThe output contains one action and an action-specific ## Instructions section.\n\nUsage:\n pr-shepherd iterate [PR] [iterate-flags]\n\nIterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Legacy no-op; workflow runs are never cancelled.\n --merge Shepherd through readiness, then emit a merge or merge-queue command.\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields.\n --help, -h Print this help and exit before GitHub, git, config, or log I/O.\n\nDurations accept s/m/h suffixes: 30s, 4.5m, 1h. A bare number is minutes; decimals are allowed only with an explicit unit (4.5m).\n\nActions:\n WAIT No immediate action; continue with the next poll.\n MARK_READY Draft PR was marked ready; continue with the next poll.\n FIX_CODE Agent action is required; follow the instructions, then continue polling.\n CANCEL Stop polling: merged/closed or ready-delay elapsed.\n ESCALATE Stop polling until a human provides direction.\n MERGE Run the emitted merge/queue command, then continue monitoring.\n\nExit codes:\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\n 15 MERGE\n A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
|
|
197
|
-
readonly poll: "pr-shepherd poll\n\nRun iterate repeatedly for
|
|
197
|
+
readonly poll: "pr-shepherd poll\n\nRun iterate repeatedly for one PR, or read compact summaries for an explicit PR set or native\nGitHub stack. Aggregate mode returns when any row needs work, every row is terminal, or timeout.\nPoll exits as soon as iterate returns MARK_READY, CANCEL, or ESCALATE, or when timeout\nreturns the last WAIT result. FIX_CODE starts a --debounce settle window (default:\npoll.debounceSeconds; built-in 1m): poll keeps\niterating at --interval, then runs one more tick after the window and returns that result.\nWith --until-terminal or --merge, poll also continues through MARK_READY.\n\nUsage:\n pr-shepherd poll [PR ...] [poll-flags] [iterate-flags]\n pr-shepherd poll --stack PR [poll-flags] [iterate-flags]\n\nPoll flags:\n --stack PR Select all entries in PR's native GitHub stack, bottom to top.\n --interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: poll.intervalSeconds (built-in 60s).\n --timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: poll.timeoutSeconds (built-in 4.5m).\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: poll.debounceSeconds (built-in 60s). 0 disables.\n --quiet-status Print only changed WAIT snapshots. Overrides poll.quietStatus.\n --no-quiet-status Print every WAIT snapshot. Overrides poll.quietStatus.\n --until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.\n\nForwarded iterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Legacy no-op; workflow runs are never cancelled.\n --merge Shepherd through readiness, then emit a merge or merge-queue command.\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields and detailed per-tick lines.\n --help, -h Print this help and exit before GitHub, git, config, or log I/O.\n\nDurations accept s/m/h suffixes: 30s, 4.5m, 1h. A bare number uses each flag's default unit (seconds\nfor --interval/--timeout/--debounce, minutes for --ready-delay/--stall-timeout); decimals are allowed only with\nan explicit unit (4.5m).\nEach WAIT tick writes an explicit still-running line to stderr by default; poll.quietStatus can change that default, --quiet-status/--no-quiet-status override it, and --verbose emits detailed per-tick lines.\nFIX_CODE debounce writes a remaining-seconds line to stderr. --timeout does not cut an in-flight debounce short.\nWith --until-terminal, --timeout is ignored for WAIT ticks and polling continues until FIX_CODE, MERGE, CANCEL, or ESCALATE. With --merge, --timeout still bounds WAIT ticks; it only continues through MARK_READY while polling remains within that timeout.\n\nExit codes: same as iterate (the final tick's action/reason decides the code).\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT (including a WAIT returned by --timeout)\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\n 15 MERGE\n A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
|
|
198
198
|
readonly clean: `pr-shepherd clean
|
|
199
199
|
|
|
200
200
|
Remove pr-shepherd state files from PR_SHEPHERD_STATE_DIR.
|
|
@@ -258,7 +258,7 @@ On POSIX, the final body-file path entry must be a readable regular file in a tr
|
|
|
258
258
|
symlinks, FIFOs, devices, and unreadable paths exit 66. Unsupported platforms fail closed with exit 66.
|
|
259
259
|
--help, -h Print this help and exit before any I/O.`;
|
|
260
260
|
readonly "log-file": "pr-shepherd log-file\n\nPrint the per-worktree append-only debug log path for the current repository.\nThe log is created by the first non-help pr-shepherd command that initializes logging.\n\nUsage:\n pr-shepherd log-file [--format text|json]\n\nFlags:\n --format text|json Print a raw path or {\"path\": \"...\"} JSON. Default: text.\n --help, -h Print this help and exit before logging setup.\n\nEnvironment:\n PR_SHEPHERD_LOG_DISABLED=1 disables logging.\n PR_SHEPHERD_STATE_DIR overrides the base state directory.\n\nExit code: 0 on success; 1 if repository identity cannot be resolved.";
|
|
261
|
-
readonly top: "pr-shepherd\n\nAutonomous PR CI monitor and review-comment resolver for agentic coding tools.\n\nUsage:\n pr-shepherd --version | -v\n pr-shepherd --help | -h\n pr-shepherd [PR] [poll-flags] [iterate-flags]\n pr-shepherd iterate [PR] [iterate-flags]\n pr-shepherd apply review [PR] [review-flags]\n pr-shepherd apply files [PR] [files...] [--tests] [--match REGEX]\n pr-shepherd apply journal [PR] <item> [--dry-run] [--format text|json]\n pr-shepherd journal extract --body-file <path>\n pr-shepherd build-suggestion-patches [PR] --thread-id ID --message MSG [groups...]\n pr-shepherd admin clean <pr|branch|current|repo|all> [value] [flags]\n pr-shepherd admin log-file [--format text|json]\n\nCommands:\n [PR]
|
|
261
|
+
readonly top: "pr-shepherd\n\nAutonomous PR CI monitor and review-comment resolver for agentic coding tools.\n\nUsage:\n pr-shepherd --version | -v\n pr-shepherd --help | -h\n pr-shepherd [PR ...] [poll-flags] [iterate-flags]\n pr-shepherd --stack PR [poll-flags] [iterate-flags]\n pr-shepherd iterate [PR] [iterate-flags]\n pr-shepherd apply review [PR] [review-flags]\n pr-shepherd apply files [PR] [files...] [--tests] [--match REGEX]\n pr-shepherd apply journal [PR] <item> [--dry-run] [--format text|json]\n pr-shepherd journal extract --body-file <path>\n pr-shepherd build-suggestion-patches [PR] --thread-id ID --message MSG [groups...]\n pr-shepherd admin clean <pr|branch|current|repo|all> [value] [flags]\n pr-shepherd admin log-file [--format text|json]\n\nCommands:\n [PR ...] Poll one PR, an explicit same-repository set, or a native stack.\n iterate Run one iterate tick (single-tick alias).\n apply review Apply review-state mutations after fixes.\n apply files Mark selected changed files as viewed.\n apply journal Append a list item to the Shepherd Journal details block of a PR body.\n journal extract Extract a validated Shepherd Journal from a local PR-body file as JSON.\n build-suggestion-patches\n Convert ordered GitHub suggestion threads into patches and commit instructions.\n admin clean Remove pr-shepherd state files.\n admin log-file Print the per-worktree debug log path.\n\nPR argument:\n PR may be a number, owner/repo#number, or a GitHub pull request URL.\n Multiple PRs must name one repository. --stack PR selects every entry in PR's native stack.\n When omitted, pr-shepherd infers the current branch's pull request.\n\nCommon flags:\n --format text|json Output Markdown text or JSON. Default: text.\n --verbose Include verbose iterate fields and detailed poll-tick lines.\n --help, -h Print help and exit before any GitHub, git, config, or log I/O.\n\nIterate flags:\n --ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.\n --stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.\n --no-auto-mark-ready Do not convert draft PRs to ready for review.\n --no-auto-cancel-actionable Legacy no-op; workflow runs are never cancelled.\n --merge Shepherd through readiness, then emit a merge or merge-queue command.\n\nPolling flags:\n --interval <duration> Delay between WAIT ticks. Bare number = seconds. Default: poll.intervalSeconds (built-in 60s).\n --timeout <duration> Poll wall-clock cap for WAIT ticks. Bare number = seconds. Default: poll.timeoutSeconds (built-in 4.5m).\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: poll.debounceSeconds (built-in 60s). 0 disables.\n --quiet-status Print only changed WAIT snapshots. Overrides poll.quietStatus.\n --no-quiet-status Print every WAIT snapshot. Overrides poll.quietStatus.\n --until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.\n\nClean variants:\n pr [number] Remove state for one PR. Defaults to current branch PR.\n branch [name] Remove state for a branch's PR. Defaults to current branch.\n current Alias for branch against the current branch.\n repo Remove all state for the current repository.\n all Remove all pr-shepherd state.\n\nExit codes: 0 done, 10-19 PR state, 64-78 shepherd failed (sysexits.h).\n 0 CANCEL (merged or ready-delay elapsed)\n 10 WAIT\n 11 MARK_READY\n 12 FIX_CODE\n 13 ESCALATE\n 14 CANCEL (closed without merging)\n 15 MERGE\nSee docs/exit-codes.md for the full sysexits.h error-code table.\n\nDuration examples: 30s, 4.5m, 1h. A bare number uses each flag's default unit (see above); decimals are allowed with an explicit unit (4.5m).\n\nRun 'pr-shepherd <command> --help' for command-specific details.";
|
|
262
262
|
};
|
|
263
263
|
/** Resolve help keys for nested public commands before any command I/O. */
|
|
264
264
|
export declare function helpKeyForArgs(args: string[]): keyof typeof USAGE;
|
package/bin/cli/poll-handler.mjs
CHANGED
|
@@ -6,8 +6,21 @@ import { validateSecondsDurationFlag } from "./duration-flag.mjs";
|
|
|
6
6
|
import { parseIterateFlags } from "./iterate-flags.mjs";
|
|
7
7
|
import { emitIterateResult } from "./iterate-emitter.mjs";
|
|
8
8
|
import { EXIT } from "../exit-codes.mjs";
|
|
9
|
+
import { runAggregatePoll } from "../commands/poll-summary.mjs";
|
|
10
|
+
import { emitPollSummaryResult } from "./poll-summary-emitter.mjs";
|
|
11
|
+
import { parsePollTargets, resolvePollTargets } from "./poll-targets.mjs";
|
|
9
12
|
export async function handlePoll(args) {
|
|
10
|
-
const
|
|
13
|
+
const parsedTargets = parsePollTargets(args);
|
|
14
|
+
if (!parsedTargets)
|
|
15
|
+
return;
|
|
16
|
+
const needsResolution = parsedTargets.stack !== undefined || parsedTargets.refs.length > 1;
|
|
17
|
+
const resolvedTargets = needsResolution
|
|
18
|
+
? await resolvePollTargets(parsedTargets)
|
|
19
|
+
: { prNumbers: [] };
|
|
20
|
+
const isAggregate = resolvedTargets.stackPrNumber !== undefined || resolvedTargets.prNumbers.length > 1;
|
|
21
|
+
const { prNumber, global: commonGlobalOpts, extra: commonExtra } = parseCommonArgs(args);
|
|
22
|
+
const globalOpts = isAggregate ? parsedTargets.global : commonGlobalOpts;
|
|
23
|
+
const extra = isAggregate ? parsedTargets.extra : commonExtra;
|
|
11
24
|
const cfg = loadConfig();
|
|
12
25
|
const flags = parseIterateFlags(extra, cfg);
|
|
13
26
|
if (flags.readyDelaySuffix === null || flags.stallTimeoutSuffix === null)
|
|
@@ -37,9 +50,8 @@ export async function handlePoll(args) {
|
|
|
37
50
|
return;
|
|
38
51
|
}
|
|
39
52
|
const quietStatus = quietStatusFlag || (!noQuietStatusFlag && cfg.poll.quietStatus);
|
|
40
|
-
const
|
|
53
|
+
const shared = {
|
|
41
54
|
...globalOpts,
|
|
42
|
-
prNumber,
|
|
43
55
|
readyDelaySeconds: flags.readyDelaySeconds,
|
|
44
56
|
stallTimeoutSeconds: flags.stallTimeoutSeconds,
|
|
45
57
|
noAutoMarkReady: flags.noAutoMarkReady,
|
|
@@ -50,7 +62,16 @@ export async function handlePoll(args) {
|
|
|
50
62
|
debounceSeconds,
|
|
51
63
|
quietStatus,
|
|
52
64
|
untilTerminal: hasFlag(extra, "--until-terminal"),
|
|
53
|
-
}
|
|
65
|
+
};
|
|
66
|
+
if (isAggregate) {
|
|
67
|
+
const result = await runAggregatePoll({
|
|
68
|
+
...shared,
|
|
69
|
+
...resolvedTargets,
|
|
70
|
+
});
|
|
71
|
+
emitPollSummaryResult(result, { format: globalOpts.format });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const result = await runPoll({ ...shared, prNumber });
|
|
54
75
|
emitIterateResult(result, {
|
|
55
76
|
format: globalOpts.format,
|
|
56
77
|
verbose: globalOpts.verbose ?? false,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { EXIT } from "../exit-codes.mjs";
|
|
2
|
+
import { formatPollSummaryResult } from "./poll-summary-formatter.mjs";
|
|
3
|
+
export function emitPollSummaryResult(result, opts) {
|
|
4
|
+
process.stdout.write(opts.format === "json" ? `${JSON.stringify(result)}\n` : `${formatPollSummaryResult(result)}\n`);
|
|
5
|
+
process.exitCode = pollSummaryExitCode(result);
|
|
6
|
+
}
|
|
7
|
+
function pollSummaryExitCode(result) {
|
|
8
|
+
const actions = new Set(result.prs.map((item) => item.action));
|
|
9
|
+
if (actions.has("escalate"))
|
|
10
|
+
return EXIT.ESCALATE;
|
|
11
|
+
if (actions.has("fix_code"))
|
|
12
|
+
return EXIT.FIX_CODE;
|
|
13
|
+
if (actions.has("merge"))
|
|
14
|
+
return EXIT.MERGE;
|
|
15
|
+
if (actions.has("mark_ready"))
|
|
16
|
+
return EXIT.MARK_READY;
|
|
17
|
+
if (actions.has("wait"))
|
|
18
|
+
return EXIT.WAIT;
|
|
19
|
+
if (result.prs.some((item) => item.reasons.includes("closed")))
|
|
20
|
+
return EXIT.CLOSED;
|
|
21
|
+
return EXIT.OK;
|
|
22
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { formatApiUsage, formatQuotaWarning } from "./api-usage-formatter.mjs";
|
|
2
|
+
import { buildQuotaAwareContinuation } from "../quota-warning.mjs";
|
|
3
|
+
export function formatPollSummaryResult(result) {
|
|
4
|
+
const selection = result.selection.kind === "stack"
|
|
5
|
+
? `stack #${result.selection.stackNumber} anchored at PR #${result.selection.anchor} (${result.selection.stackSize} PRs)`
|
|
6
|
+
: `PRs ${result.selection.requested.map((pr) => `#${pr}`).join(", ")}`;
|
|
7
|
+
const lines = [
|
|
8
|
+
`# Poll summary [${result.reason.toUpperCase()}]`,
|
|
9
|
+
"",
|
|
10
|
+
`**repo** \`${result.repo}\` · **selection** ${selection} · **mode** \`${result.mode}\``,
|
|
11
|
+
"",
|
|
12
|
+
"## Pull requests",
|
|
13
|
+
"",
|
|
14
|
+
...result.prs.map(formatItem),
|
|
15
|
+
];
|
|
16
|
+
const apiUsage = result.apiUsage ? formatApiUsage(result.apiUsage) : null;
|
|
17
|
+
const quotaWarning = formatQuotaWarning(result.quotaWarning);
|
|
18
|
+
if (quotaWarning)
|
|
19
|
+
lines.push("", quotaWarning);
|
|
20
|
+
if (apiUsage)
|
|
21
|
+
lines.push("", apiUsage);
|
|
22
|
+
lines.push("", "## Instructions", "", ...formatInstructions(result));
|
|
23
|
+
return lines.join("\n");
|
|
24
|
+
}
|
|
25
|
+
function formatInstructions(result) {
|
|
26
|
+
if (result.reason === "all_terminal")
|
|
27
|
+
return ["1. Stop — every selected PR is terminal."];
|
|
28
|
+
if (result.quotaWarning && result.reason !== "actionable") {
|
|
29
|
+
return [
|
|
30
|
+
buildQuotaAwareContinuation(result.quotaWarning, "1. This aggregate selection is non-terminal. Before continuing,"),
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
if (result.reason === "waiting" || result.reason === "timeout") {
|
|
34
|
+
return ["1. Run this aggregate selector again when the caller is ready to recheck."];
|
|
35
|
+
}
|
|
36
|
+
const instructions = [
|
|
37
|
+
"1. Choose each non-WAIT, non-CANCEL row that can proceed independently and run or delegate its exact `pollCommand`.",
|
|
38
|
+
"2. Follow each selected one-PR poll's `## Instructions` until it returns `CANCEL` or `ESCALATE`.",
|
|
39
|
+
"3. Run this aggregate poll again after selected work completes; one row's `ESCALATE` does not stop work on other rows.",
|
|
40
|
+
];
|
|
41
|
+
if (result.quotaWarning) {
|
|
42
|
+
instructions[2] = buildQuotaAwareContinuation(result.quotaWarning, "3. After selected work completes,");
|
|
43
|
+
}
|
|
44
|
+
return instructions;
|
|
45
|
+
}
|
|
46
|
+
function formatItem(item) {
|
|
47
|
+
const flags = [item.isDraft ? "draft" : null, item.isInMergeQueue ? "queued" : null]
|
|
48
|
+
.filter((value) => value !== null)
|
|
49
|
+
.join(", ");
|
|
50
|
+
const stack = item.stack
|
|
51
|
+
? ` · stack \`${item.stack.number}\` position \`${item.stack.position}/${item.stack.size}\` base \`${item.stack.baseRefName}\``
|
|
52
|
+
: "";
|
|
53
|
+
const reviewDecision = item.reviewDecision ? ` · reviewDecision \`${item.reviewDecision}\`` : "";
|
|
54
|
+
const stateFlags = flags ? ` · flags \`${flags}\`` : "";
|
|
55
|
+
const blockingReviewer = item.blockingReviewerInProgress
|
|
56
|
+
? " · blocking reviewer `in progress`"
|
|
57
|
+
: "";
|
|
58
|
+
const readyDelay = item.remainingSeconds !== undefined ? ` · ready delay \`${item.remainingSeconds}s\`` : "";
|
|
59
|
+
const checks = item.checks;
|
|
60
|
+
const review = item.review;
|
|
61
|
+
return [
|
|
62
|
+
`- [PR #${item.pr}: ${escapeMarkdownText(item.title)}](${item.url}) [${item.action.toUpperCase()}]`,
|
|
63
|
+
` - state \`${item.state}\` · mergeable \`${item.mergeable}\` · merge \`${item.mergeStateStatus}\`${reviewDecision}${stateFlags}${blockingReviewer}${readyDelay}${stack}`,
|
|
64
|
+
` - head \`${item.headRefName}\` at \`${item.headRefOid}\` · base \`${item.baseRefName}\``,
|
|
65
|
+
...(checks
|
|
66
|
+
? [` - checks: ${formatCounts(checks, checks.incomplete ? ", incomplete" : "")}`]
|
|
67
|
+
: []),
|
|
68
|
+
...(review
|
|
69
|
+
? [` - review: ${formatCounts(review, review.incomplete ? ", incomplete" : "")}`]
|
|
70
|
+
: []),
|
|
71
|
+
` - reasons: ${item.reasons.map((reason) => `\`${reason}\``).join(", ")}`,
|
|
72
|
+
...(item.pollCommand ? [` - pollCommand: \`${item.pollCommand}\``] : []),
|
|
73
|
+
].join("\n");
|
|
74
|
+
}
|
|
75
|
+
function formatCounts(counts, suffix) {
|
|
76
|
+
const plural = { inProgress: "in progress" };
|
|
77
|
+
const singular = {
|
|
78
|
+
comments: "comment",
|
|
79
|
+
inProgress: "in progress",
|
|
80
|
+
reviews: "review",
|
|
81
|
+
threads: "thread",
|
|
82
|
+
};
|
|
83
|
+
const rendered = Object.entries(counts)
|
|
84
|
+
.filter(([, count]) => typeof count === "number")
|
|
85
|
+
.map(([name, count]) => `${count} ${count === 1 ? (singular[name] ?? name) : (plural[name] ?? name)}`)
|
|
86
|
+
.join(", ");
|
|
87
|
+
return rendered ? `${rendered}${suffix}` : suffix.replace(/^, /, "");
|
|
88
|
+
}
|
|
89
|
+
function escapeMarkdownText(value) {
|
|
90
|
+
return value
|
|
91
|
+
.replace(/&/g, "&")
|
|
92
|
+
.replace(/</g, "<")
|
|
93
|
+
.replace(/>/g, ">")
|
|
94
|
+
.replace(/[\r\n]+/g, " ")
|
|
95
|
+
.replace(/([\\[\]])/g, "\\$1");
|
|
96
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type RepoInfo } from "../github/client.mts";
|
|
2
|
+
import { type ParsedPrReference } from "../pr-reference.mts";
|
|
3
|
+
import type { GlobalOptions } from "../types.mts";
|
|
4
|
+
export interface ParsedPollTargets {
|
|
5
|
+
refs: ParsedPrReference[];
|
|
6
|
+
stack: ParsedPrReference | undefined;
|
|
7
|
+
global: GlobalOptions;
|
|
8
|
+
extra: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare function parsePollTargets(args: string[]): ParsedPollTargets | null;
|
|
11
|
+
export declare function resolvePollTargets(parsed: ParsedPollTargets): Promise<{
|
|
12
|
+
prNumbers: number[];
|
|
13
|
+
stackPrNumber?: number;
|
|
14
|
+
targetRepository?: RepoInfo;
|
|
15
|
+
}>;
|