pr-shepherd 0.38.0 → 0.38.1

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
3
  "description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
4
- "version": "0.38.0",
4
+ "version": "0.38.1",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
7
7
  "email": "jonathanrichardong@gmail.com"
@@ -177,7 +177,7 @@ Flags:
177
177
  PR may be a number or GitHub pull request URL. When omitted, the current branch PR is inferred.
178
178
  Exit code: 0 on success; nonzero on failure (sysexits.h — see docs/exit-codes.md).`;
179
179
  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 Do not cancel in-progress runs before actionable fixes.\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\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 A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
180
- readonly poll: "pr-shepherd poll\n\nRun iterate repeatedly for WAIT ticks and during the FIX_CODE debounce window. Print only the\nfinal tick to stdout.\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 1m): poll keeps\niterating at --interval, then runs one more tick after the window and returns that result.\nWith --until-terminal, poll also continues through MARK_READY.\n\nUsage:\n pr-shepherd poll [PR] [poll-flags] [iterate-flags]\n\nPoll flags:\n --interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: 60s.\n --timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: 4.5m.\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.\n --quiet-status During WAIT polling, print only changed status snapshots.\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 Do not cancel in-progress runs before actionable fixes.\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 a single dot to stderr by default; --quiet-status prints only changed WAIT snapshots, 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, CANCEL, or ESCALATE.\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 A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
180
+ readonly poll: "pr-shepherd poll\n\nRun iterate repeatedly for WAIT ticks and during the FIX_CODE debounce window. Print only the\nfinal tick to stdout.\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 1m): poll keeps\niterating at --interval, then runs one more tick after the window and returns that result.\nWith --until-terminal, poll also continues through MARK_READY.\n\nUsage:\n pr-shepherd poll [PR] [poll-flags] [iterate-flags]\n\nPoll flags:\n --interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: 60s.\n --timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: 4.5m.\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.\n --quiet-status During WAIT polling, print only changed status snapshots.\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 Do not cancel in-progress runs before actionable fixes.\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; --quiet-status prints only changed WAIT snapshots, 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, CANCEL, or ESCALATE.\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 A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
181
181
  readonly clean: `pr-shepherd clean
182
182
 
183
183
  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 Do not cancel in-progress runs before actionable fixes.\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\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 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 WAIT ticks and during the FIX_CODE debounce window. Print only the\nfinal tick to stdout.\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 1m): poll keeps\niterating at --interval, then runs one more tick after the window and returns that result.\nWith --until-terminal, poll also continues through MARK_READY.\n\nUsage:\n pr-shepherd poll [PR] [poll-flags] [iterate-flags]\n\nPoll flags:\n --interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: 60s.\n --timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: 4.5m.\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.\n --quiet-status During WAIT polling, print only changed status snapshots.\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 Do not cancel in-progress runs before actionable fixes.\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 a single dot to stderr by default; --quiet-status prints only changed WAIT snapshots, 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, CANCEL, or ESCALATE.\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 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 WAIT ticks and during the FIX_CODE debounce window. Print only the\nfinal tick to stdout.\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 1m): poll keeps\niterating at --interval, then runs one more tick after the window and returns that result.\nWith --until-terminal, poll also continues through MARK_READY.\n\nUsage:\n pr-shepherd poll [PR] [poll-flags] [iterate-flags]\n\nPoll flags:\n --interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: 60s.\n --timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: 4.5m.\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.\n --quiet-status During WAIT polling, print only changed status snapshots.\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 Do not cancel in-progress runs before actionable fixes.\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; --quiet-status prints only changed WAIT snapshots, 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, CANCEL, or ESCALATE.\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 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;
@@ -63,7 +63,7 @@ Forwarded iterate flags:
63
63
  Durations accept s/m/h suffixes: 30s, 4.5m, 1h. A bare number uses each flag's default unit (seconds
64
64
  for --interval/--timeout/--debounce, minutes for --ready-delay/--stall-timeout); decimals are allowed only with
65
65
  an explicit unit (4.5m).
66
- 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.
66
+ Each WAIT tick writes an explicit still-running line to stderr by default; --quiet-status prints only changed WAIT snapshots, and --verbose emits detailed per-tick lines.
67
67
  FIX_CODE debounce writes a remaining-seconds line to stderr. --timeout does not cut an in-flight debounce short.
68
68
  With --until-terminal, --timeout is ignored for WAIT ticks and polling continues until FIX_CODE, CANCEL, or ESCALATE.
69
69
 
@@ -76,4 +76,4 @@ Exit codes: same as iterate (the final tick's action/reason decides the code).
76
76
  14 CANCEL (closed without merging)
77
77
  A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).`;
78
78
  /** Public help page for the default PR polling invocation. */
79
- export const DEFAULT_USAGE = POLL_USAGE.replaceAll("pr-shepherd poll", "pr-shepherd [PR]");
79
+ export const DEFAULT_USAGE = POLL_USAGE.replace(/^pr-shepherd poll$/m, "pr-shepherd [PR]").replace(/^ {2}pr-shepherd poll \[PR\]/m, " pr-shepherd [PR]");
@@ -177,7 +177,7 @@ Flags:
177
177
  PR may be a number or GitHub pull request URL. When omitted, the current branch PR is inferred.
178
178
  Exit code: 0 on success; nonzero on failure (sysexits.h — see docs/exit-codes.md).`;
179
179
  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 Do not cancel in-progress runs before actionable fixes.\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\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 A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
180
- readonly poll: "pr-shepherd poll\n\nRun iterate repeatedly for WAIT ticks and during the FIX_CODE debounce window. Print only the\nfinal tick to stdout.\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 1m): poll keeps\niterating at --interval, then runs one more tick after the window and returns that result.\nWith --until-terminal, poll also continues through MARK_READY.\n\nUsage:\n pr-shepherd poll [PR] [poll-flags] [iterate-flags]\n\nPoll flags:\n --interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: 60s.\n --timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: 4.5m.\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.\n --quiet-status During WAIT polling, print only changed status snapshots.\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 Do not cancel in-progress runs before actionable fixes.\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 a single dot to stderr by default; --quiet-status prints only changed WAIT snapshots, 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, CANCEL, or ESCALATE.\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 A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
180
+ readonly poll: "pr-shepherd poll\n\nRun iterate repeatedly for WAIT ticks and during the FIX_CODE debounce window. Print only the\nfinal tick to stdout.\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 1m): poll keeps\niterating at --interval, then runs one more tick after the window and returns that result.\nWith --until-terminal, poll also continues through MARK_READY.\n\nUsage:\n pr-shepherd poll [PR] [poll-flags] [iterate-flags]\n\nPoll flags:\n --interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: 60s.\n --timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: 4.5m.\n --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.\n --quiet-status During WAIT polling, print only changed status snapshots.\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 Do not cancel in-progress runs before actionable fixes.\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; --quiet-status prints only changed WAIT snapshots, 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, CANCEL, or ESCALATE.\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 A command/validation/GitHub failure exits with a sysexits.h code instead (see docs/exit-codes.md).";
181
181
  readonly clean: `pr-shepherd clean
182
182
 
183
183
  Remove pr-shepherd state files from PR_SHEPHERD_STATE_DIR.
@@ -1,5 +1,5 @@
1
1
  import type { CheckAnnotation, ClassifiedCheck, ShepherdReport, TriagedCheck } from "../types.mts";
2
- export declare function checksWithUnseenAnnotations(report: ShepherdReport): TriagedCheck[];
2
+ export declare function checksWithActionableAnnotations(report: ShepherdReport): TriagedCheck[];
3
3
  export declare function attachAndMergeCheckAnnotations(buckets: {
4
4
  passing: ClassifiedCheck[];
5
5
  failing: TriagedCheck[];
@@ -2,14 +2,14 @@ import { fetchCheckRunAnnotations } from "../github/check-annotations.mjs";
2
2
  function shouldFetchCheckAnnotations(check) {
3
3
  return check.id != null && check.status === "COMPLETED" && check.hasAnnotations === true;
4
4
  }
5
- export function checksWithUnseenAnnotations(report) {
5
+ export function checksWithActionableAnnotations(report) {
6
6
  return [
7
7
  ...report.checks.failing,
8
8
  ...report.checks.passing,
9
9
  ...report.checks.skipped,
10
10
  ...report.checks.filtered,
11
11
  ...(report.checks.ignored ?? []),
12
- ].filter((c) => (c.annotations?.length ?? 0) > 0);
12
+ ].filter((c) => c.conclusion !== "SUCCESS" && (c.annotations?.length ?? 0) > 0);
13
13
  }
14
14
  export async function attachAndMergeCheckAnnotations(buckets, seenMap, prNumber) {
15
15
  const candidates = [
@@ -7,7 +7,7 @@ import { deriveMergeStatus } from "../merge-status/derive.mjs";
7
7
  import { loadConfig } from "../config/load.mjs";
8
8
  import { classifyVisibleComments } from "../comments/visible-comments.mjs";
9
9
  import { computeStatus } from "./check-status.mjs";
10
- import { attachAndMergeCheckAnnotations } from "./check-annotations.mjs";
10
+ import { annotationMarkerBody, attachAndMergeCheckAnnotations } from "./check-annotations.mjs";
11
11
  import { buildTerminalReport } from "./check-terminal-report.mjs";
12
12
  import { isBlockedByFilteredCheck, refreshReadyMergeability, refreshUnknownMergeability, } from "./ready-mergeability.mjs";
13
13
  import { loadSeenMap, markSeen, classifyItem } from "../state/seen-comments.mjs";
@@ -85,7 +85,16 @@ export async function runCheck(opts) {
85
85
  const changesRequestedReviewVisibility = classifyChangesRequestedReviewsForDisplay(batchData.changesRequestedReviews.filter((r) => !partition.suppressedChangesRequestedIds.has(r.id)), seenMap, botUsernames);
86
86
  const approvedReviewVisibility = classifyReviewsForDisplay(batchData.approvedReviews, seenMap);
87
87
  if (opts.persistSeen !== false) {
88
+ const successfulAnnotations = [
89
+ ...merged.passing,
90
+ ...merged.skipped,
91
+ ...merged.filtered,
92
+ ...merged.ignored,
93
+ ]
94
+ .filter((check) => check.conclusion === "SUCCESS")
95
+ .flatMap((check) => check.annotations ?? []);
88
96
  await Promise.allSettled([
97
+ ...successfulAnnotations.map((a) => markSeen(stateKey, a.id, annotationMarkerBody(a))),
89
98
  ...firstLookComments.map((c) => markSeen(stateKey, c.id, c.body)),
90
99
  ...threadVisibility.toMarkSeen.map((t) => markSeen(stateKey, t.id, threadTranscriptBody(t))),
91
100
  ...visibleCommentClassification.toMarkSeen.map((c) => markSeen(stateKey, c.id, c.body)),
@@ -117,6 +126,9 @@ export async function runCheck(opts) {
117
126
  batchData = refreshed.batchData;
118
127
  mergeStatus = refreshed.mergeStatus;
119
128
  status = refreshed.status;
129
+ if (mergeStatus.state === "MERGED" || mergeStatus.state === "CLOSED") {
130
+ return buildTerminalReport(prNumber, repo, batchData, mergeStatus, mergeStatus.state);
131
+ }
120
132
  }
121
133
  const blockedByFilteredCheck = isBlockedByFilteredCheck(mergeStatus, verdict);
122
134
  return {
@@ -8,7 +8,7 @@ import { buildResolveCommand } from "./classify.mjs";
8
8
  import { buildFixInstructions } from "./render.mjs";
9
9
  import { applyStallGuard } from "./stall.mjs";
10
10
  import { tryCancelRun, buildAutoCancelRunIdsWithOptions, buildInProgressRunIds, buildRunProtection, } from "./helpers.mjs";
11
- import { annotationMarkerBody, checksWithUnseenAnnotations } from "../check-annotations.mjs";
11
+ import { annotationMarkerBody, checksWithActionableAnnotations } from "../check-annotations.mjs";
12
12
  import { threadTranscriptBody } from "../../threads/transcript.mjs";
13
13
  import { isHumanAuthor, isConfiguredBotAuthor } from "../../comments/authors.mjs";
14
14
  import { loadConfig } from "../../config/load.mjs";
@@ -30,7 +30,7 @@ function nextFixAttempts(stored, headSha, threads) {
30
30
  export async function handleFixCode(ctx) {
31
31
  const { base, report, opts, headSha, stallKey, prNumber, stallTimeoutSeconds, repoOwner, repoName, reviewSummaryIds, firstLookSummaries, editedSummaries, surfacedApprovals, botUsernames, ruleAutoResolveThreadIds, } = ctx;
32
32
  const failingChecks = report.checks.failing;
33
- const annotatedExtra = checksWithUnseenAnnotations(report).filter((c) => c.category !== "failing");
33
+ const annotatedExtra = checksWithActionableAnnotations(report).filter((c) => c.category !== "failing");
34
34
  const { protectedRunIds, protectedRuns } = buildRunProtection([...failingChecks, ...report.checks.inProgress], opts.neverCancelRuns);
35
35
  const stored = await readFixAttempts({ owner: repoOwner, repo: repoName, pr: prNumber });
36
36
  const { threadAttempts, threadBodyHashes } = nextFixAttempts(stored, headSha, report.threads.actionable);
@@ -12,7 +12,7 @@ import { clearStallState } from "../../state/iterate-stall.mjs";
12
12
  import { handleFixCode } from "./fix-code.mjs";
13
13
  import { normalizeBotUsernames } from "../../comments/authors.mjs";
14
14
  import { autoMinimizeComments } from "../../comments/resolve.mjs";
15
- import { checksWithUnseenAnnotations } from "../check-annotations.mjs";
15
+ import { checksWithActionableAnnotations } from "../check-annotations.mjs";
16
16
  export async function runIterate(opts) {
17
17
  const config = loadConfig();
18
18
  const botUsernames = normalizeBotUsernames(config.botUsernames);
@@ -70,7 +70,7 @@ export async function runIterate(opts) {
70
70
  report.comments.firstLook.length > 0 ||
71
71
  report.changesRequestedReviews.length > 0 ||
72
72
  report.checks.failing.length > 0 ||
73
- checksWithUnseenAnnotations(report).length > 0 ||
73
+ checksWithActionableAnnotations(report).length > 0 ||
74
74
  report.mergeStatus.status === "CONFLICTS" ||
75
75
  reviewSummaryIds.length > 0 ||
76
76
  firstLookSummaries.length > 0 ||
@@ -1,7 +1,7 @@
1
1
  import { readStallState, writeStallState } from "../../state/iterate-stall.mjs";
2
2
  import { toAgentThread, toAgentComment, toAgentStalledCheck } from "../../reporters/agent.mjs";
3
3
  import { buildEscalateSuggestion, buildEscalateHumanMessage, formatDurationApprox, } from "./escalate.mjs";
4
- import { checksWithUnseenAnnotations } from "../check-annotations.mjs";
4
+ import { checksWithActionableAnnotations } from "../check-annotations.mjs";
5
5
  function computeStallFingerprint(action, headSha, base, report, reviewSummaryIds) {
6
6
  const checks = [
7
7
  ...report.checks.failing.map((f) => `failing:${f.name}:${f.conclusion}`),
@@ -14,7 +14,7 @@ function computeStallFingerprint(action, headSha, base, report, reviewSummaryIds
14
14
  const reviews = report.changesRequestedReviews.map((r) => r.id).sort();
15
15
  const summaries = [...reviewSummaryIds].sort();
16
16
  const ruleAutoResolveSummaries = (report.ruleAutoResolveReviewSummaryIds ?? []).sort((a, b) => a.localeCompare(b));
17
- const annotations = checksWithUnseenAnnotations(report)
17
+ const annotations = checksWithActionableAnnotations(report)
18
18
  .flatMap((c) => (c.annotations ?? []).map((a) => a.id))
19
19
  .sort((a, b) => a.localeCompare(b));
20
20
  return JSON.stringify({
@@ -6,7 +6,7 @@ function writeTickProgress(tick, elapsedSeconds, sleepSeconds, verbose) {
6
6
  process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s] WAIT — sleeping ${sleepSeconds}s\n`);
7
7
  }
8
8
  else {
9
- process.stderr.write(".");
9
+ process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s] WAIT — still running; next tick in ${sleepSeconds}s\n`);
10
10
  }
11
11
  }
12
12
  function waitSignature(result) {
@@ -73,7 +73,6 @@ export async function runPoll(opts) {
73
73
  const verbose = opts.verbose === true;
74
74
  const quietStatus = quietStatusOpt === true;
75
75
  const untilTerminal = untilTerminalOpt === true;
76
- let dotsPrinted = false;
77
76
  let lastWaitSignature = null;
78
77
  // When prNumber is omitted, iterateOpts.prNumber starts undefined and each tick would otherwise
79
78
  // re-infer the PR from the current branch. That inference query only matches OPEN PRs, so once
@@ -110,8 +109,6 @@ export async function runPoll(opts) {
110
109
  verbose,
111
110
  lastWaitSignature,
112
111
  });
113
- if (!quietStatus && !verbose)
114
- dotsPrinted = true;
115
112
  await sleep(intervalMs);
116
113
  continue;
117
114
  }
@@ -124,10 +121,6 @@ export async function runPoll(opts) {
124
121
  debounceUntil ??= Date.now() + debounceMs;
125
122
  const remainingMs = debounceUntil - Date.now();
126
123
  if (remainingMs > 0) {
127
- if (dotsPrinted) {
128
- process.stderr.write("\n");
129
- dotsPrinted = false;
130
- }
131
124
  writeDebounceProgress(tick, Date.now() - start, remainingMs);
132
125
  await sleep(Math.min(intervalMs, remainingMs));
133
126
  }
@@ -138,7 +131,5 @@ export async function runPoll(opts) {
138
131
  }
139
132
  break;
140
133
  }
141
- if (dotsPrinted)
142
- process.stderr.write("\n");
143
134
  return lastResult;
144
135
  }
@@ -24,6 +24,7 @@ async function readMergeability(prNumber, repo, batchData) {
24
24
  const restState = await getMergeableState(prNumber, repo.owner, repo.name);
25
25
  return {
26
26
  ...batchData,
27
+ state: restState.state ?? batchData.state,
27
28
  mergeable: restState.mergeable ?? batchData.mergeable,
28
29
  mergeStateStatus: restState.mergeStateStatus ?? batchData.mergeStateStatus,
29
30
  };
@@ -34,13 +34,15 @@ export declare function getPullRequestBody(pr: number, owner: string, name: stri
34
34
  /** Overwrites the PR body. */
35
35
  export declare function updatePullRequestBody(pullRequestId: string, body: string): Promise<void>;
36
36
  /**
37
- * Fetches `mergeable` and `mergeStateStatus` via the REST API.
37
+ * Fetches PR state, `mergeable`, and `mergeStateStatus` via the REST API.
38
38
  *
39
- * Used as a fallback when the GraphQL API returns `UNKNOWN` for these fields
40
- * a known GitHub quirk where GraphQL lags behind the REST layer.
39
+ * Used when the GraphQL API returns `UNKNOWN` for mergeability or before a
40
+ * READY handoff. The same response carries state so a concurrent merge or
41
+ * close can supersede the earlier GraphQL snapshot without another request.
41
42
  */
42
43
  export declare function getMergeableState(pr: number, owner: string, repo: string): Promise<{
43
44
  mergeable: MergeableState;
44
45
  mergeStateStatus: MergeStateStatus;
46
+ state?: "OPEN" | "CLOSED" | "MERGED";
45
47
  }>;
46
48
  export declare function getCurrentBranch(): Promise<string>;
@@ -83,16 +83,23 @@ export async function updatePullRequestBody(pullRequestId, body) {
83
83
  await httpGraphql(UPDATE_PR_BODY_MUTATION, { pullRequestId, body });
84
84
  }
85
85
  /**
86
- * Fetches `mergeable` and `mergeStateStatus` via the REST API.
86
+ * Fetches PR state, `mergeable`, and `mergeStateStatus` via the REST API.
87
87
  *
88
- * Used as a fallback when the GraphQL API returns `UNKNOWN` for these fields
89
- * a known GitHub quirk where GraphQL lags behind the REST layer.
88
+ * Used when the GraphQL API returns `UNKNOWN` for mergeability or before a
89
+ * READY handoff. The same response carries state so a concurrent merge or
90
+ * close can supersede the earlier GraphQL snapshot without another request.
90
91
  */
91
92
  export async function getMergeableState(pr, owner, repo) {
92
93
  const data = await rest("GET", `/repos/${owner}/${repo}/pulls/${pr}`);
93
94
  const mergeable = data.mergeable === true ? "MERGEABLE" : data.mergeable === false ? "CONFLICTING" : "UNKNOWN";
94
95
  const mergeStateStatus = data.mergeable_state.toUpperCase();
95
- return { mergeable, mergeStateStatus };
96
+ const rawState = data.state?.toUpperCase();
97
+ const state = data.merged_at != null
98
+ ? "MERGED"
99
+ : rawState === "OPEN" || rawState === "CLOSED"
100
+ ? rawState
101
+ : undefined;
102
+ return { mergeable, mergeStateStatus, ...(state !== undefined && { state }) };
96
103
  }
97
104
  // ---------------------------------------------------------------------------
98
105
  // Internal helpers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
- "version": "0.38.0",
3
+ "version": "0.38.1",
4
4
  "description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
5
5
  "keywords": [
6
6
  "automation",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-shepherd",
3
- "version": "0.38.0",
3
+ "version": "0.38.1",
4
4
  "description": "Autonomous PR CI monitor and review-comment resolver for Codex.",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "pr-shepherd": {
4
4
  "command": "npx",
5
- "args": ["--yes", "--package", "pr-shepherd@0.38.0", "pr-shepherd-mcp"]
5
+ "args": ["--yes", "--package", "pr-shepherd@0.38.1", "pr-shepherd-mcp"]
6
6
  }
7
7
  }
8
8
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "pr-shepherd": {
3
3
  "command": "npx",
4
- "args": ["--yes", "--package", "pr-shepherd@0.38.0", "pr-shepherd-mcp"]
4
+ "args": ["--yes", "--package", "pr-shepherd@0.38.1", "pr-shepherd-mcp"]
5
5
  }
6
6
  }