@releasekit/notes 0.3.0-next.4 → 0.3.1
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 +2 -0
- package/dist/{aggregator-JZ3VUZKP.js → aggregator-XJ2EILO3.js} +2 -2
- package/dist/{chunk-TSLTZ26C.js → chunk-DCQ32FVH.js} +5 -3
- package/dist/chunk-E4454SIS.js +243 -0
- package/dist/{chunk-QUBVC5LF.js → chunk-ENAWZXFG.js} +559 -146
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +134 -111
- package/dist/index.d.ts +8 -219
- package/dist/index.js +9 -12
- package/package.json +14 -12
- package/dist/chunk-QCF6V2IY.js +0 -135
- package/dist/cli.cjs +0 -1939
- package/dist/cli.d.cts +0 -1
- package/dist/index.cjs +0 -1866
- package/dist/index.d.cts +0 -219
|
@@ -1,18 +1,424 @@
|
|
|
1
1
|
import {
|
|
2
|
+
EXIT_CODES,
|
|
3
|
+
ReleaseKitError,
|
|
4
|
+
debug,
|
|
2
5
|
formatVersion,
|
|
6
|
+
info,
|
|
3
7
|
renderMarkdown,
|
|
8
|
+
success,
|
|
9
|
+
warn,
|
|
4
10
|
writeMarkdown
|
|
5
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-E4454SIS.js";
|
|
12
|
+
|
|
13
|
+
// ../config/dist/index.js
|
|
14
|
+
import * as fs from "fs";
|
|
15
|
+
import * as path from "path";
|
|
16
|
+
import * as TOML from "smol-toml";
|
|
17
|
+
import * as fs3 from "fs";
|
|
18
|
+
import * as path3 from "path";
|
|
19
|
+
import { z as z2 } from "zod";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import * as fs2 from "fs";
|
|
22
|
+
import * as os from "os";
|
|
23
|
+
import * as path2 from "path";
|
|
24
|
+
var ConfigError = class extends ReleaseKitError {
|
|
25
|
+
code = "CONFIG_ERROR";
|
|
26
|
+
suggestions;
|
|
27
|
+
constructor(message, suggestions) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.suggestions = suggestions ?? [
|
|
30
|
+
"Check that releasekit.config.json exists and is valid JSON",
|
|
31
|
+
"Run with --verbose for more details"
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var MAX_JSONC_LENGTH = 1e5;
|
|
36
|
+
function parseJsonc(content) {
|
|
37
|
+
if (content.length > MAX_JSONC_LENGTH) {
|
|
38
|
+
throw new Error(`JSONC content too long: ${content.length} characters (max ${MAX_JSONC_LENGTH})`);
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(content);
|
|
42
|
+
} catch {
|
|
43
|
+
const cleaned = content.replace(/\/\/[^\r\n]{0,10000}$/gm, "").replace(/\/\*[\s\S]{0,50000}?\*\//g, "").trim();
|
|
44
|
+
return JSON.parse(cleaned);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
var GitConfigSchema = z.object({
|
|
48
|
+
remote: z.string().default("origin"),
|
|
49
|
+
branch: z.string().default("main"),
|
|
50
|
+
pushMethod: z.enum(["auto", "ssh", "https"]).default("auto"),
|
|
51
|
+
/**
|
|
52
|
+
* Optional env var name containing a GitHub token for HTTPS pushes.
|
|
53
|
+
* When set, publish steps can use this token without mutating git remotes.
|
|
54
|
+
*/
|
|
55
|
+
httpsTokenEnv: z.string().optional(),
|
|
56
|
+
push: z.boolean().optional(),
|
|
57
|
+
skipHooks: z.boolean().optional()
|
|
58
|
+
});
|
|
59
|
+
var MonorepoConfigSchema = z.object({
|
|
60
|
+
mode: z.enum(["root", "packages", "both"]).optional(),
|
|
61
|
+
rootPath: z.string().optional(),
|
|
62
|
+
packagesPath: z.string().optional(),
|
|
63
|
+
mainPackage: z.string().optional()
|
|
64
|
+
});
|
|
65
|
+
var BranchPatternSchema = z.object({
|
|
66
|
+
pattern: z.string(),
|
|
67
|
+
releaseType: z.enum(["major", "minor", "patch", "prerelease"])
|
|
68
|
+
});
|
|
69
|
+
var VersionCargoConfigSchema = z.object({
|
|
70
|
+
enabled: z.boolean().default(true),
|
|
71
|
+
paths: z.array(z.string()).optional()
|
|
72
|
+
});
|
|
73
|
+
var VersionConfigSchema = z.object({
|
|
74
|
+
tagTemplate: z.string().default("v{version}"),
|
|
75
|
+
packageSpecificTags: z.boolean().default(false),
|
|
76
|
+
preset: z.string().default("conventional"),
|
|
77
|
+
sync: z.boolean().default(true),
|
|
78
|
+
packages: z.array(z.string()).default([]),
|
|
79
|
+
mainPackage: z.string().optional(),
|
|
80
|
+
updateInternalDependencies: z.enum(["major", "minor", "patch", "no-internal-update"]).default("minor"),
|
|
81
|
+
skip: z.array(z.string()).optional(),
|
|
82
|
+
commitMessage: z.string().optional(),
|
|
83
|
+
versionStrategy: z.enum(["branchPattern", "commitMessage"]).default("commitMessage"),
|
|
84
|
+
branchPatterns: z.array(BranchPatternSchema).optional(),
|
|
85
|
+
defaultReleaseType: z.enum(["major", "minor", "patch", "prerelease"]).optional(),
|
|
86
|
+
mismatchStrategy: z.enum(["error", "warn", "ignore", "prefer-package", "prefer-git"]).default("warn"),
|
|
87
|
+
versionPrefix: z.string().default(""),
|
|
88
|
+
prereleaseIdentifier: z.string().optional(),
|
|
89
|
+
strictReachable: z.boolean().default(false),
|
|
90
|
+
cargo: VersionCargoConfigSchema.optional()
|
|
91
|
+
});
|
|
92
|
+
var NpmConfigSchema = z.object({
|
|
93
|
+
enabled: z.boolean().default(true),
|
|
94
|
+
auth: z.enum(["auto", "oidc", "token"]).default("auto"),
|
|
95
|
+
provenance: z.boolean().default(true),
|
|
96
|
+
access: z.enum(["public", "restricted"]).default("public"),
|
|
97
|
+
registry: z.string().default("https://registry.npmjs.org"),
|
|
98
|
+
copyFiles: z.array(z.string()).default(["LICENSE"]),
|
|
99
|
+
tag: z.string().default("latest")
|
|
100
|
+
});
|
|
101
|
+
var CargoPublishConfigSchema = z.object({
|
|
102
|
+
enabled: z.boolean().default(false),
|
|
103
|
+
noVerify: z.boolean().default(false),
|
|
104
|
+
publishOrder: z.array(z.string()).default([]),
|
|
105
|
+
clean: z.boolean().default(false)
|
|
106
|
+
});
|
|
107
|
+
var PublishGitConfigSchema = z.object({
|
|
108
|
+
push: z.boolean().default(true),
|
|
109
|
+
pushMethod: z.enum(["auto", "ssh", "https"]).optional(),
|
|
110
|
+
remote: z.string().optional(),
|
|
111
|
+
branch: z.string().optional(),
|
|
112
|
+
httpsTokenEnv: z.string().optional(),
|
|
113
|
+
skipHooks: z.boolean().optional()
|
|
114
|
+
});
|
|
115
|
+
var GitHubReleaseConfigSchema = z.object({
|
|
116
|
+
enabled: z.boolean().default(true),
|
|
117
|
+
draft: z.boolean().default(true),
|
|
118
|
+
perPackage: z.boolean().default(true),
|
|
119
|
+
prerelease: z.union([z.literal("auto"), z.boolean()]).default("auto"),
|
|
120
|
+
/**
|
|
121
|
+
* Controls how release notes are sourced for GitHub releases.
|
|
122
|
+
* - 'auto': Use RELEASE_NOTES.md if it exists, then per-package changelog
|
|
123
|
+
* data from the version output, then GitHub's auto-generated notes.
|
|
124
|
+
* - 'github': Always use GitHub's auto-generated notes.
|
|
125
|
+
* - 'none': No notes body.
|
|
126
|
+
* - Any other string: Treated as a file path to read notes from.
|
|
127
|
+
*/
|
|
128
|
+
releaseNotes: z.union([z.literal("auto"), z.literal("github"), z.literal("none"), z.string()]).default("auto")
|
|
129
|
+
});
|
|
130
|
+
var VerifyRegistryConfigSchema = z.object({
|
|
131
|
+
enabled: z.boolean().default(true),
|
|
132
|
+
maxAttempts: z.number().int().positive().default(5),
|
|
133
|
+
initialDelay: z.number().int().positive().default(15e3),
|
|
134
|
+
backoffMultiplier: z.number().positive().default(2)
|
|
135
|
+
});
|
|
136
|
+
var VerifyConfigSchema = z.object({
|
|
137
|
+
npm: VerifyRegistryConfigSchema.default({
|
|
138
|
+
enabled: true,
|
|
139
|
+
maxAttempts: 5,
|
|
140
|
+
initialDelay: 15e3,
|
|
141
|
+
backoffMultiplier: 2
|
|
142
|
+
}),
|
|
143
|
+
cargo: VerifyRegistryConfigSchema.default({
|
|
144
|
+
enabled: true,
|
|
145
|
+
maxAttempts: 10,
|
|
146
|
+
initialDelay: 3e4,
|
|
147
|
+
backoffMultiplier: 2
|
|
148
|
+
})
|
|
149
|
+
});
|
|
150
|
+
var PublishConfigSchema = z.object({
|
|
151
|
+
git: PublishGitConfigSchema.optional(),
|
|
152
|
+
npm: NpmConfigSchema.default({
|
|
153
|
+
enabled: true,
|
|
154
|
+
auth: "auto",
|
|
155
|
+
provenance: true,
|
|
156
|
+
access: "public",
|
|
157
|
+
registry: "https://registry.npmjs.org",
|
|
158
|
+
copyFiles: ["LICENSE"],
|
|
159
|
+
tag: "latest"
|
|
160
|
+
}),
|
|
161
|
+
cargo: CargoPublishConfigSchema.default({
|
|
162
|
+
enabled: false,
|
|
163
|
+
noVerify: false,
|
|
164
|
+
publishOrder: [],
|
|
165
|
+
clean: false
|
|
166
|
+
}),
|
|
167
|
+
githubRelease: GitHubReleaseConfigSchema.default({
|
|
168
|
+
enabled: true,
|
|
169
|
+
draft: true,
|
|
170
|
+
perPackage: true,
|
|
171
|
+
prerelease: "auto",
|
|
172
|
+
releaseNotes: "auto"
|
|
173
|
+
}),
|
|
174
|
+
verify: VerifyConfigSchema.default({
|
|
175
|
+
npm: {
|
|
176
|
+
enabled: true,
|
|
177
|
+
maxAttempts: 5,
|
|
178
|
+
initialDelay: 15e3,
|
|
179
|
+
backoffMultiplier: 2
|
|
180
|
+
},
|
|
181
|
+
cargo: {
|
|
182
|
+
enabled: true,
|
|
183
|
+
maxAttempts: 10,
|
|
184
|
+
initialDelay: 3e4,
|
|
185
|
+
backoffMultiplier: 2
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
});
|
|
189
|
+
var TemplateConfigSchema = z.object({
|
|
190
|
+
path: z.string().optional(),
|
|
191
|
+
engine: z.enum(["handlebars", "liquid", "ejs"]).optional()
|
|
192
|
+
});
|
|
193
|
+
var OutputConfigSchema = z.object({
|
|
194
|
+
format: z.enum(["markdown", "github-release", "json"]),
|
|
195
|
+
file: z.string().optional(),
|
|
196
|
+
options: z.record(z.string(), z.unknown()).optional(),
|
|
197
|
+
templates: TemplateConfigSchema.optional()
|
|
198
|
+
});
|
|
199
|
+
var LLMOptionsSchema = z.object({
|
|
200
|
+
timeout: z.number().optional(),
|
|
201
|
+
maxTokens: z.number().optional(),
|
|
202
|
+
temperature: z.number().optional()
|
|
203
|
+
});
|
|
204
|
+
var LLMRetryConfigSchema = z.object({
|
|
205
|
+
maxAttempts: z.number().int().positive().optional(),
|
|
206
|
+
initialDelay: z.number().nonnegative().optional(),
|
|
207
|
+
maxDelay: z.number().positive().optional(),
|
|
208
|
+
backoffFactor: z.number().positive().optional()
|
|
209
|
+
});
|
|
210
|
+
var LLMTasksConfigSchema = z.object({
|
|
211
|
+
summarize: z.boolean().optional(),
|
|
212
|
+
enhance: z.boolean().optional(),
|
|
213
|
+
categorize: z.boolean().optional(),
|
|
214
|
+
releaseNotes: z.boolean().optional()
|
|
215
|
+
});
|
|
216
|
+
var LLMCategorySchema = z.object({
|
|
217
|
+
name: z.string(),
|
|
218
|
+
description: z.string(),
|
|
219
|
+
scopes: z.array(z.string()).optional()
|
|
220
|
+
});
|
|
221
|
+
var ScopeRulesSchema = z.object({
|
|
222
|
+
allowed: z.array(z.string()).optional(),
|
|
223
|
+
caseSensitive: z.boolean().default(false),
|
|
224
|
+
invalidScopeAction: z.enum(["remove", "keep", "fallback"]).default("remove"),
|
|
225
|
+
fallbackScope: z.string().optional()
|
|
226
|
+
});
|
|
227
|
+
var ScopeConfigSchema = z.object({
|
|
228
|
+
mode: z.enum(["restricted", "packages", "none", "unrestricted"]).default("unrestricted"),
|
|
229
|
+
rules: ScopeRulesSchema.optional()
|
|
230
|
+
});
|
|
231
|
+
var LLMPromptOverridesSchema = z.object({
|
|
232
|
+
enhance: z.string().optional(),
|
|
233
|
+
categorize: z.string().optional(),
|
|
234
|
+
enhanceAndCategorize: z.string().optional(),
|
|
235
|
+
summarize: z.string().optional(),
|
|
236
|
+
releaseNotes: z.string().optional()
|
|
237
|
+
});
|
|
238
|
+
var LLMPromptsConfigSchema = z.object({
|
|
239
|
+
instructions: LLMPromptOverridesSchema.optional(),
|
|
240
|
+
templates: LLMPromptOverridesSchema.optional()
|
|
241
|
+
});
|
|
242
|
+
var LLMConfigSchema = z.object({
|
|
243
|
+
provider: z.string(),
|
|
244
|
+
model: z.string(),
|
|
245
|
+
baseURL: z.string().optional(),
|
|
246
|
+
apiKey: z.string().optional(),
|
|
247
|
+
options: LLMOptionsSchema.optional(),
|
|
248
|
+
concurrency: z.number().int().positive().optional(),
|
|
249
|
+
retry: LLMRetryConfigSchema.optional(),
|
|
250
|
+
tasks: LLMTasksConfigSchema.optional(),
|
|
251
|
+
categories: z.array(LLMCategorySchema).optional(),
|
|
252
|
+
style: z.string().optional(),
|
|
253
|
+
scopes: ScopeConfigSchema.optional(),
|
|
254
|
+
prompts: LLMPromptsConfigSchema.optional()
|
|
255
|
+
});
|
|
256
|
+
var NotesInputConfigSchema = z.object({
|
|
257
|
+
source: z.string().optional(),
|
|
258
|
+
file: z.string().optional()
|
|
259
|
+
});
|
|
260
|
+
var NotesConfigSchema = z.object({
|
|
261
|
+
input: NotesInputConfigSchema.optional(),
|
|
262
|
+
output: z.array(OutputConfigSchema).default([{ format: "markdown", file: "CHANGELOG.md" }]),
|
|
263
|
+
monorepo: MonorepoConfigSchema.optional(),
|
|
264
|
+
templates: TemplateConfigSchema.optional(),
|
|
265
|
+
llm: LLMConfigSchema.optional(),
|
|
266
|
+
updateStrategy: z.enum(["prepend", "regenerate"]).default("prepend")
|
|
267
|
+
});
|
|
268
|
+
var CILabelsConfigSchema = z.object({
|
|
269
|
+
stable: z.string().default("release:stable"),
|
|
270
|
+
prerelease: z.string().default("release:prerelease"),
|
|
271
|
+
skip: z.string().default("release:skip"),
|
|
272
|
+
major: z.string().default("release:major"),
|
|
273
|
+
minor: z.string().default("release:minor"),
|
|
274
|
+
patch: z.string().default("release:patch")
|
|
275
|
+
});
|
|
276
|
+
var CIConfigSchema = z.object({
|
|
277
|
+
releaseStrategy: z.enum(["manual", "direct", "standing-pr", "scheduled"]).default("direct"),
|
|
278
|
+
releaseTrigger: z.enum(["commit", "label"]).default("label"),
|
|
279
|
+
prPreview: z.boolean().default(true),
|
|
280
|
+
autoRelease: z.boolean().default(false),
|
|
281
|
+
/**
|
|
282
|
+
* Commit message prefixes that should not trigger a release.
|
|
283
|
+
* Defaults to `['chore: release ']` to match the release commit template
|
|
284
|
+
* (`chore: release ${packageName} v${version}`) and provide a
|
|
285
|
+
* secondary loop-prevention guard alongside `[skip ci]`.
|
|
286
|
+
*/
|
|
287
|
+
skipPatterns: z.array(z.string()).default(["chore: release "]),
|
|
288
|
+
minChanges: z.number().int().positive().default(1),
|
|
289
|
+
labels: CILabelsConfigSchema.default({
|
|
290
|
+
stable: "release:stable",
|
|
291
|
+
prerelease: "release:prerelease",
|
|
292
|
+
skip: "release:skip",
|
|
293
|
+
major: "release:major",
|
|
294
|
+
minor: "release:minor",
|
|
295
|
+
patch: "release:patch"
|
|
296
|
+
})
|
|
297
|
+
});
|
|
298
|
+
var ReleaseCIConfigSchema = z.object({
|
|
299
|
+
skipPatterns: z.array(z.string().min(1)).optional(),
|
|
300
|
+
minChanges: z.number().int().positive().optional(),
|
|
301
|
+
/** Set to `false` to disable GitHub release creation in CI. */
|
|
302
|
+
githubRelease: z.literal(false).optional(),
|
|
303
|
+
/** Set to `false` to disable changelog generation in CI. */
|
|
304
|
+
notes: z.literal(false).optional()
|
|
305
|
+
});
|
|
306
|
+
var ReleaseConfigSchema = z.object({
|
|
307
|
+
/**
|
|
308
|
+
* Optional steps to enable. The version step always runs; only 'notes' and
|
|
309
|
+
* 'publish' can be opted out. Omitting a step is equivalent to --skip-<step>.
|
|
310
|
+
*/
|
|
311
|
+
steps: z.array(z.enum(["notes", "publish"])).min(1).optional(),
|
|
312
|
+
ci: ReleaseCIConfigSchema.optional()
|
|
313
|
+
});
|
|
314
|
+
var ReleaseKitConfigSchema = z.object({
|
|
315
|
+
git: GitConfigSchema.optional(),
|
|
316
|
+
monorepo: MonorepoConfigSchema.optional(),
|
|
317
|
+
version: VersionConfigSchema.optional(),
|
|
318
|
+
publish: PublishConfigSchema.optional(),
|
|
319
|
+
notes: NotesConfigSchema.optional(),
|
|
320
|
+
ci: CIConfigSchema.optional(),
|
|
321
|
+
release: ReleaseConfigSchema.optional()
|
|
322
|
+
});
|
|
323
|
+
var MAX_INPUT_LENGTH = 1e4;
|
|
324
|
+
function substituteVariables(value) {
|
|
325
|
+
if (value.length > MAX_INPUT_LENGTH) {
|
|
326
|
+
throw new Error(`Input too long: ${value.length} characters (max ${MAX_INPUT_LENGTH})`);
|
|
327
|
+
}
|
|
328
|
+
const envPattern = /\{env:([^}]{1,1000})\}/g;
|
|
329
|
+
const filePattern = /\{file:([^}]{1,1000})\}/g;
|
|
330
|
+
let result = value;
|
|
331
|
+
result = result.replace(envPattern, (_, varName) => {
|
|
332
|
+
return process.env[varName] ?? "";
|
|
333
|
+
});
|
|
334
|
+
result = result.replace(filePattern, (_, filePath) => {
|
|
335
|
+
const expandedPath = filePath.startsWith("~") ? path2.join(os.homedir(), filePath.slice(1)) : filePath;
|
|
336
|
+
try {
|
|
337
|
+
return fs2.readFileSync(expandedPath, "utf-8").trim();
|
|
338
|
+
} catch {
|
|
339
|
+
return "";
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
var SOLE_REFERENCE_PATTERN = /^\{(?:env|file):[^}]+\}$/;
|
|
345
|
+
function substituteInObject(obj) {
|
|
346
|
+
if (typeof obj === "string") {
|
|
347
|
+
const result = substituteVariables(obj);
|
|
348
|
+
if (result === "" && SOLE_REFERENCE_PATTERN.test(obj)) {
|
|
349
|
+
return void 0;
|
|
350
|
+
}
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
if (Array.isArray(obj)) {
|
|
354
|
+
return obj.map((item) => substituteInObject(item));
|
|
355
|
+
}
|
|
356
|
+
if (obj && typeof obj === "object") {
|
|
357
|
+
const result = {};
|
|
358
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
359
|
+
result[key] = substituteInObject(value);
|
|
360
|
+
}
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
363
|
+
return obj;
|
|
364
|
+
}
|
|
365
|
+
var AUTH_DIR = path2.join(os.homedir(), ".config", "releasekit");
|
|
366
|
+
var AUTH_FILE = path2.join(AUTH_DIR, "auth.json");
|
|
367
|
+
function loadAuth() {
|
|
368
|
+
if (fs2.existsSync(AUTH_FILE)) {
|
|
369
|
+
try {
|
|
370
|
+
const content = fs2.readFileSync(AUTH_FILE, "utf-8");
|
|
371
|
+
return JSON.parse(content);
|
|
372
|
+
} catch {
|
|
373
|
+
return {};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return {};
|
|
377
|
+
}
|
|
378
|
+
function saveAuth(provider, apiKey) {
|
|
379
|
+
if (!fs2.existsSync(AUTH_DIR)) {
|
|
380
|
+
fs2.mkdirSync(AUTH_DIR, { recursive: true });
|
|
381
|
+
}
|
|
382
|
+
const existing = loadAuth();
|
|
383
|
+
existing[provider] = apiKey;
|
|
384
|
+
fs2.writeFileSync(AUTH_FILE, JSON.stringify(existing, null, 2), { encoding: "utf-8", mode: 384 });
|
|
385
|
+
}
|
|
386
|
+
var CONFIG_FILE = "releasekit.config.json";
|
|
387
|
+
function loadConfigFile(configPath) {
|
|
388
|
+
if (!fs3.existsSync(configPath)) {
|
|
389
|
+
return {};
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
const content = fs3.readFileSync(configPath, "utf-8");
|
|
393
|
+
const parsed = parseJsonc(content);
|
|
394
|
+
const substituted = substituteInObject(parsed);
|
|
395
|
+
return ReleaseKitConfigSchema.parse(substituted);
|
|
396
|
+
} catch (error) {
|
|
397
|
+
if (error instanceof z2.ZodError) {
|
|
398
|
+
const issues = error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
399
|
+
throw new ConfigError(`Config validation errors:
|
|
400
|
+
${issues}`);
|
|
401
|
+
}
|
|
402
|
+
if (error instanceof SyntaxError) {
|
|
403
|
+
throw new ConfigError(`Invalid JSON in config file: ${error.message}`);
|
|
404
|
+
}
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function loadConfig(options) {
|
|
409
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
410
|
+
const configPath = options?.configPath ?? path3.join(cwd, CONFIG_FILE);
|
|
411
|
+
return loadConfigFile(configPath);
|
|
412
|
+
}
|
|
413
|
+
function loadNotesConfig(options) {
|
|
414
|
+
const config = loadConfig(options);
|
|
415
|
+
return config.notes;
|
|
416
|
+
}
|
|
6
417
|
|
|
7
418
|
// src/core/config.ts
|
|
8
|
-
|
|
9
|
-
loadAuth,
|
|
10
|
-
loadNotesConfig as loadSharedNotesConfig,
|
|
11
|
-
saveAuth
|
|
12
|
-
} from "@releasekit/config";
|
|
13
|
-
function loadConfig(projectDir = process.cwd(), configFile) {
|
|
419
|
+
function loadConfig2(projectDir = process.cwd(), configFile) {
|
|
14
420
|
const options = { cwd: projectDir, configPath: configFile };
|
|
15
|
-
return
|
|
421
|
+
return loadNotesConfig(options) ?? getDefaultConfig();
|
|
16
422
|
}
|
|
17
423
|
function getDefaultConfig() {
|
|
18
424
|
return {
|
|
@@ -22,8 +428,6 @@ function getDefaultConfig() {
|
|
|
22
428
|
}
|
|
23
429
|
|
|
24
430
|
// src/errors/index.ts
|
|
25
|
-
import { EXIT_CODES, ReleaseKitError } from "@releasekit/core";
|
|
26
|
-
import { EXIT_CODES as EXIT_CODES2 } from "@releasekit/core";
|
|
27
431
|
var NotesError = class extends ReleaseKitError {
|
|
28
432
|
};
|
|
29
433
|
var InputParseError = class extends NotesError {
|
|
@@ -59,7 +463,7 @@ var GitHubError = class extends NotesError {
|
|
|
59
463
|
"Verify repository exists and is accessible"
|
|
60
464
|
];
|
|
61
465
|
};
|
|
62
|
-
var
|
|
466
|
+
var ConfigError2 = class extends NotesError {
|
|
63
467
|
code = "CONFIG_ERROR";
|
|
64
468
|
suggestions = [
|
|
65
469
|
"Check config file syntax",
|
|
@@ -84,8 +488,8 @@ function getExitCode(error) {
|
|
|
84
488
|
}
|
|
85
489
|
}
|
|
86
490
|
|
|
87
|
-
// src/input/
|
|
88
|
-
import * as
|
|
491
|
+
// src/input/version-output.ts
|
|
492
|
+
import * as fs4 from "fs";
|
|
89
493
|
function normalizeEntryType(type) {
|
|
90
494
|
const typeMap = {
|
|
91
495
|
added: "added",
|
|
@@ -103,7 +507,7 @@ function normalizeEntryType(type) {
|
|
|
103
507
|
};
|
|
104
508
|
return typeMap[type.toLowerCase()] ?? "changed";
|
|
105
509
|
}
|
|
106
|
-
function
|
|
510
|
+
function parseVersionOutput(json) {
|
|
107
511
|
let data;
|
|
108
512
|
try {
|
|
109
513
|
data = JSON.parse(json);
|
|
@@ -131,30 +535,29 @@ function parsePackageVersioner(json) {
|
|
|
131
535
|
}));
|
|
132
536
|
const repoUrl = packages[0]?.repoUrl ?? null;
|
|
133
537
|
return {
|
|
134
|
-
source: "
|
|
538
|
+
source: "version",
|
|
135
539
|
packages,
|
|
136
540
|
metadata: {
|
|
137
541
|
repoUrl: repoUrl ?? void 0
|
|
138
542
|
}
|
|
139
543
|
};
|
|
140
544
|
}
|
|
141
|
-
function
|
|
142
|
-
const content =
|
|
143
|
-
return
|
|
545
|
+
function parseVersionOutputFile(filePath) {
|
|
546
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
547
|
+
return parseVersionOutput(content);
|
|
144
548
|
}
|
|
145
|
-
async function
|
|
549
|
+
async function parseVersionOutputStdin() {
|
|
146
550
|
const chunks = [];
|
|
147
551
|
for await (const chunk of process.stdin) {
|
|
148
552
|
chunks.push(chunk);
|
|
149
553
|
}
|
|
150
554
|
const content = chunks.join("");
|
|
151
|
-
return
|
|
555
|
+
return parseVersionOutput(content);
|
|
152
556
|
}
|
|
153
557
|
|
|
154
558
|
// src/output/json.ts
|
|
155
|
-
import * as
|
|
156
|
-
import * as
|
|
157
|
-
import { debug, info, success } from "@releasekit/core";
|
|
559
|
+
import * as fs5 from "fs";
|
|
560
|
+
import * as path4 from "path";
|
|
158
561
|
function renderJson(contexts) {
|
|
159
562
|
return JSON.stringify(
|
|
160
563
|
{
|
|
@@ -180,23 +583,22 @@ function writeJson(outputPath, contexts, dryRun) {
|
|
|
180
583
|
debug("--- End Preview ---");
|
|
181
584
|
return;
|
|
182
585
|
}
|
|
183
|
-
const dir =
|
|
184
|
-
if (!
|
|
185
|
-
|
|
586
|
+
const dir = path4.dirname(outputPath);
|
|
587
|
+
if (!fs5.existsSync(dir)) {
|
|
588
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
186
589
|
}
|
|
187
|
-
|
|
590
|
+
fs5.writeFileSync(outputPath, content, "utf-8");
|
|
188
591
|
success(`JSON output written to ${outputPath}`);
|
|
189
592
|
}
|
|
190
593
|
|
|
191
594
|
// src/core/pipeline.ts
|
|
192
|
-
import * as
|
|
193
|
-
import * as
|
|
194
|
-
import { debug as debug2, info as info3, success as success3, warn as warn3 } from "@releasekit/core";
|
|
595
|
+
import * as fs10 from "fs";
|
|
596
|
+
import * as path8 from "path";
|
|
195
597
|
|
|
196
598
|
// src/llm/defaults.ts
|
|
197
599
|
var LLM_DEFAULTS = {
|
|
198
600
|
timeout: 6e4,
|
|
199
|
-
maxTokens:
|
|
601
|
+
maxTokens: 4e3,
|
|
200
602
|
temperature: 0.7,
|
|
201
603
|
concurrency: 5,
|
|
202
604
|
retry: {
|
|
@@ -386,8 +788,15 @@ var OpenAICompatibleProvider = class extends BaseLLMProvider {
|
|
|
386
788
|
}
|
|
387
789
|
};
|
|
388
790
|
|
|
389
|
-
// src/
|
|
390
|
-
|
|
791
|
+
// src/utils/json.ts
|
|
792
|
+
function extractJsonFromResponse(response) {
|
|
793
|
+
const stripped = response.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "").trim();
|
|
794
|
+
const objectMatch = stripped.match(/\{[\s\S]*\}/);
|
|
795
|
+
if (objectMatch) return objectMatch[0];
|
|
796
|
+
const arrayMatch = stripped.match(/\[[\s\S]*\]/);
|
|
797
|
+
if (arrayMatch) return arrayMatch[0];
|
|
798
|
+
return stripped;
|
|
799
|
+
}
|
|
391
800
|
|
|
392
801
|
// src/llm/prompts.ts
|
|
393
802
|
function resolvePrompt(taskName, defaultPrompt, promptsConfig) {
|
|
@@ -519,8 +928,7 @@ async function categorizeEntries(provider, entries, context) {
|
|
|
519
928
|
const prompt = promptTemplate.replace("{{entries}}", entriesText);
|
|
520
929
|
try {
|
|
521
930
|
const response = await provider.complete(prompt);
|
|
522
|
-
const
|
|
523
|
-
const parsed = JSON.parse(cleaned);
|
|
931
|
+
const parsed = JSON.parse(extractJsonFromResponse(response));
|
|
524
932
|
const result = [];
|
|
525
933
|
if (hasCustomCategories && parsed.categories) {
|
|
526
934
|
const categoryMap = parsed.categories;
|
|
@@ -601,9 +1009,6 @@ async function enhanceEntries(provider, entries, context, concurrency = LLM_DEFA
|
|
|
601
1009
|
return results;
|
|
602
1010
|
}
|
|
603
1011
|
|
|
604
|
-
// src/llm/tasks/enhance-and-categorize.ts
|
|
605
|
-
import { warn as warn2 } from "@releasekit/core";
|
|
606
|
-
|
|
607
1012
|
// src/utils/retry.ts
|
|
608
1013
|
function sleep(ms) {
|
|
609
1014
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
@@ -681,8 +1086,7 @@ async function enhanceAndCategorize(provider, entries, context) {
|
|
|
681
1086
|
const defaultPrompt = buildPrompt(entries, context.categories, context.style);
|
|
682
1087
|
const prompt = resolvePrompt("enhanceAndCategorize", defaultPrompt, context.prompts);
|
|
683
1088
|
const response = await provider.complete(prompt);
|
|
684
|
-
const
|
|
685
|
-
const parsed = JSON.parse(cleaned);
|
|
1089
|
+
const parsed = JSON.parse(extractJsonFromResponse(response));
|
|
686
1090
|
if (!Array.isArray(parsed.entries)) {
|
|
687
1091
|
throw new Error('Response missing "entries" array');
|
|
688
1092
|
}
|
|
@@ -714,7 +1118,7 @@ async function enhanceAndCategorize(provider, entries, context) {
|
|
|
714
1118
|
return { enhancedEntries: validatedEntries, categories };
|
|
715
1119
|
}, retryOpts);
|
|
716
1120
|
} catch (error) {
|
|
717
|
-
|
|
1121
|
+
warn(
|
|
718
1122
|
`Combined enhance+categorize failed after ${retryOpts.maxAttempts} attempts: ${error instanceof Error ? error.message : String(error)}`
|
|
719
1123
|
);
|
|
720
1124
|
return {
|
|
@@ -826,7 +1230,6 @@ function createProvider(config) {
|
|
|
826
1230
|
|
|
827
1231
|
// src/output/github-release.ts
|
|
828
1232
|
import { Octokit } from "@octokit/rest";
|
|
829
|
-
import { info as info2, success as success2 } from "@releasekit/core";
|
|
830
1233
|
var GitHubClient = class {
|
|
831
1234
|
octokit;
|
|
832
1235
|
owner;
|
|
@@ -848,7 +1251,7 @@ var GitHubClient = class {
|
|
|
848
1251
|
} else {
|
|
849
1252
|
body = renderMarkdown([context]);
|
|
850
1253
|
}
|
|
851
|
-
|
|
1254
|
+
info(`Creating GitHub release for ${tagName}`);
|
|
852
1255
|
try {
|
|
853
1256
|
const response = await this.octokit.repos.createRelease({
|
|
854
1257
|
owner: this.owner,
|
|
@@ -860,7 +1263,7 @@ var GitHubClient = class {
|
|
|
860
1263
|
prerelease: options.prerelease ?? false,
|
|
861
1264
|
generate_release_notes: options.generateNotes ?? false
|
|
862
1265
|
});
|
|
863
|
-
|
|
1266
|
+
success(`Release created: ${response.data.html_url}`);
|
|
864
1267
|
return {
|
|
865
1268
|
id: response.data.id,
|
|
866
1269
|
htmlUrl: response.data.html_url,
|
|
@@ -878,7 +1281,7 @@ var GitHubClient = class {
|
|
|
878
1281
|
} else {
|
|
879
1282
|
body = renderMarkdown([context]);
|
|
880
1283
|
}
|
|
881
|
-
|
|
1284
|
+
info(`Updating GitHub release ${releaseId}`);
|
|
882
1285
|
try {
|
|
883
1286
|
const response = await this.octokit.repos.updateRelease({
|
|
884
1287
|
owner: this.owner,
|
|
@@ -890,7 +1293,7 @@ var GitHubClient = class {
|
|
|
890
1293
|
draft: options.draft ?? false,
|
|
891
1294
|
prerelease: options.prerelease ?? false
|
|
892
1295
|
});
|
|
893
|
-
|
|
1296
|
+
success(`Release updated: ${response.data.html_url}`);
|
|
894
1297
|
return {
|
|
895
1298
|
id: response.data.id,
|
|
896
1299
|
htmlUrl: response.data.html_url,
|
|
@@ -940,7 +1343,7 @@ async function createGitHubRelease(context, options) {
|
|
|
940
1343
|
}
|
|
941
1344
|
|
|
942
1345
|
// src/templates/ejs.ts
|
|
943
|
-
import * as
|
|
1346
|
+
import * as fs6 from "fs";
|
|
944
1347
|
import ejs from "ejs";
|
|
945
1348
|
function renderEjs(template, context) {
|
|
946
1349
|
try {
|
|
@@ -950,16 +1353,16 @@ function renderEjs(template, context) {
|
|
|
950
1353
|
}
|
|
951
1354
|
}
|
|
952
1355
|
function renderEjsFile(filePath, context) {
|
|
953
|
-
if (!
|
|
1356
|
+
if (!fs6.existsSync(filePath)) {
|
|
954
1357
|
throw new TemplateError(`Template file not found: ${filePath}`);
|
|
955
1358
|
}
|
|
956
|
-
const template =
|
|
1359
|
+
const template = fs6.readFileSync(filePath, "utf-8");
|
|
957
1360
|
return renderEjs(template, context);
|
|
958
1361
|
}
|
|
959
1362
|
|
|
960
1363
|
// src/templates/handlebars.ts
|
|
961
|
-
import * as
|
|
962
|
-
import * as
|
|
1364
|
+
import * as fs7 from "fs";
|
|
1365
|
+
import * as path5 from "path";
|
|
963
1366
|
import Handlebars from "handlebars";
|
|
964
1367
|
function registerHandlebarsHelpers() {
|
|
965
1368
|
Handlebars.registerHelper("capitalize", (str) => {
|
|
@@ -985,28 +1388,28 @@ function renderHandlebars(template, context) {
|
|
|
985
1388
|
}
|
|
986
1389
|
}
|
|
987
1390
|
function renderHandlebarsFile(filePath, context) {
|
|
988
|
-
if (!
|
|
1391
|
+
if (!fs7.existsSync(filePath)) {
|
|
989
1392
|
throw new TemplateError(`Template file not found: ${filePath}`);
|
|
990
1393
|
}
|
|
991
|
-
const template =
|
|
1394
|
+
const template = fs7.readFileSync(filePath, "utf-8");
|
|
992
1395
|
return renderHandlebars(template, context);
|
|
993
1396
|
}
|
|
994
1397
|
function renderHandlebarsComposable(templateDir, context) {
|
|
995
1398
|
registerHandlebarsHelpers();
|
|
996
|
-
const versionPath =
|
|
997
|
-
const entryPath =
|
|
998
|
-
const documentPath =
|
|
999
|
-
if (!
|
|
1399
|
+
const versionPath = path5.join(templateDir, "version.hbs");
|
|
1400
|
+
const entryPath = path5.join(templateDir, "entry.hbs");
|
|
1401
|
+
const documentPath = path5.join(templateDir, "document.hbs");
|
|
1402
|
+
if (!fs7.existsSync(documentPath)) {
|
|
1000
1403
|
throw new TemplateError(`Document template not found: ${documentPath}`);
|
|
1001
1404
|
}
|
|
1002
|
-
if (
|
|
1003
|
-
Handlebars.registerPartial("version",
|
|
1405
|
+
if (fs7.existsSync(versionPath)) {
|
|
1406
|
+
Handlebars.registerPartial("version", fs7.readFileSync(versionPath, "utf-8"));
|
|
1004
1407
|
}
|
|
1005
|
-
if (
|
|
1006
|
-
Handlebars.registerPartial("entry",
|
|
1408
|
+
if (fs7.existsSync(entryPath)) {
|
|
1409
|
+
Handlebars.registerPartial("entry", fs7.readFileSync(entryPath, "utf-8"));
|
|
1007
1410
|
}
|
|
1008
1411
|
try {
|
|
1009
|
-
const compiled = Handlebars.compile(
|
|
1412
|
+
const compiled = Handlebars.compile(fs7.readFileSync(documentPath, "utf-8"));
|
|
1010
1413
|
return compiled(context);
|
|
1011
1414
|
} catch (error) {
|
|
1012
1415
|
throw new TemplateError(`Handlebars render error: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -1014,8 +1417,8 @@ function renderHandlebarsComposable(templateDir, context) {
|
|
|
1014
1417
|
}
|
|
1015
1418
|
|
|
1016
1419
|
// src/templates/liquid.ts
|
|
1017
|
-
import * as
|
|
1018
|
-
import * as
|
|
1420
|
+
import * as fs8 from "fs";
|
|
1421
|
+
import * as path6 from "path";
|
|
1019
1422
|
import { Liquid } from "liquidjs";
|
|
1020
1423
|
function createLiquidEngine(root) {
|
|
1021
1424
|
return new Liquid({
|
|
@@ -1033,15 +1436,15 @@ function renderLiquid(template, context) {
|
|
|
1033
1436
|
}
|
|
1034
1437
|
}
|
|
1035
1438
|
function renderLiquidFile(filePath, context) {
|
|
1036
|
-
if (!
|
|
1439
|
+
if (!fs8.existsSync(filePath)) {
|
|
1037
1440
|
throw new TemplateError(`Template file not found: ${filePath}`);
|
|
1038
1441
|
}
|
|
1039
|
-
const template =
|
|
1442
|
+
const template = fs8.readFileSync(filePath, "utf-8");
|
|
1040
1443
|
return renderLiquid(template, context);
|
|
1041
1444
|
}
|
|
1042
1445
|
function renderLiquidComposable(templateDir, context) {
|
|
1043
|
-
const documentPath =
|
|
1044
|
-
if (!
|
|
1446
|
+
const documentPath = path6.join(templateDir, "document.liquid");
|
|
1447
|
+
if (!fs8.existsSync(documentPath)) {
|
|
1045
1448
|
throw new TemplateError(`Document template not found: ${documentPath}`);
|
|
1046
1449
|
}
|
|
1047
1450
|
const engine = createLiquidEngine(templateDir);
|
|
@@ -1053,10 +1456,10 @@ function renderLiquidComposable(templateDir, context) {
|
|
|
1053
1456
|
}
|
|
1054
1457
|
|
|
1055
1458
|
// src/templates/loader.ts
|
|
1056
|
-
import * as
|
|
1057
|
-
import * as
|
|
1459
|
+
import * as fs9 from "fs";
|
|
1460
|
+
import * as path7 from "path";
|
|
1058
1461
|
function getEngineFromFile(filePath) {
|
|
1059
|
-
const ext =
|
|
1462
|
+
const ext = path7.extname(filePath).toLowerCase();
|
|
1060
1463
|
switch (ext) {
|
|
1061
1464
|
case ".liquid":
|
|
1062
1465
|
return "liquid";
|
|
@@ -1090,10 +1493,10 @@ function getRenderFileFn(engine) {
|
|
|
1090
1493
|
}
|
|
1091
1494
|
}
|
|
1092
1495
|
function detectTemplateMode(templatePath) {
|
|
1093
|
-
if (!
|
|
1496
|
+
if (!fs9.existsSync(templatePath)) {
|
|
1094
1497
|
throw new TemplateError(`Template path not found: ${templatePath}`);
|
|
1095
1498
|
}
|
|
1096
|
-
const stat =
|
|
1499
|
+
const stat = fs9.statSync(templatePath);
|
|
1097
1500
|
if (stat.isFile()) {
|
|
1098
1501
|
return "single";
|
|
1099
1502
|
}
|
|
@@ -1111,7 +1514,7 @@ function renderSingleFile(templatePath, context, engine) {
|
|
|
1111
1514
|
};
|
|
1112
1515
|
}
|
|
1113
1516
|
function renderComposable(templateDir, context, engine) {
|
|
1114
|
-
const files =
|
|
1517
|
+
const files = fs9.readdirSync(templateDir);
|
|
1115
1518
|
const engineMap = {
|
|
1116
1519
|
liquid: { document: "document.liquid", version: "version.liquid", entry: "entry.liquid" },
|
|
1117
1520
|
handlebars: { document: "document.hbs", version: "version.hbs", entry: "entry.hbs" },
|
|
@@ -1134,15 +1537,15 @@ function renderComposable(templateDir, context, engine) {
|
|
|
1134
1537
|
return { content: renderHandlebarsComposable(templateDir, context), engine: resolvedEngine };
|
|
1135
1538
|
}
|
|
1136
1539
|
const expectedFiles = engineMap[resolvedEngine];
|
|
1137
|
-
const documentPath =
|
|
1138
|
-
if (!
|
|
1540
|
+
const documentPath = path7.join(templateDir, expectedFiles.document);
|
|
1541
|
+
if (!fs9.existsSync(documentPath)) {
|
|
1139
1542
|
throw new TemplateError(`Document template not found: ${expectedFiles.document}`);
|
|
1140
1543
|
}
|
|
1141
|
-
const versionPath =
|
|
1142
|
-
const entryPath =
|
|
1544
|
+
const versionPath = path7.join(templateDir, expectedFiles.version);
|
|
1545
|
+
const entryPath = path7.join(templateDir, expectedFiles.entry);
|
|
1143
1546
|
const render = getRenderFn(resolvedEngine);
|
|
1144
|
-
const entryTemplate =
|
|
1145
|
-
const versionTemplate =
|
|
1547
|
+
const entryTemplate = fs9.existsSync(entryPath) ? fs9.readFileSync(entryPath, "utf-8") : null;
|
|
1548
|
+
const versionTemplate = fs9.existsSync(versionPath) ? fs9.readFileSync(versionPath, "utf-8") : null;
|
|
1146
1549
|
if (entryTemplate && versionTemplate) {
|
|
1147
1550
|
const versionsWithEntries = context.versions.map((versionCtx) => {
|
|
1148
1551
|
const entries = versionCtx.entries.map((entry) => {
|
|
@@ -1153,7 +1556,7 @@ function renderComposable(templateDir, context, engine) {
|
|
|
1153
1556
|
});
|
|
1154
1557
|
const docContext = { ...context, renderedVersions: versionsWithEntries };
|
|
1155
1558
|
return {
|
|
1156
|
-
content: render(
|
|
1559
|
+
content: render(fs9.readFileSync(documentPath, "utf-8"), docContext),
|
|
1157
1560
|
engine: resolvedEngine
|
|
1158
1561
|
};
|
|
1159
1562
|
}
|
|
@@ -1213,6 +1616,16 @@ function generateCompareUrl(repoUrl, from, to, packageName) {
|
|
|
1213
1616
|
}
|
|
1214
1617
|
return `${repoUrl}/compare/${fromVersion}...${toVersion}`;
|
|
1215
1618
|
}
|
|
1619
|
+
function buildOrderedCategories(rawCategories, configCategories) {
|
|
1620
|
+
const order = configCategories?.map((c) => c.name) ?? [];
|
|
1621
|
+
const mapped = rawCategories.map((c) => ({ name: c.category, entries: c.entries }));
|
|
1622
|
+
if (order.length === 0) return mapped;
|
|
1623
|
+
return mapped.sort((a, b) => {
|
|
1624
|
+
const ai = order.indexOf(a.name);
|
|
1625
|
+
const bi = order.indexOf(b.name);
|
|
1626
|
+
return (ai === -1 ? order.length : ai) - (bi === -1 ? order.length : bi);
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1216
1629
|
function createTemplateContext(pkg) {
|
|
1217
1630
|
const compareUrl = pkg.repoUrl && pkg.previousVersion ? generateCompareUrl(pkg.repoUrl, pkg.previousVersion, pkg.version, pkg.packageName) : void 0;
|
|
1218
1631
|
return {
|
|
@@ -1260,60 +1673,57 @@ async function processWithLLM(context, config) {
|
|
|
1260
1673
|
entries: context.entries
|
|
1261
1674
|
};
|
|
1262
1675
|
try {
|
|
1263
|
-
|
|
1676
|
+
info(`Using LLM provider: ${config.llm.provider}${config.llm.model ? ` (${config.llm.model})` : ""}`);
|
|
1264
1677
|
if (config.llm.baseURL) {
|
|
1265
|
-
|
|
1678
|
+
info(`LLM base URL: ${config.llm.baseURL}`);
|
|
1266
1679
|
}
|
|
1267
1680
|
const rawProvider = createProvider(config.llm);
|
|
1268
1681
|
const retryOpts = config.llm.retry ?? LLM_DEFAULTS.retry;
|
|
1682
|
+
const configOptions = config.llm.options;
|
|
1269
1683
|
const provider = {
|
|
1270
1684
|
name: rawProvider.name,
|
|
1271
|
-
|
|
1685
|
+
// Merge user-configured options (timeout, maxTokens, temperature) as base defaults,
|
|
1686
|
+
// allowing any per-call overrides to take precedence.
|
|
1687
|
+
complete: (prompt, opts) => withRetry(() => rawProvider.complete(prompt, { ...configOptions, ...opts }), retryOpts)
|
|
1272
1688
|
};
|
|
1273
1689
|
const activeTasks = Object.entries(tasks).filter(([, enabled]) => enabled).map(([name]) => name);
|
|
1274
|
-
|
|
1690
|
+
info(`Running LLM tasks: ${activeTasks.join(", ")}`);
|
|
1275
1691
|
if (tasks.enhance && tasks.categorize) {
|
|
1276
|
-
|
|
1692
|
+
info("Enhancing and categorizing entries with LLM...");
|
|
1277
1693
|
const result = await enhanceAndCategorize(provider, context.entries, llmContext);
|
|
1278
1694
|
enhanced.entries = result.enhancedEntries;
|
|
1279
|
-
enhanced.categories =
|
|
1280
|
-
|
|
1281
|
-
enhanced.categories[cat.category] = cat.entries;
|
|
1282
|
-
}
|
|
1283
|
-
info3(`Enhanced ${enhanced.entries.length} entries into ${result.categories.length} categories`);
|
|
1695
|
+
enhanced.categories = buildOrderedCategories(result.categories, llmContext.categories);
|
|
1696
|
+
info(`Enhanced ${enhanced.entries.length} entries into ${result.categories.length} categories`);
|
|
1284
1697
|
} else {
|
|
1285
1698
|
if (tasks.enhance) {
|
|
1286
|
-
|
|
1699
|
+
info("Enhancing entries with LLM...");
|
|
1287
1700
|
enhanced.entries = await enhanceEntries(provider, context.entries, llmContext, config.llm.concurrency);
|
|
1288
|
-
|
|
1701
|
+
info(`Enhanced ${enhanced.entries.length} entries`);
|
|
1289
1702
|
}
|
|
1290
1703
|
if (tasks.categorize) {
|
|
1291
|
-
|
|
1704
|
+
info("Categorizing entries with LLM...");
|
|
1292
1705
|
const categorized = await categorizeEntries(provider, enhanced.entries, llmContext);
|
|
1293
|
-
enhanced.categories =
|
|
1294
|
-
|
|
1295
|
-
enhanced.categories[cat.category] = cat.entries;
|
|
1296
|
-
}
|
|
1297
|
-
info3(`Created ${categorized.length} categories`);
|
|
1706
|
+
enhanced.categories = buildOrderedCategories(categorized, llmContext.categories);
|
|
1707
|
+
info(`Created ${categorized.length} categories`);
|
|
1298
1708
|
}
|
|
1299
1709
|
}
|
|
1300
1710
|
if (tasks.summarize) {
|
|
1301
|
-
|
|
1711
|
+
info("Summarizing entries with LLM...");
|
|
1302
1712
|
enhanced.summary = await summarizeEntries(provider, enhanced.entries, llmContext);
|
|
1303
1713
|
if (enhanced.summary) {
|
|
1304
|
-
|
|
1305
|
-
|
|
1714
|
+
info("Summary generated successfully");
|
|
1715
|
+
debug(`Summary: ${enhanced.summary.substring(0, 100)}...`);
|
|
1306
1716
|
} else {
|
|
1307
|
-
|
|
1717
|
+
warn("Summary generation returned empty result");
|
|
1308
1718
|
}
|
|
1309
1719
|
}
|
|
1310
1720
|
if (tasks.releaseNotes) {
|
|
1311
|
-
|
|
1721
|
+
info("Generating release notes with LLM...");
|
|
1312
1722
|
enhanced.releaseNotes = await generateReleaseNotes(provider, enhanced.entries, llmContext);
|
|
1313
1723
|
if (enhanced.releaseNotes) {
|
|
1314
|
-
|
|
1724
|
+
info("Release notes generated successfully");
|
|
1315
1725
|
} else {
|
|
1316
|
-
|
|
1726
|
+
warn("Release notes generation returned empty result");
|
|
1317
1727
|
}
|
|
1318
1728
|
}
|
|
1319
1729
|
return {
|
|
@@ -1321,8 +1731,8 @@ async function processWithLLM(context, config) {
|
|
|
1321
1731
|
enhanced
|
|
1322
1732
|
};
|
|
1323
1733
|
} catch (error) {
|
|
1324
|
-
|
|
1325
|
-
|
|
1734
|
+
warn(`LLM processing failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1735
|
+
warn("Falling back to raw entries");
|
|
1326
1736
|
return context;
|
|
1327
1737
|
}
|
|
1328
1738
|
}
|
|
@@ -1330,17 +1740,17 @@ function getBuiltinTemplatePath(style) {
|
|
|
1330
1740
|
let packageRoot;
|
|
1331
1741
|
try {
|
|
1332
1742
|
const currentUrl = import.meta.url;
|
|
1333
|
-
packageRoot =
|
|
1334
|
-
packageRoot =
|
|
1743
|
+
packageRoot = path8.dirname(new URL(currentUrl).pathname);
|
|
1744
|
+
packageRoot = path8.join(packageRoot, "..", "..");
|
|
1335
1745
|
} catch {
|
|
1336
1746
|
packageRoot = __dirname;
|
|
1337
1747
|
}
|
|
1338
|
-
return
|
|
1748
|
+
return path8.join(packageRoot, "templates", style);
|
|
1339
1749
|
}
|
|
1340
1750
|
async function generateWithTemplate(contexts, config, outputPath, dryRun) {
|
|
1341
1751
|
let templatePath;
|
|
1342
1752
|
if (config.templates?.path) {
|
|
1343
|
-
templatePath =
|
|
1753
|
+
templatePath = path8.resolve(config.templates.path);
|
|
1344
1754
|
} else {
|
|
1345
1755
|
templatePath = getBuiltinTemplatePath("keep-a-changelog");
|
|
1346
1756
|
}
|
|
@@ -1350,79 +1760,83 @@ async function generateWithTemplate(contexts, config, outputPath, dryRun) {
|
|
|
1350
1760
|
);
|
|
1351
1761
|
const result = renderTemplate(templatePath, documentContext, config.templates?.engine);
|
|
1352
1762
|
if (dryRun) {
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
debug2(result.content);
|
|
1356
|
-
debug2("--- End Preview ---");
|
|
1763
|
+
info(`[DRY RUN] Changelog preview (would write to ${outputPath}):`);
|
|
1764
|
+
info(result.content);
|
|
1357
1765
|
return;
|
|
1358
1766
|
}
|
|
1359
1767
|
if (outputPath === "-") {
|
|
1360
1768
|
process.stdout.write(result.content);
|
|
1361
1769
|
return;
|
|
1362
1770
|
}
|
|
1363
|
-
const dir =
|
|
1364
|
-
if (!
|
|
1365
|
-
|
|
1771
|
+
const dir = path8.dirname(outputPath);
|
|
1772
|
+
if (!fs10.existsSync(dir)) {
|
|
1773
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
1366
1774
|
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1775
|
+
fs10.writeFileSync(outputPath, result.content, "utf-8");
|
|
1776
|
+
const label = /changelog/i.test(outputPath) ? "Changelog" : "Release notes";
|
|
1777
|
+
success(`${label} written to ${outputPath} (using ${result.engine} template)`);
|
|
1369
1778
|
}
|
|
1370
1779
|
async function runPipeline(input, config, dryRun) {
|
|
1371
|
-
|
|
1780
|
+
debug(`Processing ${input.packages.length} package(s)`);
|
|
1372
1781
|
let contexts = input.packages.map(createTemplateContext);
|
|
1373
1782
|
if (config.llm && !process.env.CHANGELOG_NO_LLM) {
|
|
1374
|
-
|
|
1783
|
+
info("Processing with LLM enhancement");
|
|
1375
1784
|
contexts = await Promise.all(contexts.map((ctx) => processWithLLM(ctx, config)));
|
|
1376
1785
|
}
|
|
1377
1786
|
const files = [];
|
|
1787
|
+
const fmtOpts = {
|
|
1788
|
+
includePackageName: contexts.length > 1 || contexts.some((c) => c.packageName.includes("/"))
|
|
1789
|
+
};
|
|
1378
1790
|
for (const output of config.output) {
|
|
1379
|
-
const
|
|
1380
|
-
|
|
1791
|
+
const file = output.file ?? (output.format === "json" ? "changelog.json" : "CHANGELOG.md");
|
|
1792
|
+
const isChangelog = /changelog/i.test(file);
|
|
1793
|
+
const outputKind = isChangelog ? "changelog" : "release notes";
|
|
1794
|
+
info(`Generating ${outputKind} \u2192 ${file}`);
|
|
1381
1795
|
switch (output.format) {
|
|
1382
1796
|
case "markdown": {
|
|
1383
|
-
const
|
|
1797
|
+
const file2 = output.file ?? "CHANGELOG.md";
|
|
1384
1798
|
try {
|
|
1385
1799
|
const effectiveTemplateConfig = output.templates ?? config.templates;
|
|
1386
1800
|
if (effectiveTemplateConfig?.path || output.options?.template) {
|
|
1387
1801
|
const configWithTemplate = { ...config, templates: effectiveTemplateConfig };
|
|
1388
|
-
await generateWithTemplate(contexts, configWithTemplate,
|
|
1802
|
+
await generateWithTemplate(contexts, configWithTemplate, file2, dryRun);
|
|
1389
1803
|
} else {
|
|
1390
|
-
writeMarkdown(
|
|
1804
|
+
writeMarkdown(file2, contexts, config, dryRun, fmtOpts);
|
|
1391
1805
|
}
|
|
1392
|
-
if (!dryRun) files.push(
|
|
1806
|
+
if (!dryRun) files.push(file2);
|
|
1393
1807
|
} catch (error) {
|
|
1394
|
-
|
|
1808
|
+
warn(`Failed to write ${file2}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1395
1809
|
}
|
|
1396
1810
|
break;
|
|
1397
1811
|
}
|
|
1398
1812
|
case "json": {
|
|
1399
|
-
const
|
|
1813
|
+
const file2 = output.file ?? "changelog.json";
|
|
1400
1814
|
try {
|
|
1401
|
-
writeJson(
|
|
1402
|
-
if (!dryRun) files.push(
|
|
1815
|
+
writeJson(file2, contexts, dryRun);
|
|
1816
|
+
if (!dryRun) files.push(file2);
|
|
1403
1817
|
} catch (error) {
|
|
1404
|
-
|
|
1818
|
+
warn(`Failed to write ${file2}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1405
1819
|
}
|
|
1406
1820
|
break;
|
|
1407
1821
|
}
|
|
1408
1822
|
case "github-release": {
|
|
1409
1823
|
if (dryRun) {
|
|
1410
|
-
|
|
1824
|
+
info("[DRY RUN] Would create GitHub release");
|
|
1411
1825
|
break;
|
|
1412
1826
|
}
|
|
1413
1827
|
const firstContext = contexts[0];
|
|
1414
1828
|
if (!firstContext) {
|
|
1415
|
-
|
|
1829
|
+
warn("No context available for GitHub release");
|
|
1416
1830
|
break;
|
|
1417
1831
|
}
|
|
1418
1832
|
const repoUrl = firstContext.repoUrl;
|
|
1419
1833
|
if (!repoUrl) {
|
|
1420
|
-
|
|
1834
|
+
warn("No repo URL available, cannot create GitHub release");
|
|
1421
1835
|
break;
|
|
1422
1836
|
}
|
|
1423
1837
|
const parsed = parseRepoUrl(repoUrl);
|
|
1424
1838
|
if (!parsed) {
|
|
1425
|
-
|
|
1839
|
+
warn(`Could not parse repo URL: ${repoUrl}`);
|
|
1426
1840
|
break;
|
|
1427
1841
|
}
|
|
1428
1842
|
await createGitHubRelease(firstContext, {
|
|
@@ -1436,7 +1850,7 @@ async function runPipeline(input, config, dryRun) {
|
|
|
1436
1850
|
}
|
|
1437
1851
|
}
|
|
1438
1852
|
if (config.monorepo?.mode) {
|
|
1439
|
-
const { detectMonorepo, writeMonorepoChangelogs } = await import("./aggregator-
|
|
1853
|
+
const { detectMonorepo, writeMonorepoChangelogs } = await import("./aggregator-XJ2EILO3.js");
|
|
1440
1854
|
const cwd = process.cwd();
|
|
1441
1855
|
const detected = detectMonorepo(cwd);
|
|
1442
1856
|
if (detected.isMonorepo) {
|
|
@@ -1460,26 +1874,25 @@ async function runPipeline(input, config, dryRun) {
|
|
|
1460
1874
|
return { packageNotes, files };
|
|
1461
1875
|
}
|
|
1462
1876
|
async function processInput(inputJson, config, dryRun) {
|
|
1463
|
-
const input =
|
|
1877
|
+
const input = parseVersionOutput(inputJson);
|
|
1464
1878
|
return runPipeline(input, config, dryRun);
|
|
1465
1879
|
}
|
|
1466
1880
|
|
|
1467
1881
|
export {
|
|
1468
1882
|
loadAuth,
|
|
1469
1883
|
saveAuth,
|
|
1470
|
-
loadConfig,
|
|
1884
|
+
loadConfig2 as loadConfig,
|
|
1471
1885
|
getDefaultConfig,
|
|
1472
1886
|
NotesError,
|
|
1473
1887
|
InputParseError,
|
|
1474
1888
|
TemplateError,
|
|
1475
1889
|
LLMError,
|
|
1476
1890
|
GitHubError,
|
|
1477
|
-
ConfigError,
|
|
1891
|
+
ConfigError2 as ConfigError,
|
|
1478
1892
|
getExitCode,
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
parsePackageVersionerStdin,
|
|
1893
|
+
parseVersionOutput,
|
|
1894
|
+
parseVersionOutputFile,
|
|
1895
|
+
parseVersionOutputStdin,
|
|
1483
1896
|
renderJson,
|
|
1484
1897
|
writeJson,
|
|
1485
1898
|
createTemplateContext,
|