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.
- package/LICENSE +22 -0
- package/README.md +142 -0
- package/dist/cli/cmux.d.ts +9 -0
- package/dist/cli/cmux.js +274 -0
- package/dist/cli/comment.d.ts +2 -0
- package/dist/cli/comment.js +91 -0
- package/dist/cli/github.d.ts +84 -0
- package/dist/cli/github.js +364 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +397 -0
- package/dist/cli/turns.d.ts +14 -0
- package/dist/cli/turns.js +333 -0
- package/dist/cli/utils.d.ts +27 -0
- package/dist/cli/utils.js +204 -0
- package/dist/client/assets/arc-CHYjdFTk.js +1 -0
- package/dist/client/assets/architecture-7EHR7CIX-weHBYDgM.js +1 -0
- package/dist/client/assets/architectureDiagram-3BPJPVTR-DRa2msuJ.js +36 -0
- package/dist/client/assets/array-BifhSqXX.js +1 -0
- package/dist/client/assets/blockDiagram-GPEHLZMM-BTdKsXy5.js +132 -0
- package/dist/client/assets/c4Diagram-AAUBKEIU-CwTvCkOg.js +10 -0
- package/dist/client/assets/channel-Cv40nQW1.js +1 -0
- package/dist/client/assets/chunk-2J33WTMH-BFsjrTtc.js +1 -0
- package/dist/client/assets/chunk-3OPIFGDE-V_xVw1dq.js +62 -0
- package/dist/client/assets/chunk-4BX2VUAB-DoLO6LLy.js +1 -0
- package/dist/client/assets/chunk-55IACEB6-CHgBwbdB.js +1 -0
- package/dist/client/assets/chunk-5ZQYHXKU-DMixAgQG.js +2 -0
- package/dist/client/assets/chunk-727SXJPM-C-3FZTmm.js +206 -0
- package/dist/client/assets/chunk-AQP2D5EJ-DpQZhXt-.js +231 -0
- package/dist/client/assets/chunk-BSJP7CBP-BnO9E_G4.js +1 -0
- package/dist/client/assets/chunk-CSCIHK7Q-BxItphyO.js +124 -0
- package/dist/client/assets/chunk-FMBD7UC4-4hEcfvfT.js +15 -0
- package/dist/client/assets/chunk-KSCS5N6A-DnArkpOI.js +10 -0
- package/dist/client/assets/chunk-L5ZTLDWV-DQoYJ1BW.js +1 -0
- package/dist/client/assets/chunk-LZXEDZCA-CdCevBX-.js +2 -0
- package/dist/client/assets/chunk-ND2GUHAM-Dt89U_nU.js +1 -0
- package/dist/client/assets/chunk-NNHCCRGN-DlpIbxXb.js +159 -0
- package/dist/client/assets/chunk-NZK2D7GU-C2Ev_f_w.js +1 -0
- package/dist/client/assets/chunk-O5CBEL6O-BeZ3MFnJ.js +70 -0
- package/dist/client/assets/chunk-QZHKN3VN-BmDOSWHp.js +1 -0
- package/dist/client/assets/chunk-WU5MYG2G-gLuInTk4.js +1 -0
- package/dist/client/assets/chunk-XPW4576I-DPbDc8_T.js +32 -0
- package/dist/client/assets/classDiagram-4FO5ZUOK-DOpVKUrb.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DOpVKUrb.js +1 -0
- package/dist/client/assets/cose-bilkent-S5V4N54A-CCPyalms.js +1 -0
- package/dist/client/assets/cytoscape.esm-FqbQrHcz.js +321 -0
- package/dist/client/assets/dagre-BM42HDAG-6jrYMjX3.js +4 -0
- package/dist/client/assets/dagre-Bx709z4p.js +1 -0
- package/dist/client/assets/defaultLocale-C8Fc0cco.js +1 -0
- package/dist/client/assets/diagram-2AECGRRQ-dCQ_KoXl.js +43 -0
- package/dist/client/assets/diagram-5GNKFQAL-B3Xj4-XE.js +10 -0
- package/dist/client/assets/diagram-KO2AKTUF-DaGgF2yL.js +3 -0
- package/dist/client/assets/diagram-LMA3HP47-CF_c4tPT.js +24 -0
- package/dist/client/assets/diagram-OG6HWLK6-BgBHbJkT.js +24 -0
- package/dist/client/assets/dist-7wVkzZne.js +1 -0
- package/dist/client/assets/erDiagram-TEJ5UH35-BZgX1grR.js +85 -0
- package/dist/client/assets/eventmodeling-FCH6USID-BaI6RNKr.js +1 -0
- package/dist/client/assets/flowDiagram-I6XJVG4X-BFNUa12V.js +162 -0
- package/dist/client/assets/ganttDiagram-6RSMTGT7-C8t6BThI.js +292 -0
- package/dist/client/assets/gitGraph-WXDBUCRP-BlicfDkM.js +1 -0
- package/dist/client/assets/gitGraphDiagram-PVQCEYII-BmnIXuXP.js +106 -0
- package/dist/client/assets/graphlib-B8gBHxth.js +1 -0
- package/dist/client/assets/index-BfVtbKTZ.css +2 -0
- package/dist/client/assets/index-p8zzfjdu.js +87 -0
- package/dist/client/assets/info-J43DQDTF-DHLG38N3.js +1 -0
- package/dist/client/assets/infoDiagram-5YYISTIA-oQ-lq-45.js +2 -0
- package/dist/client/assets/init-D6jRqBbL.js +1 -0
- package/dist/client/assets/ishikawaDiagram-YF4QCWOH-DrODlBLD.js +70 -0
- package/dist/client/assets/journeyDiagram-JHISSGLW-Bxg5wCQz.js +139 -0
- package/dist/client/assets/kanban-definition-UN3LZRKU-DAdvUDIB.js +89 -0
- package/dist/client/assets/katex-Vhh-h91d.js +257 -0
- package/dist/client/assets/line-D8kNd46u.js +1 -0
- package/dist/client/assets/linear-jwGZSrg4.js +1 -0
- package/dist/client/assets/mermaid-parser.core-PVPfUfyU.js +4 -0
- package/dist/client/assets/mermaid.core-DwdP6CFg.js +9 -0
- package/dist/client/assets/mindmap-definition-RKZ34NQL-DeOoL56Q.js +96 -0
- package/dist/client/assets/ordinal-hYBb2elL.js +1 -0
- package/dist/client/assets/packet-YPE3B663-BBEaxdJP.js +1 -0
- package/dist/client/assets/path-BWPyau1x.js +1 -0
- package/dist/client/assets/pie-LRSECV5Y-fvTRFm-2.js +1 -0
- package/dist/client/assets/pieDiagram-4H26LBE5-oep1px2r.js +30 -0
- package/dist/client/assets/prism-bash-D6zCJ74D.js +1 -0
- package/dist/client/assets/prism-clojure-_z-zoNcE.js +1 -0
- package/dist/client/assets/prism-csharp-XPn2bw0d.js +1 -0
- package/dist/client/assets/prism-dart-CDv16Fbu.js +1 -0
- package/dist/client/assets/prism-elixir-DhHhV5Rj.js +1 -0
- package/dist/client/assets/prism-haskell-5hq1Nh9c.js +1 -0
- package/dist/client/assets/prism-hcl-B6Z0Ouoo.js +1 -0
- package/dist/client/assets/prism-java-6wtusvIl.js +1 -0
- package/dist/client/assets/prism-markup-templating-KkSMBj6Y.js +1 -0
- package/dist/client/assets/prism-nix-C88dTX93.js +1 -0
- package/dist/client/assets/prism-perl-Lsbi34sH.js +1 -0
- package/dist/client/assets/prism-php-Co4fM40K.js +1 -0
- package/dist/client/assets/prism-protobuf-yzSHlhwy.js +1 -0
- package/dist/client/assets/prism-ruby-DFCASkuD.js +1 -0
- package/dist/client/assets/prism-scala-VkM3wCOL.js +1 -0
- package/dist/client/assets/prism-solidity-DTom4k5p.js +1 -0
- package/dist/client/assets/prism-sql-CYhVDMEE.js +1 -0
- package/dist/client/assets/prism-vim-BgpkjCK5.js +1 -0
- package/dist/client/assets/quadrantDiagram-W4KKPZXB-D9l66CHb.js +7 -0
- package/dist/client/assets/radar-GUYGQ44K-DAU5EPVd.js +1 -0
- package/dist/client/assets/requirementDiagram-4Y6WPE33-helpQlwb.js +84 -0
- package/dist/client/assets/rough.esm-CSKSodPl.js +1 -0
- package/dist/client/assets/sankeyDiagram-5OEKKPKP-C9sNuuvA.js +40 -0
- package/dist/client/assets/sequenceDiagram-3UESZ5HK-BP0bhCwQ.js +162 -0
- package/dist/client/assets/src-CDAPGf2l.js +1 -0
- package/dist/client/assets/stateDiagram-AJRCARHV-CsFizpla.js +1 -0
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Bvn8tanl.js +1 -0
- package/dist/client/assets/timeline-definition-PNZ67QCA-BnkmHs1F.js +120 -0
- package/dist/client/assets/treeView-BLDUP644-DD9HZO5W.js +1 -0
- package/dist/client/assets/treemap-LRROVOQU-BBKuQ1lo.js +1 -0
- package/dist/client/assets/vennDiagram-CIIHVFJN-BPFTHflF.js +34 -0
- package/dist/client/assets/wardley-L42UT6IY-y88gDkwb.js +1 -0
- package/dist/client/assets/wardleyDiagram-YWT4CUSO-BlcA7IVh.js +78 -0
- package/dist/client/assets/xychartDiagram-2RQKCTM6-DX5vUCRH.js +7 -0
- package/dist/client/favicon.png +0 -0
- package/dist/client/index.html +14 -0
- package/dist/client/logo.png +0 -0
- package/dist/client/site-data/blobs/080c0e6/cHVibGljL3NpdGUtZGF0YS9vZy1pbWFnZS5wbmc.png +0 -0
- package/dist/client/site-data/blobs/55f23a1/bGFuZGluZy9wdWJsaWMvZGlmaXQvbG9nby5wbmc.png +0 -0
- package/dist/client/site-data/blobs/66ff7c6/cHVibGljL2xvZ28ucG5n.png +0 -0
- package/dist/client/site-data/blobs/e6977fe/cHVibGljL2xvZ28ucG5n.png +0 -0
- package/dist/client/site-data/manifest.json +1 -0
- package/dist/client/site-data/og-image.png +0 -0
- package/dist/client/site-data/snapshots/55f23a1...080c0e6.json +1 -0
- package/dist/client/site-data/snapshots/66ff7c6...e6977fe.json +1 -0
- package/dist/client/site-data/snapshots/7d40fd4...a72112f-comments.json +1 -0
- package/dist/client/site-data/snapshots/7d40fd4...a72112f.json +1 -0
- package/dist/server/file-watcher.d.ts +33 -0
- package/dist/server/file-watcher.js +340 -0
- package/dist/server/generated-file-check.d.ts +17 -0
- package/dist/server/generated-file-check.js +280 -0
- package/dist/server/git-diff.d.ts +94 -0
- package/dist/server/git-diff.js +909 -0
- package/dist/server/grouping/cache.d.ts +3 -0
- package/dist/server/grouping/cache.js +22 -0
- package/dist/server/grouping/claudeCli.d.ts +10 -0
- package/dist/server/grouping/claudeCli.js +95 -0
- package/dist/server/grouping/digest.d.ts +15 -0
- package/dist/server/grouping/digest.js +38 -0
- package/dist/server/grouping/reconcile.d.ts +6 -0
- package/dist/server/grouping/reconcile.js +34 -0
- package/dist/server/grouping/types.d.ts +9 -0
- package/dist/server/grouping/types.js +1 -0
- package/dist/server/server.d.ts +37 -0
- package/dist/server/server.js +1283 -0
- package/dist/shared/grouping.d.ts +4 -0
- package/dist/shared/grouping.js +170 -0
- package/dist/types/diff.d.ts +263 -0
- package/dist/types/diff.js +1 -0
- package/dist/types/watch.d.ts +59 -0
- package/dist/types/watch.js +8 -0
- package/dist/utils/commentFormatting.d.ts +6 -0
- package/dist/utils/commentFormatting.js +90 -0
- package/dist/utils/commentImports.d.ts +11 -0
- package/dist/utils/commentImports.js +386 -0
- package/dist/utils/createId.d.ts +1 -0
- package/dist/utils/createId.js +5 -0
- package/dist/utils/diffMode.d.ts +3 -0
- package/dist/utils/diffMode.js +13 -0
- package/dist/utils/diffSelection.d.ts +13 -0
- package/dist/utils/diffSelection.js +39 -0
- package/dist/utils/editorOptions.d.ts +60 -0
- package/dist/utils/editorOptions.js +171 -0
- package/dist/utils/fileUtils.d.ts +12 -0
- package/dist/utils/fileUtils.js +21 -0
- package/dist/utils/hubConfig.d.ts +32 -0
- package/dist/utils/hubConfig.js +51 -0
- package/dist/utils/suggestionUtils.d.ts +19 -0
- package/dist/utils/suggestionUtils.js +31 -0
- package/dist/utils/turnLog.d.ts +47 -0
- package/dist/utils/turnLog.js +133 -0
- 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
|
+
}
|