@releasekit/notes 0.2.0-next.0 → 0.2.0-next.10
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-NHDLQLG2.js → chunk-ACXCEHQT.js} +177 -55
- 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.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
runPipeline,
|
|
12
12
|
saveAuth,
|
|
13
13
|
writeMonorepoChangelogs
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-ACXCEHQT.js";
|
|
15
15
|
|
|
16
16
|
// src/cli.ts
|
|
17
17
|
import * as fs from "fs";
|
|
@@ -20,7 +20,7 @@ import { error, info, setLogLevel, setQuietMode, success } from "@releasekit/cor
|
|
|
20
20
|
import { Command } from "commander";
|
|
21
21
|
var program = new Command();
|
|
22
22
|
program.name("releasekit-notes").description("Generate changelogs with LLM-powered enhancement and flexible templating").version("0.1.0");
|
|
23
|
-
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) => {
|
|
23
|
+
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) => {
|
|
24
24
|
setVerbosity(options.verbose);
|
|
25
25
|
if (options.quiet) setQuietMode(true);
|
|
26
26
|
try {
|
|
@@ -73,6 +73,15 @@ program.command("generate", { isDefault: true }).description("Generate changelog
|
|
|
73
73
|
inputJson = await readStdin();
|
|
74
74
|
}
|
|
75
75
|
const input = parsePackageVersioner(inputJson);
|
|
76
|
+
if (options.target) {
|
|
77
|
+
const before = input.packages.length;
|
|
78
|
+
input.packages = input.packages.filter((p) => p.packageName === options.target);
|
|
79
|
+
if (input.packages.length === 0) {
|
|
80
|
+
info(`No changelog found for package "${options.target}" (had ${before} package(s))`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
info(`Filtered to package: ${options.target}`);
|
|
84
|
+
}
|
|
76
85
|
if (options.monorepo || config.monorepo) {
|
|
77
86
|
const monorepoMode = options.monorepo ?? config.monorepo?.mode ?? "both";
|
|
78
87
|
const detected = detectMonorepo(process.cwd());
|
package/dist/index.cjs
CHANGED
|
@@ -75,7 +75,7 @@ function getDefaultConfig() {
|
|
|
75
75
|
// src/core/pipeline.ts
|
|
76
76
|
var fs8 = __toESM(require("fs"), 1);
|
|
77
77
|
var path6 = __toESM(require("path"), 1);
|
|
78
|
-
var
|
|
78
|
+
var import_core8 = require("@releasekit/core");
|
|
79
79
|
|
|
80
80
|
// src/input/package-versioner.ts
|
|
81
81
|
var fs = __toESM(require("fs"), 1);
|
|
@@ -416,15 +416,26 @@ Entries:
|
|
|
416
416
|
Output only valid JSON, nothing else:`;
|
|
417
417
|
function buildCustomCategorizePrompt(categories) {
|
|
418
418
|
const categoryList = categories.map((c) => `- "${c.name}": ${c.description}`).join("\n");
|
|
419
|
+
const developerCategory = categories.find((c) => c.name === "Developer");
|
|
420
|
+
let scopeInstructions = "";
|
|
421
|
+
if (developerCategory) {
|
|
422
|
+
const scopeMatch = developerCategory.description.match(/from:\s*([^.]+)/);
|
|
423
|
+
if (scopeMatch?.[1]) {
|
|
424
|
+
const scopes = scopeMatch[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
425
|
+
if (scopes.length > 0) {
|
|
426
|
+
scopeInstructions = `
|
|
427
|
+
|
|
428
|
+
For the "Developer" category, you MUST assign a scope from this exact list: ${scopes.join(", ")}.
|
|
429
|
+
`;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
419
433
|
return `You are categorizing changelog entries for a software release.
|
|
420
434
|
|
|
421
|
-
Given the following entries, group them into the specified categories. Only use the categories listed below
|
|
435
|
+
Given the following entries, group them into the specified categories. Only use the categories listed below in this exact order:
|
|
422
436
|
|
|
423
437
|
Categories:
|
|
424
|
-
${categoryList}
|
|
425
|
-
|
|
426
|
-
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").
|
|
427
|
-
|
|
438
|
+
${categoryList}${scopeInstructions}
|
|
428
439
|
Output a JSON object with two fields:
|
|
429
440
|
- "categories": an object where keys are category names and values are arrays of entry indices (0-based)
|
|
430
441
|
- "scopes": an object where keys are entry indices (as strings) and values are scope labels
|
|
@@ -456,7 +467,8 @@ async function categorizeEntries(provider, entries, context) {
|
|
|
456
467
|
entries[idx] = { ...entries[idx], scope };
|
|
457
468
|
}
|
|
458
469
|
}
|
|
459
|
-
for (const [category,
|
|
470
|
+
for (const [category, rawIndices] of Object.entries(categoryMap)) {
|
|
471
|
+
const indices = Array.isArray(rawIndices) ? rawIndices : [];
|
|
460
472
|
const categoryEntries = indices.map((i) => entries[i]).filter((e) => e !== void 0);
|
|
461
473
|
if (categoryEntries.length > 0) {
|
|
462
474
|
result.push({ category, entries: categoryEntries });
|
|
@@ -464,7 +476,8 @@ async function categorizeEntries(provider, entries, context) {
|
|
|
464
476
|
}
|
|
465
477
|
} else {
|
|
466
478
|
const categoryMap = parsed;
|
|
467
|
-
for (const [category,
|
|
479
|
+
for (const [category, rawIndices] of Object.entries(categoryMap)) {
|
|
480
|
+
const indices = Array.isArray(rawIndices) ? rawIndices : [];
|
|
468
481
|
const categoryEntries = indices.map((i) => entries[i]).filter((e) => e !== void 0);
|
|
469
482
|
if (categoryEntries.length > 0) {
|
|
470
483
|
result.push({ category, entries: categoryEntries });
|
|
@@ -522,6 +535,113 @@ async function enhanceEntries(provider, entries, context, concurrency = LLM_DEFA
|
|
|
522
535
|
return results;
|
|
523
536
|
}
|
|
524
537
|
|
|
538
|
+
// src/llm/tasks/enhance-and-categorize.ts
|
|
539
|
+
var import_core4 = require("@releasekit/core");
|
|
540
|
+
|
|
541
|
+
// src/utils/retry.ts
|
|
542
|
+
function sleep(ms) {
|
|
543
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
544
|
+
}
|
|
545
|
+
async function withRetry(fn, options = {}) {
|
|
546
|
+
const maxAttempts = options.maxAttempts ?? 3;
|
|
547
|
+
const initialDelay = options.initialDelay ?? 1e3;
|
|
548
|
+
const maxDelay = options.maxDelay ?? 3e4;
|
|
549
|
+
const backoffFactor = options.backoffFactor ?? 2;
|
|
550
|
+
let lastError;
|
|
551
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
552
|
+
try {
|
|
553
|
+
return await fn();
|
|
554
|
+
} catch (error) {
|
|
555
|
+
lastError = error;
|
|
556
|
+
if (attempt < maxAttempts - 1) {
|
|
557
|
+
const base = Math.min(initialDelay * backoffFactor ** attempt, maxDelay);
|
|
558
|
+
const jitter = base * 0.2 * (Math.random() * 2 - 1);
|
|
559
|
+
await sleep(Math.max(0, base + jitter));
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
throw lastError;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// src/llm/tasks/enhance-and-categorize.ts
|
|
567
|
+
function buildPrompt(entries, categories, style) {
|
|
568
|
+
const entriesText = entries.map((e, i) => `${i}. [${e.type}]${e.scope ? ` (${e.scope})` : ""}: ${e.description}`).join("\n");
|
|
569
|
+
const styleText = style || 'Use present tense ("Add feature" not "Added feature"). Be concise.';
|
|
570
|
+
const categorySection = categories ? `Categories (use ONLY these):
|
|
571
|
+
${categories.map((c) => `- "${c.name}": ${c.description}`).join("\n")}` : `Categories: Group into meaningful categories (e.g., "New", "Fixed", "Changed", "Removed").`;
|
|
572
|
+
return `You are generating release notes for a software project. Given the following changelog entries, do two things:
|
|
573
|
+
|
|
574
|
+
1. **Rewrite** each entry as a clear, user-friendly description
|
|
575
|
+
2. **Categorize** each entry into the appropriate category
|
|
576
|
+
|
|
577
|
+
Style guidelines:
|
|
578
|
+
- ${styleText}
|
|
579
|
+
- Be concise (1 short sentence per entry)
|
|
580
|
+
- Focus on what changed, not implementation details
|
|
581
|
+
|
|
582
|
+
${categorySection}
|
|
583
|
+
|
|
584
|
+
${categories ? 'For entries in categories involving internal/developer changes, set a "scope" field with a short subcategory label (e.g., "CI", "Dependencies", "Testing").' : ""}
|
|
585
|
+
|
|
586
|
+
Entries:
|
|
587
|
+
${entriesText}
|
|
588
|
+
|
|
589
|
+
Output a JSON object with:
|
|
590
|
+
- "entries": array of objects, one per input entry (same order), each with: { "description": "rewritten text", "category": "CategoryName", "scope": "optional subcategory label or null" }
|
|
591
|
+
|
|
592
|
+
Output only valid JSON, nothing else:`;
|
|
593
|
+
}
|
|
594
|
+
async function enhanceAndCategorize(provider, entries, context) {
|
|
595
|
+
if (entries.length === 0) {
|
|
596
|
+
return { enhancedEntries: [], categories: [] };
|
|
597
|
+
}
|
|
598
|
+
const retryOpts = LLM_DEFAULTS.retry;
|
|
599
|
+
try {
|
|
600
|
+
return await withRetry(async () => {
|
|
601
|
+
const prompt = buildPrompt(entries, context.categories, context.style);
|
|
602
|
+
const response = await provider.complete(prompt);
|
|
603
|
+
const cleaned = response.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "").trim();
|
|
604
|
+
const parsed = JSON.parse(cleaned);
|
|
605
|
+
if (!Array.isArray(parsed.entries)) {
|
|
606
|
+
throw new Error('Response missing "entries" array');
|
|
607
|
+
}
|
|
608
|
+
const enhancedEntries = entries.map((original, i) => {
|
|
609
|
+
const result = parsed.entries[i];
|
|
610
|
+
if (!result) return original;
|
|
611
|
+
return {
|
|
612
|
+
...original,
|
|
613
|
+
description: result.description || original.description,
|
|
614
|
+
scope: result.scope || original.scope
|
|
615
|
+
};
|
|
616
|
+
});
|
|
617
|
+
const categoryMap = /* @__PURE__ */ new Map();
|
|
618
|
+
for (let i = 0; i < parsed.entries.length; i++) {
|
|
619
|
+
const result = parsed.entries[i];
|
|
620
|
+
const category = result?.category || "General";
|
|
621
|
+
const entry = enhancedEntries[i];
|
|
622
|
+
if (!entry) continue;
|
|
623
|
+
if (!categoryMap.has(category)) {
|
|
624
|
+
categoryMap.set(category, []);
|
|
625
|
+
}
|
|
626
|
+
categoryMap.get(category).push(entry);
|
|
627
|
+
}
|
|
628
|
+
const categories = [];
|
|
629
|
+
for (const [category, catEntries] of categoryMap) {
|
|
630
|
+
categories.push({ category, entries: catEntries });
|
|
631
|
+
}
|
|
632
|
+
return { enhancedEntries, categories };
|
|
633
|
+
}, retryOpts);
|
|
634
|
+
} catch (error) {
|
|
635
|
+
(0, import_core4.warn)(
|
|
636
|
+
`Combined enhance+categorize failed after ${retryOpts.maxAttempts} attempts: ${error instanceof Error ? error.message : String(error)}`
|
|
637
|
+
);
|
|
638
|
+
return {
|
|
639
|
+
enhancedEntries: entries,
|
|
640
|
+
categories: [{ category: "General", entries }]
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
525
645
|
// src/llm/tasks/release-notes.ts
|
|
526
646
|
var RELEASE_NOTES_PROMPT = `You are writing release notes for a software project.
|
|
527
647
|
|
|
@@ -622,12 +742,12 @@ function createProvider(config) {
|
|
|
622
742
|
|
|
623
743
|
// src/output/github-release.ts
|
|
624
744
|
var import_rest = require("@octokit/rest");
|
|
625
|
-
var
|
|
745
|
+
var import_core6 = require("@releasekit/core");
|
|
626
746
|
|
|
627
747
|
// src/output/markdown.ts
|
|
628
748
|
var fs2 = __toESM(require("fs"), 1);
|
|
629
749
|
var path = __toESM(require("path"), 1);
|
|
630
|
-
var
|
|
750
|
+
var import_core5 = require("@releasekit/core");
|
|
631
751
|
var TYPE_ORDER = ["added", "changed", "deprecated", "removed", "fixed", "security"];
|
|
632
752
|
var TYPE_LABELS = {
|
|
633
753
|
added: "Added",
|
|
@@ -726,15 +846,19 @@ ${body}`;
|
|
|
726
846
|
function writeMarkdown(outputPath, contexts, config, dryRun) {
|
|
727
847
|
const content = renderMarkdown(contexts);
|
|
728
848
|
if (dryRun) {
|
|
729
|
-
(0,
|
|
849
|
+
(0, import_core5.info)("--- Changelog Preview ---");
|
|
730
850
|
console.log(content);
|
|
731
|
-
(0,
|
|
851
|
+
(0, import_core5.info)("--- End Preview ---");
|
|
732
852
|
return;
|
|
733
853
|
}
|
|
734
854
|
const dir = path.dirname(outputPath);
|
|
735
855
|
if (!fs2.existsSync(dir)) {
|
|
736
856
|
fs2.mkdirSync(dir, { recursive: true });
|
|
737
857
|
}
|
|
858
|
+
if (outputPath === "-") {
|
|
859
|
+
process.stdout.write(content);
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
738
862
|
if (config.updateStrategy === "prepend" && fs2.existsSync(outputPath) && contexts.length === 1) {
|
|
739
863
|
const firstContext = contexts[0];
|
|
740
864
|
if (firstContext) {
|
|
@@ -744,7 +868,7 @@ function writeMarkdown(outputPath, contexts, config, dryRun) {
|
|
|
744
868
|
} else {
|
|
745
869
|
fs2.writeFileSync(outputPath, content, "utf-8");
|
|
746
870
|
}
|
|
747
|
-
(0,
|
|
871
|
+
(0, import_core5.success)(`Changelog written to ${outputPath}`);
|
|
748
872
|
}
|
|
749
873
|
|
|
750
874
|
// src/output/github-release.ts
|
|
@@ -769,7 +893,7 @@ var GitHubClient = class {
|
|
|
769
893
|
} else {
|
|
770
894
|
body = renderMarkdown([context]);
|
|
771
895
|
}
|
|
772
|
-
(0,
|
|
896
|
+
(0, import_core6.info)(`Creating GitHub release for ${tagName}`);
|
|
773
897
|
try {
|
|
774
898
|
const response = await this.octokit.repos.createRelease({
|
|
775
899
|
owner: this.owner,
|
|
@@ -781,7 +905,7 @@ var GitHubClient = class {
|
|
|
781
905
|
prerelease: options.prerelease ?? false,
|
|
782
906
|
generate_release_notes: options.generateNotes ?? false
|
|
783
907
|
});
|
|
784
|
-
(0,
|
|
908
|
+
(0, import_core6.success)(`Release created: ${response.data.html_url}`);
|
|
785
909
|
return {
|
|
786
910
|
id: response.data.id,
|
|
787
911
|
htmlUrl: response.data.html_url,
|
|
@@ -799,7 +923,7 @@ var GitHubClient = class {
|
|
|
799
923
|
} else {
|
|
800
924
|
body = renderMarkdown([context]);
|
|
801
925
|
}
|
|
802
|
-
(0,
|
|
926
|
+
(0, import_core6.info)(`Updating GitHub release ${releaseId}`);
|
|
803
927
|
try {
|
|
804
928
|
const response = await this.octokit.repos.updateRelease({
|
|
805
929
|
owner: this.owner,
|
|
@@ -811,7 +935,7 @@ var GitHubClient = class {
|
|
|
811
935
|
draft: options.draft ?? false,
|
|
812
936
|
prerelease: options.prerelease ?? false
|
|
813
937
|
});
|
|
814
|
-
(0,
|
|
938
|
+
(0, import_core6.success)(`Release updated: ${response.data.html_url}`);
|
|
815
939
|
return {
|
|
816
940
|
id: response.data.id,
|
|
817
941
|
htmlUrl: response.data.html_url,
|
|
@@ -863,7 +987,7 @@ async function createGitHubRelease(context, options) {
|
|
|
863
987
|
// src/output/json.ts
|
|
864
988
|
var fs3 = __toESM(require("fs"), 1);
|
|
865
989
|
var path2 = __toESM(require("path"), 1);
|
|
866
|
-
var
|
|
990
|
+
var import_core7 = require("@releasekit/core");
|
|
867
991
|
function renderJson(contexts) {
|
|
868
992
|
return JSON.stringify(
|
|
869
993
|
{
|
|
@@ -883,9 +1007,9 @@ function renderJson(contexts) {
|
|
|
883
1007
|
function writeJson(outputPath, contexts, dryRun) {
|
|
884
1008
|
const content = renderJson(contexts);
|
|
885
1009
|
if (dryRun) {
|
|
886
|
-
(0,
|
|
1010
|
+
(0, import_core7.info)("--- JSON Output Preview ---");
|
|
887
1011
|
console.log(content);
|
|
888
|
-
(0,
|
|
1012
|
+
(0, import_core7.info)("--- End Preview ---");
|
|
889
1013
|
return;
|
|
890
1014
|
}
|
|
891
1015
|
const dir = path2.dirname(outputPath);
|
|
@@ -893,7 +1017,7 @@ function writeJson(outputPath, contexts, dryRun) {
|
|
|
893
1017
|
fs3.mkdirSync(dir, { recursive: true });
|
|
894
1018
|
}
|
|
895
1019
|
fs3.writeFileSync(outputPath, content, "utf-8");
|
|
896
|
-
(0,
|
|
1020
|
+
(0, import_core7.success)(`JSON output written to ${outputPath}`);
|
|
897
1021
|
}
|
|
898
1022
|
|
|
899
1023
|
// src/templates/ejs.ts
|
|
@@ -1150,41 +1274,24 @@ function renderTemplate(templatePath, context, engine) {
|
|
|
1150
1274
|
return renderComposable(templatePath, context, engine);
|
|
1151
1275
|
}
|
|
1152
1276
|
|
|
1153
|
-
// src/utils/retry.ts
|
|
1154
|
-
function sleep(ms) {
|
|
1155
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1156
|
-
}
|
|
1157
|
-
async function withRetry(fn, options = {}) {
|
|
1158
|
-
const maxAttempts = options.maxAttempts ?? 3;
|
|
1159
|
-
const initialDelay = options.initialDelay ?? 1e3;
|
|
1160
|
-
const maxDelay = options.maxDelay ?? 3e4;
|
|
1161
|
-
const backoffFactor = options.backoffFactor ?? 2;
|
|
1162
|
-
let lastError;
|
|
1163
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1164
|
-
try {
|
|
1165
|
-
return await fn();
|
|
1166
|
-
} catch (error) {
|
|
1167
|
-
lastError = error;
|
|
1168
|
-
if (attempt < maxAttempts - 1) {
|
|
1169
|
-
const base = Math.min(initialDelay * backoffFactor ** attempt, maxDelay);
|
|
1170
|
-
const jitter = base * 0.2 * (Math.random() * 2 - 1);
|
|
1171
|
-
await sleep(Math.max(0, base + jitter));
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
throw lastError;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
1277
|
// src/core/pipeline.ts
|
|
1179
1278
|
var import_meta = {};
|
|
1279
|
+
function extractVersionFromTag(tag) {
|
|
1280
|
+
if (tag.includes("@") && !tag.startsWith("@")) {
|
|
1281
|
+
return tag;
|
|
1282
|
+
}
|
|
1283
|
+
return tag.replace(/^v/, "");
|
|
1284
|
+
}
|
|
1180
1285
|
function generateCompareUrl(repoUrl, from, to) {
|
|
1286
|
+
const fromVersion = extractVersionFromTag(from);
|
|
1287
|
+
const toVersion = extractVersionFromTag(to);
|
|
1181
1288
|
if (/gitlab\.com/i.test(repoUrl)) {
|
|
1182
|
-
return `${repoUrl}/-/compare/${
|
|
1289
|
+
return `${repoUrl}/-/compare/${fromVersion}...${toVersion}`;
|
|
1183
1290
|
}
|
|
1184
1291
|
if (/bitbucket\.org/i.test(repoUrl)) {
|
|
1185
|
-
return `${repoUrl}/branches/compare/${
|
|
1292
|
+
return `${repoUrl}/branches/compare/${fromVersion}..${toVersion}`;
|
|
1186
1293
|
}
|
|
1187
|
-
return `${repoUrl}/compare/${
|
|
1294
|
+
return `${repoUrl}/compare/${fromVersion}...${toVersion}`;
|
|
1188
1295
|
}
|
|
1189
1296
|
function createTemplateContext(pkg) {
|
|
1190
1297
|
const compareUrl = pkg.repoUrl && pkg.previousVersion ? generateCompareUrl(pkg.repoUrl, pkg.previousVersion, pkg.version) : void 0;
|
|
@@ -1231,9 +1338,9 @@ async function processWithLLM(context, config) {
|
|
|
1231
1338
|
entries: context.entries
|
|
1232
1339
|
};
|
|
1233
1340
|
try {
|
|
1234
|
-
(0,
|
|
1341
|
+
(0, import_core8.info)(`Using LLM provider: ${config.llm.provider}${config.llm.model ? ` (${config.llm.model})` : ""}`);
|
|
1235
1342
|
if (config.llm.baseURL) {
|
|
1236
|
-
(0,
|
|
1343
|
+
(0, import_core8.info)(`LLM base URL: ${config.llm.baseURL}`);
|
|
1237
1344
|
}
|
|
1238
1345
|
const rawProvider = createProvider(config.llm);
|
|
1239
1346
|
const retryOpts = config.llm.retry ?? LLM_DEFAULTS.retry;
|
|
@@ -1242,38 +1349,49 @@ async function processWithLLM(context, config) {
|
|
|
1242
1349
|
complete: (prompt, opts) => withRetry(() => rawProvider.complete(prompt, opts), retryOpts)
|
|
1243
1350
|
};
|
|
1244
1351
|
const activeTasks = Object.entries(tasks).filter(([, enabled]) => enabled).map(([name]) => name);
|
|
1245
|
-
(0,
|
|
1246
|
-
if (tasks.enhance) {
|
|
1247
|
-
(0,
|
|
1248
|
-
|
|
1249
|
-
|
|
1352
|
+
(0, import_core8.info)(`Running LLM tasks: ${activeTasks.join(", ")}`);
|
|
1353
|
+
if (tasks.enhance && tasks.categorize) {
|
|
1354
|
+
(0, import_core8.info)("Enhancing and categorizing entries with LLM...");
|
|
1355
|
+
const result = await enhanceAndCategorize(provider, context.entries, llmContext);
|
|
1356
|
+
enhanced.entries = result.enhancedEntries;
|
|
1357
|
+
enhanced.categories = {};
|
|
1358
|
+
for (const cat of result.categories) {
|
|
1359
|
+
enhanced.categories[cat.category] = cat.entries;
|
|
1360
|
+
}
|
|
1361
|
+
(0, import_core8.info)(`Enhanced ${enhanced.entries.length} entries into ${result.categories.length} categories`);
|
|
1362
|
+
} else {
|
|
1363
|
+
if (tasks.enhance) {
|
|
1364
|
+
(0, import_core8.info)("Enhancing entries with LLM...");
|
|
1365
|
+
enhanced.entries = await enhanceEntries(provider, context.entries, llmContext, config.llm.concurrency);
|
|
1366
|
+
(0, import_core8.info)(`Enhanced ${enhanced.entries.length} entries`);
|
|
1367
|
+
}
|
|
1368
|
+
if (tasks.categorize) {
|
|
1369
|
+
(0, import_core8.info)("Categorizing entries with LLM...");
|
|
1370
|
+
const categorized = await categorizeEntries(provider, enhanced.entries, llmContext);
|
|
1371
|
+
enhanced.categories = {};
|
|
1372
|
+
for (const cat of categorized) {
|
|
1373
|
+
enhanced.categories[cat.category] = cat.entries;
|
|
1374
|
+
}
|
|
1375
|
+
(0, import_core8.info)(`Created ${categorized.length} categories`);
|
|
1376
|
+
}
|
|
1250
1377
|
}
|
|
1251
1378
|
if (tasks.summarize) {
|
|
1252
|
-
(0,
|
|
1379
|
+
(0, import_core8.info)("Summarizing entries with LLM...");
|
|
1253
1380
|
enhanced.summary = await summarizeEntries(provider, enhanced.entries, llmContext);
|
|
1254
1381
|
if (enhanced.summary) {
|
|
1255
|
-
(0,
|
|
1256
|
-
(0,
|
|
1382
|
+
(0, import_core8.info)("Summary generated successfully");
|
|
1383
|
+
(0, import_core8.debug)(`Summary: ${enhanced.summary.substring(0, 100)}...`);
|
|
1257
1384
|
} else {
|
|
1258
|
-
(0,
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
if (tasks.categorize) {
|
|
1262
|
-
(0, import_core7.info)("Categorizing entries with LLM...");
|
|
1263
|
-
const categorized = await categorizeEntries(provider, enhanced.entries, llmContext);
|
|
1264
|
-
enhanced.categories = {};
|
|
1265
|
-
for (const cat of categorized) {
|
|
1266
|
-
enhanced.categories[cat.category] = cat.entries;
|
|
1385
|
+
(0, import_core8.warn)("Summary generation returned empty result");
|
|
1267
1386
|
}
|
|
1268
|
-
(0, import_core7.info)(`Created ${categorized.length} categories`);
|
|
1269
1387
|
}
|
|
1270
1388
|
if (tasks.releaseNotes) {
|
|
1271
|
-
(0,
|
|
1389
|
+
(0, import_core8.info)("Generating release notes with LLM...");
|
|
1272
1390
|
enhanced.releaseNotes = await generateReleaseNotes(provider, enhanced.entries, llmContext);
|
|
1273
1391
|
if (enhanced.releaseNotes) {
|
|
1274
|
-
(0,
|
|
1392
|
+
(0, import_core8.info)("Release notes generated successfully");
|
|
1275
1393
|
} else {
|
|
1276
|
-
(0,
|
|
1394
|
+
(0, import_core8.warn)("Release notes generation returned empty result");
|
|
1277
1395
|
}
|
|
1278
1396
|
}
|
|
1279
1397
|
return {
|
|
@@ -1282,8 +1400,8 @@ async function processWithLLM(context, config) {
|
|
|
1282
1400
|
enhanced
|
|
1283
1401
|
};
|
|
1284
1402
|
} catch (error) {
|
|
1285
|
-
(0,
|
|
1286
|
-
(0,
|
|
1403
|
+
(0, import_core8.warn)(`LLM processing failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1404
|
+
(0, import_core8.warn)("Falling back to raw entries");
|
|
1287
1405
|
return context;
|
|
1288
1406
|
}
|
|
1289
1407
|
}
|
|
@@ -1311,9 +1429,13 @@ async function generateWithTemplate(contexts, config, outputPath, dryRun) {
|
|
|
1311
1429
|
);
|
|
1312
1430
|
const result = renderTemplate(templatePath, documentContext, config.templates?.engine);
|
|
1313
1431
|
if (dryRun) {
|
|
1314
|
-
(0,
|
|
1432
|
+
(0, import_core8.info)("--- Changelog Preview ---");
|
|
1315
1433
|
console.log(result.content);
|
|
1316
|
-
(0,
|
|
1434
|
+
(0, import_core8.info)("--- End Preview ---");
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
if (outputPath === "-") {
|
|
1438
|
+
process.stdout.write(result.content);
|
|
1317
1439
|
return;
|
|
1318
1440
|
}
|
|
1319
1441
|
const dir = path6.dirname(outputPath);
|
|
@@ -1321,17 +1443,17 @@ async function generateWithTemplate(contexts, config, outputPath, dryRun) {
|
|
|
1321
1443
|
fs8.mkdirSync(dir, { recursive: true });
|
|
1322
1444
|
}
|
|
1323
1445
|
fs8.writeFileSync(outputPath, result.content, "utf-8");
|
|
1324
|
-
(0,
|
|
1446
|
+
(0, import_core8.success)(`Changelog written to ${outputPath} (using ${result.engine} template)`);
|
|
1325
1447
|
}
|
|
1326
1448
|
async function runPipeline(input, config, dryRun) {
|
|
1327
|
-
(0,
|
|
1449
|
+
(0, import_core8.debug)(`Processing ${input.packages.length} package(s)`);
|
|
1328
1450
|
let contexts = input.packages.map(createTemplateContext);
|
|
1329
1451
|
if (config.llm && !process.env.CHANGELOG_NO_LLM) {
|
|
1330
|
-
(0,
|
|
1452
|
+
(0, import_core8.info)("Processing with LLM enhancement");
|
|
1331
1453
|
contexts = await Promise.all(contexts.map((ctx) => processWithLLM(ctx, config)));
|
|
1332
1454
|
}
|
|
1333
1455
|
for (const output of config.output) {
|
|
1334
|
-
(0,
|
|
1456
|
+
(0, import_core8.info)(`Generating ${output.format} output`);
|
|
1335
1457
|
switch (output.format) {
|
|
1336
1458
|
case "markdown": {
|
|
1337
1459
|
const file = output.file ?? "CHANGELOG.md";
|
|
@@ -1349,22 +1471,22 @@ async function runPipeline(input, config, dryRun) {
|
|
|
1349
1471
|
}
|
|
1350
1472
|
case "github-release": {
|
|
1351
1473
|
if (dryRun) {
|
|
1352
|
-
(0,
|
|
1474
|
+
(0, import_core8.info)("[DRY RUN] Would create GitHub release");
|
|
1353
1475
|
break;
|
|
1354
1476
|
}
|
|
1355
1477
|
const firstContext = contexts[0];
|
|
1356
1478
|
if (!firstContext) {
|
|
1357
|
-
(0,
|
|
1479
|
+
(0, import_core8.warn)("No context available for GitHub release");
|
|
1358
1480
|
break;
|
|
1359
1481
|
}
|
|
1360
1482
|
const repoUrl = firstContext.repoUrl;
|
|
1361
1483
|
if (!repoUrl) {
|
|
1362
|
-
(0,
|
|
1484
|
+
(0, import_core8.warn)("No repo URL available, cannot create GitHub release");
|
|
1363
1485
|
break;
|
|
1364
1486
|
}
|
|
1365
1487
|
const parsed = parseRepoUrl(repoUrl);
|
|
1366
1488
|
if (!parsed) {
|
|
1367
|
-
(0,
|
|
1489
|
+
(0, import_core8.warn)(`Could not parse repo URL: ${repoUrl}`);
|
|
1368
1490
|
break;
|
|
1369
1491
|
}
|
|
1370
1492
|
await createGitHubRelease(firstContext, {
|
|
@@ -1386,7 +1508,7 @@ async function processInput(inputJson, config, dryRun) {
|
|
|
1386
1508
|
// src/monorepo/aggregator.ts
|
|
1387
1509
|
var fs9 = __toESM(require("fs"), 1);
|
|
1388
1510
|
var path7 = __toESM(require("path"), 1);
|
|
1389
|
-
var
|
|
1511
|
+
var import_core9 = require("@releasekit/core");
|
|
1390
1512
|
|
|
1391
1513
|
// src/monorepo/splitter.ts
|
|
1392
1514
|
function splitByPackage(contexts) {
|
|
@@ -1400,7 +1522,7 @@ function splitByPackage(contexts) {
|
|
|
1400
1522
|
// src/monorepo/aggregator.ts
|
|
1401
1523
|
function writeFile(outputPath, content, dryRun) {
|
|
1402
1524
|
if (dryRun) {
|
|
1403
|
-
(0,
|
|
1525
|
+
(0, import_core9.info)(`[DRY RUN] Would write to ${outputPath}`);
|
|
1404
1526
|
console.log(content);
|
|
1405
1527
|
return;
|
|
1406
1528
|
}
|
|
@@ -1409,7 +1531,7 @@ function writeFile(outputPath, content, dryRun) {
|
|
|
1409
1531
|
fs9.mkdirSync(dir, { recursive: true });
|
|
1410
1532
|
}
|
|
1411
1533
|
fs9.writeFileSync(outputPath, content, "utf-8");
|
|
1412
|
-
(0,
|
|
1534
|
+
(0, import_core9.success)(`Changelog written to ${outputPath}`);
|
|
1413
1535
|
}
|
|
1414
1536
|
function aggregateToRoot(contexts) {
|
|
1415
1537
|
const aggregated = {
|
|
@@ -1434,7 +1556,7 @@ function writeMonorepoChangelogs(contexts, options, config, dryRun) {
|
|
|
1434
1556
|
if (options.mode === "root" || options.mode === "both") {
|
|
1435
1557
|
const aggregated = aggregateToRoot(contexts);
|
|
1436
1558
|
const rootPath = path7.join(options.rootPath, "CHANGELOG.md");
|
|
1437
|
-
(0,
|
|
1559
|
+
(0, import_core9.info)(`Writing root changelog to ${rootPath}`);
|
|
1438
1560
|
const rootContent = config.updateStrategy === "prepend" && fs9.existsSync(rootPath) ? prependVersion(rootPath, aggregated) : renderMarkdown([aggregated]);
|
|
1439
1561
|
writeFile(rootPath, rootContent, dryRun);
|
|
1440
1562
|
}
|
|
@@ -1446,11 +1568,11 @@ function writeMonorepoChangelogs(contexts, options, config, dryRun) {
|
|
|
1446
1568
|
const packageDir = packageDirMap.get(packageName) ?? (simpleName ? packageDirMap.get(simpleName) : void 0) ?? null;
|
|
1447
1569
|
if (packageDir) {
|
|
1448
1570
|
const changelogPath = path7.join(packageDir, "CHANGELOG.md");
|
|
1449
|
-
(0,
|
|
1571
|
+
(0, import_core9.info)(`Writing changelog for ${packageName} to ${changelogPath}`);
|
|
1450
1572
|
const pkgContent = config.updateStrategy === "prepend" && fs9.existsSync(changelogPath) ? prependVersion(changelogPath, ctx) : renderMarkdown([ctx]);
|
|
1451
1573
|
writeFile(changelogPath, pkgContent, dryRun);
|
|
1452
1574
|
} else {
|
|
1453
|
-
(0,
|
|
1575
|
+
(0, import_core9.info)(`Could not find directory for package ${packageName}, skipping`);
|
|
1454
1576
|
}
|
|
1455
1577
|
}
|
|
1456
1578
|
}
|
package/dist/index.js
CHANGED