gh-load-pull-request 0.4.2 → 0.6.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/README.md +6 -18
- package/package.json +2 -2
- package/src/formatters.mjs +245 -0
- package/src/gh-load-pull-request.mjs +452 -379
- package/src/version.mjs +1 -2
package/README.md
CHANGED
|
@@ -36,12 +36,8 @@ gh-load-pull-request owner/private-repo#456
|
|
|
36
36
|
Install globally for system-wide access:
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
# Using bun
|
|
40
39
|
bun install -g gh-load-pull-request
|
|
41
40
|
|
|
42
|
-
# Using npm
|
|
43
|
-
npm install -g gh-load-pull-request
|
|
44
|
-
|
|
45
41
|
# After installation, use anywhere:
|
|
46
42
|
gh-load-pull-request --help
|
|
47
43
|
```
|
|
@@ -51,11 +47,7 @@ gh-load-pull-request --help
|
|
|
51
47
|
Remove the global installation:
|
|
52
48
|
|
|
53
49
|
```bash
|
|
54
|
-
# Using bun
|
|
55
50
|
bun uninstall -g gh-load-pull-request
|
|
56
|
-
|
|
57
|
-
# Using npm
|
|
58
|
-
npm uninstall -g gh-load-pull-request
|
|
59
51
|
```
|
|
60
52
|
|
|
61
53
|
### Local Installation
|
|
@@ -66,7 +58,7 @@ git clone https://github.com/link-foundation/gh-load-pull-request.git
|
|
|
66
58
|
cd gh-load-pull-request
|
|
67
59
|
|
|
68
60
|
# Install dependencies
|
|
69
|
-
|
|
61
|
+
bun install
|
|
70
62
|
|
|
71
63
|
# Make the script executable
|
|
72
64
|
chmod +x gh-load-pull-request.mjs
|
|
@@ -174,7 +166,7 @@ gh-load-pull-request owner/repo#123 | claude-analyze
|
|
|
174
166
|
|
|
175
167
|
## Requirements
|
|
176
168
|
|
|
177
|
-
- [Bun](https://bun.sh/) (>=1.2.0)
|
|
169
|
+
- [Bun](https://bun.sh/) (>=1.2.0) runtime
|
|
178
170
|
- For private repositories (optional):
|
|
179
171
|
- [GitHub CLI](https://cli.github.com/) (recommended) OR
|
|
180
172
|
- GitHub personal access token (via `--token` or `GITHUB_TOKEN` env var)
|
|
@@ -191,11 +183,7 @@ gh-load-pull-request owner/repo#123 | claude-analyze
|
|
|
191
183
|
|
|
192
184
|
```bash
|
|
193
185
|
# Run all tests
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
# Or run test files directly
|
|
197
|
-
node tests/all.test.mjs
|
|
198
|
-
node tests/cli.test.mjs
|
|
186
|
+
bun test
|
|
199
187
|
```
|
|
200
188
|
|
|
201
189
|
## Development
|
|
@@ -206,7 +194,7 @@ git clone https://github.com/link-foundation/gh-load-pull-request.git
|
|
|
206
194
|
cd gh-load-pull-request
|
|
207
195
|
|
|
208
196
|
# Install dependencies
|
|
209
|
-
|
|
197
|
+
bun install
|
|
210
198
|
|
|
211
199
|
# Make executable
|
|
212
200
|
chmod +x gh-load-pull-request.mjs
|
|
@@ -215,10 +203,10 @@ chmod +x gh-load-pull-request.mjs
|
|
|
215
203
|
./gh-load-pull-request.mjs owner/repo#123
|
|
216
204
|
|
|
217
205
|
# Run tests
|
|
218
|
-
|
|
206
|
+
bun test
|
|
219
207
|
|
|
220
208
|
# Run linting
|
|
221
|
-
|
|
209
|
+
bun run lint
|
|
222
210
|
|
|
223
211
|
# Bump version
|
|
224
212
|
./version.mjs patch # or minor, major
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gh-load-pull-request",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Download GitHub pull request and convert it to markdown",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/gh-load-pull-request.mjs",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"homepage": "https://github.com/link-foundation/gh-load-pull-request#readme",
|
|
46
46
|
"engines": {
|
|
47
|
-
"
|
|
47
|
+
"bun": ">=1.2.0"
|
|
48
48
|
},
|
|
49
49
|
"files": [
|
|
50
50
|
"src/",
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatters for gh-load-pull-request
|
|
3
|
+
* Contains functions for converting PR data to markdown and JSON formats
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Format a date string for display
|
|
8
|
+
* @param {string} dateStr - ISO date string
|
|
9
|
+
* @returns {string} Formatted date string
|
|
10
|
+
*/
|
|
11
|
+
export function formatDate(dateStr) {
|
|
12
|
+
if (!dateStr) {
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
const date = new Date(dateStr);
|
|
16
|
+
return date
|
|
17
|
+
.toISOString()
|
|
18
|
+
.replace('T', ' ')
|
|
19
|
+
.replace(/\.\d+Z$/, ' UTC');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Convert PR data to JSON format
|
|
24
|
+
* @param {Object} data - PR data from loadPullRequest
|
|
25
|
+
* @param {Array} downloadedImages - Array of downloaded image info
|
|
26
|
+
* @returns {string} JSON string
|
|
27
|
+
*/
|
|
28
|
+
export function convertToJson(data, downloadedImages = []) {
|
|
29
|
+
const { pr, files, comments, reviewComments, reviews, commits } = data;
|
|
30
|
+
|
|
31
|
+
return JSON.stringify(
|
|
32
|
+
{
|
|
33
|
+
pullRequest: {
|
|
34
|
+
number: pr.number,
|
|
35
|
+
title: pr.title,
|
|
36
|
+
state: pr.state,
|
|
37
|
+
draft: pr.draft,
|
|
38
|
+
merged: pr.merged,
|
|
39
|
+
url: pr.html_url,
|
|
40
|
+
author: {
|
|
41
|
+
login: pr.user.login,
|
|
42
|
+
url: `https://github.com/${pr.user.login}`,
|
|
43
|
+
},
|
|
44
|
+
createdAt: pr.created_at,
|
|
45
|
+
updatedAt: pr.updated_at,
|
|
46
|
+
mergedAt: pr.merged_at,
|
|
47
|
+
closedAt: pr.closed_at,
|
|
48
|
+
mergedBy: pr.merged_by
|
|
49
|
+
? {
|
|
50
|
+
login: pr.merged_by.login,
|
|
51
|
+
url: `https://github.com/${pr.merged_by.login}`,
|
|
52
|
+
}
|
|
53
|
+
: null,
|
|
54
|
+
base: {
|
|
55
|
+
ref: pr.base.ref,
|
|
56
|
+
sha: pr.base.sha,
|
|
57
|
+
},
|
|
58
|
+
head: {
|
|
59
|
+
ref: pr.head.ref,
|
|
60
|
+
sha: pr.head.sha,
|
|
61
|
+
},
|
|
62
|
+
additions: pr.additions,
|
|
63
|
+
deletions: pr.deletions,
|
|
64
|
+
changedFiles: pr.changed_files,
|
|
65
|
+
labels: pr.labels?.map((l) => ({ name: l.name, color: l.color })) || [],
|
|
66
|
+
assignees:
|
|
67
|
+
pr.assignees?.map((a) => ({
|
|
68
|
+
login: a.login,
|
|
69
|
+
url: `https://github.com/${a.login}`,
|
|
70
|
+
})) || [],
|
|
71
|
+
requestedReviewers:
|
|
72
|
+
pr.requested_reviewers?.map((r) => ({
|
|
73
|
+
login: r.login,
|
|
74
|
+
url: `https://github.com/${r.login}`,
|
|
75
|
+
})) || [],
|
|
76
|
+
milestone: pr.milestone
|
|
77
|
+
? { title: pr.milestone.title, number: pr.milestone.number }
|
|
78
|
+
: null,
|
|
79
|
+
body: pr.body,
|
|
80
|
+
},
|
|
81
|
+
commits: commits.map((c) => ({
|
|
82
|
+
sha: c.sha,
|
|
83
|
+
message: c.commit.message,
|
|
84
|
+
author: c.author?.login || c.commit.author?.name || 'unknown',
|
|
85
|
+
url: c.html_url,
|
|
86
|
+
date: c.commit.author?.date,
|
|
87
|
+
})),
|
|
88
|
+
files: files.map((f) => ({
|
|
89
|
+
filename: f.filename,
|
|
90
|
+
status: f.status,
|
|
91
|
+
additions: f.additions,
|
|
92
|
+
deletions: f.deletions,
|
|
93
|
+
previousFilename: f.previous_filename,
|
|
94
|
+
patch: f.patch,
|
|
95
|
+
})),
|
|
96
|
+
reviews: reviews.map((r) => ({
|
|
97
|
+
id: r.id,
|
|
98
|
+
author: r.user.login,
|
|
99
|
+
state: r.state,
|
|
100
|
+
body: r.body,
|
|
101
|
+
submittedAt: r.submitted_at,
|
|
102
|
+
})),
|
|
103
|
+
reviewComments: reviewComments.map((c) => ({
|
|
104
|
+
id: c.id,
|
|
105
|
+
author: c.user.login,
|
|
106
|
+
body: c.body,
|
|
107
|
+
path: c.path,
|
|
108
|
+
line: c.line,
|
|
109
|
+
createdAt: c.created_at,
|
|
110
|
+
diffHunk: c.diff_hunk,
|
|
111
|
+
reviewId: c.pull_request_review_id,
|
|
112
|
+
})),
|
|
113
|
+
comments: comments.map((c) => ({
|
|
114
|
+
id: c.id,
|
|
115
|
+
author: c.user.login,
|
|
116
|
+
body: c.body,
|
|
117
|
+
createdAt: c.created_at,
|
|
118
|
+
})),
|
|
119
|
+
downloadedImages: downloadedImages.map((img) => ({
|
|
120
|
+
originalUrl: img.originalUrl,
|
|
121
|
+
localPath: img.relativePath,
|
|
122
|
+
format: img.format,
|
|
123
|
+
})),
|
|
124
|
+
},
|
|
125
|
+
null,
|
|
126
|
+
2
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Generate markdown for metadata section
|
|
132
|
+
* @param {Object} pr - Pull request data
|
|
133
|
+
* @returns {string} Markdown content
|
|
134
|
+
*/
|
|
135
|
+
export function generateMetadataMarkdown(pr) {
|
|
136
|
+
let markdown = `## Metadata\n\n`;
|
|
137
|
+
|
|
138
|
+
markdown += `| Field | Value |\n`;
|
|
139
|
+
markdown += `|-------|-------|\n`;
|
|
140
|
+
markdown += `| **Number** | #${pr.number} |\n`;
|
|
141
|
+
markdown += `| **URL** | ${pr.html_url} |\n`;
|
|
142
|
+
markdown += `| **Author** | [@${pr.user.login}](https://github.com/${pr.user.login}) |\n`;
|
|
143
|
+
markdown += `| **State** | ${pr.state}${pr.merged ? ' (merged)' : pr.draft ? ' (draft)' : ''} |\n`;
|
|
144
|
+
markdown += `| **Created** | ${formatDate(pr.created_at)} |\n`;
|
|
145
|
+
markdown += `| **Updated** | ${formatDate(pr.updated_at)} |\n`;
|
|
146
|
+
|
|
147
|
+
if (pr.merged_at) {
|
|
148
|
+
markdown += `| **Merged** | ${formatDate(pr.merged_at)} |\n`;
|
|
149
|
+
if (pr.merged_by) {
|
|
150
|
+
markdown += `| **Merged by** | [@${pr.merged_by.login}](https://github.com/${pr.merged_by.login}) |\n`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (pr.closed_at && !pr.merged_at) {
|
|
154
|
+
markdown += `| **Closed** | ${formatDate(pr.closed_at)} |\n`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
markdown += `| **Base** | \`${pr.base.ref}\` |\n`;
|
|
158
|
+
markdown += `| **Head** | \`${pr.head.ref}\` |\n`;
|
|
159
|
+
markdown += `| **Additions** | +${pr.additions} |\n`;
|
|
160
|
+
markdown += `| **Deletions** | -${pr.deletions} |\n`;
|
|
161
|
+
markdown += `| **Changed Files** | ${pr.changed_files} |\n`;
|
|
162
|
+
markdown += '\n';
|
|
163
|
+
|
|
164
|
+
if (pr.labels && pr.labels.length > 0) {
|
|
165
|
+
markdown += `**Labels:** ${pr.labels.map((l) => `\`${l.name}\``).join(', ')}\n\n`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (pr.assignees && pr.assignees.length > 0) {
|
|
169
|
+
markdown += `**Assignees:** ${pr.assignees.map((a) => `[@${a.login}](https://github.com/${a.login})`).join(', ')}\n\n`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (pr.requested_reviewers && pr.requested_reviewers.length > 0) {
|
|
173
|
+
markdown += `**Requested Reviewers:** ${pr.requested_reviewers.map((r) => `[@${r.login}](https://github.com/${r.login})`).join(', ')}\n\n`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (pr.milestone) {
|
|
177
|
+
markdown += `**Milestone:** ${pr.milestone.title}\n\n`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return markdown;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Generate markdown for commits section
|
|
185
|
+
* @param {Array} commits - Array of commit data
|
|
186
|
+
* @returns {string} Markdown content
|
|
187
|
+
*/
|
|
188
|
+
export function generateCommitsMarkdown(commits) {
|
|
189
|
+
if (commits.length === 0) {
|
|
190
|
+
return '';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let markdown = `## Commits (${commits.length})\n\n`;
|
|
194
|
+
|
|
195
|
+
for (const commit of commits) {
|
|
196
|
+
const message = commit.commit.message.split('\n')[0];
|
|
197
|
+
const sha = commit.sha.substring(0, 7);
|
|
198
|
+
const author =
|
|
199
|
+
commit.author?.login || commit.commit.author?.name || 'unknown';
|
|
200
|
+
const authorLink = commit.author
|
|
201
|
+
? `[@${author}](https://github.com/${author})`
|
|
202
|
+
: author;
|
|
203
|
+
markdown += `- [\`${sha}\`](${commit.html_url}) ${message} — ${authorLink}\n`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
markdown += '\n';
|
|
207
|
+
return markdown;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Generate markdown for files changed section
|
|
212
|
+
* @param {Array} files - Array of file data
|
|
213
|
+
* @returns {string} Markdown content
|
|
214
|
+
*/
|
|
215
|
+
export function generateFilesMarkdown(files) {
|
|
216
|
+
if (files.length === 0) {
|
|
217
|
+
return '';
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let markdown = `## Files Changed (${files.length})\n\n`;
|
|
221
|
+
markdown += `| Status | File | Changes |\n`;
|
|
222
|
+
markdown += `|--------|------|--------:|\n`;
|
|
223
|
+
|
|
224
|
+
for (const file of files) {
|
|
225
|
+
const statusIcon =
|
|
226
|
+
file.status === 'added'
|
|
227
|
+
? '🆕 Added'
|
|
228
|
+
: file.status === 'removed'
|
|
229
|
+
? '🗑️ Removed'
|
|
230
|
+
: file.status === 'modified'
|
|
231
|
+
? '✏️ Modified'
|
|
232
|
+
: file.status === 'renamed'
|
|
233
|
+
? '📝 Renamed'
|
|
234
|
+
: `📄 ${file.status}`;
|
|
235
|
+
const changes = `+${file.additions} -${file.deletions}`;
|
|
236
|
+
let filename = file.filename;
|
|
237
|
+
if (file.status === 'renamed' && file.previous_filename) {
|
|
238
|
+
filename = `${file.previous_filename} → ${file.filename}`;
|
|
239
|
+
}
|
|
240
|
+
markdown += `| ${statusIcon} | \`${filename}\` | ${changes} |\n`;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
markdown += '\n';
|
|
244
|
+
return markdown;
|
|
245
|
+
}
|