pr-shepherd 0.42.0 → 0.44.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 (145) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +24 -18
  3. package/bin/api.d.mts +3 -2
  4. package/bin/api.mjs +13 -18
  5. package/bin/checks/classify.d.mts +3 -1
  6. package/bin/checks/classify.mjs +5 -2
  7. package/bin/cli/args.mjs +14 -9
  8. package/bin/cli/default-poll.mjs +1 -0
  9. package/bin/cli/fix-formatter.mjs +20 -8
  10. package/bin/cli/handlers.mjs +1 -0
  11. package/bin/cli/help-command-pages.d.mts +9 -6
  12. package/bin/cli/help-command-pages.mjs +7 -4
  13. package/bin/cli/help-iterate-poll-pages.d.mts +2 -2
  14. package/bin/cli/help-iterate-poll-pages.mjs +9 -4
  15. package/bin/cli/help-top-page.d.mts +1 -1
  16. package/bin/cli/help-top-page.mjs +2 -2
  17. package/bin/cli/help.d.mts +10 -7
  18. package/bin/cli/iterate-flags.d.mts +1 -0
  19. package/bin/cli/iterate-flags.mjs +2 -0
  20. package/bin/cli/iterate-formatter.d.mts +1 -1
  21. package/bin/cli/iterate-formatter.mjs +9 -6
  22. package/bin/cli/iterate-instructions.mjs +15 -0
  23. package/bin/cli/iterate-lean.d.mts +1 -2
  24. package/bin/cli/iterate-lean.mjs +16 -17
  25. package/bin/cli/iterate-merge-formatter.d.mts +3 -0
  26. package/bin/cli/iterate-merge-formatter.mjs +52 -0
  27. package/bin/cli/iterate-verbose.d.mts +6 -0
  28. package/bin/cli/iterate-verbose.mjs +14 -0
  29. package/bin/cli/journal-formatter.mjs +3 -0
  30. package/bin/cli/journal-handler.mjs +11 -7
  31. package/bin/cli/list-formatters.d.mts +3 -1
  32. package/bin/cli/list-formatters.mjs +9 -7
  33. package/bin/cli/mark-files-as-viewed-formatter.mjs +7 -1
  34. package/bin/cli/mutate-formatter.mjs +4 -0
  35. package/bin/cli/poll-handler.mjs +1 -0
  36. package/bin/cli/runner.mjs +4 -3
  37. package/bin/commands/check-terminal-report.mjs +1 -0
  38. package/bin/commands/check.mjs +64 -7
  39. package/bin/commands/commit-suggestion-instruction.d.mts +1 -1
  40. package/bin/commands/commit-suggestion-instruction.mjs +2 -2
  41. package/bin/commands/commit-suggestion.mjs +3 -1
  42. package/bin/commands/iterate/base.d.mts +5 -0
  43. package/bin/commands/iterate/base.mjs +25 -0
  44. package/bin/commands/iterate/check-instructions.d.mts +5 -19
  45. package/bin/commands/iterate/check-instructions.mjs +24 -25
  46. package/bin/commands/iterate/classify.d.mts +2 -2
  47. package/bin/commands/iterate/classify.mjs +43 -25
  48. package/bin/commands/iterate/escalate.d.mts +3 -1
  49. package/bin/commands/iterate/escalate.mjs +29 -2
  50. package/bin/commands/iterate/fix-code.mjs +81 -26
  51. package/bin/commands/iterate/helpers.d.mts +0 -2
  52. package/bin/commands/iterate/helpers.mjs +6 -17
  53. package/bin/commands/iterate/index.mjs +31 -43
  54. package/bin/commands/iterate/mark-ready.d.mts +2 -0
  55. package/bin/commands/iterate/mark-ready.mjs +38 -0
  56. package/bin/commands/iterate/merge-state.d.mts +15 -0
  57. package/bin/commands/iterate/merge-state.mjs +82 -0
  58. package/bin/commands/iterate/merge.d.mts +13 -0
  59. package/bin/commands/iterate/merge.mjs +45 -0
  60. package/bin/commands/iterate/render.d.mts +2 -2
  61. package/bin/commands/iterate/render.mjs +19 -20
  62. package/bin/commands/iterate/stall.mjs +4 -2
  63. package/bin/commands/iterate/thread-mutation-routing.d.mts +9 -0
  64. package/bin/commands/iterate/thread-mutation-routing.mjs +29 -0
  65. package/bin/commands/journal/index.d.mts +5 -0
  66. package/bin/commands/journal/index.mjs +11 -2
  67. package/bin/commands/mark-files-as-viewed.d.mts +2 -1
  68. package/bin/commands/mark-files-as-viewed.mjs +5 -92
  69. package/bin/commands/poll.mjs +3 -1
  70. package/bin/commands/resolve-mutate.mjs +51 -9
  71. package/bin/commands/shepherd-journal.d.mts +1 -1
  72. package/bin/commands/shepherd-journal.mjs +2 -2
  73. package/bin/commands/suggestion-patch-item.d.mts +1 -1
  74. package/bin/commands/suggestion-patch-item.mjs +3 -11
  75. package/bin/commands/suggestion-patches.mjs +3 -3
  76. package/bin/comments/authors.d.mts +6 -0
  77. package/bin/comments/authors.mjs +5 -0
  78. package/bin/comments/marker.d.mts +6 -1
  79. package/bin/comments/marker.mjs +8 -1
  80. package/bin/comments/resolve.d.mts +4 -0
  81. package/bin/comments/thread-visibility.mjs +10 -10
  82. package/bin/comments/visible-comments.mjs +2 -1
  83. package/bin/config/load.d.mts +5 -1
  84. package/bin/config/load.mjs +34 -0
  85. package/bin/config/merge-command-args.d.mts +2 -0
  86. package/bin/config/merge-command-args.mjs +44 -0
  87. package/bin/config.json +3 -0
  88. package/bin/exit-codes.d.mts +2 -0
  89. package/bin/exit-codes.mjs +4 -0
  90. package/bin/github/batch-parse-checks.d.mts +3 -0
  91. package/bin/github/batch-parse-checks.mjs +29 -0
  92. package/bin/github/batch-parsers-rules.d.mts +3 -1
  93. package/bin/github/batch-parsers-rules.mjs +33 -0
  94. package/bin/github/batch-parsers.d.mts +2 -2
  95. package/bin/github/batch-parsers.mjs +40 -27
  96. package/bin/github/batch-raw-rules.d.mts +48 -0
  97. package/bin/github/batch-raw-types.d.mts +12 -0
  98. package/bin/github/batch-raw-types.mjs +0 -1
  99. package/bin/github/batch.mjs +3 -1
  100. package/bin/github/client.d.mts +1 -0
  101. package/bin/github/client.mjs +8 -1
  102. package/bin/github/gql/batch-pr-page.gql +6 -0
  103. package/bin/github/gql/batch-pr.gql +105 -0
  104. package/bin/github/gql/commit-check-contexts.gql +58 -0
  105. package/bin/github/gql/get-pr-body.gql +1 -0
  106. package/bin/github/gql/review-thread-comments.gql +1 -0
  107. package/bin/github/gql/suggestion-threads.gql +1 -0
  108. package/bin/github/merge-queue-checks.d.mts +4 -0
  109. package/bin/github/merge-queue-checks.mjs +48 -0
  110. package/bin/github/queries.d.mts +2 -0
  111. package/bin/github/queries.mjs +2 -0
  112. package/bin/github/suggestion-thread.mjs +1 -0
  113. package/bin/mcp/server.mjs +13 -6
  114. package/bin/pr-reference.d.mts +13 -0
  115. package/bin/pr-reference.mjs +23 -1
  116. package/bin/reporters/agent.mjs +11 -3
  117. package/bin/threads/transcript.d.mts +2 -0
  118. package/bin/threads/transcript.mjs +2 -0
  119. package/bin/types/activity.d.mts +2 -0
  120. package/bin/types/agent-thread.d.mts +1 -0
  121. package/bin/types/escalate.d.mts +35 -0
  122. package/bin/types/escalate.mjs +1 -0
  123. package/bin/types/github.d.mts +27 -0
  124. package/bin/types/github.mjs +0 -3
  125. package/bin/types/iterate.d.mts +23 -39
  126. package/bin/types/merge-action.d.mts +12 -0
  127. package/bin/types/merge-action.mjs +1 -0
  128. package/bin/types/merge-queue.d.mts +13 -0
  129. package/bin/types/merge-queue.mjs +1 -0
  130. package/bin/types/merge-requirements.d.mts +17 -0
  131. package/bin/types/report.d.mts +22 -1
  132. package/bin/types/review-thread.d.mts +1 -0
  133. package/bin/types/suggestion-patch.d.mts +1 -1
  134. package/bin/types.d.mts +3 -0
  135. package/bin/types.mjs +3 -0
  136. package/bin/util/markdown.d.mts +2 -0
  137. package/bin/util/markdown.mjs +7 -0
  138. package/package.json +1 -1
  139. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  140. package/plugins/pr-shepherd/.codex.mcp.json +1 -1
  141. package/plugins/pr-shepherd/.mcp.json +1 -1
  142. package/plugins/pr-shepherd/skills/mark-files-as-viewed/SKILL.md +3 -3
  143. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +17 -15
  144. package/bin/commands/iterate/reruns.d.mts +0 -20
  145. package/bin/commands/iterate/reruns.mjs +0 -105
@@ -0,0 +1,44 @@
1
+ const MERGE_STRATEGY_FLAGS = new Map([
2
+ ["--merge", "merge"],
3
+ ["-m", "merge"],
4
+ ["--squash", "squash"],
5
+ ["-s", "squash"],
6
+ ["--rebase", "rebase"],
7
+ ["-r", "rebase"],
8
+ ]);
9
+ /** Return active merge strategies while validating compound strategy syntax. */
10
+ export function findMergeStrategies(args) {
11
+ const strategies = [];
12
+ for (const arg of args) {
13
+ const exact = MERGE_STRATEGY_FLAGS.get(arg);
14
+ if (exact) {
15
+ strategies.push(exact);
16
+ continue;
17
+ }
18
+ const longAssignment = /^(--merge|--squash|--rebase)=(.*)$/.exec(arg);
19
+ const shortAssignment = /^(-[msr])=(.*)$/.exec(arg);
20
+ const assignment = longAssignment ?? shortAssignment;
21
+ if (assignment) {
22
+ if (assignment[2] !== "true" && assignment[2] !== "false") {
23
+ throw new Error(`Invalid config: merge.commandArgs strategy ${arg} must use true or false`);
24
+ }
25
+ if (assignment[2] === "true") {
26
+ strategies.push(MERGE_STRATEGY_FLAGS.get(assignment[1]));
27
+ }
28
+ continue;
29
+ }
30
+ const shortBundle = /^-([dmrs]{2,})$/.exec(arg);
31
+ if (shortBundle) {
32
+ for (const flag of shortBundle[1]) {
33
+ const strategy = MERGE_STRATEGY_FLAGS.get(`-${flag}`);
34
+ if (strategy)
35
+ strategies.push(strategy);
36
+ }
37
+ continue;
38
+ }
39
+ if (/^-[dmrs].+/.test(arg)) {
40
+ throw new Error(`Invalid config: merge.commandArgs cannot safely parse compound short option ${arg}; use separate flags or long options`);
41
+ }
42
+ }
43
+ return strategies;
44
+ }
package/bin/config.json CHANGED
@@ -36,6 +36,9 @@
36
36
  "mergeStatus": {
37
37
  "blockingReviewerLogins": ["copilot"]
38
38
  },
39
+ "merge": {
40
+ "commandArgs": []
41
+ },
39
42
  "actions": {
40
43
  "autoMinimizeSuppressed": true,
41
44
  "autoMarkReady": true,
@@ -23,6 +23,8 @@ export declare const EXIT: Readonly<{
23
23
  readonly ESCALATE: 13;
24
24
  /** `cancel` + `closed` — PR closed without merging. */
25
25
  readonly CLOSED: 14;
26
+ /** Agent should run the emitted merge or queue command. */
27
+ readonly MERGE: 15;
26
28
  /** Bad/unknown flag, unknown subcommand, missing required arg, invalid duration. */
27
29
  readonly USAGE: 64;
28
30
  /** Malformed caller data: bad `--require-sha`, `PRRC_*` IDs, bad repo string. */
@@ -22,6 +22,8 @@ export const EXIT = Object.freeze({
22
22
  ESCALATE: 13,
23
23
  /** `cancel` + `closed` — PR closed without merging. */
24
24
  CLOSED: 14,
25
+ /** Agent should run the emitted merge or queue command. */
26
+ MERGE: 15,
25
27
  /** Bad/unknown flag, unknown subcommand, missing required arg, invalid duration. */
26
28
  USAGE: 64,
27
29
  /** Malformed caller data: bad `--require-sha`, `PRRC_*` IDs, bad repo string. */
@@ -65,6 +67,8 @@ export function iterateResultToExitCode(result) {
65
67
  return EXIT.FIX_CODE;
66
68
  case "escalate":
67
69
  return EXIT.ESCALATE;
70
+ case "merge":
71
+ return EXIT.MERGE;
68
72
  }
69
73
  }
70
74
  export function errorToExitCode(err) {
@@ -0,0 +1,3 @@
1
+ import type { CheckRun } from "../types.mts";
2
+ import type { RawContextNode } from "./batch-raw-types.mts";
3
+ export declare function parseCheckNodes(nodes: Array<RawContextNode | null> | undefined, mergeCommitOid?: string): CheckRun[];
@@ -0,0 +1,29 @@
1
+ import { mapCheckRunNode, mapStatusContextState, parseCreatedAt } from "./batch-parser-helpers.mjs";
2
+ export function parseCheckNodes(nodes, mergeCommitOid) {
3
+ return (nodes ?? []).flatMap((node) => {
4
+ const scope = mergeCommitOid
5
+ ? { scope: "merge_group", commitOid: mergeCommitOid }
6
+ : {};
7
+ if (node?.__typename === "CheckRun")
8
+ return [{ ...mapCheckRunNode(node), ...scope }];
9
+ if (node?.__typename !== "StatusContext")
10
+ return [];
11
+ const { status, conclusion } = mapStatusContextState(node.state);
12
+ const summary = node.description?.trim() || undefined;
13
+ const createdAtUnix = node.createdAt ? parseCreatedAt(node.createdAt) : undefined;
14
+ return [
15
+ {
16
+ name: node.context,
17
+ status,
18
+ conclusion,
19
+ source: "status_context",
20
+ detailsUrl: node.targetUrl ?? "",
21
+ event: null,
22
+ runId: null,
23
+ ...scope,
24
+ ...(createdAtUnix !== undefined && { createdAtUnix }),
25
+ ...(summary !== undefined && { summary }),
26
+ },
27
+ ];
28
+ });
29
+ }
@@ -1,6 +1,8 @@
1
- import type { BranchRules, MergeQueueEntryStatus, StackStatus } from "../types.mts";
1
+ import type { AutoMergeRequestStatus, BranchRules, MergeQueueEntryStatus, MergeQueueRemovalStatus, StackStatus } from "../types.mts";
2
2
  import type { RawBaseRef, RawPrMergeFields } from "./batch-raw-rules.mts";
3
3
  export declare const EMPTY_BRANCH_RULES: BranchRules;
4
4
  export declare function parseBranchRules(baseRef: RawBaseRef | null | undefined): BranchRules;
5
5
  export declare function parseMergeQueueEntry(raw: RawPrMergeFields["mergeQueueEntry"]): MergeQueueEntryStatus | null;
6
+ export declare function parseAutoMergeRequest(raw: RawPrMergeFields["autoMergeRequest"]): AutoMergeRequestStatus | null;
7
+ export declare function parseLatestMergeQueueRemoval(raw: RawPrMergeFields): MergeQueueRemovalStatus | null;
6
8
  export declare function parseStack(raw: RawPrMergeFields): StackStatus | null;
@@ -32,8 +32,41 @@ export function parseMergeQueueEntry(raw) {
32
32
  position: raw.position,
33
33
  state: raw.state,
34
34
  estimatedTimeToMerge: raw.estimatedTimeToMerge,
35
+ ...(raw.headCommit?.oid && { headCommitOid: raw.headCommit.oid }),
36
+ ...(raw.enqueuedAt && { enqueuedAtUnix: parseUnix(raw.enqueuedAt) }),
37
+ ...(raw.enqueuer?.login && { enqueuer: raw.enqueuer.login }),
35
38
  };
36
39
  }
40
+ export function parseAutoMergeRequest(raw) {
41
+ if (!raw)
42
+ return null;
43
+ return {
44
+ enabledAtUnix: parseUnix(raw.enabledAt),
45
+ mergeMethod: raw.mergeMethod,
46
+ ...(raw.enabledBy?.login && { enabledBy: raw.enabledBy.login }),
47
+ };
48
+ }
49
+ export function parseLatestMergeQueueRemoval(raw) {
50
+ const removal = raw.mergeQueueRemovals?.nodes[0];
51
+ if (!removal)
52
+ return null;
53
+ const lastAddedAt = raw.mergeQueueAdditions?.nodes[0]?.createdAt;
54
+ if (lastAddedAt && Date.parse(lastAddedAt) > Date.parse(removal.createdAt))
55
+ return null;
56
+ return {
57
+ reason: removal.reason,
58
+ createdAtUnix: parseUnix(removal.createdAt),
59
+ ...(removal.actor?.login && { actor: removal.actor.login }),
60
+ ...(removal.beforeCommit?.oid && { beforeCommitOid: removal.beforeCommit.oid }),
61
+ ...(removal.beforeCommit?.parents && {
62
+ beforeCommitParentOids: removal.beforeCommit.parents.nodes.map((parent) => parent.oid),
63
+ }),
64
+ };
65
+ }
66
+ function parseUnix(value) {
67
+ const ms = Date.parse(value);
68
+ return Number.isFinite(ms) ? Math.floor(ms / 1000) : 0;
69
+ }
37
70
  export function parseStack(raw) {
38
71
  if (!raw.stack)
39
72
  return null;
@@ -1,3 +1,3 @@
1
1
  import type { BatchPrData } from "../types.mts";
2
- import type { RawPr, RawThread, RawComment, RawReview, RawReviewSummary, RawContextNode } from "./batch-raw-types.mts";
3
- export declare function parseRawPr(raw: RawPr, rawThreadPages: RawThread[], rawCommentNodes: RawComment[], rawReviewNodes: RawReview[], rawReviewSummaryNodes: RawReviewSummary[], rawApprovedReviewNodes: RawReviewSummary[], rawCheckNodes: RawContextNode[]): BatchPrData;
2
+ import type { RawBatchResponse, RawPr, RawThread, RawComment, RawReview, RawReviewSummary, RawContextNode } from "./batch-raw-types.mts";
3
+ export declare function parseRawPr(raw: RawPr, rawThreadPages: RawThread[], rawCommentNodes: RawComment[], rawReviewNodes: RawReview[], rawReviewSummaryNodes: RawReviewSummary[], rawApprovedReviewNodes: RawReviewSummary[], rawCheckNodes: RawContextNode[], repository?: Pick<NonNullable<RawBatchResponse["repository"]>, "viewerPermission" | "viewerCanAdminister">): BatchPrData;
@@ -1,13 +1,16 @@
1
- import { mapAuthorType, parseCreatedAt, mapStatusContextState, latestApprovedLogins, isReviewStale, mapCheckRunNode, } from "./batch-parser-helpers.mjs";
1
+ import { mapAuthorType, parseCreatedAt, latestApprovedLogins, isReviewStale, } from "./batch-parser-helpers.mjs";
2
+ import { parseCheckNodes } from "./batch-parse-checks.mjs";
3
+ import { requireContextNodes } from "./batch-response.mjs";
2
4
  import { buildPrActivitySummary } from "./activity.mjs";
3
5
  import { parseBranchProtection } from "./branch-protection.mjs";
4
- import { parseBranchRules, parseMergeQueueEntry, parseStack } from "./batch-parsers-rules.mjs";
6
+ import { parseAutoMergeRequest, parseBranchRules, parseLatestMergeQueueRemoval, parseMergeQueueEntry, parseStack, } from "./batch-parsers-rules.mjs";
5
7
  function parseReviewNode(r) {
6
8
  const base = {
7
9
  id: r.id,
8
10
  author: r.author?.login ?? "unknown",
9
11
  authorType: mapAuthorType(r.author?.__typename, r.author?.login),
10
12
  ...(r.authorAssociation !== undefined && { authorAssociation: r.authorAssociation }),
13
+ viewerCanMinimize: "viewerCanMinimize" in r && r.viewerCanMinimize === true,
11
14
  body: r.body,
12
15
  createdAtUnix: r.createdAt ? parseCreatedAt(r.createdAt) : 0,
13
16
  };
@@ -16,7 +19,7 @@ function parseReviewNode(r) {
16
19
  }
17
20
  return base;
18
21
  }
19
- export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes, rawReviewSummaryNodes, rawApprovedReviewNodes, rawCheckNodes) {
22
+ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes, rawReviewSummaryNodes, rawApprovedReviewNodes, rawCheckNodes, repository) {
20
23
  const reviewRequests = (raw.reviewRequests?.nodes ?? []).flatMap((n) => {
21
24
  const login = n.requestedReviewer?.login ?? n.requestedReviewer?.name;
22
25
  return login ? [{ login }] : [];
@@ -35,6 +38,7 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
35
38
  author: c.author?.login ?? "unknown",
36
39
  authorType: mapAuthorType(c.author?.__typename, c.author?.login),
37
40
  ...(c.authorAssociation !== undefined && { authorAssociation: c.authorAssociation }),
41
+ ...(c.viewerDidAuthor === true && { viewerDidAuthor: true }),
38
42
  body: c.body,
39
43
  url: c.url,
40
44
  createdAtUnix: c.createdAt ? parseCreatedAt(c.createdAt) : 0,
@@ -44,6 +48,8 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
44
48
  isResolved: t.isResolved,
45
49
  isOutdated: t.isOutdated,
46
50
  isMinimized: comment?.isMinimized ?? false,
51
+ viewerCanReply: t.viewerCanReply === true,
52
+ viewerCanResolve: t.viewerCanResolve === true,
47
53
  path: t.path ?? comment?.path ?? null,
48
54
  line: t.line ?? comment?.line ?? null,
49
55
  startLine: t.startLine ?? comment?.startLine ?? null,
@@ -53,6 +59,7 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
53
59
  ...(comment?.authorAssociation !== undefined && {
54
60
  authorAssociation: comment.authorAssociation,
55
61
  }),
62
+ ...(comment?.viewerDidAuthor === true && { viewerDidAuthor: true }),
56
63
  body: comment?.body ?? "",
57
64
  url: comment?.url ?? "",
58
65
  createdAtUnix: comment?.createdAt ? parseCreatedAt(comment.createdAt) : 0,
@@ -62,6 +69,7 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
62
69
  const comments = rawCommentNodes.map((c) => ({
63
70
  id: c.id,
64
71
  isMinimized: c.isMinimized,
72
+ viewerCanMinimize: c.viewerCanMinimize === true,
65
73
  author: c.author?.login ?? "unknown",
66
74
  authorType: mapAuthorType(c.author?.__typename, c.author?.login),
67
75
  ...(c.authorAssociation !== undefined && { authorAssociation: c.authorAssociation }),
@@ -86,30 +94,15 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
86
94
  const approvedReviews = rawApprovedReviewNodes
87
95
  .filter((r) => !r.isMinimized)
88
96
  .map((r) => parseReviewNode(r));
89
- const checks = rawCheckNodes.flatMap((node) => {
90
- if (node.__typename === "CheckRun") {
91
- return [mapCheckRunNode(node)];
92
- }
93
- if (node.__typename === "StatusContext") {
94
- const { status, conclusion } = mapStatusContextState(node.state);
95
- const summary = node.description?.trim() || undefined;
96
- const createdAtUnix = node.createdAt ? parseCreatedAt(node.createdAt) : undefined;
97
- return [
98
- {
99
- name: node.context,
100
- status,
101
- conclusion,
102
- source: "status_context",
103
- detailsUrl: node.targetUrl ?? "",
104
- event: null,
105
- runId: null,
106
- ...(createdAtUnix !== undefined && { createdAtUnix }),
107
- ...(summary !== undefined && { summary }),
108
- },
109
- ];
110
- }
111
- return [];
112
- });
97
+ const checks = parseCheckNodes(rawCheckNodes);
98
+ const queueHead = raw.mergeQueueEntry?.headCommit;
99
+ const removedQueueHead = raw.mergeQueueRemovals?.nodes[0]?.beforeCommit;
100
+ const mergeQueueChecks = parseCheckNodes(queueHead?.statusCheckRollup
101
+ ? requireContextNodes(queueHead.statusCheckRollup.contexts.nodes)
102
+ : undefined, queueHead?.oid);
103
+ const removedMergeQueueChecks = parseCheckNodes(removedQueueHead?.statusCheckRollup
104
+ ? requireContextNodes(removedQueueHead.statusCheckRollup.contexts.nodes)
105
+ : undefined, removedQueueHead?.oid);
113
106
  return {
114
107
  nodeId: raw.id,
115
108
  number: raw.number,
@@ -121,6 +114,16 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
121
114
  headRefOid: raw.headRefOid,
122
115
  headRefName: raw.headRefName,
123
116
  headRepoWithOwner: raw.headRepository?.nameWithOwner ?? null,
117
+ viewerAuthorization: {
118
+ repositoryPermission: repository?.viewerPermission ?? null,
119
+ viewerCanAdminister: repository?.viewerCanAdminister === true,
120
+ viewerDidAuthor: raw.viewerDidAuthor === true,
121
+ viewerCanUpdate: raw.viewerCanUpdate === true,
122
+ viewerCanEnableAutoMerge: raw.viewerCanEnableAutoMerge === true,
123
+ viewerCanEditFiles: raw.viewerCanEditFiles === true,
124
+ headRepositoryPermission: raw.headRepository?.viewerPermission ??
125
+ null,
126
+ },
124
127
  baseRefName: raw.baseRefName,
125
128
  reviewRequests,
126
129
  latestReviews,
@@ -135,6 +138,16 @@ export function parseRawPr(raw, rawThreadPages, rawCommentNodes, rawReviewNodes,
135
138
  isInMergeQueue: raw.isInMergeQueue ?? false,
136
139
  isMergeQueueEnabled: raw.isMergeQueueEnabled ?? false,
137
140
  mergeQueueEntry: parseMergeQueueEntry(raw.mergeQueueEntry),
141
+ autoMergeRequest: parseAutoMergeRequest(raw.autoMergeRequest),
142
+ latestMergeQueueRemoval: parseLatestMergeQueueRemoval(raw),
143
+ ...(mergeQueueChecks.length > 0 && { mergeQueueChecks }),
144
+ ...(queueHead?.statusCheckRollup?.contexts.pageInfo.hasNextPage && {
145
+ mergeQueueChecksIncomplete: true,
146
+ }),
147
+ ...(removedMergeQueueChecks.length > 0 && { removedMergeQueueChecks }),
148
+ ...(removedQueueHead?.statusCheckRollup?.contexts.pageInfo.hasNextPage && {
149
+ removedMergeQueueChecksIncomplete: true,
150
+ }),
138
151
  stack: parseStack(raw),
139
152
  activity: buildPrActivitySummary(raw, comments, reviewThreads, reviewSummaries, allChangesRequestedReviews, approvedReviews),
140
153
  };
@@ -12,10 +12,49 @@ export interface RawBranchProtectionRule {
12
12
  requiresDeployments?: boolean;
13
13
  requiredDeploymentEnvironments?: string[] | null;
14
14
  }
15
+ import type { RawContextNode } from "./batch-raw-types.mts";
16
+ interface RawCheckCommit {
17
+ oid: string;
18
+ committedDate?: string;
19
+ parents?: {
20
+ nodes: Array<{
21
+ oid: string;
22
+ }>;
23
+ };
24
+ statusCheckRollup: {
25
+ contexts: {
26
+ pageInfo: {
27
+ hasNextPage: boolean;
28
+ endCursor: string | null;
29
+ };
30
+ nodes: Array<RawContextNode | null>;
31
+ };
32
+ } | null;
33
+ }
15
34
  interface RawMergeQueueEntry {
16
35
  position: number;
17
36
  state: string;
18
37
  estimatedTimeToMerge: number | null;
38
+ headCommit?: RawCheckCommit | null;
39
+ enqueuedAt?: string;
40
+ enqueuer?: {
41
+ login: string;
42
+ } | null;
43
+ }
44
+ interface RawAutoMergeRequest {
45
+ enabledAt: string;
46
+ mergeMethod: string;
47
+ enabledBy?: {
48
+ login: string;
49
+ } | null;
50
+ }
51
+ interface RawMergeQueueRemoval {
52
+ reason: string | null;
53
+ actor?: {
54
+ login: string;
55
+ } | null;
56
+ createdAt: string;
57
+ beforeCommit?: RawCheckCommit | null;
19
58
  }
20
59
  interface RawStack {
21
60
  number: number;
@@ -50,6 +89,15 @@ export interface RawPrMergeFields {
50
89
  isInMergeQueue?: boolean;
51
90
  isMergeQueueEnabled?: boolean;
52
91
  mergeQueueEntry?: RawMergeQueueEntry | null;
92
+ autoMergeRequest?: RawAutoMergeRequest | null;
93
+ mergeQueueRemovals?: {
94
+ nodes: RawMergeQueueRemoval[];
95
+ } | null;
96
+ mergeQueueAdditions?: {
97
+ nodes: Array<{
98
+ createdAt: string;
99
+ }>;
100
+ } | null;
53
101
  stack?: RawStack | null;
54
102
  stackEntry?: {
55
103
  position: number;
@@ -2,6 +2,8 @@ import type { CommentAuthorAssociation } from "../types/github.mts";
2
2
  import type { RawPrMergeFields } from "./batch-raw-rules.mts";
3
3
  export interface RawBatchResponse {
4
4
  repository: {
5
+ viewerPermission: string | null;
6
+ viewerCanAdminister: boolean;
5
7
  pullRequest: RawPr | null;
6
8
  } | null;
7
9
  }
@@ -10,6 +12,10 @@ export interface RawPr extends RawPrMergeFields {
10
12
  number: number;
11
13
  state: string;
12
14
  isDraft: boolean;
15
+ viewerDidAuthor: boolean;
16
+ viewerCanUpdate: boolean;
17
+ viewerCanEnableAutoMerge: boolean;
18
+ viewerCanEditFiles: boolean;
13
19
  mergeable: string;
14
20
  mergeStateStatus: string;
15
21
  reviewDecision: string | null;
@@ -17,6 +23,7 @@ export interface RawPr extends RawPrMergeFields {
17
23
  headRefName: string;
18
24
  headRepository: {
19
25
  nameWithOwner: string;
26
+ viewerPermission: string | null;
20
27
  } | null;
21
28
  baseRefName: string;
22
29
  baseRef: import("./batch-raw-rules.mts").RawBaseRef | null;
@@ -117,6 +124,7 @@ export interface RawThreadComment {
117
124
  isMinimized: boolean;
118
125
  url: string;
119
126
  authorAssociation?: CommentAuthorAssociation;
127
+ viewerDidAuthor?: boolean;
120
128
  author: RawAuthor | null;
121
129
  pullRequestReview?: {
122
130
  id: string;
@@ -131,6 +139,8 @@ export interface RawThread {
131
139
  id: string;
132
140
  isResolved: boolean;
133
141
  isOutdated: boolean;
142
+ viewerCanReply: boolean;
143
+ viewerCanResolve: boolean;
134
144
  path?: string | null;
135
145
  line?: number | null;
136
146
  startLine?: number | null;
@@ -157,6 +167,7 @@ export interface RawReviewThreadCommentsResponse {
157
167
  export interface RawComment {
158
168
  id: string;
159
169
  isMinimized: boolean;
170
+ viewerCanMinimize: boolean;
160
171
  url: string;
161
172
  authorAssociation?: CommentAuthorAssociation;
162
173
  author: RawAuthor | null;
@@ -176,6 +187,7 @@ export interface RawReview {
176
187
  export interface RawReviewSummary {
177
188
  id: string;
178
189
  isMinimized: boolean;
190
+ viewerCanMinimize: boolean;
179
191
  authorAssociation?: CommentAuthorAssociation;
180
192
  author: RawAuthor | null;
181
193
  body: string;
@@ -1,2 +1 @@
1
- // Private raw GraphQL response types for the batch PR query.
2
1
  export {};
@@ -6,6 +6,7 @@ import { parseCheckSuitesComplete, parseSuiteStartupFailures } from "./batch-par
6
6
  import { mergeStartupFailureChecks } from "../checks/startup-failures.mjs";
7
7
  import { paginateBatchConnections } from "./batch-page.mjs";
8
8
  import { requireRawPr } from "./batch-response.mjs";
9
+ import { hydrateMergeQueueChecks } from "./merge-queue-checks.mjs";
9
10
  /**
10
11
  * Fetch all PR data needed for a `shepherd check` in one (or a few, if paginating) GraphQL requests.
11
12
  */
@@ -16,9 +17,10 @@ export async function fetchPrBatch(pr, repo, opts = {}) {
16
17
  pr,
17
18
  });
18
19
  const raw = requireRawPr(result.data, pr, repo);
20
+ await hydrateMergeQueueChecks(raw, repo);
19
21
  const paged = await paginateBatchConnections(pr, repo, raw, opts, result.rateLimit);
20
22
  const rawThreadPages = await hydrateThreadCommentPages(paged.threads);
21
- const data = parseRawPr(raw, rawThreadPages, paged.comments, paged.changesRequested, paged.reviewSummaries, paged.approvedReviews, paged.checks);
23
+ const data = parseRawPr(raw, rawThreadPages, paged.comments, paged.changesRequested, paged.reviewSummaries, paged.approvedReviews, paged.checks, result.data.repository);
22
24
  data.checks = mergeStartupFailureChecks(data.checks, parseSuiteStartupFailures(raw));
23
25
  return {
24
26
  data,
@@ -30,6 +30,7 @@ export declare function getPrHeadSha(pr: number, owner: string, name: string): P
30
30
  export declare function getPullRequestBody(pr: number, owner: string, name: string): Promise<{
31
31
  nodeId: string;
32
32
  body: string;
33
+ viewerCanUpdate?: boolean;
33
34
  }>;
34
35
  /** Overwrites the PR body. */
35
36
  export declare function updatePullRequestBody(pullRequestId: string, body: string): Promise<void>;
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-lines */
1
2
  /**
2
3
  * High-level GitHub client — wraps http.mts for application-level concerns.
3
4
  *
@@ -76,7 +77,13 @@ export async function getPullRequestBody(pr, owner, name) {
76
77
  : "PR not found or access denied";
77
78
  throw new Error(`Could not fetch body for ${owner}/${name} PR #${pr}: ${detail}`);
78
79
  }
79
- return { nodeId: pullRequest.id, body: pullRequest.body ?? "" };
80
+ return {
81
+ nodeId: pullRequest.id,
82
+ body: pullRequest.body ?? "",
83
+ ...(pullRequest.viewerCanUpdate !== undefined && {
84
+ viewerCanUpdate: pullRequest.viewerCanUpdate,
85
+ }),
86
+ };
80
87
  }
81
88
  /** Overwrites the PR body. */
82
89
  export async function updatePullRequestBody(pullRequestId, body) {
@@ -29,6 +29,8 @@ query BatchPrPage(
29
29
  id
30
30
  isResolved
31
31
  isOutdated
32
+ viewerCanReply
33
+ viewerCanResolve
32
34
  path
33
35
  line
34
36
  startLine
@@ -42,6 +44,7 @@ query BatchPrPage(
42
44
  isMinimized
43
45
  url
44
46
  authorAssociation
47
+ viewerDidAuthor
45
48
  author {
46
49
  __typename
47
50
  login
@@ -66,6 +69,7 @@ query BatchPrPage(
66
69
  nodes {
67
70
  id
68
71
  isMinimized
72
+ viewerCanMinimize
69
73
  url
70
74
  authorAssociation
71
75
  author {
@@ -108,6 +112,7 @@ query BatchPrPage(
108
112
  nodes {
109
113
  id
110
114
  isMinimized
115
+ viewerCanMinimize
111
116
  authorAssociation
112
117
  author {
113
118
  __typename
@@ -126,6 +131,7 @@ query BatchPrPage(
126
131
  nodes {
127
132
  id
128
133
  isMinimized
134
+ viewerCanMinimize
129
135
  authorAssociation
130
136
  author {
131
137
  __typename