diff-easy 0.1.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 (172) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +142 -0
  3. package/dist/cli/cmux.d.ts +9 -0
  4. package/dist/cli/cmux.js +274 -0
  5. package/dist/cli/comment.d.ts +2 -0
  6. package/dist/cli/comment.js +91 -0
  7. package/dist/cli/github.d.ts +84 -0
  8. package/dist/cli/github.js +364 -0
  9. package/dist/cli/index.d.ts +2 -0
  10. package/dist/cli/index.js +397 -0
  11. package/dist/cli/turns.d.ts +14 -0
  12. package/dist/cli/turns.js +333 -0
  13. package/dist/cli/utils.d.ts +27 -0
  14. package/dist/cli/utils.js +204 -0
  15. package/dist/client/assets/arc-CHYjdFTk.js +1 -0
  16. package/dist/client/assets/architecture-7EHR7CIX-weHBYDgM.js +1 -0
  17. package/dist/client/assets/architectureDiagram-3BPJPVTR-DRa2msuJ.js +36 -0
  18. package/dist/client/assets/array-BifhSqXX.js +1 -0
  19. package/dist/client/assets/blockDiagram-GPEHLZMM-BTdKsXy5.js +132 -0
  20. package/dist/client/assets/c4Diagram-AAUBKEIU-CwTvCkOg.js +10 -0
  21. package/dist/client/assets/channel-Cv40nQW1.js +1 -0
  22. package/dist/client/assets/chunk-2J33WTMH-BFsjrTtc.js +1 -0
  23. package/dist/client/assets/chunk-3OPIFGDE-V_xVw1dq.js +62 -0
  24. package/dist/client/assets/chunk-4BX2VUAB-DoLO6LLy.js +1 -0
  25. package/dist/client/assets/chunk-55IACEB6-CHgBwbdB.js +1 -0
  26. package/dist/client/assets/chunk-5ZQYHXKU-DMixAgQG.js +2 -0
  27. package/dist/client/assets/chunk-727SXJPM-C-3FZTmm.js +206 -0
  28. package/dist/client/assets/chunk-AQP2D5EJ-DpQZhXt-.js +231 -0
  29. package/dist/client/assets/chunk-BSJP7CBP-BnO9E_G4.js +1 -0
  30. package/dist/client/assets/chunk-CSCIHK7Q-BxItphyO.js +124 -0
  31. package/dist/client/assets/chunk-FMBD7UC4-4hEcfvfT.js +15 -0
  32. package/dist/client/assets/chunk-KSCS5N6A-DnArkpOI.js +10 -0
  33. package/dist/client/assets/chunk-L5ZTLDWV-DQoYJ1BW.js +1 -0
  34. package/dist/client/assets/chunk-LZXEDZCA-CdCevBX-.js +2 -0
  35. package/dist/client/assets/chunk-ND2GUHAM-Dt89U_nU.js +1 -0
  36. package/dist/client/assets/chunk-NNHCCRGN-DlpIbxXb.js +159 -0
  37. package/dist/client/assets/chunk-NZK2D7GU-C2Ev_f_w.js +1 -0
  38. package/dist/client/assets/chunk-O5CBEL6O-BeZ3MFnJ.js +70 -0
  39. package/dist/client/assets/chunk-QZHKN3VN-BmDOSWHp.js +1 -0
  40. package/dist/client/assets/chunk-WU5MYG2G-gLuInTk4.js +1 -0
  41. package/dist/client/assets/chunk-XPW4576I-DPbDc8_T.js +32 -0
  42. package/dist/client/assets/classDiagram-4FO5ZUOK-DOpVKUrb.js +1 -0
  43. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DOpVKUrb.js +1 -0
  44. package/dist/client/assets/cose-bilkent-S5V4N54A-CCPyalms.js +1 -0
  45. package/dist/client/assets/cytoscape.esm-FqbQrHcz.js +321 -0
  46. package/dist/client/assets/dagre-BM42HDAG-6jrYMjX3.js +4 -0
  47. package/dist/client/assets/dagre-Bx709z4p.js +1 -0
  48. package/dist/client/assets/defaultLocale-C8Fc0cco.js +1 -0
  49. package/dist/client/assets/diagram-2AECGRRQ-dCQ_KoXl.js +43 -0
  50. package/dist/client/assets/diagram-5GNKFQAL-B3Xj4-XE.js +10 -0
  51. package/dist/client/assets/diagram-KO2AKTUF-DaGgF2yL.js +3 -0
  52. package/dist/client/assets/diagram-LMA3HP47-CF_c4tPT.js +24 -0
  53. package/dist/client/assets/diagram-OG6HWLK6-BgBHbJkT.js +24 -0
  54. package/dist/client/assets/dist-7wVkzZne.js +1 -0
  55. package/dist/client/assets/erDiagram-TEJ5UH35-BZgX1grR.js +85 -0
  56. package/dist/client/assets/eventmodeling-FCH6USID-BaI6RNKr.js +1 -0
  57. package/dist/client/assets/flowDiagram-I6XJVG4X-BFNUa12V.js +162 -0
  58. package/dist/client/assets/ganttDiagram-6RSMTGT7-C8t6BThI.js +292 -0
  59. package/dist/client/assets/gitGraph-WXDBUCRP-BlicfDkM.js +1 -0
  60. package/dist/client/assets/gitGraphDiagram-PVQCEYII-BmnIXuXP.js +106 -0
  61. package/dist/client/assets/graphlib-B8gBHxth.js +1 -0
  62. package/dist/client/assets/index-BfVtbKTZ.css +2 -0
  63. package/dist/client/assets/index-p8zzfjdu.js +87 -0
  64. package/dist/client/assets/info-J43DQDTF-DHLG38N3.js +1 -0
  65. package/dist/client/assets/infoDiagram-5YYISTIA-oQ-lq-45.js +2 -0
  66. package/dist/client/assets/init-D6jRqBbL.js +1 -0
  67. package/dist/client/assets/ishikawaDiagram-YF4QCWOH-DrODlBLD.js +70 -0
  68. package/dist/client/assets/journeyDiagram-JHISSGLW-Bxg5wCQz.js +139 -0
  69. package/dist/client/assets/kanban-definition-UN3LZRKU-DAdvUDIB.js +89 -0
  70. package/dist/client/assets/katex-Vhh-h91d.js +257 -0
  71. package/dist/client/assets/line-D8kNd46u.js +1 -0
  72. package/dist/client/assets/linear-jwGZSrg4.js +1 -0
  73. package/dist/client/assets/mermaid-parser.core-PVPfUfyU.js +4 -0
  74. package/dist/client/assets/mermaid.core-DwdP6CFg.js +9 -0
  75. package/dist/client/assets/mindmap-definition-RKZ34NQL-DeOoL56Q.js +96 -0
  76. package/dist/client/assets/ordinal-hYBb2elL.js +1 -0
  77. package/dist/client/assets/packet-YPE3B663-BBEaxdJP.js +1 -0
  78. package/dist/client/assets/path-BWPyau1x.js +1 -0
  79. package/dist/client/assets/pie-LRSECV5Y-fvTRFm-2.js +1 -0
  80. package/dist/client/assets/pieDiagram-4H26LBE5-oep1px2r.js +30 -0
  81. package/dist/client/assets/prism-bash-D6zCJ74D.js +1 -0
  82. package/dist/client/assets/prism-clojure-_z-zoNcE.js +1 -0
  83. package/dist/client/assets/prism-csharp-XPn2bw0d.js +1 -0
  84. package/dist/client/assets/prism-dart-CDv16Fbu.js +1 -0
  85. package/dist/client/assets/prism-elixir-DhHhV5Rj.js +1 -0
  86. package/dist/client/assets/prism-haskell-5hq1Nh9c.js +1 -0
  87. package/dist/client/assets/prism-hcl-B6Z0Ouoo.js +1 -0
  88. package/dist/client/assets/prism-java-6wtusvIl.js +1 -0
  89. package/dist/client/assets/prism-markup-templating-KkSMBj6Y.js +1 -0
  90. package/dist/client/assets/prism-nix-C88dTX93.js +1 -0
  91. package/dist/client/assets/prism-perl-Lsbi34sH.js +1 -0
  92. package/dist/client/assets/prism-php-Co4fM40K.js +1 -0
  93. package/dist/client/assets/prism-protobuf-yzSHlhwy.js +1 -0
  94. package/dist/client/assets/prism-ruby-DFCASkuD.js +1 -0
  95. package/dist/client/assets/prism-scala-VkM3wCOL.js +1 -0
  96. package/dist/client/assets/prism-solidity-DTom4k5p.js +1 -0
  97. package/dist/client/assets/prism-sql-CYhVDMEE.js +1 -0
  98. package/dist/client/assets/prism-vim-BgpkjCK5.js +1 -0
  99. package/dist/client/assets/quadrantDiagram-W4KKPZXB-D9l66CHb.js +7 -0
  100. package/dist/client/assets/radar-GUYGQ44K-DAU5EPVd.js +1 -0
  101. package/dist/client/assets/requirementDiagram-4Y6WPE33-helpQlwb.js +84 -0
  102. package/dist/client/assets/rough.esm-CSKSodPl.js +1 -0
  103. package/dist/client/assets/sankeyDiagram-5OEKKPKP-C9sNuuvA.js +40 -0
  104. package/dist/client/assets/sequenceDiagram-3UESZ5HK-BP0bhCwQ.js +162 -0
  105. package/dist/client/assets/src-CDAPGf2l.js +1 -0
  106. package/dist/client/assets/stateDiagram-AJRCARHV-CsFizpla.js +1 -0
  107. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Bvn8tanl.js +1 -0
  108. package/dist/client/assets/timeline-definition-PNZ67QCA-BnkmHs1F.js +120 -0
  109. package/dist/client/assets/treeView-BLDUP644-DD9HZO5W.js +1 -0
  110. package/dist/client/assets/treemap-LRROVOQU-BBKuQ1lo.js +1 -0
  111. package/dist/client/assets/vennDiagram-CIIHVFJN-BPFTHflF.js +34 -0
  112. package/dist/client/assets/wardley-L42UT6IY-y88gDkwb.js +1 -0
  113. package/dist/client/assets/wardleyDiagram-YWT4CUSO-BlcA7IVh.js +78 -0
  114. package/dist/client/assets/xychartDiagram-2RQKCTM6-DX5vUCRH.js +7 -0
  115. package/dist/client/favicon.png +0 -0
  116. package/dist/client/index.html +14 -0
  117. package/dist/client/logo.png +0 -0
  118. package/dist/client/site-data/blobs/080c0e6/cHVibGljL3NpdGUtZGF0YS9vZy1pbWFnZS5wbmc.png +0 -0
  119. package/dist/client/site-data/blobs/55f23a1/bGFuZGluZy9wdWJsaWMvZGlmaXQvbG9nby5wbmc.png +0 -0
  120. package/dist/client/site-data/blobs/66ff7c6/cHVibGljL2xvZ28ucG5n.png +0 -0
  121. package/dist/client/site-data/blobs/e6977fe/cHVibGljL2xvZ28ucG5n.png +0 -0
  122. package/dist/client/site-data/manifest.json +1 -0
  123. package/dist/client/site-data/og-image.png +0 -0
  124. package/dist/client/site-data/snapshots/55f23a1...080c0e6.json +1 -0
  125. package/dist/client/site-data/snapshots/66ff7c6...e6977fe.json +1 -0
  126. package/dist/client/site-data/snapshots/7d40fd4...a72112f-comments.json +1 -0
  127. package/dist/client/site-data/snapshots/7d40fd4...a72112f.json +1 -0
  128. package/dist/server/file-watcher.d.ts +33 -0
  129. package/dist/server/file-watcher.js +340 -0
  130. package/dist/server/generated-file-check.d.ts +17 -0
  131. package/dist/server/generated-file-check.js +280 -0
  132. package/dist/server/git-diff.d.ts +94 -0
  133. package/dist/server/git-diff.js +909 -0
  134. package/dist/server/grouping/cache.d.ts +3 -0
  135. package/dist/server/grouping/cache.js +22 -0
  136. package/dist/server/grouping/claudeCli.d.ts +10 -0
  137. package/dist/server/grouping/claudeCli.js +95 -0
  138. package/dist/server/grouping/digest.d.ts +15 -0
  139. package/dist/server/grouping/digest.js +38 -0
  140. package/dist/server/grouping/reconcile.d.ts +6 -0
  141. package/dist/server/grouping/reconcile.js +34 -0
  142. package/dist/server/grouping/types.d.ts +9 -0
  143. package/dist/server/grouping/types.js +1 -0
  144. package/dist/server/server.d.ts +37 -0
  145. package/dist/server/server.js +1283 -0
  146. package/dist/shared/grouping.d.ts +4 -0
  147. package/dist/shared/grouping.js +170 -0
  148. package/dist/types/diff.d.ts +263 -0
  149. package/dist/types/diff.js +1 -0
  150. package/dist/types/watch.d.ts +59 -0
  151. package/dist/types/watch.js +8 -0
  152. package/dist/utils/commentFormatting.d.ts +6 -0
  153. package/dist/utils/commentFormatting.js +90 -0
  154. package/dist/utils/commentImports.d.ts +11 -0
  155. package/dist/utils/commentImports.js +386 -0
  156. package/dist/utils/createId.d.ts +1 -0
  157. package/dist/utils/createId.js +5 -0
  158. package/dist/utils/diffMode.d.ts +3 -0
  159. package/dist/utils/diffMode.js +13 -0
  160. package/dist/utils/diffSelection.d.ts +13 -0
  161. package/dist/utils/diffSelection.js +39 -0
  162. package/dist/utils/editorOptions.d.ts +60 -0
  163. package/dist/utils/editorOptions.js +171 -0
  164. package/dist/utils/fileUtils.d.ts +12 -0
  165. package/dist/utils/fileUtils.js +21 -0
  166. package/dist/utils/hubConfig.d.ts +32 -0
  167. package/dist/utils/hubConfig.js +51 -0
  168. package/dist/utils/suggestionUtils.d.ts +19 -0
  169. package/dist/utils/suggestionUtils.js +31 -0
  170. package/dist/utils/turnLog.d.ts +47 -0
  171. package/dist/utils/turnLog.js +133 -0
  172. package/package.json +105 -0
@@ -0,0 +1,364 @@
1
+ import { execFileSync } from 'child_process';
2
+ const PR_REVIEW_THREADS_GRAPHQL_QUERY = `
3
+ query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {
4
+ repository(owner: $owner, name: $repo) {
5
+ pullRequest(number: $number) {
6
+ reviewThreads(first: 100, after: $endCursor) {
7
+ nodes {
8
+ id
9
+ isResolved
10
+ isOutdated
11
+ subjectType
12
+ path
13
+ diffSide
14
+ startDiffSide
15
+ line
16
+ startLine
17
+ originalLine
18
+ originalStartLine
19
+ comments(first: 100) {
20
+ nodes {
21
+ id
22
+ body
23
+ createdAt
24
+ updatedAt
25
+ author {
26
+ login
27
+ }
28
+ }
29
+ }
30
+ }
31
+ pageInfo {
32
+ hasNextPage
33
+ endCursor
34
+ }
35
+ }
36
+ }
37
+ }
38
+ }
39
+ `;
40
+ const PR_DETAILS_GRAPHQL_QUERY = `
41
+ query($owner: String!, $repo: String!, $number: Int!) {
42
+ repository(owner: $owner, name: $repo) {
43
+ pullRequest(number: $number) {
44
+ title
45
+ body
46
+ }
47
+ }
48
+ }
49
+ `;
50
+ // gh output (a full PR diff or a page of review threads) routinely exceeds
51
+ // Node's default 1MB child-process buffer, which surfaces as `spawnSync gh
52
+ // ENOBUFS`. Give it plenty of headroom so large PRs open cleanly.
53
+ const GH_MAX_BUFFER = 100 * 1024 * 1024; // 100MB
54
+ function isPositiveInteger(value) {
55
+ return typeof value === 'number' && Number.isInteger(value) && value > 0;
56
+ }
57
+ function formatGhCommandError(error) {
58
+ const stderr = error.stderr;
59
+ const stderrText = typeof stderr === 'string'
60
+ ? stderr.trim()
61
+ : Buffer.isBuffer(stderr)
62
+ ? stderr.toString('utf8').trim()
63
+ : '';
64
+ const message = stderrText || (error instanceof Error ? error.message : 'Unknown error while running gh');
65
+ return new Error(`${message}\nTry: gh auth login`);
66
+ }
67
+ function warnPrCommentImport(threadId, message) {
68
+ const threadLabel = threadId ? ` ${threadId}` : '';
69
+ console.warn(`Warning: Skipping PR review thread${threadLabel}: ${message}`);
70
+ }
71
+ function createSingleLinePosition(side, line) {
72
+ return { side, line };
73
+ }
74
+ function createMultiLinePosition(side, start, end) {
75
+ const line = { start, end };
76
+ return { side, line };
77
+ }
78
+ function createRightSidePosition(thread) {
79
+ if (!isPositiveInteger(thread.line)) {
80
+ warnPrCommentImport(thread.id ?? undefined, 'RIGHT thread is missing line.');
81
+ return null;
82
+ }
83
+ const startLine = thread.startLine !== undefined && thread.startLine !== null ? thread.startLine : null;
84
+ const isSingleLineWithRedundantStart = startLine !== null && startLine === thread.line && thread.startDiffSide == null;
85
+ const hasMultiLineMetadata = thread.startDiffSide !== undefined && thread.startDiffSide !== null
86
+ ? true
87
+ : startLine !== null && !isSingleLineWithRedundantStart;
88
+ if (!hasMultiLineMetadata) {
89
+ return createSingleLinePosition('new', thread.line);
90
+ }
91
+ if (thread.startDiffSide !== 'RIGHT') {
92
+ warnPrCommentImport(thread.id ?? undefined, 'RIGHT thread has mismatched startDiffSide.');
93
+ return null;
94
+ }
95
+ if (!isPositiveInteger(startLine) || startLine > thread.line) {
96
+ warnPrCommentImport(thread.id ?? undefined, 'RIGHT thread has an invalid multi-line range.');
97
+ return null;
98
+ }
99
+ return createMultiLinePosition('new', startLine, thread.line);
100
+ }
101
+ function createLeftSidePosition(thread) {
102
+ if (!isPositiveInteger(thread.originalLine)) {
103
+ warnPrCommentImport(thread.id ?? undefined, 'LEFT thread is missing originalLine.');
104
+ return null;
105
+ }
106
+ const originalStartLine = thread.originalStartLine !== undefined && thread.originalStartLine !== null
107
+ ? thread.originalStartLine
108
+ : null;
109
+ const isSingleLineWithRedundantStart = originalStartLine !== null &&
110
+ originalStartLine === thread.originalLine &&
111
+ thread.startDiffSide == null;
112
+ const hasMultiLineMetadata = thread.startDiffSide !== undefined && thread.startDiffSide !== null
113
+ ? true
114
+ : originalStartLine !== null && !isSingleLineWithRedundantStart;
115
+ if (!hasMultiLineMetadata) {
116
+ return createSingleLinePosition('old', thread.originalLine);
117
+ }
118
+ if (thread.startDiffSide !== 'LEFT') {
119
+ warnPrCommentImport(thread.id ?? undefined, 'LEFT thread has mismatched startDiffSide.');
120
+ return null;
121
+ }
122
+ if (!isPositiveInteger(originalStartLine) || originalStartLine > thread.originalLine) {
123
+ warnPrCommentImport(thread.id ?? undefined, 'LEFT thread has an invalid multi-line range.');
124
+ return null;
125
+ }
126
+ return createMultiLinePosition('old', originalStartLine, thread.originalLine);
127
+ }
128
+ function getThreadPosition(thread) {
129
+ if (thread.diffSide === 'RIGHT') {
130
+ return createRightSidePosition(thread);
131
+ }
132
+ if (thread.diffSide === 'LEFT') {
133
+ return createLeftSidePosition(thread);
134
+ }
135
+ warnPrCommentImport(thread.id ?? undefined, 'Unsupported diffSide.');
136
+ return null;
137
+ }
138
+ function toSortedComments(thread) {
139
+ return [...(thread.comments?.nodes ?? [])].sort((left, right) => (left.createdAt ?? '').localeCompare(right.createdAt ?? ''));
140
+ }
141
+ function toCommentImportsForThread(thread) {
142
+ if (thread.isResolved || thread.isOutdated || thread.subjectType !== 'LINE') {
143
+ return [];
144
+ }
145
+ if (typeof thread.path !== 'string' || thread.path.trim().length === 0) {
146
+ warnPrCommentImport(thread.id ?? undefined, 'Thread is missing path.');
147
+ return [];
148
+ }
149
+ const filePath = thread.path;
150
+ const position = getThreadPosition(thread);
151
+ if (!position) {
152
+ return [];
153
+ }
154
+ const comments = toSortedComments(thread);
155
+ if (comments.length === 0) {
156
+ warnPrCommentImport(thread.id ?? undefined, 'Thread has no comments.');
157
+ return [];
158
+ }
159
+ const rootComment = comments[0];
160
+ if (!rootComment ||
161
+ typeof rootComment.id !== 'string' ||
162
+ typeof rootComment.body !== 'string' ||
163
+ typeof rootComment.createdAt !== 'string' ||
164
+ typeof rootComment.updatedAt !== 'string') {
165
+ warnPrCommentImport(thread.id ?? undefined, 'Thread has a comment with missing fields.');
166
+ return [];
167
+ }
168
+ const commentImports = [
169
+ {
170
+ type: 'thread',
171
+ id: rootComment.id,
172
+ filePath,
173
+ position,
174
+ body: rootComment.body,
175
+ author: rootComment.author?.login ?? undefined,
176
+ createdAt: rootComment.createdAt,
177
+ updatedAt: rootComment.updatedAt,
178
+ },
179
+ ];
180
+ for (const comment of comments.slice(1)) {
181
+ if (typeof comment.id !== 'string' ||
182
+ typeof comment.body !== 'string' ||
183
+ typeof comment.createdAt !== 'string' ||
184
+ typeof comment.updatedAt !== 'string') {
185
+ warnPrCommentImport(thread.id ?? undefined, 'Thread has a comment with missing fields.');
186
+ continue;
187
+ }
188
+ commentImports.push({
189
+ type: 'reply',
190
+ id: comment.id,
191
+ filePath,
192
+ position,
193
+ body: comment.body,
194
+ author: comment.author?.login ?? undefined,
195
+ createdAt: comment.createdAt,
196
+ updatedAt: comment.updatedAt,
197
+ });
198
+ }
199
+ return commentImports;
200
+ }
201
+ export function parseGitHubPrUrl(url) {
202
+ try {
203
+ const urlObj = new URL(url);
204
+ const pathParts = urlObj.pathname.split('/').filter(Boolean);
205
+ if (pathParts.length < 4 || pathParts[2] !== 'pull') {
206
+ return null;
207
+ }
208
+ const owner = pathParts[0];
209
+ const repo = pathParts[1];
210
+ const pullNumber = parseInt(pathParts[3], 10);
211
+ if (isNaN(pullNumber)) {
212
+ return null;
213
+ }
214
+ return { owner, repo, pullNumber, hostname: urlObj.hostname };
215
+ }
216
+ catch {
217
+ return null;
218
+ }
219
+ }
220
+ export function getPrPatch(prArg) {
221
+ try {
222
+ const patch = execFileSync('gh', ['pr', 'diff', prArg], {
223
+ encoding: 'utf8',
224
+ stdio: ['ignore', 'pipe', 'pipe'],
225
+ maxBuffer: GH_MAX_BUFFER,
226
+ });
227
+ if (!patch.trim()) {
228
+ throw new Error('No diff content returned from gh pr diff');
229
+ }
230
+ return patch;
231
+ }
232
+ catch (error) {
233
+ throw formatGhCommandError(error);
234
+ }
235
+ }
236
+ export function parsePrCommentImportsResponse(response) {
237
+ const errors = response.errors?.map((error) => error.message).filter((message) => message) ?? [];
238
+ if (errors.length > 0) {
239
+ throw new Error(errors.join('\n'));
240
+ }
241
+ const reviewThreads = response.data?.repository?.pullRequest?.reviewThreads;
242
+ if (!reviewThreads) {
243
+ return {
244
+ commentImports: [],
245
+ pageInfo: {
246
+ hasNextPage: false,
247
+ endCursor: null,
248
+ },
249
+ };
250
+ }
251
+ return {
252
+ commentImports: (reviewThreads.nodes ?? []).flatMap((thread) => toCommentImportsForThread(thread)),
253
+ pageInfo: {
254
+ hasNextPage: reviewThreads.pageInfo?.hasNextPage === true,
255
+ endCursor: reviewThreads.pageInfo?.endCursor ?? null,
256
+ },
257
+ };
258
+ }
259
+ export function getPrCommentImports(prArg) {
260
+ const pullRequestInfo = parseGitHubPrUrl(prArg);
261
+ if (!pullRequestInfo) {
262
+ throw new Error('Invalid GitHub PR URL');
263
+ }
264
+ const { owner, repo, pullNumber, hostname } = pullRequestInfo;
265
+ const commentImports = [];
266
+ let endCursor = null;
267
+ while (true) {
268
+ try {
269
+ const args = [
270
+ 'api',
271
+ 'graphql',
272
+ '--hostname',
273
+ hostname,
274
+ '-f',
275
+ `query=${PR_REVIEW_THREADS_GRAPHQL_QUERY}`,
276
+ '-F',
277
+ `owner=${owner}`,
278
+ '-F',
279
+ `repo=${repo}`,
280
+ '-F',
281
+ `number=${pullNumber}`,
282
+ ];
283
+ if (endCursor) {
284
+ args.push('-F', `endCursor=${endCursor}`);
285
+ }
286
+ const stdout = execFileSync('gh', args, {
287
+ encoding: 'utf8',
288
+ stdio: ['ignore', 'pipe', 'pipe'],
289
+ maxBuffer: GH_MAX_BUFFER,
290
+ });
291
+ const parsed = JSON.parse(stdout);
292
+ const page = parsePrCommentImportsResponse(parsed);
293
+ commentImports.push(...page.commentImports);
294
+ if (!page.pageInfo.hasNextPage) {
295
+ return Promise.resolve(commentImports);
296
+ }
297
+ if (!page.pageInfo.endCursor) {
298
+ throw new Error('GitHub GraphQL response indicated more pages without endCursor.');
299
+ }
300
+ endCursor = page.pageInfo.endCursor;
301
+ }
302
+ catch (error) {
303
+ if (error instanceof SyntaxError) {
304
+ throw new Error('Invalid JSON returned from gh api graphql');
305
+ }
306
+ if (error instanceof Error && !('stderr' in error)) {
307
+ throw error;
308
+ }
309
+ throw formatGhCommandError(error);
310
+ }
311
+ }
312
+ }
313
+ export function parsePrDetailsResponse(response) {
314
+ const errors = response.errors?.map((error) => error.message).filter((message) => message) ?? [];
315
+ if (errors.length > 0) {
316
+ throw new Error(errors.join('\n'));
317
+ }
318
+ const pullRequest = response.data?.repository?.pullRequest;
319
+ if (!pullRequest) {
320
+ return null;
321
+ }
322
+ return {
323
+ title: pullRequest.title ?? '',
324
+ body: pullRequest.body ?? '',
325
+ };
326
+ }
327
+ export function getPrDetails(prArg) {
328
+ const pullRequestInfo = parseGitHubPrUrl(prArg);
329
+ if (!pullRequestInfo) {
330
+ throw new Error('Invalid GitHub PR URL');
331
+ }
332
+ const { owner, repo, pullNumber, hostname } = pullRequestInfo;
333
+ try {
334
+ const stdout = execFileSync('gh', [
335
+ 'api',
336
+ 'graphql',
337
+ '--hostname',
338
+ hostname,
339
+ '-f',
340
+ `query=${PR_DETAILS_GRAPHQL_QUERY}`,
341
+ '-F',
342
+ `owner=${owner}`,
343
+ '-F',
344
+ `repo=${repo}`,
345
+ '-F',
346
+ `number=${pullNumber}`,
347
+ ], {
348
+ encoding: 'utf8',
349
+ stdio: ['ignore', 'pipe', 'pipe'],
350
+ maxBuffer: GH_MAX_BUFFER,
351
+ });
352
+ const parsed = JSON.parse(stdout);
353
+ return Promise.resolve(parsePrDetailsResponse(parsed));
354
+ }
355
+ catch (error) {
356
+ if (error instanceof SyntaxError) {
357
+ throw new Error('Invalid JSON returned from gh api graphql');
358
+ }
359
+ if (error instanceof Error && !('stderr' in error)) {
360
+ throw error;
361
+ }
362
+ throw formatGhCommandError(error);
363
+ }
364
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};