pr-shepherd 0.34.0 → 0.36.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 (231) hide show
  1. package/.claude-plugin/plugin.json +3 -2
  2. package/.grok-plugin/marketplace.json +17 -0
  3. package/README.md +80 -43
  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/checks/triage.mjs +11 -3
  11. package/bin/classify/apply.d.mts +18 -0
  12. package/bin/classify/apply.mjs +4 -0
  13. package/bin/classify/loader.d.mts +10 -0
  14. package/bin/classify/types.d.mts +35 -0
  15. package/bin/cli/args.d.mts +18 -0
  16. package/bin/cli/args.mjs +1 -0
  17. package/bin/cli/clean-formatter.d.mts +2 -0
  18. package/bin/cli/default-poll.d.mts +2 -0
  19. package/bin/cli/default-poll.mjs +1 -0
  20. package/bin/cli/duration-flag.d.mts +2 -0
  21. package/bin/cli/duration.d.mts +13 -0
  22. package/bin/cli/fence.d.mts +1 -0
  23. package/bin/cli/fix-formatter-extra.d.mts +3 -0
  24. package/bin/cli/fix-formatter.d.mts +2 -0
  25. package/bin/cli/fix-formatter.mjs +6 -6
  26. package/bin/cli/formatters.d.mts +7 -0
  27. package/bin/cli/handlers.d.mts +4 -0
  28. package/bin/cli/handlers.mjs +11 -10
  29. package/bin/cli/help-command-pages.d.mts +231 -0
  30. package/bin/cli/help-command-pages.mjs +106 -1
  31. package/bin/cli/help-iterate-poll-pages.d.mts +4 -0
  32. package/bin/cli/help-iterate-poll-pages.mjs +14 -7
  33. package/bin/cli/help-log-file-page.d.mts +1 -0
  34. package/bin/cli/help-top-page.d.mts +1 -0
  35. package/bin/cli/help-top-page.mjs +16 -16
  36. package/bin/cli/help.d.mts +236 -0
  37. package/bin/cli/help.mjs +17 -0
  38. package/bin/cli/iterate-emitter.d.mts +8 -0
  39. package/bin/cli/iterate-flags.d.mts +11 -0
  40. package/bin/cli/iterate-formatter.d.mts +19 -0
  41. package/bin/cli/iterate-formatter.mjs +11 -2
  42. package/bin/cli/iterate-instructions.d.mts +6 -0
  43. package/bin/cli/iterate-lean.d.mts +12 -0
  44. package/bin/cli/iterate-lean.mjs +1 -0
  45. package/bin/cli/journal-formatter.d.mts +2 -0
  46. package/bin/cli/journal-formatter.mjs +15 -0
  47. package/bin/cli/journal-handler.d.mts +1 -0
  48. package/bin/cli/journal-handler.mjs +7 -20
  49. package/bin/cli/list-formatters.d.mts +76 -0
  50. package/bin/cli/list-formatters.mjs +9 -9
  51. package/bin/cli/mark-files-as-viewed-flags.d.mts +11 -0
  52. package/bin/cli/mark-files-as-viewed-formatter.d.mts +2 -0
  53. package/bin/cli/mutate-formatter.d.mts +2 -0
  54. package/bin/cli/poll-handler.d.mts +1 -0
  55. package/bin/cli/poll-handler.mjs +9 -2
  56. package/bin/cli/resolve-validators.d.mts +3 -0
  57. package/bin/cli/resolve-validators.mjs +3 -3
  58. package/bin/cli/runner.d.mts +7 -0
  59. package/bin/cli/suggestion-renderer.d.mts +3 -0
  60. package/bin/cli/validate-default-args.d.mts +6 -0
  61. package/bin/cli-parser.d.mts +2 -0
  62. package/bin/cli-parser.mjs +72 -16
  63. package/bin/commands/check-annotations.d.mts +5 -0
  64. package/bin/commands/check-status.d.mts +3 -0
  65. package/bin/commands/check-terminal-report.d.mts +5 -0
  66. package/bin/commands/check.d.mts +7 -0
  67. package/bin/commands/check.mjs +28 -23
  68. package/bin/commands/clean.d.mts +21 -0
  69. package/bin/commands/commit-suggestion-instruction.d.mts +8 -0
  70. package/bin/commands/commit-suggestion-instruction.mjs +3 -3
  71. package/bin/commands/commit-suggestion.d.mts +8 -0
  72. package/bin/commands/commit-suggestion.mjs +20 -13
  73. package/bin/commands/iterate/check-instructions.d.mts +16 -0
  74. package/bin/commands/iterate/check-instructions.mjs +3 -3
  75. package/bin/commands/iterate/classify.d.mts +18 -0
  76. package/bin/commands/iterate/classify.mjs +4 -4
  77. package/bin/commands/iterate/escalate.d.mts +31 -0
  78. package/bin/commands/iterate/escalate.mjs +7 -4
  79. package/bin/commands/iterate/fix-code.d.mts +25 -0
  80. package/bin/commands/iterate/fix-code.mjs +1 -1
  81. package/bin/commands/iterate/helpers.d.mts +14 -0
  82. package/bin/commands/iterate/helpers.mjs +19 -7
  83. package/bin/commands/iterate/index.d.mts +2 -0
  84. package/bin/commands/iterate/index.mjs +8 -12
  85. package/bin/commands/iterate/render.d.mts +5 -0
  86. package/bin/commands/iterate/render.mjs +8 -6
  87. package/bin/commands/iterate/reruns.d.mts +20 -0
  88. package/bin/commands/iterate/stall.d.mts +6 -0
  89. package/bin/commands/journal/index.d.mts +14 -0
  90. package/bin/commands/journal/index.mjs +1 -0
  91. package/bin/commands/journal/transform.d.mts +22 -0
  92. package/bin/commands/log-file.d.mts +5 -0
  93. package/bin/commands/mark-files-as-viewed.d.mts +26 -0
  94. package/bin/commands/mark-files-as-viewed.mjs +1 -0
  95. package/bin/commands/poll.d.mts +12 -0
  96. package/bin/commands/poll.mjs +52 -17
  97. package/bin/commands/ready-delay.d.mts +29 -0
  98. package/bin/commands/ready-delay.mjs +3 -13
  99. package/bin/commands/ready-mergeability.d.mts +15 -0
  100. package/bin/commands/resolve-mutate.d.mts +4 -0
  101. package/bin/commands/resolve-mutate.mjs +1 -0
  102. package/bin/commands/resolve.d.mts +4 -0
  103. package/bin/commands/shepherd-journal.d.mts +7 -0
  104. package/bin/commands/shepherd-journal.mjs +2 -2
  105. package/bin/comments/authors.d.mts +14 -0
  106. package/bin/comments/marker.d.mts +2 -0
  107. package/bin/comments/minimize-policy.d.mts +4 -0
  108. package/bin/comments/pending-ops.d.mts +15 -0
  109. package/bin/comments/rate-limit.d.mts +18 -0
  110. package/bin/comments/resolve.d.mts +34 -0
  111. package/bin/comments/resolve.mjs +1 -0
  112. package/bin/comments/review-thread-markers.d.mts +8 -0
  113. package/bin/comments/review-visibility.d.mts +28 -0
  114. package/bin/comments/sha-poll.d.mts +2 -0
  115. package/bin/comments/thread-visibility.d.mts +11 -0
  116. package/bin/comments/visible-comments.d.mts +11 -0
  117. package/bin/config/load.d.mts +60 -0
  118. package/bin/config/load.mjs +129 -16
  119. package/bin/config.json +0 -2
  120. package/bin/execution-context.d.mts +9 -0
  121. package/bin/execution-context.mjs +19 -0
  122. package/bin/exit-codes.d.mts +51 -0
  123. package/bin/github/activity.d.mts +3 -0
  124. package/bin/github/activity.mjs +7 -0
  125. package/bin/github/batch-page-helpers.d.mts +45 -0
  126. package/bin/github/batch-page-helpers.mjs +63 -0
  127. package/bin/github/batch-page.d.mts +14 -0
  128. package/bin/github/batch-page.mjs +62 -0
  129. package/bin/github/batch-parse-suites.d.mts +4 -0
  130. package/bin/github/batch-parse-suites.mjs +25 -0
  131. package/bin/github/batch-parser-helpers.d.mts +22 -0
  132. package/bin/github/batch-parsers-rules.d.mts +6 -0
  133. package/bin/github/batch-parsers-rules.mjs +122 -0
  134. package/bin/github/batch-parsers.d.mts +3 -0
  135. package/bin/github/batch-parsers.mjs +12 -0
  136. package/bin/github/batch-raw-rules.d.mts +59 -0
  137. package/bin/github/batch-raw-rules.mjs +1 -0
  138. package/bin/github/batch-raw-types.d.mts +216 -0
  139. package/bin/github/batch-response.d.mts +4 -0
  140. package/bin/github/batch.d.mts +24 -0
  141. package/bin/github/batch.mjs +14 -120
  142. package/bin/github/branch-protection.d.mts +3 -0
  143. package/bin/github/check-annotations.d.mts +2 -0
  144. package/bin/github/client.d.mts +46 -0
  145. package/bin/github/client.mjs +7 -2
  146. package/bin/github/errors.d.mts +25 -0
  147. package/bin/github/gql/batch-pr-page.gql +189 -0
  148. package/bin/github/gql/batch-pr.gql +79 -21
  149. package/bin/github/gql/commit-suggestion-thread.gql +40 -0
  150. package/bin/github/gql/review-thread-comments.gql +1 -0
  151. package/bin/github/graphql-http.d.mts +19 -0
  152. package/bin/github/graphql-response.d.mts +7 -0
  153. package/bin/github/http-auth.d.mts +4 -0
  154. package/bin/github/http-request.d.mts +7 -0
  155. package/bin/github/http-utils.d.mts +11 -0
  156. package/bin/github/http.d.mts +6 -0
  157. package/bin/github/http.mjs +2 -1
  158. package/bin/github/pagination.d.mts +46 -0
  159. package/bin/github/pagination.mjs +3 -2
  160. package/bin/github/queries.d.mts +28 -0
  161. package/bin/github/queries.mjs +4 -0
  162. package/bin/github/rest-http.d.mts +7 -0
  163. package/bin/github/rest-http.mjs +25 -86
  164. package/bin/github/rest-text.d.mts +1 -0
  165. package/bin/github/rest-text.mjs +88 -0
  166. package/bin/github/suggestion-thread.d.mts +9 -0
  167. package/bin/github/suggestion-thread.mjs +45 -0
  168. package/bin/github/thread-comments.d.mts +2 -0
  169. package/bin/github/thread-comments.mjs +12 -8
  170. package/bin/index.d.mts +10 -0
  171. package/bin/index.mjs +1 -1
  172. package/bin/log/log-file.d.mts +28 -0
  173. package/bin/log/session.d.mts +31 -0
  174. package/bin/log/setup.d.mts +6 -0
  175. package/bin/mcp/index.d.mts +5 -0
  176. package/bin/mcp/index.mjs +8 -0
  177. package/bin/mcp/server.d.mts +8 -0
  178. package/bin/mcp/server.mjs +157 -0
  179. package/bin/mcp-stdio.d.mts +2 -0
  180. package/bin/mcp-stdio.mjs +7 -0
  181. package/bin/merge-status/derive.d.mts +19 -0
  182. package/bin/merge-status/derive.mjs +2 -0
  183. package/bin/merge-status/requirements-format.d.mts +3 -0
  184. package/bin/merge-status/requirements-format.mjs +88 -0
  185. package/bin/merge-status/requirements.d.mts +2 -0
  186. package/bin/merge-status/requirements.mjs +51 -0
  187. package/bin/reporters/agent.d.mts +23 -0
  188. package/bin/reporters/agent.mjs +3 -0
  189. package/bin/state/base.d.mts +10 -0
  190. package/bin/state/base.mjs +23 -0
  191. package/bin/state/bot-cr-seen.d.mts +51 -0
  192. package/bin/state/bot-cr-seen.mjs +4 -14
  193. package/bin/state/fix-attempts.d.mts +27 -0
  194. package/bin/state/fix-attempts.mjs +3 -13
  195. package/bin/state/iterate-stall.d.mts +27 -0
  196. package/bin/state/iterate-stall.mjs +3 -13
  197. package/bin/state/seen-comments.d.mts +62 -0
  198. package/bin/state/seen-comments.mjs +6 -13
  199. package/bin/suggestions/extract.d.mts +8 -0
  200. package/bin/suggestions/parse.d.mts +48 -0
  201. package/bin/suggestions/patch.d.mts +14 -0
  202. package/bin/threads/transcript.d.mts +14 -0
  203. package/bin/threads/transcript.mjs +4 -0
  204. package/bin/types/activity.d.mts +30 -0
  205. package/bin/types/agent-thread.d.mts +9 -0
  206. package/bin/types/check-annotations.d.mts +14 -0
  207. package/bin/types/check-classification.d.mts +19 -0
  208. package/bin/types/github.d.mts +136 -0
  209. package/bin/types/iterate.d.mts +156 -0
  210. package/bin/types/merge-requirements.d.mts +82 -0
  211. package/bin/types/merge-requirements.mjs +2 -0
  212. package/bin/types/protected-run.d.mts +6 -0
  213. package/bin/types/report.d.mts +176 -0
  214. package/bin/types/review-thread.d.mts +12 -0
  215. package/bin/types.d.mts +10 -0
  216. package/bin/types.mjs +1 -0
  217. package/bin/util/markdown.d.mts +1 -0
  218. package/bin/util/path-segment.d.mts +4 -0
  219. package/bin/util/path-segment.mjs +2 -0
  220. package/bin/util/pool.d.mts +2 -0
  221. package/bin/util/pool.mjs +18 -0
  222. package/bin/util/sleep.d.mts +1 -0
  223. package/bin/util/worktree.d.mts +9 -0
  224. package/bin/util/worktree.mjs +4 -1
  225. package/package.json +51 -37
  226. package/plugins/pr-shepherd/.codex-plugin/plugin.json +3 -2
  227. package/plugins/pr-shepherd/.codex.mcp.json +8 -0
  228. package/plugins/pr-shepherd/.mcp.json +6 -0
  229. package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +5 -19
  230. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +6 -15
  231. package/src/classify/types.mts +12 -0
@@ -0,0 +1,15 @@
1
+ export function formatJournalResult(result) {
2
+ if (result.dryRun) {
3
+ const lines = ["Dry run — no body change written."];
4
+ if (result.previewBody !== undefined) {
5
+ lines.push("", result.previewBody);
6
+ }
7
+ return lines.join("\n");
8
+ }
9
+ if (!result.mutated)
10
+ return "No change — entry already present.";
11
+ if (!result.sectionExisted) {
12
+ return `Created ## Shepherd Journal section in PR #${result.prNumber}.`;
13
+ }
14
+ return `Appended to ## Shepherd Journal in PR #${result.prNumber}.`;
15
+ }
@@ -0,0 +1 @@
1
+ export declare function handleJournal(args: string[], command?: "apply journal" | "journal"): Promise<void>;
@@ -3,7 +3,8 @@ import { EXIT, errorToExitCode } from "../exit-codes.mjs";
3
3
  import { runJournal } from "../commands/journal/index.mjs";
4
4
  import { getFlag, parsePrNumber } from "./args.mjs";
5
5
  import { USAGE } from "./help.mjs";
6
- export async function handleJournal(args) {
6
+ import { formatJournalResult } from "./journal-formatter.mjs";
7
+ export async function handleJournal(args, command = "apply journal") {
7
8
  for (const a of args) {
8
9
  if (!a.startsWith("--"))
9
10
  continue;
@@ -11,14 +12,14 @@ export async function handleJournal(args) {
11
12
  continue;
12
13
  if (a === "--file" || a.startsWith("--file="))
13
14
  continue;
14
- process.stderr.write(`pr-shepherd: journal: unknown flag: "${a}"\n`);
15
+ process.stderr.write(`pr-shepherd: ${command}: unknown flag: "${a}"\n`);
15
16
  process.exitCode = EXIT.USAGE;
16
17
  return;
17
18
  }
18
19
  const { prNumber, extra } = parseJournalArgs(args);
19
20
  const filePath = getFlag(args, "--file");
20
21
  if (filePath !== null && extra[0]) {
21
- process.stderr.write(`pr-shepherd: journal: provide the entry as a positional argument or via --file, not both\n`);
22
+ process.stderr.write(`pr-shepherd: ${command}: provide the entry as a positional argument or via --file, not both\n`);
22
23
  process.exitCode = EXIT.USAGE;
23
24
  return;
24
25
  }
@@ -27,12 +28,12 @@ export async function handleJournal(args) {
27
28
  rawItem = filePath !== null ? await readItemSource(filePath) : extra[0];
28
29
  }
29
30
  catch (e) {
30
- process.stderr.write(`pr-shepherd: journal: ${String(e)}\n`);
31
+ process.stderr.write(`pr-shepherd: ${command}: ${String(e)}\n`);
31
32
  process.exitCode = EXIT.NOINPUT;
32
33
  return;
33
34
  }
34
35
  if (rawItem === undefined) {
35
- process.stderr.write(`${USAGE.journal}\n`);
36
+ process.stderr.write(`${USAGE[command]}\n`);
36
37
  process.exitCode = EXIT.USAGE;
37
38
  return;
38
39
  }
@@ -49,7 +50,7 @@ export async function handleJournal(args) {
49
50
  }
50
51
  }
51
52
  catch (e) {
52
- process.stderr.write(`pr-shepherd: journal: ${String(e)}\n`);
53
+ process.stderr.write(`pr-shepherd: ${command}: ${String(e)}\n`);
53
54
  process.exitCode = errorToExitCode(e);
54
55
  }
55
56
  }
@@ -96,17 +97,3 @@ function parseJournalArgs(args) {
96
97
  }
97
98
  return { prNumber, extra };
98
99
  }
99
- function formatJournalResult(result) {
100
- if (result.dryRun) {
101
- const lines = ["Dry run — no body change written."];
102
- if (result.previewBody !== undefined) {
103
- lines.push("", result.previewBody);
104
- }
105
- return lines.join("\n");
106
- }
107
- if (!result.mutated)
108
- return "No change — entry already present.";
109
- if (!result.sectionExisted)
110
- return `Created ## Shepherd Journal section in PR #${result.prNumber}.`;
111
- return `Appended to ## Shepherd Journal in PR #${result.prNumber}.`;
112
- }
@@ -0,0 +1,76 @@
1
+ import type { AuthorType, CommentAuthorAssociation, SuggestionBlock } from "../types.mts";
2
+ import type { FirstLookThread, FirstLookComment } from "../types/report.mts";
3
+ export declare function renderAuthor(author: string, authorType?: AuthorType, authorAssociation?: CommentAuthorAssociation): string;
4
+ export declare function renderBodyPreview(body: string): string;
5
+ export declare function renderThreadResolutionStatusTag(t: {
6
+ isOutdated?: boolean;
7
+ isMinimized?: boolean;
8
+ }): string;
9
+ interface ThreadBulletInput {
10
+ id: string;
11
+ reviewId?: string;
12
+ url?: string;
13
+ path?: string | null;
14
+ startLine?: number | null;
15
+ line?: number | null;
16
+ author: string;
17
+ authorType?: AuthorType;
18
+ authorAssociation?: CommentAuthorAssociation;
19
+ body: string;
20
+ comments?: Array<{
21
+ id: string;
22
+ author: string;
23
+ authorType?: AuthorType;
24
+ authorAssociation?: CommentAuthorAssociation;
25
+ body: string;
26
+ url: string;
27
+ }>;
28
+ suggestion?: SuggestionBlock;
29
+ edited?: boolean;
30
+ }
31
+ export declare function renderThreadBullet(t: ThreadBulletInput, opts?: {
32
+ statusTag?: string;
33
+ renderSuggestion?: boolean;
34
+ noBody?: boolean;
35
+ suppressEditedMarker?: boolean;
36
+ }): string;
37
+ export declare function renderThreadConversation(t: ThreadBulletInput): string;
38
+ export declare function blockquote(body: string): string;
39
+ export declare function renderCommentBullet(c: {
40
+ id: string;
41
+ url?: string;
42
+ author: string;
43
+ authorType?: AuthorType;
44
+ authorAssociation?: CommentAuthorAssociation;
45
+ body: string;
46
+ }, opts?: {
47
+ statusTag?: string;
48
+ }): string;
49
+ export declare function renderEditedCommentTag(c: {
50
+ edited?: boolean;
51
+ }): string | undefined;
52
+ export declare function renderReviewBullet(r: {
53
+ id: string;
54
+ author: string;
55
+ authorType?: AuthorType;
56
+ authorAssociation?: CommentAuthorAssociation;
57
+ body?: string;
58
+ staleBotCr?: boolean;
59
+ staleReview?: boolean;
60
+ }, opts?: {
61
+ includeBody?: boolean;
62
+ }): string;
63
+ export declare function renderReviewListSection(heading: string, items: {
64
+ id: string;
65
+ author: string;
66
+ authorType?: AuthorType;
67
+ authorAssociation?: CommentAuthorAssociation;
68
+ body?: string;
69
+ }[]): string | null;
70
+ /**
71
+ * Build bullet strings for the `## First-look items` section.
72
+ * Threads that also appear in resolutionOnlyIds have their body suppressed
73
+ * (already shown in `## Review threads to resolve`).
74
+ */
75
+ export declare function buildFirstLookBullets(firstLookThreads: FirstLookThread[], resolutionOnlyIds: Set<string>, firstLookComments: FirstLookComment[]): string[];
76
+ export {};
@@ -1,8 +1,8 @@
1
1
  import { renderLineRange, renderSuggestionBlock } from "./suggestion-renderer.mjs";
2
2
  import { threadComments } from "../threads/transcript.mjs";
3
3
  const BODY_PREVIEW_MAX = 100;
4
- export function renderAuthor(author, authorType) {
5
- return authorType ? `@${author} · ${authorType}` : `@${author}`;
4
+ export function renderAuthor(author, authorType, authorAssociation) {
5
+ return [`@${author}`, authorType, authorAssociation].filter(Boolean).join(" · ");
6
6
  }
7
7
  export function renderBodyPreview(body) {
8
8
  const normalizedBody = body.replace(/\r\n?/g, "\n");
@@ -30,7 +30,7 @@ export function renderThreadBullet(t, opts = {}) {
30
30
  const editedMarker = t.edited && !opts.suppressEditedMarker ? " [edited since first look]" : "";
31
31
  const reviewMarker = t.reviewId ? ` [reviewId=${t.reviewId}]` : "";
32
32
  const statusSuffix = opts.statusTag ? ` ${opts.statusTag}` : "";
33
- const bulletLine = `- \`threadId=${t.id}\`${link} ${loc} (${renderAuthor(t.author, t.authorType)})${reviewMarker}${suggestionMarker}${editedMarker}${statusSuffix}`;
33
+ const bulletLine = `- \`threadId=${t.id}\`${link} ${loc} (${renderAuthor(t.author, t.authorType, t.authorAssociation)})${reviewMarker}${suggestionMarker}${editedMarker}${statusSuffix}`;
34
34
  if (!opts.noBody && (!t.comments || t.comments.length === 0)) {
35
35
  const legacyLine = `${bulletLine}: ${renderBodyPreview(t.body)}`;
36
36
  return t.suggestion && opts.renderSuggestion
@@ -53,9 +53,9 @@ export function renderThreadConversation(t) {
53
53
  .map((c) => {
54
54
  const heading = c.id
55
55
  ? c.url
56
- ? `#### [commentId=${c.id}](${c.url}) (${renderAuthor(c.author, c.authorType)})`
57
- : `#### \`commentId=${c.id}\` (${renderAuthor(c.author, c.authorType)})`
58
- : `#### (${renderAuthor(c.author, c.authorType)})`;
56
+ ? `#### [commentId=${c.id}](${c.url}) (${renderAuthor(c.author, c.authorType, c.authorAssociation)})`
57
+ : `#### \`commentId=${c.id}\` (${renderAuthor(c.author, c.authorType, c.authorAssociation)})`
58
+ : `#### (${renderAuthor(c.author, c.authorType, c.authorAssociation)})`;
59
59
  return `${heading}\n\n${blockquote(c.body)}`;
60
60
  })
61
61
  .join("\n\n");
@@ -73,7 +73,7 @@ function renderThreadCommentBullets(t) {
73
73
  const link = c.url ? ` [↗](${c.url})` : "";
74
74
  const id = c.id ? `\`commentId=${c.id}\`` : "comment";
75
75
  return [
76
- ` - ${id}${link} (${renderAuthor(c.author, c.authorType)})`,
76
+ ` - ${id}${link} (${renderAuthor(c.author, c.authorType, c.authorAssociation)})`,
77
77
  indentBlockquote(c.body, " "),
78
78
  ].join("\n");
79
79
  })
@@ -88,13 +88,13 @@ function indentBlockquote(body, indent) {
88
88
  export function renderCommentBullet(c, opts = {}) {
89
89
  const link = c.url ? ` [↗](${c.url})` : "";
90
90
  const statusSuffix = opts.statusTag ? ` ${opts.statusTag}` : "";
91
- return `- \`commentId=${c.id}\`${link} (${renderAuthor(c.author, c.authorType)})${statusSuffix}: ${renderBodyPreview(c.body)}`;
91
+ return `- \`commentId=${c.id}\`${link} (${renderAuthor(c.author, c.authorType, c.authorAssociation)})${statusSuffix}: ${renderBodyPreview(c.body)}`;
92
92
  }
93
93
  export function renderEditedCommentTag(c) {
94
94
  return c.edited ? "[edited since first look]" : undefined;
95
95
  }
96
96
  export function renderReviewBullet(r, opts = {}) {
97
- const base = `- \`reviewId=${r.id}\` (${renderAuthor(r.author, r.authorType)})`;
97
+ const base = `- \`reviewId=${r.id}\` (${renderAuthor(r.author, r.authorType, r.authorAssociation)})`;
98
98
  const staleTag = r.staleReview
99
99
  ? " [stale — review is on an old commit, all threads resolved]"
100
100
  : "";
@@ -0,0 +1,11 @@
1
+ type ParseMarkFilesAsViewedResult = {
2
+ ok: true;
3
+ files: string[];
4
+ tests: boolean;
5
+ matchPatterns: string[];
6
+ } | {
7
+ ok: false;
8
+ error: string;
9
+ };
10
+ export declare function parseMarkFilesAsViewedArgs(args: string[]): ParseMarkFilesAsViewedResult;
11
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { MarkFilesAsViewedResult } from "../commands/mark-files-as-viewed.mts";
2
+ export declare function formatMarkFilesAsViewedResult(result: MarkFilesAsViewedResult): string;
@@ -0,0 +1,2 @@
1
+ import type { ResolveResult } from "../comments/resolve.mts";
2
+ export declare function formatMutateResult(result: ResolveResult): string;
@@ -0,0 +1 @@
1
+ export declare function handlePoll(args: string[]): Promise<void>;
@@ -7,6 +7,7 @@ import { parseIterateFlags } from "./iterate-flags.mjs";
7
7
  import { emitIterateResult } from "./iterate-emitter.mjs";
8
8
  const DEFAULT_POLL_INTERVAL_SECONDS = 60;
9
9
  const DEFAULT_POLL_TIMEOUT_SECONDS = 270;
10
+ const DEFAULT_POLL_DEBOUNCE_SECONDS = 60;
10
11
  export async function handlePoll(args) {
11
12
  const { prNumber, global: globalOpts, extra } = parseCommonArgs(args);
12
13
  const cfg = loadConfig();
@@ -14,15 +15,20 @@ export async function handlePoll(args) {
14
15
  if (flags.readyDelaySuffix === null || flags.stallTimeoutSuffix === null)
15
16
  return;
16
17
  const intervalStr = getFlag(extra, "--interval");
17
- const intervalSuffix = validateSecondsDurationFlag("pr-shepherd poll", "--interval", intervalStr, hasFlag(extra, "--interval"));
18
+ const intervalSuffix = validateSecondsDurationFlag("pr-shepherd", "--interval", intervalStr, hasFlag(extra, "--interval"));
18
19
  if (intervalSuffix === null)
19
20
  return;
20
21
  const intervalSeconds = parseDurationToSeconds(intervalSuffix ?? "", DEFAULT_POLL_INTERVAL_SECONDS);
21
22
  const timeoutStr = getFlag(extra, "--timeout");
22
- const timeoutSuffix = validateSecondsDurationFlag("pr-shepherd poll", "--timeout", timeoutStr, hasFlag(extra, "--timeout"));
23
+ const timeoutSuffix = validateSecondsDurationFlag("pr-shepherd", "--timeout", timeoutStr, hasFlag(extra, "--timeout"));
23
24
  if (timeoutSuffix === null)
24
25
  return;
25
26
  const timeoutSeconds = parseDurationToSeconds(timeoutSuffix ?? "", DEFAULT_POLL_TIMEOUT_SECONDS);
27
+ const debounceStr = getFlag(extra, "--debounce");
28
+ const debounceSuffix = validateSecondsDurationFlag("pr-shepherd", "--debounce", debounceStr, hasFlag(extra, "--debounce"), { allowZero: true });
29
+ if (debounceSuffix === null)
30
+ return;
31
+ const debounceSeconds = parseDurationToSeconds(debounceSuffix ?? "", DEFAULT_POLL_DEBOUNCE_SECONDS, { allowZero: true });
26
32
  const result = await runPoll({
27
33
  ...globalOpts,
28
34
  prNumber,
@@ -32,6 +38,7 @@ export async function handlePoll(args) {
32
38
  noAutoCancelActionable: flags.noAutoCancelActionable,
33
39
  intervalSeconds,
34
40
  timeoutSeconds,
41
+ debounceSeconds,
35
42
  quietStatus: hasFlag(extra, "--quiet-status"),
36
43
  untilTerminal: hasFlag(extra, "--until-terminal"),
37
44
  });
@@ -0,0 +1,3 @@
1
+ export declare function rejectPrrcMinimizeIds(ids: string[]): string[];
2
+ export declare function warnPrrcThreadIds(ids: string[]): string[];
3
+ export declare function validateRequireSha(sha: string | undefined): boolean;
@@ -2,7 +2,7 @@ import { EXIT } from "../exit-codes.mjs";
2
2
  export function rejectPrrcMinimizeIds(ids) {
3
3
  const prrcIds = ids.filter((id) => id.startsWith("PRRC_"));
4
4
  if (prrcIds.length > 0) {
5
- process.stderr.write(`pr-shepherd: resolve: --minimize-comment-ids contains thread comment IDs (PRRC_*): ${prrcIds.join(", ")}. Thread comments cannot be minimized individually — resolve the parent thread using --resolve-thread-ids with the PRRT_* thread ID instead.\n`);
5
+ process.stderr.write(`pr-shepherd: apply review: --minimize-comment-ids contains thread comment IDs (PRRC_*): ${prrcIds.join(", ")}. Thread comments cannot be minimized individually — resolve the parent thread using --resolve-thread-ids with the PRRT_* thread ID instead.\n`);
6
6
  process.exitCode = EXIT.DATAERR;
7
7
  }
8
8
  return prrcIds;
@@ -10,7 +10,7 @@ export function rejectPrrcMinimizeIds(ids) {
10
10
  export function warnPrrcThreadIds(ids) {
11
11
  const prrcIds = ids.filter((id) => id.startsWith("PRRC_"));
12
12
  if (prrcIds.length > 0) {
13
- process.stderr.write(`pr-shepherd: resolve: warning: --resolve-thread-ids contains comment IDs (PRRC_*) instead of thread IDs (PRRT_*): ${prrcIds.join(", ")}. The resolveReviewThread mutation requires PRRT_* thread IDs. Run a GraphQL query for pullRequest.reviewThreads to get the correct IDs.\n`);
13
+ process.stderr.write(`pr-shepherd: apply review: warning: --resolve-thread-ids contains comment IDs (PRRC_*) instead of thread IDs (PRRT_*): ${prrcIds.join(", ")}. The resolveReviewThread mutation requires PRRT_* thread IDs. Run a GraphQL query for pullRequest.reviewThreads to get the correct IDs.\n`);
14
14
  }
15
15
  return prrcIds;
16
16
  }
@@ -19,7 +19,7 @@ export function validateRequireSha(sha) {
19
19
  return true;
20
20
  if (/^[0-9a-f]{40}$/.test(sha))
21
21
  return true;
22
- process.stderr.write(`pr-shepherd: resolve: --require-sha must be a full 40-character lowercase hex SHA, got "${sha}". Short SHAs will never match GitHub's headRefOid. Use $(git rev-parse HEAD) to get the full SHA.\n`);
22
+ process.stderr.write(`pr-shepherd: apply review: --require-sha must be a full 40-character lowercase hex SHA, got "${sha}". Short SHAs will never match GitHub's headRefOid. Use $(git rev-parse HEAD) to get the full SHA.\n`);
23
23
  process.exitCode = EXIT.DATAERR;
24
24
  return false;
25
25
  }
@@ -0,0 +1,7 @@
1
+ interface PrShepherdCommand {
2
+ argv: string[];
3
+ text: string;
4
+ }
5
+ export declare function buildPrShepherdCommand(args: string[]): PrShepherdCommand;
6
+ export declare function renderShellCommand(argv: string[]): string;
7
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { SuggestionBlock } from "../types.mts";
2
+ export declare function renderLineRange(startLine: number | undefined, endLine: number | null): string;
3
+ export declare function renderSuggestionBlock(s: SuggestionBlock, indent?: string): string;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Validate args for default-dispatch paths (e.g. `pr-shepherd [PR] [flags]`).
3
+ * Returns false when an unexpected token is found; calls onError with the
4
+ * offending arg so callers can print their own usage message.
5
+ */
6
+ export declare function validateDefaultArgs(args: string[], flagsWithValues: ReadonlySet<string>, booleanFlags: ReadonlySet<string>, onError: (arg: string) => void): boolean;
@@ -0,0 +1,2 @@
1
+ /** CLI argument parsing and subcommand dispatch for pr-shepherd. See --help for usage. */
2
+ export declare function main(argv: string[]): Promise<void>;
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-lines */
1
2
  /** CLI argument parsing and subcommand dispatch for pr-shepherd. See --help for usage. */
2
3
  import { readFileSync } from "node:fs";
3
4
  import { EXIT, errorToExitCode } from "./exit-codes.mjs";
@@ -5,7 +6,7 @@ import { runResolveMutate } from "./commands/resolve.mjs";
5
6
  import { runLogFile } from "./commands/log-file.mjs";
6
7
  import { parseCommonArgs, getFlag, hasFlag, parseList } from "./cli/args.mjs";
7
8
  import { isDefaultPollInvocation, validateDefaultPollArgs } from "./cli/default-poll.mjs";
8
- import { USAGE, maybePrintHelp } from "./cli/help.mjs";
9
+ import { USAGE, helpKeyForArgs, maybePrintHelp } from "./cli/help.mjs";
9
10
  import { formatMutateResult } from "./cli/formatters.mjs";
10
11
  import { handleClean, handleCommitSuggestion, handleIterate, handleMarkFilesAsViewed, } from "./cli/handlers.mjs";
11
12
  import { handleJournal } from "./cli/journal-handler.mjs";
@@ -31,19 +32,33 @@ export async function main(argv) {
31
32
  await handleLogFile(args.slice(1));
32
33
  return;
33
34
  }
35
+ // The public command groups keep admin diagnostics out of the normal
36
+ // command namespace. Keep this before setupLog because log-file reads the
37
+ // log path and must not initialize/tee the log it is reporting.
38
+ if (subcommand === "admin" && args[1] === "log-file") {
39
+ await handleLogFile(args.slice(2), "admin log-file");
40
+ return;
41
+ }
34
42
  // Short-circuit all --help/-h before any I/O or logging.
35
43
  if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
36
44
  if (isDefaultPollInvocation(subcommand)) {
37
- process.stdout.write(`${USAGE.poll}\n`);
45
+ process.stdout.write(`${USAGE.default}\n`);
38
46
  }
39
47
  else {
40
- const key = subcommand != null && subcommand in USAGE
41
- ? subcommand
42
- : "top";
43
- process.stdout.write(`${USAGE[key]}\n`);
48
+ process.stdout.write(`${USAGE[helpKeyForArgs(args)]}\n`);
44
49
  }
45
50
  return;
46
51
  }
52
+ const legacyReplacement = {
53
+ poll: "pr-shepherd [PR] (the default PR polling command)",
54
+ resolve: "apply review",
55
+ "mark-files-as-viewed": "apply files",
56
+ journal: "apply journal",
57
+ "commit-suggestion": "build-suggestion-patch",
58
+ };
59
+ if (subcommand !== undefined && legacyReplacement[subcommand] !== undefined) {
60
+ warnLegacyAlias(subcommand, legacyReplacement[subcommand]);
61
+ }
47
62
  // Initialize the per-worktree log and install a stdout tee.
48
63
  await setupLog(argv);
49
64
  if (isDefaultPollInvocation(subcommand)) {
@@ -53,14 +68,23 @@ export async function main(argv) {
53
68
  return;
54
69
  }
55
70
  switch (subcommand) {
71
+ case "apply":
72
+ await handleApply(args.slice(1));
73
+ break;
74
+ case "build-suggestion-patch":
75
+ await handleCommitSuggestion(args.slice(1));
76
+ break;
77
+ case "admin":
78
+ await handleAdmin(args.slice(1));
79
+ break;
56
80
  case "resolve":
57
- await handleResolve(args.slice(1));
81
+ await handleResolve(args.slice(1), "resolve");
58
82
  break;
59
83
  case "commit-suggestion":
60
- await handleCommitSuggestion(args.slice(1));
84
+ await handleCommitSuggestion(args.slice(1), "commit-suggestion");
61
85
  break;
62
86
  case "mark-files-as-viewed":
63
- await handleMarkFilesAsViewed(args.slice(1));
87
+ await handleMarkFilesAsViewed(args.slice(1), "mark-files-as-viewed");
64
88
  break;
65
89
  case "iterate":
66
90
  await handleIterate(args.slice(1));
@@ -69,10 +93,10 @@ export async function main(argv) {
69
93
  await handlePoll(args.slice(1));
70
94
  break;
71
95
  case "clean":
72
- await handleClean(args.slice(1));
96
+ await handleClean(args.slice(1), "clean");
73
97
  break;
74
98
  case "journal":
75
- await handleJournal(args.slice(1));
99
+ await handleJournal(args.slice(1), "journal");
76
100
  break;
77
101
  default:
78
102
  process.stderr.write(`Unknown subcommand: ${subcommand ?? "(none)"}\n`);
@@ -81,6 +105,38 @@ export async function main(argv) {
81
105
  return;
82
106
  }
83
107
  }
108
+ function warnLegacyAlias(alias, replacement) {
109
+ process.stderr.write(`pr-shepherd: ${alias} is deprecated; use ${replacement.startsWith("pr-shepherd") ? replacement : `pr-shepherd ${replacement}`} instead.\n`);
110
+ }
111
+ async function handleApply(args) {
112
+ const action = args[0];
113
+ switch (action) {
114
+ case "review":
115
+ await handleResolve(args.slice(1));
116
+ return;
117
+ case "files":
118
+ await handleMarkFilesAsViewed(args.slice(1));
119
+ return;
120
+ case "journal":
121
+ await handleJournal(args.slice(1));
122
+ return;
123
+ default:
124
+ process.stderr.write(`Unknown apply action: ${action ?? "(none)"}\n`);
125
+ process.stderr.write(`${USAGE.apply}\n`);
126
+ process.exitCode = EXIT.USAGE;
127
+ }
128
+ }
129
+ async function handleAdmin(args) {
130
+ switch (args[0]) {
131
+ case "clean":
132
+ await handleClean(args.slice(1));
133
+ return;
134
+ default:
135
+ process.stderr.write(`Unknown admin command: ${args[0] ?? "(none)"}\n`);
136
+ process.stderr.write(`${USAGE.admin}\n`);
137
+ process.exitCode = EXIT.USAGE;
138
+ }
139
+ }
84
140
  function readVersion() {
85
141
  const pkgUrl = new URL("../package.json", import.meta.url);
86
142
  const pkg = JSON.parse(readFileSync(pkgUrl, "utf8"));
@@ -89,8 +145,8 @@ function readVersion() {
89
145
  // ---------------------------------------------------------------------------
90
146
  // Subcommand handlers
91
147
  // ---------------------------------------------------------------------------
92
- async function handleLogFile(args) {
93
- if (maybePrintHelp(args, "log-file"))
148
+ async function handleLogFile(args, usageKey = "log-file") {
149
+ if (maybePrintHelp(args, usageKey))
94
150
  return;
95
151
  const jsonOut = args.some((a) => a === "--format=json") ||
96
152
  (() => {
@@ -106,7 +162,7 @@ async function handleLogFile(args) {
106
162
  process.exitCode = errorToExitCode(e);
107
163
  }
108
164
  }
109
- async function handleResolve(args) {
165
+ async function handleResolve(args, command = "apply review") {
110
166
  const { prNumber, global: globalOpts, extra } = parseCommonArgs(args);
111
167
  const resolveThreadIds = parseList(getFlag(extra, "--resolve-thread-ids"));
112
168
  const replyThreadIds = parseList(getFlag(extra, "--reply-thread-ids"));
@@ -120,7 +176,7 @@ async function handleResolve(args) {
120
176
  if (rejectPrrcMinimizeIds(minimizeCommentIds).length > 0)
121
177
  return;
122
178
  if (hasFlag(extra, "--fetch")) {
123
- process.stderr.write("pr-shepherd: resolve: --fetch has been removed; run pr-shepherd iterate or poll to fetch the next action.\n");
179
+ process.stderr.write(`pr-shepherd: ${command}: --fetch has been removed; run pr-shepherd iterate to fetch the next action.\n`);
124
180
  process.exitCode = EXIT.USAGE;
125
181
  return;
126
182
  }
@@ -129,7 +185,7 @@ async function handleResolve(args) {
129
185
  minimizeCommentIds.length > 0 ||
130
186
  dismissReviewIds.length > 0;
131
187
  if (!hasAction) {
132
- process.stderr.write("pr-shepherd: resolve: an action flag is required (--reply-thread-ids, --resolve-thread-ids, --minimize-comment-ids, or --dismiss-review-ids).\n");
188
+ process.stderr.write(`pr-shepherd: ${command}: an action flag is required (--reply-thread-ids, --resolve-thread-ids, --minimize-comment-ids, or --dismiss-review-ids).\n`);
133
189
  process.exitCode = EXIT.USAGE;
134
190
  return;
135
191
  }
@@ -0,0 +1,5 @@
1
+ import type { CheckAnnotation, TriagedCheck } from "../types.mts";
2
+ export declare function attachUnseenCheckAnnotations(checks: TriagedCheck[], seenMap: Map<string, {
3
+ seenAt: number;
4
+ }>, prNumber: number): Promise<TriagedCheck[]>;
5
+ export declare function annotationMarkerBody(a: CheckAnnotation): string;
@@ -0,0 +1,3 @@
1
+ import type { MergeStatusResult, ShepherdStatus } from "../types.mts";
2
+ import type { CiVerdict } from "../checks/classify.mts";
3
+ export declare function computeStatus(verdict: CiVerdict, unresolvedThreads: number, unresolvedComments: number, mergeStatus: MergeStatusResult, changesRequestedReviews: number): ShepherdStatus;
@@ -0,0 +1,5 @@
1
+ import type { BatchPrData, MergeStatusResult, ShepherdReport } from "../types.mts";
2
+ export declare function buildTerminalReport(prNumber: number, repo: {
3
+ owner: string;
4
+ name: string;
5
+ }, batchData: BatchPrData, mergeStatus: MergeStatusResult, status: "MERGED" | "CLOSED"): ShepherdReport;
@@ -0,0 +1,7 @@
1
+ import type { GlobalOptions, ShepherdReport } from "../types.mts";
2
+ export declare function runCheck(opts: GlobalOptions & {
3
+ autoResolve?: boolean;
4
+ autoMinimizeSuppressed?: boolean;
5
+ skipTriage?: boolean;
6
+ persistSeen?: boolean;
7
+ }): Promise<ShepherdReport>;
@@ -20,6 +20,7 @@ import { normalizeBotUsernames } from "../comments/authors.mjs";
20
20
  import { discoverRuleFiles, loadRules } from "../classify/loader.mjs";
21
21
  import { buildClassifyIndex, partitionBatch } from "../classify/apply.mjs";
22
22
  import { EXIT, ShepherdError } from "../exit-codes.mjs";
23
+ import { getEffectiveCwd } from "../execution-context.mjs";
23
24
  export async function runCheck(opts) {
24
25
  const repo = await getRepoInfo();
25
26
  const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
@@ -37,7 +38,9 @@ export async function runCheck(opts) {
37
38
  if (mergeStatus.state === "MERGED" || mergeStatus.state === "CLOSED") {
38
39
  return buildTerminalReport(prNumber, repo, batchData, mergeStatus, mergeStatus.state);
39
40
  }
40
- const startupFailureChecks = await fetchStartupFailureChecks(repo, batchData.headRefOid, prNumber);
41
+ const startupFailureChecks = result.checkSuitesComplete
42
+ ? []
43
+ : await fetchStartupFailureChecks(repo, batchData.headRefOid, prNumber);
41
44
  const allChecks = mergeStartupFailureChecks(batchData.checks, startupFailureChecks);
42
45
  const classifiedChecks = classifyChecks(allChecks);
43
46
  const verdict = getCiVerdict(classifiedChecks);
@@ -50,7 +53,7 @@ export async function runCheck(opts) {
50
53
  const stateKey = { owner: repo.owner, repo: repo.name, pr: prNumber };
51
54
  const seenMap = await loadSeenMap(stateKey);
52
55
  const botUsernames = normalizeBotUsernames(config.botUsernames);
53
- const ruleSet = await loadRules(discoverRuleFiles(process.cwd()));
56
+ const ruleSet = await loadRules(discoverRuleFiles(getEffectiveCwd()));
54
57
  const classifyIndex = buildClassifyIndex(ruleSet, batchData);
55
58
  const partition = partitionBatch(classifyIndex, batchData);
56
59
  const triaged = await attachUnseenCheckAnnotations(triagedBase, seenMap, prNumber);
@@ -79,27 +82,29 @@ export async function runCheck(opts) {
79
82
  }
80
83
  const changesRequestedReviewVisibility = classifyChangesRequestedReviewsForDisplay(batchData.changesRequestedReviews.filter((r) => !partition.suppressedChangesRequestedIds.has(r.id)), seenMap, botUsernames);
81
84
  const approvedReviewVisibility = classifyReviewsForDisplay(batchData.approvedReviews, seenMap);
82
- await Promise.allSettled([
83
- ...firstLookComments.map((c) => markSeen(stateKey, c.id, c.body)),
84
- ...threadVisibility.toMarkSeen.map((t) => markSeen(stateKey, t.id, threadTranscriptBody(t))),
85
- ...visibleCommentClassification.toMarkSeen.map((c) => markSeen(stateKey, c.id, c.body)),
86
- ...[...firstLookSummaries, ...editedSummaries].map((r) => markSeen(stateKey, r.id, r.body)),
87
- ...changesRequestedReviewVisibility.toMarkSeen.map((r) => markSeen(stateKey, r.id, r.body)),
88
- ...approvedReviewVisibility.toMarkSeen.map((r) => markSeen(stateKey, r.id, r.body)),
89
- ...batchData.comments
90
- .filter((c) => partition.suppressedCommentIds.has(c.id))
91
- .map((c) => markSeen(stateKey, c.id, c.body)),
92
- ...batchData.reviewThreads
93
- .filter((t) => partition.suppressedThreadIds.has(t.id))
94
- .map((t) => markSeen(stateKey, t.id, threadTranscriptBody(t))),
95
- ...batchData.reviewSummaries
96
- .filter((r) => partition.suppressedReviewSummaryIds.has(r.id))
97
- .map((r) => markSeen(stateKey, r.id, r.body)),
98
- ...batchData.changesRequestedReviews
99
- .filter((r) => partition.suppressedChangesRequestedIds.has(r.id))
100
- .map((r) => markSeen(stateKey, r.id, r.body)),
101
- ]);
102
- await markReviewInlineThreadMarkers(stateKey, batchData.reviewThreads);
85
+ if (opts.persistSeen !== false) {
86
+ await Promise.allSettled([
87
+ ...firstLookComments.map((c) => markSeen(stateKey, c.id, c.body)),
88
+ ...threadVisibility.toMarkSeen.map((t) => markSeen(stateKey, t.id, threadTranscriptBody(t))),
89
+ ...visibleCommentClassification.toMarkSeen.map((c) => markSeen(stateKey, c.id, c.body)),
90
+ ...[...firstLookSummaries, ...editedSummaries].map((r) => markSeen(stateKey, r.id, r.body)),
91
+ ...changesRequestedReviewVisibility.toMarkSeen.map((r) => markSeen(stateKey, r.id, r.body)),
92
+ ...approvedReviewVisibility.toMarkSeen.map((r) => markSeen(stateKey, r.id, r.body)),
93
+ ...batchData.comments
94
+ .filter((c) => partition.suppressedCommentIds.has(c.id))
95
+ .map((c) => markSeen(stateKey, c.id, c.body)),
96
+ ...batchData.reviewThreads
97
+ .filter((t) => partition.suppressedThreadIds.has(t.id))
98
+ .map((t) => markSeen(stateKey, t.id, threadTranscriptBody(t))),
99
+ ...batchData.reviewSummaries
100
+ .filter((r) => partition.suppressedReviewSummaryIds.has(r.id))
101
+ .map((r) => markSeen(stateKey, r.id, r.body)),
102
+ ...batchData.changesRequestedReviews
103
+ .filter((r) => partition.suppressedChangesRequestedIds.has(r.id))
104
+ .map((r) => markSeen(stateKey, r.id, r.body)),
105
+ ]);
106
+ await markReviewInlineThreadMarkers(stateKey, batchData.reviewThreads);
107
+ }
103
108
  const { threadIds: ruleAutoResolveThreadIds, commentIds: ruleAutoResolveCommentIds, reviewSummaryIds: ruleAutoResolveReviewSummaryIds, } = await remainingRuleAutoResolveIds(partition, opts.autoMinimizeSuppressed);
104
109
  const changesRequestedReviews = changesRequestedReviewVisibility.visible;
105
110
  const changesRequestedReviewCount = batchData.changesRequestedReviews.filter((r) => !partition.suppressedChangesRequestedIds.has(r.id)).length;
@@ -0,0 +1,21 @@
1
+ export type CleanVariant = "pr" | "branch" | "current" | "repo" | "all";
2
+ interface CleanOptions {
3
+ variant: CleanVariant;
4
+ /** PR number string (for "pr") or branch name (for "branch"). Defaults to current. */
5
+ value?: string;
6
+ dryRun?: boolean;
7
+ }
8
+ export interface CleanResult {
9
+ ok: boolean;
10
+ variant: CleanVariant;
11
+ dryRun: boolean;
12
+ base: string;
13
+ target: string;
14
+ /** Paths removed (actual delete) or that would be removed (dry-run). */
15
+ deleted: string[];
16
+ /** Target path when it did not exist. */
17
+ skipped: string[];
18
+ error?: string;
19
+ }
20
+ export declare function runClean(opts: CleanOptions): Promise<CleanResult>;
21
+ export {};