Skip to content

Commit 698e9a2

Browse files
fix: drop the blank release-notes row and cut CHANGELOG on publish (#7)
GitHub treated the empty table header as a blank row above Plugin. semantic-release now promotes Keep a Changelog Unreleased into the released version, and the post-release sync PR commits CHANGELOG.md. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent e33cf67 commit 698e9a2

13 files changed

Lines changed: 200 additions & 12 deletions

.github/workflows/release.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
run: |
4949
git checkout -- package.json manifest.json 2>/dev/null || true
5050
bun run scripts/sync-release-manifests.ts
51-
if [[ -z "$(git status --porcelain -- package.json manifest.json)" ]]; then
51+
if [[ -z "$(git status --porcelain -- package.json manifest.json CHANGELOG.md)" ]]; then
5252
echo "manifests already match the latest tag"
5353
exit 0
5454
fi
@@ -58,11 +58,11 @@ jobs:
5858
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
5959
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
6060
git checkout -b "${BRANCH}"
61-
git add package.json manifest.json
61+
git add package.json manifest.json CHANGELOG.md
6262
git commit -m "chore(release): sync manifests to v${VERSION}"
6363
git push -u origin "${BRANCH}"
6464
gh pr create \
6565
--title "chore(release): sync manifests to v${VERSION}" \
66-
--body "Automated post-release manifest alignment. Sets package.json and manifest.json to the released ${VERSION}." \
66+
--body "Automated post-release manifest alignment. Sets package.json, manifest.json, and CHANGELOG.md to the released ${VERSION}." \
6767
--base main --head "${BRANCH}"
6868
gh pr merge --auto --squash --delete-branch || echo "auto-merge unavailable — merge the sync PR after CI is green"

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- semantic-release now cuts Keep a Changelog `[Unreleased]` into `## [version]` on publish and includes `CHANGELOG.md` in the post-release sync PR.
13+
14+
### Fixed
15+
16+
- Catalog GitHub release notes table no longer renders a blank header row above Plugin.
17+
- Credits sentence no longer mentions Command Code wiring or restates MIT copyright.
18+
19+
## [0.6.0] - 2026-08-28
20+
21+
### Changed
22+
23+
- Renamed the published package and GitHub repository to `@brainervirus/opencode-commandcode`.
24+
25+
## [0.5.1] - 2026-08-28
26+
27+
### Fixed
28+
29+
- Price Command Code free SKUs at `$0` before models.dev, and fill remaining paid gaps from models.dev.
30+
31+
## [0.5.0] - 2026-08-28
32+
33+
### Added
34+
1235
- GitHub flow: PR-gated `main`, CI matrix (`check (test|typecheck|pack)`), semantic-release on merge, catalog updates as PRs.
1336
- Release notes open with a catalog summary table and collapsed lists of fallback / unpriced models.
1437
- `manifest.json` describing the bundled catalog (status, cost sources, command-code version).

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Previously published as `@brainervirus/commandcode-go-opencode-provider`. Use th
1212

1313
## Credits
1414

15-
This package is based on **[FanFan4204/opencode-commandcode-provider](https://github.com/FanFan4204/opencode-commandcode-provider)**. That work started from **[brent-weatherall/opencode-commandcode-provider](https://github.com/brent-weatherall/opencode-commandcode-provider)** by **[Brent Weatherall](https://github.com/brent-weatherall)**. Thank you both — FanFan for the OpenCode provider this repo continues, and Brent for the original plugin, catalog extraction, and Command Code wiring. The license remains MIT; copyright stays with Brent Weatherall.
15+
This package is based on **[FanFan4204/opencode-commandcode-provider](https://github.com/FanFan4204/opencode-commandcode-provider)**. That work started from **[brent-weatherall/opencode-commandcode-provider](https://github.com/brent-weatherall/opencode-commandcode-provider)** by **[Brent Weatherall](https://github.com/brent-weatherall)**. Thank you both — FanFan for the OpenCode provider this repo continues, and Brent for the original plugin, catalog extraction.
1616

1717
### What this package adds
1818

release.config.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module.exports = {
44
"@semantic-release/commit-analyzer",
55
"./scripts/semantic-release-catalog-notes.cjs",
66
"@semantic-release/release-notes-generator",
7+
"./scripts/semantic-release-changelog.cjs",
78
"@semantic-release/npm",
89
"@semantic-release/github",
910
],

scripts/catalog-release-notes.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ import type { PricedModel } from "../src/catalog-release-notes.js";
88
const ROOT = join(import.meta.dir, "..");
99
const manifest = JSON.parse(readFileSync(join(ROOT, "manifest.json"), "utf-8")) as CatalogManifest;
1010
const models = JSON.parse(readFileSync(join(ROOT, "models.json"), "utf-8")) as PricedModel[];
11-
process.stdout.write(catalogReleaseNotesFromFiles({ manifest, models }));
11+
const pluginVersion = process.argv[2];
12+
process.stdout.write(catalogReleaseNotesFromFiles({ manifest, models, pluginVersion }));

scripts/cut-changelog.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env bun
2+
import { readFileSync, writeFileSync } from "node:fs";
3+
import { join } from "node:path";
4+
import { cutKeepAChangelog } from "../src/cut-changelog.ts";
5+
6+
const version = process.argv[2];
7+
const date = process.argv[3] ?? new Date().toISOString().slice(0, 10);
8+
if (!version) throw new Error("usage: cut-changelog.ts <version> [YYYY-MM-DD]");
9+
const path = join(import.meta.dir, "..", "CHANGELOG.md");
10+
writeFileSync(path, cutKeepAChangelog(readFileSync(path, "utf8"), version, date));

scripts/semantic-release-catalog-notes.cjs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ const { execFileSync } = require("node:child_process");
22
const { join } = require("node:path");
33

44
module.exports = {
5-
generateNotes() {
6-
return execFileSync("bun", [join(__dirname, "catalog-release-notes.ts")], {
7-
encoding: "utf8",
8-
});
5+
generateNotes(_config, context) {
6+
const args = [join(__dirname, "catalog-release-notes.ts")];
7+
if (context.nextRelease?.version) args.push(context.nextRelease.version);
8+
return execFileSync("bun", args, { encoding: "utf8" });
99
},
1010
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const { execFileSync } = require("node:child_process");
2+
const { join } = require("node:path");
3+
4+
// Keep a Changelog: promote ## [Unreleased] into ## [version] - date during
5+
// prepare. The post-release sync PR commits CHANGELOG.md (protected main).
6+
module.exports = {
7+
prepare(_config, context) {
8+
const version = context.nextRelease.version;
9+
const date = new Date().toISOString().slice(0, 10);
10+
execFileSync("bun", [join(__dirname, "cut-changelog.ts"), version, date], {
11+
stdio: "inherit",
12+
});
13+
},
14+
};

src/catalog-release-notes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,8 @@ export function renderCatalogReleaseNotes(input: CatalogReleaseInput): string {
8989
const sections = [
9090
headline(input.status),
9191
"",
92-
"| | |",
93-
"| --- | --- |",
9492
`| Plugin | ${input.pluginVersion} |`,
93+
"| --- | --- |",
9594
`| Command Code | ${input.commandCodeVersion} |`,
9695
`| Models | ${input.modelCount} (${input.reasoningModelCount} with reasoning) |`,
9796
`| Prices | ${priceSummary(input.costSources)} |`,
@@ -157,9 +156,10 @@ export function renderCatalogReleaseNotes(input: CatalogReleaseInput): string {
157156
export function catalogReleaseNotesFromFiles(input: {
158157
manifest: CatalogManifest;
159158
models: PricedModel[];
159+
pluginVersion?: string;
160160
}): string {
161161
return renderCatalogReleaseNotes({
162-
pluginVersion: input.manifest.pluginVersion,
162+
pluginVersion: input.pluginVersion ?? input.manifest.pluginVersion,
163163
commandCodeVersion: input.manifest.commandCodeVersion,
164164
modelCount: input.manifest.modelCount,
165165
reasoningModelCount: input.manifest.reasoningModelCount,

src/cut-changelog.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const UNRELEASED = /^## \[Unreleased\]\s*$/;
2+
const VERSION = /^## \[/;
3+
4+
export function cutKeepAChangelog(markdown: string, version: string, date: string): string {
5+
if (new RegExp(`^## \\[${escapeRegExp(version)}\\]`, "m").test(markdown)) return markdown;
6+
7+
const lines = markdown.split(/(?<=\n)/);
8+
let start: number | null = null;
9+
for (let i = 0; i < lines.length; i++) {
10+
const line = lines[i];
11+
if (line !== undefined && UNRELEASED.test(line.replace(/\n$/, ""))) {
12+
start = i;
13+
break;
14+
}
15+
}
16+
if (start === null) throw new Error("CHANGELOG.md has no ## [Unreleased] heading");
17+
18+
let end = lines.length;
19+
for (let j = start + 1; j < lines.length; j++) {
20+
const line = lines[j];
21+
if (line === undefined) continue;
22+
const stripped = line.replace(/\n$/, "");
23+
if (VERSION.test(stripped) && !UNRELEASED.test(stripped)) {
24+
end = j;
25+
break;
26+
}
27+
}
28+
29+
const before = lines.slice(0, start + 1).join("");
30+
const body = lines
31+
.slice(start + 1, end)
32+
.join("")
33+
.replace(/^\n+/, "\n");
34+
const after = lines.slice(end).join("");
35+
return `${before}\n## [${version}] - ${date}\n${body}${after}`;
36+
}
37+
38+
function escapeRegExp(value: string): string {
39+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
40+
}

0 commit comments

Comments
 (0)