Skip to content

Commit 1f431ea

Browse files
committed
chore(scripts): 添加生成CHANGELOG.md的脚本
- 在 package.json 中新增 changelog 脚本命令 - 新增 scripts/generate-changelog.js,自动从GitHub Releases生成CHANGELOG.md - 脚本支持过滤预发布版本,仅列出稳定版本记录 - 根据PR标题的conventional commit类型分组变更项 - 支持dry-run模式打印输出、指定仓库和输出路径 - 新增单元测试覆盖脚本关键功能函数 - 生成的CHANGELOG.md符合Keep a Changelog规范,便于维护和展示历史版本日志
1 parent f4ded9a commit 1f431ea

4 files changed

Lines changed: 689 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# Changelog
2+
3+
All notable changes to [Deep Code](https://github.com/lessweb/deepcode-cli) are
4+
documented in this file.
5+
6+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
7+
and the project follows
8+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html). Only stable releases
9+
are listed; pre-releases are intentionally omitted.
10+
11+
> **This file is generated automatically** from
12+
> [GitHub Releases](https://github.com/lessweb/deepcode-cli/releases). Do not
13+
> edit it by hand — run `npm run changelog` to regenerate.
14+
15+
## [0.1.31](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.31) - 2026-06-16
16+
17+
### Other
18+
19+
- chore(deps-dev): bump esbuild and tsx ([#174](https://github.com/lessweb/deepcode-cli/pull/174))
20+
21+
## [0.1.30](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.30) - 2026-06-15
22+
23+
### Fixed
24+
25+
- mcp: fix Windows MCP spawn double-quoting that breaks all MCP servers ([#164](https://github.com/lessweb/deepcode-cli/pull/164))
26+
27+
## [0.1.29](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.29) - 2026-06-09
28+
29+
### Other
30+
31+
- 修复提示输入的换行、光标定位与 busy 状态显示 ([#171](https://github.com/lessweb/deepcode-cli/pull/171))
32+
33+
## [0.1.28](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.28) - 2026-06-05
34+
35+
### Added
36+
37+
- session name can be edited now ([#159](https://github.com/lessweb/deepcode-cli/pull/159))
38+
- ui: 优化 PromptInput 组件的光标显示与布局 ([#161](https://github.com/lessweb/deepcode-cli/pull/161))
39+
40+
### Changed
41+
42+
- extract OpenAI message converter from SessionManager ([#140](https://github.com/lessweb/deepcode-cli/pull/140))
43+
44+
## [0.1.27](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.27) - 2026-06-01
45+
46+
### Other
47+
48+
- chore(deps): update ink-gradient to 4.0.1 ([#135](https://github.com/lessweb/deepcode-cli/pull/135))
49+
- chore: 更新API Key not found时的文本显示 ([#137](https://github.com/lessweb/deepcode-cli/pull/137))
50+
51+
## [0.1.26](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.26) - 2026-05-29
52+
53+
### Changed
54+
55+
- ui: 重构代码结构,调整文件路径和导入引用 ([#122](https://github.com/lessweb/deepcode-cli/pull/122))
56+
- extract telemetry into separate module with enable/disable toggle ([#130](https://github.com/lessweb/deepcode-cli/pull/130))
57+
58+
### Fixed
59+
60+
- session: 修复会话清理内存泄漏并补充回归测试 ([#123](https://github.com/lessweb/deepcode-cli/pull/123))
61+
- prompt-buffer: 修正 getCurrentSlashToken 函数逻辑 ([#129](https://github.com/lessweb/deepcode-cli/pull/129))
62+
63+
### Other
64+
65+
- chore(deps): 更新 ink 依赖到 7.0.4 版本 ([#125](https://github.com/lessweb/deepcode-cli/pull/125))
66+
67+
## [0.1.25](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.25) - 2026-05-25
68+
69+
### Added
70+
71+
- ui: add bracketed paste with large-paste marker collapsing ([#102](https://github.com/lessweb/deepcode-cli/pull/102))
72+
- ui: 会话列表支持 Delete 键删除会话 ([#114](https://github.com/lessweb/deepcode-cli/pull/114))
73+
- ui: 增加会话删除及相关UI重置功能 ([#119](https://github.com/lessweb/deepcode-cli/pull/119))
74+
- markdown 表格闭合边框渲染 + CJK/emoji 宽度适配 ([#115](https://github.com/lessweb/deepcode-cli/pull/115))
75+
76+
### Fixed
77+
78+
- permission: 处理权限拒绝状态与界面更新 ([#120](https://github.com/lessweb/deepcode-cli/pull/120))
79+
80+
### Performance
81+
82+
- reuse OpenAI client and add undici keep-alive Agent with connection warmup ([#100](https://github.com/lessweb/deepcode-cli/pull/100))
83+
84+
### Documentation
85+
86+
- 更新扩展命令菜单说明和帮助文档 ([#109](https://github.com/lessweb/deepcode-cli/pull/109))
87+
88+
## [0.1.24](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.24) - 2026-05-21
89+
90+
### Added
91+
92+
- MCP 服务器手动重连功能 ([#84](https://github.com/lessweb/deepcode-cli/pull/84))
93+
94+
### Changed
95+
96+
- Extract dropdown components ([#97](https://github.com/lessweb/deepcode-cli/pull/97))
97+
- ui: 使用 resetPromptInput 简化撤销和回绕处理 ([#101](https://github.com/lessweb/deepcode-cli/pull/101))
98+
99+
### Fixed
100+
101+
- ui: 修正组件路径拼写错误 ([#93](https://github.com/lessweb/deepcode-cli/pull/93))
102+
- resolve CJK composition bug on iOS terminals (backspace packet splitting) ([#94](https://github.com/lessweb/deepcode-cli/pull/94))
103+
104+
## [0.1.23](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.23) - 2026-05-19
105+
106+
### Added
107+
108+
- 新增 `/raw` 命令交互与终端消息直出 ([#89](https://github.com/lessweb/deepcode-cli/pull/89))
109+
- notify: pass STATUS, FAIL_REASON, BODY, TITLE as env vars to notify hook ([#90](https://github.com/lessweb/deepcode-cli/pull/90))
110+
111+
## [0.1.22](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.22) - 2026-05-18
112+
113+
### Added
114+
115+
- add -p/--prompt flag to auto-submit prompt on launch ([#86](https://github.com/lessweb/deepcode-cli/pull/86))
116+
- Add Ctrl+O live process stdout viewer ([#75](https://github.com/lessweb/deepcode-cli/pull/75))
117+
118+
## [0.1.21](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.21) - 2026-05-16
119+
120+
### Added
121+
122+
- 添加 GitHub CI 工作流,支持多平台多版本自动验证 ([#76](https://github.com/lessweb/deepcode-cli/pull/76))
123+
124+
### Changed
125+
126+
- MCP 新增状态管理增强与 UI 可视化 ([#72](https://github.com/lessweb/deepcode-cli/pull/72))
127+
128+
### Fixed
129+
130+
- resolve Windows CI failures (CRLF, MCP spawn, cross-platform test runner) ([#77](https://github.com/lessweb/deepcode-cli/pull/77))
131+
132+
### Documentation
133+
134+
- add English translations of configuration.md and mcp.md ([#56](https://github.com/lessweb/deepcode-cli/pull/56))
135+
136+
### Other
137+
138+
- style(DropdownMenu): 调整内边距优化下拉菜单布局 ([#73](https://github.com/lessweb/deepcode-cli/pull/73))
139+
140+
## [0.1.20](https://github.com/lessweb/deepcode-cli/releases/tag/v0.1.20) - 2026-05-14
141+
142+
### Added
143+
144+
- add MCP (Model Context Protocol) support with /mcp command ([#48](https://github.com/lessweb/deepcode-cli/pull/48))
145+
- Handle Shift+Enter as prompt newline ([#52](https://github.com/lessweb/deepcode-cli/pull/52))
146+
- 新增 DropdownMenu 组件 ([#58](https://github.com/lessweb/deepcode-cli/pull/58))
147+
- Add prompt undo and redo shortcuts ([#59](https://github.com/lessweb/deepcode-cli/pull/59))
148+
- ui: 优化消息视图的布局和宽度自适应 ([#66](https://github.com/lessweb/deepcode-cli/pull/66))
149+
150+
### Changed
151+
152+
- session: 简化并统一会话系统消息的处理逻辑 ([#62](https://github.com/lessweb/deepcode-cli/pull/62))
153+
154+
### Fixed
155+
156+
- improve Windows Git Bash detection ([#55](https://github.com/lessweb/deepcode-cli/pull/55))
157+
- ui: 修正 reasoningEffort 显示逻辑 ([#63](https://github.com/lessweb/deepcode-cli/pull/63))
158+
- session: 修复系统消息可见性设置错误 ([#64](https://github.com/lessweb/deepcode-cli/pull/64))
159+
- filter image_url content from API messages for DeepSeek compatibility ([#51](https://github.com/lessweb/deepcode-cli/pull/51))

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"release:version": "node scripts/version.js",
3030
"prepare:package": "node scripts/prepare-package.js",
3131
"prepare:vscode": "node scripts/prepare-vscode.js",
32+
"changelog": "node scripts/generate-changelog.js",
3233
"prepare": "husky && npm run build && npm run bundle"
3334
},
3435
"devDependencies": {
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import { test } from "node:test";
2+
import assert from "node:assert/strict";
3+
import {
4+
categorize,
5+
isNoiseEntry,
6+
parseReleaseEntries,
7+
formatEntry,
8+
formatRelease,
9+
buildChangelog,
10+
toReleaseModel,
11+
selectStableReleases,
12+
parseJsonl,
13+
} from "../generate-changelog.js";
14+
15+
// ── categorize ───────────────────────────────────────────────────────────────
16+
17+
test("categorize extracts type, scope, and description", () => {
18+
const result = categorize("feat(core): add new feature");
19+
assert.deepEqual(result, {
20+
type: "feat",
21+
scope: "core",
22+
description: "add new feature",
23+
breaking: false,
24+
});
25+
});
26+
27+
test("categorize handles no scope", () => {
28+
const result = categorize("fix: resolve crash on startup");
29+
assert.deepEqual(result, {
30+
type: "fix",
31+
scope: null,
32+
description: "resolve crash on startup",
33+
breaking: false,
34+
});
35+
});
36+
37+
test("categorize detects breaking change marker", () => {
38+
const result = categorize("feat(api)!: change response format");
39+
assert.deepEqual(result, {
40+
type: "feat",
41+
scope: "api",
42+
description: "change response format",
43+
breaking: true,
44+
});
45+
});
46+
47+
test("categorize returns raw description for non-conventional titles", () => {
48+
const result = categorize("update readme");
49+
assert.deepEqual(result, {
50+
type: null,
51+
scope: null,
52+
description: "update readme",
53+
breaking: false,
54+
});
55+
});
56+
57+
// ── isNoiseEntry ─────────────────────────────────────────────────────────────
58+
59+
test("isNoiseEntry identifies release commits", () => {
60+
assert.equal(isNoiseEntry({ type: "chore", scope: "release" }), true);
61+
});
62+
63+
test("isNoiseEntry ignores non-release chores", () => {
64+
assert.equal(isNoiseEntry({ type: "chore", scope: "deps" }), false);
65+
});
66+
67+
test("isNoiseEntry ignores other types", () => {
68+
assert.equal(isNoiseEntry({ type: "feat", scope: null }), false);
69+
});
70+
71+
// ── parseReleaseEntries ──────────────────────────────────────────────────────
72+
73+
test("parseReleaseEntries extracts entries from GitHub body", () => {
74+
const body = [
75+
"* feat(ui): add dark mode by @alice in https://github.com/o/r/pull/42",
76+
"* fix(core): resolve crash by @bob in https://github.com/o/r/pull/43",
77+
"",
78+
"**Full Changelog**: https://github.com/o/r/compare/v1.0.0...v1.1.0",
79+
].join("\n");
80+
81+
const entries = parseReleaseEntries(body);
82+
assert.equal(entries.length, 2);
83+
assert.equal(entries[0].title, "feat(ui): add dark mode");
84+
assert.equal(entries[0].author, "alice");
85+
assert.equal(entries[0].prNumber, "42");
86+
assert.equal(entries[1].title, "fix(core): resolve crash");
87+
assert.equal(entries[1].author, "bob");
88+
});
89+
90+
test("parseReleaseEntries handles empty body", () => {
91+
assert.deepEqual(parseReleaseEntries(""), []);
92+
assert.deepEqual(parseReleaseEntries(null), []);
93+
});
94+
95+
test("parseReleaseEntries handles bot authors", () => {
96+
const body = "* chore(deps): bump x by @dependabot[bot] in https://github.com/o/r/pull/1";
97+
const entries = parseReleaseEntries(body);
98+
assert.equal(entries.length, 1);
99+
assert.equal(entries[0].author, "dependabot[bot]");
100+
});
101+
102+
// ── formatEntry ──────────────────────────────────────────────────────────────
103+
104+
test("formatEntry renders known type with scope", () => {
105+
const entry = { title: "feat(ui): add dark mode", prNumber: "42", prUrl: "https://github.com/o/r/pull/42" };
106+
assert.equal(formatEntry(entry), "- ui: add dark mode ([#42](https://github.com/o/r/pull/42))");
107+
});
108+
109+
test("formatEntry renders known type without scope", () => {
110+
const entry = { title: "fix: resolve crash", prNumber: "10", prUrl: "https://github.com/o/r/pull/10" };
111+
assert.equal(formatEntry(entry), "- resolve crash ([#10](https://github.com/o/r/pull/10))");
112+
});
113+
114+
test("formatEntry renders unknown type verbatim", () => {
115+
const entry = { title: "update readme", prNumber: "5", prUrl: "https://github.com/o/r/pull/5" };
116+
assert.equal(formatEntry(entry), "- update readme ([#5](https://github.com/o/r/pull/5))");
117+
});
118+
119+
test("formatEntry marks breaking changes", () => {
120+
const entry = { title: "feat(api)!: change format", prNumber: "99", prUrl: "https://github.com/o/r/pull/99" };
121+
assert.match(formatEntry(entry), /\*\*BREAKING\*\*/);
122+
});
123+
124+
// ── formatRelease ────────────────────────────────────────────────────────────
125+
126+
test("formatRelease groups entries by section", () => {
127+
const release = {
128+
version: "1.1.0",
129+
date: "2026-01-15",
130+
htmlUrl: "https://github.com/o/r/releases/tag/v1.1.0",
131+
entries: [
132+
{ title: "feat(ui): add dark mode", author: "alice", prUrl: "https://github.com/o/r/pull/1", prNumber: "1" },
133+
{ title: "fix: crash on start", author: "bob", prUrl: "https://github.com/o/r/pull/2", prNumber: "2" },
134+
{ title: "chore(release): v1.1.0", author: "bot", prUrl: "https://github.com/o/r/pull/3", prNumber: "3" },
135+
],
136+
};
137+
138+
const md = formatRelease(release);
139+
assert.match(md, /## \[1\.1\.0\]/);
140+
assert.match(md, /### Added/);
141+
assert.match(md, /### Fixed/);
142+
assert.doesNotMatch(md, /chore\(release\)/);
143+
});
144+
145+
// ── buildChangelog ───────────────────────────────────────────────────────────
146+
147+
test("buildChangelog produces valid markdown with header", () => {
148+
const releases = [
149+
{
150+
version: "1.0.0",
151+
date: "2026-01-01",
152+
htmlUrl: "https://github.com/o/r/releases/tag/v1.0.0",
153+
entries: [
154+
{ title: "feat: initial release", author: "dev", prUrl: "https://github.com/o/r/pull/1", prNumber: "1" },
155+
],
156+
},
157+
];
158+
159+
const changelog = buildChangelog(releases);
160+
assert.match(changelog, /# Changelog/);
161+
assert.match(changelog, /## \[1\.0\.0\]/);
162+
assert.match(changelog, /### Added/);
163+
assert.ok(changelog.endsWith("\n"));
164+
});
165+
166+
// ── toReleaseModel ───────────────────────────────────────────────────────────
167+
168+
test("toReleaseModel parses stable tag", () => {
169+
const raw = {
170+
tag: "v1.2.3",
171+
date: "2026-03-15T10:00:00Z",
172+
url: "https://github.com/o/r/releases/tag/v1.2.3",
173+
body: "* feat: new thing by @user in https://github.com/o/r/pull/1",
174+
};
175+
const model = toReleaseModel(raw);
176+
assert.equal(model.version, "1.2.3");
177+
assert.equal(model.date, "2026-03-15");
178+
assert.equal(model.entries.length, 1);
179+
});
180+
181+
test("toReleaseModel returns null version for unstable tag", () => {
182+
const raw = { tag: "v1.0.0-beta.1", date: "2026-01-01", url: "", body: "" };
183+
const model = toReleaseModel(raw);
184+
assert.equal(model.version, null);
185+
});
186+
187+
// ── selectStableReleases ─────────────────────────────────────────────────────
188+
189+
test("selectStableReleases filters out pre-releases and drafts", () => {
190+
const raw = [
191+
{ tag: "v1.2.0", date: "2026-03-01", prerelease: false, draft: false, url: "", body: "" },
192+
{ tag: "v1.1.0-beta.1", date: "2026-02-01", prerelease: true, draft: false, url: "", body: "" },
193+
{ tag: "v1.0.0", date: "2026-01-01", prerelease: false, draft: false, url: "", body: "" },
194+
{ tag: "v2.0.0-draft", date: "2026-04-01", prerelease: false, draft: true, url: "", body: "" },
195+
];
196+
const stable = selectStableReleases(raw);
197+
assert.equal(stable.length, 2);
198+
assert.equal(stable[0].version, "1.2.0");
199+
assert.equal(stable[1].version, "1.0.0");
200+
});
201+
202+
// ── parseJsonl ───────────────────────────────────────────────────────────────
203+
204+
test("parseJsonl parses newline-delimited JSON", () => {
205+
const jsonl = '{"a":1}\n{"b":2}\n';
206+
const result = parseJsonl(jsonl);
207+
assert.deepEqual(result, [{ a: 1 }, { b: 2 }]);
208+
});
209+
210+
test("parseJsonl skips empty lines", () => {
211+
const jsonl = '{"a":1}\n\n \n{"b":2}\n';
212+
const result = parseJsonl(jsonl);
213+
assert.equal(result.length, 2);
214+
});

0 commit comments

Comments
 (0)