@grunnverk/github-tools 1.0.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 +190 -0
- package/README.md +314 -0
- package/dist/errors.js +124 -0
- package/dist/errors.js.map +1 -0
- package/dist/github.js +1406 -0
- package/dist/github.js.map +1 -0
- package/dist/index.d.ts +230 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/issues.js +332 -0
- package/dist/issues.js.map +1 -0
- package/dist/logger.js +15 -0
- package/dist/logger.js.map +1 -0
- package/dist/releaseNotes.js +90 -0
- package/dist/releaseNotes.js.map +1 -0
- package/guide/index.md +78 -0
- package/package.json +80 -0
package/dist/issues.js
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { getLogger } from './logger.js';
|
|
2
|
+
import { getOpenIssues, createIssue } from './github.js';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { spawnSync } from 'child_process';
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
|
|
8
|
+
// Get GitHub issues content
|
|
9
|
+
const get = async (options = {})=>{
|
|
10
|
+
const logger = getLogger();
|
|
11
|
+
const { limit = 20 } = options;
|
|
12
|
+
try {
|
|
13
|
+
logger.debug('Fetching open GitHub issues...');
|
|
14
|
+
const issuesLimit = Math.min(limit, 20); // Cap at 20
|
|
15
|
+
const githubIssues = await getOpenIssues(issuesLimit);
|
|
16
|
+
if (githubIssues.trim()) {
|
|
17
|
+
logger.debug(`Added GitHub issues to context (${githubIssues.length} characters)`);
|
|
18
|
+
return githubIssues;
|
|
19
|
+
} else {
|
|
20
|
+
logger.debug('No open GitHub issues found');
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.warn(`Failed to fetch GitHub issues: ${error.message}`);
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
// Helper function to get user choice interactively
|
|
29
|
+
async function getUserChoice(_prompt, _choices) {
|
|
30
|
+
var _choices_;
|
|
31
|
+
// Placeholder - always returns first choice
|
|
32
|
+
return ((_choices_ = _choices[0]) === null || _choices_ === void 0 ? void 0 : _choices_.key) || '';
|
|
33
|
+
}
|
|
34
|
+
// Helper function to serialize issue to structured text format
|
|
35
|
+
function serializeIssue(issue) {
|
|
36
|
+
const lines = [
|
|
37
|
+
'# Issue Editor',
|
|
38
|
+
'',
|
|
39
|
+
'# Edit the issue details below. Lines starting with "#" are comments and will be ignored.',
|
|
40
|
+
'# Valid priorities: low, medium, high',
|
|
41
|
+
'# Valid categories: ui, content, functionality, accessibility, performance, other',
|
|
42
|
+
'# Suggestions should be one per line, preceded by a "-" or "•"',
|
|
43
|
+
'',
|
|
44
|
+
`Title: ${issue.title}`,
|
|
45
|
+
'',
|
|
46
|
+
`Priority: ${issue.priority}`,
|
|
47
|
+
'',
|
|
48
|
+
`Category: ${issue.category}`,
|
|
49
|
+
'',
|
|
50
|
+
'Description:',
|
|
51
|
+
issue.description,
|
|
52
|
+
'',
|
|
53
|
+
'Suggestions:'
|
|
54
|
+
];
|
|
55
|
+
if (issue.suggestions && issue.suggestions.length > 0) {
|
|
56
|
+
issue.suggestions.forEach((suggestion)=>{
|
|
57
|
+
lines.push(`- ${suggestion}`);
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
lines.push('# Add suggestions here, one per line with "-" or "•"');
|
|
61
|
+
}
|
|
62
|
+
return lines.join('\n');
|
|
63
|
+
}
|
|
64
|
+
// Helper function to deserialize issue from structured text format
|
|
65
|
+
function deserializeIssue(content) {
|
|
66
|
+
const lines = content.split('\n');
|
|
67
|
+
// Parse the structured format
|
|
68
|
+
let title = '';
|
|
69
|
+
let priority = 'medium';
|
|
70
|
+
let category = 'other';
|
|
71
|
+
let description = '';
|
|
72
|
+
const suggestions = [];
|
|
73
|
+
let currentSection = '';
|
|
74
|
+
let descriptionLines = [];
|
|
75
|
+
for(let i = 0; i < lines.length; i++){
|
|
76
|
+
const line = lines[i].trim();
|
|
77
|
+
// Skip comment lines
|
|
78
|
+
if (line.startsWith('#')) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
// Parse field lines
|
|
82
|
+
if (line.startsWith('Title:')) {
|
|
83
|
+
title = line.substring(6).trim();
|
|
84
|
+
} else if (line.startsWith('Priority:')) {
|
|
85
|
+
const priorityValue = line.substring(9).trim().toLowerCase();
|
|
86
|
+
if (priorityValue === 'low' || priorityValue === 'medium' || priorityValue === 'high') {
|
|
87
|
+
priority = priorityValue;
|
|
88
|
+
}
|
|
89
|
+
} else if (line.startsWith('Category:')) {
|
|
90
|
+
const categoryValue = line.substring(9).trim().toLowerCase();
|
|
91
|
+
if ([
|
|
92
|
+
'ui',
|
|
93
|
+
'content',
|
|
94
|
+
'functionality',
|
|
95
|
+
'accessibility',
|
|
96
|
+
'performance',
|
|
97
|
+
'other'
|
|
98
|
+
].includes(categoryValue)) {
|
|
99
|
+
category = categoryValue;
|
|
100
|
+
}
|
|
101
|
+
} else if (line === 'Description:') {
|
|
102
|
+
currentSection = 'description';
|
|
103
|
+
descriptionLines = [];
|
|
104
|
+
} else if (line === 'Suggestions:') {
|
|
105
|
+
currentSection = 'suggestions';
|
|
106
|
+
// Process accumulated description lines
|
|
107
|
+
description = descriptionLines.join('\n').trim();
|
|
108
|
+
} else if (currentSection === 'description' && line !== '') {
|
|
109
|
+
descriptionLines.push(lines[i]); // Keep original line with spacing
|
|
110
|
+
} else if (currentSection === 'suggestions' && line !== '') {
|
|
111
|
+
// Parse suggestion line
|
|
112
|
+
const suggestionLine = line.replace(/^[-•]\s*/, '').trim();
|
|
113
|
+
if (suggestionLine) {
|
|
114
|
+
suggestions.push(suggestionLine);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// If we didn't encounter suggestions section, description might still be accumulating
|
|
119
|
+
if (currentSection === 'description') {
|
|
120
|
+
description = descriptionLines.join('\n').trim();
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
title: title || 'Untitled Issue',
|
|
124
|
+
priority,
|
|
125
|
+
category,
|
|
126
|
+
description: description || 'No description provided',
|
|
127
|
+
suggestions: suggestions.length > 0 ? suggestions : undefined
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// Helper function to edit issue using editor
|
|
131
|
+
async function editIssueInteractively(issue) {
|
|
132
|
+
const logger = getLogger();
|
|
133
|
+
const editor = process.env.EDITOR || process.env.VISUAL || 'vi';
|
|
134
|
+
// Create a temporary file for the user to edit
|
|
135
|
+
const tmpDir = os.tmpdir();
|
|
136
|
+
const tmpFilePath = path.join(tmpDir, `kodrdriv_issue_${Date.now()}.txt`);
|
|
137
|
+
// Serialize the issue to structured text format
|
|
138
|
+
const issueContent = serializeIssue(issue);
|
|
139
|
+
await fs.writeFile(tmpFilePath, issueContent, 'utf8');
|
|
140
|
+
logger.info(`📝 Opening ${editor} to edit issue...`);
|
|
141
|
+
// Open the editor synchronously so execution resumes after the user closes it
|
|
142
|
+
const result = spawnSync(editor, [
|
|
143
|
+
tmpFilePath
|
|
144
|
+
], {
|
|
145
|
+
stdio: 'inherit'
|
|
146
|
+
});
|
|
147
|
+
if (result.error) {
|
|
148
|
+
throw new Error(`Failed to launch editor '${editor}': ${result.error.message}`);
|
|
149
|
+
}
|
|
150
|
+
// Read the file back and deserialize it
|
|
151
|
+
const editedContent = await fs.readFile(tmpFilePath, 'utf8');
|
|
152
|
+
// Clean up the temporary file with proper error handling
|
|
153
|
+
try {
|
|
154
|
+
await fs.unlink(tmpFilePath);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
// Only log if it's not a "file not found" error
|
|
157
|
+
if (error.code !== 'ENOENT') {
|
|
158
|
+
logger.warn(`Failed to cleanup temporary file ${tmpFilePath}: ${error.message}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Deserialize the edited content back to an Issue object
|
|
162
|
+
const editedIssue = deserializeIssue(editedContent);
|
|
163
|
+
logger.info('✅ Issue updated successfully');
|
|
164
|
+
logger.debug(`Updated issue: ${JSON.stringify(editedIssue, null, 2)}`);
|
|
165
|
+
return editedIssue;
|
|
166
|
+
}
|
|
167
|
+
// Helper function to format issue body for GitHub
|
|
168
|
+
function formatIssueBody(issue) {
|
|
169
|
+
let body = `## Description\n\n${issue.description}\n\n`;
|
|
170
|
+
body += `## Details\n\n`;
|
|
171
|
+
body += `- **Priority:** ${issue.priority}\n`;
|
|
172
|
+
body += `- **Category:** ${issue.category}\n`;
|
|
173
|
+
body += `- **Source:** Review\n\n`;
|
|
174
|
+
if (issue.suggestions && issue.suggestions.length > 0) {
|
|
175
|
+
body += `## Suggestions\n\n`;
|
|
176
|
+
issue.suggestions.forEach((suggestion)=>{
|
|
177
|
+
body += `- ${suggestion}\n`;
|
|
178
|
+
});
|
|
179
|
+
body += '\n';
|
|
180
|
+
}
|
|
181
|
+
body += `---\n\n`;
|
|
182
|
+
body += `*This issue was automatically created from a review session.*`;
|
|
183
|
+
return body;
|
|
184
|
+
}
|
|
185
|
+
// Helper function to format results with created GitHub issues
|
|
186
|
+
function formatReviewResultsWithIssues(result, createdIssues) {
|
|
187
|
+
let output = `📝 Review Results\n\n`;
|
|
188
|
+
output += `📋 Summary: ${result.summary}\n`;
|
|
189
|
+
output += `📊 Total Issues Found: ${result.totalIssues}\n`;
|
|
190
|
+
output += `🚀 GitHub Issues Created: ${createdIssues.length}\n\n`;
|
|
191
|
+
if (result.issues && result.issues.length > 0) {
|
|
192
|
+
output += `📝 Issues Identified:\n\n`;
|
|
193
|
+
result.issues.forEach((issue, index)=>{
|
|
194
|
+
const priorityEmoji = issue.priority === 'high' ? '🔴' : issue.priority === 'medium' ? '🟡' : '🟢';
|
|
195
|
+
const categoryEmoji = issue.category === 'ui' ? '🎨' : issue.category === 'content' ? '📝' : issue.category === 'functionality' ? '⚙️' : issue.category === 'accessibility' ? '♿' : issue.category === 'performance' ? '⚡' : '🔧';
|
|
196
|
+
output += `${index + 1}. ${priorityEmoji} ${issue.title}\n`;
|
|
197
|
+
output += ` ${categoryEmoji} Category: ${issue.category} | Priority: ${issue.priority}\n`;
|
|
198
|
+
output += ` 📖 Description: ${issue.description}\n`;
|
|
199
|
+
// Check if this issue was created as a GitHub issue
|
|
200
|
+
const createdIssue = createdIssues.find((ci)=>ci.issue === issue);
|
|
201
|
+
if (createdIssue) {
|
|
202
|
+
output += ` 🔗 GitHub Issue: #${createdIssue.number} - ${createdIssue.githubUrl}\n`;
|
|
203
|
+
}
|
|
204
|
+
if (issue.suggestions && issue.suggestions.length > 0) {
|
|
205
|
+
output += ` 💡 Suggestions:\n`;
|
|
206
|
+
issue.suggestions.forEach((suggestion)=>{
|
|
207
|
+
output += ` • ${suggestion}\n`;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
output += `\n`;
|
|
211
|
+
});
|
|
212
|
+
} else {
|
|
213
|
+
output += `✅ No specific issues identified from the review.\n\n`;
|
|
214
|
+
}
|
|
215
|
+
if (createdIssues.length > 0) {
|
|
216
|
+
output += `\n🎯 Created GitHub Issues:\n`;
|
|
217
|
+
createdIssues.forEach((createdIssue)=>{
|
|
218
|
+
output += `• #${createdIssue.number}: ${createdIssue.issue.title} - ${createdIssue.githubUrl}\n`;
|
|
219
|
+
});
|
|
220
|
+
output += `\n`;
|
|
221
|
+
}
|
|
222
|
+
output += `🚀 Next Steps: Review the created GitHub issues and prioritize them in your development workflow.`;
|
|
223
|
+
return output;
|
|
224
|
+
}
|
|
225
|
+
function formatReviewResults(result) {
|
|
226
|
+
let output = `📝 Review Results\n\n`;
|
|
227
|
+
output += `📋 Summary: ${result.summary}\n`;
|
|
228
|
+
output += `📊 Total Issues Found: ${result.totalIssues}\n\n`;
|
|
229
|
+
if (result.issues && result.issues.length > 0) {
|
|
230
|
+
output += `📝 Issues Identified:\n\n`;
|
|
231
|
+
result.issues.forEach((issue, index)=>{
|
|
232
|
+
const priorityEmoji = issue.priority === 'high' ? '🔴' : issue.priority === 'medium' ? '🟡' : '🟢';
|
|
233
|
+
const categoryEmoji = issue.category === 'ui' ? '🎨' : issue.category === 'content' ? '📝' : issue.category === 'functionality' ? '⚙️' : issue.category === 'accessibility' ? '♿' : issue.category === 'performance' ? '⚡' : '🔧';
|
|
234
|
+
output += `${index + 1}. ${priorityEmoji} ${issue.title}\n`;
|
|
235
|
+
output += ` ${categoryEmoji} Category: ${issue.category} | Priority: ${issue.priority}\n`;
|
|
236
|
+
output += ` 📖 Description: ${issue.description}\n`;
|
|
237
|
+
if (issue.suggestions && issue.suggestions.length > 0) {
|
|
238
|
+
output += ` 💡 Suggestions:\n`;
|
|
239
|
+
issue.suggestions.forEach((suggestion)=>{
|
|
240
|
+
output += ` • ${suggestion}\n`;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
output += `\n`;
|
|
244
|
+
});
|
|
245
|
+
} else {
|
|
246
|
+
output += `✅ No specific issues identified from the review.\n\n`;
|
|
247
|
+
}
|
|
248
|
+
output += `🚀 Next Steps: Review the identified issues and prioritize them for your development workflow.`;
|
|
249
|
+
return output;
|
|
250
|
+
}
|
|
251
|
+
// Handle GitHub issue creation workflow
|
|
252
|
+
const handleIssueCreation = async (result, senditMode = false)=>{
|
|
253
|
+
const logger = getLogger();
|
|
254
|
+
const createdIssues = [];
|
|
255
|
+
if (!result.issues || result.issues.length === 0) {
|
|
256
|
+
return formatReviewResults(result);
|
|
257
|
+
}
|
|
258
|
+
logger.info(`🔍 Found ${result.issues.length} issues to potentially create as GitHub issues`);
|
|
259
|
+
for(let i = 0; i < result.issues.length; i++){
|
|
260
|
+
let issue = result.issues[i];
|
|
261
|
+
let shouldCreateIssue = senditMode;
|
|
262
|
+
if (!senditMode) {
|
|
263
|
+
// Interactive confirmation for each issue - keep looping until user decides
|
|
264
|
+
let userChoice = '';
|
|
265
|
+
while(userChoice !== 'c' && userChoice !== 's'){
|
|
266
|
+
// Display issue details
|
|
267
|
+
logger.info(`\n📋 Issue ${i + 1} of ${result.issues.length}:`);
|
|
268
|
+
logger.info(` Title: ${issue.title}`);
|
|
269
|
+
logger.info(` Priority: ${issue.priority} | Category: ${issue.category}`);
|
|
270
|
+
logger.info(` Description: ${issue.description}`);
|
|
271
|
+
if (issue.suggestions && issue.suggestions.length > 0) {
|
|
272
|
+
logger.info(` Suggestions: ${issue.suggestions.join(', ')}`);
|
|
273
|
+
}
|
|
274
|
+
// Get user choice
|
|
275
|
+
userChoice = await getUserChoice('\nWhat would you like to do with this issue?', [
|
|
276
|
+
{
|
|
277
|
+
key: 'c',
|
|
278
|
+
label: 'Create GitHub issue'
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
key: 's',
|
|
282
|
+
label: 'Skip this issue'
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
key: 'e',
|
|
286
|
+
label: 'Edit issue details'
|
|
287
|
+
}
|
|
288
|
+
]);
|
|
289
|
+
if (userChoice === 'c') {
|
|
290
|
+
shouldCreateIssue = true;
|
|
291
|
+
} else if (userChoice === 'e') {
|
|
292
|
+
// Allow user to edit the issue
|
|
293
|
+
issue = await editIssueInteractively(issue);
|
|
294
|
+
result.issues[i] = issue; // Update the issue in the result
|
|
295
|
+
// Continue the loop to show the updated issue and ask again
|
|
296
|
+
}
|
|
297
|
+
// If choice is 's', loop will exit and shouldCreateIssue remains false
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (shouldCreateIssue) {
|
|
301
|
+
try {
|
|
302
|
+
logger.info(`🚀 Creating GitHub issue: "${issue.title}"`);
|
|
303
|
+
// Format issue body with additional details
|
|
304
|
+
const issueBody = formatIssueBody(issue);
|
|
305
|
+
// Create labels based on priority and category
|
|
306
|
+
const labels = [
|
|
307
|
+
`priority-${issue.priority}`,
|
|
308
|
+
`category-${issue.category}`,
|
|
309
|
+
'review'
|
|
310
|
+
];
|
|
311
|
+
const createdIssue = await createIssue(issue.title, issueBody, labels);
|
|
312
|
+
createdIssues.push({
|
|
313
|
+
issue,
|
|
314
|
+
githubUrl: createdIssue.html_url,
|
|
315
|
+
number: createdIssue.number
|
|
316
|
+
});
|
|
317
|
+
logger.info(`✅ Created GitHub issue #${createdIssue.number}: ${createdIssue.html_url}`);
|
|
318
|
+
} catch (error) {
|
|
319
|
+
logger.error(`❌ Failed to create GitHub issue for "${issue.title}": ${error.message}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// Return formatted results
|
|
324
|
+
if (createdIssues.length > 0) {
|
|
325
|
+
return formatReviewResultsWithIssues(result, createdIssues);
|
|
326
|
+
} else {
|
|
327
|
+
return formatReviewResults(result);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
export { get, handleIssueCreation };
|
|
332
|
+
//# sourceMappingURL=issues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues.js","sources":["../src/issues.ts"],"sourcesContent":["import { getLogger } from './logger';\nimport { getOpenIssues, createIssue } from './github';\n\nimport path from 'path';\nimport os from 'os';\nimport { spawnSync } from 'child_process';\nimport fs from 'fs/promises';\n\nexport interface Issue {\n title: string;\n description: string;\n priority: 'low' | 'medium' | 'high';\n category: 'ui' | 'content' | 'functionality' | 'accessibility' | 'performance' | 'other';\n suggestions?: string[];\n}\n\nexport interface ReviewResult {\n summary: string;\n totalIssues: number;\n issues: Issue[];\n}\n\n// Get GitHub issues content\nexport const get = async (options: { limit?: number } = {}): Promise<string> => {\n const logger = getLogger();\n const { limit = 20 } = options;\n\n try {\n logger.debug('Fetching open GitHub issues...');\n const issuesLimit = Math.min(limit, 20); // Cap at 20\n const githubIssues = await getOpenIssues(issuesLimit);\n\n if (githubIssues.trim()) {\n logger.debug(`Added GitHub issues to context (${githubIssues.length} characters)`);\n return githubIssues;\n } else {\n logger.debug('No open GitHub issues found');\n return '';\n }\n } catch (error: any) {\n logger.warn(`Failed to fetch GitHub issues: ${error.message}`);\n return '';\n }\n};\n\n// Helper function to get user choice interactively\nasync function getUserChoice(_prompt: string, _choices: Array<{ key: string, label: string }>): Promise<string> {\n // Placeholder - always returns first choice\n return _choices[0]?.key || '';\n}\n\n// Helper function to serialize issue to structured text format\nfunction serializeIssue(issue: Issue): string {\n const lines = [\n '# Issue Editor',\n '',\n '# Edit the issue details below. Lines starting with \"#\" are comments and will be ignored.',\n '# Valid priorities: low, medium, high',\n '# Valid categories: ui, content, functionality, accessibility, performance, other',\n '# Suggestions should be one per line, preceded by a \"-\" or \"•\"',\n '',\n `Title: ${issue.title}`,\n '',\n `Priority: ${issue.priority}`,\n '',\n `Category: ${issue.category}`,\n '',\n 'Description:',\n issue.description,\n '',\n 'Suggestions:',\n ];\n\n if (issue.suggestions && issue.suggestions.length > 0) {\n issue.suggestions.forEach(suggestion => {\n lines.push(`- ${suggestion}`);\n });\n } else {\n lines.push('# Add suggestions here, one per line with \"-\" or \"•\"');\n }\n\n return lines.join('\\n');\n}\n\n// Helper function to deserialize issue from structured text format\nfunction deserializeIssue(content: string): Issue {\n const lines = content.split('\\n');\n\n // Parse the structured format\n let title = '';\n let priority: 'low' | 'medium' | 'high' = 'medium';\n let category: 'ui' | 'content' | 'functionality' | 'accessibility' | 'performance' | 'other' = 'other';\n let description = '';\n const suggestions: string[] = [];\n\n let currentSection = '';\n let descriptionLines: string[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n // Skip comment lines\n if (line.startsWith('#')) {\n continue;\n }\n\n // Parse field lines\n if (line.startsWith('Title:')) {\n title = line.substring(6).trim();\n } else if (line.startsWith('Priority:')) {\n const priorityValue = line.substring(9).trim().toLowerCase();\n if (priorityValue === 'low' || priorityValue === 'medium' || priorityValue === 'high') {\n priority = priorityValue;\n }\n } else if (line.startsWith('Category:')) {\n const categoryValue = line.substring(9).trim().toLowerCase();\n if (['ui', 'content', 'functionality', 'accessibility', 'performance', 'other'].includes(categoryValue)) {\n category = categoryValue as any;\n }\n } else if (line === 'Description:') {\n currentSection = 'description';\n descriptionLines = [];\n } else if (line === 'Suggestions:') {\n currentSection = 'suggestions';\n // Process accumulated description lines\n description = descriptionLines.join('\\n').trim();\n } else if (currentSection === 'description' && line !== '') {\n descriptionLines.push(lines[i]); // Keep original line with spacing\n } else if (currentSection === 'suggestions' && line !== '') {\n // Parse suggestion line\n const suggestionLine = line.replace(/^[-•]\\s*/, '').trim();\n if (suggestionLine) {\n suggestions.push(suggestionLine);\n }\n }\n }\n\n // If we didn't encounter suggestions section, description might still be accumulating\n if (currentSection === 'description') {\n description = descriptionLines.join('\\n').trim();\n }\n\n return {\n title: title || 'Untitled Issue',\n priority,\n category,\n description: description || 'No description provided',\n suggestions: suggestions.length > 0 ? suggestions : undefined\n };\n}\n\n// Helper function to edit issue using editor\nasync function editIssueInteractively(issue: Issue): Promise<Issue> {\n const logger = getLogger();\n const editor = process.env.EDITOR || process.env.VISUAL || 'vi';\n\n // Create a temporary file for the user to edit\n const tmpDir = os.tmpdir();\n const tmpFilePath = path.join(tmpDir, `kodrdriv_issue_${Date.now()}.txt`);\n\n // Serialize the issue to structured text format\n const issueContent = serializeIssue(issue);\n\n await fs.writeFile(tmpFilePath, issueContent, 'utf8');\n\n logger.info(`📝 Opening ${editor} to edit issue...`);\n\n // Open the editor synchronously so execution resumes after the user closes it\n const result = spawnSync(editor, [tmpFilePath], { stdio: 'inherit' });\n\n if (result.error) {\n throw new Error(`Failed to launch editor '${editor}': ${result.error.message}`);\n }\n\n // Read the file back and deserialize it\n const editedContent = await fs.readFile(tmpFilePath, 'utf8');\n\n // Clean up the temporary file with proper error handling\n try {\n await fs.unlink(tmpFilePath);\n } catch (error: any) {\n // Only log if it's not a \"file not found\" error\n if (error.code !== 'ENOENT') {\n logger.warn(`Failed to cleanup temporary file ${tmpFilePath}: ${error.message}`);\n }\n }\n\n // Deserialize the edited content back to an Issue object\n const editedIssue = deserializeIssue(editedContent);\n\n logger.info('✅ Issue updated successfully');\n logger.debug(`Updated issue: ${JSON.stringify(editedIssue, null, 2)}`);\n\n return editedIssue;\n}\n\n// Helper function to format issue body for GitHub\nfunction formatIssueBody(issue: Issue): string {\n let body = `## Description\\n\\n${issue.description}\\n\\n`;\n\n body += `## Details\\n\\n`;\n body += `- **Priority:** ${issue.priority}\\n`;\n body += `- **Category:** ${issue.category}\\n`;\n body += `- **Source:** Review\\n\\n`;\n\n if (issue.suggestions && issue.suggestions.length > 0) {\n body += `## Suggestions\\n\\n`;\n issue.suggestions.forEach(suggestion => {\n body += `- ${suggestion}\\n`;\n });\n body += '\\n';\n }\n\n body += `---\\n\\n`;\n body += `*This issue was automatically created from a review session.*`;\n\n return body;\n}\n\n// Helper function to format results with created GitHub issues\nfunction formatReviewResultsWithIssues(\n result: ReviewResult,\n createdIssues: Array<{ issue: Issue, githubUrl: string, number: number }>\n): string {\n let output = `📝 Review Results\\n\\n`;\n output += `📋 Summary: ${result.summary}\\n`;\n output += `📊 Total Issues Found: ${result.totalIssues}\\n`;\n output += `🚀 GitHub Issues Created: ${createdIssues.length}\\n\\n`;\n\n if (result.issues && result.issues.length > 0) {\n output += `📝 Issues Identified:\\n\\n`;\n\n result.issues.forEach((issue, index) => {\n const priorityEmoji = issue.priority === 'high' ? '🔴' :\n issue.priority === 'medium' ? '🟡' : '🟢';\n const categoryEmoji = issue.category === 'ui' ? '🎨' :\n issue.category === 'content' ? '📝' :\n issue.category === 'functionality' ? '⚙️' :\n issue.category === 'accessibility' ? '♿' :\n issue.category === 'performance' ? '⚡' : '🔧';\n\n output += `${index + 1}. ${priorityEmoji} ${issue.title}\\n`;\n output += ` ${categoryEmoji} Category: ${issue.category} | Priority: ${issue.priority}\\n`;\n output += ` 📖 Description: ${issue.description}\\n`;\n\n // Check if this issue was created as a GitHub issue\n const createdIssue = createdIssues.find(ci => ci.issue === issue);\n if (createdIssue) {\n output += ` 🔗 GitHub Issue: #${createdIssue.number} - ${createdIssue.githubUrl}\\n`;\n }\n\n if (issue.suggestions && issue.suggestions.length > 0) {\n output += ` 💡 Suggestions:\\n`;\n issue.suggestions.forEach(suggestion => {\n output += ` • ${suggestion}\\n`;\n });\n }\n output += `\\n`;\n });\n } else {\n output += `✅ No specific issues identified from the review.\\n\\n`;\n }\n\n if (createdIssues.length > 0) {\n output += `\\n🎯 Created GitHub Issues:\\n`;\n createdIssues.forEach(createdIssue => {\n output += `• #${createdIssue.number}: ${createdIssue.issue.title} - ${createdIssue.githubUrl}\\n`;\n });\n output += `\\n`;\n }\n\n output += `🚀 Next Steps: Review the created GitHub issues and prioritize them in your development workflow.`;\n\n return output;\n}\n\nfunction formatReviewResults(result: ReviewResult): string {\n let output = `📝 Review Results\\n\\n`;\n output += `📋 Summary: ${result.summary}\\n`;\n output += `📊 Total Issues Found: ${result.totalIssues}\\n\\n`;\n\n if (result.issues && result.issues.length > 0) {\n output += `📝 Issues Identified:\\n\\n`;\n\n result.issues.forEach((issue, index) => {\n const priorityEmoji = issue.priority === 'high' ? '🔴' :\n issue.priority === 'medium' ? '🟡' : '🟢';\n const categoryEmoji = issue.category === 'ui' ? '🎨' :\n issue.category === 'content' ? '📝' :\n issue.category === 'functionality' ? '⚙️' :\n issue.category === 'accessibility' ? '♿' :\n issue.category === 'performance' ? '⚡' : '🔧';\n\n output += `${index + 1}. ${priorityEmoji} ${issue.title}\\n`;\n output += ` ${categoryEmoji} Category: ${issue.category} | Priority: ${issue.priority}\\n`;\n output += ` 📖 Description: ${issue.description}\\n`;\n\n if (issue.suggestions && issue.suggestions.length > 0) {\n output += ` 💡 Suggestions:\\n`;\n issue.suggestions.forEach(suggestion => {\n output += ` • ${suggestion}\\n`;\n });\n }\n output += `\\n`;\n });\n } else {\n output += `✅ No specific issues identified from the review.\\n\\n`;\n }\n\n output += `🚀 Next Steps: Review the identified issues and prioritize them for your development workflow.`;\n\n return output;\n}\n\n// Handle GitHub issue creation workflow\nexport const handleIssueCreation = async (\n result: ReviewResult,\n senditMode: boolean = false\n): Promise<string> => {\n const logger = getLogger();\n const createdIssues: Array<{ issue: Issue, githubUrl: string, number: number }> = [];\n\n if (!result.issues || result.issues.length === 0) {\n return formatReviewResults(result);\n }\n\n logger.info(`🔍 Found ${result.issues.length} issues to potentially create as GitHub issues`);\n\n for (let i = 0; i < result.issues.length; i++) {\n let issue = result.issues[i];\n let shouldCreateIssue = senditMode;\n\n if (!senditMode) {\n // Interactive confirmation for each issue - keep looping until user decides\n let userChoice = '';\n while (userChoice !== 'c' && userChoice !== 's') {\n // Display issue details\n logger.info(`\\n📋 Issue ${i + 1} of ${result.issues.length}:`);\n logger.info(` Title: ${issue.title}`);\n logger.info(` Priority: ${issue.priority} | Category: ${issue.category}`);\n logger.info(` Description: ${issue.description}`);\n if (issue.suggestions && issue.suggestions.length > 0) {\n logger.info(` Suggestions: ${issue.suggestions.join(', ')}`);\n }\n\n // Get user choice\n userChoice = await getUserChoice('\\nWhat would you like to do with this issue?', [\n { key: 'c', label: 'Create GitHub issue' },\n { key: 's', label: 'Skip this issue' },\n { key: 'e', label: 'Edit issue details' }\n ]);\n\n if (userChoice === 'c') {\n shouldCreateIssue = true;\n } else if (userChoice === 'e') {\n // Allow user to edit the issue\n issue = await editIssueInteractively(issue);\n result.issues[i] = issue; // Update the issue in the result\n // Continue the loop to show the updated issue and ask again\n }\n // If choice is 's', loop will exit and shouldCreateIssue remains false\n }\n }\n\n if (shouldCreateIssue) {\n try {\n logger.info(`🚀 Creating GitHub issue: \"${issue.title}\"`);\n\n // Format issue body with additional details\n const issueBody = formatIssueBody(issue);\n\n // Create labels based on priority and category\n const labels = [\n `priority-${issue.priority}`,\n `category-${issue.category}`,\n 'review'\n ];\n\n const createdIssue = await createIssue(issue.title, issueBody, labels);\n createdIssues.push({\n issue,\n githubUrl: createdIssue.html_url,\n number: createdIssue.number\n });\n\n logger.info(`✅ Created GitHub issue #${createdIssue.number}: ${createdIssue.html_url}`);\n } catch (error: any) {\n logger.error(`❌ Failed to create GitHub issue for \"${issue.title}\": ${error.message}`);\n }\n }\n }\n\n // Return formatted results\n if (createdIssues.length > 0) {\n return formatReviewResultsWithIssues(result, createdIssues);\n } else {\n return formatReviewResults(result);\n }\n};\n"],"names":["get","options","logger","getLogger","limit","debug","issuesLimit","Math","min","githubIssues","getOpenIssues","trim","length","error","warn","message","getUserChoice","_prompt","_choices","key","serializeIssue","issue","lines","title","priority","category","description","suggestions","forEach","suggestion","push","join","deserializeIssue","content","split","currentSection","descriptionLines","i","line","startsWith","substring","priorityValue","toLowerCase","categoryValue","includes","suggestionLine","replace","undefined","editIssueInteractively","editor","process","env","EDITOR","VISUAL","tmpDir","os","tmpdir","tmpFilePath","path","Date","now","issueContent","fs","writeFile","info","result","spawnSync","stdio","Error","editedContent","readFile","unlink","code","editedIssue","JSON","stringify","formatIssueBody","body","formatReviewResultsWithIssues","createdIssues","output","summary","totalIssues","issues","index","priorityEmoji","categoryEmoji","createdIssue","find","ci","number","githubUrl","formatReviewResults","handleIssueCreation","senditMode","shouldCreateIssue","userChoice","label","issueBody","labels","createIssue","html_url"],"mappings":"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"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Default console logger if winston isn't provided
|
|
2
|
+
/* eslint-disable no-console */ const defaultLogger = {
|
|
3
|
+
info: (message)=>console.log(message),
|
|
4
|
+
warn: (message)=>console.warn(message),
|
|
5
|
+
error: (message)=>console.error(message),
|
|
6
|
+
debug: (message)=>console.debug(message)
|
|
7
|
+
};
|
|
8
|
+
/* eslint-enable no-console */ let currentLogger = defaultLogger;
|
|
9
|
+
const setLogger = (logger)=>{
|
|
10
|
+
currentLogger = logger;
|
|
11
|
+
};
|
|
12
|
+
const getLogger = ()=>currentLogger;
|
|
13
|
+
|
|
14
|
+
export { getLogger, setLogger };
|
|
15
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sources":["../src/logger.ts"],"sourcesContent":["import type { Logger } from './types';\n\n// Default console logger if winston isn't provided\n/* eslint-disable no-console */\nconst defaultLogger: Logger = {\n info: (message: string) => console.log(message),\n warn: (message: string) => console.warn(message),\n error: (message: string) => console.error(message),\n debug: (message: string) => console.debug(message),\n};\n/* eslint-enable no-console */\n\nlet currentLogger: Logger = defaultLogger;\n\nexport const setLogger = (logger: Logger): void => {\n currentLogger = logger;\n};\n\nexport const getLogger = (): Logger => currentLogger;\n\n"],"names":["defaultLogger","info","message","console","log","warn","error","debug","currentLogger","setLogger","logger","getLogger"],"mappings":"AAEA;AACA,gCACA,MAAMA,aAAAA,GAAwB;AAC1BC,IAAAA,IAAAA,EAAM,CAACC,OAAAA,GAAoBC,OAAAA,CAAQC,GAAG,CAACF,OAAAA,CAAAA;AACvCG,IAAAA,IAAAA,EAAM,CAACH,OAAAA,GAAoBC,OAAAA,CAAQE,IAAI,CAACH,OAAAA,CAAAA;AACxCI,IAAAA,KAAAA,EAAO,CAACJ,OAAAA,GAAoBC,OAAAA,CAAQG,KAAK,CAACJ,OAAAA,CAAAA;AAC1CK,IAAAA,KAAAA,EAAO,CAACL,OAAAA,GAAoBC,OAAAA,CAAQI,KAAK,CAACL,OAAAA;AAC9C,CAAA;AACA,+BAEA,IAAIM,aAAAA,GAAwBR,aAAAA;AAErB,MAAMS,YAAY,CAACC,MAAAA,GAAAA;IACtBF,aAAAA,GAAgBE,MAAAA;AACpB;AAEO,MAAMC,SAAAA,GAAY,IAAcH;;;;"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { getLogger } from './logger.js';
|
|
2
|
+
import { getOctokit, getRepoDetails } from './github.js';
|
|
3
|
+
|
|
4
|
+
// Function to truncate overly large content while preserving structure
|
|
5
|
+
const truncateContent = (content, maxLength = 3000)=>{
|
|
6
|
+
if (content.length <= maxLength) {
|
|
7
|
+
return content;
|
|
8
|
+
}
|
|
9
|
+
const lines = content.split('\n');
|
|
10
|
+
const truncatedLines = [];
|
|
11
|
+
let currentLength = 0;
|
|
12
|
+
for (const line of lines){
|
|
13
|
+
if (currentLength + line.length + 1 > maxLength) {
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
truncatedLines.push(line);
|
|
17
|
+
currentLength += line.length + 1; // +1 for newline
|
|
18
|
+
}
|
|
19
|
+
truncatedLines.push('');
|
|
20
|
+
truncatedLines.push(`... [TRUNCATED: Original content was ${content.length} characters, showing first ${currentLength}] ...`);
|
|
21
|
+
return truncatedLines.join('\n');
|
|
22
|
+
};
|
|
23
|
+
// Function to fetch recent releases from GitHub API
|
|
24
|
+
const findRecentReleaseNotes = async (limit)=>{
|
|
25
|
+
const logger = getLogger();
|
|
26
|
+
const releaseNotes = [];
|
|
27
|
+
if (limit <= 0) {
|
|
28
|
+
return releaseNotes;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const octokit = getOctokit();
|
|
32
|
+
const { owner, repo } = await getRepoDetails();
|
|
33
|
+
logger.debug(`Fetching up to ${limit} recent releases from GitHub...`);
|
|
34
|
+
const response = await octokit.repos.listReleases({
|
|
35
|
+
owner,
|
|
36
|
+
repo,
|
|
37
|
+
per_page: Math.min(limit, 100)
|
|
38
|
+
});
|
|
39
|
+
const releases = response.data;
|
|
40
|
+
if (releases.length === 0) {
|
|
41
|
+
logger.debug('No releases found in GitHub repository');
|
|
42
|
+
return releaseNotes;
|
|
43
|
+
}
|
|
44
|
+
for (const release of releases.slice(0, limit)){
|
|
45
|
+
const releaseContent = [
|
|
46
|
+
`# ${release.name || release.tag_name}`,
|
|
47
|
+
`**Tag:** ${release.tag_name}`,
|
|
48
|
+
`**Published:** ${release.published_at}`,
|
|
49
|
+
release.prerelease ? '**Type:** Pre-release' : '**Type:** Release',
|
|
50
|
+
release.draft ? '**Status:** Draft' : '**Status:** Published',
|
|
51
|
+
'',
|
|
52
|
+
release.body || 'No release notes provided'
|
|
53
|
+
].join('\n');
|
|
54
|
+
const truncatedContent = truncateContent(releaseContent);
|
|
55
|
+
releaseNotes.push(`=== GitHub Release: ${release.tag_name} ===\n${truncatedContent}`);
|
|
56
|
+
if (truncatedContent.length < releaseContent.length) {
|
|
57
|
+
logger.debug(`Found release ${release.tag_name} (${truncatedContent.length} characters, truncated from ${releaseContent.length})`);
|
|
58
|
+
} else {
|
|
59
|
+
logger.debug(`Found release ${release.tag_name} (${releaseContent.length} characters)`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
logger.debug(`Fetched ${releaseNotes.length} releases from GitHub`);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
logger.warn(`Error fetching releases from GitHub API: ${error.message}`);
|
|
65
|
+
// If we have a GitHub API error, we could fall back to checking for local release notes
|
|
66
|
+
// This maintains some backward compatibility
|
|
67
|
+
logger.debug('Falling back to local RELEASE_NOTES.md file...');
|
|
68
|
+
try {
|
|
69
|
+
const fs = await import('fs/promises');
|
|
70
|
+
const content = await fs.readFile('RELEASE_NOTES.md', 'utf-8');
|
|
71
|
+
if (content.trim()) {
|
|
72
|
+
const truncatedContent = truncateContent(content);
|
|
73
|
+
releaseNotes.push(`=== Local RELEASE_NOTES.md ===\n${truncatedContent}`);
|
|
74
|
+
logger.debug(`Found local release notes (${content.length} characters)`);
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
// No local file either, return empty array
|
|
78
|
+
logger.debug('No local RELEASE_NOTES.md file found either');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return releaseNotes.slice(0, limit);
|
|
82
|
+
};
|
|
83
|
+
const get = async (options = {})=>{
|
|
84
|
+
const { limit = 3 } = options;
|
|
85
|
+
const releaseNotes = await findRecentReleaseNotes(limit);
|
|
86
|
+
return releaseNotes.join('\n\n');
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export { findRecentReleaseNotes, get };
|
|
90
|
+
//# sourceMappingURL=releaseNotes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"releaseNotes.js","sources":["../src/releaseNotes.ts"],"sourcesContent":["import { getLogger } from './logger';\nimport { getOctokit, getRepoDetails } from './github';\n\n// Function to truncate overly large content while preserving structure\nconst truncateContent = (content: string, maxLength: number = 3000): string => {\n if (content.length <= maxLength) {\n return content;\n }\n\n const lines = content.split('\\n');\n const truncatedLines: string[] = [];\n let currentLength = 0;\n\n for (const line of lines) {\n if (currentLength + line.length + 1 > maxLength) {\n break;\n }\n truncatedLines.push(line);\n currentLength += line.length + 1; // +1 for newline\n }\n\n truncatedLines.push('');\n truncatedLines.push(`... [TRUNCATED: Original content was ${content.length} characters, showing first ${currentLength}] ...`);\n\n return truncatedLines.join('\\n');\n};\n\n// Function to fetch recent releases from GitHub API\nexport const findRecentReleaseNotes = async (limit: number): Promise<string[]> => {\n const logger = getLogger();\n const releaseNotes: string[] = [];\n\n if (limit <= 0) {\n return releaseNotes;\n }\n\n try {\n const octokit = getOctokit();\n const { owner, repo } = await getRepoDetails();\n\n logger.debug(`Fetching up to ${limit} recent releases from GitHub...`);\n\n const response = await octokit.repos.listReleases({\n owner,\n repo,\n per_page: Math.min(limit, 100), // GitHub API limit\n });\n\n const releases = response.data;\n\n if (releases.length === 0) {\n logger.debug('No releases found in GitHub repository');\n return releaseNotes;\n }\n\n for (const release of releases.slice(0, limit)) {\n const releaseContent = [\n `# ${release.name || release.tag_name}`,\n `**Tag:** ${release.tag_name}`,\n `**Published:** ${release.published_at}`,\n release.prerelease ? '**Type:** Pre-release' : '**Type:** Release',\n release.draft ? '**Status:** Draft' : '**Status:** Published',\n '',\n release.body || 'No release notes provided'\n ].join('\\n');\n\n const truncatedContent = truncateContent(releaseContent);\n releaseNotes.push(`=== GitHub Release: ${release.tag_name} ===\\n${truncatedContent}`);\n\n if (truncatedContent.length < releaseContent.length) {\n logger.debug(`Found release ${release.tag_name} (${truncatedContent.length} characters, truncated from ${releaseContent.length})`);\n } else {\n logger.debug(`Found release ${release.tag_name} (${releaseContent.length} characters)`);\n }\n }\n\n logger.debug(`Fetched ${releaseNotes.length} releases from GitHub`);\n\n } catch (error: any) {\n logger.warn(`Error fetching releases from GitHub API: ${error.message}`);\n\n // If we have a GitHub API error, we could fall back to checking for local release notes\n // This maintains some backward compatibility\n logger.debug('Falling back to local RELEASE_NOTES.md file...');\n try {\n const fs = await import('fs/promises');\n const content = await fs.readFile('RELEASE_NOTES.md', 'utf-8');\n if (content.trim()) {\n const truncatedContent = truncateContent(content);\n releaseNotes.push(`=== Local RELEASE_NOTES.md ===\\n${truncatedContent}`);\n logger.debug(`Found local release notes (${content.length} characters)`);\n }\n } catch {\n // No local file either, return empty array\n logger.debug('No local RELEASE_NOTES.md file found either');\n }\n }\n\n return releaseNotes.slice(0, limit);\n};\n\nexport const get = async (options: { limit?: number } = {}): Promise<string> => {\n const { limit = 3 } = options;\n const releaseNotes = await findRecentReleaseNotes(limit);\n return releaseNotes.join('\\n\\n');\n};\n"],"names":["truncateContent","content","maxLength","length","lines","split","truncatedLines","currentLength","line","push","join","findRecentReleaseNotes","limit","logger","getLogger","releaseNotes","octokit","getOctokit","owner","repo","getRepoDetails","debug","response","repos","listReleases","per_page","Math","min","releases","data","release","slice","releaseContent","name","tag_name","published_at","prerelease","draft","body","truncatedContent","error","warn","message","fs","readFile","trim","get","options"],"mappings":"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"}
|
package/guide/index.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @grunnverk/github-tools - Agentic Guide
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
GitHub API utilities for automation. Provides PR management, issue tracking, workflow monitoring, and release note generation.
|
|
6
|
+
|
|
7
|
+
## Key Features
|
|
8
|
+
|
|
9
|
+
- **Pull Request Management** - Create, update, and merge PRs
|
|
10
|
+
- **Issue Tracking** - Create and manage GitHub issues
|
|
11
|
+
- **Release Notes** - Generate and publish release notes
|
|
12
|
+
- **Workflow Monitoring** - Check CI/CD status
|
|
13
|
+
- **Authentication** - GitHub token management
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import {
|
|
19
|
+
createPullRequest,
|
|
20
|
+
createIssue,
|
|
21
|
+
publishReleaseNotes,
|
|
22
|
+
getPRStatus
|
|
23
|
+
} from '@grunnverk/github-tools';
|
|
24
|
+
|
|
25
|
+
// Create pull request
|
|
26
|
+
const pr = await createPullRequest({
|
|
27
|
+
owner: 'myorg',
|
|
28
|
+
repo: 'myrepo',
|
|
29
|
+
title: 'Feature: Add new command',
|
|
30
|
+
body: 'Description...',
|
|
31
|
+
head: 'feature-branch',
|
|
32
|
+
base: 'main'
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Create issue
|
|
36
|
+
const issue = await createIssue({
|
|
37
|
+
owner: 'myorg',
|
|
38
|
+
repo: 'myrepo',
|
|
39
|
+
title: 'Bug: Fix validation',
|
|
40
|
+
body: 'Details...'
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Publish release notes
|
|
44
|
+
await publishReleaseNotes({
|
|
45
|
+
owner: 'myorg',
|
|
46
|
+
repo: 'myrepo',
|
|
47
|
+
tag: 'v1.0.0',
|
|
48
|
+
notes: 'Release notes...'
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Dependencies
|
|
53
|
+
|
|
54
|
+
- @grunnverk/git-tools - Git operations
|
|
55
|
+
- @octokit/rest - GitHub API client
|
|
56
|
+
|
|
57
|
+
## Package Structure
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
src/
|
|
61
|
+
├── github.ts # GitHub API client
|
|
62
|
+
├── issues.ts # Issue management
|
|
63
|
+
├── releaseNotes.ts # Release note publishing
|
|
64
|
+
├── errors.ts # Error types
|
|
65
|
+
├── types.ts # Type definitions
|
|
66
|
+
├── logger.ts # Logging utilities
|
|
67
|
+
└── index.ts
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Key Exports
|
|
71
|
+
|
|
72
|
+
- `createPullRequest()` - Create GitHub PR
|
|
73
|
+
- `mergePullRequest()` - Merge PR
|
|
74
|
+
- `createIssue()` - Create issue
|
|
75
|
+
- `publishReleaseNotes()` - Publish release notes
|
|
76
|
+
- `getPRStatus()` - Check PR CI status
|
|
77
|
+
- `getLatestRelease()` - Get latest release info
|
|
78
|
+
|