proagents 1.6.12 โ 1.6.13
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/.proagents/.cursorrules +16 -2
- package/.proagents/.windsurfrules +16 -2
- package/.proagents/AI_INSTRUCTIONS.md +1219 -53
- package/.proagents/ANTIGRAVITY.md +16 -2
- package/.proagents/BOLT.md +16 -2
- package/.proagents/CHATGPT.md +16 -2
- package/.proagents/CLAUDE.md +16 -2
- package/.proagents/GEMINI.md +16 -2
- package/.proagents/GROQ.md +16 -2
- package/.proagents/KIRO.md +16 -2
- package/.proagents/LOVABLE.md +16 -2
- package/.proagents/PROAGENTS.md +52 -26
- package/.proagents/REPLIT.md +16 -2
- package/.proagents/docs/command-details.md +985 -82
- package/.proagents/worklog/_context.md +31 -1
- package/.proagents/worklog/ai-stats.json +19 -0
- package/README.md +85 -1
- package/bin/proagents.js +132 -1
- package/lib/commands/changelog.js +389 -0
- package/lib/commands/completion.js +413 -0
- package/lib/commands/config.js +248 -0
- package/lib/commands/doctor.js +222 -25
- package/lib/commands/help.js +22 -2
- package/lib/commands/init.js +2 -1
- package/lib/commands/open.js +188 -0
- package/lib/commands/release.js +1007 -0
- package/lib/commands/restore.js +150 -0
- package/lib/commands/stats.js +320 -0
- package/lib/commands/uninstall.js +98 -4
- package/lib/commands/upgrade.js +102 -10
- package/lib/commands/version.js +140 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1007 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, appendFileSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import { createInterface } from 'readline';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
|
|
8
|
+
// JSON output flag (set by --json option)
|
|
9
|
+
let jsonOutput = false;
|
|
10
|
+
let jsonResult = {};
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
|
|
15
|
+
// Release type configurations
|
|
16
|
+
const RELEASE_TYPES = {
|
|
17
|
+
detailed: {
|
|
18
|
+
name: 'Detailed Release',
|
|
19
|
+
description: 'Full comprehensive notes with all sections (technical + business)',
|
|
20
|
+
emoji: '๐'
|
|
21
|
+
},
|
|
22
|
+
short: {
|
|
23
|
+
name: 'Short Release',
|
|
24
|
+
description: 'Quick summary of key changes',
|
|
25
|
+
emoji: '๐'
|
|
26
|
+
},
|
|
27
|
+
client: {
|
|
28
|
+
name: 'Client Release',
|
|
29
|
+
description: 'Business-focused, non-technical for stakeholders',
|
|
30
|
+
emoji: '๐ผ'
|
|
31
|
+
},
|
|
32
|
+
developer: {
|
|
33
|
+
name: 'Developer Release',
|
|
34
|
+
description: 'Technical details, code changes, breaking changes',
|
|
35
|
+
emoji: '๐ง'
|
|
36
|
+
},
|
|
37
|
+
hotfix: {
|
|
38
|
+
name: 'Hotfix Release',
|
|
39
|
+
description: 'Quick patch notes for urgent fixes',
|
|
40
|
+
emoji: '๐'
|
|
41
|
+
},
|
|
42
|
+
prerelease: {
|
|
43
|
+
name: 'Pre-release (Beta/RC)',
|
|
44
|
+
description: 'Beta or release candidate notes',
|
|
45
|
+
emoji: '๐งช'
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Change categories
|
|
50
|
+
const CHANGE_CATEGORIES = {
|
|
51
|
+
features: { name: 'Features', emoji: 'โจ', keywords: ['feat:', 'feature', 'add ', 'new ', 'implement'] },
|
|
52
|
+
fixes: { name: 'Bug Fixes', emoji: '๐', keywords: ['fix:', 'fix ', 'bug', 'resolve', 'patch'] },
|
|
53
|
+
improvements: { name: 'Improvements', emoji: 'โก', keywords: ['improve', 'enhance', 'update', 'refactor', 'optimize'] },
|
|
54
|
+
breaking: { name: 'Breaking Changes', emoji: '๐ฅ', keywords: ['breaking', 'break:'] },
|
|
55
|
+
security: { name: 'Security', emoji: '๐', keywords: ['security', 'vulnerability', 'cve', 'auth'] },
|
|
56
|
+
docs: { name: 'Documentation', emoji: '๐', keywords: ['doc:', 'docs:', 'readme', 'documentation'] },
|
|
57
|
+
deps: { name: 'Dependencies', emoji: '๐ฆ', keywords: ['deps:', 'dependency', 'upgrade ', 'downgrade'] },
|
|
58
|
+
perf: { name: 'Performance', emoji: '๐', keywords: ['perf:', 'performance', 'speed', 'faster'] },
|
|
59
|
+
other: { name: 'Other', emoji: '๐', keywords: [] }
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get git commits with various filters
|
|
64
|
+
*/
|
|
65
|
+
function getGitCommits(options = {}) {
|
|
66
|
+
const { since, until, path, limit = 100 } = options;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
let command = 'git log';
|
|
70
|
+
|
|
71
|
+
// Date or tag range
|
|
72
|
+
if (since && until) {
|
|
73
|
+
command += ` ${since}..${until}`;
|
|
74
|
+
} else if (since) {
|
|
75
|
+
command += ` ${since}..HEAD`;
|
|
76
|
+
} else if (until) {
|
|
77
|
+
command += ` HEAD..${until}`;
|
|
78
|
+
} else {
|
|
79
|
+
// Try to get since last tag
|
|
80
|
+
try {
|
|
81
|
+
const lastTag = execSync('git describe --tags --abbrev=0 2>/dev/null', { encoding: 'utf-8' }).trim();
|
|
82
|
+
command += ` ${lastTag}..HEAD`;
|
|
83
|
+
options.detectedSince = lastTag;
|
|
84
|
+
} catch {
|
|
85
|
+
command += ` -${limit}`;
|
|
86
|
+
options.detectedSince = null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
command += ' --oneline --no-merges';
|
|
91
|
+
|
|
92
|
+
// Filter by path/module
|
|
93
|
+
if (path) {
|
|
94
|
+
command += ` -- ${path}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const commits = execSync(command, { encoding: 'utf-8' });
|
|
98
|
+
return commits.trim();
|
|
99
|
+
} catch {
|
|
100
|
+
return '';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get current version from package.json
|
|
106
|
+
*/
|
|
107
|
+
function getCurrentVersion(targetDir) {
|
|
108
|
+
const packagePath = join(targetDir, 'package.json');
|
|
109
|
+
if (existsSync(packagePath)) {
|
|
110
|
+
try {
|
|
111
|
+
const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));
|
|
112
|
+
return pkg.version || 'unknown';
|
|
113
|
+
} catch {
|
|
114
|
+
// Package.json unreadable
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return 'unknown';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Suggest version bump based on changes
|
|
122
|
+
*/
|
|
123
|
+
function suggestVersionBump(currentVersion, categories) {
|
|
124
|
+
const [major, minor, patch] = currentVersion.split('.').map(Number);
|
|
125
|
+
|
|
126
|
+
if (categories.breaking.length > 0) {
|
|
127
|
+
return {
|
|
128
|
+
type: 'major',
|
|
129
|
+
reason: `${categories.breaking.length} breaking change(s)`,
|
|
130
|
+
suggested: `${major + 1}.0.0`
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (categories.features.length > 0) {
|
|
135
|
+
return {
|
|
136
|
+
type: 'minor',
|
|
137
|
+
reason: `${categories.features.length} new feature(s)`,
|
|
138
|
+
suggested: `${major}.${minor + 1}.0`
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
type: 'patch',
|
|
144
|
+
reason: `${categories.fixes.length} fix(es), ${categories.improvements.length} improvement(s)`,
|
|
145
|
+
suggested: `${major}.${minor}.${patch + 1}`
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Parse commits into categories
|
|
151
|
+
*/
|
|
152
|
+
function categorizeCommits(commitsText, options = {}) {
|
|
153
|
+
const { include, exclude, module } = options;
|
|
154
|
+
const lines = commitsText.split('\n').filter(line => line.trim());
|
|
155
|
+
|
|
156
|
+
const categories = {
|
|
157
|
+
features: [],
|
|
158
|
+
fixes: [],
|
|
159
|
+
improvements: [],
|
|
160
|
+
breaking: [],
|
|
161
|
+
security: [],
|
|
162
|
+
docs: [],
|
|
163
|
+
deps: [],
|
|
164
|
+
perf: [],
|
|
165
|
+
other: []
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
for (const line of lines) {
|
|
169
|
+
const lower = line.toLowerCase();
|
|
170
|
+
const message = line.replace(/^[a-f0-9]+ /, ''); // Remove hash
|
|
171
|
+
|
|
172
|
+
// Module filter - check if commit message contains module name
|
|
173
|
+
if (module && !lower.includes(module.toLowerCase())) {
|
|
174
|
+
// Also check for common patterns like (module): or [module]
|
|
175
|
+
const modulePatterns = [
|
|
176
|
+
`(${module.toLowerCase()})`,
|
|
177
|
+
`[${module.toLowerCase()}]`,
|
|
178
|
+
`${module.toLowerCase()}:`
|
|
179
|
+
];
|
|
180
|
+
if (!modulePatterns.some(p => lower.includes(p))) {
|
|
181
|
+
continue; // Skip if module not mentioned
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Categorize
|
|
186
|
+
let categorized = false;
|
|
187
|
+
for (const [category, config] of Object.entries(CHANGE_CATEGORIES)) {
|
|
188
|
+
if (category === 'other') continue;
|
|
189
|
+
|
|
190
|
+
if (config.keywords.some(kw => lower.includes(kw))) {
|
|
191
|
+
categories[category].push(message);
|
|
192
|
+
categorized = true;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!categorized) {
|
|
198
|
+
categories.other.push(message);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Apply include/exclude filters
|
|
203
|
+
if (include && include.length > 0) {
|
|
204
|
+
const validCategories = include.map(i => i.toLowerCase());
|
|
205
|
+
for (const category of Object.keys(categories)) {
|
|
206
|
+
if (!validCategories.includes(category)) {
|
|
207
|
+
categories[category] = [];
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (exclude && exclude.length > 0) {
|
|
213
|
+
const excludeCategories = exclude.map(e => e.toLowerCase());
|
|
214
|
+
for (const category of excludeCategories) {
|
|
215
|
+
if (categories[category]) {
|
|
216
|
+
categories[category] = [];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return categories;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Generate DETAILED release notes
|
|
226
|
+
*/
|
|
227
|
+
function generateDetailedNotes(version, categories, since, options = {}) {
|
|
228
|
+
const date = new Date().toISOString().split('T')[0];
|
|
229
|
+
const totalChanges = Object.values(categories).flat().length;
|
|
230
|
+
|
|
231
|
+
let notes = `# Release Notes v${version}
|
|
232
|
+
|
|
233
|
+
**Release Date:** ${date}
|
|
234
|
+
**Previous Version:** ${since || 'Initial Release'}
|
|
235
|
+
${options.prerelease ? `**Status:** ${options.prerelease.toUpperCase()}\n` : ''}
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Overview
|
|
239
|
+
|
|
240
|
+
This release includes ${totalChanges} changes: ${categories.features.length} new features, ${categories.fixes.length} bug fixes, and ${categories.improvements.length} improvements.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## What's New
|
|
245
|
+
|
|
246
|
+
`;
|
|
247
|
+
|
|
248
|
+
// Add each non-empty category
|
|
249
|
+
const categoryOrder = ['breaking', 'security', 'features', 'fixes', 'improvements', 'perf', 'deps', 'docs', 'other'];
|
|
250
|
+
|
|
251
|
+
for (const category of categoryOrder) {
|
|
252
|
+
if (categories[category] && categories[category].length > 0) {
|
|
253
|
+
const config = CHANGE_CATEGORIES[category];
|
|
254
|
+
notes += `### ${config.emoji} ${config.name}\n\n`;
|
|
255
|
+
notes += categories[category].map(c => `- ${c}`).join('\n');
|
|
256
|
+
notes += '\n\n';
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
notes += `---
|
|
261
|
+
|
|
262
|
+
## Upgrade Instructions
|
|
263
|
+
|
|
264
|
+
\`\`\`bash
|
|
265
|
+
npm update proagents
|
|
266
|
+
# or
|
|
267
|
+
npx proagents init
|
|
268
|
+
\`\`\`
|
|
269
|
+
|
|
270
|
+
## Full Changelog
|
|
271
|
+
|
|
272
|
+
See all changes: [GitHub Commits](../../commits/main)
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
*Generated by ProAgents v${getCurrentVersion(process.cwd())}*
|
|
277
|
+
`;
|
|
278
|
+
|
|
279
|
+
return notes;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Generate SHORT release notes
|
|
284
|
+
*/
|
|
285
|
+
function generateShortNotes(version, categories, since, options = {}) {
|
|
286
|
+
const date = new Date().toISOString().split('T')[0];
|
|
287
|
+
const totalChanges = Object.values(categories).flat().length;
|
|
288
|
+
|
|
289
|
+
let notes = `# v${version} Release Notes${options.prerelease ? ` (${options.prerelease})` : ''}
|
|
290
|
+
|
|
291
|
+
**Date:** ${date} | **Changes:** ${totalChanges}
|
|
292
|
+
|
|
293
|
+
`;
|
|
294
|
+
|
|
295
|
+
if (categories.breaking.length > 0) {
|
|
296
|
+
notes += `**โ ๏ธ Breaking:** ${categories.breaking.length} changes\n`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const summary = [];
|
|
300
|
+
if (categories.features.length > 0) summary.push(`Features: ${categories.features.length}`);
|
|
301
|
+
if (categories.fixes.length > 0) summary.push(`Fixes: ${categories.fixes.length}`);
|
|
302
|
+
if (categories.improvements.length > 0) summary.push(`Improvements: ${categories.improvements.length}`);
|
|
303
|
+
if (categories.security.length > 0) summary.push(`Security: ${categories.security.length}`);
|
|
304
|
+
|
|
305
|
+
notes += `**${summary.join(' | ')}**\n\n`;
|
|
306
|
+
|
|
307
|
+
// Show top highlights
|
|
308
|
+
const highlights = [
|
|
309
|
+
...categories.breaking.slice(0, 2),
|
|
310
|
+
...categories.security.slice(0, 1),
|
|
311
|
+
...categories.features.slice(0, 2),
|
|
312
|
+
...categories.fixes.slice(0, 1)
|
|
313
|
+
].slice(0, 5);
|
|
314
|
+
|
|
315
|
+
if (highlights.length > 0) {
|
|
316
|
+
notes += `## Highlights\n\n`;
|
|
317
|
+
notes += highlights.map(c => `- ${c}`).join('\n');
|
|
318
|
+
notes += '\n\n';
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
notes += `---\n*Run \`proagents release --type detailed\` for full notes*\n`;
|
|
322
|
+
|
|
323
|
+
return notes;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Generate CLIENT release notes (non-technical)
|
|
328
|
+
*/
|
|
329
|
+
function generateClientNotes(version, categories, since, options = {}) {
|
|
330
|
+
const date = new Date().toLocaleDateString('en-US', {
|
|
331
|
+
year: 'numeric',
|
|
332
|
+
month: 'long',
|
|
333
|
+
day: 'numeric'
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
let notes = `# Product Update - Version ${version}${options.prerelease ? ` (${options.prerelease})` : ''}
|
|
337
|
+
|
|
338
|
+
**Release Date:** ${date}
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## What's New for You
|
|
343
|
+
|
|
344
|
+
`;
|
|
345
|
+
|
|
346
|
+
if (categories.features.length > 0) {
|
|
347
|
+
notes += `### โจ New Capabilities\n\n`;
|
|
348
|
+
notes += `We've added ${categories.features.length} new features to improve your experience:\n\n`;
|
|
349
|
+
categories.features.forEach(f => {
|
|
350
|
+
const friendly = f
|
|
351
|
+
.replace(/^feat(\(.*?\))?:?\s*/i, '')
|
|
352
|
+
.replace(/^add\s+/i, 'Added ')
|
|
353
|
+
.replace(/^implement\s+/i, 'New ');
|
|
354
|
+
notes += `- ${friendly}\n`;
|
|
355
|
+
});
|
|
356
|
+
notes += '\n';
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (categories.fixes.length > 0) {
|
|
360
|
+
notes += `### ๐ง Resolved Issues\n\n`;
|
|
361
|
+
notes += `We've fixed ${categories.fixes.length} issues to make things work better:\n\n`;
|
|
362
|
+
categories.fixes.slice(0, 5).forEach(f => {
|
|
363
|
+
const friendly = f
|
|
364
|
+
.replace(/^fix(\(.*?\))?:?\s*/i, '')
|
|
365
|
+
.replace(/^bug:?\s*/i, '');
|
|
366
|
+
notes += `- ${friendly}\n`;
|
|
367
|
+
});
|
|
368
|
+
if (categories.fixes.length > 5) {
|
|
369
|
+
notes += `- And ${categories.fixes.length - 5} more fixes\n`;
|
|
370
|
+
}
|
|
371
|
+
notes += '\n';
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (categories.improvements.length > 0) {
|
|
375
|
+
notes += `### โก Better Experience\n\n`;
|
|
376
|
+
categories.improvements.slice(0, 3).forEach(i => {
|
|
377
|
+
const friendly = i
|
|
378
|
+
.replace(/^(improve|enhance|update|refactor)(\(.*?\))?:?\s*/i, 'Improved ');
|
|
379
|
+
notes += `- ${friendly}\n`;
|
|
380
|
+
});
|
|
381
|
+
notes += '\n';
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (categories.security.length > 0) {
|
|
385
|
+
notes += `### ๐ Security Updates\n\n`;
|
|
386
|
+
notes += `We've strengthened security with ${categories.security.length} update(s).\n\n`;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (categories.breaking.length > 0) {
|
|
390
|
+
notes += `### โ ๏ธ Important Notice\n\n`;
|
|
391
|
+
notes += `This update includes some changes that may require your attention. Please review the upgrade guide or contact support if you have questions.\n\n`;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
notes += `---
|
|
395
|
+
|
|
396
|
+
## How to Update
|
|
397
|
+
|
|
398
|
+
Your system will be automatically updated, or you can manually update by following the standard upgrade process.
|
|
399
|
+
|
|
400
|
+
## Questions?
|
|
401
|
+
|
|
402
|
+
Contact our support team if you have any questions about this release.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
*Thank you for being a valued user!*
|
|
407
|
+
`;
|
|
408
|
+
|
|
409
|
+
return notes;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Generate DEVELOPER release notes
|
|
414
|
+
*/
|
|
415
|
+
function generateDeveloperNotes(version, categories, since, options = {}) {
|
|
416
|
+
const date = new Date().toISOString().split('T')[0];
|
|
417
|
+
|
|
418
|
+
let notes = `# Developer Release Notes - v${version}${options.prerelease ? `-${options.prerelease}` : ''}
|
|
419
|
+
|
|
420
|
+
**Released:** ${date}
|
|
421
|
+
**Diff:** ${since ? `${since}...v${version}` : 'See commits'}
|
|
422
|
+
${options.module ? `**Module:** ${options.module}\n` : ''}
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## TL;DR
|
|
426
|
+
|
|
427
|
+
`;
|
|
428
|
+
|
|
429
|
+
const summary = [];
|
|
430
|
+
if (categories.breaking.length > 0) summary.push(`${categories.breaking.length} BREAKING`);
|
|
431
|
+
if (categories.security.length > 0) summary.push(`${categories.security.length} security`);
|
|
432
|
+
if (categories.features.length > 0) summary.push(`${categories.features.length} features`);
|
|
433
|
+
if (categories.fixes.length > 0) summary.push(`${categories.fixes.length} fixes`);
|
|
434
|
+
if (categories.improvements.length > 0) summary.push(`${categories.improvements.length} improvements`);
|
|
435
|
+
if (categories.perf.length > 0) summary.push(`${categories.perf.length} perf`);
|
|
436
|
+
|
|
437
|
+
notes += `\`${summary.join(' | ') || 'No changes'}\`\n\n`;
|
|
438
|
+
|
|
439
|
+
// Add each non-empty category
|
|
440
|
+
const categoryOrder = ['breaking', 'security', 'features', 'fixes', 'improvements', 'perf', 'deps', 'docs', 'other'];
|
|
441
|
+
|
|
442
|
+
for (const category of categoryOrder) {
|
|
443
|
+
if (categories[category] && categories[category].length > 0) {
|
|
444
|
+
const config = CHANGE_CATEGORIES[category];
|
|
445
|
+
notes += `## ${config.emoji} ${config.name}\n\n`;
|
|
446
|
+
notes += categories[category].map(c => `- ${c}`).join('\n');
|
|
447
|
+
notes += '\n\n';
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (categories.breaking.length > 0) {
|
|
452
|
+
notes += `### Migration Guide\n\n`;
|
|
453
|
+
notes += `Review breaking changes above and update your code accordingly.\n\n`;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
notes += `---
|
|
457
|
+
|
|
458
|
+
## Technical Details
|
|
459
|
+
|
|
460
|
+
\`\`\`bash
|
|
461
|
+
# Update
|
|
462
|
+
npm update proagents
|
|
463
|
+
|
|
464
|
+
# Verify
|
|
465
|
+
npx proagents version
|
|
466
|
+
npx proagents doctor
|
|
467
|
+
\`\`\`
|
|
468
|
+
|
|
469
|
+
## API Changes
|
|
470
|
+
|
|
471
|
+
${categories.breaking.length > 0 ? 'See breaking changes above.' : 'No API changes in this release.'}
|
|
472
|
+
|
|
473
|
+
## Dependencies
|
|
474
|
+
|
|
475
|
+
Run \`npm audit\` to check for security updates.
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
*Generated by ProAgents CLI*
|
|
480
|
+
`;
|
|
481
|
+
|
|
482
|
+
return notes;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Generate HOTFIX release notes
|
|
487
|
+
*/
|
|
488
|
+
function generateHotfixNotes(version, categories, since, options = {}) {
|
|
489
|
+
const date = new Date().toISOString();
|
|
490
|
+
const timestamp = date.replace('T', ' ').slice(0, 19);
|
|
491
|
+
|
|
492
|
+
let notes = `# ๐ HOTFIX v${version}
|
|
493
|
+
|
|
494
|
+
**Released:** ${timestamp} UTC
|
|
495
|
+
**Urgency:** ${options.urgency || 'High'}
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## Critical Fixes
|
|
500
|
+
|
|
501
|
+
`;
|
|
502
|
+
|
|
503
|
+
// Prioritize security and fixes
|
|
504
|
+
if (categories.security.length > 0) {
|
|
505
|
+
notes += `### ๐ Security Patches\n\n`;
|
|
506
|
+
notes += categories.security.map(c => `- **SECURITY:** ${c}`).join('\n');
|
|
507
|
+
notes += '\n\n';
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (categories.fixes.length > 0) {
|
|
511
|
+
notes += `### ๐ Bug Fixes\n\n`;
|
|
512
|
+
notes += categories.fixes.map(c => `- ${c}`).join('\n');
|
|
513
|
+
notes += '\n\n';
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (categories.breaking.length > 0) {
|
|
517
|
+
notes += `### โ ๏ธ Breaking Changes\n\n`;
|
|
518
|
+
notes += categories.breaking.map(c => `- **BREAKING:** ${c}`).join('\n');
|
|
519
|
+
notes += '\n\n';
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
notes += `---
|
|
523
|
+
|
|
524
|
+
## Immediate Action Required
|
|
525
|
+
|
|
526
|
+
\`\`\`bash
|
|
527
|
+
# Update immediately
|
|
528
|
+
npm update proagents
|
|
529
|
+
|
|
530
|
+
# Verify fix applied
|
|
531
|
+
npx proagents version
|
|
532
|
+
\`\`\`
|
|
533
|
+
|
|
534
|
+
## Rollback (if needed)
|
|
535
|
+
|
|
536
|
+
\`\`\`bash
|
|
537
|
+
npm install proagents@${since || 'previous-version'}
|
|
538
|
+
\`\`\`
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
*This is a hotfix release. For full release notes, see the next scheduled release.*
|
|
543
|
+
`;
|
|
544
|
+
|
|
545
|
+
return notes;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Generate PRE-RELEASE notes (Beta/RC)
|
|
550
|
+
*/
|
|
551
|
+
function generatePrereleaseNotes(version, categories, since, options = {}) {
|
|
552
|
+
const date = new Date().toISOString().split('T')[0];
|
|
553
|
+
const stage = options.prerelease || 'beta';
|
|
554
|
+
|
|
555
|
+
let notes = `# ๐งช Pre-release v${version}-${stage}
|
|
556
|
+
|
|
557
|
+
**Date:** ${date}
|
|
558
|
+
**Stage:** ${stage.toUpperCase()}
|
|
559
|
+
**Status:** Testing / Not for production
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## โ ๏ธ Pre-release Notice
|
|
564
|
+
|
|
565
|
+
This is a **${stage}** release. It may contain bugs and is not recommended for production use.
|
|
566
|
+
|
|
567
|
+
**Please report issues:** [GitHub Issues](../../issues)
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## Changes in this ${stage}
|
|
572
|
+
|
|
573
|
+
`;
|
|
574
|
+
|
|
575
|
+
const categoryOrder = ['breaking', 'features', 'fixes', 'improvements', 'other'];
|
|
576
|
+
|
|
577
|
+
for (const category of categoryOrder) {
|
|
578
|
+
if (categories[category] && categories[category].length > 0) {
|
|
579
|
+
const config = CHANGE_CATEGORIES[category];
|
|
580
|
+
notes += `### ${config.emoji} ${config.name}\n\n`;
|
|
581
|
+
notes += categories[category].map(c => `- ${c}`).join('\n');
|
|
582
|
+
notes += '\n\n';
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
notes += `---
|
|
587
|
+
|
|
588
|
+
## Testing Instructions
|
|
589
|
+
|
|
590
|
+
\`\`\`bash
|
|
591
|
+
# Install pre-release
|
|
592
|
+
npm install proagents@${version}-${stage}
|
|
593
|
+
|
|
594
|
+
# Test features
|
|
595
|
+
npx proagents doctor
|
|
596
|
+
\`\`\`
|
|
597
|
+
|
|
598
|
+
## Known Issues
|
|
599
|
+
|
|
600
|
+
- [ ] List any known issues here
|
|
601
|
+
|
|
602
|
+
## Feedback
|
|
603
|
+
|
|
604
|
+
Please test and provide feedback before the stable release.
|
|
605
|
+
|
|
606
|
+
---
|
|
607
|
+
|
|
608
|
+
*This is a pre-release version. Use at your own risk.*
|
|
609
|
+
`;
|
|
610
|
+
|
|
611
|
+
return notes;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Interactive release type selection
|
|
616
|
+
*/
|
|
617
|
+
async function selectReleaseType() {
|
|
618
|
+
const rl = createInterface({
|
|
619
|
+
input: process.stdin,
|
|
620
|
+
output: process.stdout
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
const question = (prompt) => new Promise(resolve => rl.question(prompt, resolve));
|
|
624
|
+
|
|
625
|
+
console.log(chalk.bold('\nSelect Release Note Type:\n'));
|
|
626
|
+
|
|
627
|
+
const types = Object.entries(RELEASE_TYPES);
|
|
628
|
+
types.forEach(([key, value], index) => {
|
|
629
|
+
console.log(chalk.cyan(` ${index + 1}. ${value.emoji} ${value.name}`));
|
|
630
|
+
console.log(chalk.gray(` ${value.description}`));
|
|
631
|
+
});
|
|
632
|
+
console.log('');
|
|
633
|
+
|
|
634
|
+
const answer = await question(chalk.yellow('Enter choice (1-6): '));
|
|
635
|
+
rl.close();
|
|
636
|
+
|
|
637
|
+
const index = parseInt(answer) - 1;
|
|
638
|
+
if (index >= 0 && index < types.length) {
|
|
639
|
+
return types[index][0];
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
console.log(chalk.yellow('\nInvalid choice, defaulting to detailed release notes.'));
|
|
643
|
+
return 'detailed';
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Interactive filter selection
|
|
648
|
+
*/
|
|
649
|
+
async function selectFilters() {
|
|
650
|
+
const rl = createInterface({
|
|
651
|
+
input: process.stdin,
|
|
652
|
+
output: process.stdout
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
const question = (prompt) => new Promise(resolve => rl.question(prompt, resolve));
|
|
656
|
+
|
|
657
|
+
console.log(chalk.bold('\nFilter Changes (optional):\n'));
|
|
658
|
+
|
|
659
|
+
const categories = ['features', 'fixes', 'improvements', 'security', 'breaking', 'docs', 'deps', 'perf'];
|
|
660
|
+
categories.forEach((cat, i) => {
|
|
661
|
+
const config = CHANGE_CATEGORIES[cat];
|
|
662
|
+
console.log(chalk.cyan(` ${i + 1}. ${config.emoji} ${config.name}`));
|
|
663
|
+
});
|
|
664
|
+
console.log(chalk.gray(' 0. All changes (no filter)'));
|
|
665
|
+
console.log('');
|
|
666
|
+
|
|
667
|
+
const answer = await question(chalk.yellow('Include only (comma-separated, e.g., 1,2): '));
|
|
668
|
+
rl.close();
|
|
669
|
+
|
|
670
|
+
if (!answer || answer === '0') {
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const indices = answer.split(',').map(s => parseInt(s.trim()) - 1);
|
|
675
|
+
const selected = indices
|
|
676
|
+
.filter(i => i >= 0 && i < categories.length)
|
|
677
|
+
.map(i => categories[i]);
|
|
678
|
+
|
|
679
|
+
return selected.length > 0 ? selected : null;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Append to existing release notes file
|
|
684
|
+
*/
|
|
685
|
+
function appendToExistingNotes(existingPath, newNotes, version) {
|
|
686
|
+
if (!existsSync(existingPath)) {
|
|
687
|
+
writeFileSync(existingPath, newNotes);
|
|
688
|
+
return { created: true };
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const existingContent = readFileSync(existingPath, 'utf-8');
|
|
692
|
+
|
|
693
|
+
// Add separator and append
|
|
694
|
+
const separator = `\n\n---\n\n## Additional Changes (v${version})\n\n`;
|
|
695
|
+
const appendContent = newNotes
|
|
696
|
+
.replace(/^#.*?\n/, '') // Remove first heading
|
|
697
|
+
.replace(/\*Generated by.*?\*\n?$/, ''); // Remove footer
|
|
698
|
+
|
|
699
|
+
const updatedContent = existingContent.trimEnd() + separator + appendContent + `\n\n*Updated: ${new Date().toISOString()}*\n`;
|
|
700
|
+
|
|
701
|
+
writeFileSync(existingPath, updatedContent);
|
|
702
|
+
return { appended: true };
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Update CHANGELOG.md with release notes
|
|
707
|
+
*/
|
|
708
|
+
function updateChangelog(targetDir, version, categories, since) {
|
|
709
|
+
const changelogPath = join(targetDir, 'CHANGELOG.md');
|
|
710
|
+
const date = new Date().toISOString().split('T')[0];
|
|
711
|
+
|
|
712
|
+
// Generate changelog entry
|
|
713
|
+
let entry = `## [${version}] - ${date}\n\n`;
|
|
714
|
+
|
|
715
|
+
const categoryOrder = ['breaking', 'security', 'features', 'fixes', 'improvements', 'perf', 'deps', 'docs'];
|
|
716
|
+
const categoryLabels = {
|
|
717
|
+
breaking: 'BREAKING CHANGES',
|
|
718
|
+
security: 'Security',
|
|
719
|
+
features: 'Added',
|
|
720
|
+
fixes: 'Fixed',
|
|
721
|
+
improvements: 'Changed',
|
|
722
|
+
perf: 'Performance',
|
|
723
|
+
deps: 'Dependencies',
|
|
724
|
+
docs: 'Documentation'
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
for (const category of categoryOrder) {
|
|
728
|
+
if (categories[category] && categories[category].length > 0) {
|
|
729
|
+
entry += `### ${categoryLabels[category]}\n\n`;
|
|
730
|
+
entry += categories[category].map(c => `- ${c}`).join('\n');
|
|
731
|
+
entry += '\n\n';
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// Create or update CHANGELOG.md
|
|
736
|
+
if (existsSync(changelogPath)) {
|
|
737
|
+
const existing = readFileSync(changelogPath, 'utf-8');
|
|
738
|
+
|
|
739
|
+
// Find insertion point (after header, before first version)
|
|
740
|
+
const headerMatch = existing.match(/^# Changelog.*?\n\n/s);
|
|
741
|
+
if (headerMatch) {
|
|
742
|
+
const header = headerMatch[0];
|
|
743
|
+
const rest = existing.slice(header.length);
|
|
744
|
+
const updated = header + entry + rest;
|
|
745
|
+
writeFileSync(changelogPath, updated);
|
|
746
|
+
} else {
|
|
747
|
+
// No header found, prepend
|
|
748
|
+
writeFileSync(changelogPath, `# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n${entry}${existing}`);
|
|
749
|
+
}
|
|
750
|
+
} else {
|
|
751
|
+
// Create new CHANGELOG.md
|
|
752
|
+
const content = `# Changelog
|
|
753
|
+
|
|
754
|
+
All notable changes to this project will be documented in this file.
|
|
755
|
+
|
|
756
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
757
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
758
|
+
|
|
759
|
+
${entry}`;
|
|
760
|
+
writeFileSync(changelogPath, content);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return { path: changelogPath, version };
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Create git tag for release
|
|
768
|
+
*/
|
|
769
|
+
function createGitTag(version, message) {
|
|
770
|
+
const tagName = version.startsWith('v') ? version : `v${version}`;
|
|
771
|
+
|
|
772
|
+
try {
|
|
773
|
+
// Check if tag already exists
|
|
774
|
+
try {
|
|
775
|
+
execSync(`git rev-parse ${tagName}`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
776
|
+
return { error: `Tag ${tagName} already exists` };
|
|
777
|
+
} catch {
|
|
778
|
+
// Tag doesn't exist, good to create
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Create annotated tag
|
|
782
|
+
const tagMessage = message || `Release ${tagName}`;
|
|
783
|
+
execSync(`git tag -a ${tagName} -m "${tagMessage}"`, { encoding: 'utf-8' });
|
|
784
|
+
|
|
785
|
+
return { success: true, tag: tagName };
|
|
786
|
+
} catch (error) {
|
|
787
|
+
return { error: error.message };
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Generate compact changelog entry for --changelog option
|
|
793
|
+
*/
|
|
794
|
+
function generateChangelogEntry(version, categories) {
|
|
795
|
+
const date = new Date().toISOString().split('T')[0];
|
|
796
|
+
let entry = `\n## [${version}] - ${date}\n`;
|
|
797
|
+
|
|
798
|
+
if (categories.breaking.length > 0) {
|
|
799
|
+
entry += `\n### BREAKING CHANGES\n${categories.breaking.map(c => `- ${c}`).join('\n')}\n`;
|
|
800
|
+
}
|
|
801
|
+
if (categories.features.length > 0) {
|
|
802
|
+
entry += `\n### Added\n${categories.features.map(c => `- ${c}`).join('\n')}\n`;
|
|
803
|
+
}
|
|
804
|
+
if (categories.fixes.length > 0) {
|
|
805
|
+
entry += `\n### Fixed\n${categories.fixes.map(c => `- ${c}`).join('\n')}\n`;
|
|
806
|
+
}
|
|
807
|
+
if (categories.improvements.length > 0) {
|
|
808
|
+
entry += `\n### Changed\n${categories.improvements.map(c => `- ${c}`).join('\n')}\n`;
|
|
809
|
+
}
|
|
810
|
+
if (categories.security.length > 0) {
|
|
811
|
+
entry += `\n### Security\n${categories.security.map(c => `- ${c}`).join('\n')}\n`;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
return entry;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Release command - generate release notes
|
|
819
|
+
*/
|
|
820
|
+
export async function releaseCommand(options = {}) {
|
|
821
|
+
const targetDir = process.cwd();
|
|
822
|
+
|
|
823
|
+
console.log(chalk.bold('\nProAgents Release Notes Generator'));
|
|
824
|
+
console.log(chalk.gray('==================================\n'));
|
|
825
|
+
|
|
826
|
+
// Get version
|
|
827
|
+
const currentVersion = getCurrentVersion(targetDir);
|
|
828
|
+
let version = options.version || currentVersion;
|
|
829
|
+
console.log(`Current Version: ${chalk.cyan('v' + currentVersion)}`);
|
|
830
|
+
|
|
831
|
+
// Get commits with filters
|
|
832
|
+
const commitOptions = {
|
|
833
|
+
since: options.since,
|
|
834
|
+
until: options.until,
|
|
835
|
+
path: options.path,
|
|
836
|
+
limit: options.limit || 100
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
const commits = getGitCommits(commitOptions);
|
|
840
|
+
const detectedSince = commitOptions.detectedSince;
|
|
841
|
+
|
|
842
|
+
if (!commits) {
|
|
843
|
+
console.log(chalk.yellow('\nNo commits found. Make sure you are in a git repository.'));
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const commitCount = commits.split('\n').filter(l => l.trim()).length;
|
|
848
|
+
console.log(`Commits: ${chalk.cyan(commitCount)} ${detectedSince ? `since ${detectedSince}` : ''}`);
|
|
849
|
+
|
|
850
|
+
if (options.path) {
|
|
851
|
+
console.log(`Path filter: ${chalk.cyan(options.path)}`);
|
|
852
|
+
}
|
|
853
|
+
if (options.module) {
|
|
854
|
+
console.log(`Module filter: ${chalk.cyan(options.module)}`);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// Select release type
|
|
858
|
+
let releaseType = options.type;
|
|
859
|
+
if (!releaseType) {
|
|
860
|
+
releaseType = await selectReleaseType();
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Select filters if interactive
|
|
864
|
+
let includeFilter = options.include ? options.include.split(',') : null;
|
|
865
|
+
let excludeFilter = options.exclude ? options.exclude.split(',') : null;
|
|
866
|
+
|
|
867
|
+
if (!includeFilter && !excludeFilter && options.interactive) {
|
|
868
|
+
includeFilter = await selectFilters();
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
console.log(chalk.cyan(`\nGenerating ${RELEASE_TYPES[releaseType].emoji} ${RELEASE_TYPES[releaseType].name}...`));
|
|
872
|
+
|
|
873
|
+
// Categorize commits
|
|
874
|
+
const categories = categorizeCommits(commits, {
|
|
875
|
+
include: includeFilter,
|
|
876
|
+
exclude: excludeFilter,
|
|
877
|
+
module: options.module
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
// Show version bump suggestion
|
|
881
|
+
if (options.bump) {
|
|
882
|
+
const suggestion = suggestVersionBump(currentVersion, categories);
|
|
883
|
+
console.log(chalk.bold('\nVersion Bump Suggestion:'));
|
|
884
|
+
console.log(` Type: ${chalk.yellow(suggestion.type.toUpperCase())}`);
|
|
885
|
+
console.log(` Reason: ${chalk.gray(suggestion.reason)}`);
|
|
886
|
+
console.log(` Suggested: ${chalk.green('v' + suggestion.suggested)}`);
|
|
887
|
+
|
|
888
|
+
if (!options.version) {
|
|
889
|
+
version = suggestion.suggested;
|
|
890
|
+
console.log(chalk.cyan(`\nUsing suggested version: v${version}`));
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// Generate notes based on type
|
|
895
|
+
const genOptions = {
|
|
896
|
+
prerelease: options.prerelease,
|
|
897
|
+
module: options.module,
|
|
898
|
+
urgency: options.urgency
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
let notes;
|
|
902
|
+
switch (releaseType) {
|
|
903
|
+
case 'detailed':
|
|
904
|
+
notes = generateDetailedNotes(version, categories, detectedSince, genOptions);
|
|
905
|
+
break;
|
|
906
|
+
case 'short':
|
|
907
|
+
notes = generateShortNotes(version, categories, detectedSince, genOptions);
|
|
908
|
+
break;
|
|
909
|
+
case 'client':
|
|
910
|
+
notes = generateClientNotes(version, categories, detectedSince, genOptions);
|
|
911
|
+
break;
|
|
912
|
+
case 'developer':
|
|
913
|
+
notes = generateDeveloperNotes(version, categories, detectedSince, genOptions);
|
|
914
|
+
break;
|
|
915
|
+
case 'hotfix':
|
|
916
|
+
notes = generateHotfixNotes(version, categories, detectedSince, genOptions);
|
|
917
|
+
break;
|
|
918
|
+
case 'prerelease':
|
|
919
|
+
notes = generatePrereleaseNotes(version, categories, detectedSince, genOptions);
|
|
920
|
+
break;
|
|
921
|
+
default:
|
|
922
|
+
notes = generateDetailedNotes(version, categories, detectedSince, genOptions);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// Output options
|
|
926
|
+
if (options.output) {
|
|
927
|
+
const outputPath = typeof options.output === 'string'
|
|
928
|
+
? options.output
|
|
929
|
+
: join(targetDir, `RELEASE_NOTES_v${version}.md`);
|
|
930
|
+
|
|
931
|
+
if (options.append && existsSync(outputPath)) {
|
|
932
|
+
const result = appendToExistingNotes(outputPath, notes, version);
|
|
933
|
+
console.log(chalk.green(`\n${RELEASE_TYPES[releaseType].emoji} Release notes appended to: ${outputPath}`));
|
|
934
|
+
} else {
|
|
935
|
+
writeFileSync(outputPath, notes);
|
|
936
|
+
console.log(chalk.green(`\n${RELEASE_TYPES[releaseType].emoji} Release notes saved to: ${outputPath}`));
|
|
937
|
+
}
|
|
938
|
+
} else {
|
|
939
|
+
console.log(chalk.gray('\n' + 'โ'.repeat(50) + '\n'));
|
|
940
|
+
console.log(notes);
|
|
941
|
+
console.log(chalk.gray('โ'.repeat(50)));
|
|
942
|
+
console.log(chalk.gray('\nTip: Use --output to save to file'));
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// Update CHANGELOG.md if requested
|
|
946
|
+
if (options.changelog) {
|
|
947
|
+
const changelogResult = updateChangelog(targetDir, version, categories, detectedSince);
|
|
948
|
+
if (!options.json) {
|
|
949
|
+
console.log(chalk.green(`\n๐ CHANGELOG.md updated with v${version}`));
|
|
950
|
+
}
|
|
951
|
+
if (options.json) {
|
|
952
|
+
jsonResult.changelog = changelogResult;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// Create git tag if requested
|
|
957
|
+
if (options.tag) {
|
|
958
|
+
const tagMessage = options.tagMessage || `Release v${version}`;
|
|
959
|
+
const tagResult = createGitTag(version, tagMessage);
|
|
960
|
+
|
|
961
|
+
if (!options.json) {
|
|
962
|
+
if (tagResult.success) {
|
|
963
|
+
console.log(chalk.green(`\n๐ท๏ธ Created git tag: ${tagResult.tag}`));
|
|
964
|
+
} else {
|
|
965
|
+
console.log(chalk.yellow(`\nโ ๏ธ Tag creation failed: ${tagResult.error}`));
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
if (options.json) {
|
|
969
|
+
jsonResult.tag = tagResult;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// Summary
|
|
974
|
+
const summaryItems = [
|
|
975
|
+
{ label: 'Features', count: categories.features.length, color: chalk.green },
|
|
976
|
+
{ label: 'Fixes', count: categories.fixes.length, color: chalk.blue },
|
|
977
|
+
{ label: 'Improvements', count: categories.improvements.length, color: chalk.cyan },
|
|
978
|
+
{ label: 'Security', count: categories.security.length, color: chalk.yellow },
|
|
979
|
+
{ label: 'Breaking', count: categories.breaking.length, color: chalk.red },
|
|
980
|
+
{ label: 'Performance', count: categories.perf.length, color: chalk.magenta },
|
|
981
|
+
{ label: 'Docs', count: categories.docs.length, color: chalk.gray },
|
|
982
|
+
{ label: 'Dependencies', count: categories.deps.length, color: chalk.white }
|
|
983
|
+
];
|
|
984
|
+
|
|
985
|
+
// JSON output mode
|
|
986
|
+
if (options.json) {
|
|
987
|
+
jsonResult.version = version;
|
|
988
|
+
jsonResult.releaseType = releaseType;
|
|
989
|
+
jsonResult.categories = {};
|
|
990
|
+
for (const item of summaryItems) {
|
|
991
|
+
if (item.count > 0) {
|
|
992
|
+
jsonResult.categories[item.label.toLowerCase()] = item.count;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
jsonResult.notes = notes;
|
|
996
|
+
console.log(JSON.stringify(jsonResult, null, 2));
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
console.log(chalk.bold('\nSummary:'));
|
|
1001
|
+
for (const item of summaryItems) {
|
|
1002
|
+
if (item.count > 0) {
|
|
1003
|
+
console.log(` ${item.color(item.label + ':')} ${item.count}`);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
console.log('');
|
|
1007
|
+
}
|