Skip to content

Commit a064efe

Browse files
committed
feat: enhance archive command to support summary option and update changelog during archiving
1 parent a6d9615 commit a064efe

23 files changed

Lines changed: 527 additions & 41 deletions

File tree

apps/cli/src/commands/archive.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,26 @@ export function registerArchiveCommand(program: Command): void {
1616
.description('归档一个已完成的变更')
1717
.argument('<name>', '变更名称')
1818
.option('--json', '输出 JSON 格式')
19-
.action(async (name: string, options: { json?: boolean }) => {
20-
try {
21-
const { changes } = createContext()
22-
const result = await changes.archive(name)
19+
.option('--summary <text>', '变更摘要(写入 changelog)')
20+
.action(
21+
async (name: string, options: { json?: boolean; summary?: string }) => {
22+
try {
23+
const { changes } = createContext()
24+
const result = await changes.archive(name, {
25+
summary: options.summary,
26+
})
2327

24-
if (options.json) {
25-
console.log(JSON.stringify(result, null, 2))
26-
return
27-
}
28+
if (options.json) {
29+
console.log(JSON.stringify(result, null, 2))
30+
return
31+
}
2832

29-
p.intro('MarchenSpec CLI')
30-
p.log.success(`变更 "${name}" 归档成功`)
31-
p.outro(`运行 marchen list 查看剩余变更`)
32-
} catch (error) {
33-
handleError(error)
34-
}
35-
})
33+
p.intro('MarchenSpec CLI')
34+
p.log.success(`变更 "${name}" 归档成功`)
35+
p.outro(`运行 marchen list 查看剩余变更`)
36+
} catch (error) {
37+
handleError(error)
38+
}
39+
},
40+
)
3641
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-04-19
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
## Context
2+
3+
MarchenSpec 的 archive 机制将完成的变更移动到 `marchen/archive/` 目录,但归档后没有任何"浓缩"的知识出口。explore skill 启动时只检查当前 open 变更,无法快速获取历史变更信息。需要在 archive 流程中自动沉淀一行摘要到 `changelog.md`,供 explore 阶段作为上下文索引。
4+
5+
当前 archive 流程:读取 metadata → 更新 status → moveDir → return ArchiveResult。
6+
7+
## Goals / Non-Goals
8+
9+
**Goals:**
10+
- archive 时自动写入一行摘要到 `marchen/changelog.md`
11+
- 支持通过 `--summary` 传入摘要文本(CLI 直接使用或 skill 传入 AI 生成的摘要)
12+
- 没有 summary 时仍写入条目(只有名称和链接,无描述)
13+
- init 时创建空 changelog.md
14+
- explore skill 启动时读取 changelog.md 作为历史上下文
15+
16+
**Non-Goals:**
17+
- 不做 RAG 或向量检索(changelog 规模远不需要)
18+
- 不恢复主 specs 机制
19+
- 不自动从 proposal 提取摘要(CLI 层不做 AI 调用,摘要生成由 skill 层负责)
20+
21+
## Decisions
22+
23+
### 1. changelog.md 格式:纯文本一行一条
24+
25+
每条 entry 格式:
26+
```
27+
- YYYY-MM-DD: [change-name](./archive/YYYY-MM-DD-change-name/) — 摘要文本
28+
```
29+
30+
无 summary 时:
31+
```
32+
- YYYY-MM-DD: [change-name](./archive/YYYY-MM-DD-change-name/)
33+
```
34+
35+
理由:格式确定性高,由代码模板拼接,不需要解析 markdown AST。AI 全量读取后自行判断相关性。
36+
37+
### 2. 写入方式:fs 包新增 appendFile
38+
39+
使用 `appendFile` 而非 `readFile + writeFile`,语义更清晰,且 Node.js 原生支持。
40+
41+
### 3. summary 来源:CLI `--summary` 参数
42+
43+
CLI 层只负责接收和传递 summary 文本,不做任何智能提取。两条路径:
44+
- 直接 CLI:用户手动传 `--summary "..."`,或不传(只写名称)
45+
- archive skill:AI 读 proposal 生成摘要后传给 `--summary`
46+
47+
### 4. changelog.md 位置:`marchen/changelog.md`
48+
49+
`changes/``archive/` 同级,属于 spec 系统的一部分。entry 中的链接使用相对路径 `./archive/...`
50+
51+
### 5. explore skill 消费方式:直接 cat
52+
53+
explore skill 模板中加一步 `cat marchen/changelog.md`,AI 全量读取后按需深入相关 archive。不需要新增 CLI 命令。
54+
55+
## Risks / Trade-offs
56+
57+
- [changelog.md 不存在] → archive 时先检查文件是否存在,不存在则创建(兼容未 re-init 的旧项目)
58+
- [summary 含换行符] → 写入前 trim 并替换换行为空格
59+
- [changelog 增长] → 当前 18 条,预计年增 ~100 条,~10KB,AI context 完全可承受。未来如需可按年分组,但现在不做
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
## Why
2+
3+
archive 后知识"沉没"——18 个 archive 目录、70+ 个 markdown 文件,explore 时无法快速获取项目做过什么决策。需要一个轻量索引,让 AI 在 explore 阶段能快速定位相关历史变更并按需深入。
4+
5+
## What Changes
6+
7+
- `Workspace.initialize()` 创建空 `changelog.md`(标题 + 空内容)
8+
- `ChangeManager.archive()` 归档后自动 append 一行摘要到 `changelog.md`
9+
- CLI `archive` 命令新增 `--summary` 选项,传递摘要文本
10+
- fs 包新增 `appendFile()` 函数
11+
- explore skill 模板增加"读取 changelog.md"步骤
12+
- archive skill 模板增加"AI 生成摘要传给 --summary"步骤
13+
14+
## Capabilities
15+
16+
### New Capabilities
17+
18+
- `changelog-generation`: archive 时自动写入 changelog.md 的机制(格式、写入逻辑、init 创建)
19+
20+
### Modified Capabilities
21+
22+
- `change-creation`: `marchen init` 时额外创建 changelog.md
23+
- `archive-command`: archive 命令支持 --summary 参数,归档后写入 changelog
24+
25+
## Impact
26+
27+
- `packages/fs` — 新增 `appendFile` 函数
28+
- `packages/shared` — 新增 `ArchiveOptions` 类型
29+
- `packages/core``ChangeManager.archive()` 签名变更 + changelog 写入逻辑;`Workspace.initialize()` 创建 changelog.md
30+
- `apps/cli` — archive 命令加 `--summary` 选项
31+
- `packages/config/templates/skills/` — explore.md 和 archive.md 模板更新
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## MODIFIED Requirements
2+
3+
### 需求: 归档变更
4+
5+
系统 SHALL 将已完成的变更移动到 archive 目录,更新元数据,并写入 changelog 条目。
6+
7+
#### 场景: 带 summary 归档
8+
9+
- **WHEN** 用户执行 `marchen archive <name> --summary "摘要文本"`
10+
- **THEN** 移动变更到 `archive/YYYY-MM-DD-<name>/`,更新 metadata status 为 archived,追加带摘要的条目到 changelog.md
11+
12+
#### 场景: 不带 summary 归档
13+
14+
- **WHEN** 用户执行 `marchen archive <name>`
15+
- **THEN** 移动变更到 `archive/YYYY-MM-DD-<name>/`,更新 metadata status 为 archived,追加仅含名称链接的条目到 changelog.md
16+
17+
#### 场景: JSON 输出
18+
19+
- **WHEN** 用户执行 `marchen archive <name> --json`
20+
- **THEN** 输出 JSON 格式的 ArchiveResult
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## MODIFIED Requirements
2+
3+
### 需求: 工作区初始化
4+
5+
系统在初始化 MarchenSpec 工作区时 SHALL 创建标准目录结构、默认配置、skill/command 文件,以及空的 changelog.md。
6+
7+
#### 场景: 完整初始化
8+
9+
- **WHEN** 用户执行 `marchen init`
10+
- **THEN** 创建 `marchen/` 目录结构(changes/、archive/、config.yaml、changelog.md)并生成 `.claude/skills/``.claude/commands/` 文件
11+
12+
#### 场景: changelog.md 已存在时初始化
13+
14+
- **WHEN** 用户执行 `marchen init --force``marchen/changelog.md` 已存在
15+
- **THEN** 不覆盖已有的 `changelog.md`,保留历史记录
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
## ADDED Requirements
2+
3+
### 需求: changelog 文件初始化
4+
5+
系统在 `marchen init` 时 SHALL 创建 `marchen/changelog.md` 文件,内容为 `# 变更日志\n`
6+
7+
#### 场景: 首次初始化
8+
9+
- **WHEN** 用户执行 `marchen init`
10+
- **THEN**`marchen/` 目录下创建 `changelog.md`,内容为标题行 `# 变更日志`
11+
12+
#### 场景: 重复初始化
13+
14+
- **WHEN** 用户执行 `marchen init --force``changelog.md` 已存在
15+
- **THEN** 不覆盖已有的 `changelog.md`
16+
17+
### 需求: archive 时写入 changelog 条目
18+
19+
系统在归档变更时 SHALL 自动追加一行条目到 `marchen/changelog.md`
20+
21+
#### 场景: 带 summary 归档
22+
23+
- **WHEN** 执行 `marchen archive my-feature --summary "实现了用户认证"`
24+
- **THEN** 追加 `- 2026-04-19: [my-feature](./archive/2026-04-19-my-feature/) — 实现了用户认证` 到 changelog.md 末尾
25+
26+
#### 场景: 不带 summary 归档
27+
28+
- **WHEN** 执行 `marchen archive my-feature`(无 --summary)
29+
- **THEN** 追加 `- 2026-04-19: [my-feature](./archive/2026-04-19-my-feature/)` 到 changelog.md 末尾
30+
31+
#### 场景: changelog.md 不存在时归档
32+
33+
- **WHEN** 执行 `marchen archive``marchen/changelog.md` 不存在(旧项目未 re-init)
34+
- **THEN** 自动创建 `changelog.md`(含标题行)后再追加条目
35+
36+
### 需求: changelog 条目格式
37+
38+
每条 changelog entry SHALL 遵循固定格式。
39+
40+
#### 场景: 格式规范
41+
42+
- **WHEN** 写入一条 changelog entry
43+
- **THEN** 格式为 `- YYYY-MM-DD: [<name>](./archive/YYYY-MM-DD-<name>/) — <summary>`,其中日期从 archivedAt 截取前 10 位,name 为变更名称,summary 为可选摘要
44+
45+
#### 场景: summary 含换行符
46+
47+
- **WHEN** summary 文本包含换行符
48+
- **THEN** 系统 SHALL 将换行符替换为空格,确保 entry 为单行
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
## 1. fs 包扩展
2+
3+
- [x] 1.1 在 `packages/fs/src/file.ts` 新增 `appendFile(path, content)` 函数
4+
- [x] 1.2 在 `packages/fs/src/index.ts` 导出 `appendFile`
5+
- [x] 1.3 为 `appendFile` 编写单元测试
6+
7+
## 2. shared 包类型扩展
8+
9+
- [x] 2.1 在 `packages/shared/src/types.ts` 新增 `ArchiveOptions` 接口(含可选 `summary` 字段)
10+
- [x] 2.2 导出 `ArchiveOptions` 类型
11+
12+
## 3. core 包 — changelog 写入逻辑
13+
14+
- [x] 3.1 `Workspace` 类新增 `changelogPath` 只读属性(`join(specDir, 'changelog.md')`
15+
- [x] 3.2 `Workspace.initialize()` 中创建空 `changelog.md`(内容 `# 变更日志\n`),已存在时跳过
16+
- [x] 3.3 `ChangeManager.archive()` 方法签名增加可选 `options?: ArchiveOptions` 参数
17+
- [x] 3.4 archive 方法末尾:拼接 entry 字符串,调用 `appendFile` 写入 changelog.md(不存在时先创建)
18+
- [x] 3.5 summary 写入前 trim 并替换换行为空格
19+
- [x] 3.6 为 changelog 写入逻辑编写单元测试
20+
21+
## 4. CLI — archive 命令扩展
22+
23+
- [x] 4.1 `apps/cli/src/commands/archive.ts` 新增 `--summary <text>` 选项
24+
- [x] 4.2 将 summary 传递给 `changes.archive(name, { summary })`
25+
26+
## 5. Skill 模板更新
27+
28+
- [x] 5.1 更新 `packages/config/templates/skills/archive.md`:归档前读取 proposal.md 生成一句话摘要,传给 `--summary`
29+
- [x] 5.2 更新 `packages/config/templates/skills/explore.md`:检查上下文时加 `cat marchen/changelog.md` 步骤
30+
- [x] 5.3 运行 `pnpm generate` 重新生成 codegen 文件

openspec/specs/archive-skill/spec.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# archive-skill
22

3-
## ADDED Requirements
3+
## 需求
44

55
### 需求: marchen init SHALL 生成 archive skill 模板
66

@@ -38,6 +38,25 @@ skill MUST 调用 `marchen status <name> --json` 检查 artifact 和 task 完成
3838
- **AND** 用 AskUserQuestion 确认是否继续
3939
- **AND** 用户确认后执行归档,不阻塞
4040

41+
### 需求: 归档变更
42+
43+
系统 SHALL 将已完成的变更移动到 archive 目录,更新元数据,并写入 changelog 条目。
44+
45+
#### 场景: 带 summary 归档
46+
47+
- **WHEN** 用户执行 `marchen archive <name> --summary "摘要文本"`
48+
- **THEN** 移动变更到 `archive/YYYY-MM-DD-<name>/`,更新 metadata status 为 archived,追加带摘要的条目到 changelog.md
49+
50+
#### 场景: 不带 summary 归档
51+
52+
- **WHEN** 用户执行 `marchen archive <name>`
53+
- **THEN** 移动变更到 `archive/YYYY-MM-DD-<name>/`,更新 metadata status 为 archived,追加仅含名称链接的条目到 changelog.md
54+
55+
#### 场景: JSON 输出
56+
57+
- **WHEN** 用户执行 `marchen archive <name> --json`
58+
- **THEN** 输出 JSON 格式的 ArchiveResult
59+
4160
### 需求: archive skill SHALL 在未提供名称时让用户选择
4261

4362
skill MUST 调用 `marchen list --json` 获取 open 变更列表,用 AskUserQuestion 让用户选择。

openspec/specs/change-creation/spec.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ archive 方法 MUST 返回 `ArchiveResult` 对象,包含归档后的路径和
5454
- **WHEN** 调用 `archive('my-feature')``archive/YYYY-MM-DD-my-feature` 已存在
5555
- **THEN** 抛出 `ValidationError`,提示目标目录已存在
5656

57+
### 需求: 工作区初始化
58+
59+
系统在初始化 MarchenSpec 工作区时 SHALL 创建标准目录结构、默认配置、skill/command 文件,以及空的 changelog.md。
60+
61+
#### 场景: 完整初始化
62+
63+
- **WHEN** 用户执行 `marchen init`
64+
- **THEN** 创建 `marchen/` 目录结构(changes/、archive/、config.yaml、changelog.md)并生成 `.claude/skills/``.claude/commands/` 文件
65+
66+
#### 场景: changelog.md 已存在时初始化
67+
68+
- **WHEN** 用户执行 `marchen init --force``marchen/changelog.md` 已存在
69+
- **THEN** 不覆盖已有的 `changelog.md`,保留历史记录
70+
5771
### 需求: `marchen archive` SHALL 支持 `--json` 输出
5872

5973
命令 MUST 支持 `--json` 可选参数,输出归档结果的 JSON 格式。

0 commit comments

Comments
 (0)