pr-shepherd 0.60.1 → 1.0.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 (197) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +7 -5
  3. package/bin/api.d.mts +35 -24
  4. package/bin/api.mjs +68 -48
  5. package/bin/checks/classify.d.mts +1 -1
  6. package/bin/checks/classify.mjs +4 -4
  7. package/bin/checks/log-excerpt.mjs +1 -1
  8. package/bin/cli/arg-flags.mjs +0 -3
  9. package/bin/cli/clean-handler.d.mts +1 -1
  10. package/bin/cli/clean-handler.mjs +5 -15
  11. package/bin/cli/default-poll.d.mts +2 -0
  12. package/bin/cli/default-poll.mjs +9 -6
  13. package/bin/cli/formatters.d.mts +2 -3
  14. package/bin/cli/formatters.mjs +0 -26
  15. package/bin/cli/handlers.d.mts +3 -3
  16. package/bin/cli/handlers.mjs +45 -35
  17. package/bin/cli/help-command-pages.d.mts +13 -158
  18. package/bin/cli/help-command-pages.mjs +12 -157
  19. package/bin/cli/help-iterate-poll-pages.d.mts +4 -3
  20. package/bin/cli/help-iterate-poll-pages.mjs +76 -36
  21. package/bin/cli/help-log-file-page.d.mts +1 -1
  22. package/bin/cli/help-log-file-page.mjs +3 -5
  23. package/bin/cli/help-top-page.d.mts +1 -1
  24. package/bin/cli/help-top-page.mjs +19 -20
  25. package/bin/cli/help.d.mts +14 -159
  26. package/bin/cli/iterate-checks-formatter.mjs +1 -1
  27. package/bin/cli/iterate-flags.d.mts +2 -4
  28. package/bin/cli/iterate-flags.mjs +4 -20
  29. package/bin/cli/iterate-formatter.d.mts +1 -1
  30. package/bin/cli/iterate-formatter.mjs +6 -6
  31. package/bin/cli/iterate-instructions.d.mts +1 -1
  32. package/bin/cli/iterate-instructions.mjs +5 -6
  33. package/bin/cli/iterate-lean.mjs +32 -40
  34. package/bin/cli/iterate-verbose.mjs +1 -1
  35. package/bin/cli/journal-formatter.mjs +0 -3
  36. package/bin/cli/journal-handler.d.mts +1 -1
  37. package/bin/cli/journal-handler.mjs +2 -1
  38. package/bin/cli/mutate-formatter.mjs +0 -9
  39. package/bin/cli/next-check-format.d.mts +0 -4
  40. package/bin/cli/next-check-format.mjs +0 -4
  41. package/bin/cli/poll-handler.d.mts +3 -0
  42. package/bin/cli/poll-handler.mjs +88 -47
  43. package/bin/cli/poll-summary-emitter.mjs +2 -2
  44. package/bin/cli/poll-summary-formatter.mjs +2 -2
  45. package/bin/cli/poll-targets.d.mts +2 -2
  46. package/bin/cli/poll-targets.mjs +15 -35
  47. package/bin/cli/removed-flags.d.mts +3 -0
  48. package/bin/cli/removed-flags.mjs +24 -0
  49. package/bin/cli/runner.d.mts +1 -1
  50. package/bin/cli/runner.mjs +2 -2
  51. package/bin/cli/stack-overview.d.mts +0 -1
  52. package/bin/cli/stack-overview.mjs +1 -2
  53. package/bin/cli/{fix-formatter-extra.d.mts → triage-formatter-extra.d.mts} +1 -2
  54. package/bin/cli/{fix-formatter-extra.mjs → triage-formatter-extra.mjs} +0 -6
  55. package/bin/cli/triage-formatter.d.mts +4 -0
  56. package/bin/cli/{fix-formatter.mjs → triage-formatter.mjs} +31 -42
  57. package/bin/cli/unknown-flags.d.mts +6 -0
  58. package/bin/cli/unknown-flags.mjs +29 -0
  59. package/bin/cli-parser.mjs +41 -62
  60. package/bin/commands/check-annotations.d.mts +1 -1
  61. package/bin/commands/check-annotations.mjs +1 -1
  62. package/bin/commands/check-review-budget.d.mts +1 -1
  63. package/bin/commands/check-review-budget.mjs +1 -1
  64. package/bin/commands/check-wait-detectors.d.mts +1 -1
  65. package/bin/commands/check-wait-detectors.mjs +1 -1
  66. package/bin/commands/check.d.mts +1 -1
  67. package/bin/commands/check.mjs +7 -7
  68. package/bin/commands/commit-suggestion-instruction.d.mts +2 -2
  69. package/bin/commands/commit-suggestion-instruction.mjs +3 -3
  70. package/bin/commands/iterate/api-usage.mjs +8 -8
  71. package/bin/commands/iterate/check-instructions.d.mts +4 -4
  72. package/bin/commands/iterate/check-instructions.mjs +8 -8
  73. package/bin/commands/iterate/classify.mjs +2 -2
  74. package/bin/commands/iterate/escalate.mjs +6 -6
  75. package/bin/commands/iterate/index.mjs +20 -23
  76. package/bin/commands/iterate/merge-plan.mjs +3 -3
  77. package/bin/commands/iterate/merge-state.mjs +4 -7
  78. package/bin/commands/iterate/native-stack-rebase.mjs +1 -1
  79. package/bin/commands/iterate/render.d.mts +1 -1
  80. package/bin/commands/iterate/render.mjs +2 -2
  81. package/bin/commands/iterate/stall.mjs +11 -9
  82. package/bin/commands/iterate/{fix-loop-instruction.mjs → triage-loop-instruction.mjs} +7 -3
  83. package/bin/commands/iterate/{fix-code.d.mts → triage.d.mts} +2 -2
  84. package/bin/commands/iterate/{fix-code.mjs → triage.mjs} +14 -23
  85. package/bin/commands/iterate/unreported-required.mjs +2 -5
  86. package/bin/commands/journal/index.d.mts +1 -3
  87. package/bin/commands/journal/index.mjs +1 -1
  88. package/bin/commands/mark-files-as-viewed.d.mts +1 -3
  89. package/bin/commands/mark-files-as-viewed.mjs +1 -1
  90. package/bin/commands/next-check.d.mts +2 -2
  91. package/bin/commands/next-check.mjs +4 -2
  92. package/bin/commands/poll-progress.mjs +1 -1
  93. package/bin/commands/poll-summary-explicit-instructions.mjs +1 -1
  94. package/bin/commands/poll-summary-instructions.mjs +7 -7
  95. package/bin/commands/poll-summary.mjs +4 -4
  96. package/bin/commands/poll.d.mts +1 -1
  97. package/bin/commands/poll.mjs +4 -4
  98. package/bin/commands/resolve-mutate.d.mts +1 -1
  99. package/bin/commands/resolve-mutate.mjs +1 -1
  100. package/bin/commands/rest-stack-merge-readiness.mjs +1 -1
  101. package/bin/commands/single-tick.d.mts +5 -0
  102. package/bin/commands/single-tick.mjs +7 -0
  103. package/bin/commands/stack-drain.mjs +1 -1
  104. package/bin/commands/stack-merge-flag.mjs +1 -1
  105. package/bin/commands/stack-stall.d.mts +2 -2
  106. package/bin/commands/stack-stall.mjs +2 -2
  107. package/bin/commands/stack-work.d.mts +2 -2
  108. package/bin/commands/stack-work.mjs +2 -2
  109. package/bin/commands/suggestion-patch-item.d.mts +0 -1
  110. package/bin/commands/suggestion-patch-item.mjs +0 -12
  111. package/bin/commands/suggestion-patches.d.mts +2 -2
  112. package/bin/commands/suggestion-patches.mjs +1 -1
  113. package/bin/commands/tick-instructions.d.mts +9 -0
  114. package/bin/commands/tick-instructions.mjs +23 -0
  115. package/bin/commands/tick.d.mts +15 -0
  116. package/bin/commands/{event-mode.mjs → tick.mjs} +39 -55
  117. package/bin/comments/minimize-policy.mjs +1 -3
  118. package/bin/comments/resolve.d.mts +0 -23
  119. package/bin/comments/resolve.mjs +0 -4
  120. package/bin/comments/review-output-budget.d.mts +1 -1
  121. package/bin/comments/review-output-budget.mjs +1 -1
  122. package/bin/comments/sha-poll.mjs +3 -1
  123. package/bin/config/default-bot-usernames.d.mts +2 -0
  124. package/bin/config/default-bot-usernames.mjs +15 -0
  125. package/bin/config/load.d.mts +4 -138
  126. package/bin/config/load.mjs +88 -443
  127. package/bin/config/merge-command-args.mjs +2 -2
  128. package/bin/config/merge-method.d.mts +1 -2
  129. package/bin/config/merge-method.mjs +1 -8
  130. package/bin/config/renames.d.mts +8 -0
  131. package/bin/config/renames.mjs +87 -0
  132. package/bin/config/resolve.d.mts +31 -0
  133. package/bin/config/resolve.mjs +89 -0
  134. package/bin/config/schema.d.mts +161 -0
  135. package/bin/config/schema.mjs +168 -0
  136. package/bin/exit-codes.d.mts +1 -1
  137. package/bin/exit-codes.mjs +3 -3
  138. package/bin/github/poll-summary-check-blockers.mjs +1 -1
  139. package/bin/github/poll-summary-command.mjs +8 -6
  140. package/bin/github/poll-summary-projector.mjs +2 -2
  141. package/bin/github/poll-summary-review.mjs +1 -1
  142. package/bin/github/poll-summary-route.mjs +9 -9
  143. package/bin/github/poll-summary-unreported.mjs +1 -1
  144. package/bin/github/poll-summary.mjs +1 -1
  145. package/bin/github/queries.d.mts +1 -1
  146. package/bin/github/queries.mjs +1 -1
  147. package/bin/mcp/server.mjs +48 -54
  148. package/bin/merge-status/derive.mjs +1 -1
  149. package/bin/state/durable-state.d.mts +1 -3
  150. package/bin/state/durable-state.mjs +2 -8
  151. package/bin/state/fix-attempts.d.mts +2 -2
  152. package/bin/state/fix-attempts.mjs +1 -1
  153. package/bin/state/merge-request.mjs +1 -1
  154. package/bin/state/pr-fingerprint.mjs +4 -11
  155. package/bin/state/seen-comments.d.mts +4 -5
  156. package/bin/state/seen-comments.mjs +8 -14
  157. package/bin/state/stack-stall.d.mts +1 -1
  158. package/bin/state/stack-stall.mjs +1 -1
  159. package/bin/state/stall-state-store.d.mts +1 -1
  160. package/bin/state/stall-state-store.mjs +1 -1
  161. package/bin/types/escalate.d.mts +2 -2
  162. package/bin/types/iterate.d.mts +12 -26
  163. package/bin/types/merge-queue.d.mts +2 -2
  164. package/bin/types/next-check.d.mts +1 -1
  165. package/bin/types/poll-summary.d.mts +3 -6
  166. package/bin/types/report.d.mts +2 -2
  167. package/bin/types/suggestion-patch.d.mts +1 -8
  168. package/package.json +2 -1
  169. package/plugins/pr-shepherd/.claude-plugin/plugin.json +1 -1
  170. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  171. package/plugins/pr-shepherd/.codex.mcp.json +1 -1
  172. package/plugins/pr-shepherd/.mcp.json +1 -1
  173. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +6 -6
  174. package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +1 -1
  175. package/plugins/pr-shepherd/skills/pr-shepherd/references/{cloud-event-loop.md → event-loop.md} +5 -5
  176. package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +3 -3
  177. package/plugins/pr-shepherd/skills/pr-shepherd/references/merge-queue-ejection.md +1 -1
  178. package/plugins/pr-shepherd/skills/pr-shepherd/references/suggestion-patches.md +1 -1
  179. package/plugins/pr-shepherd/skills/pr-shepherd/references/{fix-code-loop.md → triage-loop.md} +3 -3
  180. package/plugins/pr-shepherd/skills/reduce-pr-noise/SKILL.md +1 -1
  181. package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +2 -2
  182. package/plugins/pr-shepherd/skills/reduce-pr-noise/references/settings.md +11 -11
  183. package/pr-shepherd.schema.json +361 -0
  184. package/bin/cli/event-state-scope.d.mts +0 -19
  185. package/bin/cli/event-state-scope.mjs +0 -60
  186. package/bin/cli/fix-formatter.d.mts +0 -4
  187. package/bin/commands/commit-suggestion.d.mts +0 -8
  188. package/bin/commands/commit-suggestion.mjs +0 -26
  189. package/bin/commands/event-instructions.d.mts +0 -11
  190. package/bin/commands/event-instructions.mjs +0 -27
  191. package/bin/commands/event-mode.d.mts +0 -14
  192. package/bin/commands/poll-mode.d.mts +0 -8
  193. package/bin/commands/poll-mode.mjs +0 -14
  194. package/bin/config.json +0 -68
  195. package/bin/types/protected-run.d.mts +0 -6
  196. package/bin/types/protected-run.mjs +0 -1
  197. /package/bin/commands/iterate/{fix-loop-instruction.d.mts → triage-loop-instruction.d.mts} +0 -0
@@ -77,22 +77,22 @@ function pendingReviewCommands(resolveCommand, resolveOnlyCommand) {
77
77
  };
78
78
  return Object.keys(pending).length > 0 ? pending : undefined;
79
79
  }
80
- export async function handleFixCode(ctx) {
81
- const result = await handleFixCodeTick(ctx);
80
+ export async function handleTriage(ctx) {
81
+ const result = await handleTriageTick(ctx);
82
82
  // Every escalation from a capped tick carries the rollup: the reader sees a subset.
83
83
  const { reviewItems } = ctx.report;
84
84
  if (result.action !== "escalate" || reviewItems === undefined)
85
85
  return result;
86
86
  return { ...result, escalate: { ...result.escalate, reviewItems } };
87
87
  }
88
- async function handleFixCodeTick(ctx) {
88
+ async function handleTriageTick(ctx) {
89
89
  const { base, report, opts, headSha, stallKey, prNumber, stallTimeoutSeconds, repoOwner, repoName, reviewSummaryIds, firstLookSummaries, editedSummaries, surfacedApprovals, botUsernames, releasedCheckNames = EMPTY_RELEASED, ruleAutoResolveThreadIds, repairInstructions, } = ctx;
90
90
  const prReference = formatPrUrl(report.repo, prNumber);
91
91
  const failingChecks = report.checks.failing;
92
92
  const annotatedExtra = checksWithActionableAnnotations(report).filter((c) => c.category !== "failing");
93
93
  const allThreads = [...report.threads.actionable, ...report.threads.resolutionOnly];
94
94
  const ruleAutoResolveIds = new Set(ruleAutoResolveThreadIds ?? []);
95
- const resolveOtherHumanThreads = loadConfig().iterate.resolveOtherHumanThreads ?? "none";
95
+ const resolveOtherHumanThreads = loadConfig().iterate["resolve-other-human-threads"] ?? "none";
96
96
  const routedThreadMutations = buildThreadMutationRouting(allThreads, botUsernames, [...ruleAutoResolveIds], resolveOtherHumanThreads);
97
97
  const replyIdSet = new Set(routedThreadMutations.replyThreadIds);
98
98
  const resolveIdSet = new Set(routedThreadMutations.resolveThreadIds);
@@ -107,12 +107,11 @@ async function handleFixCodeTick(ctx) {
107
107
  ((thread.path !== null && thread.line !== null) ||
108
108
  threadHasAuthorizedMutation(thread, replyIdSet, resolveIdSet)));
109
109
  const retryableActionableThreads = mutationActionableThreads.filter((thread) => thread.path !== null && thread.line !== null);
110
- const protectedRuns = [];
111
110
  const stored = await readFixAttempts({ owner: repoOwner, repo: repoName, pr: prNumber });
112
111
  await aliasThreadFixAttempts({ owner: repoOwner, name: repoName }, prNumber, retryableActionableThreads.map(({ id }) => id), stored);
113
- const countFixCodeAttempt = opts.persistSeen !== false;
112
+ const countTriageAttempt = opts.persistSeen !== false;
114
113
  const priorThreadAttempts = previousFixAttempts(stored, retryableActionableThreads);
115
- const { threadAttempts, threadBodyHashes } = nextFixAttempts(stored, retryableActionableThreads, countFixCodeAttempt);
114
+ const { threadAttempts, threadBodyHashes } = nextFixAttempts(stored, retryableActionableThreads, countTriageAttempt);
116
115
  const resolutionOnlyThreadsForWork = report.threads.resolutionOnly.filter((thread) => !skippedThreadIds.has(thread.id) &&
117
116
  ((thread.path !== null && thread.line !== null) ||
118
117
  threadHasAuthorizedMutation(thread, replyIdSet, resolveIdSet)));
@@ -126,7 +125,7 @@ async function handleFixCodeTick(ctx) {
126
125
  ...(report.comments.minimizeIds ?? report.comments.actionable.map((comment) => comment.id)),
127
126
  ...reviewSummaryIds,
128
127
  ], changesRequestedReviewsForWork, checks, prReference, botUsernames, ruleAutoResolveThreadIds, report.viewerAuthorization, allThreads, resolveOtherHumanThreads);
129
- const escalateTriggers = countFixCodeAttempt
128
+ const escalateTriggers = countTriageAttempt
130
129
  ? checkEscalateTriggers(retryableActionableThreads, priorThreadAttempts)
131
130
  : { triggers: [], thrashHistory: undefined };
132
131
  if (escalateTriggers.triggers.length > 0) {
@@ -161,7 +160,6 @@ async function handleFixCodeTick(ctx) {
161
160
  // it does not confirm the credential executing `gh` has that scope; an unauthorized rerun
162
161
  // simply fails when the agent runs it, the same residual risk as every other CLI-recommended
163
162
  // git/gh mutation in this codebase.
164
- const cancelled = [];
165
163
  const baseLookup = validateBaseBranch(report.baseBranch);
166
164
  const threads = report.threads.actionable.map(toAgentThread);
167
165
  const resolutionOnlyThreads = report.threads.resolutionOnly;
@@ -204,11 +202,7 @@ async function handleFixCodeTick(ctx) {
204
202
  const { changesRequestedReviews } = report;
205
203
  const hasConflicts = report.mergeStatus.status === "CONFLICTS";
206
204
  const isBehind = report.mergeStatus.status === "BEHIND";
207
- const { behindBaseHint, instructions: configuredInstructions } = loadConfig().iterate;
208
- // Only surface in-progress runs when a push is plausible — resolution-only and
209
- // summary-only iterations have no path to a push, so listing runs would prompt
210
- // unnecessary cancellation.
211
- const inProgressRunIds = [];
205
+ const { "behind-base-hint": behindBaseHint, instructions: configuredInstructions } = loadConfig().iterate;
212
206
  const commentMinimizeIds = report.comments.minimizeIds ?? actionableComments.map((c) => c.id);
213
207
  const belongsToActiveWorkflowRun = (check) => check.runId !== null && inProgressWorkflowRunIds.has(check.runId);
214
208
  const manualFollowUpChecks = failingAgentChecks.filter((check) => !releasedCheckNames.has(check.name) &&
@@ -265,7 +259,7 @@ async function handleFixCodeTick(ctx) {
265
259
  };
266
260
  }
267
261
  // Push access to the PR head branch is a usage precondition. Build review mutations for
268
- // conflict ticks normally so the caller can push and complete the same fix_code cycle.
262
+ // conflict ticks normally so the caller can push and complete the same triage cycle.
269
263
  const { resolveCommand, resolveOnlyCommand } = buildReviewCommands(failingAgentChecks);
270
264
  // Safety: if the base branch is unknown, escalate when a push is plausible — the agent
271
265
  // would need the correct base to rebase safely. This is a conservative guard, not a
@@ -346,7 +340,7 @@ async function handleFixCodeTick(ctx) {
346
340
  const stackRebase = hasConflicts || (isBehind && exhaustedAttempts.length > 0) || queueEjection
347
341
  ? buildNativeStackLayerRebase(report.repo, { number: prNumber, baseBranch: baseLookup.branch }, stack, trunkConflict, report.stackBottomPr)
348
342
  : undefined;
349
- const instructions = buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseLookup.branch, resolveCommand, hasConflicts, prReference, cancelled.length, firstLookThreads, firstLookComments, firstLookSummaries, editedSummaries, inProgressRunIds, resolutionOnlyThreads, resolveOnlyCommand, behindBaseHint, isBehind, canGenerateGithubMutation(report.viewerAuthorization?.viewerCanUpdate, "ready"), exhaustedAttempts.length > 0, stackRebase, queueEjection, opts.instructions ?? configuredInstructions);
343
+ const instructions = buildFixInstructions(threads, actionableComments, checks, changesRequestedReviews, baseLookup.branch, resolveCommand, hasConflicts, prReference, firstLookThreads, firstLookComments, firstLookSummaries, editedSummaries, resolutionOnlyThreads, resolveOnlyCommand, behindBaseHint, isBehind, canGenerateGithubMutation(report.viewerAuthorization?.viewerCanUpdate, "ready"), exhaustedAttempts.length > 0, stackRebase, queueEjection, opts.instructions ?? configuredInstructions);
350
344
  if (recovery && "unavailable" in recovery)
351
345
  instructions.unshift(recovery.unavailable);
352
346
  if (acknowledgmentRecovery && "unavailable" in acknowledgmentRecovery)
@@ -391,8 +385,8 @@ async function handleFixCodeTick(ctx) {
391
385
  baseBranch: baseLookup.branch,
392
386
  ...(mergedBasePullRequests.length > 0 && { mergedBasePullRequests }),
393
387
  ...(trunkConflict && { stackTrunkConflict: trunkConflict.trunk }),
394
- action: "fix_code",
395
- fix: {
388
+ action: "triage",
389
+ triage: {
396
390
  threads,
397
391
  resolutionOnlyThreads,
398
392
  actionableComments,
@@ -409,16 +403,13 @@ async function handleFixCodeTick(ctx) {
409
403
  instructions,
410
404
  firstLookThreads,
411
405
  firstLookComments,
412
- inProgressRunIds,
413
- protectedRuns,
414
406
  ...(report.reviewItems && { reviewItems: report.reviewItems }),
415
407
  },
416
- cancelled,
417
408
  };
418
409
  const result = await applyStallGuard(stallKey, stallTimeoutSeconds, headSha, base, prNumber, prospectiveResult, report, reviewSummaryIds);
419
- if (result.action === "fix_code" && opts.persistSeen !== false) {
410
+ if (result.action === "triage" && opts.persistSeen !== false) {
420
411
  await writeFixAttempts({ owner: repoOwner, repo: repoName, pr: prNumber }, { headSha, threadAttempts, threadBodyHashes });
421
- await Promise.allSettled(result.fix.checks.flatMap((ch) => (ch.annotations ?? []).map((a) => markSeen(stallKey, a.id, annotationMarkerBody(a)))));
412
+ await Promise.allSettled(result.triage.checks.flatMap((ch) => (ch.annotations ?? []).map((a) => markSeen(stallKey, a.id, annotationMarkerBody(a)))));
422
413
  }
423
414
  return result;
424
415
  }
@@ -84,8 +84,8 @@ export function buildUnreportedFixResult(base, report, instructions) {
84
84
  const prUrl = formatPrUrl(report.repo, report.pr);
85
85
  return {
86
86
  ...base,
87
- action: "fix_code",
88
- fix: {
87
+ action: "triage",
88
+ triage: {
89
89
  threads: [],
90
90
  resolutionOnlyThreads: [],
91
91
  actionableComments: [],
@@ -102,12 +102,9 @@ export function buildUnreportedFixResult(base, report, instructions) {
102
102
  hasMutations: false,
103
103
  },
104
104
  instructions: [...instructions, buildFixCompletionInstruction()],
105
- inProgressRunIds: [],
106
- protectedRuns: [],
107
105
  firstLookThreads: [],
108
106
  firstLookComments: [],
109
107
  },
110
- cancelled: [],
111
108
  };
112
109
  }
113
110
  function stackRebase(report) {
@@ -13,8 +13,6 @@ export interface JournalResult {
13
13
  sectionExisted: boolean;
14
14
  dryRun: boolean;
15
15
  previewBody?: string;
16
- /** @deprecated Explicit journal requests now rely on GitHub's mutation response. */
17
- authorizationSkipped?: "denied-or-unverifiable";
18
16
  }
19
- /** @deprecated Hidden implementation for standalone `journal`; use `apply journal`. */
17
+ /** Implementation for `apply journal`. */
20
18
  export declare function runJournal(opts: RunJournalOptions): Promise<JournalResult>;
@@ -1,6 +1,6 @@
1
1
  import { getRepoInfo, getPullRequestBody, updatePullRequestBody, getCurrentPrNumber, } from "../../github/client.mjs";
2
2
  import { validateJournalItem, appendJournalItem } from "./transform.mjs";
3
- /** @deprecated Hidden implementation for standalone `journal`; use `apply journal`. */
3
+ /** Implementation for `apply journal`. */
4
4
  export async function runJournal(opts) {
5
5
  const validation = validateJournalItem(opts.rawItem);
6
6
  if (!validation.ok) {
@@ -21,8 +21,6 @@ export interface MarkFilesAsViewedResult {
21
21
  errors: string[];
22
22
  rateLimit?: ResolveRateLimitStop;
23
23
  unmarkedPaths?: string[];
24
- /** @deprecated Explicit file-view requests are attempted; GitHub authorizes the mutation. */
25
- authorizationSkipped?: "unverifiable";
26
24
  }
27
- /** @deprecated Hidden implementation for `mark-files-as-viewed`; use `apply files`. */
25
+ /** Implementation for `apply files`. */
28
26
  export declare function runMarkFilesAsViewed(opts: MarkFilesAsViewedOptions): Promise<MarkFilesAsViewedResult>;
@@ -34,7 +34,7 @@ const FILES_QUERY = `query PullRequestFiles($owner: String!, $repo: String!, $pr
34
34
  const TEST_FILE_RE = /(^|\/)(tests?|__tests__|spec)(\/|$)|\.(test|spec)\.[cm]?[jt]sx?$|_tests?\.rs$|(^|\/)tests?\.rs$/i;
35
35
  // Keep mutation batches small so rate-limit stops leave an ordered pending list.
36
36
  const MARK_FILES_CHUNK_SIZE = 10;
37
- /** @deprecated Hidden implementation for `mark-files-as-viewed`; use `apply files`. */
37
+ /** Implementation for `apply files`. */
38
38
  export async function runMarkFilesAsViewed(opts) {
39
39
  return githubOperation("markFileAsViewed", () => runGraphqlMarkFilesAsViewed(opts), async () => {
40
40
  throw new UnsupportedRestOperationError("markFileAsViewed");
@@ -8,10 +8,10 @@ export interface NextCheckCandidate {
8
8
  seconds: number;
9
9
  }
10
10
  /** The earliest candidate as a `NextCheck`, with `at` rounded up to a whole minute. */
11
- export declare function earliestNextCheck(candidates: NextCheckCandidate[], nowMs?: number): NextCheck | undefined;
11
+ export declare function earliestNextCheck(candidates: NextCheckCandidate[], nowMs?: number, minimumSeconds?: number): NextCheck | undefined;
12
12
  interface NextCheckFacts {
13
13
  /** The action Shepherd returned for this tick. */
14
- action: "wait" | "ready" | "mark_ready" | "fix_code" | "merge" | "cancel" | "escalate";
14
+ action: "wait" | "ready" | "mark_ready" | "triage" | "merge" | "cancel" | "escalate";
15
15
  /** Ready-delay seconds left; only meaningful for `ready`. */
16
16
  remainingSeconds?: number;
17
17
  queued?: boolean;
@@ -3,11 +3,13 @@ export const MERGE_QUEUE_RECHECK_SECONDS = 300;
3
3
  /** Backstop for a missed GitHub event; events normally wake the session long before this. */
4
4
  export const SAFETY_NET_SECONDS = 3000;
5
5
  /** The earliest candidate as a `NextCheck`, with `at` rounded up to a whole minute. */
6
- export function earliestNextCheck(candidates, nowMs = Date.now()) {
6
+ export function earliestNextCheck(candidates, nowMs = Date.now(), minimumSeconds = 0) {
7
7
  const first = [...candidates].sort((a, b) => a.seconds - b.seconds)[0];
8
8
  if (!first)
9
9
  return undefined;
10
- const atMs = Math.ceil((nowMs + Math.max(0, first.seconds) * 1000) / 60_000) * 60_000;
10
+ // A quota warning's interval is a floor: no wake-up may come sooner than it permits.
11
+ const seconds = Math.max(0, first.seconds, minimumSeconds);
12
+ const atMs = Math.ceil((nowMs + seconds * 1000) / 60_000) * 60_000;
11
13
  return {
12
14
  at: new Date(atMs).toISOString().replace(/\.\d{3}Z$/, "Z"),
13
15
  inSeconds: Math.max(0, Math.round((atMs - nowMs) / 1000)),
@@ -53,7 +53,7 @@ export function writeWaitProgress(opts) {
53
53
  export function writeDebounceProgress(tick, elapsedMs, remainingMs) {
54
54
  const elapsedSeconds = Math.round(elapsedMs / 1000);
55
55
  const remainingSeconds = Math.round(remainingMs / 1000);
56
- process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s] FIX_CODE — debounce ${remainingSeconds}s remaining\n`);
56
+ process.stderr.write(`[poll tick ${tick} / +${elapsedSeconds}s] TRIAGE — debounce ${remainingSeconds}s remaining\n`);
57
57
  }
58
58
  export function writeRuleAutoResolveProgress(result) {
59
59
  if (result.ruleAutoResolve?.summary)
@@ -25,7 +25,7 @@ export function explicitInstructions(result) {
25
25
  ];
26
26
  }
27
27
  if (result.reason === "waiting" || result.reason === "timeout") {
28
- return ["1. Run this aggregate selector again when the caller is ready to recheck."];
28
+ return ["1. Run this aggregate command again when the caller is ready to recheck."];
29
29
  }
30
30
  const instructions = [
31
31
  "1. Choose each non-WAIT, non-CANCEL row that can proceed independently and run or delegate its exact `pollCommand`.",
@@ -24,7 +24,7 @@ export function planPollSummary(result, mergeRequested) {
24
24
  const layer = needsOwnSession
25
25
  ? {
26
26
  ...item,
27
- action: "fix_code",
27
+ action: "triage",
28
28
  reasons: [
29
29
  ...item.reasons,
30
30
  staleChildren.has(item.pr) ? "stale-ancestry" : "ready-receipt-required",
@@ -108,8 +108,8 @@ function planStack(result, mergeRequested) {
108
108
  if (ownedWork) {
109
109
  const rerun = onlyDraftReadinessVerification(work.sessions, work.idle) &&
110
110
  work.sessions.every((item) => item.owned)
111
- ? "If every listed one-PR probe returns `[CANCEL]` with reason `auto-mark-ready-disabled`, report the human handoff above and stop this selector. If any probe returns `[ESCALATE]`, report that handoff and stop after the other probes finish. Otherwise follow each probe's instructions and rerun this same `--stack` selector when that work completes."
112
- : "After the listed one-PR sessions, rerun this same `--stack` selector. Stop for the human handoff only when no autonomous shepherding remains.";
111
+ ? "If every listed one-PR probe returns `[CANCEL]` with reason `auto-mark-ready-disabled`, report the human handoff above and stop this `stack` command. If any probe returns `[ESCALATE]`, report that handoff and stop after the other probes finish. Otherwise follow each probe's instructions and rerun this same `stack` command when that work completes."
112
+ : "After the listed one-PR sessions, rerun this same `stack` command. Stop for the human handoff only when no autonomous shepherding remains.";
113
113
  appendStackRerunInstruction(instructions, runnableCandidates, rerun);
114
114
  }
115
115
  return { action: stop ? "escalate" : "shepherd", stackMergeable: false, instructions };
@@ -129,7 +129,7 @@ function planStack(result, mergeRequested) {
129
129
  instructions.push(`${instructions.length + 1}. PR #${item.pr} needs a one-PR session, but Shepherd could not produce its command. ${ownedWork ? "After autonomous shepherding, ask" : "Stop and ask"} for direction.`);
130
130
  }
131
131
  if (ownedWork) {
132
- appendStackRerunInstruction(instructions, runnableCandidates, "After the listed one-PR sessions, rerun this same `--stack` selector. Stop for the human handoff only when no autonomous shepherding remains.");
132
+ appendStackRerunInstruction(instructions, runnableCandidates, "After the listed one-PR sessions, rerun this same `stack` command. Stop for the human handoff only when no autonomous shepherding remains.");
133
133
  }
134
134
  return { action: ownedWork ? "shepherd" : "escalate", stackMergeable: false, instructions };
135
135
  }
@@ -149,10 +149,10 @@ function planStack(result, mergeRequested) {
149
149
  appendAutonomousInstructions(instructions, work.sessions);
150
150
  if (onlyDraftReadinessVerification(work.sessions, work.idle) &&
151
151
  work.sessions.every((item) => item.owned)) {
152
- instructions.push(`${instructions.length + 1}. If every listed one-PR probe returns \`[CANCEL]\` with reason \`auto-mark-ready-disabled\`, stop shepherding this stack and leave those drafts unchanged. If any probe returns \`[ESCALATE]\`, report that handoff and stop after the other probes finish. Otherwise follow each probe's instructions and rerun this same \`--stack\` selector when that work completes.`);
152
+ instructions.push(`${instructions.length + 1}. If every listed one-PR probe returns \`[CANCEL]\` with reason \`auto-mark-ready-disabled\`, stop shepherding this stack and leave those drafts unchanged. If any probe returns \`[ESCALATE]\`, report that handoff and stop after the other probes finish. Otherwise follow each probe's instructions and rerun this same \`stack\` command when that work completes.`);
153
153
  return { action: "shepherd", stackMergeable: false, instructions };
154
154
  }
155
- const action = appendStackRerunInstruction(instructions, work.sessions, "After the selected one-PR sessions, rerun this same `--stack` selector.");
155
+ const action = appendStackRerunInstruction(instructions, work.sessions, "After the selected one-PR sessions, rerun this same `stack` command.");
156
156
  return { action, stackMergeable: false, instructions };
157
157
  }
158
158
  if (open.some((item) => item.isInMergeQueue)) {
@@ -176,7 +176,7 @@ function planStack(result, mergeRequested) {
176
176
  const instructions = [];
177
177
  const sessions = open.filter((item) => item.action !== "cancel");
178
178
  appendAutonomousInstructions(instructions, sessions);
179
- const action = appendStackRerunInstruction(instructions, sessions, "After the selected one-PR sessions, rerun this same `--stack` selector.");
179
+ const action = appendStackRerunInstruction(instructions, sessions, "After the selected one-PR sessions, rerun this same `stack` command.");
180
180
  return { action, stackMergeable, instructions };
181
181
  }
182
182
  return retargetWaitPlan(open[0]);
@@ -61,14 +61,14 @@ async function readPollSummaryTick(opts) {
61
61
  ...(fetched.stackAncestry?.length && { stackAncestry: fetched.stackAncestry }),
62
62
  ...(fetched.allowedMergeMethods && { allowedMergeMethods: fetched.allowedMergeMethods }),
63
63
  }, opts.merge === true);
64
- const result = await applyStackStallGuard(planned, repo, opts.stallTimeoutSeconds ?? loadConfig().iterate.stallTimeoutMinutes * 60);
64
+ const result = await applyStackStallGuard(planned, repo, opts.stallTimeoutSeconds ?? loadConfig().iterate["stall-timeout-minutes"] * 60);
65
65
  return { result, repo, ...(fetched.handoffSource && { handoffSource: fetched.handoffSource }) };
66
66
  }
67
67
  async function runAggregatePollCore(opts) {
68
68
  const intervalMs = Math.min(opts.intervalSeconds * 1000, MAX_TIMER_MS);
69
69
  const timeoutMs = Math.min(opts.timeoutSeconds * 1000, MAX_TIMER_MS);
70
70
  const debounceMs = Math.min((opts.debounceSeconds ?? 60) * 1000, MAX_TIMER_MS);
71
- const quotaBands = loadConfig().watch.graphqlQuotaWarnings;
71
+ const quotaBands = loadConfig().quotaWarningBands;
72
72
  const start = Date.now();
73
73
  let tick = 0;
74
74
  let debounceUntil = null;
@@ -139,7 +139,7 @@ async function runAggregatePollCore(opts) {
139
139
  : last.prs.some((item) => ["escalate", "merge", "mark_ready"].includes(item.action));
140
140
  const hasFix = last.selection.kind === "stack"
141
141
  ? last.nextAction === "shepherd" && last.reason !== "waiting"
142
- : last.prs.some((item) => item.action === "fix_code");
142
+ : last.prs.some((item) => item.action === "triage");
143
143
  const warning = await aggregateQuotaWarning(last, quotaBands, opts.intervalSeconds);
144
144
  pendingQuotaWarning = carryQuotaWarning(pendingQuotaWarning, warning);
145
145
  if (allTerminal) {
@@ -205,7 +205,7 @@ function attachUsage(result, mergeRequested) {
205
205
  const enriched = apiUsage ? { ...result, apiUsage } : result;
206
206
  // A disappeared native stack is an aggregate-level terminal handoff. Keep
207
207
  // that explicit escalation intact even when the one-PR fallback contains
208
- // open orphan rows that would otherwise be re-planned as fix_code.
208
+ // open orphan rows that would otherwise be re-planned as triage.
209
209
  if (enriched.selection.kind === "stack" && enriched.nextAction === "escalate") {
210
210
  return enriched;
211
211
  }
@@ -2,7 +2,7 @@ import type { IterateCommandOptions, IterateResult } from "../types.mts";
2
2
  export interface PollCommandOptions extends IterateCommandOptions {
3
3
  intervalSeconds: number;
4
4
  timeoutSeconds: number;
5
- /** Settle window after first FIX_CODE before returning. Default 60. 0 disables. */
5
+ /** Settle window after first TRIAGE before returning. Default 60. 0 disables. */
6
6
  debounceSeconds?: number;
7
7
  quietStatus?: boolean;
8
8
  untilTerminal?: boolean;
@@ -21,7 +21,7 @@ async function runPollCore(opts) {
21
21
  const timeoutMs = Math.min(timeoutSeconds * 1000, MAX_TIMER_MS);
22
22
  const debounceSeconds = debounceSecondsOpt ?? DEFAULT_POLL_DEBOUNCE_SECONDS;
23
23
  const debounceMs = Math.min(debounceSeconds * 1000, MAX_TIMER_MS);
24
- const quotaBands = loadConfig().watch.graphqlQuotaWarnings;
24
+ const quotaBands = loadConfig().quotaWarningBands;
25
25
  const start = Date.now();
26
26
  let tick = 0;
27
27
  let lastResult;
@@ -82,10 +82,10 @@ async function runPollCore(opts) {
82
82
  };
83
83
  if (untilTerminal &&
84
84
  pendingQuotaWarning !== undefined &&
85
- !(lastResult.action === "fix_code" && debounceSeconds > 0 && !pastDebounce) &&
85
+ !(lastResult.action === "triage" && debounceSeconds > 0 && !pastDebounce) &&
86
86
  !(debounceUntil !== null && !pastDebounce)) {
87
87
  await refreshIfReturning();
88
- if (lastResult.action === "fix_code" && debounceSeconds > 0 && !pastDebounce) {
88
+ if (lastResult.action === "triage" && debounceSeconds > 0 && !pastDebounce) {
89
89
  debounceUntil ??= Date.now() + debounceMs;
90
90
  const remainingMs = Math.max(debounceUntil - Date.now(), 0);
91
91
  writeRuleAutoResolveProgress(lastResult);
@@ -142,7 +142,7 @@ async function runPollCore(opts) {
142
142
  await sleep(sleepMs);
143
143
  continue;
144
144
  }
145
- if (lastResult.action === "fix_code" && debounceSeconds > 0 && !pastDebounce) {
145
+ if (lastResult.action === "triage" && debounceSeconds > 0 && !pastDebounce) {
146
146
  writeRuleAutoResolveProgress(lastResult);
147
147
  debounceUntil ??= Date.now() + debounceMs;
148
148
  const remainingMs = debounceUntil - Date.now();
@@ -1,4 +1,4 @@
1
1
  import type { ResolveOptions } from "../types.mts";
2
2
  import type { ResolveCommandOptions } from "./resolve.mts";
3
- /** @deprecated Hidden implementation for `resolve`; use `apply review`. */
3
+ /** Implementation for `apply review`. */
4
4
  export declare function runResolveMutate(opts: ResolveCommandOptions & ResolveOptions): Promise<import("../comments/resolve.mts").ResolveResult>;
@@ -7,7 +7,7 @@ import { threadTranscriptBodies } from "../threads/transcript.mjs";
7
7
  import { addPrShepherdMarker } from "../comments/marker.mjs";
8
8
  import { adoptedReplyThreads } from "../comments/uncertain-replies.mjs";
9
9
  import { EXIT, ShepherdError } from "../exit-codes.mjs";
10
- /** @deprecated Hidden implementation for `resolve`; use `apply review`. */
10
+ /** Implementation for `apply review`. */
11
11
  export async function runResolveMutate(opts) {
12
12
  const repo = opts.targetRepository ?? (await getRepoInfo());
13
13
  const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
@@ -70,5 +70,5 @@ function guardMember(member) {
70
70
  };
71
71
  }
72
72
  function fail(message) {
73
- throw new ShepherdError(`${message}; rerun the --stack --merge selector before applying this request`, EXIT.UNAVAILABLE);
73
+ throw new ShepherdError(`${message}; rerun \`pr-shepherd stack <PR> --merge\` before applying this request`, EXIT.UNAVAILABLE);
74
74
  }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * True when a looping command (the default command, `stack`) runs one tick instead of
3
+ * sleeping: inside a Claude Code cloud session, where the session owns the next wake-up.
4
+ */
5
+ export declare function runsSingleTick(): boolean;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * True when a looping command (the default command, `stack`) runs one tick instead of
3
+ * sleeping: inside a Claude Code cloud session, where the session owns the next wake-up.
4
+ */
5
+ export function runsSingleTick() {
6
+ return process.env["CLAUDE_CODE_REMOTE"] === "true";
7
+ }
@@ -92,7 +92,7 @@ export function planPrefixDrain(result, mergeRequested) {
92
92
  const handoffs = findHumanHandoffs(result);
93
93
  if (handoffs)
94
94
  appendHumanHandoffInstructions(instructions, handoffs, false);
95
- instructions.push(`${instructions.length + 1}. After the merge attempt, rerun this same \`--stack --merge\` selector; GitHub retargets the next layer onto \`${top.stack.baseRefName}\`. Shepherd any layer that GitHub rejects or ejects.`);
95
+ instructions.push(`${instructions.length + 1}. After the merge attempt, rerun this same \`stack --merge\` command; GitHub retargets the next layer onto \`${top.stack.baseRefName}\`. Shepherd any layer that GitHub rejects or ejects.`);
96
96
  return {
97
97
  action: "merge",
98
98
  stackMergeable: gaps.length === 0 && open.every(isStackLayerReady),
@@ -2,5 +2,5 @@ import { loadConfig } from "../config/load.mjs";
2
2
  import { configuredMergeMethod, mergeMethodFlag, } from "../config/merge-method.mjs";
3
3
  /** Stack commands prefer squash unless config or the repository selects another method. */
4
4
  export function stackMergeFlag(allowed) {
5
- return mergeMethodFlag(allowed, "squash", configuredMergeMethod(loadConfig().merge ?? {}));
5
+ return mergeMethodFlag(allowed, "squash", configuredMergeMethod(loadConfig().merge));
6
6
  }
@@ -1,6 +1,6 @@
1
1
  import type { PollSummaryItem, PollSummaryResult } from "../types.mts";
2
2
  /**
3
- * A `--stack` plan that can only wait never reruns its layers' one-PR sessions, so their own
3
+ * A `stack` plan that can only wait never reruns its layers' one-PR sessions, so their own
4
4
  * stall guards cannot fire. Escalate `stall-timeout` once that idle plan's fingerprint stays
5
5
  * unchanged for `stallTimeoutSeconds`; any other plan, or `0`, clears the timer. When the enabled
6
6
  * timer cannot be read or written, escalate `stall-state-unavailable` instead of restarting it.
@@ -14,6 +14,6 @@ export declare function applyStackStallGuard(planned: {
14
14
  }, stallTimeoutSeconds: number): Promise<PollSummaryResult>;
15
15
  /**
16
16
  * Unix seconds when an unchanged idle stack plan trips `stall-timeout`, or undefined when no
17
- * stack timer is running. Event mode schedules its next tick no later than this.
17
+ * stack timer is running. A single tick schedules its next tick no later than this.
18
18
  */
19
19
  export declare function readStackStallDeadline(result: PollSummaryResult, stallTimeoutSeconds: number): Promise<number | undefined>;
@@ -3,7 +3,7 @@ import { formatDurationApprox } from "./iterate/escalate.mjs";
3
3
  import { summaryStatusSignature } from "./poll-summary-signature.mjs";
4
4
  import { describeIdleLayers } from "./stack-drain.mjs";
5
5
  /**
6
- * A `--stack` plan that can only wait never reruns its layers' one-PR sessions, so their own
6
+ * A `stack` plan that can only wait never reruns its layers' one-PR sessions, so their own
7
7
  * stall guards cannot fire. Escalate `stall-timeout` once that idle plan's fingerprint stays
8
8
  * unchanged for `stallTimeoutSeconds`; any other plan, or `0`, clears the timer. When the enabled
9
9
  * timer cannot be read or written, escalate `stall-state-unavailable` instead of restarting it.
@@ -44,7 +44,7 @@ export async function applyStackStallGuard(planned, repo, stallTimeoutSeconds) {
44
44
  }
45
45
  /**
46
46
  * Unix seconds when an unchanged idle stack plan trips `stall-timeout`, or undefined when no
47
- * stack timer is running. Event mode schedules its next tick no later than this.
47
+ * stack timer is running. A single tick schedules its next tick no later than this.
48
48
  */
49
49
  export async function readStackStallDeadline(result, stallTimeoutSeconds) {
50
50
  const [owner, repo] = result.repo.split("/");
@@ -1,5 +1,5 @@
1
1
  import type { PollSummaryItem } from "../types.mts";
2
- /** How the stack selector routes the autonomous layers that still need work. */
2
+ /** How the stack command routes the autonomous layers that still need work. */
3
3
  export interface StackWork {
4
4
  /** Layers that need a one-PR Shepherd session, with or without a known command. */
5
5
  sessions: PollSummaryItem[];
@@ -18,7 +18,7 @@ export declare function onlyDraftReadinessVerification(sessions: PollSummaryItem
18
18
  */
19
19
  export declare function splitStackWork(candidates: PollSummaryItem[], staleChildren: ReadonlySet<number>): StackWork;
20
20
  /**
21
- * The closing step after routed sessions: rerun the selector, or stop when nothing is owned.
21
+ * The closing step after routed sessions: rerun the stack command, or stop when nothing is owned.
22
22
  * Returns the plan action: a stop is a handoff, so no rerun, quota cadence, or wake-up follows it.
23
23
  */
24
24
  export declare function appendStackRerunInstruction(instructions: string[], layers: PollSummaryItem[], rerun: string): "shepherd" | "escalate";
@@ -29,14 +29,14 @@ function isProbed(item) {
29
29
  return item.pollProbe === true && item.pollCommand !== undefined;
30
30
  }
31
31
  /**
32
- * Work exists, but every routed layer belongs to another author: rerunning the stack selector
32
+ * Work exists, but every routed layer belongs to another author: rerunning the stack command
33
33
  * cannot change anything, so the caller reports the overview and stops instead.
34
34
  */
35
35
  function onlyUnownedWork(layers) {
36
36
  return layers.length > 0 && layers.every((item) => !item.owned);
37
37
  }
38
38
  /**
39
- * The closing step after routed sessions: rerun the selector, or stop when nothing is owned.
39
+ * The closing step after routed sessions: rerun the stack command, or stop when nothing is owned.
40
40
  * Returns the plan action: a stop is a handoff, so no rerun, quota cadence, or wake-up follows it.
41
41
  */
42
42
  export function appendStackRerunInstruction(instructions, layers, rerun) {
@@ -10,6 +10,5 @@ export declare function buildSuggestionPatchItem({ thread, request, originalCont
10
10
  request: SuggestionPatchRequest;
11
11
  originalContent: string;
12
12
  }): SuggestionPatchResult;
13
- export declare function buildSingularInstructions(patch: SuggestionPatchResult, prReference: string | number): string[];
14
13
  export declare function buildCommitCommand(patch: SuggestionPatchResult): string;
15
14
  export declare function quotePath(path: string): string;
@@ -61,18 +61,6 @@ export function buildSuggestionPatchItem({ thread, request, originalContent, })
61
61
  filesToStage: [thread.path],
62
62
  };
63
63
  }
64
- export function buildSingularInstructions(patch, prReference) {
65
- const range = patch.startLine === patch.endLine
66
- ? `line ${patch.startLine}`
67
- : `lines ${patch.startLine}–${patch.endLine}`;
68
- return [
69
- `Apply the patch to \`${patch.path}\`: run \`git apply\` with the diff shown above, or edit the file directly using the line range (${range}).`,
70
- `Stage the file: \`git add -- ${quotePath(patch.path)}\``,
71
- `Commit: \`${buildCommitCommand(patch)}\``,
72
- `Use the originating iterate output for any authorization-checked GitHub review action for thread \`${patch.threadId}\` on ${String(prReference)}.`,
73
- "Shepherd cannot verify authorization for the Git credential that would push this branch, so this output does not recommend a push.",
74
- ];
75
- }
76
64
  export function buildCommitCommand(patch) {
77
65
  return [
78
66
  "git commit",
@@ -1,6 +1,6 @@
1
- import type { BuildSuggestionPatchesResult, GlobalOptions } from "../types.mts";
1
+ import type { SuggestResult, GlobalOptions } from "../types.mts";
2
2
  import { type SuggestionPatchRequest } from "./suggestion-patch-item.mts";
3
3
  export interface SuggestionPatchesOptions extends GlobalOptions {
4
4
  suggestions: readonly SuggestionPatchRequest[];
5
5
  }
6
- export declare function runSuggestionPatches(opts: SuggestionPatchesOptions): Promise<BuildSuggestionPatchesResult>;
6
+ export declare function runSuggestionPatches(opts: SuggestionPatchesOptions): Promise<SuggestResult>;
@@ -21,7 +21,7 @@ export async function runSuggestionPatches(opts) {
21
21
  const status = await getPathsStatus(paths);
22
22
  if (status !== "") {
23
23
  throw new ShepherdError(`Suggestion target files have uncommitted changes:\n${status}\n` +
24
- "Commit or stash them before running build-suggestion-patches.", EXIT.UNAVAILABLE);
24
+ "Commit or stash them before running suggest.", EXIT.UNAVAILABLE);
25
25
  }
26
26
  const originals = await readOriginals(data.headRefOid, paths);
27
27
  const patches = threads.map((thread, index) => buildSuggestionPatchItem({
@@ -0,0 +1,9 @@
1
+ import type { NextCheck } from "../types/next-check.mts";
2
+ /**
3
+ * Steps for a single tick whose PR is waiting: end the turn, keep one wake-up, and let the
4
+ * next tick come from a PR event or the wake-up. Always printed after any state-specific
5
+ * sentence.
6
+ */
7
+ export declare function tickWaitSteps(next: NextCheck): string[];
8
+ /** One aggregate step: sessions still run first, and only then does the turn end. */
9
+ export declare function tickAggregateStep(next: NextCheck, waiting: boolean): string;
@@ -0,0 +1,23 @@
1
+ import { playbookPointer } from "./playbook-pointer.mjs";
2
+ function wakeUpClause(next) {
3
+ const kind = next.reason === "safety-net" ? "safety-net wake-up" : "wake-up";
4
+ return `Keep exactly one ${kind} at \`${next.at}\` (\`${next.reason}\`).`;
5
+ }
6
+ /**
7
+ * Steps for a single tick whose PR is waiting: end the turn, keep one wake-up, and let the
8
+ * next tick come from a PR event or the wake-up. Always printed after any state-specific
9
+ * sentence.
10
+ */
11
+ export function tickWaitSteps(next) {
12
+ return [
13
+ `End this turn now without sleeping or polling. ${playbookPointer("Event loop")}`,
14
+ `${wakeUpClause(next)} When a PR event or that wake-up arrives, rerun this command with the same options and act only on Shepherd's output, never on the event payload.`,
15
+ ];
16
+ }
17
+ /** One aggregate step: sessions still run first, and only then does the turn end. */
18
+ export function tickAggregateStep(next, waiting) {
19
+ const lead = waiting
20
+ ? "End this turn now without sleeping or polling."
21
+ : "Once the steps above are done and the rerun still waits, end the turn without sleeping.";
22
+ return `${lead} ${wakeUpClause(next)} When a PR event or that wake-up arrives, rerun this command with the same options and act only on Shepherd's output, never on the event payload. ${playbookPointer("Event loop")}`;
23
+ }
@@ -0,0 +1,15 @@
1
+ import type { IterateCommandOptions, IterateResult, PollSummaryCommandOptions, PollSummaryResult } from "../types.mts";
2
+ import { type PollCommandOptions } from "./poll.mts";
3
+ import { type AggregatePollCommandOptions } from "./poll-summary.mts";
4
+ /**
5
+ * One iterate tick: no settle window and no sleeping, seen markers persist immediately, and
6
+ * `nextCheck` reports when to come back if no GitHub event arrives first. The tick's result is
7
+ * never discarded, so edited-comment markers are written on the result it returns.
8
+ */
9
+ export declare function runIterateTick(opts: IterateCommandOptions): Promise<IterateResult>;
10
+ /** One aggregate tick, with `nextCheck` unless nothing remains to wait for. */
11
+ export declare function runPollSummaryTick(opts: PollSummaryCommandOptions): Promise<PollSummaryResult>;
12
+ /** The default command: the sleeping loop, or one tick in a cloud session. */
13
+ export declare function runPollCommand(opts: PollCommandOptions): Promise<IterateResult>;
14
+ /** The aggregate loop (`stack`, several PRs), or one tick in a cloud session. */
15
+ export declare function runAggregatePollCommand(opts: AggregatePollCommandOptions): Promise<PollSummaryResult>;