@releasekit/notes 0.3.1 → 0.4.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.
@@ -0,0 +1,246 @@
1
+ # LLM Providers
2
+
3
+ `@releasekit/notes` can use an LLM to enhance changelog entries, generate summaries, or produce prose release notes. All LLM configuration lives under `notes.releaseNotes.llm` in `releasekit.config.json`.
4
+
5
+ ## Supported Providers
6
+
7
+ | Provider key | Service | Auth |
8
+ |---|---|---|
9
+ | `openai` | OpenAI (GPT models) | `OPENAI_API_KEY` |
10
+ | `anthropic` | Anthropic (Claude models) | `ANTHROPIC_API_KEY` |
11
+ | `ollama` | Ollama (local models) | None |
12
+ | `openai-compatible` | Any OpenAI-compatible endpoint | Varies |
13
+
14
+ ---
15
+
16
+ ## Storing API Keys
17
+
18
+ Use the `auth` subcommand to store API keys securely in `~/.config/releasekit/auth.json`:
19
+
20
+ ```bash
21
+ # Interactive prompt
22
+ releasekit-notes auth openai
23
+ releasekit-notes auth anthropic
24
+
25
+ # Pass key directly
26
+ releasekit-notes auth openai --key sk-...
27
+ ```
28
+
29
+ Alternatively, set the provider's environment variable (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.). Environment variables take precedence over stored keys.
30
+
31
+ ---
32
+
33
+ ## Provider Setup
34
+
35
+ ### OpenAI
36
+
37
+ ```json
38
+ {
39
+ "notes": {
40
+ "releaseNotes": {
41
+ "llm": {
42
+ "provider": "openai",
43
+ "model": "gpt-4o-mini",
44
+ "tasks": { "enhance": true }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ Set `OPENAI_API_KEY` or run `releasekit-notes auth openai`.
52
+
53
+ Recommended models: `gpt-4o-mini` (fast, cheap), `gpt-4o` (higher quality).
54
+
55
+ ### Anthropic
56
+
57
+ ```json
58
+ {
59
+ "notes": {
60
+ "releaseNotes": {
61
+ "llm": {
62
+ "provider": "anthropic",
63
+ "model": "claude-haiku-4-5",
64
+ "tasks": { "releaseNotes": true }
65
+ }
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ Set `ANTHROPIC_API_KEY` or run `releasekit-notes auth anthropic`.
72
+
73
+ Recommended models: `claude-haiku-4-5` (fast), `claude-sonnet-4-5` (balanced).
74
+
75
+ ### Ollama (local)
76
+
77
+ Ollama runs entirely on your machine — no API key required.
78
+
79
+ ```bash
80
+ # Pull a model first
81
+ ollama pull llama3.2
82
+ ```
83
+
84
+ ```json
85
+ {
86
+ "notes": {
87
+ "releaseNotes": {
88
+ "llm": {
89
+ "provider": "ollama",
90
+ "model": "llama3.2",
91
+ "tasks": { "enhance": true }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ Ollama defaults to `http://localhost:11434`. Override with `baseURL` if needed.
99
+
100
+ ### OpenAI-Compatible Endpoint
101
+
102
+ For self-hosted or third-party OpenAI-compatible APIs (LM Studio, vLLM, Azure OpenAI, etc.):
103
+
104
+ ```json
105
+ {
106
+ "notes": {
107
+ "releaseNotes": {
108
+ "llm": {
109
+ "provider": "openai-compatible",
110
+ "model": "mistral-7b-instruct",
111
+ "baseURL": "http://localhost:1234/v1",
112
+ "tasks": { "enhance": true }
113
+ }
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ Set `baseURL` to the endpoint's base path. If the endpoint requires authentication, set `apiKey` in the config or store it with `releasekit-notes auth openai-compatible`.
120
+
121
+ ---
122
+
123
+ ## LLM Tasks
124
+
125
+ Tasks are configured under `llm.tasks`. Multiple tasks can be enabled simultaneously.
126
+
127
+ ### `enhance`
128
+
129
+ Rewrites each changelog entry description to be clearer and more user-facing.
130
+
131
+ **Input:** `"fix: npe in auth middleware when token is null"`
132
+ **Output:** `"Fixed a crash that could occur during authentication when no token was present"`
133
+
134
+ ```json
135
+ { "tasks": { "enhance": true } }
136
+ ```
137
+
138
+ Entry descriptions are processed in parallel (default concurrency: 3). Adjust with `concurrency`:
139
+
140
+ ```json
141
+ { "concurrency": 5 }
142
+ ```
143
+
144
+ ### `summarize`
145
+
146
+ Generates a one-paragraph summary of the entire release, added to the template context as `enhanced.summary`.
147
+
148
+ ```json
149
+ { "tasks": { "summarize": true } }
150
+ ```
151
+
152
+ ### `categorize`
153
+
154
+ Groups entries into user-friendly categories rather than conventional commit types. The result is available in templates as `enhanced.categories`.
155
+
156
+ ```json
157
+ { "tasks": { "categorize": true } }
158
+ ```
159
+
160
+ Provide category hints for better grouping:
161
+
162
+ ```json
163
+ {
164
+ "tasks": { "categorize": true },
165
+ "categories": [
166
+ { "name": "New Features", "description": "New user-facing functionality" },
167
+ { "name": "Bug Fixes", "description": "Corrections to existing behaviour" },
168
+ { "name": "Under the Hood", "description": "Internal changes users may not notice" }
169
+ ]
170
+ }
171
+ ```
172
+
173
+ ### `releaseNotes`
174
+
175
+ Generates complete prose release notes for the version — suitable for a GitHub release body. Available in the pipeline result as `releaseNotes` and used automatically when `publish.githubRelease.body` is set to `"releaseNotes"`.
176
+
177
+ ```json
178
+ { "tasks": { "releaseNotes": true } }
179
+ ```
180
+
181
+ You do not need `mode` or `file` configured for this task — the generated text is passed through the pipeline even without file output.
182
+
183
+ ---
184
+
185
+ ## Prompt Customisation
186
+
187
+ Override the built-in prompt instructions or templates for any task. Keys are task names.
188
+
189
+ ### Custom instructions
190
+
191
+ Append extra instructions to the built-in prompt:
192
+
193
+ ```json
194
+ {
195
+ "llm": {
196
+ "prompts": {
197
+ "instructions": {
198
+ "enhance": "Write in a friendly tone for non-technical users. Keep entries under 15 words.",
199
+ "releaseNotes": "Start with a one-sentence headline, then group changes by theme."
200
+ }
201
+ }
202
+ }
203
+ }
204
+ ```
205
+
206
+ ### Full prompt template override
207
+
208
+ Replace the entire prompt for a task. The string is sent to the LLM verbatim — no placeholder substitution is applied.
209
+
210
+ ```json
211
+ {
212
+ "llm": {
213
+ "prompts": {
214
+ "templates": {
215
+ "enhance": "You are a technical writer. Rewrite the changelog entry below as a single, concise sentence in plain English. Return only the rewritten text, nothing else."
216
+ }
217
+ }
218
+ }
219
+ }
220
+ ```
221
+
222
+ ---
223
+
224
+ ## CLI Flags
225
+
226
+ LLM options can also be set via CLI flags without a config file:
227
+
228
+ ```bash
229
+ releasekit-notes \
230
+ --llm-provider anthropic \
231
+ --llm-model claude-haiku-4-5 \
232
+ --llm-tasks enhance,release-notes \
233
+ --input version-output.json
234
+ ```
235
+
236
+ Use `--no-llm` to disable LLM processing even when it is configured:
237
+
238
+ ```bash
239
+ releasekit-notes --no-llm
240
+ ```
241
+
242
+ Use `--no-release-notes` to disable release notes entirely (including LLM):
243
+
244
+ ```bash
245
+ releasekit-notes --no-release-notes
246
+ ```
@@ -0,0 +1,124 @@
1
+ # Monorepo Support
2
+
3
+ `@releasekit/notes` can write changelogs and release notes at the repo root, inside each package directory, or both — controlled independently for changelog and release notes output.
4
+
5
+ ## Output Modes
6
+
7
+ | Mode | Changelog written to | Release notes written to |
8
+ |------|---------------------|--------------------------|
9
+ | `"root"` | `<repo-root>/CHANGELOG.md` | `<repo-root>/RELEASE_NOTES.md` |
10
+ | `"packages"` | `packages/<name>/CHANGELOG.md` (each package) | `packages/<name>/RELEASE_NOTES.md` |
11
+ | `"both"` | Both root and per-package | Both root and per-package |
12
+
13
+ Set `mode` on `changelog` and `releaseNotes` independently:
14
+
15
+ ```json
16
+ {
17
+ "notes": {
18
+ "changelog": { "mode": "packages" },
19
+ "releaseNotes": { "mode": "root" }
20
+ }
21
+ }
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Root Changelog (aggregated)
27
+
28
+ In `"root"` or `"both"` mode, the root changelog aggregates entries from all packages. Packages with a `@scope/` prefix include the package name in the version header:
29
+
30
+ ```markdown
31
+ ## [@myorg/core@2.0.0] — 2026-03-15
32
+
33
+ ### Added
34
+ - New streaming API
35
+
36
+ ## [utils@1.1.0] — 2026-03-15
37
+
38
+ ### Fixed
39
+ - Memory leak in event handler
40
+ ```
41
+
42
+ Unscoped packages omit the name:
43
+
44
+ ```markdown
45
+ ## [2.0.0] — 2026-03-15
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Per-Package Changelogs
51
+
52
+ In `"packages"` or `"both"` mode, a separate changelog is written inside each package directory. The path is determined by the package's location in the workspace.
53
+
54
+ Custom file name applies to all packages:
55
+
56
+ ```json
57
+ {
58
+ "notes": {
59
+ "changelog": { "mode": "packages", "file": "CHANGES.md" }
60
+ }
61
+ }
62
+ ```
63
+
64
+ ---
65
+
66
+ ## CLI Flags
67
+
68
+ Use `--changelog-mode` / `--release-notes-mode` to override config:
69
+
70
+ ```bash
71
+ # Root changelog only
72
+ releasekit-notes --changelog-mode root
73
+
74
+ # Per-package changelogs
75
+ releasekit-notes --changelog-mode packages
76
+
77
+ # Both outputs
78
+ releasekit-notes --changelog-mode both
79
+
80
+ # Use --monorepo as a shorthand (applies to both outputs)
81
+ releasekit-notes --monorepo packages
82
+ ```
83
+
84
+ When `--monorepo` is set, it applies to both `changelog` and `releaseNotes` modes. Explicit `--changelog-mode` or `--release-notes-mode` flags take priority over `--monorepo` when both are present.
85
+
86
+ ---
87
+
88
+ ## Monorepo Path Configuration
89
+
90
+ Package locations are detected automatically from your workspace configuration (`pnpm-workspace.yaml`, `package.json` workspaces). To override root or packages directory paths, use the top-level `monorepo` config:
91
+
92
+ ```json
93
+ {
94
+ "monorepo": {
95
+ "rootPath": ".",
96
+ "packagesPath": "packages"
97
+ },
98
+ "notes": {
99
+ "changelog": { "mode": "both" }
100
+ }
101
+ }
102
+ ```
103
+
104
+ ---
105
+
106
+ ## LLM with Monorepo
107
+
108
+ LLM tasks run per-package. Each package's entries are processed independently. With concurrency set, multiple packages can be processed in parallel:
109
+
110
+ ```json
111
+ {
112
+ "notes": {
113
+ "changelog": { "mode": "packages" },
114
+ "releaseNotes": {
115
+ "llm": {
116
+ "provider": "openai",
117
+ "model": "gpt-4o-mini",
118
+ "concurrency": 5,
119
+ "tasks": { "enhance": true }
120
+ }
121
+ }
122
+ }
123
+ }
124
+ ```
@@ -0,0 +1,204 @@
1
+ # Templates
2
+
3
+ `@releasekit/notes` supports custom templates for both changelog and release notes output. Templates can be single files or composable directory layouts, and can use Liquid, Handlebars, or EJS.
4
+
5
+ ## Built-in Templates
6
+
7
+ | Name | Engine | Default for |
8
+ |------|--------|-------------|
9
+ | `keep-a-changelog` | Liquid | Changelog (default) |
10
+ | `angular` | Handlebars | — |
11
+ | `github-release` | EJS | — |
12
+
13
+ The default `keep-a-changelog` template produces [Keep a Changelog](https://keepachangelog.com)-formatted output. Switch to a different built-in via the `engine` option or use `--template` / `--engine` on the CLI.
14
+
15
+ ---
16
+
17
+ ## Custom Templates
18
+
19
+ ### Single-file template
20
+
21
+ Pass a path to any single template file:
22
+
23
+ ```bash
24
+ releasekit-notes --template ./my-changelog.liquid
25
+ ```
26
+
27
+ ```json
28
+ {
29
+ "notes": {
30
+ "changelog": {
31
+ "templates": { "path": "./my-changelog.liquid" }
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ The file receives the full [Document context](#document-context) and must render the complete changelog document.
38
+
39
+ ### Composable directory layout
40
+
41
+ Pass a directory containing named template files. Each file renders a different level of the document:
42
+
43
+ ```
44
+ templates/
45
+ ├── document.liquid # Outer document wrapper (receives DocumentContext)
46
+ ├── version.liquid # One version block (receives TemplateContext)
47
+ └── entry.liquid # One changelog entry (receives ChangelogEntry)
48
+ ```
49
+
50
+ ```bash
51
+ releasekit-notes --template ./templates/
52
+ ```
53
+
54
+ All three files are optional — omit any you don't need to customise, and the built-in for that level is used.
55
+
56
+ ---
57
+
58
+ ## Template Context
59
+
60
+ ### Document context
61
+
62
+ The outermost template (`document`) receives:
63
+
64
+ | Variable | Type | Description |
65
+ |----------|------|-------------|
66
+ | `project.name` | `string` | Repository/package name |
67
+ | `project.repoUrl` | `string \| null` | Repository URL |
68
+ | `versions` | `TemplateContext[]` | All rendered versions, newest first |
69
+ | `unreleased` | `TemplateContext \| undefined` | Unreleased changes, if any |
70
+
71
+ ### Version context
72
+
73
+ Each version block (`version`) receives:
74
+
75
+ | Variable | Type | Description |
76
+ |----------|------|-------------|
77
+ | `packageName` | `string` | Package name (e.g. `@releasekit/notes`) |
78
+ | `version` | `string` | New version string (e.g. `1.2.0`) |
79
+ | `previousVersion` | `string \| null` | Previous version string |
80
+ | `date` | `string` | Release date in `YYYY-MM-DD` format |
81
+ | `repoUrl` | `string \| null` | Repository URL |
82
+ | `compareUrl` | `string \| undefined` | Link to diff between previous and current version |
83
+ | `entries` | `ChangelogEntry[]` | Raw changelog entries |
84
+ | `enhanced` | `EnhancedData \| undefined` | LLM-processed data (see below) |
85
+
86
+ ### Entry
87
+
88
+ Each entry in `entries` has:
89
+
90
+ | Field | Type | Values |
91
+ |-------|------|--------|
92
+ | `type` | `ChangelogType` | `"added"`, `"changed"`, `"deprecated"`, `"removed"`, `"fixed"`, `"security"` |
93
+ | `description` | `string` | Entry description (enhanced by LLM if `tasks.enhance` is on) |
94
+ | `scope` | `string \| undefined` | Conventional commit scope |
95
+ | `breaking` | `boolean \| undefined` | `true` for breaking changes |
96
+ | `issueIds` | `string[] \| undefined` | Referenced issue/PR numbers |
97
+
98
+ ### Enhanced data (`enhanced`)
99
+
100
+ Present when any LLM task ran successfully:
101
+
102
+ | Field | Type | Description |
103
+ |-------|------|-------------|
104
+ | `enhanced.summary` | `string \| undefined` | One-paragraph release summary (`summarize` task) |
105
+ | `enhanced.categories` | `Category[] \| undefined` | Grouped entries (`categorize` task) |
106
+ | `enhanced.releaseNotes` | `string \| undefined` | Full prose release notes (`releaseNotes` task) |
107
+
108
+ Each `Category` in `enhanced.categories`:
109
+
110
+ | Field | Type | Description |
111
+ |-------|------|-------------|
112
+ | `name` | `string` | Category display name |
113
+ | `entries` | `ChangelogEntry[]` | Entries in this category |
114
+
115
+ ---
116
+
117
+ ## Examples
118
+
119
+ ### Liquid (single file)
120
+
121
+ ```liquid
122
+ # Changelog
123
+
124
+ {% for v in versions %}
125
+ ## [{{ v.version }}] — {{ v.date }}
126
+
127
+ {% if v.enhanced.summary %}
128
+ {{ v.enhanced.summary }}
129
+
130
+ {% endif %}
131
+ {% for entry in v.entries %}
132
+ - **{{ entry.type }}**: {{ entry.description }}{% if entry.scope %} *({{ entry.scope }})*{% endif %}
133
+ {% endfor %}
134
+
135
+ {% endfor %}
136
+ ```
137
+
138
+ ### Liquid (composable — `version.liquid`)
139
+
140
+ ```liquid
141
+ ## [{{ version }}]({{ compareUrl }}) — {{ date }}
142
+
143
+ {% if enhanced.categories %}
144
+ {% for cat in enhanced.categories %}
145
+ ### {{ cat.name }}
146
+
147
+ {% for entry in cat.entries %}
148
+ - {{ entry.description }}
149
+ {% endfor %}
150
+ {% endfor %}
151
+ {% else %}
152
+ {% for entry in entries %}
153
+ - {{ entry.description }}
154
+ {% endfor %}
155
+ {% endif %}
156
+ ```
157
+
158
+ ### Handlebars (`version.hbs`)
159
+
160
+ ```handlebars
161
+ ## [{{version}}] — {{date}}
162
+
163
+ {{#if enhanced.summary}}
164
+ > {{enhanced.summary}}
165
+
166
+ {{/if}}
167
+ {{#each entries}}
168
+ - **{{type}}**: {{description}}
169
+ {{/each}}
170
+ ```
171
+
172
+ ### EJS (`release.md.ejs`)
173
+
174
+ ```ejs
175
+ ## <%= version %> — <%= date %>
176
+
177
+ <% if (enhanced && enhanced.releaseNotes) { %>
178
+ <%= enhanced.releaseNotes %>
179
+ <% } else { %>
180
+ <% entries.forEach(entry => { %>
181
+ - **<%= entry.type %>**: <%= entry.description %>
182
+ <% }) %>
183
+ <% } %>
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Engine Selection
189
+
190
+ The engine is inferred from the file extension (`.liquid`, `.hbs`/`.handlebars`, `.ejs`) if not explicitly set. Override via config or CLI:
191
+
192
+ ```json
193
+ {
194
+ "notes": {
195
+ "changelog": {
196
+ "templates": { "path": "./templates/", "engine": "handlebars" }
197
+ }
198
+ }
199
+ }
200
+ ```
201
+
202
+ ```bash
203
+ releasekit-notes --template ./templates/ --engine handlebars
204
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@releasekit/notes",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "Release notes and changelog generation with LLM-powered enhancement and flexible templating",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
@@ -38,6 +38,7 @@
38
38
  },
39
39
  "files": [
40
40
  "dist",
41
+ "docs",
41
42
  "templates",
42
43
  "README.md",
43
44
  "LICENSE"
@@ -51,7 +52,7 @@
51
52
  "chalk": "^5.6.2",
52
53
  "commander": "^14.0.3",
53
54
  "ejs": "^4.0.1",
54
- "handlebars": "^4.7.8",
55
+ "handlebars": "^4.7.9",
55
56
  "liquidjs": "^10.25.0",
56
57
  "openai": "^6.27.0",
57
58
  "smol-toml": "^1.6.1",
@@ -65,8 +66,8 @@
65
66
  "tsup": "^8.5.1",
66
67
  "typescript": "^5.9.3",
67
68
  "vitest": "^4.1.0",
68
- "@releasekit/core": "0.0.0",
69
- "@releasekit/config": "0.0.0"
69
+ "@releasekit/config": "0.0.0",
70
+ "@releasekit/core": "0.0.0"
70
71
  },
71
72
  "engines": {
72
73
  "node": ">=20"