pr-shepherd 0.32.2 → 0.32.4
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/bin/cli/duration-flag.mjs +7 -27
- package/bin/cli/exit-codes.mjs +7 -16
- package/bin/cli/handlers.mjs +1 -1
- package/bin/cli/help-command-pages.mjs +11 -7
- package/bin/cli/help-top-page.mjs +5 -5
- package/bin/cli/iterate-flags.mjs +10 -7
- package/bin/cli/poll-handler.mjs +1 -1
- package/bin/commands/iterate/escalate.mjs +16 -4
- package/bin/commands/iterate/stall.mjs +5 -5
- package/bin/commands/poll.mjs +8 -1
- package/package.json +2 -2
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { parseSecondsDurationParts } from "./exit-codes.mjs";
|
|
2
|
-
export function validateSecondsDurationFlag(command, flag, value, presentAsSeparateArg) {
|
|
2
|
+
export function validateSecondsDurationFlag(command, flag, value, presentAsSeparateArg, opts = {}) {
|
|
3
|
+
const bareUnit = opts.defaultUnit === "m" ? "minutes" : "seconds";
|
|
4
|
+
const example = opts.defaultUnit === "m" ? "15m" : "30s";
|
|
3
5
|
if (value === null) {
|
|
4
6
|
if (presentAsSeparateArg) {
|
|
5
|
-
process.stderr.write(`${command}: ${flag} requires a value (e.g. ${flag}
|
|
7
|
+
process.stderr.write(`${command}: ${flag} requires a value (e.g. ${flag} ${example})\n`);
|
|
6
8
|
process.exitCode = 1;
|
|
7
9
|
return null;
|
|
8
10
|
}
|
|
@@ -10,34 +12,12 @@ export function validateSecondsDurationFlag(command, flag, value, presentAsSepar
|
|
|
10
12
|
}
|
|
11
13
|
const trimmed = value.trim();
|
|
12
14
|
if (trimmed.startsWith("--")) {
|
|
13
|
-
process.stderr.write(`${command}: ${flag} requires a value (e.g. ${flag}
|
|
15
|
+
process.stderr.write(`${command}: ${flag} requires a value (e.g. ${flag} ${example})\n`);
|
|
14
16
|
process.exitCode = 1;
|
|
15
17
|
return null;
|
|
16
18
|
}
|
|
17
|
-
if (!parseSecondsDurationParts(trimmed)) {
|
|
18
|
-
process.stderr.write(`${command}: invalid ${flag}: ${value}. Expected a duration like 30s, 4.5m, 1h, or bare
|
|
19
|
-
process.exitCode = 1;
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
return trimmed;
|
|
23
|
-
}
|
|
24
|
-
export function validateDurationFlag(command, flag, value, presentAsSeparateArg) {
|
|
25
|
-
if (value === null) {
|
|
26
|
-
if (presentAsSeparateArg) {
|
|
27
|
-
process.stderr.write(`${command}: ${flag} requires a value (e.g. ${flag} 15m)\n`);
|
|
28
|
-
process.exitCode = 1;
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
33
|
-
const trimmed = value.trim();
|
|
34
|
-
if (trimmed.startsWith("--")) {
|
|
35
|
-
process.stderr.write(`${command}: ${flag} requires a value (e.g. ${flag} 15m)\n`);
|
|
36
|
-
process.exitCode = 1;
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
if (!/^\d+(?:m|min|minutes?|h|hours?)?$/.test(trimmed)) {
|
|
40
|
-
process.stderr.write(`${command}: invalid ${flag}: ${value}. Expected a duration like 5m, 2h, 10m, or 1h.\n`);
|
|
19
|
+
if (!parseSecondsDurationParts(trimmed, opts)) {
|
|
20
|
+
process.stderr.write(`${command}: invalid ${flag}: ${value}. Expected a duration like 30s, 4.5m, 1h, or a bare number (${bareUnit}).\n`);
|
|
41
21
|
process.exitCode = 1;
|
|
42
22
|
return null;
|
|
43
23
|
}
|
package/bin/cli/exit-codes.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { loadConfig } from "../config/load.mjs";
|
|
2
1
|
const SECOND_DURATION_UNITS = new Set([
|
|
3
2
|
"s",
|
|
4
3
|
"sec",
|
|
@@ -12,17 +11,7 @@ const SECOND_DURATION_UNITS = new Set([
|
|
|
12
11
|
"hour",
|
|
13
12
|
"hours",
|
|
14
13
|
]);
|
|
15
|
-
export function
|
|
16
|
-
const m = /^(\d+)(m|min|minutes?|h|hours?)?$/.exec(s.trim());
|
|
17
|
-
if (!m)
|
|
18
|
-
return defaultMinutes ?? loadConfig().watch.readyDelayMinutes;
|
|
19
|
-
const n = parseInt(m[1], 10);
|
|
20
|
-
const unit = m[2] ?? "m";
|
|
21
|
-
if (unit.startsWith("h"))
|
|
22
|
-
return n * 60;
|
|
23
|
-
return n;
|
|
24
|
-
}
|
|
25
|
-
export function parseSecondsDurationParts(s) {
|
|
14
|
+
export function parseSecondsDurationParts(s, opts = {}) {
|
|
26
15
|
const trimmed = s.trim();
|
|
27
16
|
const match = /^(\d+(?:\.\d+)?)([a-z]+)?$/.exec(trimmed);
|
|
28
17
|
if (!match)
|
|
@@ -31,16 +20,18 @@ export function parseSecondsDurationParts(s) {
|
|
|
31
20
|
const explicitUnit = match[2];
|
|
32
21
|
if (!amount || (amount.includes(".") && !explicitUnit))
|
|
33
22
|
return null;
|
|
34
|
-
const unit = explicitUnit ?? "s";
|
|
23
|
+
const unit = explicitUnit ?? opts.defaultUnit ?? "s";
|
|
35
24
|
if (!SECOND_DURATION_UNITS.has(unit))
|
|
36
25
|
return null;
|
|
37
26
|
const value = Number(amount);
|
|
38
|
-
if (!Number.isFinite(value)
|
|
27
|
+
if (!Number.isFinite(value))
|
|
28
|
+
return null;
|
|
29
|
+
if (opts.allowZero ? value < 0 : value <= 0)
|
|
39
30
|
return null;
|
|
40
31
|
return { value, unit };
|
|
41
32
|
}
|
|
42
|
-
export function parseDurationToSeconds(s, defaultSeconds) {
|
|
43
|
-
const parsed = parseSecondsDurationParts(s);
|
|
33
|
+
export function parseDurationToSeconds(s, defaultSeconds, opts = {}) {
|
|
34
|
+
const parsed = parseSecondsDurationParts(s, opts);
|
|
44
35
|
if (!parsed)
|
|
45
36
|
return defaultSeconds;
|
|
46
37
|
if (parsed.unit.startsWith("h"))
|
package/bin/cli/handlers.mjs
CHANGED
|
@@ -94,7 +94,7 @@ export async function handleIterate(args) {
|
|
|
94
94
|
const { prNumber, global: globalOpts, extra } = parseCommonArgs(args);
|
|
95
95
|
const cfg = loadConfig();
|
|
96
96
|
const flags = parseIterateFlags(extra, cfg);
|
|
97
|
-
if (flags.readyDelaySuffix === null)
|
|
97
|
+
if (flags.readyDelaySuffix === null || flags.stallTimeoutSuffix === null)
|
|
98
98
|
return;
|
|
99
99
|
const result = await runIterate({
|
|
100
100
|
...globalOpts,
|
|
@@ -80,14 +80,16 @@ Usage:
|
|
|
80
80
|
pr-shepherd iterate [PR] [iterate-flags]
|
|
81
81
|
|
|
82
82
|
Iterate flags:
|
|
83
|
-
--ready-delay <duration> Settle window before a clean PR cancels. Example: 15m.
|
|
84
|
-
--stall-timeout <duration> Escalate repeated unchanged failures after this duration.
|
|
83
|
+
--ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.
|
|
84
|
+
--stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.
|
|
85
85
|
--no-auto-mark-ready Do not convert draft PRs to ready for review.
|
|
86
86
|
--no-auto-cancel-actionable Do not cancel in-progress runs before actionable fixes.
|
|
87
87
|
--format text|json Output Markdown text or JSON. Default: text.
|
|
88
88
|
--verbose Include verbose iterate fields.
|
|
89
89
|
--help, -h Print this help and exit before GitHub, git, config, or log I/O.
|
|
90
90
|
|
|
91
|
+
Durations accept s/m/h suffixes: 30s, 4.5m, 1h. A bare number is minutes; decimals are allowed only with an explicit unit (4.5m).
|
|
92
|
+
|
|
91
93
|
Actions:
|
|
92
94
|
WAIT No immediate code action; recheck later or use pr-shepherd poll.
|
|
93
95
|
MARK_READY Draft PR was marked ready for review.
|
|
@@ -110,21 +112,23 @@ Usage:
|
|
|
110
112
|
pr-shepherd poll [PR] [poll-flags] [iterate-flags]
|
|
111
113
|
|
|
112
114
|
Poll flags:
|
|
113
|
-
--interval <duration> Sleep between WAIT ticks. Default: 60s.
|
|
114
|
-
--timeout <duration> Maximum wall-clock wait. Default: 4.5m.
|
|
115
|
+
--interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: 60s.
|
|
116
|
+
--timeout <duration> Maximum wall-clock wait. Bare number = seconds. Default: 4.5m.
|
|
115
117
|
--quiet-status During WAIT polling, print only changed status snapshots.
|
|
116
118
|
--until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.
|
|
117
119
|
|
|
118
120
|
Forwarded iterate flags:
|
|
119
|
-
--ready-delay <duration> Settle window before a clean PR cancels. Example: 15m.
|
|
120
|
-
--stall-timeout <duration> Escalate repeated unchanged failures after this duration.
|
|
121
|
+
--ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.
|
|
122
|
+
--stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.
|
|
121
123
|
--no-auto-mark-ready Do not convert draft PRs to ready for review.
|
|
122
124
|
--no-auto-cancel-actionable Do not cancel in-progress runs before actionable fixes.
|
|
123
125
|
--format text|json Output Markdown text or JSON. Default: text.
|
|
124
126
|
--verbose Include verbose iterate fields and detailed per-tick lines.
|
|
125
127
|
--help, -h Print this help and exit before GitHub, git, config, or log I/O.
|
|
126
128
|
|
|
127
|
-
Durations accept
|
|
129
|
+
Durations accept s/m/h suffixes: 30s, 4.5m, 1h. A bare number uses each flag's default unit (seconds
|
|
130
|
+
for --interval/--timeout, minutes for --ready-delay/--stall-timeout); decimals are allowed only with
|
|
131
|
+
an explicit unit (4.5m).
|
|
128
132
|
Each WAIT tick writes a single dot to stderr by default; --quiet-status prints only changed WAIT snapshots, and --verbose emits detailed per-tick lines.
|
|
129
133
|
With --until-terminal, --timeout is ignored for WAIT ticks and polling continues until FIX_CODE, CANCEL, or ESCALATE.
|
|
130
134
|
|
|
@@ -36,14 +36,14 @@ Common flags:
|
|
|
36
36
|
--help, -h Print help and exit before any GitHub, git, config, or log I/O.
|
|
37
37
|
|
|
38
38
|
Iterate flags:
|
|
39
|
-
--ready-delay <duration> Settle window before a clean PR cancels. Example: 15m.
|
|
40
|
-
--stall-timeout <duration> Escalate repeated unchanged failures after this duration.
|
|
39
|
+
--ready-delay <duration> Settle window before a clean PR cancels. Bare number = minutes. Example: 15m.
|
|
40
|
+
--stall-timeout <duration> Escalate repeated unchanged failures after this duration. Bare number = minutes. 0 disables.
|
|
41
41
|
--no-auto-mark-ready Do not convert draft PRs to ready for review.
|
|
42
42
|
--no-auto-cancel-actionable Do not cancel in-progress runs before actionable fixes.
|
|
43
43
|
|
|
44
44
|
Poll flags:
|
|
45
|
-
--interval <duration> Delay between WAIT ticks. Default: 60s.
|
|
46
|
-
--timeout <duration> Poll wall-clock cap. Default: 4.5m.
|
|
45
|
+
--interval <duration> Delay between WAIT ticks. Bare number = seconds. Default: 60s.
|
|
46
|
+
--timeout <duration> Poll wall-clock cap. Bare number = seconds. Default: 4.5m.
|
|
47
47
|
--quiet-status During WAIT polling, print only changed status snapshots.
|
|
48
48
|
--until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.
|
|
49
49
|
|
|
@@ -60,6 +60,6 @@ Exit codes for iterate and poll:
|
|
|
60
60
|
2 CANCEL
|
|
61
61
|
3 ESCALATE
|
|
62
62
|
|
|
63
|
-
Duration examples: 30s, 4.5m, 1h
|
|
63
|
+
Duration 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).
|
|
64
64
|
|
|
65
65
|
Run 'pr-shepherd <command> --help' for command-specific details.`;
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { getFlag, hasFlag } from "./args.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { parseDurationToSeconds } from "./exit-codes.mjs";
|
|
3
|
+
import { validateSecondsDurationFlag } from "./duration-flag.mjs";
|
|
4
|
+
// --ready-delay and --stall-timeout are minute-family flags: a bare number means minutes, and 0 is a
|
|
5
|
+
// valid value (it disables the ready-delay settle window / stall-timeout escalation, respectively).
|
|
6
|
+
const MINUTE_FLAG_OPTS = { defaultUnit: "m", allowZero: true };
|
|
4
7
|
export function parseIterateFlags(extra, cfg) {
|
|
5
8
|
const readyDelayStr = getFlag(extra, "--ready-delay");
|
|
6
|
-
const readyDelaySuffix =
|
|
7
|
-
const readyDelaySeconds =
|
|
9
|
+
const readyDelaySuffix = validateSecondsDurationFlag("pr-shepherd", "--ready-delay", readyDelayStr, hasFlag(extra, "--ready-delay"), MINUTE_FLAG_OPTS);
|
|
10
|
+
const readyDelaySeconds = parseDurationToSeconds(readyDelaySuffix ?? "", cfg.watch.readyDelayMinutes * 60, MINUTE_FLAG_OPTS);
|
|
8
11
|
const noAutoMarkReady = hasFlag(extra, "--no-auto-mark-ready");
|
|
9
12
|
const noAutoCancelActionable = hasFlag(extra, "--no-auto-cancel-actionable");
|
|
10
13
|
const stallTimeoutStr = getFlag(extra, "--stall-timeout");
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
: cfg.iterate.stallTimeoutMinutes * 60;
|
|
14
|
+
const stallTimeoutSuffix = validateSecondsDurationFlag("pr-shepherd", "--stall-timeout", stallTimeoutStr, hasFlag(extra, "--stall-timeout"), MINUTE_FLAG_OPTS);
|
|
15
|
+
const stallTimeoutSeconds = parseDurationToSeconds(stallTimeoutSuffix ?? "", cfg.iterate.stallTimeoutMinutes * 60, MINUTE_FLAG_OPTS);
|
|
14
16
|
return {
|
|
15
17
|
readyDelaySuffix,
|
|
16
18
|
readyDelaySeconds,
|
|
19
|
+
stallTimeoutSuffix,
|
|
17
20
|
stallTimeoutSeconds,
|
|
18
21
|
noAutoMarkReady,
|
|
19
22
|
noAutoCancelActionable,
|
package/bin/cli/poll-handler.mjs
CHANGED
|
@@ -11,7 +11,7 @@ export async function handlePoll(args) {
|
|
|
11
11
|
const { prNumber, global: globalOpts, extra } = parseCommonArgs(args);
|
|
12
12
|
const cfg = loadConfig();
|
|
13
13
|
const flags = parseIterateFlags(extra, cfg);
|
|
14
|
-
if (flags.readyDelaySuffix === null)
|
|
14
|
+
if (flags.readyDelaySuffix === null || flags.stallTimeoutSuffix === null)
|
|
15
15
|
return;
|
|
16
16
|
const intervalStr = getFlag(extra, "--interval");
|
|
17
17
|
const intervalSuffix = validateSecondsDurationFlag("pr-shepherd poll", "--interval", intervalStr, hasFlag(extra, "--interval"));
|
|
@@ -25,6 +25,19 @@ export function checkEscalateTriggers(actionableThreads, threadAttempts) {
|
|
|
25
25
|
* shell commands by `buildFixInstructions`, so we reject anything outside
|
|
26
26
|
* `[A-Za-z0-9._/-]` to prevent shell injection.
|
|
27
27
|
*/
|
|
28
|
+
/**
|
|
29
|
+
* Render a seconds count as an approximate human duration: "8 seconds" below one minute,
|
|
30
|
+
* otherwise whole minutes ("166 minutes", "60 minutes") — matching the precision the
|
|
31
|
+
* generic stall timer has always reported, just adding a seconds tier for sub-minute ages
|
|
32
|
+
* instead of always flooring to "0 minutes".
|
|
33
|
+
*/
|
|
34
|
+
export function formatDurationApprox(seconds) {
|
|
35
|
+
const s = Math.max(0, Math.floor(seconds));
|
|
36
|
+
if (s < 60)
|
|
37
|
+
return `${s} second${s === 1 ? "" : "s"}`;
|
|
38
|
+
const mins = Math.floor(s / 60);
|
|
39
|
+
return `${mins} minute${mins === 1 ? "" : "s"}`;
|
|
40
|
+
}
|
|
28
41
|
export function validateBaseBranch(raw) {
|
|
29
42
|
const trimmed = raw.trim();
|
|
30
43
|
if (trimmed === "") {
|
|
@@ -64,8 +77,7 @@ export function buildEscalateHumanMessage(escalate, pr) {
|
|
|
64
77
|
: c.detailsUrl
|
|
65
78
|
? `external \`${c.detailsUrl}\``
|
|
66
79
|
: "no run ID";
|
|
67
|
-
|
|
68
|
-
lines.push(`- check \`${c.name}\` — ${c.status} ${c.source}, ${target}, waiting ${ageMinutes} minute${ageMinutes === 1 ? "" : "s"}`);
|
|
80
|
+
lines.push(`- check \`${c.name}\` — ${c.status} ${c.source}, ${target}, waiting ${formatDurationApprox(c.ageSeconds)}`);
|
|
69
81
|
if (c.summary)
|
|
70
82
|
lines.push(` > ${c.summary}`);
|
|
71
83
|
}
|
|
@@ -110,8 +122,8 @@ export function buildEscalateHumanMessage(escalate, pr) {
|
|
|
110
122
|
}
|
|
111
123
|
export function buildEscalateSuggestion(triggers, detail) {
|
|
112
124
|
if (triggers.includes("stall-timeout")) {
|
|
113
|
-
const
|
|
114
|
-
return `No progress detected for ${
|
|
125
|
+
const duration = detail ?? "60 minutes";
|
|
126
|
+
return `No progress detected for ${duration} — state has not changed. This is a manual checkpoint: inspect the PR and apply a manual fix before resuming.`;
|
|
115
127
|
}
|
|
116
128
|
if (triggers.includes("base-branch-unknown")) {
|
|
117
129
|
const reason = detail ? ` (${detail})` : "";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readStallState, writeStallState } from "../../state/iterate-stall.mjs";
|
|
2
2
|
import { toAgentThread, toAgentComment, toAgentStalledCheck } from "../../reporters/agent.mjs";
|
|
3
|
-
import { buildEscalateSuggestion, buildEscalateHumanMessage } from "./escalate.mjs";
|
|
3
|
+
import { buildEscalateSuggestion, buildEscalateHumanMessage, formatDurationApprox, } from "./escalate.mjs";
|
|
4
4
|
function computeStallFingerprint(action, headSha, base, report, reviewSummaryIds) {
|
|
5
5
|
const checks = [
|
|
6
6
|
...report.checks.failing.map((f) => `failing:${f.name}:${f.conclusion}`),
|
|
@@ -37,14 +37,14 @@ export async function applyStallGuard(stallKey, stallTimeoutSeconds, headSha, ba
|
|
|
37
37
|
action: prospectiveResult.action,
|
|
38
38
|
});
|
|
39
39
|
if (stalledChecks.length > 0) {
|
|
40
|
-
const
|
|
40
|
+
const stalledDuration = formatDurationApprox(Math.max(...stalledChecks.map((c) => c.ageSeconds)));
|
|
41
41
|
const escalateBase = {
|
|
42
42
|
triggers: ["stall-timeout"],
|
|
43
43
|
unresolvedThreads: [],
|
|
44
44
|
ambiguousComments: [],
|
|
45
45
|
changesRequestedReviews: [],
|
|
46
46
|
stalledChecks,
|
|
47
|
-
suggestion: buildEscalateSuggestion(["stall-timeout"],
|
|
47
|
+
suggestion: buildEscalateSuggestion(["stall-timeout"], stalledDuration),
|
|
48
48
|
};
|
|
49
49
|
return {
|
|
50
50
|
...base,
|
|
@@ -68,13 +68,13 @@ export async function applyStallGuard(stallKey, stallTimeoutSeconds, headSha, ba
|
|
|
68
68
|
await writeStallState(stallKey, { fingerprint, firstSeenAt: nowSeconds });
|
|
69
69
|
}
|
|
70
70
|
else if (ageSeconds >= stallTimeoutSeconds) {
|
|
71
|
-
const
|
|
71
|
+
const stalledDuration = formatDurationApprox(ageSeconds);
|
|
72
72
|
const escalateBase = {
|
|
73
73
|
triggers: ["stall-timeout"],
|
|
74
74
|
unresolvedThreads: [...report.threads.actionable, ...report.threads.resolutionOnly].map(toAgentThread),
|
|
75
75
|
ambiguousComments: report.comments.actionable.map(toAgentComment),
|
|
76
76
|
changesRequestedReviews: report.changesRequestedReviews,
|
|
77
|
-
suggestion: buildEscalateSuggestion(["stall-timeout"],
|
|
77
|
+
suggestion: buildEscalateSuggestion(["stall-timeout"], stalledDuration),
|
|
78
78
|
};
|
|
79
79
|
return {
|
|
80
80
|
...base,
|
package/bin/commands/poll.mjs
CHANGED
|
@@ -66,9 +66,16 @@ export async function runPoll(opts) {
|
|
|
66
66
|
const untilTerminal = untilTerminalOpt === true;
|
|
67
67
|
let dotsPrinted = false;
|
|
68
68
|
let lastWaitSignature = null;
|
|
69
|
+
// When prNumber is omitted, iterateOpts.prNumber starts undefined and each tick would otherwise
|
|
70
|
+
// re-infer the PR from the current branch. That inference query only matches OPEN PRs, so once
|
|
71
|
+
// the monitored PR merges it returns nothing and runIterate throws instead of reporting the
|
|
72
|
+
// terminal CANCEL/merged result. Pin the PR resolved by the first tick so later ticks target it
|
|
73
|
+
// directly and never re-run branch discovery.
|
|
74
|
+
let prNumber = opts.prNumber;
|
|
69
75
|
while (true) {
|
|
70
76
|
tick += 1;
|
|
71
|
-
lastResult = await runIterate(iterateOpts);
|
|
77
|
+
lastResult = await runIterate({ ...iterateOpts, prNumber });
|
|
78
|
+
prNumber ??= lastResult.pr;
|
|
72
79
|
if (lastResult.action !== "wait" && !(untilTerminal && lastResult.action === "mark_ready")) {
|
|
73
80
|
break;
|
|
74
81
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pr-shepherd",
|
|
3
|
-
"version": "0.32.
|
|
3
|
+
"version": "0.32.4",
|
|
4
4
|
"description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Jonathan Ong",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@vitest/coverage-v8": "^4.1.4",
|
|
39
39
|
"husky": "^9.1.7",
|
|
40
40
|
"knip": "^6.14.1",
|
|
41
|
-
"oxfmt": "^0.
|
|
41
|
+
"oxfmt": "^0.57.0",
|
|
42
42
|
"oxlint": "^1.60.0",
|
|
43
43
|
"typescript": "^6.0.3",
|
|
44
44
|
"vitest": "^4.1.4"
|