@releasekit/notes 0.2.0-next.1 → 0.2.0-next.11
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/dist/{chunk-W7DVGQ7D.js → chunk-3VS3PBTN.js} +178 -56
- package/dist/cli.cjs +239 -108
- package/dist/cli.js +11 -2
- package/dist/index.cjs +212 -90
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
// src/cli.ts
|
|
27
27
|
var fs10 = __toESM(require("fs"), 1);
|
|
28
28
|
var readline = __toESM(require("readline"), 1);
|
|
29
|
-
var
|
|
29
|
+
var import_core10 = require("@releasekit/core");
|
|
30
30
|
var import_commander = require("commander");
|
|
31
31
|
|
|
32
32
|
// src/core/config.ts
|
|
@@ -45,7 +45,7 @@ function getDefaultConfig() {
|
|
|
45
45
|
// src/core/pipeline.ts
|
|
46
46
|
var fs8 = __toESM(require("fs"), 1);
|
|
47
47
|
var path6 = __toESM(require("path"), 1);
|
|
48
|
-
var
|
|
48
|
+
var import_core8 = require("@releasekit/core");
|
|
49
49
|
|
|
50
50
|
// src/input/package-versioner.ts
|
|
51
51
|
var fs = __toESM(require("fs"), 1);
|
|
@@ -366,15 +366,26 @@ Entries:
|
|
|
366
366
|
Output only valid JSON, nothing else:`;
|
|
367
367
|
function buildCustomCategorizePrompt(categories) {
|
|
368
368
|
const categoryList = categories.map((c) => `- "${c.name}": ${c.description}`).join("\n");
|
|
369
|
+
const developerCategory = categories.find((c) => c.name === "Developer");
|
|
370
|
+
let scopeInstructions = "";
|
|
371
|
+
if (developerCategory) {
|
|
372
|
+
const scopeMatch = developerCategory.description.match(/from:\s*([^.]+)/);
|
|
373
|
+
if (scopeMatch?.[1]) {
|
|
374
|
+
const scopes = scopeMatch[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
375
|
+
if (scopes.length > 0) {
|
|
376
|
+
scopeInstructions = `
|
|
377
|
+
|
|
378
|
+
For the "Developer" category, you MUST assign a scope from this exact list: ${scopes.join(", ")}.
|
|
379
|
+
`;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
369
383
|
return `You are categorizing changelog entries for a software release.
|
|
370
384
|
|
|
371
|
-
Given the following entries, group them into the specified categories. Only use the categories listed below
|
|
385
|
+
Given the following entries, group them into the specified categories. Only use the categories listed below in this exact order:
|
|
372
386
|
|
|
373
387
|
Categories:
|
|
374
|
-
${categoryList}
|
|
375
|
-
|
|
376
|
-
For entries in categories that involve internal/developer changes, set a "scope" field on those entries with a short subcategory label (e.g., "CI", "Dependencies", "Testing", "Code Quality", "Build System").
|
|
377
|
-
|
|
388
|
+
${categoryList}${scopeInstructions}
|
|
378
389
|
Output a JSON object with two fields:
|
|
379
390
|
- "categories": an object where keys are category names and values are arrays of entry indices (0-based)
|
|
380
391
|
- "scopes": an object where keys are entry indices (as strings) and values are scope labels
|
|
@@ -474,6 +485,113 @@ async function enhanceEntries(provider, entries, context, concurrency = LLM_DEFA
|
|
|
474
485
|
return results;
|
|
475
486
|
}
|
|
476
487
|
|
|
488
|
+
// src/llm/tasks/enhance-and-categorize.ts
|
|
489
|
+
var import_core4 = require("@releasekit/core");
|
|
490
|
+
|
|
491
|
+
// src/utils/retry.ts
|
|
492
|
+
function sleep(ms) {
|
|
493
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
494
|
+
}
|
|
495
|
+
async function withRetry(fn, options = {}) {
|
|
496
|
+
const maxAttempts = options.maxAttempts ?? 3;
|
|
497
|
+
const initialDelay = options.initialDelay ?? 1e3;
|
|
498
|
+
const maxDelay = options.maxDelay ?? 3e4;
|
|
499
|
+
const backoffFactor = options.backoffFactor ?? 2;
|
|
500
|
+
let lastError;
|
|
501
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
502
|
+
try {
|
|
503
|
+
return await fn();
|
|
504
|
+
} catch (error2) {
|
|
505
|
+
lastError = error2;
|
|
506
|
+
if (attempt < maxAttempts - 1) {
|
|
507
|
+
const base = Math.min(initialDelay * backoffFactor ** attempt, maxDelay);
|
|
508
|
+
const jitter = base * 0.2 * (Math.random() * 2 - 1);
|
|
509
|
+
await sleep(Math.max(0, base + jitter));
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
throw lastError;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// src/llm/tasks/enhance-and-categorize.ts
|
|
517
|
+
function buildPrompt(entries, categories, style) {
|
|
518
|
+
const entriesText = entries.map((e, i) => `${i}. [${e.type}]${e.scope ? ` (${e.scope})` : ""}: ${e.description}`).join("\n");
|
|
519
|
+
const styleText = style || 'Use present tense ("Add feature" not "Added feature"). Be concise.';
|
|
520
|
+
const categorySection = categories ? `Categories (use ONLY these):
|
|
521
|
+
${categories.map((c) => `- "${c.name}": ${c.description}`).join("\n")}` : `Categories: Group into meaningful categories (e.g., "New", "Fixed", "Changed", "Removed").`;
|
|
522
|
+
return `You are generating release notes for a software project. Given the following changelog entries, do two things:
|
|
523
|
+
|
|
524
|
+
1. **Rewrite** each entry as a clear, user-friendly description
|
|
525
|
+
2. **Categorize** each entry into the appropriate category
|
|
526
|
+
|
|
527
|
+
Style guidelines:
|
|
528
|
+
- ${styleText}
|
|
529
|
+
- Be concise (1 short sentence per entry)
|
|
530
|
+
- Focus on what changed, not implementation details
|
|
531
|
+
|
|
532
|
+
${categorySection}
|
|
533
|
+
|
|
534
|
+
${categories ? 'For entries in categories involving internal/developer changes, set a "scope" field with a short subcategory label (e.g., "CI", "Dependencies", "Testing").' : ""}
|
|
535
|
+
|
|
536
|
+
Entries:
|
|
537
|
+
${entriesText}
|
|
538
|
+
|
|
539
|
+
Output a JSON object with:
|
|
540
|
+
- "entries": array of objects, one per input entry (same order), each with: { "description": "rewritten text", "category": "CategoryName", "scope": "optional subcategory label or null" }
|
|
541
|
+
|
|
542
|
+
Output only valid JSON, nothing else:`;
|
|
543
|
+
}
|
|
544
|
+
async function enhanceAndCategorize(provider, entries, context) {
|
|
545
|
+
if (entries.length === 0) {
|
|
546
|
+
return { enhancedEntries: [], categories: [] };
|
|
547
|
+
}
|
|
548
|
+
const retryOpts = LLM_DEFAULTS.retry;
|
|
549
|
+
try {
|
|
550
|
+
return await withRetry(async () => {
|
|
551
|
+
const prompt = buildPrompt(entries, context.categories, context.style);
|
|
552
|
+
const response = await provider.complete(prompt);
|
|
553
|
+
const cleaned = response.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "").trim();
|
|
554
|
+
const parsed = JSON.parse(cleaned);
|
|
555
|
+
if (!Array.isArray(parsed.entries)) {
|
|
556
|
+
throw new Error('Response missing "entries" array');
|
|
557
|
+
}
|
|
558
|
+
const enhancedEntries = entries.map((original, i) => {
|
|
559
|
+
const result = parsed.entries[i];
|
|
560
|
+
if (!result) return original;
|
|
561
|
+
return {
|
|
562
|
+
...original,
|
|
563
|
+
description: result.description || original.description,
|
|
564
|
+
scope: result.scope || original.scope
|
|
565
|
+
};
|
|
566
|
+
});
|
|
567
|
+
const categoryMap = /* @__PURE__ */ new Map();
|
|
568
|
+
for (let i = 0; i < parsed.entries.length; i++) {
|
|
569
|
+
const result = parsed.entries[i];
|
|
570
|
+
const category = result?.category || "General";
|
|
571
|
+
const entry = enhancedEntries[i];
|
|
572
|
+
if (!entry) continue;
|
|
573
|
+
if (!categoryMap.has(category)) {
|
|
574
|
+
categoryMap.set(category, []);
|
|
575
|
+
}
|
|
576
|
+
categoryMap.get(category).push(entry);
|
|
577
|
+
}
|
|
578
|
+
const categories = [];
|
|
579
|
+
for (const [category, catEntries] of categoryMap) {
|
|
580
|
+
categories.push({ category, entries: catEntries });
|
|
581
|
+
}
|
|
582
|
+
return { enhancedEntries, categories };
|
|
583
|
+
}, retryOpts);
|
|
584
|
+
} catch (error2) {
|
|
585
|
+
(0, import_core4.warn)(
|
|
586
|
+
`Combined enhance+categorize failed after ${retryOpts.maxAttempts} attempts: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
587
|
+
);
|
|
588
|
+
return {
|
|
589
|
+
enhancedEntries: entries,
|
|
590
|
+
categories: [{ category: "General", entries }]
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
477
595
|
// src/llm/tasks/release-notes.ts
|
|
478
596
|
var RELEASE_NOTES_PROMPT = `You are writing release notes for a software project.
|
|
479
597
|
|
|
@@ -574,12 +692,12 @@ function createProvider(config) {
|
|
|
574
692
|
|
|
575
693
|
// src/output/github-release.ts
|
|
576
694
|
var import_rest = require("@octokit/rest");
|
|
577
|
-
var
|
|
695
|
+
var import_core6 = require("@releasekit/core");
|
|
578
696
|
|
|
579
697
|
// src/output/markdown.ts
|
|
580
698
|
var fs2 = __toESM(require("fs"), 1);
|
|
581
699
|
var path = __toESM(require("path"), 1);
|
|
582
|
-
var
|
|
700
|
+
var import_core5 = require("@releasekit/core");
|
|
583
701
|
var TYPE_ORDER = ["added", "changed", "deprecated", "removed", "fixed", "security"];
|
|
584
702
|
var TYPE_LABELS = {
|
|
585
703
|
added: "Added",
|
|
@@ -678,15 +796,19 @@ ${body}`;
|
|
|
678
796
|
function writeMarkdown(outputPath, contexts, config, dryRun) {
|
|
679
797
|
const content = renderMarkdown(contexts);
|
|
680
798
|
if (dryRun) {
|
|
681
|
-
(0,
|
|
799
|
+
(0, import_core5.info)("--- Changelog Preview ---");
|
|
682
800
|
console.log(content);
|
|
683
|
-
(0,
|
|
801
|
+
(0, import_core5.info)("--- End Preview ---");
|
|
684
802
|
return;
|
|
685
803
|
}
|
|
686
804
|
const dir = path.dirname(outputPath);
|
|
687
805
|
if (!fs2.existsSync(dir)) {
|
|
688
806
|
fs2.mkdirSync(dir, { recursive: true });
|
|
689
807
|
}
|
|
808
|
+
if (outputPath === "-") {
|
|
809
|
+
process.stdout.write(content);
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
690
812
|
if (config.updateStrategy === "prepend" && fs2.existsSync(outputPath) && contexts.length === 1) {
|
|
691
813
|
const firstContext = contexts[0];
|
|
692
814
|
if (firstContext) {
|
|
@@ -696,7 +818,7 @@ function writeMarkdown(outputPath, contexts, config, dryRun) {
|
|
|
696
818
|
} else {
|
|
697
819
|
fs2.writeFileSync(outputPath, content, "utf-8");
|
|
698
820
|
}
|
|
699
|
-
(0,
|
|
821
|
+
(0, import_core5.success)(`Changelog written to ${outputPath}`);
|
|
700
822
|
}
|
|
701
823
|
|
|
702
824
|
// src/output/github-release.ts
|
|
@@ -721,7 +843,7 @@ var GitHubClient = class {
|
|
|
721
843
|
} else {
|
|
722
844
|
body = renderMarkdown([context]);
|
|
723
845
|
}
|
|
724
|
-
(0,
|
|
846
|
+
(0, import_core6.info)(`Creating GitHub release for ${tagName}`);
|
|
725
847
|
try {
|
|
726
848
|
const response = await this.octokit.repos.createRelease({
|
|
727
849
|
owner: this.owner,
|
|
@@ -733,7 +855,7 @@ var GitHubClient = class {
|
|
|
733
855
|
prerelease: options.prerelease ?? false,
|
|
734
856
|
generate_release_notes: options.generateNotes ?? false
|
|
735
857
|
});
|
|
736
|
-
(0,
|
|
858
|
+
(0, import_core6.success)(`Release created: ${response.data.html_url}`);
|
|
737
859
|
return {
|
|
738
860
|
id: response.data.id,
|
|
739
861
|
htmlUrl: response.data.html_url,
|
|
@@ -751,7 +873,7 @@ var GitHubClient = class {
|
|
|
751
873
|
} else {
|
|
752
874
|
body = renderMarkdown([context]);
|
|
753
875
|
}
|
|
754
|
-
(0,
|
|
876
|
+
(0, import_core6.info)(`Updating GitHub release ${releaseId}`);
|
|
755
877
|
try {
|
|
756
878
|
const response = await this.octokit.repos.updateRelease({
|
|
757
879
|
owner: this.owner,
|
|
@@ -763,7 +885,7 @@ var GitHubClient = class {
|
|
|
763
885
|
draft: options.draft ?? false,
|
|
764
886
|
prerelease: options.prerelease ?? false
|
|
765
887
|
});
|
|
766
|
-
(0,
|
|
888
|
+
(0, import_core6.success)(`Release updated: ${response.data.html_url}`);
|
|
767
889
|
return {
|
|
768
890
|
id: response.data.id,
|
|
769
891
|
htmlUrl: response.data.html_url,
|
|
@@ -815,7 +937,7 @@ async function createGitHubRelease(context, options) {
|
|
|
815
937
|
// src/output/json.ts
|
|
816
938
|
var fs3 = __toESM(require("fs"), 1);
|
|
817
939
|
var path2 = __toESM(require("path"), 1);
|
|
818
|
-
var
|
|
940
|
+
var import_core7 = require("@releasekit/core");
|
|
819
941
|
function renderJson(contexts) {
|
|
820
942
|
return JSON.stringify(
|
|
821
943
|
{
|
|
@@ -835,9 +957,9 @@ function renderJson(contexts) {
|
|
|
835
957
|
function writeJson(outputPath, contexts, dryRun) {
|
|
836
958
|
const content = renderJson(contexts);
|
|
837
959
|
if (dryRun) {
|
|
838
|
-
(0,
|
|
960
|
+
(0, import_core7.info)("--- JSON Output Preview ---");
|
|
839
961
|
console.log(content);
|
|
840
|
-
(0,
|
|
962
|
+
(0, import_core7.info)("--- End Preview ---");
|
|
841
963
|
return;
|
|
842
964
|
}
|
|
843
965
|
const dir = path2.dirname(outputPath);
|
|
@@ -845,7 +967,7 @@ function writeJson(outputPath, contexts, dryRun) {
|
|
|
845
967
|
fs3.mkdirSync(dir, { recursive: true });
|
|
846
968
|
}
|
|
847
969
|
fs3.writeFileSync(outputPath, content, "utf-8");
|
|
848
|
-
(0,
|
|
970
|
+
(0, import_core7.success)(`JSON output written to ${outputPath}`);
|
|
849
971
|
}
|
|
850
972
|
|
|
851
973
|
// src/templates/ejs.ts
|
|
@@ -1102,44 +1224,29 @@ function renderTemplate(templatePath, context, engine) {
|
|
|
1102
1224
|
return renderComposable(templatePath, context, engine);
|
|
1103
1225
|
}
|
|
1104
1226
|
|
|
1105
|
-
// src/utils/retry.ts
|
|
1106
|
-
function sleep(ms) {
|
|
1107
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1108
|
-
}
|
|
1109
|
-
async function withRetry(fn, options = {}) {
|
|
1110
|
-
const maxAttempts = options.maxAttempts ?? 3;
|
|
1111
|
-
const initialDelay = options.initialDelay ?? 1e3;
|
|
1112
|
-
const maxDelay = options.maxDelay ?? 3e4;
|
|
1113
|
-
const backoffFactor = options.backoffFactor ?? 2;
|
|
1114
|
-
let lastError;
|
|
1115
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1116
|
-
try {
|
|
1117
|
-
return await fn();
|
|
1118
|
-
} catch (error2) {
|
|
1119
|
-
lastError = error2;
|
|
1120
|
-
if (attempt < maxAttempts - 1) {
|
|
1121
|
-
const base = Math.min(initialDelay * backoffFactor ** attempt, maxDelay);
|
|
1122
|
-
const jitter = base * 0.2 * (Math.random() * 2 - 1);
|
|
1123
|
-
await sleep(Math.max(0, base + jitter));
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
throw lastError;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
1227
|
// src/core/pipeline.ts
|
|
1131
1228
|
var import_meta = {};
|
|
1132
|
-
function generateCompareUrl(repoUrl, from, to) {
|
|
1229
|
+
function generateCompareUrl(repoUrl, from, to, packageName) {
|
|
1230
|
+
const isPackageSpecific = from.includes("@") && packageName && from.includes(packageName);
|
|
1231
|
+
let fromVersion;
|
|
1232
|
+
let toVersion;
|
|
1233
|
+
if (isPackageSpecific) {
|
|
1234
|
+
fromVersion = from;
|
|
1235
|
+
toVersion = `${packageName}@${to.startsWith("v") ? "" : "v"}${to}`;
|
|
1236
|
+
} else {
|
|
1237
|
+
fromVersion = from.replace(/^v/, "");
|
|
1238
|
+
toVersion = to.replace(/^v/, "");
|
|
1239
|
+
}
|
|
1133
1240
|
if (/gitlab\.com/i.test(repoUrl)) {
|
|
1134
|
-
return `${repoUrl}/-/compare/${
|
|
1241
|
+
return `${repoUrl}/-/compare/${fromVersion}...${toVersion}`;
|
|
1135
1242
|
}
|
|
1136
1243
|
if (/bitbucket\.org/i.test(repoUrl)) {
|
|
1137
|
-
return `${repoUrl}/branches/compare/${
|
|
1244
|
+
return `${repoUrl}/branches/compare/${fromVersion}..${toVersion}`;
|
|
1138
1245
|
}
|
|
1139
|
-
return `${repoUrl}/compare/${
|
|
1246
|
+
return `${repoUrl}/compare/${fromVersion}...${toVersion}`;
|
|
1140
1247
|
}
|
|
1141
1248
|
function createTemplateContext(pkg) {
|
|
1142
|
-
const compareUrl = pkg.repoUrl && pkg.previousVersion ? generateCompareUrl(pkg.repoUrl, pkg.previousVersion, pkg.version) : void 0;
|
|
1249
|
+
const compareUrl = pkg.repoUrl && pkg.previousVersion ? generateCompareUrl(pkg.repoUrl, pkg.previousVersion, pkg.version, pkg.packageName) : void 0;
|
|
1143
1250
|
return {
|
|
1144
1251
|
packageName: pkg.packageName,
|
|
1145
1252
|
version: pkg.version,
|
|
@@ -1183,9 +1290,9 @@ async function processWithLLM(context, config) {
|
|
|
1183
1290
|
entries: context.entries
|
|
1184
1291
|
};
|
|
1185
1292
|
try {
|
|
1186
|
-
(0,
|
|
1293
|
+
(0, import_core8.info)(`Using LLM provider: ${config.llm.provider}${config.llm.model ? ` (${config.llm.model})` : ""}`);
|
|
1187
1294
|
if (config.llm.baseURL) {
|
|
1188
|
-
(0,
|
|
1295
|
+
(0, import_core8.info)(`LLM base URL: ${config.llm.baseURL}`);
|
|
1189
1296
|
}
|
|
1190
1297
|
const rawProvider = createProvider(config.llm);
|
|
1191
1298
|
const retryOpts = config.llm.retry ?? LLM_DEFAULTS.retry;
|
|
@@ -1194,38 +1301,49 @@ async function processWithLLM(context, config) {
|
|
|
1194
1301
|
complete: (prompt, opts) => withRetry(() => rawProvider.complete(prompt, opts), retryOpts)
|
|
1195
1302
|
};
|
|
1196
1303
|
const activeTasks = Object.entries(tasks).filter(([, enabled]) => enabled).map(([name]) => name);
|
|
1197
|
-
(0,
|
|
1198
|
-
if (tasks.enhance) {
|
|
1199
|
-
(0,
|
|
1200
|
-
|
|
1201
|
-
|
|
1304
|
+
(0, import_core8.info)(`Running LLM tasks: ${activeTasks.join(", ")}`);
|
|
1305
|
+
if (tasks.enhance && tasks.categorize) {
|
|
1306
|
+
(0, import_core8.info)("Enhancing and categorizing entries with LLM...");
|
|
1307
|
+
const result = await enhanceAndCategorize(provider, context.entries, llmContext);
|
|
1308
|
+
enhanced.entries = result.enhancedEntries;
|
|
1309
|
+
enhanced.categories = {};
|
|
1310
|
+
for (const cat of result.categories) {
|
|
1311
|
+
enhanced.categories[cat.category] = cat.entries;
|
|
1312
|
+
}
|
|
1313
|
+
(0, import_core8.info)(`Enhanced ${enhanced.entries.length} entries into ${result.categories.length} categories`);
|
|
1314
|
+
} else {
|
|
1315
|
+
if (tasks.enhance) {
|
|
1316
|
+
(0, import_core8.info)("Enhancing entries with LLM...");
|
|
1317
|
+
enhanced.entries = await enhanceEntries(provider, context.entries, llmContext, config.llm.concurrency);
|
|
1318
|
+
(0, import_core8.info)(`Enhanced ${enhanced.entries.length} entries`);
|
|
1319
|
+
}
|
|
1320
|
+
if (tasks.categorize) {
|
|
1321
|
+
(0, import_core8.info)("Categorizing entries with LLM...");
|
|
1322
|
+
const categorized = await categorizeEntries(provider, enhanced.entries, llmContext);
|
|
1323
|
+
enhanced.categories = {};
|
|
1324
|
+
for (const cat of categorized) {
|
|
1325
|
+
enhanced.categories[cat.category] = cat.entries;
|
|
1326
|
+
}
|
|
1327
|
+
(0, import_core8.info)(`Created ${categorized.length} categories`);
|
|
1328
|
+
}
|
|
1202
1329
|
}
|
|
1203
1330
|
if (tasks.summarize) {
|
|
1204
|
-
(0,
|
|
1331
|
+
(0, import_core8.info)("Summarizing entries with LLM...");
|
|
1205
1332
|
enhanced.summary = await summarizeEntries(provider, enhanced.entries, llmContext);
|
|
1206
1333
|
if (enhanced.summary) {
|
|
1207
|
-
(0,
|
|
1208
|
-
(0,
|
|
1334
|
+
(0, import_core8.info)("Summary generated successfully");
|
|
1335
|
+
(0, import_core8.debug)(`Summary: ${enhanced.summary.substring(0, 100)}...`);
|
|
1209
1336
|
} else {
|
|
1210
|
-
(0,
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
if (tasks.categorize) {
|
|
1214
|
-
(0, import_core7.info)("Categorizing entries with LLM...");
|
|
1215
|
-
const categorized = await categorizeEntries(provider, enhanced.entries, llmContext);
|
|
1216
|
-
enhanced.categories = {};
|
|
1217
|
-
for (const cat of categorized) {
|
|
1218
|
-
enhanced.categories[cat.category] = cat.entries;
|
|
1337
|
+
(0, import_core8.warn)("Summary generation returned empty result");
|
|
1219
1338
|
}
|
|
1220
|
-
(0, import_core7.info)(`Created ${categorized.length} categories`);
|
|
1221
1339
|
}
|
|
1222
1340
|
if (tasks.releaseNotes) {
|
|
1223
|
-
(0,
|
|
1341
|
+
(0, import_core8.info)("Generating release notes with LLM...");
|
|
1224
1342
|
enhanced.releaseNotes = await generateReleaseNotes(provider, enhanced.entries, llmContext);
|
|
1225
1343
|
if (enhanced.releaseNotes) {
|
|
1226
|
-
(0,
|
|
1344
|
+
(0, import_core8.info)("Release notes generated successfully");
|
|
1227
1345
|
} else {
|
|
1228
|
-
(0,
|
|
1346
|
+
(0, import_core8.warn)("Release notes generation returned empty result");
|
|
1229
1347
|
}
|
|
1230
1348
|
}
|
|
1231
1349
|
return {
|
|
@@ -1234,8 +1352,8 @@ async function processWithLLM(context, config) {
|
|
|
1234
1352
|
enhanced
|
|
1235
1353
|
};
|
|
1236
1354
|
} catch (error2) {
|
|
1237
|
-
(0,
|
|
1238
|
-
(0,
|
|
1355
|
+
(0, import_core8.warn)(`LLM processing failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
1356
|
+
(0, import_core8.warn)("Falling back to raw entries");
|
|
1239
1357
|
return context;
|
|
1240
1358
|
}
|
|
1241
1359
|
}
|
|
@@ -1263,9 +1381,13 @@ async function generateWithTemplate(contexts, config, outputPath, dryRun) {
|
|
|
1263
1381
|
);
|
|
1264
1382
|
const result = renderTemplate(templatePath, documentContext, config.templates?.engine);
|
|
1265
1383
|
if (dryRun) {
|
|
1266
|
-
(0,
|
|
1384
|
+
(0, import_core8.info)("--- Changelog Preview ---");
|
|
1267
1385
|
console.log(result.content);
|
|
1268
|
-
(0,
|
|
1386
|
+
(0, import_core8.info)("--- End Preview ---");
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
if (outputPath === "-") {
|
|
1390
|
+
process.stdout.write(result.content);
|
|
1269
1391
|
return;
|
|
1270
1392
|
}
|
|
1271
1393
|
const dir = path6.dirname(outputPath);
|
|
@@ -1273,17 +1395,17 @@ async function generateWithTemplate(contexts, config, outputPath, dryRun) {
|
|
|
1273
1395
|
fs8.mkdirSync(dir, { recursive: true });
|
|
1274
1396
|
}
|
|
1275
1397
|
fs8.writeFileSync(outputPath, result.content, "utf-8");
|
|
1276
|
-
(0,
|
|
1398
|
+
(0, import_core8.success)(`Changelog written to ${outputPath} (using ${result.engine} template)`);
|
|
1277
1399
|
}
|
|
1278
1400
|
async function runPipeline(input, config, dryRun) {
|
|
1279
|
-
(0,
|
|
1401
|
+
(0, import_core8.debug)(`Processing ${input.packages.length} package(s)`);
|
|
1280
1402
|
let contexts = input.packages.map(createTemplateContext);
|
|
1281
1403
|
if (config.llm && !process.env.CHANGELOG_NO_LLM) {
|
|
1282
|
-
(0,
|
|
1404
|
+
(0, import_core8.info)("Processing with LLM enhancement");
|
|
1283
1405
|
contexts = await Promise.all(contexts.map((ctx) => processWithLLM(ctx, config)));
|
|
1284
1406
|
}
|
|
1285
1407
|
for (const output of config.output) {
|
|
1286
|
-
(0,
|
|
1408
|
+
(0, import_core8.info)(`Generating ${output.format} output`);
|
|
1287
1409
|
switch (output.format) {
|
|
1288
1410
|
case "markdown": {
|
|
1289
1411
|
const file = output.file ?? "CHANGELOG.md";
|
|
@@ -1301,22 +1423,22 @@ async function runPipeline(input, config, dryRun) {
|
|
|
1301
1423
|
}
|
|
1302
1424
|
case "github-release": {
|
|
1303
1425
|
if (dryRun) {
|
|
1304
|
-
(0,
|
|
1426
|
+
(0, import_core8.info)("[DRY RUN] Would create GitHub release");
|
|
1305
1427
|
break;
|
|
1306
1428
|
}
|
|
1307
1429
|
const firstContext = contexts[0];
|
|
1308
1430
|
if (!firstContext) {
|
|
1309
|
-
(0,
|
|
1431
|
+
(0, import_core8.warn)("No context available for GitHub release");
|
|
1310
1432
|
break;
|
|
1311
1433
|
}
|
|
1312
1434
|
const repoUrl = firstContext.repoUrl;
|
|
1313
1435
|
if (!repoUrl) {
|
|
1314
|
-
(0,
|
|
1436
|
+
(0, import_core8.warn)("No repo URL available, cannot create GitHub release");
|
|
1315
1437
|
break;
|
|
1316
1438
|
}
|
|
1317
1439
|
const parsed = parseRepoUrl(repoUrl);
|
|
1318
1440
|
if (!parsed) {
|
|
1319
|
-
(0,
|
|
1441
|
+
(0, import_core8.warn)(`Could not parse repo URL: ${repoUrl}`);
|
|
1320
1442
|
break;
|
|
1321
1443
|
}
|
|
1322
1444
|
await createGitHubRelease(firstContext, {
|
|
@@ -1334,7 +1456,7 @@ async function runPipeline(input, config, dryRun) {
|
|
|
1334
1456
|
// src/monorepo/aggregator.ts
|
|
1335
1457
|
var fs9 = __toESM(require("fs"), 1);
|
|
1336
1458
|
var path7 = __toESM(require("path"), 1);
|
|
1337
|
-
var
|
|
1459
|
+
var import_core9 = require("@releasekit/core");
|
|
1338
1460
|
|
|
1339
1461
|
// src/monorepo/splitter.ts
|
|
1340
1462
|
function splitByPackage(contexts) {
|
|
@@ -1348,7 +1470,7 @@ function splitByPackage(contexts) {
|
|
|
1348
1470
|
// src/monorepo/aggregator.ts
|
|
1349
1471
|
function writeFile(outputPath, content, dryRun) {
|
|
1350
1472
|
if (dryRun) {
|
|
1351
|
-
(0,
|
|
1473
|
+
(0, import_core9.info)(`[DRY RUN] Would write to ${outputPath}`);
|
|
1352
1474
|
console.log(content);
|
|
1353
1475
|
return;
|
|
1354
1476
|
}
|
|
@@ -1357,7 +1479,7 @@ function writeFile(outputPath, content, dryRun) {
|
|
|
1357
1479
|
fs9.mkdirSync(dir, { recursive: true });
|
|
1358
1480
|
}
|
|
1359
1481
|
fs9.writeFileSync(outputPath, content, "utf-8");
|
|
1360
|
-
(0,
|
|
1482
|
+
(0, import_core9.success)(`Changelog written to ${outputPath}`);
|
|
1361
1483
|
}
|
|
1362
1484
|
function aggregateToRoot(contexts) {
|
|
1363
1485
|
const aggregated = {
|
|
@@ -1382,7 +1504,7 @@ function writeMonorepoChangelogs(contexts, options, config, dryRun) {
|
|
|
1382
1504
|
if (options.mode === "root" || options.mode === "both") {
|
|
1383
1505
|
const aggregated = aggregateToRoot(contexts);
|
|
1384
1506
|
const rootPath = path7.join(options.rootPath, "CHANGELOG.md");
|
|
1385
|
-
(0,
|
|
1507
|
+
(0, import_core9.info)(`Writing root changelog to ${rootPath}`);
|
|
1386
1508
|
const rootContent = config.updateStrategy === "prepend" && fs9.existsSync(rootPath) ? prependVersion(rootPath, aggregated) : renderMarkdown([aggregated]);
|
|
1387
1509
|
writeFile(rootPath, rootContent, dryRun);
|
|
1388
1510
|
}
|
|
@@ -1394,11 +1516,11 @@ function writeMonorepoChangelogs(contexts, options, config, dryRun) {
|
|
|
1394
1516
|
const packageDir = packageDirMap.get(packageName) ?? (simpleName ? packageDirMap.get(simpleName) : void 0) ?? null;
|
|
1395
1517
|
if (packageDir) {
|
|
1396
1518
|
const changelogPath = path7.join(packageDir, "CHANGELOG.md");
|
|
1397
|
-
(0,
|
|
1519
|
+
(0, import_core9.info)(`Writing changelog for ${packageName} to ${changelogPath}`);
|
|
1398
1520
|
const pkgContent = config.updateStrategy === "prepend" && fs9.existsSync(changelogPath) ? prependVersion(changelogPath, ctx) : renderMarkdown([ctx]);
|
|
1399
1521
|
writeFile(changelogPath, pkgContent, dryRun);
|
|
1400
1522
|
} else {
|
|
1401
|
-
(0,
|
|
1523
|
+
(0, import_core9.info)(`Could not find directory for package ${packageName}, skipping`);
|
|
1402
1524
|
}
|
|
1403
1525
|
}
|
|
1404
1526
|
}
|
|
@@ -1463,9 +1585,9 @@ function detectMonorepo(cwd) {
|
|
|
1463
1585
|
// src/cli.ts
|
|
1464
1586
|
var program = new import_commander.Command();
|
|
1465
1587
|
program.name("releasekit-notes").description("Generate changelogs with LLM-powered enhancement and flexible templating").version("0.1.0");
|
|
1466
|
-
program.command("generate", { isDefault: true }).description("Generate changelog from input data").option("-i, --input <file>", "Input file (default: stdin)").option("-o, --output <spec>", "Output spec (format:file)", collectOutputs, []).option("-t, --template <path>", "Template file or directory").option("-e, --engine <engine>", "Template engine (handlebars|liquid|ejs)").option("--monorepo <mode>", "Monorepo mode (root|packages|both)").option("--llm-provider <provider>", "LLM provider").option("--llm-model <model>", "LLM model").option("--llm-base-url <url>", "LLM base URL (for openai-compatible provider)").option("--llm-tasks <tasks>", "Comma-separated LLM tasks").option("--no-llm", "Disable LLM processing").option("--config <path>", "Config file path").option("--dry-run", "Preview without writing").option("--regenerate", "Regenerate entire changelog").option("-v, --verbose", "Increase verbosity", increaseVerbosity, 0).option("-q, --quiet", "Suppress non-error output").action(async (options) => {
|
|
1588
|
+
program.command("generate", { isDefault: true }).description("Generate changelog from input data").option("-i, --input <file>", "Input file (default: stdin)").option("-o, --output <spec>", "Output spec (format:file)", collectOutputs, []).option("-t, --template <path>", "Template file or directory").option("-e, --engine <engine>", "Template engine (handlebars|liquid|ejs)").option("--monorepo <mode>", "Monorepo mode (root|packages|both)").option("--llm-provider <provider>", "LLM provider").option("--llm-model <model>", "LLM model").option("--llm-base-url <url>", "LLM base URL (for openai-compatible provider)").option("--llm-tasks <tasks>", "Comma-separated LLM tasks").option("--no-llm", "Disable LLM processing").option("--target <package>", "Filter to a specific package name").option("--config <path>", "Config file path").option("--dry-run", "Preview without writing").option("--regenerate", "Regenerate entire changelog").option("-v, --verbose", "Increase verbosity", increaseVerbosity, 0).option("-q, --quiet", "Suppress non-error output").action(async (options) => {
|
|
1467
1589
|
setVerbosity(options.verbose);
|
|
1468
|
-
if (options.quiet) (0,
|
|
1590
|
+
if (options.quiet) (0, import_core10.setQuietMode)(true);
|
|
1469
1591
|
try {
|
|
1470
1592
|
const config = loadConfig(process.cwd(), options.config);
|
|
1471
1593
|
if (options.output.length > 0) {
|
|
@@ -1484,7 +1606,7 @@ program.command("generate", { isDefault: true }).description("Generate changelog
|
|
|
1484
1606
|
config.templates = { ...config.templates, engine: options.engine };
|
|
1485
1607
|
}
|
|
1486
1608
|
if (options.llm === false) {
|
|
1487
|
-
(0,
|
|
1609
|
+
(0, import_core10.info)("LLM processing disabled via --no-llm flag");
|
|
1488
1610
|
delete config.llm;
|
|
1489
1611
|
} else if (options.llmProvider || options.llmModel || options.llmBaseUrl || options.llmTasks) {
|
|
1490
1612
|
config.llm = config.llm ?? { provider: "openai-compatible", model: "" };
|
|
@@ -1500,13 +1622,13 @@ program.command("generate", { isDefault: true }).description("Generate changelog
|
|
|
1500
1622
|
releaseNotes: taskNames.includes("release-notes") || taskNames.includes("releaseNotes")
|
|
1501
1623
|
};
|
|
1502
1624
|
}
|
|
1503
|
-
(0,
|
|
1625
|
+
(0, import_core10.info)(`LLM configured: ${config.llm.provider}${config.llm.model ? ` (${config.llm.model})` : ""}`);
|
|
1504
1626
|
if (config.llm.baseURL) {
|
|
1505
|
-
(0,
|
|
1627
|
+
(0, import_core10.info)(`LLM base URL: ${config.llm.baseURL}`);
|
|
1506
1628
|
}
|
|
1507
1629
|
const taskList = Object.entries(config.llm.tasks || {}).filter(([, enabled]) => enabled).map(([name]) => name).join(", ");
|
|
1508
1630
|
if (taskList) {
|
|
1509
|
-
(0,
|
|
1631
|
+
(0, import_core10.info)(`LLM tasks: ${taskList}`);
|
|
1510
1632
|
}
|
|
1511
1633
|
}
|
|
1512
1634
|
let inputJson;
|
|
@@ -1516,14 +1638,23 @@ program.command("generate", { isDefault: true }).description("Generate changelog
|
|
|
1516
1638
|
inputJson = await readStdin();
|
|
1517
1639
|
}
|
|
1518
1640
|
const input = parsePackageVersioner(inputJson);
|
|
1641
|
+
if (options.target) {
|
|
1642
|
+
const before = input.packages.length;
|
|
1643
|
+
input.packages = input.packages.filter((p) => p.packageName === options.target);
|
|
1644
|
+
if (input.packages.length === 0) {
|
|
1645
|
+
(0, import_core10.info)(`No changelog found for package "${options.target}" (had ${before} package(s))`);
|
|
1646
|
+
return;
|
|
1647
|
+
}
|
|
1648
|
+
(0, import_core10.info)(`Filtered to package: ${options.target}`);
|
|
1649
|
+
}
|
|
1519
1650
|
if (options.monorepo || config.monorepo) {
|
|
1520
1651
|
const monorepoMode = options.monorepo ?? config.monorepo?.mode ?? "both";
|
|
1521
1652
|
const detected = detectMonorepo(process.cwd());
|
|
1522
1653
|
if (!detected.isMonorepo) {
|
|
1523
|
-
(0,
|
|
1654
|
+
(0, import_core10.info)("No monorepo detected, using single package mode");
|
|
1524
1655
|
await runPipeline(input, config, options.dryRun ?? false);
|
|
1525
1656
|
} else {
|
|
1526
|
-
(0,
|
|
1657
|
+
(0, import_core10.info)(`Monorepo detected with packages at ${detected.packagesPath}`);
|
|
1527
1658
|
const contexts = input.packages.map(createTemplateContext);
|
|
1528
1659
|
writeMonorepoChangelogs(
|
|
1529
1660
|
contexts,
|
|
@@ -1540,9 +1671,9 @@ program.command("generate", { isDefault: true }).description("Generate changelog
|
|
|
1540
1671
|
await runPipeline(input, config, options.dryRun ?? false);
|
|
1541
1672
|
}
|
|
1542
1673
|
if (options.dryRun) {
|
|
1543
|
-
(0,
|
|
1674
|
+
(0, import_core10.info)("Dry run complete - no files were written");
|
|
1544
1675
|
} else {
|
|
1545
|
-
(0,
|
|
1676
|
+
(0, import_core10.success)("Changelog generation complete");
|
|
1546
1677
|
}
|
|
1547
1678
|
} catch (err) {
|
|
1548
1679
|
handleError(err);
|
|
@@ -1551,7 +1682,7 @@ program.command("generate", { isDefault: true }).description("Generate changelog
|
|
|
1551
1682
|
program.command("init").description("Create default configuration file").option("-f, --force", "Overwrite existing config").action((options) => {
|
|
1552
1683
|
const configPath = "releasekit.config.json";
|
|
1553
1684
|
if (fs10.existsSync(configPath) && !options.force) {
|
|
1554
|
-
(0,
|
|
1685
|
+
(0, import_core10.error)(`Config file already exists at ${configPath}. Use --force to overwrite.`);
|
|
1555
1686
|
process.exit(import_core2.EXIT_CODES.GENERAL_ERROR);
|
|
1556
1687
|
}
|
|
1557
1688
|
const defaultConfig = {
|
|
@@ -1562,7 +1693,7 @@ program.command("init").description("Create default configuration file").option(
|
|
|
1562
1693
|
}
|
|
1563
1694
|
};
|
|
1564
1695
|
fs10.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
|
|
1565
|
-
(0,
|
|
1696
|
+
(0, import_core10.success)(`Created config file at ${configPath}`);
|
|
1566
1697
|
});
|
|
1567
1698
|
program.command("auth <provider>").description("Configure API key for an LLM provider").option("--key <key>", "API key (omit to be prompted)").action(async (provider, options) => {
|
|
1568
1699
|
let apiKey;
|
|
@@ -1572,14 +1703,14 @@ program.command("auth <provider>").description("Configure API key for an LLM pro
|
|
|
1572
1703
|
apiKey = await promptSecret(`Enter API key for ${provider}: `);
|
|
1573
1704
|
}
|
|
1574
1705
|
if (!apiKey.trim()) {
|
|
1575
|
-
(0,
|
|
1706
|
+
(0, import_core10.error)("API key cannot be empty");
|
|
1576
1707
|
process.exit(import_core2.EXIT_CODES.GENERAL_ERROR);
|
|
1577
1708
|
}
|
|
1578
1709
|
(0, import_config.saveAuth)(provider, apiKey.trim());
|
|
1579
|
-
(0,
|
|
1710
|
+
(0, import_core10.success)(`API key saved for ${provider}`);
|
|
1580
1711
|
});
|
|
1581
1712
|
program.command("providers").description("List available LLM providers").action(() => {
|
|
1582
|
-
(0,
|
|
1713
|
+
(0, import_core10.info)("Available LLM providers:");
|
|
1583
1714
|
console.log(" openai - OpenAI (GPT models)");
|
|
1584
1715
|
console.log(" anthropic - Anthropic (Claude models)");
|
|
1585
1716
|
console.log(" ollama - Ollama (local models)");
|
|
@@ -1600,7 +1731,7 @@ function increaseVerbosity(_, previous) {
|
|
|
1600
1731
|
}
|
|
1601
1732
|
function setVerbosity(level) {
|
|
1602
1733
|
const levels = ["info", "debug", "trace"];
|
|
1603
|
-
(0,
|
|
1734
|
+
(0, import_core10.setLogLevel)(levels[Math.min(level, levels.length - 1)] ?? "info");
|
|
1604
1735
|
}
|
|
1605
1736
|
async function readStdin() {
|
|
1606
1737
|
const chunks = [];
|
|
@@ -1623,7 +1754,7 @@ function handleError(err) {
|
|
|
1623
1754
|
err.logError();
|
|
1624
1755
|
process.exit(getExitCode(err));
|
|
1625
1756
|
}
|
|
1626
|
-
(0,
|
|
1757
|
+
(0, import_core10.error)(err instanceof Error ? err.message : String(err));
|
|
1627
1758
|
process.exit(import_core2.EXIT_CODES.GENERAL_ERROR);
|
|
1628
1759
|
}
|
|
1629
1760
|
program.parse();
|