pm-changelog 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ ## Unreleased - 2026-05-23
4
+
5
+ ### Added
6
+
7
+ - Regenerate release changelog from pm items (pmc-a6qg)
8
+ - Harden npm package metadata and CI release gates (pmc-otpe)
9
+
10
+ ### Security
11
+
12
+ - Audit git history for private data exposure (pmc-91po)
13
+
14
+ ### Other
15
+
16
+ - Final release verification and npm publication audit (pmc-jk1a)
17
+ - Release pm-changelog 0.1.0 as a production-ready pm package (pmc-ysps)
18
+ - Document pm governance for the package lifecycle (pmc-xl68)
19
+ - Align GitHub repository settings for public package release (pmc-w1sp)
20
+ - Verify pm-changelog in a clean temporary project (pmc-800h)
21
+
22
+ ## 0.1.0 - 2026-05-17
23
+
24
+ ### Added
25
+
26
+ - Initial `pm-changelog` CLI for generating `CHANGELOG.md` from pm item JSON or `pm list-all --json`.
27
+ - Programmatic APIs for creating, merging, reading, and writing changelogs from Node.js scripts and CI runners.
28
+ - Package metadata for `pm install github.com/unbraind/pm-changelog --project`, `pm install npm:pm-changelog --project`, and catalog discovery.
29
+ - `--release-version` for the pm extension command so release headings do not collide with the global `pm --version` flag.
30
+ - Custom pm executable support via `--pm-bin` and `readPmItems({ pmBin })`.
31
+ - Programmatic runner wrapper support with `readPmItems({ pmArgs, cwd, env })`.
32
+ - pm-cli extension command: `pm changelog generate`.
33
+ - GitHub Actions support with JSON summaries, check mode, prepend mode, and `$GITHUB_OUTPUT` fields.
34
+ - Optional `$GITHUB_STEP_SUMMARY` publishing via `--github-step-summary`.
35
+ - GitHub Actions CI workflow for validating package builds and tests.
36
+ - Release and milestone grouping for projects that store release metadata on pm items.
37
+ - Tracked built runtime output so GitHub and local pm package installs work without a separate build step.
38
+
39
+ ### Security
40
+
41
+ - Item URLs are omitted by default.
42
+ - Opt-in item links strip credentials, query strings, and fragments before markdown output.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 unbraind
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,318 @@
1
+ # pm-changelog
2
+
3
+ Generate `CHANGELOG.md` from pm-cli items for local releases, GitHub Actions, runners, and scripts.
4
+
5
+ The package provides:
6
+
7
+ - `pm-changelog`, a standalone CLI that reads `pm list-all --json` or JSON input
8
+ - `createChangelog()`, `generateChangelog()`, `mergeChangelog()`, and `writeChangelog()` programmatic APIs
9
+ - `pm changelog generate`, a pm-cli extension command
10
+
11
+ ## Install
12
+
13
+ Install as a pm package from GitHub:
14
+
15
+ ```bash
16
+ pm install github.com/unbraind/pm-changelog --project
17
+ ```
18
+
19
+ Then run the extension command:
20
+
21
+ ```bash
22
+ pm changelog generate --mode prepend --output CHANGELOG.md
23
+ ```
24
+
25
+ Install the standalone CLI/API package from npm:
26
+
27
+ ```bash
28
+ npm install --save-dev pm-changelog @unbrained/pm-cli
29
+ ```
30
+
31
+ Use the local checkout for development:
32
+
33
+ ```bash
34
+ npm install
35
+ npm run build
36
+ ```
37
+
38
+ Local checkout extension install:
39
+
40
+ ```bash
41
+ pm install ./pm-changelog --project
42
+ ```
43
+
44
+ The repository tracks `dist/` intentionally so GitHub and local pm package installs work without a build step. npm packaging still runs `npm run build` through `prepack`.
45
+
46
+ Package metadata is declared in `package.json` under `pm`, and the runtime extension manifest is `manifest.json` at the package root.
47
+
48
+ Supported package-manager sources:
49
+
50
+ ```bash
51
+ pm install github.com/unbraind/pm-changelog --project
52
+ pm install npm:pm-changelog --project
53
+ pm install ./pm-changelog --project
54
+ ```
55
+
56
+ ## Package Layout
57
+
58
+ ```text
59
+ pm-changelog/
60
+ manifest.json # pm extension manifest loaded by the package manager
61
+ package.json # npm metadata plus pm package catalog/install metadata
62
+ LICENSE # MIT license for npm and public repository consumers
63
+ dist/ # built CLI, API, and extension runtime tracked for pm installs
64
+ src/ # TypeScript source
65
+ test/ # node:test coverage for generator, CLI, and runner behavior
66
+ ```
67
+
68
+ ## Release Verification
69
+
70
+ Run the full local release gate before tagging or publishing:
71
+
72
+ ```bash
73
+ npm run release:check
74
+ ```
75
+
76
+ The release gate type-checks the TypeScript source, runs the full test suite, audits production dependencies, verifies the npm package contents with a dry run, and checks that `CHANGELOG.md` is current.
77
+
78
+ ## CLI
79
+
80
+ Generate `CHANGELOG.md` from the current pm project:
81
+
82
+ ```bash
83
+ pm-changelog
84
+ ```
85
+
86
+ Generate release notes for a CI release:
87
+
88
+ ```bash
89
+ pm-changelog --pm-root . --version "$GITHUB_REF_NAME" --since 2026-05-01
90
+ ```
91
+
92
+ Create or update `CHANGELOG.md` while preserving older entries:
93
+
94
+ ```bash
95
+ pm-changelog --mode prepend --version "$GITHUB_REF_NAME" --output CHANGELOG.md
96
+ ```
97
+
98
+ After building, the package also exposes npm scripts for projects that install it locally:
99
+
100
+ ```bash
101
+ npm run changelog -- --version "$GITHUB_REF_NAME"
102
+ npm run changelog:check -- --version "$GITHUB_REF_NAME"
103
+ ```
104
+
105
+ Emit runner-readable metadata:
106
+
107
+ ```bash
108
+ pm-changelog --mode prepend --version "$GITHUB_REF_NAME" --json
109
+ ```
110
+
111
+ Fail CI if the committed changelog is stale without rewriting it:
112
+
113
+ ```bash
114
+ pm-changelog --mode prepend --version "$GITHUB_REF_NAME" --check
115
+ ```
116
+
117
+ Expose summary values to later GitHub Actions steps:
118
+
119
+ ```bash
120
+ pm-changelog --mode prepend --version "$GITHUB_REF_NAME" --json --github-output
121
+ ```
122
+
123
+ Append the generated changelog markdown to the GitHub Actions job summary:
124
+
125
+ ```bash
126
+ pm-changelog --mode prepend --version "$GITHUB_REF_NAME" --github-step-summary
127
+ ```
128
+
129
+ Print markdown instead of writing a file:
130
+
131
+ ```bash
132
+ pm-changelog --stdout --version 1.2.0
133
+ ```
134
+
135
+ Read JSON from a previous step:
136
+
137
+ ```bash
138
+ pm list-all --json | pm-changelog --stdin --stdout
139
+ ```
140
+
141
+ Use a pinned or wrapped pm executable in a runner:
142
+
143
+ ```bash
144
+ pm-changelog --pm-bin ./node_modules/.bin/pm --mode prepend --version "$GITHUB_REF_NAME"
145
+ ```
146
+
147
+ Pass runner-specific arguments and a working directory to wrapped pm commands:
148
+
149
+ ```bash
150
+ pm-changelog --pm-bin ./pm-wrapper --pm-arg --profile --pm-arg ci --pm-cwd "$GITHUB_WORKSPACE" --mode prepend
151
+ ```
152
+
153
+ Generate one section per `release` metadata value from pm items:
154
+
155
+ ```bash
156
+ pm-changelog --group-by release --mode prepend --output CHANGELOG.md
157
+ ```
158
+
159
+ Useful options:
160
+
161
+ | Option | Default | Description |
162
+ |---|---:|---|
163
+ | `--output <file>` | `CHANGELOG.md` | Output path |
164
+ | `--stdout` | false | Print markdown instead of writing a file |
165
+ | `--input <file>` | - | Read pm JSON from a file |
166
+ | `--stdin` | false | Read pm JSON from stdin |
167
+ | `--pm-root <dir>` | - | Run `pm --path <dir> list-all --json` |
168
+ | `--pm-bin <file>` | `pm` | pm executable to run, useful for pinned local installs and runner wrappers |
169
+ | `--pm-arg <arg>` | - | Extra argument passed before `list-all --json`; repeat for multiple args |
170
+ | `--pm-cwd <dir>` | - | Working directory for running pm |
171
+ | `--version <version>` | `Unreleased` | Version heading |
172
+ | `--date <date>` | today | Release date |
173
+ | `--since <date>` | - | Include items changed on or after date |
174
+ | `--until <date>` | - | Include items changed on or before date |
175
+ | `--status <list>` | `closed` | Comma-separated statuses |
176
+ | `--group-by <mode>` | `version` | `version`, `release`, or `milestone` |
177
+ | `--mode <mode>` | `replace` | `replace` or `prepend` existing changelog |
178
+ | `--json` | false | Print JSON summary for automation |
179
+ | `--check` | false | Do not write; exit 1 if the output file would change |
180
+ | `--github-output` | false | Write `output`, `mode`, `action`, `changed`, `item_count`, and `bytes` to `$GITHUB_OUTPUT` |
181
+ | `--github-step-summary` | false | Append generated markdown to `$GITHUB_STEP_SUMMARY` |
182
+ | `--include-empty` | false | Emit an empty section when no items match |
183
+ | `--include-links` | false | Include item `url` values in generated entries |
184
+
185
+ ## pm-cli command
186
+
187
+ ```bash
188
+ pm changelog generate
189
+ pm changelog generate --release-version 1.2.0 --output CHANGELOG.md
190
+ pm changelog generate --stdout --group-by milestone
191
+ pm changelog generate --stdout --group-by release
192
+ pm changelog generate --mode prepend --release-version "$GITHUB_REF_NAME"
193
+ pm changelog generate --check --mode prepend --release-version "$GITHUB_REF_NAME"
194
+ ```
195
+
196
+ The pm extension command uses `--release-version` because `pm --version` is a global CLI flag. The standalone `pm-changelog` binary uses `--version`.
197
+
198
+ ## Programmatic API
199
+
200
+ ```ts
201
+ import { readPmItems, writeChangelog } from "pm-changelog";
202
+
203
+ const items = readPmItems({
204
+ pmRoot: process.cwd(),
205
+ pmBin: "./node_modules/.bin/pm",
206
+ });
207
+ const result = writeChangelog({
208
+ items,
209
+ output: "CHANGELOG.md",
210
+ mode: "prepend",
211
+ groupBy: "release",
212
+ since: process.env.CHANGELOG_SINCE,
213
+ includeLinks: false,
214
+ });
215
+
216
+ console.log({
217
+ action: result.action,
218
+ changed: result.changed,
219
+ items: result.itemCount,
220
+ output: result.output,
221
+ });
222
+ ```
223
+
224
+ Use `version` when a runner is generating one release section from the current job context. Use `groupBy: "release"` or `--group-by release` when pm items already carry release metadata and a runner should rebuild multiple sections in one pass.
225
+
226
+ Item links are omitted by default so public CI jobs do not accidentally publish private tracker URLs. Pass `--include-links` or `includeLinks: true` when item URLs are safe to expose. When links are included, credentials, query strings, and fragments are stripped before markdown is emitted.
227
+
228
+ You can also pass items directly:
229
+
230
+ ```ts
231
+ import { generateChangelog } from "pm-changelog";
232
+
233
+ const markdown = generateChangelog({
234
+ version: "1.2.0",
235
+ items: [
236
+ {
237
+ id: "pm-123",
238
+ title: "Fix CSV import status handling",
239
+ status: "closed",
240
+ type: "Bug",
241
+ tags: ["fix"],
242
+ updated_at: "2026-05-17T09:00:00Z",
243
+ },
244
+ ],
245
+ });
246
+ ```
247
+
248
+ Runner wrappers can provide extra pm arguments, a working directory, and environment:
249
+
250
+ ```ts
251
+ import { readPmItems } from "pm-changelog";
252
+
253
+ const items = readPmItems({
254
+ pmBin: process.env.PM_BIN ?? "pm",
255
+ pmArgs: ["--profile", "ci"],
256
+ cwd: process.env.GITHUB_WORKSPACE,
257
+ env: process.env,
258
+ });
259
+ ```
260
+
261
+ ## Categorization
262
+
263
+ Items are grouped into Keep a Changelog-style sections using `type`, `tags`, and title keywords:
264
+
265
+ - `Added`: feature, feat, added, add, new
266
+ - `Changed`: change, refactor, update, improve
267
+ - `Fixed`: fix, bug, hotfix, regression
268
+ - `Removed`: removed, delete
269
+ - `Security`: security, CVE, vulnerability
270
+ - `Deprecated`: deprecated, deprecation
271
+ - `Other`: anything else
272
+
273
+ ## GitHub Actions Example
274
+
275
+ ```yaml
276
+ name: Changelog
277
+
278
+ on:
279
+ workflow_dispatch:
280
+ release:
281
+ types: [published]
282
+
283
+ jobs:
284
+ changelog:
285
+ runs-on: ubuntu-latest
286
+ permissions:
287
+ contents: write
288
+ steps:
289
+ - uses: actions/checkout@v4
290
+ - uses: actions/setup-node@v4
291
+ with:
292
+ node-version: 20
293
+ - run: npm ci
294
+ - run: npm run build
295
+ - name: Generate changelog
296
+ id: changelog
297
+ run: node dist/cli.js --mode prepend --version "${GITHUB_REF_NAME}" --output CHANGELOG.md --json --github-output --github-step-summary
298
+ - name: Commit changelog
299
+ if: steps.changelog.outputs.changed == 'true'
300
+ run: |
301
+ git config user.name "github-actions[bot]"
302
+ git config user.email "github-actions[bot]@users.noreply.github.com"
303
+ git add CHANGELOG.md
304
+ git commit -m "docs: update changelog"
305
+ git push
306
+ ```
307
+
308
+ ## Build
309
+
310
+ ```bash
311
+ npm run build
312
+ ```
313
+
314
+ TypeScript 5, ES2022 target, NodeNext module resolution.
315
+
316
+ ## License
317
+
318
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,310 @@
1
+ #!/usr/bin/env node
2
+ import { appendFileSync, existsSync, readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { stdin } from "node:process";
5
+ import { createChangelog, mergeChangelog, parsePmItemsJson, readPmItems, writeChangelog, } from "./generator.js";
6
+ async function main() {
7
+ const options = parseArgs(process.argv.slice(2));
8
+ const items = await loadItems(options);
9
+ const outputPath = resolve(options.output);
10
+ if (!options.stdout) {
11
+ const result = writeChangelog({
12
+ items,
13
+ output: outputPath,
14
+ title: options.title,
15
+ version: options.version,
16
+ date: options.date,
17
+ since: options.since,
18
+ until: options.until,
19
+ includeStatuses: options.statuses,
20
+ groupBy: options.groupBy,
21
+ includeEmpty: options.includeEmpty,
22
+ includeLinks: options.includeLinks,
23
+ mode: options.mode,
24
+ check: options.check,
25
+ });
26
+ const summary = buildSummary(options, result, outputPath);
27
+ if (options.githubOutput)
28
+ writeGitHubOutput(summary);
29
+ if (options.githubStepSummary)
30
+ writeGitHubStepSummary(result.markdown);
31
+ if (options.json) {
32
+ process.stdout.write(JSON.stringify(summary) + "\n");
33
+ }
34
+ else if (options.check) {
35
+ console.error(result.changed ? `Changelog is out of date: ${outputPath}` : `Changelog is up to date: ${outputPath}`);
36
+ }
37
+ else {
38
+ console.error(`Wrote ${outputPath}`);
39
+ }
40
+ if (options.check && result.changed)
41
+ process.exit(1);
42
+ return;
43
+ }
44
+ const generated = createChangelog({
45
+ items,
46
+ title: options.title,
47
+ version: options.version,
48
+ date: options.date,
49
+ since: options.since,
50
+ until: options.until,
51
+ includeStatuses: options.statuses,
52
+ groupBy: options.groupBy,
53
+ includeEmpty: options.includeEmpty,
54
+ includeLinks: options.includeLinks,
55
+ });
56
+ const existing = options.mode === "prepend" && existsSync(outputPath)
57
+ ? readFileSync(outputPath, "utf-8")
58
+ : undefined;
59
+ const merged = options.mode === "prepend"
60
+ ? mergeChangelog(existing, generated.markdown, { title: options.title })
61
+ : { markdown: generated.markdown, action: "replaced", changed: true };
62
+ if (options.stdout) {
63
+ if (options.json) {
64
+ const summary = buildSummary(options, {
65
+ output: outputPath,
66
+ markdown: merged.markdown,
67
+ action: merged.action,
68
+ changed: merged.changed,
69
+ itemCount: generated.itemCount,
70
+ bytes: Buffer.byteLength(merged.markdown, "utf-8"),
71
+ });
72
+ if (options.githubOutput)
73
+ writeGitHubOutput(summary);
74
+ if (options.githubStepSummary)
75
+ writeGitHubStepSummary(merged.markdown);
76
+ process.stdout.write(JSON.stringify(summary) + "\n");
77
+ return;
78
+ }
79
+ if (options.githubStepSummary)
80
+ writeGitHubStepSummary(merged.markdown);
81
+ process.stdout.write(merged.markdown);
82
+ return;
83
+ }
84
+ }
85
+ function parseArgs(args) {
86
+ const options = {
87
+ output: "CHANGELOG.md",
88
+ stdout: false,
89
+ json: false,
90
+ stdin: false,
91
+ pmArgs: [],
92
+ groupBy: "version",
93
+ includeEmpty: false,
94
+ includeLinks: false,
95
+ mode: "replace",
96
+ check: false,
97
+ githubOutput: false,
98
+ githubStepSummary: false,
99
+ };
100
+ for (let i = 0; i < args.length; i++) {
101
+ const arg = args[i];
102
+ switch (arg) {
103
+ case "--help":
104
+ case "-h":
105
+ printHelp();
106
+ process.exit(0);
107
+ case "--output":
108
+ case "-o":
109
+ options.output = requireValue(args, ++i, arg);
110
+ break;
111
+ case "--stdout":
112
+ options.stdout = true;
113
+ break;
114
+ case "--json":
115
+ options.json = true;
116
+ break;
117
+ case "--check":
118
+ options.check = true;
119
+ break;
120
+ case "--github-output":
121
+ case "--set-output":
122
+ options.githubOutput = true;
123
+ break;
124
+ case "--github-step-summary":
125
+ options.githubStepSummary = true;
126
+ break;
127
+ case "--input":
128
+ case "-i":
129
+ options.input = requireValue(args, ++i, arg);
130
+ break;
131
+ case "--stdin":
132
+ options.stdin = true;
133
+ break;
134
+ case "--pm-root":
135
+ options.pmRoot = requireValue(args, ++i, arg);
136
+ break;
137
+ case "--pm-bin":
138
+ options.pmBin = requireValue(args, ++i, arg);
139
+ break;
140
+ case "--pm-arg":
141
+ options.pmArgs.push(requireAnyValue(args, ++i, arg));
142
+ break;
143
+ case "--pm-cwd":
144
+ options.pmCwd = requireValue(args, ++i, arg);
145
+ break;
146
+ case "--title":
147
+ options.title = requireValue(args, ++i, arg);
148
+ break;
149
+ case "--version":
150
+ options.version = requireValue(args, ++i, arg);
151
+ break;
152
+ case "--date":
153
+ options.date = requireValue(args, ++i, arg);
154
+ break;
155
+ case "--since":
156
+ options.since = requireValue(args, ++i, arg);
157
+ break;
158
+ case "--until":
159
+ options.until = requireValue(args, ++i, arg);
160
+ break;
161
+ case "--status":
162
+ case "--statuses":
163
+ options.statuses = requireValue(args, ++i, arg)
164
+ .split(",")
165
+ .map((status) => status.trim())
166
+ .filter(Boolean);
167
+ break;
168
+ case "--group-by":
169
+ options.groupBy = parseGroupBy(requireValue(args, ++i, arg));
170
+ break;
171
+ case "--mode":
172
+ options.mode = parseMode(requireValue(args, ++i, arg));
173
+ break;
174
+ case "--include-empty":
175
+ options.includeEmpty = true;
176
+ break;
177
+ case "--include-links":
178
+ options.includeLinks = true;
179
+ break;
180
+ case "--no-links":
181
+ options.includeLinks = false;
182
+ break;
183
+ default:
184
+ throw new Error(`Unknown option: ${arg}`);
185
+ }
186
+ }
187
+ return options;
188
+ }
189
+ async function loadItems(options) {
190
+ if (options.stdin) {
191
+ return parsePmItemsJson(await readStdin());
192
+ }
193
+ if (options.input) {
194
+ return parsePmItemsJson(readFileSync(resolve(options.input), "utf-8"));
195
+ }
196
+ return readPmItems({
197
+ pmRoot: options.pmRoot,
198
+ pmBin: options.pmBin,
199
+ pmArgs: options.pmArgs,
200
+ cwd: options.pmCwd ? resolve(options.pmCwd) : undefined,
201
+ });
202
+ }
203
+ function readStdin() {
204
+ return new Promise((resolvePromise, reject) => {
205
+ let data = "";
206
+ stdin.setEncoding("utf-8");
207
+ stdin.on("data", (chunk) => {
208
+ data += chunk;
209
+ });
210
+ stdin.on("end", () => resolvePromise(data));
211
+ stdin.on("error", reject);
212
+ });
213
+ }
214
+ function parseGroupBy(value) {
215
+ if (value === "version" || value === "release" || value === "milestone")
216
+ return value;
217
+ throw new Error("--group-by must be 'version', 'release', or 'milestone'");
218
+ }
219
+ function parseMode(value) {
220
+ if (value === "replace" || value === "prepend")
221
+ return value;
222
+ throw new Error("--mode must be 'replace' or 'prepend'");
223
+ }
224
+ function buildSummary(options, result, output = result.output) {
225
+ return {
226
+ output,
227
+ mode: options.mode,
228
+ action: result.action,
229
+ changed: result.changed,
230
+ itemCount: result.itemCount,
231
+ bytes: result.bytes,
232
+ check: options.check,
233
+ markdown: options.stdout ? result.markdown : undefined,
234
+ };
235
+ }
236
+ function writeGitHubOutput(summary) {
237
+ const githubOutput = process.env.GITHUB_OUTPUT;
238
+ if (!githubOutput) {
239
+ throw new Error("--github-output requires the GITHUB_OUTPUT environment variable");
240
+ }
241
+ const lines = [
242
+ `output=${String(summary.output ?? "")}`,
243
+ `mode=${String(summary.mode ?? "")}`,
244
+ `action=${String(summary.action ?? "")}`,
245
+ `changed=${String(summary.changed ?? "")}`,
246
+ `item_count=${String(summary.itemCount ?? "")}`,
247
+ `bytes=${String(summary.bytes ?? "")}`,
248
+ ];
249
+ appendFileSync(githubOutput, `${lines.join("\n")}\n`, "utf-8");
250
+ }
251
+ function writeGitHubStepSummary(markdown) {
252
+ const githubStepSummary = process.env.GITHUB_STEP_SUMMARY;
253
+ if (!githubStepSummary) {
254
+ throw new Error("--github-step-summary requires the GITHUB_STEP_SUMMARY environment variable");
255
+ }
256
+ appendFileSync(githubStepSummary, `${markdown.trimEnd()}\n`, "utf-8");
257
+ }
258
+ function requireValue(args, index, flag) {
259
+ const value = args[index];
260
+ if (!value || value.startsWith("--")) {
261
+ throw new Error(`${flag} requires a value`);
262
+ }
263
+ return value;
264
+ }
265
+ function requireAnyValue(args, index, flag) {
266
+ const value = args[index];
267
+ if (!value) {
268
+ throw new Error(`${flag} requires a value`);
269
+ }
270
+ return value;
271
+ }
272
+ function printHelp() {
273
+ process.stdout.write(`pm-changelog
274
+
275
+ Generate CHANGELOG.md from pm-cli items.
276
+
277
+ Usage:
278
+ pm-changelog [options]
279
+
280
+ Options:
281
+ -o, --output <file> Write changelog to a file (default: CHANGELOG.md)
282
+ --stdout Print markdown instead of writing a file
283
+ --json Print a JSON summary for CI/runners
284
+ --check Do not write; exit 1 when output would change
285
+ --github-output Write summary fields to $GITHUB_OUTPUT
286
+ --github-step-summary Append generated markdown to $GITHUB_STEP_SUMMARY
287
+ -i, --input <file> Read pm JSON from a file instead of running pm
288
+ --stdin Read pm JSON from stdin
289
+ --pm-root <dir> pm project root for "pm --path <dir> list-all --json"
290
+ --pm-bin <file> pm executable to run (default: pm)
291
+ --pm-arg <arg> Extra argument passed before "list-all --json" (repeatable)
292
+ --pm-cwd <dir> Working directory for running pm
293
+ --title <text> Changelog title (default: Changelog)
294
+ --version <version> Version heading (default: Unreleased)
295
+ --date <date> Release date (default: today)
296
+ --since <date> Include items changed on or after this date
297
+ --until <date> Include items changed on or before this date
298
+ --status <list> Comma-separated statuses (default: closed)
299
+ --group-by <mode> version, release, or milestone (default: version)
300
+ --mode <mode> replace or prepend existing changelog (default: replace)
301
+ --include-empty Emit an empty release section when no items match
302
+ --include-links Include item URLs in generated entries (default: false)
303
+ `);
304
+ }
305
+ main().catch((error) => {
306
+ const message = error instanceof Error ? error.message : String(error);
307
+ console.error(message);
308
+ process.exit(1);
309
+ });
310
+ //# sourceMappingURL=cli.js.map