pr-shepherd 0.34.0 → 0.35.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 (187) hide show
  1. package/.claude-plugin/plugin.json +3 -2
  2. package/.grok-plugin/marketplace.json +17 -0
  3. package/README.md +56 -39
  4. package/bin/api.d.mts +80 -0
  5. package/bin/api.mjs +236 -0
  6. package/bin/checks/classify.d.mts +41 -0
  7. package/bin/checks/startup-failures.d.mts +2 -0
  8. package/bin/checks/superseded.d.mts +21 -0
  9. package/bin/checks/triage.d.mts +4 -0
  10. package/bin/classify/apply.d.mts +18 -0
  11. package/bin/classify/apply.mjs +4 -0
  12. package/bin/classify/loader.d.mts +10 -0
  13. package/bin/classify/types.d.mts +35 -0
  14. package/bin/cli/args.d.mts +18 -0
  15. package/bin/cli/clean-formatter.d.mts +2 -0
  16. package/bin/cli/default-poll.d.mts +2 -0
  17. package/bin/cli/duration-flag.d.mts +2 -0
  18. package/bin/cli/duration.d.mts +13 -0
  19. package/bin/cli/fence.d.mts +1 -0
  20. package/bin/cli/fix-formatter-extra.d.mts +3 -0
  21. package/bin/cli/fix-formatter.d.mts +2 -0
  22. package/bin/cli/fix-formatter.mjs +6 -6
  23. package/bin/cli/formatters.d.mts +7 -0
  24. package/bin/cli/handlers.d.mts +4 -0
  25. package/bin/cli/handlers.mjs +11 -10
  26. package/bin/cli/help-command-pages.d.mts +231 -0
  27. package/bin/cli/help-command-pages.mjs +106 -1
  28. package/bin/cli/help-iterate-poll-pages.d.mts +4 -0
  29. package/bin/cli/help-iterate-poll-pages.mjs +4 -2
  30. package/bin/cli/help-log-file-page.d.mts +1 -0
  31. package/bin/cli/help-top-page.d.mts +1 -0
  32. package/bin/cli/help-top-page.mjs +14 -15
  33. package/bin/cli/help.d.mts +236 -0
  34. package/bin/cli/help.mjs +17 -0
  35. package/bin/cli/iterate-emitter.d.mts +8 -0
  36. package/bin/cli/iterate-flags.d.mts +11 -0
  37. package/bin/cli/iterate-formatter.d.mts +19 -0
  38. package/bin/cli/iterate-instructions.d.mts +6 -0
  39. package/bin/cli/iterate-lean.d.mts +12 -0
  40. package/bin/cli/journal-formatter.d.mts +2 -0
  41. package/bin/cli/journal-formatter.mjs +15 -0
  42. package/bin/cli/journal-handler.d.mts +1 -0
  43. package/bin/cli/journal-handler.mjs +7 -20
  44. package/bin/cli/list-formatters.d.mts +76 -0
  45. package/bin/cli/list-formatters.mjs +9 -9
  46. package/bin/cli/mark-files-as-viewed-flags.d.mts +11 -0
  47. package/bin/cli/mark-files-as-viewed-formatter.d.mts +2 -0
  48. package/bin/cli/mutate-formatter.d.mts +2 -0
  49. package/bin/cli/poll-handler.d.mts +1 -0
  50. package/bin/cli/poll-handler.mjs +2 -2
  51. package/bin/cli/resolve-validators.d.mts +3 -0
  52. package/bin/cli/resolve-validators.mjs +3 -3
  53. package/bin/cli/runner.d.mts +7 -0
  54. package/bin/cli/suggestion-renderer.d.mts +3 -0
  55. package/bin/cli/validate-default-args.d.mts +6 -0
  56. package/bin/cli-parser.d.mts +2 -0
  57. package/bin/cli-parser.mjs +72 -16
  58. package/bin/commands/check-annotations.d.mts +5 -0
  59. package/bin/commands/check-status.d.mts +3 -0
  60. package/bin/commands/check-terminal-report.d.mts +5 -0
  61. package/bin/commands/check.d.mts +6 -0
  62. package/bin/commands/check.mjs +2 -1
  63. package/bin/commands/clean.d.mts +21 -0
  64. package/bin/commands/commit-suggestion-instruction.d.mts +8 -0
  65. package/bin/commands/commit-suggestion-instruction.mjs +3 -3
  66. package/bin/commands/commit-suggestion.d.mts +8 -0
  67. package/bin/commands/commit-suggestion.mjs +11 -10
  68. package/bin/commands/iterate/check-instructions.d.mts +16 -0
  69. package/bin/commands/iterate/check-instructions.mjs +3 -3
  70. package/bin/commands/iterate/classify.d.mts +18 -0
  71. package/bin/commands/iterate/classify.mjs +4 -4
  72. package/bin/commands/iterate/escalate.d.mts +31 -0
  73. package/bin/commands/iterate/escalate.mjs +7 -4
  74. package/bin/commands/iterate/fix-code.d.mts +25 -0
  75. package/bin/commands/iterate/helpers.d.mts +13 -0
  76. package/bin/commands/iterate/helpers.mjs +4 -1
  77. package/bin/commands/iterate/index.d.mts +2 -0
  78. package/bin/commands/iterate/index.mjs +5 -2
  79. package/bin/commands/iterate/render.d.mts +5 -0
  80. package/bin/commands/iterate/render.mjs +8 -6
  81. package/bin/commands/iterate/reruns.d.mts +20 -0
  82. package/bin/commands/iterate/stall.d.mts +6 -0
  83. package/bin/commands/journal/index.d.mts +14 -0
  84. package/bin/commands/journal/index.mjs +1 -0
  85. package/bin/commands/journal/transform.d.mts +22 -0
  86. package/bin/commands/log-file.d.mts +5 -0
  87. package/bin/commands/mark-files-as-viewed.d.mts +26 -0
  88. package/bin/commands/mark-files-as-viewed.mjs +1 -0
  89. package/bin/commands/poll.d.mts +10 -0
  90. package/bin/commands/poll.mjs +1 -0
  91. package/bin/commands/ready-delay.d.mts +29 -0
  92. package/bin/commands/ready-mergeability.d.mts +15 -0
  93. package/bin/commands/resolve-mutate.d.mts +4 -0
  94. package/bin/commands/resolve-mutate.mjs +1 -0
  95. package/bin/commands/resolve.d.mts +4 -0
  96. package/bin/commands/shepherd-journal.d.mts +7 -0
  97. package/bin/commands/shepherd-journal.mjs +2 -2
  98. package/bin/comments/authors.d.mts +14 -0
  99. package/bin/comments/marker.d.mts +2 -0
  100. package/bin/comments/minimize-policy.d.mts +4 -0
  101. package/bin/comments/pending-ops.d.mts +15 -0
  102. package/bin/comments/rate-limit.d.mts +18 -0
  103. package/bin/comments/resolve.d.mts +34 -0
  104. package/bin/comments/resolve.mjs +1 -0
  105. package/bin/comments/review-thread-markers.d.mts +8 -0
  106. package/bin/comments/review-visibility.d.mts +28 -0
  107. package/bin/comments/sha-poll.d.mts +2 -0
  108. package/bin/comments/thread-visibility.d.mts +11 -0
  109. package/bin/comments/visible-comments.d.mts +11 -0
  110. package/bin/config/load.d.mts +60 -0
  111. package/bin/config/load.mjs +72 -1
  112. package/bin/config.json +0 -2
  113. package/bin/execution-context.d.mts +9 -0
  114. package/bin/execution-context.mjs +19 -0
  115. package/bin/exit-codes.d.mts +51 -0
  116. package/bin/github/activity.d.mts +3 -0
  117. package/bin/github/activity.mjs +7 -0
  118. package/bin/github/batch-parser-helpers.d.mts +22 -0
  119. package/bin/github/batch-parsers.d.mts +3 -0
  120. package/bin/github/batch-parsers.mjs +6 -0
  121. package/bin/github/batch-raw-types.d.mts +207 -0
  122. package/bin/github/batch-response.d.mts +4 -0
  123. package/bin/github/batch.d.mts +22 -0
  124. package/bin/github/branch-protection.d.mts +3 -0
  125. package/bin/github/check-annotations.d.mts +2 -0
  126. package/bin/github/client.d.mts +46 -0
  127. package/bin/github/client.mjs +7 -2
  128. package/bin/github/errors.d.mts +25 -0
  129. package/bin/github/gql/batch-pr.gql +5 -0
  130. package/bin/github/gql/review-thread-comments.gql +1 -0
  131. package/bin/github/graphql-http.d.mts +19 -0
  132. package/bin/github/graphql-response.d.mts +7 -0
  133. package/bin/github/http-auth.d.mts +4 -0
  134. package/bin/github/http-request.d.mts +7 -0
  135. package/bin/github/http-utils.d.mts +11 -0
  136. package/bin/github/http.d.mts +5 -0
  137. package/bin/github/pagination.d.mts +45 -0
  138. package/bin/github/queries.d.mts +24 -0
  139. package/bin/github/rest-http.d.mts +2 -0
  140. package/bin/github/thread-comments.d.mts +2 -0
  141. package/bin/index.d.mts +10 -0
  142. package/bin/index.mjs +1 -1
  143. package/bin/log/log-file.d.mts +28 -0
  144. package/bin/log/session.d.mts +31 -0
  145. package/bin/log/setup.d.mts +6 -0
  146. package/bin/mcp/index.d.mts +5 -0
  147. package/bin/mcp/index.mjs +8 -0
  148. package/bin/mcp/server.d.mts +8 -0
  149. package/bin/mcp/server.mjs +157 -0
  150. package/bin/mcp-stdio.d.mts +2 -0
  151. package/bin/mcp-stdio.mjs +7 -0
  152. package/bin/merge-status/derive.d.mts +19 -0
  153. package/bin/reporters/agent.d.mts +23 -0
  154. package/bin/reporters/agent.mjs +3 -0
  155. package/bin/state/base.d.mts +1 -0
  156. package/bin/state/bot-cr-seen.d.mts +51 -0
  157. package/bin/state/bot-cr-seen.mjs +1 -1
  158. package/bin/state/fix-attempts.d.mts +27 -0
  159. package/bin/state/iterate-stall.d.mts +27 -0
  160. package/bin/state/seen-comments.d.mts +62 -0
  161. package/bin/suggestions/extract.d.mts +8 -0
  162. package/bin/suggestions/parse.d.mts +48 -0
  163. package/bin/suggestions/patch.d.mts +14 -0
  164. package/bin/threads/transcript.d.mts +14 -0
  165. package/bin/threads/transcript.mjs +4 -0
  166. package/bin/types/activity.d.mts +30 -0
  167. package/bin/types/agent-thread.d.mts +9 -0
  168. package/bin/types/check-annotations.d.mts +14 -0
  169. package/bin/types/check-classification.d.mts +19 -0
  170. package/bin/types/github.d.mts +139 -0
  171. package/bin/types/iterate.d.mts +157 -0
  172. package/bin/types/protected-run.d.mts +6 -0
  173. package/bin/types/report.d.mts +176 -0
  174. package/bin/types/review-thread.d.mts +12 -0
  175. package/bin/types.d.mts +9 -0
  176. package/bin/util/markdown.d.mts +1 -0
  177. package/bin/util/path-segment.d.mts +2 -0
  178. package/bin/util/sleep.d.mts +1 -0
  179. package/bin/util/worktree.d.mts +9 -0
  180. package/bin/util/worktree.mjs +4 -1
  181. package/package.json +51 -37
  182. package/plugins/pr-shepherd/.codex-plugin/plugin.json +3 -2
  183. package/plugins/pr-shepherd/.codex.mcp.json +8 -0
  184. package/plugins/pr-shepherd/.mcp.json +6 -0
  185. package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +5 -19
  186. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +6 -15
  187. package/src/classify/types.mts +12 -0
@@ -1,6 +1,7 @@
1
1
  import { execFile as execFileCb } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
3
  import { rest } from "../../github/http.mjs";
4
+ import { getExecutionCwd } from "../../execution-context.mjs";
4
5
  const execFile = promisify(execFileCb);
5
6
  export { buildAutoCancelRunIdsWithOptions, buildInProgressRunIds, buildRunProtection, } from "./reruns.mjs";
6
7
  export function buildSummary(report) {
@@ -114,7 +115,9 @@ export async function tryCancelRun(runId, owner, repo) {
114
115
  }
115
116
  export async function getCurrentHeadSha() {
116
117
  try {
117
- const { stdout } = await execFile("git", ["rev-parse", "HEAD"]);
118
+ const { stdout } = await execFile("git", ["rev-parse", "HEAD"], {
119
+ cwd: getExecutionCwd(),
120
+ });
118
121
  return stdout.trim();
119
122
  }
120
123
  catch {
@@ -0,0 +1,2 @@
1
+ import type { IterateCommandOptions, IterateResult } from "../../types.mts";
2
+ export declare function runIterate(opts: IterateCommandOptions): Promise<IterateResult>;
@@ -25,7 +25,10 @@ export async function runIterate(opts) {
25
25
  const report = await runCheck({
26
26
  ...opts,
27
27
  prNumber,
28
- autoResolve: config.actions.autoResolveOutdated,
28
+ // Outdated Shepherd-visible threads are always resolved by iterate. The
29
+ // old actions.autoResolveOutdated switch is retained only as an ignored
30
+ // compatibility key in the config loader.
31
+ autoResolve: true,
29
32
  autoMinimizeSuppressed: config.actions.autoMinimizeSuppressed,
30
33
  });
31
34
  const [repoOwner, repoName] = report.repo.split("/");
@@ -48,7 +51,7 @@ export async function runIterate(opts) {
48
51
  // GitHub can still return a null/error/rate-limit result per ID without
49
52
  // throwing (autoMinimizeComments reports this via `errors`, not a rejection);
50
53
  // any ID it did not confirm minimized falls back into the agent-facing set so
51
- // the resolve command remains a working fallback instead of silently dropping it.
54
+ // the apply command remains a working fallback instead of silently dropping it.
52
55
  let reviewSummaryIds = minimizeIds;
53
56
  if (selfMinimizeIds.length > 0) {
54
57
  const { minimized } = await autoMinimizeComments(selfMinimizeIds);
@@ -0,0 +1,5 @@
1
+ import type { AgentThread, AgentComment, AgentCheck, Review, ResolveCommand, FirstLookThread, FirstLookComment, ReviewThread } from "../../types.mts";
2
+ /** Render a resolve command as a shell snippet. Appends `--require-sha "$HEAD_SHA"` when set. */
3
+ export declare function renderResolveCommand(rc: ResolveCommand): string;
4
+ export declare function buildFixInstructions(threads: AgentThread[], actionableComments: AgentComment[], checks: AgentCheck[], changesRequestedReviews: Review[], baseBranch: string, resolveCommand: ResolveCommand, hasConflicts: boolean, prNumber: number, cancelledCount: number, firstLookThreads?: FirstLookThread[], firstLookComments?: FirstLookComment[], firstLookSummaries?: Review[], editedSummaries?: Review[], inProgressRunIds?: string[], resolutionOnlyThreads?: ReviewThread[], resolveOnlyCommand?: ResolveCommand, behindBaseHint?: string, // iterate.behindBaseHint — see buildBehindBaseHintInstruction
5
+ isBehind?: boolean): string[];
@@ -32,14 +32,16 @@ isBehind = false) {
32
32
  if (changesRequestedReviews.length > 0)
33
33
  actionableSections.push("`## Changes-requested reviews`");
34
34
  const sectionRef = actionableSections.length > 0 ? `under ${actionableSections.join(", ")}` : "above";
35
- const resolveClause = resolveCommand.hasMutations ? ", then run the `resolve:` command" : "";
35
+ const resolveClause = resolveCommand.hasMutations
36
+ ? ", then run the `apply review:` command"
37
+ : "";
36
38
  if (hasConflicts) {
37
39
  // Conflicts make push mandatory regardless of whether code edits are needed.
38
40
  instructions.push(`The branch has merge conflicts that must be resolved before merging (see \`**branch**\` above). Apply any code edits for items ${sectionRef}, then commit and push${resolveClause}.`);
39
41
  }
40
42
  else {
41
43
  const skipClause = resolveCommand.hasMutations
42
- ? "skip the commit/push and run the `resolve:` command"
44
+ ? "skip the commit/push and run the `apply review:` command"
43
45
  : "no push is needed";
44
46
  instructions.push(`Decide for each item ${sectionRef} whether a code change is warranted. **If any code changes are needed:** apply edits, commit, push${resolveClause}. **If no code changes are needed:** ${skipClause}.`);
45
47
  }
@@ -59,12 +61,12 @@ isBehind = false) {
59
61
  // is only accurate when threads are present.
60
62
  const filesRef = threads.length > 0 ? "each file referenced above" : "the relevant files";
61
63
  const suggestionFallback = hasSuggestions
62
- ? ` When applying a \`[suggestion]\` thread manually (e.g. after a failed \`commit-suggestion\` run), replace the exact line range shown in the heading (\`path:startLine-endLine\`) with the replacement shown in its \`Replaces lines …\` block verbatim — an empty replacement deletes those lines, a single blank line replaces the range with one blank line.`
64
+ ? ` When applying a \`[suggestion]\` thread manually (e.g. after a failed \`build-suggestion-patch\` run), replace the exact line range shown in the heading (\`path:startLine-endLine\`) with the replacement shown in its \`Replaces lines …\` block verbatim — an empty replacement deletes those lines, a single blank line replaces the range with one blank line.`
63
65
  : "";
64
66
  instructions.push(`Apply code fixes: read and edit ${filesRef}.${suggestionFallback}`);
65
67
  }
66
68
  if (resolutionOnlyThreads.length > 0) {
67
- instructions.push(`Review the threads under \`## Review threads to resolve\`. Human-authored threads are replied to by the \`resolve:\` command shown below; Shepherd does not resolve them. Bot/non-human threads are included in \`--resolve-thread-ids\`.`);
69
+ instructions.push(`Review the threads under \`## Review threads to resolve\`. Human-authored threads are replied to by the \`apply review:\` command shown below; Shepherd does not resolve them. Bot/non-human threads are included in \`--resolve-thread-ids\`.`);
68
70
  }
69
71
  instructions.push(...buildFailingCheckInstructions(checks));
70
72
  if (checks.some((c) => (c.annotations?.length ?? 0) > 0)) {
@@ -74,7 +76,7 @@ isBehind = false) {
74
76
  const staleClause = buildCrStaleClause(changesRequestedReviews);
75
77
  instructions.push(`For each bullet under \`## Changes-requested reviews\` above: read the review body and apply the requested changes.${staleClause}`);
76
78
  if ((resolveCommand.dismissReviewIds?.length ?? 0) > 0)
77
- instructions.push(`Pass every ID listed in \`--dismiss-review-ids\` to the \`resolve:\` command verbatim — these are bot/non-human CR reviews that the agent (not the author) must dismiss. Dropping an ID leaves the PR in \`CHANGES_REQUESTED\` state; the next tick re-surfaces it as \`[pending dismissal]\` and an unattended bot CR escalates after \`iterate.stallTimeoutMinutes\`.`);
79
+ instructions.push(`Pass every ID listed in \`--dismiss-review-ids\` to the \`apply review:\` command verbatim — these are bot/non-human CR reviews that the agent (not the author) must dismiss. Dropping an ID leaves the PR in \`CHANGES_REQUESTED\` state; the next tick re-surfaces it as \`[pending dismissal]\` and an unattended bot CR escalates after \`iterate.stallTimeoutMinutes\`.`);
78
80
  }
79
81
  if (resolveOnlyCommand?.hasMutations)
80
82
  instructions.push(`Run the \`resolve-only:\` command shown above — no substitutions needed.`);
@@ -84,7 +86,7 @@ isBehind = false) {
84
86
  }
85
87
  const firstLookTotal = firstLookThreads.length + firstLookComments.length;
86
88
  if (firstLookTotal > 0) {
87
- instructions.push(`Items in \`## First-look items\` are shown so you can acknowledge their current status before acting. If a first-look thread also appears under \`## Review threads to resolve\`, its ID is already included in the \`resolve:\` command; otherwise do not pass first-look-only IDs to mutation flags.`);
89
+ instructions.push(`Items in \`## First-look items\` are shown so you can acknowledge their current status before acting. If a first-look thread also appears under \`## Review threads to resolve\`, its ID is already included in the \`apply review:\` command; otherwise do not pass first-look-only IDs to mutation flags.`);
88
90
  }
89
91
  if (firstLookSummaries.length > 0)
90
92
  instructions.push(SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE);
@@ -0,0 +1,20 @@
1
+ import type { ShepherdReport } from "../../types.mts";
2
+ import type { ProtectedRun } from "../../types/protected-run.mts";
3
+ type FailingCheck = ShepherdReport["checks"]["failing"][number];
4
+ type InProgressCheck = ShepherdReport["checks"]["inProgress"][number];
5
+ type CancellableCheck = FailingCheck | InProgressCheck;
6
+ interface RunProtection {
7
+ protectedRunIds: Set<string>;
8
+ protectedRuns: ProtectedRun[];
9
+ }
10
+ interface BuildRunIdOptions {
11
+ protectedRunIds?: Set<string>;
12
+ }
13
+ export declare function buildAutoCancelRunIdsWithOptions(report: ShepherdReport, opts?: BuildRunIdOptions): string[];
14
+ interface BuildInProgressRunIdOptions {
15
+ suppressProtectedFreshReruns?: boolean;
16
+ protectedRunIds?: Set<string>;
17
+ }
18
+ export declare function buildInProgressRunIds(report: ShepherdReport, cancelledSet: Set<string>, opts?: BuildInProgressRunIdOptions): string[];
19
+ export declare function buildRunProtection(checks: CancellableCheck[], patterns?: string[]): RunProtection;
20
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { IterateResult, IterateResultBase, ShepherdReport } from "../../types.mts";
2
+ export declare function applyStallGuard(stallKey: {
3
+ owner: string;
4
+ repo: string;
5
+ pr: number;
6
+ }, stallTimeoutSeconds: number, headSha: string, base: IterateResultBase, prNumber: number, prospectiveResult: IterateResult, report: ShepherdReport, reviewSummaryIds: string[]): Promise<IterateResult>;
@@ -0,0 +1,14 @@
1
+ export interface RunJournalOptions {
2
+ prNumber: number | undefined;
3
+ rawItem: string;
4
+ dryRun: boolean;
5
+ }
6
+ export interface JournalResult {
7
+ prNumber: number;
8
+ mutated: boolean;
9
+ sectionExisted: boolean;
10
+ dryRun: boolean;
11
+ previewBody?: string;
12
+ }
13
+ /** @deprecated Hidden implementation for standalone `journal`; use `apply journal`. */
14
+ export declare function runJournal(opts: RunJournalOptions): Promise<JournalResult>;
@@ -1,5 +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
4
  export async function runJournal(opts) {
4
5
  const validation = validateJournalItem(opts.rawItem);
5
6
  if (!validation.ok) {
@@ -0,0 +1,22 @@
1
+ export interface AppendResult {
2
+ body: string;
3
+ mutated: boolean;
4
+ sectionExisted: boolean;
5
+ }
6
+ type ValidationOk = {
7
+ ok: true;
8
+ item: string;
9
+ };
10
+ type ValidationError = {
11
+ ok: false;
12
+ error: string;
13
+ };
14
+ export type ValidationResult = ValidationOk | ValidationError;
15
+ /** Validates that the input is a properly formed markdown list item. */
16
+ export declare function validateJournalItem(input: string): ValidationResult;
17
+ /**
18
+ * Appends a validated list item to the ## Shepherd Journal section of a PR body.
19
+ * Creates the section at the end if absent. Skips if the exact item is already present (idempotent).
20
+ */
21
+ export declare function appendJournalItem(body: string, item: string): AppendResult;
22
+ export {};
@@ -0,0 +1,5 @@
1
+ interface LogFileResult {
2
+ path: string;
3
+ }
4
+ export declare function runLogFile(): Promise<LogFileResult>;
5
+ export {};
@@ -0,0 +1,26 @@
1
+ import { type ResolveRateLimitStop } from "../comments/rate-limit.mts";
2
+ import type { GlobalOptions } from "../types.mts";
3
+ export interface MarkFilesAsViewedOptions extends GlobalOptions {
4
+ prNumber?: number;
5
+ files: string[];
6
+ tests?: boolean;
7
+ matchPatterns?: string[];
8
+ }
9
+ export interface MarkFilesAsViewedResult {
10
+ repo: string;
11
+ prNumber: number;
12
+ pullRequestId: string;
13
+ requestedPaths: string[];
14
+ testSelector: boolean;
15
+ matchPatterns: string[];
16
+ matchedPaths: string[];
17
+ markedPaths: string[];
18
+ alreadyViewedPaths: string[];
19
+ missingPaths: string[];
20
+ unmatchedSelectors: string[];
21
+ errors: string[];
22
+ rateLimit?: ResolveRateLimitStop;
23
+ unmarkedPaths?: string[];
24
+ }
25
+ /** @deprecated Hidden implementation for `mark-files-as-viewed`; use `apply files`. */
26
+ export declare function runMarkFilesAsViewed(opts: MarkFilesAsViewedOptions): Promise<MarkFilesAsViewedResult>;
@@ -23,6 +23,7 @@ const FILES_QUERY = `query PullRequestFiles($owner: String!, $repo: String!, $pr
23
23
  }`;
24
24
  const TEST_FILE_RE = /(^|\/)(tests?|__tests__|spec)(\/|$)|\.(test|spec)\.[cm]?[jt]sx?$|_tests?\.rs$|(^|\/)tests?\.rs$/i;
25
25
  const BULK_CHUNK_SIZE = 10;
26
+ /** @deprecated Hidden implementation for `mark-files-as-viewed`; use `apply files`. */
26
27
  export async function runMarkFilesAsViewed(opts) {
27
28
  const repo = await getRepoInfo();
28
29
  const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
@@ -0,0 +1,10 @@
1
+ import type { IterateCommandOptions, IterateResult } from "../types.mts";
2
+ interface PollCommandOptions extends IterateCommandOptions {
3
+ intervalSeconds: number;
4
+ timeoutSeconds: number;
5
+ quietStatus?: boolean;
6
+ untilTerminal?: boolean;
7
+ }
8
+ /** @deprecated Hidden implementation for the legacy `poll` alias. */
9
+ export declare function runPoll(opts: PollCommandOptions): Promise<IterateResult>;
10
+ export {};
@@ -54,6 +54,7 @@ function writeWaitProgress(opts) {
54
54
  }
55
55
  return signature;
56
56
  }
57
+ /** @deprecated Hidden implementation for the legacy `poll` alias. */
57
58
  export async function runPoll(opts) {
58
59
  const { intervalSeconds, timeoutSeconds, quietStatus: quietStatusOpt, untilTerminal: untilTerminalOpt, ...iterateOpts } = opts;
59
60
  const intervalMs = Math.min(intervalSeconds * 1000, MAX_TIMER_MS);
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Ready-delay state machine for the shepherd iterate loop.
3
+ *
4
+ * When all READY conditions hold, shepherd writes a `ready-since.txt` marker
5
+ * to the state dir. The loop continues until the PR has been READY for
6
+ * `readyDelaySeconds` consecutively. Any not-READY result resets the timer.
7
+ */
8
+ interface ReadyDelayState {
9
+ isReady: boolean;
10
+ /**
11
+ * When true, the loop should cancel itself — the PR has been READY for
12
+ * longer than the configured ready-delay.
13
+ */
14
+ shouldCancel: boolean;
15
+ /** How many seconds remain in the ready-delay. */
16
+ remainingSeconds: number;
17
+ }
18
+ /**
19
+ * Update the ready-delay state machine and return the current decision.
20
+ *
21
+ * Call this at the end of each sweep iteration:
22
+ * - If `isReady == true`: start or continue the ready timer.
23
+ * - If `isReady == false`: reset the timer.
24
+ *
25
+ * When `shouldCancel == true`, the formatter tells loop-capable agents to cancel
26
+ * the loop and tells one-shot agents to stop.
27
+ */
28
+ export declare function updateReadyDelay(prNumber: number, isReady: boolean, readyDelaySeconds: number, owner: string, repo: string): Promise<ReadyDelayState>;
29
+ export {};
@@ -0,0 +1,15 @@
1
+ import { type RepoInfo } from "../github/client.mts";
2
+ import type { CiVerdict } from "../checks/classify.mts";
3
+ import type { BatchPrData, MergeStatusResult, ShepherdStatus } from "../types.mts";
4
+ interface ReadyMergeabilityRefresh {
5
+ batchData: BatchPrData;
6
+ mergeStatus: MergeStatusResult;
7
+ status: ShepherdStatus;
8
+ }
9
+ export declare function refreshUnknownMergeability(prNumber: number, repo: RepoInfo, batchData: BatchPrData): Promise<{
10
+ batchData: BatchPrData;
11
+ didRefresh: boolean;
12
+ }>;
13
+ export declare function refreshReadyMergeability(prNumber: number, repo: RepoInfo, batchData: BatchPrData, verdict: CiVerdict, unresolvedThreads: number, unresolvedComments: number, changesRequestedCount: number): Promise<ReadyMergeabilityRefresh>;
14
+ export declare function isBlockedByFilteredCheck(mergeStatus: MergeStatusResult, verdict: CiVerdict): boolean;
15
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { ResolveOptions } from "../types.mts";
2
+ import type { ResolveCommandOptions } from "./resolve.mts";
3
+ /** @deprecated Hidden implementation for `resolve`; use `apply review`. */
4
+ export declare function runResolveMutate(opts: ResolveCommandOptions & ResolveOptions): Promise<import("../comments/resolve.mts").ResolveResult>;
@@ -7,6 +7,7 @@ import { markReplySeen } from "../state/seen-comments.mjs";
7
7
  import { threadTranscriptBody } from "../threads/transcript.mjs";
8
8
  import { addPrShepherdMarker } from "../comments/marker.mjs";
9
9
  import { EXIT, ShepherdError } from "../exit-codes.mjs";
10
+ /** @deprecated Hidden implementation for `resolve`; use `apply review`. */
10
11
  export async function runResolveMutate(opts) {
11
12
  const repo = await getRepoInfo();
12
13
  const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
@@ -0,0 +1,4 @@
1
+ import type { GlobalOptions } from "../types.mts";
2
+ export { runResolveMutate } from "./resolve-mutate.mts";
3
+ export interface ResolveCommandOptions extends GlobalOptions {
4
+ }
@@ -0,0 +1,7 @@
1
+ export declare const SHEPHERD_JOURNAL_SECTION = "## Shepherd Journal";
2
+ export declare const SHEPHERD_JOURNAL_SECTION_PATTERN: RegExp;
3
+ export declare const SHEPHERD_JOURNAL_APPEND_HINT = "If this section already exists, append your entries under it instead of creating a duplicate heading.";
4
+ export declare const SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE = "Review the bodies shown under `## Review summaries (first look)` \u2014 you are seeing these for the first time. Eligible non-human IDs, when present, are already included in `--minimize-comment-ids` in the `apply review:` or `resolve-only:` command above; if any warrants a Shepherd Journal note, append it before applying review mutations.";
5
+ export declare function buildShepherdJournalInstruction(prNumber: number, itemReferenceGuidance: string): string;
6
+ export declare const SHEPHERD_JOURNAL_REFERENCE_GUIDANCE_THREADS_AND_COMMENTS_IN_ITEM_HEADINGS = "For threads and comments, use the markdown link shown in its heading above; for reviews, reference the review ID.";
7
+ export declare const SHEPHERD_JOURNAL_REFERENCE_GUIDANCE_THREADS_AND_COMMENTS_IN_ITEMS = "For threads and comments, use the markdown link shown in each item's bullet above; for reviews, reference the review ID.";
@@ -1,10 +1,10 @@
1
1
  export const SHEPHERD_JOURNAL_SECTION = "## Shepherd Journal";
2
2
  export const SHEPHERD_JOURNAL_SECTION_PATTERN = /^##\s+Shepherd\s+Journal$/;
3
3
  export const SHEPHERD_JOURNAL_APPEND_HINT = "If this section already exists, append your entries under it instead of creating a duplicate heading.";
4
- export const SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE = "Review the bodies shown under `## Review summaries (first look)` — you are seeing these for the first time. Eligible non-human IDs, when present, are already included in `--minimize-comment-ids` in the resolve or resolve-only command above; if any warrants a Shepherd Journal note, append it before running resolve.";
4
+ export const SHEPHERD_JOURNAL_FIRST_LOOK_GUIDANCE = "Review the bodies shown under `## Review summaries (first look)` — you are seeing these for the first time. Eligible non-human IDs, when present, are already included in `--minimize-comment-ids` in the `apply review:` or `resolve-only:` command above; if any warrants a Shepherd Journal note, append it before applying review mutations.";
5
5
  export function buildShepherdJournalInstruction(prNumber, itemReferenceGuidance) {
6
6
  return [
7
- `For any large decisions or rejections you made this iteration, run \`pr-shepherd journal ${prNumber} '- <decision>'\` to append an entry to the \`${SHEPHERD_JOURNAL_SECTION}\` section.`,
7
+ `For any large decisions or rejections you made this iteration, run \`pr-shepherd apply journal ${prNumber} '- <decision>'\` to append an entry to the \`${SHEPHERD_JOURNAL_SECTION}\` section.`,
8
8
  itemReferenceGuidance,
9
9
  `The command is idempotent — re-running with the same text is a no-op.`,
10
10
  ].join(" ");
@@ -0,0 +1,14 @@
1
+ import type { AuthorType } from "../types.mts";
2
+ export type NormalizedBotUsernames = ReadonlySet<string>;
3
+ export declare function normalizeBotUsernames(botUsernames?: readonly string[] | undefined | null): NormalizedBotUsernames;
4
+ export declare function normalizeAuthorType(typeName: string | undefined | null, login: string | undefined | null): AuthorType;
5
+ export declare function isHumanAuthor(author: {
6
+ author?: string;
7
+ login?: string;
8
+ authorType?: AuthorType;
9
+ }): boolean;
10
+ export declare function isConfiguredBotAuthor(author: {
11
+ author?: string;
12
+ login?: string;
13
+ authorType?: AuthorType;
14
+ }, botUsernames?: NormalizedBotUsernames): boolean;
@@ -0,0 +1,2 @@
1
+ export declare function hasPrShepherdMarker(body: string): boolean;
2
+ export declare function addPrShepherdMarker(body: string): string;
@@ -0,0 +1,4 @@
1
+ import type { MinimizeCommentsPolicy } from "../config/load.mts";
2
+ import type { AuthorType } from "../types.mts";
3
+ import { type NormalizedBotUsernames } from "./authors.mts";
4
+ export declare function shouldMinimizeAuthor(authorType: AuthorType | undefined, policy: MinimizeCommentsPolicy | undefined, author?: string, botUsernames?: NormalizedBotUsernames): boolean;
@@ -0,0 +1,15 @@
1
+ import type { ResolveResult } from "./resolve.mts";
2
+ export type ResolveMutationOp = {
3
+ kind: "p";
4
+ id: string;
5
+ } | {
6
+ kind: "r";
7
+ id: string;
8
+ } | {
9
+ kind: "m";
10
+ id: string;
11
+ } | {
12
+ kind: "d";
13
+ id: string;
14
+ };
15
+ export declare function setPendingOps(result: ResolveResult, ops: ResolveMutationOp[]): void;
@@ -0,0 +1,18 @@
1
+ export interface ResolveRateLimitStop {
2
+ message: string;
3
+ retryAfterSeconds?: number;
4
+ limit?: number;
5
+ remaining?: number;
6
+ resetAt?: number;
7
+ }
8
+ export declare function rateLimitFromError(err: unknown, fallbackMessage: string): ResolveRateLimitStop | null;
9
+ export declare function rateLimitFromGraphQlResult(messages: string[], meta: {
10
+ rateLimit?: {
11
+ remaining?: unknown;
12
+ limit?: unknown;
13
+ resetAt?: unknown;
14
+ };
15
+ retryAfterSeconds?: unknown;
16
+ stopOnZeroRemaining?: boolean;
17
+ }): ResolveRateLimitStop | undefined;
18
+ export declare function isRateLimitMessage(message: string): boolean;
@@ -0,0 +1,34 @@
1
+ import { type RepoInfo } from "../github/client.mts";
2
+ import type { ResolveOptions } from "../types.mts";
3
+ import { type ResolveRateLimitStop } from "./rate-limit.mts";
4
+ export interface ResolveResult {
5
+ repliedThreads: string[];
6
+ resolvedThreads: string[];
7
+ minimizedComments: string[];
8
+ dismissedReviews: string[];
9
+ errors: string[];
10
+ skippedDismissals?: string[];
11
+ skippedHumanResolves?: string[];
12
+ skippedHumanMinimizes?: string[];
13
+ skippedHumanDismissals?: string[];
14
+ skippedNonHumanReplies?: string[];
15
+ rateLimit?: ResolveRateLimitStop;
16
+ unrepliedThreads?: string[];
17
+ unresolvedThreads?: string[];
18
+ unminimizedComments?: string[];
19
+ undismissedReviews?: string[];
20
+ }
21
+ export declare function applyResolveOptions(pr: number, repo: RepoInfo, opts: ResolveOptions): Promise<ResolveResult>;
22
+ /** @deprecated Compatibility alias; use `autoResolveThreads`. */
23
+ export declare function autoResolveOutdated(threadIds: string[]): Promise<{
24
+ resolved: string[];
25
+ errors: string[];
26
+ }>;
27
+ export declare function autoResolveThreads(threadIds: string[]): Promise<{
28
+ resolved: string[];
29
+ errors: string[];
30
+ }>;
31
+ export declare function autoMinimizeComments(minimizeIds: string[]): Promise<{
32
+ minimized: string[];
33
+ errors: string[];
34
+ }>;
@@ -56,6 +56,7 @@ export async function applyResolveOptions(pr, repo, opts) {
56
56
  await bulkApply(replyThreadIds, resolveThreadIds, minimizeCommentIds, filteredDismissReviewIds, opts.dismissMessage ?? "", result);
57
57
  return result;
58
58
  }
59
+ /** @deprecated Compatibility alias; use `autoResolveThreads`. */
59
60
  export async function autoResolveOutdated(threadIds) {
60
61
  return autoResolveThreads(threadIds);
61
62
  }
@@ -0,0 +1,8 @@
1
+ import type { ReviewThread } from "../types.mts";
2
+ interface StateKey {
3
+ owner: string;
4
+ repo: string;
5
+ pr: number;
6
+ }
7
+ export declare function markReviewInlineThreadMarkers(key: StateKey, threads: readonly ReviewThread[]): Promise<void>;
8
+ export {};
@@ -0,0 +1,28 @@
1
+ import { type SeenMarker } from "../state/seen-comments.mts";
2
+ import { type NormalizedBotUsernames } from "./authors.mts";
3
+ import type { Review } from "../types.mts";
4
+ interface ReviewVisibility {
5
+ visible: Review[];
6
+ toMarkSeen: Review[];
7
+ }
8
+ export declare function classifyReviewsForDisplay(reviews: Review[], seenMap: Map<string, SeenMarker>): ReviewVisibility;
9
+ /**
10
+ * Bot CHANGES_REQUESTED reviews are different from every other surfaceable
11
+ * item: the agent — not the author — is responsible for dismissing them via
12
+ * `--dismiss-review-ids` after a fix push. If the agent forgets, the review
13
+ * stays in `CHANGES_REQUESTED` state and silently blocks the PR.
14
+ *
15
+ * The standard seen-gate (`classifyReviewsForDisplay`) suppresses items with
16
+ * unchanged bodies, which would also drop the bot CR ID from the
17
+ * `--dismiss-review-ids` flag — making the bug irrecoverable. This function
18
+ * keeps every bot CR in the visible set on every tick, using the seen-map
19
+ * only to pick the render form:
20
+ *
21
+ * - `new` / `edited` → full bullet (caller renders body normally).
22
+ * - `unchanged` → flagged `staleBotCr: true` so the formatter emits a terse
23
+ * one-line reminder.
24
+ *
25
+ * Human-authored CR reviews continue to flow through the standard seen-gate.
26
+ */
27
+ export declare function classifyChangesRequestedReviewsForDisplay(reviews: Review[], seenMap: Map<string, SeenMarker>, botUsernames: NormalizedBotUsernames): ReviewVisibility;
28
+ export {};
@@ -0,0 +1,2 @@
1
+ import { type RepoInfo } from "../github/client.mts";
2
+ export declare function waitForSha(pr: number, repo: RepoInfo, expectedSha: string): Promise<void>;
@@ -0,0 +1,11 @@
1
+ import { type SeenMarker } from "../state/seen-comments.mts";
2
+ import { type NormalizedBotUsernames } from "./authors.mts";
3
+ import type { FirstLookThread, ReviewThread } from "../types.mts";
4
+ interface ThreadVisibility {
5
+ activeThreads: ReviewThread[];
6
+ resolutionOnlyThreads: ReviewThread[];
7
+ firstLookThreads: FirstLookThread[];
8
+ toMarkSeen: ReviewThread[];
9
+ }
10
+ export declare function classifyThreadVisibility(threads: ReviewThread[], seenMap: Map<string, SeenMarker>, botUsernames?: NormalizedBotUsernames): ThreadVisibility;
11
+ export {};
@@ -0,0 +1,11 @@
1
+ import { type SeenMarker } from "../state/seen-comments.mts";
2
+ import type { MinimizeCommentsPolicy } from "../config/load.mts";
3
+ import type { ActionableComment, PrComment } from "../types.mts";
4
+ import type { NormalizedBotUsernames } from "./authors.mts";
5
+ interface VisibleCommentClassification {
6
+ actionable: ActionableComment[];
7
+ minimizeIds: string[];
8
+ toMarkSeen: ActionableComment[];
9
+ }
10
+ export declare function classifyVisibleComments(comments: PrComment[], seenMap: Map<string, SeenMarker>, minimizeComments: MinimizeCommentsPolicy | undefined, botUsernames?: NormalizedBotUsernames): VisibleCommentClassification;
11
+ export {};
@@ -0,0 +1,60 @@
1
+ declare const MINIMIZE_COMMENTS_POLICIES: readonly ["all", "bots", "users", "none"];
2
+ export type MinimizeCommentsPolicy = (typeof MINIMIZE_COMMENTS_POLICIES)[number];
3
+ interface PrShepherdConfig {
4
+ /** Optional user classification configuration; preserved for rule consumers. */
5
+ classify?: unknown;
6
+ /** GitHub logins that should be treated as bots even when GitHub reports User/Unknown. */
7
+ botUsernames: string[];
8
+ /** Case-insensitive glob patterns for check/status context names Shepherd should ignore. */
9
+ ignoreChecks: string[];
10
+ iterate: {
11
+ fixAttemptsPerThread: number;
12
+ stallTimeoutMinutes: number;
13
+ /**
14
+ * When `true`, APPROVED-state reviews are also eligible for minimization — defaults to `false`
15
+ * so approvals stay visible. `minimizeComments` still filters by GitHub author type.
16
+ */
17
+ minimizeApprovals: boolean;
18
+ /**
19
+ * Which GitHub author classes should be auto-minimized for minimizable PR comments and review
20
+ * summaries. Items excluded by this policy are still surfaced once (and after edits) via seen
21
+ * markers so they do not repeat forever.
22
+ */
23
+ minimizeComments: MinimizeCommentsPolicy;
24
+ /**
25
+ * One-liner hint appended to the `fix_code` push instruction when the branch is behind its
26
+ * base — e.g. "rebase --force-with-lease" or "see .agents/skills/git-and-prs.md". Empty
27
+ * (default) omits the hint entirely; the CLI never prescribes rebase/merge mechanics itself.
28
+ */
29
+ behindBaseHint: string;
30
+ };
31
+ watch: {
32
+ readyDelayMinutes: number;
33
+ };
34
+ resolve: {
35
+ shaPoll: {
36
+ intervalMs: number;
37
+ maxAttempts: number;
38
+ };
39
+ };
40
+ checks: {
41
+ ciTriggerEvents: string[];
42
+ };
43
+ mergeStatus: {
44
+ blockingReviewerLogins: string[];
45
+ };
46
+ actions: {
47
+ autoMinimizeSuppressed: boolean;
48
+ autoMarkReady: boolean;
49
+ /** Case-insensitive glob patterns for workflow/check names Shepherd must not cancel. */
50
+ neverCancelRuns: string[];
51
+ /** @deprecated Accepted for compatibility, ignored by the loader. */
52
+ autoResolveOutdated?: boolean;
53
+ /** @deprecated Accepted for compatibility, ignored by the loader. */
54
+ commitSuggestions?: boolean;
55
+ };
56
+ }
57
+ export declare function loadConfig(): PrShepherdConfig;
58
+ /** Reset the config cache — for use in tests that change directories. */
59
+ export declare function _resetConfigCache(): void;
60
+ export {};