pr-shepherd 0.46.8 → 0.48.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.
Files changed (55) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +2 -0
  3. package/bin/api.d.mts +1 -1
  4. package/bin/cli/args.mjs +1 -0
  5. package/bin/cli/default-poll.mjs +1 -0
  6. package/bin/cli/help-command-pages.d.mts +1 -1
  7. package/bin/cli/help-iterate-poll-pages.d.mts +1 -1
  8. package/bin/cli/help-iterate-poll-pages.mjs +6 -5
  9. package/bin/cli/help-top-page.d.mts +1 -1
  10. package/bin/cli/help-top-page.mjs +5 -4
  11. package/bin/cli/help.d.mts +2 -2
  12. package/bin/cli/poll-handler.mjs +15 -7
  13. package/bin/commands/check-fingerprint.d.mts +8 -0
  14. package/bin/commands/check-fingerprint.mjs +69 -0
  15. package/bin/commands/check.d.mts +1 -0
  16. package/bin/commands/check.mjs +23 -3
  17. package/bin/commands/iterate/api-usage.d.mts +1 -1
  18. package/bin/commands/iterate/api-usage.mjs +6 -2
  19. package/bin/commands/iterate/base.mjs +1 -0
  20. package/bin/commands/iterate/mark-ready.mjs +8 -0
  21. package/bin/commands/iterate/run.mjs +1 -1
  22. package/bin/commands/poll-progress.d.mts +11 -0
  23. package/bin/commands/poll-progress.mjs +52 -0
  24. package/bin/commands/poll-quota.d.mts +9 -0
  25. package/bin/commands/poll-quota.mjs +36 -0
  26. package/bin/commands/poll-run.d.mts +1 -1
  27. package/bin/commands/poll-run.mjs +2 -2
  28. package/bin/commands/poll.mjs +66 -66
  29. package/bin/config/load.d.mts +7 -0
  30. package/bin/config/load.mjs +81 -20
  31. package/bin/config.json +9 -3
  32. package/bin/github/batch-raw-rules.d.mts +4 -1
  33. package/bin/github/batch-raw-types.d.mts +14 -0
  34. package/bin/github/batch.d.mts +2 -0
  35. package/bin/github/batch.mjs +2 -0
  36. package/bin/github/fingerprint-fields.d.mts +58 -0
  37. package/bin/github/fingerprint-fields.mjs +39 -0
  38. package/bin/github/fingerprint.d.mts +35 -0
  39. package/bin/github/fingerprint.mjs +67 -0
  40. package/bin/github/gql/batch-pr.gql +25 -120
  41. package/bin/github/gql/commit-check-suites.gql +19 -0
  42. package/bin/github/gql/pr-fingerprint.gql +75 -0
  43. package/bin/github/gql/pr-merge-policy.gql +49 -0
  44. package/bin/github/merge-queue-checks.mjs +57 -30
  45. package/bin/github/queries.d.mts +2 -0
  46. package/bin/github/queries.mjs +4 -1
  47. package/bin/quota-warning.mjs +1 -1
  48. package/bin/state/pr-fingerprint.d.mts +20 -0
  49. package/bin/state/pr-fingerprint.mjs +90 -0
  50. package/bin/types/iterate.d.mts +12 -3
  51. package/bin/types/report.d.mts +2 -0
  52. package/package.json +2 -2
  53. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  54. package/plugins/pr-shepherd/.codex.mcp.json +1 -1
  55. package/plugins/pr-shepherd/.mcp.json +1 -1
@@ -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.46.8",
4
+ "version": "0.48.0",
5
5
  "author": {
6
6
  "name": "Jonathan Ong",
7
7
  "email": "jonathanrichardong@gmail.com"
package/README.md CHANGED
@@ -137,6 +137,8 @@ pr-shepherd owner/repo#42 # poll a PR in an explicit repository
137
137
  pr-shepherd https://github.com/owner/repo/pull/42
138
138
  ```
139
139
 
140
+ 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
+
140
142
  ### Apply Review And Journal Changes, Or Select Files
141
143
 
142
144
  Use `apply` with ordered operations to reply/resolve/minimize/dismiss review items, mark selected changed files as viewed, or append an idempotent Shepherd Journal item. Explicit operations are attempted and surface GitHub's per-operation results; generated iterate guidance remains capability-filtered. Use `build_suggestion_patches` to turn ordered review suggestions into checked patches and commit metadata; it never changes the worktree or git history.
package/bin/api.d.mts CHANGED
@@ -8,7 +8,7 @@ export interface CreatePrShepherdOptions {
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
- export type IterateInput = Omit<IterateCommandOptions, "format" | "prNumber" | "targetRepository" | "persistSeen" | "deferQuotaWarning"> & {
11
+ export type IterateInput = Omit<IterateCommandOptions, "format" | "prNumber" | "targetRepository" | "persistSeen" | "fingerprintCache" | "deferQuotaWarning" | "quotaWarningMinimumPollIntervalMinutes"> & {
12
12
  pr?: PrReference;
13
13
  };
14
14
  export interface ReviewMutationsOperation {
package/bin/cli/args.mjs CHANGED
@@ -30,6 +30,7 @@ const BOOLEAN_FLAGS = new Set([
30
30
  "--no-auto-mark-ready",
31
31
  "--no-auto-cancel-actionable",
32
32
  "--quiet-status",
33
+ "--no-quiet-status",
33
34
  "--until-terminal",
34
35
  "--merge",
35
36
  "--dry-run",
@@ -15,6 +15,7 @@ const DEFAULT_POLL_BOOLEAN_FLAGS = new Set([
15
15
  "--no-auto-mark-ready",
16
16
  "--no-auto-cancel-actionable",
17
17
  "--quiet-status",
18
+ "--no-quiet-status",
18
19
  "--until-terminal",
19
20
  "--merge",
20
21
  ]);
@@ -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 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 or --merge, 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 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; --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, 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).";
197
+ 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 or --merge, 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: 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 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 or --merge, 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 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; --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, 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).";
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 or --merge, 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: 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;
@@ -48,10 +48,11 @@ Usage:
48
48
  pr-shepherd poll [PR] [poll-flags] [iterate-flags]
49
49
 
50
50
  Poll flags:
51
- --interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: 60s.
52
- --timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: 4.5m.
53
- --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.
54
- --quiet-status During WAIT polling, print only changed status snapshots.
51
+ --interval <duration> Sleep between WAIT ticks. Bare number = seconds. Default: poll.intervalSeconds (built-in 60s).
52
+ --timeout <duration> Maximum wall-clock wait for WAIT ticks. Bare number = seconds. Default: poll.timeoutSeconds (built-in 4.5m).
53
+ --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: poll.debounceSeconds (built-in 60s). 0 disables.
54
+ --quiet-status Print only changed WAIT snapshots. Overrides poll.quietStatus.
55
+ --no-quiet-status Print every WAIT snapshot. Overrides poll.quietStatus.
55
56
  --until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.
56
57
 
57
58
  Forwarded iterate flags:
@@ -67,7 +68,7 @@ Forwarded iterate flags:
67
68
  Durations accept s/m/h suffixes: 30s, 4.5m, 1h. A bare number uses each flag's default unit (seconds
68
69
  for --interval/--timeout/--debounce, minutes for --ready-delay/--stall-timeout); decimals are allowed only with
69
70
  an explicit unit (4.5m).
70
- 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.
71
+ Each 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.
71
72
  FIX_CODE debounce writes a remaining-seconds line to stderr. --timeout does not cut an in-flight debounce short.
72
73
  With --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.
73
74
 
@@ -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] Poll until non-WAIT or timeout. This is the default command.\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 such as 42 or a GitHub pull request URL.\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: 60s.\n --timeout <duration> Poll wall-clock cap 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\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.";
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] Poll until non-WAIT or timeout. This is the default command.\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 such as 42 or a GitHub pull request URL.\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.";
@@ -44,10 +44,11 @@ Iterate flags:
44
44
  --merge Shepherd through readiness, then emit a merge or merge-queue command.
45
45
 
46
46
  Polling flags:
47
- --interval <duration> Delay between WAIT ticks. Bare number = seconds. Default: 60s.
48
- --timeout <duration> Poll wall-clock cap for WAIT ticks. Bare number = seconds. Default: 4.5m.
49
- --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: 60s. 0 disables.
50
- --quiet-status During WAIT polling, print only changed status snapshots.
47
+ --interval <duration> Delay between WAIT ticks. Bare number = seconds. Default: poll.intervalSeconds (built-in 60s).
48
+ --timeout <duration> Poll wall-clock cap for WAIT ticks. Bare number = seconds. Default: poll.timeoutSeconds (built-in 4.5m).
49
+ --debounce <duration> Settle window after first FIX_CODE before returning. Bare number = seconds. Default: poll.debounceSeconds (built-in 60s). 0 disables.
50
+ --quiet-status Print only changed WAIT snapshots. Overrides poll.quietStatus.
51
+ --no-quiet-status Print every WAIT snapshot. Overrides poll.quietStatus.
51
52
  --until-terminal Continue through WAIT/MARK_READY until FIX_CODE/CANCEL/ESCALATE.
52
53
 
53
54
  Clean variants:
@@ -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 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 or --merge, 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 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; --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, 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).";
197
+ 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 or --merge, 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: 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] Poll until non-WAIT or timeout. This is the default command.\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 such as 42 or a GitHub pull request URL.\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: 60s.\n --timeout <duration> Poll wall-clock cap 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\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.";
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] Poll until non-WAIT or timeout. This is the default command.\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 such as 42 or a GitHub pull request URL.\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;
@@ -5,9 +5,7 @@ import { parseDurationToSeconds } from "./duration.mjs";
5
5
  import { validateSecondsDurationFlag } from "./duration-flag.mjs";
6
6
  import { parseIterateFlags } from "./iterate-flags.mjs";
7
7
  import { emitIterateResult } from "./iterate-emitter.mjs";
8
- const DEFAULT_POLL_INTERVAL_SECONDS = 60;
9
- const DEFAULT_POLL_TIMEOUT_SECONDS = 270;
10
- const DEFAULT_POLL_DEBOUNCE_SECONDS = 60;
8
+ import { EXIT } from "../exit-codes.mjs";
11
9
  export async function handlePoll(args) {
12
10
  const { prNumber, global: globalOpts, extra } = parseCommonArgs(args);
13
11
  const cfg = loadConfig();
@@ -18,17 +16,27 @@ export async function handlePoll(args) {
18
16
  const intervalSuffix = validateSecondsDurationFlag("pr-shepherd", "--interval", intervalStr, hasFlag(extra, "--interval"));
19
17
  if (intervalSuffix === null)
20
18
  return;
21
- const intervalSeconds = parseDurationToSeconds(intervalSuffix ?? "", DEFAULT_POLL_INTERVAL_SECONDS);
19
+ const intervalSeconds = parseDurationToSeconds(intervalSuffix ?? "", cfg.poll.intervalSeconds);
22
20
  const timeoutStr = getFlag(extra, "--timeout");
23
21
  const timeoutSuffix = validateSecondsDurationFlag("pr-shepherd", "--timeout", timeoutStr, hasFlag(extra, "--timeout"));
24
22
  if (timeoutSuffix === null)
25
23
  return;
26
- const timeoutSeconds = parseDurationToSeconds(timeoutSuffix ?? "", DEFAULT_POLL_TIMEOUT_SECONDS);
24
+ const timeoutSeconds = parseDurationToSeconds(timeoutSuffix ?? "", cfg.poll.timeoutSeconds);
27
25
  const debounceStr = getFlag(extra, "--debounce");
28
26
  const debounceSuffix = validateSecondsDurationFlag("pr-shepherd", "--debounce", debounceStr, hasFlag(extra, "--debounce"), { allowZero: true });
29
27
  if (debounceSuffix === null)
30
28
  return;
31
- const debounceSeconds = parseDurationToSeconds(debounceSuffix ?? "", DEFAULT_POLL_DEBOUNCE_SECONDS, { allowZero: true });
29
+ const debounceSeconds = parseDurationToSeconds(debounceSuffix ?? "", cfg.poll.debounceSeconds, {
30
+ allowZero: true,
31
+ });
32
+ const quietStatusFlag = hasFlag(extra, "--quiet-status");
33
+ const noQuietStatusFlag = hasFlag(extra, "--no-quiet-status");
34
+ if (quietStatusFlag && noQuietStatusFlag) {
35
+ process.stderr.write("pr-shepherd: --quiet-status and --no-quiet-status cannot be used together\n");
36
+ process.exitCode = EXIT.USAGE;
37
+ return;
38
+ }
39
+ const quietStatus = quietStatusFlag || (!noQuietStatusFlag && cfg.poll.quietStatus);
32
40
  const result = await runPoll({
33
41
  ...globalOpts,
34
42
  prNumber,
@@ -40,7 +48,7 @@ export async function handlePoll(args) {
40
48
  intervalSeconds,
41
49
  timeoutSeconds,
42
50
  debounceSeconds,
43
- quietStatus: hasFlag(extra, "--quiet-status"),
51
+ quietStatus,
44
52
  untilTerminal: hasFlag(extra, "--until-terminal"),
45
53
  });
46
54
  emitIterateResult(result, {
@@ -0,0 +1,8 @@
1
+ import { type RepoInfo } from "../github/client.mts";
2
+ import type { PrShepherdConfig } from "../config/load.mts";
3
+ import type { ShepherdReport } from "../types.mts";
4
+ export declare function tryReuseFingerprintReport(prNumber: number, repo: RepoInfo, stateKey: {
5
+ owner: string;
6
+ repo: string;
7
+ pr: number;
8
+ }, config: PrShepherdConfig): Promise<ShepherdReport | null>;
@@ -0,0 +1,69 @@
1
+ import { getMergeableState } from "../github/client.mjs";
2
+ import { fetchPrFingerprint, fingerprintsEqual, } from "../github/fingerprint.mjs";
3
+ import { fingerprintInputDigest, loadPrFingerprint } from "../state/pr-fingerprint.mjs";
4
+ import { hasCheckDrivenActionableWork } from "./check-annotations.mjs";
5
+ function reportAllowsFingerprintSkip(report) {
6
+ if (report.status === "READY")
7
+ return false;
8
+ if (report.mergeStatus.state !== "OPEN")
9
+ return false;
10
+ if (report.mergeQueue?.inQueue === true)
11
+ return false;
12
+ return (report.threads.actionable.length === 0 &&
13
+ report.threads.resolutionOnly.length === 0 &&
14
+ report.threads.firstLook.length === 0 &&
15
+ (report.threads.ruleAutoResolveIds?.length ?? 0) === 0 &&
16
+ report.comments.actionable.length === 0 &&
17
+ (report.comments.minimizeIds?.length ?? 0) === 0 &&
18
+ report.comments.firstLook.length === 0 &&
19
+ report.changesRequestedReviews.length === 0 &&
20
+ report.approvedReviews.length === 0 &&
21
+ report.firstLookSummaries.length === 0 &&
22
+ report.editedSummaries.length === 0 &&
23
+ (report.ruleAutoResolveReviewSummaryIds?.length ?? 0) === 0 &&
24
+ !hasCheckDrivenActionableWork(report.checks, report.mergeStatus.status));
25
+ }
26
+ export async function tryReuseFingerprintReport(prNumber, repo, stateKey, config) {
27
+ const cached = await loadPrFingerprint(stateKey);
28
+ if (cached?.inputDigest == null || cached.inputDigest !== fingerprintInputDigest(config)) {
29
+ return null;
30
+ }
31
+ if (!reportAllowsFingerprintSkip(cached.report))
32
+ return null;
33
+ const live = await fetchPrFingerprint(prNumber, repo);
34
+ if (live.isInMergeQueue || cached.fingerprint.isInMergeQueue)
35
+ return null;
36
+ if (!live.checkSuitesComplete || !cached.fingerprint.checkSuitesComplete)
37
+ return null;
38
+ if (live.commentCount > 100 || cached.fingerprint.commentCount > 100)
39
+ return null;
40
+ if (live.reviewCount > 100 || cached.fingerprint.reviewCount > 100)
41
+ return null;
42
+ if (live.threadCount > 20 || cached.fingerprint.threadCount > 20)
43
+ return null;
44
+ if (live.hasMultiCommentThreads || cached.fingerprint.hasMultiCommentThreads)
45
+ return null;
46
+ if (!live.rulesComplete || !cached.fingerprint.rulesComplete)
47
+ return null;
48
+ if (!fingerprintsEqual(cached.fingerprint, live))
49
+ return null;
50
+ if (!(await cachedReportSurvivesMergeabilityRefresh(prNumber, repo, cached.report, cached.fingerprint))) {
51
+ return null;
52
+ }
53
+ return { ...cached.report, fingerprintReused: true };
54
+ }
55
+ async function cachedReportSurvivesMergeabilityRefresh(prNumber, repo, report, fingerprint) {
56
+ const restDerived = fingerprint.mergeable !== report.mergeStatus.mergeable ||
57
+ fingerprint.mergeStateStatus !== report.mergeStatus.mergeStateStatus;
58
+ if (report.status !== "READY" && report.mergeStatus.status !== "UNKNOWN" && !restDerived) {
59
+ return true;
60
+ }
61
+ const rest = await getMergeableState(prNumber, repo.owner, repo.name);
62
+ if (rest.state === "MERGED" || rest.state === "CLOSED")
63
+ return false;
64
+ if (rest.mergeable !== report.mergeStatus.mergeable)
65
+ return false;
66
+ if (rest.mergeStateStatus !== report.mergeStatus.mergeStateStatus)
67
+ return false;
68
+ return true;
69
+ }
@@ -4,5 +4,6 @@ export declare function runCheck(opts: GlobalOptions & {
4
4
  autoMinimizeSuppressed?: boolean;
5
5
  skipTriage?: boolean;
6
6
  persistSeen?: boolean;
7
+ fingerprintCache?: boolean;
7
8
  merge?: boolean;
8
9
  }): Promise<ShepherdReport>;
@@ -1,4 +1,6 @@
1
1
  import { fetchPrBatch } from "../github/batch.mjs";
2
+ import { storePrFingerprint } from "../state/pr-fingerprint.mjs";
3
+ import { tryReuseFingerprintReport } from "./check-fingerprint.mjs";
2
4
  import { getRepoInfo, getCurrentPrNumber } from "../github/client.mjs";
3
5
  import { classifyChecks, getCiVerdict } from "../checks/classify.mjs";
4
6
  import { mergeStartupFailureChecks } from "../checks/startup-failures.mjs";
@@ -30,6 +32,12 @@ export async function runCheck(opts) {
30
32
  }
31
33
  const stateKey = { owner: repo.owner, repo: repo.name, pr: prNumber };
32
34
  const config = loadConfig();
35
+ const reuseFingerprint = opts.fingerprintCache === true;
36
+ if (reuseFingerprint) {
37
+ const cached = await tryReuseFingerprintReport(prNumber, repo, stateKey, config);
38
+ if (cached)
39
+ return cached;
40
+ }
33
41
  const paginateApprovedReviews = config.iterate.minimizeApprovals;
34
42
  const result = await fetchPrBatch(prNumber, repo, { paginateApprovedReviews });
35
43
  let batchData = result.data;
@@ -38,7 +46,11 @@ export async function runCheck(opts) {
38
46
  const didRefreshMergeability = unknownRefresh.didRefresh;
39
47
  let mergeStatus = deriveMergeStatus(batchData);
40
48
  if (mergeStatus.state === "MERGED" || mergeStatus.state === "CLOSED") {
41
- return buildTerminalReport(prNumber, repo, batchData, mergeStatus, mergeStatus.state);
49
+ const terminal = buildTerminalReport(prNumber, repo, batchData, mergeStatus, mergeStatus.state);
50
+ if (result.fingerprint) {
51
+ await storePrFingerprint(stateKey, result.fingerprint, terminal, config);
52
+ }
53
+ return terminal;
42
54
  }
43
55
  const startupFailuresNeedAttempt = batchData.checks.some((check) => check.source === "startup_failure" && check.runAttempt === undefined);
44
56
  const startupFailureChecks = result.checkSuitesComplete && !startupFailuresNeedAttempt
@@ -145,7 +157,11 @@ export async function runCheck(opts) {
145
157
  mergeStatus = refreshed.mergeStatus;
146
158
  status = refreshed.status;
147
159
  if (mergeStatus.state === "MERGED" || mergeStatus.state === "CLOSED") {
148
- return buildTerminalReport(prNumber, repo, batchData, mergeStatus, mergeStatus.state);
160
+ const terminal = buildTerminalReport(prNumber, repo, batchData, mergeStatus, mergeStatus.state);
161
+ if (result.fingerprint) {
162
+ await storePrFingerprint(stateKey, result.fingerprint, terminal, config);
163
+ }
164
+ return terminal;
149
165
  }
150
166
  }
151
167
  // Mirrors the deferral gate in commands/iterate/index.mts exactly (via the shared
@@ -225,7 +241,7 @@ export async function runCheck(opts) {
225
241
  batchData.isInMergeQueue ||
226
242
  batchData.autoMergeRequest ||
227
243
  batchData.latestMergeQueueRemoval);
228
- return {
244
+ const report = {
229
245
  pr: prNumber,
230
246
  nodeId: batchData.nodeId,
231
247
  headSha: batchData.headRefOid,
@@ -290,6 +306,10 @@ export async function runCheck(opts) {
290
306
  },
291
307
  }),
292
308
  };
309
+ if (result.fingerprint) {
310
+ await storePrFingerprint(stateKey, result.fingerprint, report, config);
311
+ }
312
+ return report;
293
313
  }
294
314
  async function remainingRuleAutoResolveIds(partition, autoMinimizeSuppressed = false) {
295
315
  const consumedIds = autoMinimizeSuppressed
@@ -1,2 +1,2 @@
1
1
  import type { IterateResult } from "../../types.mts";
2
- export declare function attachApiUsage(result: IterateResult, persistWarning: boolean, preservePersistedWarning?: boolean): Promise<IterateResult>;
2
+ export declare function attachApiUsage(result: IterateResult, persistWarning: boolean, preservePersistedWarning?: boolean, minimumPollIntervalMinutes?: number): Promise<IterateResult>;
@@ -5,7 +5,7 @@ import { buildQuotaAwareContinuation } from "../../quota-warning.mjs";
5
5
  function shouldWarn(result) {
6
6
  return ["wait", "mark_ready", "merge", "fix_code"].includes(result.action);
7
7
  }
8
- export async function attachApiUsage(result, persistWarning, preservePersistedWarning = false) {
8
+ export async function attachApiUsage(result, persistWarning, preservePersistedWarning = false, minimumPollIntervalMinutes = 0) {
9
9
  const apiUsage = summarizeApiTelemetry();
10
10
  if (apiUsage === undefined)
11
11
  return result;
@@ -13,7 +13,11 @@ export async function attachApiUsage(result, persistWarning, preservePersistedWa
13
13
  if (quotaWarning === undefined && apiUsage.graphql !== undefined && shouldWarn(result)) {
14
14
  const [owner, repo] = result.repo.split("/");
15
15
  if (owner && repo) {
16
- quotaWarning = await evaluateWorktreeGraphqlQuotaWarning({ owner, repo }, loadConfig().watch.graphqlQuotaWarnings, apiUsage.graphql, persistWarning);
16
+ const bands = loadConfig().watch.graphqlQuotaWarnings.map((band) => ({
17
+ ...band,
18
+ pollIntervalMinutes: Math.max(band.pollIntervalMinutes, minimumPollIntervalMinutes),
19
+ }));
20
+ quotaWarning = await evaluateWorktreeGraphqlQuotaWarning({ owner, repo }, bands, apiUsage.graphql, persistWarning);
17
21
  }
18
22
  }
19
23
  const { quotaWarning: _deferredWarning, ...baseResult } = result;
@@ -21,5 +21,6 @@ export function buildIterateBase(report, readyState) {
21
21
  ...buildSuppressedCheckFields(report),
22
22
  activity: report.activity,
23
23
  mergeQueue: report.mergeQueue,
24
+ ...(report.fingerprintReused === true && { fingerprintReused: true }),
24
25
  };
25
26
  }
@@ -4,6 +4,14 @@ import { buildEscalateHumanMessage, buildEscalateSuggestion } from "./escalate.m
4
4
  export async function markReadyIfAuthorized(enabled, base, report) {
5
5
  if (!enabled)
6
6
  return null;
7
+ if (report.fingerprintReused === true) {
8
+ return {
9
+ ...base,
10
+ action: "mark_ready",
11
+ markedReady: false,
12
+ log: `READY: PR #${report.pr} is ready to mark; refresh required before converting draft`,
13
+ };
14
+ }
7
15
  if (report.viewerAuthorization?.viewerCanUpdate === true) {
8
16
  await graphql(MARK_PR_READY_MUTATION, { pullRequestId: report.nodeId });
9
17
  return {
@@ -3,6 +3,6 @@ import { attachApiUsage } from "./api-usage.mjs";
3
3
  export function withIterateApiUsage(opts, runCore) {
4
4
  return withApiTelemetryScope(async () => {
5
5
  const result = await runCore();
6
- return attachApiUsage(result, opts.deferQuotaWarning !== true);
6
+ return attachApiUsage(result, opts.deferQuotaWarning !== true, false, opts.quotaWarningMinimumPollIntervalMinutes);
7
7
  });
8
8
  }
@@ -0,0 +1,11 @@
1
+ import type { IterateResult } from "../types.mts";
2
+ export declare function writeWaitProgress(opts: {
3
+ tick: number;
4
+ elapsedMs: number;
5
+ sleepMs: number;
6
+ result: IterateResult;
7
+ quietStatus: boolean;
8
+ verbose: boolean;
9
+ lastWaitSignature: string | null;
10
+ }): string | null;
11
+ export declare function writeDebounceProgress(tick: number, elapsedMs: number, remainingMs: number): void;
@@ -0,0 +1,52 @@
1
+ function writeTickProgress(tick, elapsedSeconds, detail) {
2
+ process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s] WAIT — ${detail}\n`);
3
+ }
4
+ function waitSignature(result) {
5
+ const activity = result.activity ?? {
6
+ commitCount: 0,
7
+ reviewRoundCount: 0,
8
+ latestCommitCommittedAtUnix: null,
9
+ reviewItemsSinceLatestCommit: [],
10
+ };
11
+ return JSON.stringify({
12
+ status: result.status,
13
+ mergeStateStatus: result.mergeStateStatus,
14
+ reviewDecision: result.reviewDecision,
15
+ state: result.state,
16
+ active: (result.inProgressChecks ?? []).map((c) => [c.name, c.status, c.runId]),
17
+ commitCount: activity.commitCount,
18
+ latestCommitCommittedAtUnix: activity.latestCommitCommittedAtUnix,
19
+ reviewRoundCount: activity.reviewRoundCount,
20
+ reviewItemsSinceLatestCommit: activity.reviewItemsSinceLatestCommit.length,
21
+ });
22
+ }
23
+ function writeQuietStatus(tick, elapsedSeconds, sleepSeconds, result) {
24
+ const activeChecks = result.inProgressChecks ?? [];
25
+ const activeCheckText = activeChecks.map((c) => `${c.name} (${c.status})`).join(", ");
26
+ const active = activeChecks.length > 0 ? ` · active: ${activeCheckText}` : "";
27
+ const commitCount = result.activity?.commitCount ?? 0;
28
+ const reviewItems = result.activity?.reviewItemsSinceLatestCommit.length ?? 0;
29
+ const reviewRounds = result.activity?.reviewRoundCount ?? 0;
30
+ const commitSeg = commitCount > 0 ? ` · ${commitCount} commits` : "";
31
+ const reviewRoundSeg = reviewRounds > 0 ? ` · ${reviewRounds} review rounds` : "";
32
+ const reviewSeg = reviewItems > 0 ? ` · ${reviewItems} review items since latest commit` : "";
33
+ process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s] WAIT ${result.status}/${result.mergeStateStatus}/${result.reviewDecision ?? "NO_REVIEW_DECISION"}${active}${commitSeg}${reviewRoundSeg}${reviewSeg} — sleeping ${sleepSeconds}s\n`);
34
+ }
35
+ export function writeWaitProgress(opts) {
36
+ const elapsedSeconds = Math.round(opts.elapsedMs / 1000);
37
+ const sleepSeconds = Math.round(opts.sleepMs / 1000);
38
+ if (!opts.quietStatus) {
39
+ writeTickProgress(opts.tick, elapsedSeconds, opts.verbose ? `sleeping ${sleepSeconds}s` : `still running; next tick in ${sleepSeconds}s`);
40
+ return opts.lastWaitSignature;
41
+ }
42
+ const signature = waitSignature(opts.result);
43
+ if (signature !== opts.lastWaitSignature) {
44
+ writeQuietStatus(opts.tick, elapsedSeconds, sleepSeconds, opts.result);
45
+ }
46
+ return signature;
47
+ }
48
+ export function writeDebounceProgress(tick, elapsedMs, remainingMs) {
49
+ const elapsedSeconds = Math.round(elapsedMs / 1000);
50
+ const remainingSeconds = Math.round(remainingMs / 1000);
51
+ process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s] FIX_CODE — debounce ${remainingSeconds}s remaining\n`);
52
+ }
@@ -0,0 +1,9 @@
1
+ import type { GraphqlQuotaWarningBand } from "../config/load.mts";
2
+ import type { GraphqlApiUsage } from "../types.mts";
3
+ /** Sleep at least `--interval`, and at least the active crossed quota band. */
4
+ export declare function graphqlQuotaPollIntervalMs(bands: GraphqlQuotaWarningBand[], usage: Pick<GraphqlApiUsage, "remaining" | "limit"> | undefined, fallbackMs: number, maxMs: number): number;
5
+ /**
6
+ * Retry delay for `--until-terminal` when GitHub returns a GraphQL 429 / secondary
7
+ * limit. `null` means the error is not a retryable rate limit.
8
+ */
9
+ export declare function pollGraphQlRetryAfterMs(err: unknown): number | null;
@@ -0,0 +1,36 @@
1
+ import { GitHubRequestError } from "../github/errors.mjs";
2
+ import { isRateLimitMessage } from "../comments/rate-limit.mjs";
3
+ const GRAPHQL_RETRY_AFTER_DEFAULT_MS = 60_000;
4
+ /** Sleep at least `--interval`, and at least the active crossed quota band. */
5
+ export function graphqlQuotaPollIntervalMs(bands, usage, fallbackMs, maxMs) {
6
+ if (usage === undefined || usage.limit <= 0 || bands.length === 0) {
7
+ return Math.min(fallbackMs, maxMs);
8
+ }
9
+ const crossed = bands.filter((band) => usage.remaining * 100 <= usage.limit * band.remainingPercent);
10
+ if (crossed.length === 0)
11
+ return Math.min(fallbackMs, maxMs);
12
+ const active = crossed.reduce((lowest, band) => band.remainingPercent < lowest.remainingPercent ? band : lowest);
13
+ const bandMs = active.pollIntervalMinutes * 60_000;
14
+ return Math.min(Math.max(fallbackMs, bandMs), maxMs);
15
+ }
16
+ /**
17
+ * Retry delay for `--until-terminal` when GitHub returns a GraphQL 429 / secondary
18
+ * limit. `null` means the error is not a retryable rate limit.
19
+ */
20
+ export function pollGraphQlRetryAfterMs(err) {
21
+ if (!(err instanceof GitHubRequestError))
22
+ return null;
23
+ const retryable = err.status === 429 ||
24
+ err.retryAfterSeconds !== undefined ||
25
+ isRateLimitMessage(err.message) ||
26
+ (err.graphqlErrors?.some((error) => isRateLimitMessage(error.message)) ?? false) ||
27
+ (err.rateLimit !== undefined && err.rateLimit.remaining <= 0);
28
+ if (!retryable)
29
+ return null;
30
+ if (err.retryAfterSeconds !== undefined)
31
+ return Math.max(err.retryAfterSeconds, 0) * 1000;
32
+ if (err.rateLimit !== undefined && err.rateLimit.remaining <= 0) {
33
+ return Math.max(err.rateLimit.resetAt * 1000 - Date.now(), 0);
34
+ }
35
+ return GRAPHQL_RETRY_AFTER_DEFAULT_MS;
36
+ }
@@ -1,2 +1,2 @@
1
1
  import type { IterateResult } from "../types.mts";
2
- export declare function withPollApiUsage(runCore: () => Promise<IterateResult>, preservePersistedWarning: boolean): Promise<IterateResult>;
2
+ export declare function withPollApiUsage(runCore: () => Promise<IterateResult>, preservePersistedWarning: boolean, minimumPollIntervalMinutes: number): Promise<IterateResult>;
@@ -1,8 +1,8 @@
1
1
  import { withApiTelemetryScope } from "../github/api-telemetry.mjs";
2
2
  import { attachApiUsage } from "./iterate/api-usage.mjs";
3
- export function withPollApiUsage(runCore, preservePersistedWarning) {
3
+ export function withPollApiUsage(runCore, preservePersistedWarning, minimumPollIntervalMinutes) {
4
4
  return withApiTelemetryScope(async () => {
5
5
  const result = await runCore();
6
- return attachApiUsage(result, true, preservePersistedWarning);
6
+ return attachApiUsage(result, true, preservePersistedWarning, minimumPollIntervalMinutes);
7
7
  });
8
8
  }