@releasekit/notes 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -73
- package/dist/{aggregator-XJ2EILO3.js → aggregator-IUQUAVJC.js} +2 -2
- package/dist/{chunk-E4454SIS.js → chunk-7TJSPQPW.js} +1 -1
- package/dist/{chunk-DCQ32FVH.js → chunk-F7MUVHZ2.js} +5 -5
- package/dist/{chunk-ENAWZXFG.js → chunk-QX23CBNW.js} +190 -334
- package/dist/cli.js +93 -64
- package/dist/index.js +44 -7
- package/docs/configuration.md +276 -0
- package/docs/llm-providers.md +246 -0
- package/docs/monorepo.md +124 -0
- package/docs/templates.md +204 -0
- package/package.json +5 -4
|
@@ -4,11 +4,10 @@ import {
|
|
|
4
4
|
debug,
|
|
5
5
|
formatVersion,
|
|
6
6
|
info,
|
|
7
|
-
renderMarkdown,
|
|
8
7
|
success,
|
|
9
8
|
warn,
|
|
10
9
|
writeMarkdown
|
|
11
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-7TJSPQPW.js";
|
|
12
11
|
|
|
13
12
|
// ../config/dist/index.js
|
|
14
13
|
import * as fs from "fs";
|
|
@@ -118,14 +117,14 @@ var GitHubReleaseConfigSchema = z.object({
|
|
|
118
117
|
perPackage: z.boolean().default(true),
|
|
119
118
|
prerelease: z.union([z.literal("auto"), z.boolean()]).default("auto"),
|
|
120
119
|
/**
|
|
121
|
-
* Controls
|
|
122
|
-
* - 'auto': Use
|
|
123
|
-
*
|
|
124
|
-
* - '
|
|
125
|
-
* - '
|
|
126
|
-
* -
|
|
120
|
+
* Controls the source for the GitHub release body.
|
|
121
|
+
* - 'auto': Use release notes if enabled, else changelog, else GitHub auto-generated.
|
|
122
|
+
* - 'releaseNotes': Use LLM-generated release notes (requires notes.releaseNotes.enabled: true).
|
|
123
|
+
* - 'changelog': Use formatted changelog entries.
|
|
124
|
+
* - 'generated': Use GitHub's auto-generated notes.
|
|
125
|
+
* - 'none': No body.
|
|
127
126
|
*/
|
|
128
|
-
|
|
127
|
+
body: z.enum(["auto", "releaseNotes", "changelog", "generated", "none"]).default("auto")
|
|
129
128
|
});
|
|
130
129
|
var VerifyRegistryConfigSchema = z.object({
|
|
131
130
|
enabled: z.boolean().default(true),
|
|
@@ -169,7 +168,7 @@ var PublishConfigSchema = z.object({
|
|
|
169
168
|
draft: true,
|
|
170
169
|
perPackage: true,
|
|
171
170
|
prerelease: "auto",
|
|
172
|
-
|
|
171
|
+
body: "auto"
|
|
173
172
|
}),
|
|
174
173
|
verify: VerifyConfigSchema.default({
|
|
175
174
|
npm: {
|
|
@@ -190,10 +189,10 @@ var TemplateConfigSchema = z.object({
|
|
|
190
189
|
path: z.string().optional(),
|
|
191
190
|
engine: z.enum(["handlebars", "liquid", "ejs"]).optional()
|
|
192
191
|
});
|
|
193
|
-
var
|
|
194
|
-
|
|
192
|
+
var LocationModeSchema = z.enum(["root", "packages", "both"]);
|
|
193
|
+
var ChangelogConfigSchema = z.object({
|
|
194
|
+
mode: LocationModeSchema.optional(),
|
|
195
195
|
file: z.string().optional(),
|
|
196
|
-
options: z.record(z.string(), z.unknown()).optional(),
|
|
197
196
|
templates: TemplateConfigSchema.optional()
|
|
198
197
|
});
|
|
199
198
|
var LLMOptionsSchema = z.object({
|
|
@@ -253,17 +252,20 @@ var LLMConfigSchema = z.object({
|
|
|
253
252
|
scopes: ScopeConfigSchema.optional(),
|
|
254
253
|
prompts: LLMPromptsConfigSchema.optional()
|
|
255
254
|
});
|
|
255
|
+
var ReleaseNotesConfigSchema = z.object({
|
|
256
|
+
mode: LocationModeSchema.optional(),
|
|
257
|
+
file: z.string().optional(),
|
|
258
|
+
templates: TemplateConfigSchema.optional(),
|
|
259
|
+
llm: LLMConfigSchema.optional()
|
|
260
|
+
});
|
|
256
261
|
var NotesInputConfigSchema = z.object({
|
|
257
262
|
source: z.string().optional(),
|
|
258
263
|
file: z.string().optional()
|
|
259
264
|
});
|
|
260
265
|
var NotesConfigSchema = z.object({
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
templates: TemplateConfigSchema.optional(),
|
|
265
|
-
llm: LLMConfigSchema.optional(),
|
|
266
|
-
updateStrategy: z.enum(["prepend", "regenerate"]).default("prepend")
|
|
266
|
+
changelog: z.union([z.literal(false), ChangelogConfigSchema]).optional(),
|
|
267
|
+
releaseNotes: z.union([z.literal(false), ReleaseNotesConfigSchema]).optional(),
|
|
268
|
+
updateStrategy: z.enum(["prepend", "regenerate"]).optional()
|
|
267
269
|
});
|
|
268
270
|
var CILabelsConfigSchema = z.object({
|
|
269
271
|
stable: z.string().default("release:stable"),
|
|
@@ -410,22 +412,6 @@ function loadConfig(options) {
|
|
|
410
412
|
const configPath = options?.configPath ?? path3.join(cwd, CONFIG_FILE);
|
|
411
413
|
return loadConfigFile(configPath);
|
|
412
414
|
}
|
|
413
|
-
function loadNotesConfig(options) {
|
|
414
|
-
const config = loadConfig(options);
|
|
415
|
-
return config.notes;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// src/core/config.ts
|
|
419
|
-
function loadConfig2(projectDir = process.cwd(), configFile) {
|
|
420
|
-
const options = { cwd: projectDir, configPath: configFile };
|
|
421
|
-
return loadNotesConfig(options) ?? getDefaultConfig();
|
|
422
|
-
}
|
|
423
|
-
function getDefaultConfig() {
|
|
424
|
-
return {
|
|
425
|
-
output: [{ format: "markdown", file: "CHANGELOG.md" }],
|
|
426
|
-
updateStrategy: "prepend"
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
415
|
|
|
430
416
|
// src/errors/index.ts
|
|
431
417
|
var NotesError = class extends ReleaseKitError {
|
|
@@ -555,50 +541,28 @@ async function parseVersionOutputStdin() {
|
|
|
555
541
|
return parseVersionOutput(content);
|
|
556
542
|
}
|
|
557
543
|
|
|
558
|
-
// src/
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
packageName: ctx.packageName,
|
|
566
|
-
version: ctx.version,
|
|
567
|
-
previousVersion: ctx.previousVersion,
|
|
568
|
-
date: ctx.date,
|
|
569
|
-
entries: ctx.entries,
|
|
570
|
-
compareUrl: ctx.compareUrl
|
|
571
|
-
}))
|
|
572
|
-
},
|
|
573
|
-
null,
|
|
574
|
-
2
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
function writeJson(outputPath, contexts, dryRun) {
|
|
578
|
-
const content = renderJson(contexts);
|
|
579
|
-
if (dryRun) {
|
|
580
|
-
info(`Would write JSON output to ${outputPath}`);
|
|
581
|
-
debug("--- JSON Output Preview ---");
|
|
582
|
-
debug(content);
|
|
583
|
-
debug("--- End Preview ---");
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
const dir = path4.dirname(outputPath);
|
|
587
|
-
if (!fs5.existsSync(dir)) {
|
|
588
|
-
fs5.mkdirSync(dir, { recursive: true });
|
|
544
|
+
// src/core/config.ts
|
|
545
|
+
function loadConfig2(projectDir = process.cwd(), configFile) {
|
|
546
|
+
const options = { cwd: projectDir, configPath: configFile };
|
|
547
|
+
const fullConfig = loadConfig(options);
|
|
548
|
+
const config = fullConfig.notes ?? getDefaultConfig();
|
|
549
|
+
if (fullConfig.monorepo && !config.monorepo) {
|
|
550
|
+
config.monorepo = fullConfig.monorepo;
|
|
589
551
|
}
|
|
590
|
-
|
|
591
|
-
|
|
552
|
+
return config;
|
|
553
|
+
}
|
|
554
|
+
function getDefaultConfig() {
|
|
555
|
+
return {};
|
|
592
556
|
}
|
|
593
557
|
|
|
594
558
|
// src/core/pipeline.ts
|
|
595
|
-
import * as
|
|
596
|
-
import * as
|
|
559
|
+
import * as fs9 from "fs";
|
|
560
|
+
import * as path7 from "path";
|
|
597
561
|
|
|
598
562
|
// src/llm/defaults.ts
|
|
599
563
|
var LLM_DEFAULTS = {
|
|
600
564
|
timeout: 6e4,
|
|
601
|
-
maxTokens:
|
|
565
|
+
maxTokens: 16384,
|
|
602
566
|
temperature: 0.7,
|
|
603
567
|
concurrency: 5,
|
|
604
568
|
retry: {
|
|
@@ -610,7 +574,7 @@ var LLM_DEFAULTS = {
|
|
|
610
574
|
models: {
|
|
611
575
|
openai: "gpt-4o-mini",
|
|
612
576
|
"openai-compatible": "gpt-4o-mini",
|
|
613
|
-
anthropic: "claude-
|
|
577
|
+
anthropic: "claude-haiku-4-5-20251001",
|
|
614
578
|
ollama: "llama3.2"
|
|
615
579
|
}
|
|
616
580
|
};
|
|
@@ -700,6 +664,13 @@ var OllamaProvider = class extends BaseLLMProvider {
|
|
|
700
664
|
body: JSON.stringify(requestBody)
|
|
701
665
|
});
|
|
702
666
|
if (!response.ok) {
|
|
667
|
+
if (response.status === 401 || response.status === 403) {
|
|
668
|
+
const text2 = await response.text();
|
|
669
|
+
const keyHint = this.apiKey ? "OLLAMA_API_KEY is set but may be invalid or rejected by the server." : "OLLAMA_API_KEY is not set. Set the environment variable or use --no-llm to skip LLM processing.";
|
|
670
|
+
throw new LLMError(
|
|
671
|
+
`Ollama request failed: ${response.status} ${response.statusText}${text2 ? ` - ${text2}` : ""}. ${keyHint}`
|
|
672
|
+
);
|
|
673
|
+
}
|
|
703
674
|
const text = await response.text();
|
|
704
675
|
throw new LLMError(`Ollama request failed: ${response.status} ${text}`);
|
|
705
676
|
}
|
|
@@ -1228,122 +1199,8 @@ function createProvider(config) {
|
|
|
1228
1199
|
}
|
|
1229
1200
|
}
|
|
1230
1201
|
|
|
1231
|
-
// src/output/github-release.ts
|
|
1232
|
-
import { Octokit } from "@octokit/rest";
|
|
1233
|
-
var GitHubClient = class {
|
|
1234
|
-
octokit;
|
|
1235
|
-
owner;
|
|
1236
|
-
repo;
|
|
1237
|
-
constructor(options) {
|
|
1238
|
-
const token = options.token ?? process.env.GITHUB_TOKEN;
|
|
1239
|
-
if (!token) {
|
|
1240
|
-
throw new GitHubError("GITHUB_TOKEN not set. Set it as an environment variable.");
|
|
1241
|
-
}
|
|
1242
|
-
this.octokit = new Octokit({ auth: token });
|
|
1243
|
-
this.owner = options.owner;
|
|
1244
|
-
this.repo = options.repo;
|
|
1245
|
-
}
|
|
1246
|
-
async createRelease(context, options = {}) {
|
|
1247
|
-
const tagName = `v${context.version}`;
|
|
1248
|
-
let body;
|
|
1249
|
-
if (context.enhanced?.releaseNotes) {
|
|
1250
|
-
body = context.enhanced.releaseNotes;
|
|
1251
|
-
} else {
|
|
1252
|
-
body = renderMarkdown([context]);
|
|
1253
|
-
}
|
|
1254
|
-
info(`Creating GitHub release for ${tagName}`);
|
|
1255
|
-
try {
|
|
1256
|
-
const response = await this.octokit.repos.createRelease({
|
|
1257
|
-
owner: this.owner,
|
|
1258
|
-
repo: this.repo,
|
|
1259
|
-
tag_name: tagName,
|
|
1260
|
-
name: tagName,
|
|
1261
|
-
body,
|
|
1262
|
-
draft: options.draft ?? false,
|
|
1263
|
-
prerelease: options.prerelease ?? false,
|
|
1264
|
-
generate_release_notes: options.generateNotes ?? false
|
|
1265
|
-
});
|
|
1266
|
-
success(`Release created: ${response.data.html_url}`);
|
|
1267
|
-
return {
|
|
1268
|
-
id: response.data.id,
|
|
1269
|
-
htmlUrl: response.data.html_url,
|
|
1270
|
-
tagName
|
|
1271
|
-
};
|
|
1272
|
-
} catch (error) {
|
|
1273
|
-
throw new GitHubError(`Failed to create release: ${error instanceof Error ? error.message : String(error)}`);
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
async updateRelease(releaseId, context, options = {}) {
|
|
1277
|
-
const tagName = `v${context.version}`;
|
|
1278
|
-
let body;
|
|
1279
|
-
if (context.enhanced?.releaseNotes) {
|
|
1280
|
-
body = context.enhanced.releaseNotes;
|
|
1281
|
-
} else {
|
|
1282
|
-
body = renderMarkdown([context]);
|
|
1283
|
-
}
|
|
1284
|
-
info(`Updating GitHub release ${releaseId}`);
|
|
1285
|
-
try {
|
|
1286
|
-
const response = await this.octokit.repos.updateRelease({
|
|
1287
|
-
owner: this.owner,
|
|
1288
|
-
repo: this.repo,
|
|
1289
|
-
release_id: releaseId,
|
|
1290
|
-
tag_name: tagName,
|
|
1291
|
-
name: tagName,
|
|
1292
|
-
body,
|
|
1293
|
-
draft: options.draft ?? false,
|
|
1294
|
-
prerelease: options.prerelease ?? false
|
|
1295
|
-
});
|
|
1296
|
-
success(`Release updated: ${response.data.html_url}`);
|
|
1297
|
-
return {
|
|
1298
|
-
id: response.data.id,
|
|
1299
|
-
htmlUrl: response.data.html_url,
|
|
1300
|
-
tagName
|
|
1301
|
-
};
|
|
1302
|
-
} catch (error) {
|
|
1303
|
-
throw new GitHubError(`Failed to update release: ${error instanceof Error ? error.message : String(error)}`);
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
async getReleaseByTag(tag) {
|
|
1307
|
-
try {
|
|
1308
|
-
const response = await this.octokit.repos.getReleaseByTag({
|
|
1309
|
-
owner: this.owner,
|
|
1310
|
-
repo: this.repo,
|
|
1311
|
-
tag
|
|
1312
|
-
});
|
|
1313
|
-
return {
|
|
1314
|
-
id: response.data.id,
|
|
1315
|
-
htmlUrl: response.data.html_url,
|
|
1316
|
-
tagName: response.data.tag_name
|
|
1317
|
-
};
|
|
1318
|
-
} catch {
|
|
1319
|
-
return null;
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
};
|
|
1323
|
-
function parseRepoUrl(repoUrl) {
|
|
1324
|
-
const patterns = [
|
|
1325
|
-
/^https:\/\/github\.com\/([^/]+)\/([^/]+)/,
|
|
1326
|
-
/^git@github\.com:([^/]+)\/([^/]+)/,
|
|
1327
|
-
/^github\.com\/([^/]+)\/([^/]+)/
|
|
1328
|
-
];
|
|
1329
|
-
for (const pattern of patterns) {
|
|
1330
|
-
const match = repoUrl.match(pattern);
|
|
1331
|
-
if (match?.[1] && match[2]) {
|
|
1332
|
-
return {
|
|
1333
|
-
owner: match[1],
|
|
1334
|
-
repo: match[2].replace(/\.git$/, "")
|
|
1335
|
-
};
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
return null;
|
|
1339
|
-
}
|
|
1340
|
-
async function createGitHubRelease(context, options) {
|
|
1341
|
-
const client = new GitHubClient(options);
|
|
1342
|
-
return client.createRelease(context, options);
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
1202
|
// src/templates/ejs.ts
|
|
1346
|
-
import * as
|
|
1203
|
+
import * as fs5 from "fs";
|
|
1347
1204
|
import ejs from "ejs";
|
|
1348
1205
|
function renderEjs(template, context) {
|
|
1349
1206
|
try {
|
|
@@ -1353,16 +1210,16 @@ function renderEjs(template, context) {
|
|
|
1353
1210
|
}
|
|
1354
1211
|
}
|
|
1355
1212
|
function renderEjsFile(filePath, context) {
|
|
1356
|
-
if (!
|
|
1213
|
+
if (!fs5.existsSync(filePath)) {
|
|
1357
1214
|
throw new TemplateError(`Template file not found: ${filePath}`);
|
|
1358
1215
|
}
|
|
1359
|
-
const template =
|
|
1216
|
+
const template = fs5.readFileSync(filePath, "utf-8");
|
|
1360
1217
|
return renderEjs(template, context);
|
|
1361
1218
|
}
|
|
1362
1219
|
|
|
1363
1220
|
// src/templates/handlebars.ts
|
|
1364
|
-
import * as
|
|
1365
|
-
import * as
|
|
1221
|
+
import * as fs6 from "fs";
|
|
1222
|
+
import * as path4 from "path";
|
|
1366
1223
|
import Handlebars from "handlebars";
|
|
1367
1224
|
function registerHandlebarsHelpers() {
|
|
1368
1225
|
Handlebars.registerHelper("capitalize", (str) => {
|
|
@@ -1388,28 +1245,28 @@ function renderHandlebars(template, context) {
|
|
|
1388
1245
|
}
|
|
1389
1246
|
}
|
|
1390
1247
|
function renderHandlebarsFile(filePath, context) {
|
|
1391
|
-
if (!
|
|
1248
|
+
if (!fs6.existsSync(filePath)) {
|
|
1392
1249
|
throw new TemplateError(`Template file not found: ${filePath}`);
|
|
1393
1250
|
}
|
|
1394
|
-
const template =
|
|
1251
|
+
const template = fs6.readFileSync(filePath, "utf-8");
|
|
1395
1252
|
return renderHandlebars(template, context);
|
|
1396
1253
|
}
|
|
1397
1254
|
function renderHandlebarsComposable(templateDir, context) {
|
|
1398
1255
|
registerHandlebarsHelpers();
|
|
1399
|
-
const versionPath =
|
|
1400
|
-
const entryPath =
|
|
1401
|
-
const documentPath =
|
|
1402
|
-
if (!
|
|
1256
|
+
const versionPath = path4.join(templateDir, "version.hbs");
|
|
1257
|
+
const entryPath = path4.join(templateDir, "entry.hbs");
|
|
1258
|
+
const documentPath = path4.join(templateDir, "document.hbs");
|
|
1259
|
+
if (!fs6.existsSync(documentPath)) {
|
|
1403
1260
|
throw new TemplateError(`Document template not found: ${documentPath}`);
|
|
1404
1261
|
}
|
|
1405
|
-
if (
|
|
1406
|
-
Handlebars.registerPartial("version",
|
|
1262
|
+
if (fs6.existsSync(versionPath)) {
|
|
1263
|
+
Handlebars.registerPartial("version", fs6.readFileSync(versionPath, "utf-8"));
|
|
1407
1264
|
}
|
|
1408
|
-
if (
|
|
1409
|
-
Handlebars.registerPartial("entry",
|
|
1265
|
+
if (fs6.existsSync(entryPath)) {
|
|
1266
|
+
Handlebars.registerPartial("entry", fs6.readFileSync(entryPath, "utf-8"));
|
|
1410
1267
|
}
|
|
1411
1268
|
try {
|
|
1412
|
-
const compiled = Handlebars.compile(
|
|
1269
|
+
const compiled = Handlebars.compile(fs6.readFileSync(documentPath, "utf-8"));
|
|
1413
1270
|
return compiled(context);
|
|
1414
1271
|
} catch (error) {
|
|
1415
1272
|
throw new TemplateError(`Handlebars render error: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1417,8 +1274,8 @@ function renderHandlebarsComposable(templateDir, context) {
|
|
|
1417
1274
|
}
|
|
1418
1275
|
|
|
1419
1276
|
// src/templates/liquid.ts
|
|
1420
|
-
import * as
|
|
1421
|
-
import * as
|
|
1277
|
+
import * as fs7 from "fs";
|
|
1278
|
+
import * as path5 from "path";
|
|
1422
1279
|
import { Liquid } from "liquidjs";
|
|
1423
1280
|
function createLiquidEngine(root) {
|
|
1424
1281
|
return new Liquid({
|
|
@@ -1436,15 +1293,15 @@ function renderLiquid(template, context) {
|
|
|
1436
1293
|
}
|
|
1437
1294
|
}
|
|
1438
1295
|
function renderLiquidFile(filePath, context) {
|
|
1439
|
-
if (!
|
|
1296
|
+
if (!fs7.existsSync(filePath)) {
|
|
1440
1297
|
throw new TemplateError(`Template file not found: ${filePath}`);
|
|
1441
1298
|
}
|
|
1442
|
-
const template =
|
|
1299
|
+
const template = fs7.readFileSync(filePath, "utf-8");
|
|
1443
1300
|
return renderLiquid(template, context);
|
|
1444
1301
|
}
|
|
1445
1302
|
function renderLiquidComposable(templateDir, context) {
|
|
1446
|
-
const documentPath =
|
|
1447
|
-
if (!
|
|
1303
|
+
const documentPath = path5.join(templateDir, "document.liquid");
|
|
1304
|
+
if (!fs7.existsSync(documentPath)) {
|
|
1448
1305
|
throw new TemplateError(`Document template not found: ${documentPath}`);
|
|
1449
1306
|
}
|
|
1450
1307
|
const engine = createLiquidEngine(templateDir);
|
|
@@ -1456,10 +1313,10 @@ function renderLiquidComposable(templateDir, context) {
|
|
|
1456
1313
|
}
|
|
1457
1314
|
|
|
1458
1315
|
// src/templates/loader.ts
|
|
1459
|
-
import * as
|
|
1460
|
-
import * as
|
|
1316
|
+
import * as fs8 from "fs";
|
|
1317
|
+
import * as path6 from "path";
|
|
1461
1318
|
function getEngineFromFile(filePath) {
|
|
1462
|
-
const ext =
|
|
1319
|
+
const ext = path6.extname(filePath).toLowerCase();
|
|
1463
1320
|
switch (ext) {
|
|
1464
1321
|
case ".liquid":
|
|
1465
1322
|
return "liquid";
|
|
@@ -1493,10 +1350,10 @@ function getRenderFileFn(engine) {
|
|
|
1493
1350
|
}
|
|
1494
1351
|
}
|
|
1495
1352
|
function detectTemplateMode(templatePath) {
|
|
1496
|
-
if (!
|
|
1353
|
+
if (!fs8.existsSync(templatePath)) {
|
|
1497
1354
|
throw new TemplateError(`Template path not found: ${templatePath}`);
|
|
1498
1355
|
}
|
|
1499
|
-
const stat =
|
|
1356
|
+
const stat = fs8.statSync(templatePath);
|
|
1500
1357
|
if (stat.isFile()) {
|
|
1501
1358
|
return "single";
|
|
1502
1359
|
}
|
|
@@ -1514,7 +1371,7 @@ function renderSingleFile(templatePath, context, engine) {
|
|
|
1514
1371
|
};
|
|
1515
1372
|
}
|
|
1516
1373
|
function renderComposable(templateDir, context, engine) {
|
|
1517
|
-
const files =
|
|
1374
|
+
const files = fs8.readdirSync(templateDir);
|
|
1518
1375
|
const engineMap = {
|
|
1519
1376
|
liquid: { document: "document.liquid", version: "version.liquid", entry: "entry.liquid" },
|
|
1520
1377
|
handlebars: { document: "document.hbs", version: "version.hbs", entry: "entry.hbs" },
|
|
@@ -1537,15 +1394,15 @@ function renderComposable(templateDir, context, engine) {
|
|
|
1537
1394
|
return { content: renderHandlebarsComposable(templateDir, context), engine: resolvedEngine };
|
|
1538
1395
|
}
|
|
1539
1396
|
const expectedFiles = engineMap[resolvedEngine];
|
|
1540
|
-
const documentPath =
|
|
1541
|
-
if (!
|
|
1397
|
+
const documentPath = path6.join(templateDir, expectedFiles.document);
|
|
1398
|
+
if (!fs8.existsSync(documentPath)) {
|
|
1542
1399
|
throw new TemplateError(`Document template not found: ${expectedFiles.document}`);
|
|
1543
1400
|
}
|
|
1544
|
-
const versionPath =
|
|
1545
|
-
const entryPath =
|
|
1401
|
+
const versionPath = path6.join(templateDir, expectedFiles.version);
|
|
1402
|
+
const entryPath = path6.join(templateDir, expectedFiles.entry);
|
|
1546
1403
|
const render = getRenderFn(resolvedEngine);
|
|
1547
|
-
const entryTemplate =
|
|
1548
|
-
const versionTemplate =
|
|
1404
|
+
const entryTemplate = fs8.existsSync(entryPath) ? fs8.readFileSync(entryPath, "utf-8") : null;
|
|
1405
|
+
const versionTemplate = fs8.existsSync(versionPath) ? fs8.readFileSync(versionPath, "utf-8") : null;
|
|
1549
1406
|
if (entryTemplate && versionTemplate) {
|
|
1550
1407
|
const versionsWithEntries = context.versions.map((versionCtx) => {
|
|
1551
1408
|
const entries = versionCtx.entries.map((entry) => {
|
|
@@ -1556,7 +1413,7 @@ function renderComposable(templateDir, context, engine) {
|
|
|
1556
1413
|
});
|
|
1557
1414
|
const docContext = { ...context, renderedVersions: versionsWithEntries };
|
|
1558
1415
|
return {
|
|
1559
|
-
content: render(
|
|
1416
|
+
content: render(fs8.readFileSync(documentPath, "utf-8"), docContext),
|
|
1560
1417
|
engine: resolvedEngine
|
|
1561
1418
|
};
|
|
1562
1419
|
}
|
|
@@ -1654,36 +1511,31 @@ function createDocumentContext(contexts, repoUrl) {
|
|
|
1654
1511
|
compareUrls: Object.keys(compareUrls).length > 0 ? compareUrls : void 0
|
|
1655
1512
|
};
|
|
1656
1513
|
}
|
|
1657
|
-
async function processWithLLM(context,
|
|
1658
|
-
|
|
1659
|
-
return context;
|
|
1660
|
-
}
|
|
1661
|
-
const tasks = config.llm.tasks ?? {};
|
|
1514
|
+
async function processWithLLM(context, llmConfig) {
|
|
1515
|
+
const tasks = llmConfig.tasks ?? {};
|
|
1662
1516
|
const llmContext = {
|
|
1663
1517
|
packageName: context.packageName,
|
|
1664
1518
|
version: context.version,
|
|
1665
1519
|
previousVersion: context.previousVersion ?? void 0,
|
|
1666
1520
|
date: context.date,
|
|
1667
|
-
categories:
|
|
1668
|
-
style:
|
|
1669
|
-
scopes:
|
|
1670
|
-
prompts:
|
|
1521
|
+
categories: llmConfig.categories,
|
|
1522
|
+
style: llmConfig.style,
|
|
1523
|
+
scopes: llmConfig.scopes,
|
|
1524
|
+
prompts: llmConfig.prompts
|
|
1671
1525
|
};
|
|
1672
1526
|
const enhanced = {
|
|
1673
1527
|
entries: context.entries
|
|
1674
1528
|
};
|
|
1675
1529
|
try {
|
|
1676
|
-
info(`Using LLM provider: ${
|
|
1677
|
-
if (
|
|
1678
|
-
info(`LLM base URL: ${
|
|
1530
|
+
info(`Using LLM provider: ${llmConfig.provider}${llmConfig.model ? ` (${llmConfig.model})` : ""}`);
|
|
1531
|
+
if (llmConfig.baseURL) {
|
|
1532
|
+
info(`LLM base URL: ${llmConfig.baseURL}`);
|
|
1679
1533
|
}
|
|
1680
|
-
const rawProvider = createProvider(
|
|
1681
|
-
const retryOpts =
|
|
1682
|
-
const configOptions =
|
|
1534
|
+
const rawProvider = createProvider(llmConfig);
|
|
1535
|
+
const retryOpts = llmConfig.retry ?? LLM_DEFAULTS.retry;
|
|
1536
|
+
const configOptions = llmConfig.options;
|
|
1683
1537
|
const provider = {
|
|
1684
1538
|
name: rawProvider.name,
|
|
1685
|
-
// Merge user-configured options (timeout, maxTokens, temperature) as base defaults,
|
|
1686
|
-
// allowing any per-call overrides to take precedence.
|
|
1687
1539
|
complete: (prompt, opts) => withRetry(() => rawProvider.complete(prompt, { ...configOptions, ...opts }), retryOpts)
|
|
1688
1540
|
};
|
|
1689
1541
|
const activeTasks = Object.entries(tasks).filter(([, enabled]) => enabled).map(([name]) => name);
|
|
@@ -1697,7 +1549,7 @@ async function processWithLLM(context, config) {
|
|
|
1697
1549
|
} else {
|
|
1698
1550
|
if (tasks.enhance) {
|
|
1699
1551
|
info("Enhancing entries with LLM...");
|
|
1700
|
-
enhanced.entries = await enhanceEntries(provider, context.entries, llmContext,
|
|
1552
|
+
enhanced.entries = await enhanceEntries(provider, context.entries, llmContext, llmConfig.concurrency);
|
|
1701
1553
|
info(`Enhanced ${enhanced.entries.length} entries`);
|
|
1702
1554
|
}
|
|
1703
1555
|
if (tasks.categorize) {
|
|
@@ -1740,25 +1592,22 @@ function getBuiltinTemplatePath(style) {
|
|
|
1740
1592
|
let packageRoot;
|
|
1741
1593
|
try {
|
|
1742
1594
|
const currentUrl = import.meta.url;
|
|
1743
|
-
packageRoot =
|
|
1744
|
-
packageRoot =
|
|
1595
|
+
packageRoot = path7.dirname(new URL(currentUrl).pathname);
|
|
1596
|
+
packageRoot = path7.join(packageRoot, "..", "..");
|
|
1745
1597
|
} catch {
|
|
1746
1598
|
packageRoot = __dirname;
|
|
1747
1599
|
}
|
|
1748
|
-
return
|
|
1600
|
+
return path7.join(packageRoot, "templates", style);
|
|
1749
1601
|
}
|
|
1750
|
-
async function generateWithTemplate(contexts,
|
|
1602
|
+
async function generateWithTemplate(contexts, templatesConfig, outputPath, repoUrl, dryRun) {
|
|
1751
1603
|
let templatePath;
|
|
1752
|
-
if (
|
|
1753
|
-
templatePath =
|
|
1604
|
+
if (templatesConfig?.path) {
|
|
1605
|
+
templatePath = path7.resolve(templatesConfig.path);
|
|
1754
1606
|
} else {
|
|
1755
1607
|
templatePath = getBuiltinTemplatePath("keep-a-changelog");
|
|
1756
1608
|
}
|
|
1757
|
-
const documentContext = createDocumentContext(
|
|
1758
|
-
|
|
1759
|
-
config.templates?.path ? void 0 : contexts[0]?.repoUrl ?? void 0
|
|
1760
|
-
);
|
|
1761
|
-
const result = renderTemplate(templatePath, documentContext, config.templates?.engine);
|
|
1609
|
+
const documentContext = createDocumentContext(contexts, templatesConfig?.path ? void 0 : repoUrl);
|
|
1610
|
+
const result = renderTemplate(templatePath, documentContext, templatesConfig?.engine);
|
|
1762
1611
|
if (dryRun) {
|
|
1763
1612
|
info(`[DRY RUN] Changelog preview (would write to ${outputPath}):`);
|
|
1764
1613
|
info(result.content);
|
|
@@ -1768,121 +1617,128 @@ async function generateWithTemplate(contexts, config, outputPath, dryRun) {
|
|
|
1768
1617
|
process.stdout.write(result.content);
|
|
1769
1618
|
return;
|
|
1770
1619
|
}
|
|
1771
|
-
const dir =
|
|
1772
|
-
if (!
|
|
1773
|
-
|
|
1620
|
+
const dir = path7.dirname(outputPath);
|
|
1621
|
+
if (!fs9.existsSync(dir)) {
|
|
1622
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
1774
1623
|
}
|
|
1775
|
-
|
|
1624
|
+
fs9.writeFileSync(outputPath, result.content, "utf-8");
|
|
1776
1625
|
const label = /changelog/i.test(outputPath) ? "Changelog" : "Release notes";
|
|
1777
1626
|
success(`${label} written to ${outputPath} (using ${result.engine} template)`);
|
|
1778
1627
|
}
|
|
1779
1628
|
async function runPipeline(input, config, dryRun) {
|
|
1780
1629
|
debug(`Processing ${input.packages.length} package(s)`);
|
|
1781
1630
|
let contexts = input.packages.map(createTemplateContext);
|
|
1782
|
-
|
|
1631
|
+
const changelogConfig = config.changelog === false ? false : { mode: "root", ...config.changelog ?? {} };
|
|
1632
|
+
const releaseNotesConfig = config.releaseNotes === false || config.releaseNotes === void 0 ? void 0 : config.releaseNotes.mode !== void 0 || config.releaseNotes.file !== void 0 ? { mode: "root", ...config.releaseNotes } : config.releaseNotes;
|
|
1633
|
+
const llmConfig = releaseNotesConfig?.llm;
|
|
1634
|
+
if (llmConfig && !process.env.CHANGELOG_NO_LLM) {
|
|
1783
1635
|
info("Processing with LLM enhancement");
|
|
1784
|
-
contexts = await Promise.all(contexts.map((ctx) => processWithLLM(ctx,
|
|
1636
|
+
contexts = await Promise.all(contexts.map((ctx) => processWithLLM(ctx, llmConfig)));
|
|
1785
1637
|
}
|
|
1786
1638
|
const files = [];
|
|
1787
1639
|
const fmtOpts = {
|
|
1788
1640
|
includePackageName: contexts.length > 1 || contexts.some((c) => c.packageName.includes("/"))
|
|
1789
1641
|
};
|
|
1790
|
-
|
|
1791
|
-
const
|
|
1792
|
-
const
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
if (!dryRun) files.push(file2);
|
|
1807
|
-
} catch (error) {
|
|
1808
|
-
warn(`Failed to write ${file2}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1809
|
-
}
|
|
1810
|
-
break;
|
|
1811
|
-
}
|
|
1812
|
-
case "json": {
|
|
1813
|
-
const file2 = output.file ?? "changelog.json";
|
|
1814
|
-
try {
|
|
1815
|
-
writeJson(file2, contexts, dryRun);
|
|
1816
|
-
if (!dryRun) files.push(file2);
|
|
1817
|
-
} catch (error) {
|
|
1818
|
-
warn(`Failed to write ${file2}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1642
|
+
if (changelogConfig !== false && changelogConfig.mode) {
|
|
1643
|
+
const fileName = changelogConfig.file ?? "CHANGELOG.md";
|
|
1644
|
+
const mode = changelogConfig.mode;
|
|
1645
|
+
info(`Generating changelog \u2192 ${fileName}`);
|
|
1646
|
+
try {
|
|
1647
|
+
if (mode === "root" || mode === "both") {
|
|
1648
|
+
if (changelogConfig.templates?.path) {
|
|
1649
|
+
await generateWithTemplate(
|
|
1650
|
+
contexts,
|
|
1651
|
+
changelogConfig.templates,
|
|
1652
|
+
fileName,
|
|
1653
|
+
contexts[0]?.repoUrl ?? void 0,
|
|
1654
|
+
dryRun
|
|
1655
|
+
);
|
|
1656
|
+
} else {
|
|
1657
|
+
writeMarkdown(fileName, contexts, config, dryRun, fmtOpts);
|
|
1819
1658
|
}
|
|
1820
|
-
|
|
1659
|
+
if (!dryRun) files.push(fileName);
|
|
1821
1660
|
}
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
break;
|
|
1826
|
-
}
|
|
1827
|
-
const firstContext = contexts[0];
|
|
1828
|
-
if (!firstContext) {
|
|
1829
|
-
warn("No context available for GitHub release");
|
|
1830
|
-
break;
|
|
1831
|
-
}
|
|
1832
|
-
const repoUrl = firstContext.repoUrl;
|
|
1833
|
-
if (!repoUrl) {
|
|
1834
|
-
warn("No repo URL available, cannot create GitHub release");
|
|
1835
|
-
break;
|
|
1836
|
-
}
|
|
1837
|
-
const parsed = parseRepoUrl(repoUrl);
|
|
1838
|
-
if (!parsed) {
|
|
1839
|
-
warn(`Could not parse repo URL: ${repoUrl}`);
|
|
1840
|
-
break;
|
|
1841
|
-
}
|
|
1842
|
-
await createGitHubRelease(firstContext, {
|
|
1843
|
-
owner: parsed.owner,
|
|
1844
|
-
repo: parsed.repo,
|
|
1845
|
-
draft: output.options?.draft,
|
|
1846
|
-
prerelease: output.options?.prerelease
|
|
1847
|
-
});
|
|
1848
|
-
break;
|
|
1661
|
+
if (mode === "packages" || mode === "both") {
|
|
1662
|
+
const monoFiles = await writeMonorepoFiles(contexts, config, dryRun, changelogConfig.file ?? "CHANGELOG.md");
|
|
1663
|
+
files.push(...monoFiles);
|
|
1849
1664
|
}
|
|
1665
|
+
} catch (error) {
|
|
1666
|
+
warn(`Failed to write changelog: ${error instanceof Error ? error.message : String(error)}`);
|
|
1850
1667
|
}
|
|
1851
1668
|
}
|
|
1852
|
-
if (
|
|
1853
|
-
const
|
|
1854
|
-
const
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1669
|
+
if (releaseNotesConfig?.mode) {
|
|
1670
|
+
const fileName = releaseNotesConfig.file ?? "RELEASE_NOTES.md";
|
|
1671
|
+
const mode = releaseNotesConfig.mode;
|
|
1672
|
+
info(`Generating release notes \u2192 ${fileName}`);
|
|
1673
|
+
try {
|
|
1674
|
+
if (mode === "root" || mode === "both") {
|
|
1675
|
+
if (releaseNotesConfig.templates?.path) {
|
|
1676
|
+
await generateWithTemplate(
|
|
1677
|
+
contexts,
|
|
1678
|
+
releaseNotesConfig.templates,
|
|
1679
|
+
fileName,
|
|
1680
|
+
contexts[0]?.repoUrl ?? void 0,
|
|
1681
|
+
dryRun
|
|
1682
|
+
);
|
|
1683
|
+
} else {
|
|
1684
|
+
writeMarkdown(fileName, contexts, config, dryRun, fmtOpts);
|
|
1685
|
+
}
|
|
1686
|
+
if (!dryRun) files.push(fileName);
|
|
1687
|
+
}
|
|
1688
|
+
if (mode === "packages" || mode === "both") {
|
|
1689
|
+
const monoFiles = await writeMonorepoFiles(
|
|
1690
|
+
contexts,
|
|
1691
|
+
config,
|
|
1692
|
+
dryRun,
|
|
1693
|
+
releaseNotesConfig.file ?? "RELEASE_NOTES.md"
|
|
1694
|
+
);
|
|
1695
|
+
files.push(...monoFiles);
|
|
1696
|
+
}
|
|
1697
|
+
} catch (error) {
|
|
1698
|
+
warn(`Failed to write release notes: ${error instanceof Error ? error.message : String(error)}`);
|
|
1868
1699
|
}
|
|
1869
1700
|
}
|
|
1870
1701
|
const packageNotes = {};
|
|
1702
|
+
const releaseNotesResult = {};
|
|
1871
1703
|
for (const ctx of contexts) {
|
|
1872
1704
|
packageNotes[ctx.packageName] = formatVersion(ctx);
|
|
1705
|
+
if (ctx.enhanced?.releaseNotes) {
|
|
1706
|
+
releaseNotesResult[ctx.packageName] = ctx.enhanced.releaseNotes;
|
|
1707
|
+
}
|
|
1873
1708
|
}
|
|
1874
|
-
return {
|
|
1709
|
+
return {
|
|
1710
|
+
packageNotes,
|
|
1711
|
+
files,
|
|
1712
|
+
releaseNotes: Object.keys(releaseNotesResult).length > 0 ? releaseNotesResult : void 0
|
|
1713
|
+
};
|
|
1875
1714
|
}
|
|
1876
1715
|
async function processInput(inputJson, config, dryRun) {
|
|
1877
1716
|
const input = parseVersionOutput(inputJson);
|
|
1878
1717
|
return runPipeline(input, config, dryRun);
|
|
1879
1718
|
}
|
|
1719
|
+
async function writeMonorepoFiles(contexts, config, dryRun, fileName) {
|
|
1720
|
+
const { detectMonorepo, writeMonorepoChangelogs } = await import("./aggregator-IUQUAVJC.js");
|
|
1721
|
+
const cwd = process.cwd();
|
|
1722
|
+
const detected = detectMonorepo(cwd);
|
|
1723
|
+
if (!detected.isMonorepo) return [];
|
|
1724
|
+
const monoFiles = writeMonorepoChangelogs(
|
|
1725
|
+
contexts,
|
|
1726
|
+
{
|
|
1727
|
+
rootPath: config.monorepo?.rootPath ?? cwd,
|
|
1728
|
+
packagesPath: config.monorepo?.packagesPath ?? detected.packagesPath,
|
|
1729
|
+
mode: "packages",
|
|
1730
|
+
fileName
|
|
1731
|
+
},
|
|
1732
|
+
config,
|
|
1733
|
+
dryRun
|
|
1734
|
+
);
|
|
1735
|
+
return monoFiles;
|
|
1736
|
+
}
|
|
1880
1737
|
|
|
1881
1738
|
export {
|
|
1882
1739
|
loadAuth,
|
|
1883
1740
|
saveAuth,
|
|
1884
|
-
|
|
1885
|
-
getDefaultConfig,
|
|
1741
|
+
loadConfig,
|
|
1886
1742
|
NotesError,
|
|
1887
1743
|
InputParseError,
|
|
1888
1744
|
TemplateError,
|
|
@@ -1893,8 +1749,8 @@ export {
|
|
|
1893
1749
|
parseVersionOutput,
|
|
1894
1750
|
parseVersionOutputFile,
|
|
1895
1751
|
parseVersionOutputStdin,
|
|
1896
|
-
|
|
1897
|
-
|
|
1752
|
+
loadConfig2,
|
|
1753
|
+
getDefaultConfig,
|
|
1898
1754
|
createTemplateContext,
|
|
1899
1755
|
runPipeline,
|
|
1900
1756
|
processInput
|