@releasekit/notes 0.3.0-next.4 → 0.3.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 CHANGED
@@ -19,6 +19,8 @@ npm install -g @releasekit/notes
19
19
  pnpm add -g @releasekit/notes
20
20
  ```
21
21
 
22
+ > **Note:** This package is ESM only and requires Node.js 20+.
23
+
22
24
  ## Quick Start
23
25
 
24
26
  ```bash
package/dist/cli.js CHANGED
@@ -1,174 +1 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- EXIT_CODES,
4
- NotesError,
5
- getDefaultConfig,
6
- getExitCode,
7
- loadConfig,
8
- parsePackageVersioner,
9
- runPipeline,
10
- saveAuth
11
- } from "./chunk-QUBVC5LF.js";
12
- import "./chunk-QCF6V2IY.js";
13
-
14
- // src/cli.ts
15
- import * as fs from "fs";
16
- import * as readline from "readline";
17
- import { error, info, setLogLevel, setQuietMode, success } from "@releasekit/core";
18
- import { Command } from "commander";
19
- var program = new Command();
20
- program.name("releasekit-notes").description("Generate changelogs with LLM-powered enhancement and flexible templating").version("0.1.0");
21
- 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) => {
22
- setVerbosity(options.verbose);
23
- if (options.quiet) setQuietMode(true);
24
- try {
25
- const config = loadConfig(process.cwd(), options.config);
26
- if (options.output.length > 0) {
27
- config.output = options.output;
28
- }
29
- if (config.output.length === 0) {
30
- config.output = getDefaultConfig().output;
31
- }
32
- if (options.regenerate) {
33
- config.updateStrategy = "regenerate";
34
- }
35
- if (options.template) {
36
- config.templates = { ...config.templates, path: options.template };
37
- }
38
- if (options.engine) {
39
- config.templates = { ...config.templates, engine: options.engine };
40
- }
41
- if (options.llm === false) {
42
- info("LLM processing disabled via --no-llm flag");
43
- delete config.llm;
44
- } else if (options.llmProvider || options.llmModel || options.llmBaseUrl || options.llmTasks) {
45
- config.llm = config.llm ?? { provider: "openai-compatible", model: "" };
46
- if (options.llmProvider) config.llm.provider = options.llmProvider;
47
- if (options.llmModel) config.llm.model = options.llmModel;
48
- if (options.llmBaseUrl) config.llm.baseURL = options.llmBaseUrl;
49
- if (options.llmTasks) {
50
- const taskNames = options.llmTasks.split(",").map((t) => t.trim());
51
- config.llm.tasks = {
52
- enhance: taskNames.includes("enhance"),
53
- summarize: taskNames.includes("summarize"),
54
- categorize: taskNames.includes("categorize"),
55
- releaseNotes: taskNames.includes("release-notes") || taskNames.includes("releaseNotes")
56
- };
57
- }
58
- info(`LLM configured: ${config.llm.provider}${config.llm.model ? ` (${config.llm.model})` : ""}`);
59
- if (config.llm.baseURL) {
60
- info(`LLM base URL: ${config.llm.baseURL}`);
61
- }
62
- const taskList = Object.entries(config.llm.tasks || {}).filter(([, enabled]) => enabled).map(([name]) => name).join(", ");
63
- if (taskList) {
64
- info(`LLM tasks: ${taskList}`);
65
- }
66
- }
67
- let inputJson;
68
- if (options.input) {
69
- inputJson = fs.readFileSync(options.input, "utf-8");
70
- } else {
71
- inputJson = await readStdin();
72
- }
73
- const input = parsePackageVersioner(inputJson);
74
- if (options.target) {
75
- const before = input.packages.length;
76
- input.packages = input.packages.filter((p) => p.packageName === options.target);
77
- if (input.packages.length === 0) {
78
- info(`No changelog found for package "${options.target}" (had ${before} package(s))`);
79
- return;
80
- }
81
- info(`Filtered to package: ${options.target}`);
82
- }
83
- if (options.monorepo) {
84
- config.monorepo = { ...config.monorepo, mode: options.monorepo };
85
- }
86
- await runPipeline(input, config, options.dryRun ?? false);
87
- if (options.dryRun) {
88
- info("Dry run complete - no files were written");
89
- } else {
90
- success("Changelog generation complete");
91
- }
92
- } catch (err) {
93
- handleError(err);
94
- }
95
- });
96
- program.command("init").description("Create default configuration file").option("-f, --force", "Overwrite existing config").action((options) => {
97
- const configPath = "releasekit.config.json";
98
- if (fs.existsSync(configPath) && !options.force) {
99
- error(`Config file already exists at ${configPath}. Use --force to overwrite.`);
100
- process.exit(EXIT_CODES.GENERAL_ERROR);
101
- }
102
- const defaultConfig = {
103
- $schema: "https://releasekit.dev/schema.json",
104
- notes: {
105
- output: [{ format: "markdown", file: "CHANGELOG.md" }],
106
- updateStrategy: "prepend"
107
- }
108
- };
109
- fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
110
- success(`Created config file at ${configPath}`);
111
- });
112
- program.command("auth <provider>").description("Configure API key for an LLM provider").option("--key <key>", "API key (omit to be prompted)").action(async (provider, options) => {
113
- let apiKey;
114
- if (options.key) {
115
- apiKey = options.key;
116
- } else {
117
- apiKey = await promptSecret(`Enter API key for ${provider}: `);
118
- }
119
- if (!apiKey.trim()) {
120
- error("API key cannot be empty");
121
- process.exit(EXIT_CODES.GENERAL_ERROR);
122
- }
123
- saveAuth(provider, apiKey.trim());
124
- success(`API key saved for ${provider}`);
125
- });
126
- program.command("providers").description("List available LLM providers").action(() => {
127
- info("Available LLM providers:");
128
- console.log(" openai - OpenAI (GPT models)");
129
- console.log(" anthropic - Anthropic (Claude models)");
130
- console.log(" ollama - Ollama (local models)");
131
- console.log(" openai-compatible - Any OpenAI-compatible endpoint");
132
- });
133
- function collectOutputs(value, previous) {
134
- const parts = value.split(":");
135
- const format = parts[0] ?? "markdown";
136
- const file = parts[1];
137
- const spec = { format };
138
- if (file) {
139
- spec.file = file;
140
- }
141
- return [...previous, spec];
142
- }
143
- function increaseVerbosity(_, previous) {
144
- return previous + 1;
145
- }
146
- function setVerbosity(level) {
147
- const levels = ["info", "debug", "trace"];
148
- setLogLevel(levels[Math.min(level, levels.length - 1)] ?? "info");
149
- }
150
- async function readStdin() {
151
- const chunks = [];
152
- for await (const chunk of process.stdin) {
153
- chunks.push(chunk);
154
- }
155
- return chunks.join("");
156
- }
157
- function promptSecret(prompt) {
158
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
159
- return new Promise((resolve) => {
160
- rl.question(prompt, (answer) => {
161
- rl.close();
162
- resolve(answer);
163
- });
164
- });
165
- }
166
- function handleError(err) {
167
- if (err instanceof NotesError) {
168
- err.logError();
169
- process.exit(getExitCode(err));
170
- }
171
- error(err instanceof Error ? err.message : String(err));
172
- process.exit(EXIT_CODES.GENERAL_ERROR);
173
- }
174
- program.parse();
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@releasekit/notes",
3
- "version": "0.3.0-next.4",
3
+ "version": "0.3.0",
4
4
  "description": "Release notes and changelog generation with LLM-powered enhancement and flexible templating",
5
5
  "type": "module",
6
- "main": "./dist/index.cjs",
7
6
  "module": "./dist/index.js",
8
7
  "types": "./dist/index.d.ts",
9
8
  "exports": {
@@ -11,10 +10,12 @@
11
10
  "import": {
12
11
  "types": "./dist/index.d.ts",
13
12
  "default": "./dist/index.js"
14
- },
15
- "require": {
16
- "types": "./dist/index.d.cts",
17
- "default": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "./cli": {
16
+ "import": {
17
+ "types": "./dist/cli.d.ts",
18
+ "default": "./dist/cli.js"
18
19
  }
19
20
  }
20
21
  },
@@ -53,9 +54,8 @@
53
54
  "handlebars": "^4.7.8",
54
55
  "liquidjs": "^10.25.0",
55
56
  "openai": "^6.27.0",
56
- "zod": "^4.3.6",
57
- "@releasekit/config": "0.1.0",
58
- "@releasekit/core": "0.1.0"
57
+ "smol-toml": "^1.6.1",
58
+ "zod": "^4.3.6"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@biomejs/biome": "^2.4.6",
@@ -64,14 +64,16 @@
64
64
  "@vitest/coverage-v8": "^4.1.0",
65
65
  "tsup": "^8.5.1",
66
66
  "typescript": "^5.9.3",
67
- "vitest": "^4.1.0"
67
+ "vitest": "^4.1.0",
68
+ "@releasekit/config": "0.0.0",
69
+ "@releasekit/core": "0.0.0"
68
70
  },
69
71
  "engines": {
70
72
  "node": ">=20"
71
73
  },
72
74
  "scripts": {
73
- "build": "tsup src/index.ts src/cli.ts --format esm,cjs --dts",
74
- "dev": "tsup src/index.ts src/cli.ts --format esm,cjs --watch --dts",
75
+ "build": "tsup",
76
+ "dev": "tsup --watch",
75
77
  "clean": "rm -rf dist coverage .turbo",
76
78
  "test": "vitest run",
77
79
  "test:unit": "vitest run --coverage",
@@ -1,13 +0,0 @@
1
- import {
2
- aggregateToRoot,
3
- detectMonorepo,
4
- splitByPackage,
5
- writeMonorepoChangelogs
6
- } from "./chunk-TSLTZ26C.js";
7
- import "./chunk-QCF6V2IY.js";
8
- export {
9
- aggregateToRoot,
10
- detectMonorepo,
11
- splitByPackage,
12
- writeMonorepoChangelogs
13
- };
@@ -1,135 +0,0 @@
1
- // src/output/markdown.ts
2
- import * as fs from "fs";
3
- import * as path from "path";
4
- import { debug, info, success } from "@releasekit/core";
5
- var TYPE_ORDER = ["added", "changed", "deprecated", "removed", "fixed", "security"];
6
- var TYPE_LABELS = {
7
- added: "Added",
8
- changed: "Changed",
9
- deprecated: "Deprecated",
10
- removed: "Removed",
11
- fixed: "Fixed",
12
- security: "Security"
13
- };
14
- function groupEntriesByType(entries) {
15
- const grouped = /* @__PURE__ */ new Map();
16
- for (const type of TYPE_ORDER) {
17
- grouped.set(type, []);
18
- }
19
- for (const entry of entries) {
20
- const existing = grouped.get(entry.type) ?? [];
21
- existing.push(entry);
22
- grouped.set(entry.type, existing);
23
- }
24
- return grouped;
25
- }
26
- function formatEntry(entry) {
27
- let line;
28
- if (entry.breaking && entry.scope) {
29
- line = `- **BREAKING** **${entry.scope}**: ${entry.description}`;
30
- } else if (entry.breaking) {
31
- line = `- **BREAKING** ${entry.description}`;
32
- } else if (entry.scope) {
33
- line = `- **${entry.scope}**: ${entry.description}`;
34
- } else {
35
- line = `- ${entry.description}`;
36
- }
37
- if (entry.issueIds && entry.issueIds.length > 0) {
38
- line += ` (${entry.issueIds.join(", ")})`;
39
- }
40
- return line;
41
- }
42
- function formatVersion(context, options) {
43
- const lines = [];
44
- const versionLabel = options?.includePackageName && context.packageName ? `${context.packageName}@${context.version}` : context.version;
45
- const versionHeader = context.previousVersion ? `## [${versionLabel}]` : `## ${versionLabel}`;
46
- lines.push(`${versionHeader} - ${context.date}`);
47
- lines.push("");
48
- if (context.compareUrl) {
49
- lines.push(`[Full Changelog](${context.compareUrl})`);
50
- lines.push("");
51
- }
52
- if (context.enhanced?.summary) {
53
- lines.push(context.enhanced.summary);
54
- lines.push("");
55
- }
56
- const grouped = groupEntriesByType(context.entries);
57
- for (const [type, entries] of grouped) {
58
- if (entries.length === 0) continue;
59
- lines.push(`### ${TYPE_LABELS[type]}`);
60
- for (const entry of entries) {
61
- lines.push(formatEntry(entry));
62
- }
63
- lines.push("");
64
- }
65
- return lines.join("\n");
66
- }
67
- function formatHeader() {
68
- return `# Changelog
69
-
70
- All notable changes to this project will be documented in this file.
71
-
72
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
73
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
74
-
75
- `;
76
- }
77
- function renderMarkdown(contexts, options) {
78
- const sections = [formatHeader()];
79
- for (const context of contexts) {
80
- sections.push(formatVersion(context, options));
81
- }
82
- return sections.join("\n");
83
- }
84
- function prependVersion(existingPath, context, options) {
85
- let existing = "";
86
- if (fs.existsSync(existingPath)) {
87
- existing = fs.readFileSync(existingPath, "utf-8");
88
- const headerEnd = existing.indexOf("\n## ");
89
- if (headerEnd >= 0) {
90
- const header = existing.slice(0, headerEnd);
91
- const body = existing.slice(headerEnd + 1);
92
- const newVersion = formatVersion(context, options);
93
- return `${header}
94
-
95
- ${newVersion}
96
- ${body}`;
97
- }
98
- }
99
- return renderMarkdown([context]);
100
- }
101
- function writeMarkdown(outputPath, contexts, config, dryRun) {
102
- const content = renderMarkdown(contexts);
103
- if (dryRun) {
104
- info(`Would write changelog to ${outputPath}`);
105
- debug("--- Changelog Preview ---");
106
- debug(content);
107
- debug("--- End Preview ---");
108
- return;
109
- }
110
- const dir = path.dirname(outputPath);
111
- if (!fs.existsSync(dir)) {
112
- fs.mkdirSync(dir, { recursive: true });
113
- }
114
- if (outputPath === "-") {
115
- process.stdout.write(content);
116
- return;
117
- }
118
- if (config.updateStrategy === "prepend" && fs.existsSync(outputPath) && contexts.length === 1) {
119
- const firstContext = contexts[0];
120
- if (firstContext) {
121
- const updated = prependVersion(outputPath, firstContext);
122
- fs.writeFileSync(outputPath, updated, "utf-8");
123
- }
124
- } else {
125
- fs.writeFileSync(outputPath, content, "utf-8");
126
- }
127
- success(`Changelog written to ${outputPath}`);
128
- }
129
-
130
- export {
131
- formatVersion,
132
- renderMarkdown,
133
- prependVersion,
134
- writeMarkdown
135
- };