marchen 0.8.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/LICENSE +21 -0
- package/README.md +36 -0
- package/dist/index.d.mts +13 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1954 -0
- package/dist/index.mjs.map +1 -0
- package/dist/search-manager-zPFpsFnI.mjs +3 -0
- package/dist/search-manager-zPFpsFnI.mjs.map +1 -0
- package/package.json +38 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1954 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{a as e,c as t,i as n,o as r,r as i,s as a,t as o}from"./search-manager-zPFpsFnI.mjs";import{createRequire as s}from"node:module";import{Command as c}from"commander";import*as l from"@clack/prompts";import{dirname as u,join as d,resolve as f}from"node:path";import{promises as p}from"node:fs";import m from"js-yaml";import h from"picocolors";var g=class extends Error{constructor(e){super(e),this.name=`MarchenError`}},_=class extends g{constructor(e){super(e),this.name=`ValidationError`}},v=class extends g{constructor(e,t){super(e),this.hint=t,this.name=`StateError`}},y=class extends g{constructor(e,t,n){super(e),this.path=t,this.cause=n,this.name=`FileSystemError`}};const b={apply:{fileName:`apply.md`,content:`---
|
|
3
|
+
name: "Marchen: Apply"
|
|
4
|
+
description: 按变更的 tasks.md 逐个实现任务
|
|
5
|
+
category: Workflow
|
|
6
|
+
tags: [workflow, implementation]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
按变更的 tasks.md 逐个实现任务,完成后勾选 checkbox。
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
**输入**:\`/marchen:apply\` 后面跟变更名称,或省略自动推断。
|
|
14
|
+
|
|
15
|
+
**流程**
|
|
16
|
+
|
|
17
|
+
1. **选择变更**
|
|
18
|
+
|
|
19
|
+
有名称就用,没有则:
|
|
20
|
+
- 从对话上下文推断
|
|
21
|
+
- 只有一个 open 变更时自动选择
|
|
22
|
+
- 多个变更时 \`marchen list --json\` + **AskUserQuestion** 让用户选
|
|
23
|
+
|
|
24
|
+
显示:"使用变更: \`<name>\`"
|
|
25
|
+
|
|
26
|
+
2. **获取实现指令**
|
|
27
|
+
|
|
28
|
+
\`\`\`bash
|
|
29
|
+
marchen instructions <name> apply --json
|
|
30
|
+
\`\`\`
|
|
31
|
+
|
|
32
|
+
返回 JSON 包含:
|
|
33
|
+
- \`state\`:\`"ready"\` / \`"blocked"\` / \`"all_done"\`
|
|
34
|
+
- \`progress\`:\`{ total, completed, remaining }\`
|
|
35
|
+
- \`context\`:所有 artifact 的信息数组,每项包含 \`id\`、\`status\`、\`path\`、\`content\`
|
|
36
|
+
- \`instruction\`:实现指引
|
|
37
|
+
- \`changeDir\`:变更目录绝对路径
|
|
38
|
+
|
|
39
|
+
根据 \`state\` 处理:
|
|
40
|
+
- \`"blocked"\` → 提示先完成 artifacts(\`/marchen:propose\`)
|
|
41
|
+
- \`"all_done"\` → 提示归档(\`/marchen:archive\`)
|
|
42
|
+
- \`"ready"\` → 继续
|
|
43
|
+
|
|
44
|
+
3. **显示进度**
|
|
45
|
+
|
|
46
|
+
从返回的 JSON 读取并显示:
|
|
47
|
+
"变更: \`<name>\` | 进度: N/M | 下一个: <第一个未完成任务的描述>"
|
|
48
|
+
|
|
49
|
+
4. **逐个实现任务**
|
|
50
|
+
|
|
51
|
+
从 \`context\` 中读取所有 artifact 内容作为上下文。
|
|
52
|
+
|
|
53
|
+
对每个未完成任务:
|
|
54
|
+
- 显示 "任务 N/M: <描述>"
|
|
55
|
+
- 实现代码改动
|
|
56
|
+
- 在 tasks.md 中勾选:\`- [ ]\` → \`- [x]\`
|
|
57
|
+
文件路径:\`<changeDir>/tasks.md\`
|
|
58
|
+
- 显示 "✓ 完成"
|
|
59
|
+
- 继续下一个
|
|
60
|
+
|
|
61
|
+
**暂停条件:**
|
|
62
|
+
- 任务不清晰 → 询问用户
|
|
63
|
+
- 发现设计问题 → 建议更新 artifact
|
|
64
|
+
- 遇到错误或阻塞 → 报告并等待
|
|
65
|
+
- 用户中断
|
|
66
|
+
|
|
67
|
+
5. **显示结果**
|
|
68
|
+
|
|
69
|
+
全部完成时:
|
|
70
|
+
"全部完成 (N/N),可以用 \`/marchen:review\` 检查实现,或直接 \`/marchen:archive\` 归档。"
|
|
71
|
+
|
|
72
|
+
暂停时:
|
|
73
|
+
"暂停于任务 N/M: <原因>"
|
|
74
|
+
|
|
75
|
+
**护栏**
|
|
76
|
+
|
|
77
|
+
- 实现前必须读 context 中的 artifact 内容
|
|
78
|
+
- 每完成一个任务立即勾选 checkbox,不要攒着
|
|
79
|
+
- 改动最小化,只做任务要求的事
|
|
80
|
+
- 不确定就暂停问,不要猜
|
|
81
|
+
- 如果实现过程中遇到不确定的设计决策,可以用 \`marchen search "<关键词>" --json\` 搜索历史变更中的相关方案
|
|
82
|
+
- 使用 AskUserQuestion 时,选项不超过 4 个
|
|
83
|
+
- \`instruction\` 是给你的指引,不要原样复制到代码注释中
|
|
84
|
+
`},archive:{fileName:`archive.md`,content:`---
|
|
85
|
+
name: "Marchen: Archive"
|
|
86
|
+
description: 归档已完成的变更
|
|
87
|
+
category: Workflow
|
|
88
|
+
tags: [workflow, archive]
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
归档已完成的变更,检查完成度后执行归档。
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
**输入**:用户的请求可包含变更名称(如 \`/marchen:archive add-auth\`),也可不带。
|
|
96
|
+
|
|
97
|
+
**流程**
|
|
98
|
+
|
|
99
|
+
1. **确定变更名称**
|
|
100
|
+
|
|
101
|
+
有名称就用,没有则:
|
|
102
|
+
- 从对话上下文推断
|
|
103
|
+
- 只有一个 open 变更时自动选择
|
|
104
|
+
- 多个变更时 \`marchen list --json\` + **AskUserQuestion** 让用户选
|
|
105
|
+
|
|
106
|
+
**重要**:不要猜测或自动选择,必须让用户确认。
|
|
107
|
+
|
|
108
|
+
2. **检查完成度**
|
|
109
|
+
|
|
110
|
+
\`\`\`bash
|
|
111
|
+
marchen status <name> --json
|
|
112
|
+
\`\`\`
|
|
113
|
+
|
|
114
|
+
解析 JSON,检查:
|
|
115
|
+
- \`artifacts\`:每个 artifact 的 \`status\` 是否为 \`filled\`
|
|
116
|
+
- \`tasks.completed\` vs \`tasks.total\`(\`tasks\` 为 null 时视为无任务,跳过检查)
|
|
117
|
+
|
|
118
|
+
**如果全部完成:** 直接进入下一步。
|
|
119
|
+
|
|
120
|
+
**如果有未完成的 artifact 或 task:**
|
|
121
|
+
- 显示警告,列出未完成项
|
|
122
|
+
- 用 **AskUserQuestion** 确认是否继续
|
|
123
|
+
- 用户确认后继续,不阻塞
|
|
124
|
+
|
|
125
|
+
3. **生成摘要**
|
|
126
|
+
|
|
127
|
+
读取 \`marchen/changes/<name>/proposal.md\`,从中生成一句话中文摘要(≤50字),概括这次变更做了什么。摘要应包含关键语义词,便于后续 AI 检索。
|
|
128
|
+
|
|
129
|
+
4. **执行归档**
|
|
130
|
+
|
|
131
|
+
\`\`\`bash
|
|
132
|
+
marchen archive <name> --summary "<生成的摘要>" --json
|
|
133
|
+
\`\`\`
|
|
134
|
+
|
|
135
|
+
解析返回的 JSON 获取归档结果。
|
|
136
|
+
|
|
137
|
+
5. **显示结果**
|
|
138
|
+
|
|
139
|
+
\`\`\`
|
|
140
|
+
变更 "<name>" 已归档
|
|
141
|
+
Schema: <schema>
|
|
142
|
+
归档到: <archivedTo>
|
|
143
|
+
\`\`\`
|
|
144
|
+
|
|
145
|
+
如有警告(未完成项),一并显示。
|
|
146
|
+
|
|
147
|
+
**护栏**
|
|
148
|
+
|
|
149
|
+
- 未提供名称时必须用 AskUserQuestion 让用户选择
|
|
150
|
+
- 用 \`status --json\` 检查完成度,不要自己读文件判断
|
|
151
|
+
- 警告不阻塞归档,只提醒 + 确认
|
|
152
|
+
- 使用 AskUserQuestion 时,选项不超过 4 个
|
|
153
|
+
`},explore:{fileName:`explore.md`,content:`---
|
|
154
|
+
name: "Marchen: Explore"
|
|
155
|
+
description: 进入探索模式 — 思考想法、调查问题、厘清需求
|
|
156
|
+
category: Workflow
|
|
157
|
+
tags: [workflow, explore, thinking]
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
进入探索模式。深入思考,自由可视化,跟随对话走向任何方向。
|
|
161
|
+
|
|
162
|
+
**重要:探索模式只用于思考,不用于实现。** 可以读文件、搜索代码、调查代码库,但绝不能写代码或实现功能。如果用户要求实现,提醒他们先退出探索模式并用 \`/marchen:propose\` 创建变更。可以创建 Marchen artifact(proposal、design、spec)——那是捕获思考,不是实现。
|
|
163
|
+
|
|
164
|
+
**这是一种姿态,不是工作流。** 没有固定步骤、没有必须的顺序、没有强制输出。你是帮助用户探索的思考伙伴。
|
|
165
|
+
|
|
166
|
+
**输入**:\`/marchen:explore\` 后面可以跟任何内容:
|
|
167
|
+
- 模糊的想法:"实时协作"
|
|
168
|
+
- 具体的问题:"auth 系统越来越难维护了"
|
|
169
|
+
- 变更名称:"add-dark-mode"(在该变更上下文中探索)
|
|
170
|
+
- 方案比较:"postgres vs sqlite"
|
|
171
|
+
- 什么都不带也行
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 姿态
|
|
176
|
+
|
|
177
|
+
- **好奇,不武断** — 提出自然涌现的问题,不按脚本走
|
|
178
|
+
- **开放线索,不审问** — 展示多个有趣方向,让用户跟随感兴趣的。不要把他们引导到单一路径上
|
|
179
|
+
- **视觉化** — 大量使用 ASCII 图表来辅助思考
|
|
180
|
+
- **自适应** — 跟随有趣的线索,有新信息时及时转向
|
|
181
|
+
- **耐心** — 不急于下结论,让问题的形状自然浮现
|
|
182
|
+
- **接地气** — 在相关时探索实际代码库,不要空谈理论
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 你可以做什么
|
|
187
|
+
|
|
188
|
+
根据用户带来的内容,你可能会:
|
|
189
|
+
|
|
190
|
+
**探索问题空间**
|
|
191
|
+
- 提出从用户所说内容中自然涌现的澄清问题
|
|
192
|
+
- 挑战假设
|
|
193
|
+
- 重新框定问题
|
|
194
|
+
- 寻找类比
|
|
195
|
+
|
|
196
|
+
**调查代码库**
|
|
197
|
+
- 映射与讨论相关的现有架构
|
|
198
|
+
- 找到集成点
|
|
199
|
+
- 识别已有的模式
|
|
200
|
+
- 发现隐藏的复杂性
|
|
201
|
+
|
|
202
|
+
**比较方案**
|
|
203
|
+
- 头脑风暴多种方案
|
|
204
|
+
- 构建比较表
|
|
205
|
+
- 勾勒权衡
|
|
206
|
+
- 推荐路径(如果被问到)
|
|
207
|
+
|
|
208
|
+
**可视化**
|
|
209
|
+
\`\`\`
|
|
210
|
+
┌─────────────────────────────────────────┐
|
|
211
|
+
│ Use ASCII diagrams liberally │
|
|
212
|
+
├─────────────────────────────────────────┤
|
|
213
|
+
│ │
|
|
214
|
+
│ ┌────────┐ ┌────────┐ │
|
|
215
|
+
│ │ State │────────▶│ State │ │
|
|
216
|
+
│ │ A │ │ B │ │
|
|
217
|
+
│ └────────┘ └────────┘ │
|
|
218
|
+
│ │
|
|
219
|
+
│ System diagrams, state machines, │
|
|
220
|
+
│ data flows, architecture sketches, │
|
|
221
|
+
│ dependency graphs, comparison tables │
|
|
222
|
+
│ │
|
|
223
|
+
└─────────────────────────────────────────┘
|
|
224
|
+
\`\`\`
|
|
225
|
+
|
|
226
|
+
**发现风险和未知**
|
|
227
|
+
- 识别可能出错的地方
|
|
228
|
+
- 找到理解上的空白
|
|
229
|
+
- 建议 spike 或调查
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Marchen 感知
|
|
234
|
+
|
|
235
|
+
你了解 Marchen 系统。自然地使用它,不要强制。
|
|
236
|
+
|
|
237
|
+
### 检查上下文
|
|
238
|
+
|
|
239
|
+
开始时快速检查现有状态和历史:
|
|
240
|
+
|
|
241
|
+
1. 当前变更:
|
|
242
|
+
\`\`\`bash
|
|
243
|
+
marchen list --json
|
|
244
|
+
\`\`\`
|
|
245
|
+
|
|
246
|
+
2. 变更历史概览:
|
|
247
|
+
\`\`\`bash
|
|
248
|
+
cat marchen/changelog.md
|
|
249
|
+
\`\`\`
|
|
250
|
+
这是所有已归档变更的索引,每条包含日期、变更名和一句话摘要。先扫一遍找到与用户话题相关的条目。如果找到,直接读对应 archive 目录下的 proposal.md 或 design.md 了解详情。
|
|
251
|
+
|
|
252
|
+
3. 语义搜索:
|
|
253
|
+
|
|
254
|
+
这是 RAG 搜索,不是 grep——构造语义完整的短语,不要用单个泛词。
|
|
255
|
+
|
|
256
|
+
\`\`\`bash
|
|
257
|
+
marchen search "<语义完整的查询短语>" --json
|
|
258
|
+
\`\`\`
|
|
259
|
+
|
|
260
|
+
**查询构造指引:**
|
|
261
|
+
- 用描述性短语,不用单个词:
|
|
262
|
+
"初始化适配多个 agent 客户端" → \`"multi-agent provider 初始化"\`
|
|
263
|
+
"之前怎么处理错误的" → \`"错误处理 error handling 重构"\`
|
|
264
|
+
"暗色模式的设计决策" → \`"dark mode 设计方案"\`
|
|
265
|
+
- 中英文混合效果更好(归档内容里中英都有)
|
|
266
|
+
- 如果结果不理想,换个角度重新构造查询
|
|
267
|
+
|
|
268
|
+
如果有匹配结果(score >= 0.4),读取对应 archive 目录下的 design.md 或 proposal.md 了解详细决策。
|
|
269
|
+
|
|
270
|
+
如果 \`marchen search\` 不可用(命令报错),回退到 changelog.md + 手动读 archive 目录。
|
|
271
|
+
|
|
272
|
+
这告诉你:
|
|
273
|
+
- 是否有进行中的变更
|
|
274
|
+
- 它们的名称、schema 和状态
|
|
275
|
+
- 项目过去做过哪些变更
|
|
276
|
+
- 用户可能在做什么
|
|
277
|
+
|
|
278
|
+
如果用户提到了特定变更名称,读取它的 artifact 作为上下文。
|
|
279
|
+
|
|
280
|
+
### 没有变更时
|
|
281
|
+
|
|
282
|
+
自由思考。当洞察结晶时,根据复杂度推荐下一步:
|
|
283
|
+
|
|
284
|
+
**判断标准:**
|
|
285
|
+
- \`/marchen:lite\` — bug 修复、小改动、单一任务组、不需要设计文档
|
|
286
|
+
- \`/marchen:propose\` — 新功能、多步骤、需要 design/specs、涉及多模块
|
|
287
|
+
|
|
288
|
+
**推荐方式:** 直接在回复中输出推荐,说明理由,让用户自行输入命令。示例:
|
|
289
|
+
|
|
290
|
+
> 想法差不多成型了。这个改动比较简单(只涉及一个文件的小调整),建议用 \`/marchen:lite\` 直接走轻量流程。
|
|
291
|
+
>
|
|
292
|
+
> 如果你觉得需要更完整的设计文档,也可以用 \`/marchen:propose\`。
|
|
293
|
+
|
|
294
|
+
根据讨论内容给出你的推荐和理由,但让用户自己决定输入哪个命令。
|
|
295
|
+
|
|
296
|
+
### 有变更时
|
|
297
|
+
|
|
298
|
+
如果用户提到了变更或你发现某个变更相关:
|
|
299
|
+
|
|
300
|
+
1. **读取已有 artifact 作为上下文**
|
|
301
|
+
- \`marchen/changes/<name>/proposal.md\`
|
|
302
|
+
- \`marchen/changes/<name>/design.md\`
|
|
303
|
+
- \`marchen/changes/<name>/tasks.md\`
|
|
304
|
+
- 等
|
|
305
|
+
|
|
306
|
+
2. **在对话中自然引用**
|
|
307
|
+
- "你的 design 提到用 Redis,但我们刚发现 SQLite 更合适……"
|
|
308
|
+
- "proposal 把范围限定在付费用户,但我们现在觉得应该面向所有人……"
|
|
309
|
+
|
|
310
|
+
3. **在做出决策时提议捕获**
|
|
311
|
+
|
|
312
|
+
| 洞察类型 | 捕获到哪里 |
|
|
313
|
+
|---------|-----------|
|
|
314
|
+
| 发现新需求 | \`specs/<capability>/spec.md\` |
|
|
315
|
+
| 需求变更 | \`specs/<capability>/spec.md\` |
|
|
316
|
+
| 做出设计决策 | \`design.md\` |
|
|
317
|
+
| 范围变更 | \`proposal.md\` |
|
|
318
|
+
| 发现新工作 | \`tasks.md\` |
|
|
319
|
+
| 假设被推翻 | 相关 artifact |
|
|
320
|
+
|
|
321
|
+
示例:
|
|
322
|
+
- "这是一个设计决策。要记录到 design.md 吗?"
|
|
323
|
+
- "这是新需求。要加到 specs 里吗?"
|
|
324
|
+
- "这改变了范围。要更新 proposal 吗?"
|
|
325
|
+
|
|
326
|
+
4. **用户决定** — 提议后继续。不施压,不自动捕获。
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## 不必做的事
|
|
331
|
+
|
|
332
|
+
- 按脚本走
|
|
333
|
+
- 每次问同样的问题
|
|
334
|
+
- 产出特定 artifact
|
|
335
|
+
- 得出结论
|
|
336
|
+
- 如果有价值的岔路就不必守住话题
|
|
337
|
+
- 简短(这是思考时间)
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## 结束探索
|
|
342
|
+
|
|
343
|
+
没有固定的结束方式。探索可能:
|
|
344
|
+
|
|
345
|
+
- **流向下一阶段**:用 **AskUserQuestion** 提供 \`/marchen:lite\` 和 \`/marchen:propose\` 选项,附带推荐理由
|
|
346
|
+
- **更新 artifact**:"已将这些决策更新到 design.md"
|
|
347
|
+
- **只是提供清晰度**:用户得到了需要的,继续前进
|
|
348
|
+
- **稍后继续**:"随时可以继续"
|
|
349
|
+
|
|
350
|
+
当想法结晶时,你可以提供总结——但不是必须的。有时候思考过程本身就是价值。
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## 护栏
|
|
355
|
+
|
|
356
|
+
- **不实现** — 绝不写代码或实现功能。创建 Marchen artifact 可以,写应用代码不行
|
|
357
|
+
- **不伪装理解** — 不清楚就深挖
|
|
358
|
+
- **不催促** — 探索是思考时间,不是任务时间
|
|
359
|
+
- **不强制结构** — 让模式自然浮现
|
|
360
|
+
- **不自动捕获** — 提议保存洞察,不要直接做
|
|
361
|
+
- **要可视化** — 一张好图胜过千言万语
|
|
362
|
+
- **要探索代码库** — 让讨论扎根于现实
|
|
363
|
+
- **要质疑假设** — 包括用户的和你自己的
|
|
364
|
+
`},lite:{fileName:`lite.md`,content:`---
|
|
365
|
+
name: "Marchen: Lite"
|
|
366
|
+
description: 一键式轻量变更流程。创建 lite 变更、实现任务、询问归档,一气呵成
|
|
367
|
+
category: Workflow
|
|
368
|
+
tags: [workflow, lite]
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
一键式轻量变更 — 使用 lite schema 创建变更,自动实现任务,完成后询问归档。
|
|
372
|
+
适合 bug 修复、小改动、explore 之后的快速执行。
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
**输入**:\`/marchen:lite\` 后面跟变更名称(kebab-case)或变更描述。
|
|
377
|
+
|
|
378
|
+
**流程**
|
|
379
|
+
|
|
380
|
+
1. **确定变更名称**
|
|
381
|
+
|
|
382
|
+
如果提供了输入,直接使用或从描述中提取 kebab-case 名称(如"修复登录 bug" → \`fix-login-bug\`)。
|
|
383
|
+
|
|
384
|
+
如果没有输入,用 **AskUserQuestion** 工具询问:
|
|
385
|
+
> "你想做什么变更?描述一下你要构建或修复的内容。"
|
|
386
|
+
|
|
387
|
+
从回答中提取 kebab-case 名称。
|
|
388
|
+
|
|
389
|
+
**重要**:必须理解用户想做什么才能继续。
|
|
390
|
+
|
|
391
|
+
2. **创建变更目录**
|
|
392
|
+
|
|
393
|
+
\`\`\`bash
|
|
394
|
+
marchen new <name> --schema lite
|
|
395
|
+
\`\`\`
|
|
396
|
+
|
|
397
|
+
创建 \`marchen/changes/<name>/\` 目录,包含 \`.metadata.yaml\` 和 \`tasks.md\` 骨架。
|
|
398
|
+
|
|
399
|
+
如果同名变更已存在,用 **AskUserQuestion** 询问用户是继续已有变更还是换个名称。
|
|
400
|
+
|
|
401
|
+
3. **获取 tasks 指令**
|
|
402
|
+
|
|
403
|
+
\`\`\`bash
|
|
404
|
+
marchen status <name> --json
|
|
405
|
+
\`\`\`
|
|
406
|
+
|
|
407
|
+
确认变更创建成功,然后获取 tasks 的创建指令:
|
|
408
|
+
|
|
409
|
+
\`\`\`bash
|
|
410
|
+
marchen instructions <name> tasks --json
|
|
411
|
+
\`\`\`
|
|
412
|
+
|
|
413
|
+
返回 JSON 包含:
|
|
414
|
+
- \`template\`:tasks.md 的骨架结构(含 \`## 背景\` 章节)
|
|
415
|
+
- \`instruction\`:如何填充 tasks 的指导文本
|
|
416
|
+
- \`outputPath\`:写入路径(\`tasks.md\`)
|
|
417
|
+
- \`context\`:上下文信息(lite schema 下为空数组)
|
|
418
|
+
|
|
419
|
+
4. **填充 tasks.md**
|
|
420
|
+
|
|
421
|
+
根据用户描述 + \`instruction\` 指引 + \`template\` 结构,填充 tasks.md:
|
|
422
|
+
- \`## 背景\`:简要说明变更目的和方案
|
|
423
|
+
- 任务列表:按组分类,checkbox 格式
|
|
424
|
+
|
|
425
|
+
写入 \`marchen/changes/<name>/tasks.md\`。
|
|
426
|
+
|
|
427
|
+
如果用户描述太模糊,用 **AskUserQuestion** 澄清关键信息。
|
|
428
|
+
|
|
429
|
+
5. **开始实现**
|
|
430
|
+
|
|
431
|
+
获取实现指令:
|
|
432
|
+
|
|
433
|
+
\`\`\`bash
|
|
434
|
+
marchen instructions <name> apply --json
|
|
435
|
+
\`\`\`
|
|
436
|
+
|
|
437
|
+
返回 JSON 包含:
|
|
438
|
+
- \`state\`:\`"ready"\` / \`"blocked"\` / \`"all_done"\`
|
|
439
|
+
- \`progress\`:\`{ total, completed, remaining }\`
|
|
440
|
+
- \`context\`:所有 artifact 的信息数组
|
|
441
|
+
- \`instruction\`:实现指引
|
|
442
|
+
- \`changeDir\`:变更目录绝对路径
|
|
443
|
+
|
|
444
|
+
显示:"变更: \`<name>\` | 任务: 0/N | 开始实现..."
|
|
445
|
+
|
|
446
|
+
对每个未完成任务:
|
|
447
|
+
- 显示 "任务 N/M: <描述>"
|
|
448
|
+
- 实现代码改动
|
|
449
|
+
- 在 tasks.md 中勾选:\`- [ ]\` → \`- [x]\`
|
|
450
|
+
文件路径:\`<changeDir>/tasks.md\`
|
|
451
|
+
- 显示 "✓ 完成"
|
|
452
|
+
- 继续下一个
|
|
453
|
+
|
|
454
|
+
**暂停条件:**
|
|
455
|
+
- 任务不清晰 → 询问用户
|
|
456
|
+
- 发现设计问题 → 建议更新 artifact
|
|
457
|
+
- 遇到错误或阻塞 → 报告并等待
|
|
458
|
+
- 用户中断
|
|
459
|
+
|
|
460
|
+
暂停时显示:"暂停于任务 N/M: <原因>",流程结束。
|
|
461
|
+
|
|
462
|
+
6. **全部完成 → 询问归档**
|
|
463
|
+
|
|
464
|
+
所有任务完成后,用 **AskUserQuestion** 询问:
|
|
465
|
+
|
|
466
|
+
> "全部任务已完成 (N/N),是否归档这个变更?"
|
|
467
|
+
> - 归档
|
|
468
|
+
> - 暂不归档
|
|
469
|
+
|
|
470
|
+
**如果用户选择归档:**
|
|
471
|
+
|
|
472
|
+
读取 \`marchen/changes/<name>/tasks.md\` 的背景段,生成一句话中文摘要(≤50字)。
|
|
473
|
+
|
|
474
|
+
\`\`\`bash
|
|
475
|
+
marchen archive <name> --summary "<摘要>" --json
|
|
476
|
+
\`\`\`
|
|
477
|
+
|
|
478
|
+
显示:
|
|
479
|
+
\`\`\`
|
|
480
|
+
变更 "<name>" 已归档
|
|
481
|
+
归档到: <archivedTo>
|
|
482
|
+
\`\`\`
|
|
483
|
+
|
|
484
|
+
**如果用户选择暂不归档:**
|
|
485
|
+
|
|
486
|
+
显示:"好的,后续可以用 \`/marchen:archive <name>\` 归档。"
|
|
487
|
+
|
|
488
|
+
**护栏**
|
|
489
|
+
|
|
490
|
+
- 必须使用 \`--schema lite\` 创建变更
|
|
491
|
+
- tasks.md 的 \`## 背景\` 章节必须填写,不能留空
|
|
492
|
+
- 任务粒度要小到一个会话内能完成
|
|
493
|
+
- 如果上下文关键信息不清楚,询问用户;但小疑问优先做合理判断,保持节奏
|
|
494
|
+
- 已存在同名变更时必须询问用户,不要覆盖
|
|
495
|
+
- 实现前必须读 context 中的 artifact 内容
|
|
496
|
+
- 每完成一个任务立即勾选 checkbox,不要攒着
|
|
497
|
+
- 改动最小化,只做任务要求的事
|
|
498
|
+
- 不确定就暂停问,不要猜
|
|
499
|
+
- \`instruction\` 是给你的指引,不要把它原样复制到代码注释或 tasks.md 中
|
|
500
|
+
- 使用 AskUserQuestion 时,选项不超过 4 个
|
|
501
|
+
`},"propose-preview":{fileName:`propose-preview.md`,content:`---
|
|
502
|
+
name: "Marchen: Propose Preview"
|
|
503
|
+
description: 预览 propose 生成的变更摘要
|
|
504
|
+
category: Workflow
|
|
505
|
+
tags: [workflow, preview]
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
预览一个变更的浓缩摘要 — 纯终端输出,不写文件。
|
|
509
|
+
|
|
510
|
+
适用于 propose 完成后人快速 review,避免来回翻 4~7 个 artifact 文件。
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
**输入**:\`/marchen:propose-preview\` 后面跟变更名称,或省略自动推断。
|
|
515
|
+
|
|
516
|
+
**流程**
|
|
517
|
+
|
|
518
|
+
1. **选择变更**
|
|
519
|
+
|
|
520
|
+
有名称就用,没有则:
|
|
521
|
+
- 从对话上下文推断
|
|
522
|
+
- 只有一个 open 变更时自动选择
|
|
523
|
+
- 多个变更时 \`marchen list --json\` + **AskUserQuestion** 让用户选
|
|
524
|
+
|
|
525
|
+
显示:"Preview 变更: \`<name>\`"
|
|
526
|
+
|
|
527
|
+
2. **获取 artifact 内容**
|
|
528
|
+
|
|
529
|
+
\`\`\`bash
|
|
530
|
+
marchen instructions <name> apply --json
|
|
531
|
+
\`\`\`
|
|
532
|
+
|
|
533
|
+
返回 JSON 包含:
|
|
534
|
+
- \`schemaName\`:\`"full"\` / \`"lite"\`
|
|
535
|
+
- \`state\`:\`"ready"\` / \`"blocked"\` / \`"all_done"\`
|
|
536
|
+
- \`progress\`:\`{ total, completed, remaining }\`
|
|
537
|
+
- \`context\`:所有 artifact 的内容数组(\`id\` / \`status\` / \`content\`)
|
|
538
|
+
|
|
539
|
+
**如果 state 为 \`blocked\`**:打印 "变更未填完,先用 /marchen:propose 补齐 artifact 再预览",结束。不要强行摘要半成品。
|
|
540
|
+
|
|
541
|
+
3. **生成卡片并直接打印**
|
|
542
|
+
|
|
543
|
+
根据 \`schemaName\` 选模板:
|
|
544
|
+
- \`full\` → 四段:改了什么 / 关键决策 / 影响范围 / 风险
|
|
545
|
+
- \`lite\` → 两段:改了什么 / 任务概览
|
|
546
|
+
|
|
547
|
+
严格按下面的"摘要规则"生成,输出为单个卡片,禁止加任何解释段落。
|
|
548
|
+
|
|
549
|
+
4. **末尾追加一行下一步提示**
|
|
550
|
+
|
|
551
|
+
\`\`\`
|
|
552
|
+
/marchen:apply <name> 开始实现
|
|
553
|
+
/marchen:propose <name> 修改提案
|
|
554
|
+
\`\`\`
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## 摘要规则(必须严格遵守)
|
|
559
|
+
|
|
560
|
+
**通用约束:**
|
|
561
|
+
|
|
562
|
+
- 卡片框宽 70 字符(含 \`│\` 边框),便于主流终端整齐显示
|
|
563
|
+
- 每行内容(去掉边框后)≤ 60 字符;超出必须截断/合并/重写,不许折行
|
|
564
|
+
- 禁止粘贴 artifact 原文片段,所有内容必须重新组织、压缩
|
|
565
|
+
- 中文为主,技术名词保留英文(如 \`SearchManager\`、\`SDK\`)
|
|
566
|
+
- 宁可少写,不要堆。摘不下就合并或截断,在框底加 \`更多详情见 marchen/changes/<name>/\`
|
|
567
|
+
|
|
568
|
+
**full schema 段落上限:**
|
|
569
|
+
|
|
570
|
+
| 段落 | 上限 | 来源 |
|
|
571
|
+
|---------|------------------|---------------------------------|
|
|
572
|
+
| 改了什么 | 6 条 bullet | proposal 的"能力"小节 |
|
|
573
|
+
| 关键决策 | 5 条 bullet | design 的"决策"小节 |
|
|
574
|
+
| 影响范围 | 8 节点 ASCII 图 | proposal 的"影响范围" + design |
|
|
575
|
+
| 风险 | 3 条 bullet | design 的"风险与权衡" |
|
|
576
|
+
|
|
577
|
+
**lite schema 段落上限:**
|
|
578
|
+
|
|
579
|
+
| 段落 | 上限 | 来源 |
|
|
580
|
+
|---------|------------------|-------------------------------|
|
|
581
|
+
| 改了什么 | 6 条 bullet | tasks.md 任务组标题 + 推断 |
|
|
582
|
+
| 任务概览 | 1 行/任务组 | tasks.md 一级标题 + 完成进度 |
|
|
583
|
+
|
|
584
|
+
**影响范围图退化策略:**
|
|
585
|
+
|
|
586
|
+
如果节点数超 8 个 / 关系不清晰 / 没有明显依赖结构 → 改用 bullet 列模块名,不要硬画歪斜的 ASCII。
|
|
587
|
+
|
|
588
|
+
**任务进度条规则(lite):**
|
|
589
|
+
|
|
590
|
+
进度条固定 10 格宽,按 \`completed/total\` 比例画。例如 \`8/8\` → \`██████████\`,\`3/5\` → \`██████░░░░\`。
|
|
591
|
+
|
|
592
|
+
---
|
|
593
|
+
|
|
594
|
+
## 输出样例
|
|
595
|
+
|
|
596
|
+
**full schema:**
|
|
597
|
+
|
|
598
|
+
\`\`\`
|
|
599
|
+
╭─ <name> ────────────────────────────── full · <N> 任务 ─╮
|
|
600
|
+
│ │
|
|
601
|
+
│ <一句话动机,从 proposal "动机" 段提炼,≤ 55 字> │
|
|
602
|
+
│ │
|
|
603
|
+
│ ━━ 改了什么 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
604
|
+
│ ✚ <capability-name> <一句话能力描述> │
|
|
605
|
+
│ ... │
|
|
606
|
+
│ │
|
|
607
|
+
│ ━━ 关键决策 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
608
|
+
│ 1. <决策内容> │
|
|
609
|
+
│ ... │
|
|
610
|
+
│ │
|
|
611
|
+
│ ━━ 影响范围 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
612
|
+
│ <ASCII 图 或 bullet 模块列表> │
|
|
613
|
+
│ │
|
|
614
|
+
│ ━━ 风险 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
615
|
+
│ • <风险> │
|
|
616
|
+
│ │
|
|
617
|
+
╰──────────────────────────────────────────────────────────╯
|
|
618
|
+
\`\`\`
|
|
619
|
+
|
|
620
|
+
**lite schema:**
|
|
621
|
+
|
|
622
|
+
\`\`\`
|
|
623
|
+
╭─ <name> ──────────────────────────── lite · <N> 任务 ─╮
|
|
624
|
+
│ │
|
|
625
|
+
│ <一句话动机,从 tasks.md "背景" 段提炼> │
|
|
626
|
+
│ │
|
|
627
|
+
│ ━━ 改了什么 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
628
|
+
│ • <推断的核心变更> │
|
|
629
|
+
│ │
|
|
630
|
+
│ ━━ 任务概览 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
631
|
+
│ 1. <任务组标题> ██████████ N/M ✓ │
|
|
632
|
+
│ │
|
|
633
|
+
╰───────────────────────────────────────────────────────╯
|
|
634
|
+
\`\`\`
|
|
635
|
+
|
|
636
|
+
**护栏**
|
|
637
|
+
|
|
638
|
+
- 不写文件,纯 stdout 输出
|
|
639
|
+
- 不调用 AskUserQuestion 询问"要打印什么",遵守摘要规则即可
|
|
640
|
+
- 不展开补全 artifact 信息——artifact 里没有的,摘要里就不该有
|
|
641
|
+
- state 为 \`blocked\` 时拒绝生成,不强行摘要半成品
|
|
642
|
+
- 不要在卡片外加解释段落,只打印卡片 + 下一步提示
|
|
643
|
+
- \`instruction\` 字段(apply JSON 里有)是给 apply 用的,不是给 preview 的,忽略它
|
|
644
|
+
`},propose:{fileName:`propose.md`,content:`---
|
|
645
|
+
name: "Marchen: Propose"
|
|
646
|
+
description: 提出新变更,创建并填充所有 artifact
|
|
647
|
+
category: Workflow
|
|
648
|
+
tags: [workflow, artifacts]
|
|
649
|
+
---
|
|
650
|
+
|
|
651
|
+
提出新变更 — 创建变更目录并按依赖顺序生成所有 artifact。
|
|
652
|
+
|
|
653
|
+
将创建以下 artifact:
|
|
654
|
+
- proposal.md(动机和变更内容)
|
|
655
|
+
- specs/(每个能力的需求规格)
|
|
656
|
+
- design.md(技术方案)
|
|
657
|
+
- tasks.md(实现任务清单)
|
|
658
|
+
|
|
659
|
+
完成后可用 /marchen:apply 开始实现。
|
|
660
|
+
|
|
661
|
+
---
|
|
662
|
+
|
|
663
|
+
**输入**:\`/marchen:propose\` 后面跟变更名称(kebab-case)或变更描述。
|
|
664
|
+
|
|
665
|
+
**流程**
|
|
666
|
+
|
|
667
|
+
1. **确定变更名称**
|
|
668
|
+
|
|
669
|
+
如果提供了输入,直接使用或从描述中提取 kebab-case 名称(如"添加用户认证" → \`add-user-auth\`)。
|
|
670
|
+
|
|
671
|
+
如果没有输入,用 **AskUserQuestion** 工具询问:
|
|
672
|
+
> "你想做什么变更?描述一下你要构建或修复的内容。"
|
|
673
|
+
|
|
674
|
+
从回答中提取 kebab-case 名称。
|
|
675
|
+
|
|
676
|
+
**重要**:必须理解用户想做什么才能继续。
|
|
677
|
+
|
|
678
|
+
2. **创建变更目录**
|
|
679
|
+
|
|
680
|
+
\`\`\`bash
|
|
681
|
+
marchen new <name>
|
|
682
|
+
\`\`\`
|
|
683
|
+
|
|
684
|
+
创建 \`marchen/changes/<name>/\` 目录和 \`.metadata.yaml\`。
|
|
685
|
+
|
|
686
|
+
如果同名变更已存在,用 **AskUserQuestion** 询问用户是继续已有变更还是换个名称。
|
|
687
|
+
|
|
688
|
+
3. **循环创建 artifact**
|
|
689
|
+
|
|
690
|
+
用 **TaskCreate** 工具创建任务列表追踪进度。
|
|
691
|
+
|
|
692
|
+
循环执行以下步骤:
|
|
693
|
+
|
|
694
|
+
a. **查询当前状态**
|
|
695
|
+
\`\`\`bash
|
|
696
|
+
marchen status <name> --json
|
|
697
|
+
\`\`\`
|
|
698
|
+
返回 JSON 包含:
|
|
699
|
+
- \`workflow.next\`:下一个应该创建的 artifact ID,全部完成时为 \`null\`
|
|
700
|
+
- \`workflow.ready\`:当前可以创建的 artifact 列表
|
|
701
|
+
- \`workflow.blocked\`:被阻塞的 artifact 列表
|
|
702
|
+
- \`artifacts\`:每个 artifact 的状态详情(\`id\`、\`status\`、\`path\`)
|
|
703
|
+
|
|
704
|
+
如果 \`workflow.next\` 为 \`null\` → 全部完成,跳到第 4 步。
|
|
705
|
+
|
|
706
|
+
b. **获取创建指令**
|
|
707
|
+
\`\`\`bash
|
|
708
|
+
marchen instructions <name> <workflow.next> --json
|
|
709
|
+
\`\`\`
|
|
710
|
+
返回 JSON 包含:
|
|
711
|
+
- \`template\`:artifact 的 markdown 骨架结构,用它作为输出文件的框架
|
|
712
|
+
- \`instruction\`:如何填充该 artifact 的指导文本
|
|
713
|
+
- \`outputPath\`:写入路径(相对于变更目录)
|
|
714
|
+
- \`context\`:上下文 artifact 的信息数组,每项包含 \`id\`、\`status\`、\`content\`(已填充的内容直接在这里,不需要额外读文件)
|
|
715
|
+
- \`unlocks\`:完成此 artifact 后解锁的 artifact 列表
|
|
716
|
+
|
|
717
|
+
c. **创建 artifact**
|
|
718
|
+
|
|
719
|
+
根据 artifact 类型处理:
|
|
720
|
+
|
|
721
|
+
**普通 artifact(proposal / design / tasks):**
|
|
722
|
+
- 读取 \`context\` 中 \`status\` 为 \`filled\` 的 \`content\` 作为上下文
|
|
723
|
+
- 按 \`instruction\` 指引 + \`template\` 结构生成内容
|
|
724
|
+
- 写入 \`marchen/changes/<name>/<outputPath>\`
|
|
725
|
+
- 写入后验证文件存在
|
|
726
|
+
|
|
727
|
+
**specs(目录型 artifact,outputPath 为 \`specs/\`):**
|
|
728
|
+
- 读取 proposal 内容(在 \`context\` 中,\`id\` 为 \`proposal\` 的 \`content\`)
|
|
729
|
+
- 从 proposal 的"能力"章节提取能力列表(kebab-case 名称)
|
|
730
|
+
- 为每个能力:
|
|
731
|
+
- 创建目录 \`marchen/changes/<name>/specs/<capability>/\`
|
|
732
|
+
- 按 \`template\` 结构 + \`instruction\` 指引生成 spec 内容
|
|
733
|
+
- 写入 \`specs/<capability>/spec.md\`
|
|
734
|
+
- 写入后验证每个 spec 文件存在
|
|
735
|
+
|
|
736
|
+
**如果 proposal 的上下文不够清晰**(用户描述太模糊):
|
|
737
|
+
- 用 **AskUserQuestion** 澄清关键信息
|
|
738
|
+
- 然后继续创建
|
|
739
|
+
|
|
740
|
+
d. 显示进度:"已创建 \`<artifact-id>\`",标记任务完成,回到步骤 a。
|
|
741
|
+
|
|
742
|
+
4. **显示最终状态**
|
|
743
|
+
|
|
744
|
+
\`\`\`bash
|
|
745
|
+
marchen status <name>
|
|
746
|
+
\`\`\`
|
|
747
|
+
|
|
748
|
+
**输出**
|
|
749
|
+
|
|
750
|
+
完成后显示:
|
|
751
|
+
- 变更名称和目录位置
|
|
752
|
+
- 已创建的 artifact 列表及简要说明
|
|
753
|
+
- 用纯文字(不调用 AskUserQuestion,不自动执行)提示下一步两个并列选项,由用户自行决定:
|
|
754
|
+
|
|
755
|
+
\`\`\`
|
|
756
|
+
下一步:
|
|
757
|
+
/marchen:apply <name> 直接开始实现
|
|
758
|
+
/marchen:propose-preview <name> 先看一眼浓缩摘要再决定
|
|
759
|
+
\`\`\`
|
|
760
|
+
|
|
761
|
+
**护栏**
|
|
762
|
+
|
|
763
|
+
- 按依赖顺序创建,不跳过 artifact
|
|
764
|
+
- 每次循环创建一个 artifact(specs 算一个,但包含多个文件)
|
|
765
|
+
- 写入后验证文件存在再继续下一个
|
|
766
|
+
- 如果上下文关键信息不清楚,询问用户;但小疑问优先做合理判断,保持节奏
|
|
767
|
+
- 已存在同名变更时必须询问用户,不要覆盖
|
|
768
|
+
- \`instruction\` 是给你的指引,不要把它原样复制到 artifact 文件中
|
|
769
|
+
- 使用 AskUserQuestion 时,选项不超过 4 个;需要更多选项时合并或分步询问
|
|
770
|
+
`},review:{fileName:`review.md`,content:`---
|
|
771
|
+
name: "Marchen: Review"
|
|
772
|
+
description: 对照变更意图检查代码实现,支持 chrome-devtools MCP 的 UI 场景验证
|
|
773
|
+
category: Workflow
|
|
774
|
+
tags: [workflow, review]
|
|
775
|
+
---
|
|
776
|
+
|
|
777
|
+
对照变更的 artifact 检查代码改动,必要时驱动浏览器验证 UI 行为,报告遗漏、偏差和阻塞。
|
|
778
|
+
|
|
779
|
+
---
|
|
780
|
+
|
|
781
|
+
**输入**:\`/marchen:review\` 后面跟变更名称,或省略自动推断。
|
|
782
|
+
|
|
783
|
+
**流程**
|
|
784
|
+
|
|
785
|
+
1. **选择变更**
|
|
786
|
+
|
|
787
|
+
有名称就用,没有则:
|
|
788
|
+
- 从对话上下文推断
|
|
789
|
+
- 只有一个 open 变更时自动选择
|
|
790
|
+
- 多个变更时 \`marchen list --json\` + **AskUserQuestion** 让用户选
|
|
791
|
+
|
|
792
|
+
显示:"Review 变更: \`<name>\`"
|
|
793
|
+
|
|
794
|
+
2. **嗅探 diff 中的 UI 改动**
|
|
795
|
+
|
|
796
|
+
执行 \`git diff --name-only HEAD\`,判断改动是否涉及前端 UI(组件、样式、模板、静态资源等)。命中则记下命中文件,用于下一步的提示。
|
|
797
|
+
|
|
798
|
+
3. **AskUserQuestion 选择 review 模式**
|
|
799
|
+
|
|
800
|
+
用 **AskUserQuestion** 让用户三选一:
|
|
801
|
+
|
|
802
|
+
- **代码 review**(默认):对照 artifact 检查 git diff,不跑代码
|
|
803
|
+
- **UI 验证**:用 chrome-devtools MCP 实际打开页面验证 specs 中的场景
|
|
804
|
+
- **两者都做**:先代码、再 UI
|
|
805
|
+
|
|
806
|
+
若上一步命中 UI 文件,在问题描述里附一句:"检测到 diff 涉及 UI 文件(<列出命中文件>),建议选 UI 验证或两者都做。"
|
|
807
|
+
未命中时不附加提示,默认推荐"代码 review"。
|
|
808
|
+
|
|
809
|
+
保存用户选择为 \`<mode>\`。
|
|
810
|
+
|
|
811
|
+
4. **获取意图与改动(公共前置)**
|
|
812
|
+
|
|
813
|
+
- 执行 \`marchen instructions <name> apply --json\`,从返回 JSON 的 \`context\` 数组里读取 \`status\` 为 "filled" 的 artifact(proposal/specs/design/tasks)作为变更意图
|
|
814
|
+
- 执行 \`git diff HEAD\`;为空则 \`git diff HEAD~1\`;仍为空则报告 "未检测到代码改动" 并结束
|
|
815
|
+
|
|
816
|
+
diff 大到可能挤占 context 时,先 \`git diff --stat HEAD\` 看摘要,再按需读单个文件 diff。
|
|
817
|
+
|
|
818
|
+
5. **代码 review**(mode 为 "代码 review" 或 "两者都做" 时执行)
|
|
819
|
+
|
|
820
|
+
逐条对照变更意图和代码改动,输出报告:
|
|
821
|
+
|
|
822
|
+
**任务完成度** — tasks 中每个任务是否有对应改动:
|
|
823
|
+
- ✅ 任务: <描述> — 已实现
|
|
824
|
+
- ❌ 任务: <描述> — 未找到对应改动
|
|
825
|
+
|
|
826
|
+
**一致性检查** — 实现是否符合 design 决策:
|
|
827
|
+
- ✅ <决策> — 已遵守
|
|
828
|
+
- ⚠️ <决策> — 实现有偏差:<说明>
|
|
829
|
+
|
|
830
|
+
**需求覆盖** — specs 中需求是否被覆盖:
|
|
831
|
+
- ✅ <需求> — 已覆盖
|
|
832
|
+
- ❌ <需求> — 未覆盖
|
|
833
|
+
|
|
834
|
+
**发现的问题(如有)**
|
|
835
|
+
- <文件:行号> <问题描述>
|
|
836
|
+
|
|
837
|
+
全部通过:输出 "✅ 代码 review 通过。"
|
|
838
|
+
|
|
839
|
+
遇到无法判断的情况(tasks 描述与 diff 对不上但可能是改名了、spec 表述含糊等),直接用 **AskUserQuestion** 就地问用户,不要积攒到最后。
|
|
840
|
+
|
|
841
|
+
6. **UI 验证**(mode 为 "UI 验证" 或 "两者都做" 时执行)
|
|
842
|
+
|
|
843
|
+
### a. 检测 chrome-devtools MCP 可用性
|
|
844
|
+
|
|
845
|
+
尝试调用 chrome-devtools MCP 的只读探测工具(例如列出已打开的页面)。工具不存在或调用失败 → 在报告里写:
|
|
846
|
+
|
|
847
|
+
> ⏭ chrome-devtools MCP 不可用,跳过 UI 验证。
|
|
848
|
+
> 安装方法:\`npx chrome-devtools-mcp@latest\`,并参考所用 AI 工具的 MCP 配置文档将其注册。
|
|
849
|
+
|
|
850
|
+
然后跳过本节剩余步骤。
|
|
851
|
+
|
|
852
|
+
### b. 提取待验证场景
|
|
853
|
+
|
|
854
|
+
优先从变更的 specs 文件中提取所有 \`#### 场景:\` 块(按 spec 文件分组)。
|
|
855
|
+
若 specs 不存在或不含场景 → 退化到从 tasks/proposal 推断 UI 相关验证点,并在报告中标注 "基于 tasks 推断,覆盖度可能不完整"。
|
|
856
|
+
两者都没有可提取场景 → 报告 "未找到可验证的 UI 场景" 并跳过本节。
|
|
857
|
+
|
|
858
|
+
### c. 推断 dev server URL
|
|
859
|
+
|
|
860
|
+
从项目文件(脚本、框架配置、环境变量、README 等)推断 dev server 地址和启动命令,不要硬猜端口。
|
|
861
|
+
|
|
862
|
+
推出候选 URL 后用 chrome-devtools MCP 的导航工具打开,再用快照工具确认页面像被测应用(合理 title、含框架 root 节点或变更描述涉及的标志性内容)。看着不像被测应用 → 当作未能推断。
|
|
863
|
+
|
|
864
|
+
推不出 URL → 全部场景 ⏭ "URL unknown",在报告里说明推断依据,提示用户确认地址后重新运行 review,跳过本节执行步骤。
|
|
865
|
+
|
|
866
|
+
### d. 推断到 URL 但 dev server 未启动 → 询问是否帮忙起
|
|
867
|
+
|
|
868
|
+
推出来 URL 但导航失败(连接拒绝)→ 主会话 **AskUserQuestion**,附上推断依据和启动命令:
|
|
869
|
+
|
|
870
|
+
- **帮我起 dev server 然后继续**
|
|
871
|
+
- **我手动起,起完告诉你**
|
|
872
|
+
- **跳过 UI 验证**
|
|
873
|
+
|
|
874
|
+
选"帮我起":
|
|
875
|
+
1. 用 Bash 的 \`run_in_background\` 执行推断到的启动命令(如 \`pnpm dev\`、\`npm run dev\`)
|
|
876
|
+
2. 轮询端口直到可访问;出现明显异常(进程退出 / 长时间无任何输出 / 报错日志)才判定启动失败,让用户手动检查
|
|
877
|
+
3. 启动成功后继续执行后续步骤,并在 review 结束时显式告知用户后台进程 PID 和端口,提示 "用完请自行 kill <PID>"——MUST NOT 自动 kill
|
|
878
|
+
|
|
879
|
+
选"我手动起":等用户告知启动完成后继续;用户可以新会话执行 \`/marchen:review\` 重跑。
|
|
880
|
+
|
|
881
|
+
选"跳过 UI":所有场景 ⏭ "dev server 未启动",跳过后续步骤。
|
|
882
|
+
|
|
883
|
+
### e. 乐观执行场景
|
|
884
|
+
|
|
885
|
+
对每个待验证场景:
|
|
886
|
+
|
|
887
|
+
1. 用 chrome-devtools MCP 的导航工具打开对应路径
|
|
888
|
+
2. 用快照工具观察页面状态
|
|
889
|
+
3. 把场景的 GIVEN/WHEN/THEN 翻译成具体交互(点击、填写、读取文本)并执行
|
|
890
|
+
4. 比对页面状态与场景预期
|
|
891
|
+
|
|
892
|
+
遇到阻塞(登录墙、权限不足、缺数据、表单需要真实输入等):**主会话可以就地 AskUserQuestion**——例如"需要登录账号才能继续,提供测试账号?/ 跳过这个场景 / 暂停 review",而不必积攒到最后。
|
|
893
|
+
|
|
894
|
+
仍无法继续的场景 → 记录 ⏭ + 阻塞原因,跳到下一个。
|
|
895
|
+
场景翻译不出具体操作(例如纯后端行为描述)→ ⏭ "不适合 UI 验证"。
|
|
896
|
+
|
|
897
|
+
### f. UI 报告格式
|
|
898
|
+
|
|
899
|
+
\`\`\`
|
|
900
|
+
## UI 验证(chrome-devtools MCP)
|
|
901
|
+
|
|
902
|
+
测试目标:<URL 或 "未确定">
|
|
903
|
+
|
|
904
|
+
✅ <场景标题> — 通过
|
|
905
|
+
说明:<可选简述>
|
|
906
|
+
❌ <场景标题> — 失败
|
|
907
|
+
证据:<console error 摘要 / 页面文案 / 关键截图路径>
|
|
908
|
+
⏭ <场景标题> — 跳过:<阻塞原因>
|
|
909
|
+
\`\`\`
|
|
910
|
+
|
|
911
|
+
7. **展示报告并按结果分支**
|
|
912
|
+
|
|
913
|
+
- **全部通过且无 ⏭**:提示 "可以用 \`/marchen:archive\` 归档。"
|
|
914
|
+
- **有 ❌ 失败**:提示用户修复后可以再次 \`/marchen:review\`。
|
|
915
|
+
- **有 ⏭ 跳过**:用 **AskUserQuestion** 让用户选择:
|
|
916
|
+
- **补信息后继续**:收集所需信息(账号、数据等),就地再跑被跳过的场景
|
|
917
|
+
- **跳过这些场景,继续归档**:保留报告,提示 \`/marchen:archive\`
|
|
918
|
+
- **暂停去修复**:什么都不做
|
|
919
|
+
|
|
920
|
+
如本次 review 启动了后台 dev server,再次提醒用户进程信息(PID/端口)。
|
|
921
|
+
|
|
922
|
+
**护栏**
|
|
923
|
+
|
|
924
|
+
- 不要修改任何代码,只报告(review 不是 apply)
|
|
925
|
+
- UI 验证阻塞即停,不要硬闯(不登录、不猜数据、不绕权限);要继续就向用户索取信息
|
|
926
|
+
- 起 dev server 必须先获得用户授权(AskUserQuestion);起完不自动 kill,告知 PID 让用户自行管理
|
|
927
|
+
- 用户提供的凭据/账号等敏感数据:不写入任何 artifact,不在报告里复述明文,截图前对密码/token/邮箱等敏感字段脱敏或回避
|
|
928
|
+
- 大 diff 先看 \`git diff --stat\`,按需读单个文件,避免灌爆 context
|
|
929
|
+
- 使用 AskUserQuestion 时,选项不超过 4 个
|
|
930
|
+
`}},x={antigravity:{id:`antigravity`,name:`Antigravity`,skillDir:`.agent/skills`},"claude-code":{id:`claude-code`,name:`Claude Code`,skillDir:`.claude/skills`,commandDir:`.claude/commands/marchen`},codex:{id:`codex`,name:`Codex`,skillDir:`.codex/skills`},copilot:{id:`copilot`,name:`GitHub Copilot`,skillDir:`.github/skills`},cursor:{id:`cursor`,name:`Cursor`,skillDir:`.cursor/skills`},"gemini-cli":{id:`gemini-cli`,name:`Gemini CLI`,skillDir:`.gemini/skills`},kilocode:{id:`kilocode`,name:`Kilo Code`,skillDir:`.kilocode/skills`},kiro:{id:`kiro`,name:`Kiro`,skillDir:`.kiro/skills`},opencode:{id:`opencode`,name:`OpenCode`,skillDir:`.opencode/skills`},windsurf:{id:`windsurf`,name:`Windsurf`,skillDir:`.windsurf/skills`}},S=[`claude-code`],C={full:{name:`full`,artifacts:[{id:`proposal`,generates:`proposal.md`,requires:[],template:`## 动机
|
|
931
|
+
|
|
932
|
+
<!-- 说明这个变更的动机。解决什么问题?为什么现在做? -->
|
|
933
|
+
|
|
934
|
+
## 变更内容
|
|
935
|
+
|
|
936
|
+
<!-- 描述具体变更内容。列出新增的能力、修改或移除的部分。 -->
|
|
937
|
+
|
|
938
|
+
## 能力
|
|
939
|
+
|
|
940
|
+
### 新增能力
|
|
941
|
+
<!-- 新增的能力。使用 kebab-case 命名,每个会生成 specs/<name>/spec.md -->
|
|
942
|
+
|
|
943
|
+
### 修改能力
|
|
944
|
+
<!-- 需要修改的现有能力。仅当规范级别的行为发生变化时才列出。 -->
|
|
945
|
+
|
|
946
|
+
## 影响范围
|
|
947
|
+
|
|
948
|
+
<!-- 受影响的代码、API、依赖或系统 -->
|
|
949
|
+
`,instruction:`根据用户的描述,填写 proposal 的各个部分。
|
|
950
|
+
- 动机:说明为什么要做这个变更,解决什么问题
|
|
951
|
+
- 变更内容:列出具体改动
|
|
952
|
+
- 能力:列出需要创建 spec 的 capability(kebab-case 命名),每个会生成 specs/<name>/spec.md
|
|
953
|
+
- 影响范围:说明涉及的代码和系统
|
|
954
|
+
保持简洁,1-2 页。聚焦"为什么"和"做什么",不要写实现细节。`},{id:`specs`,generates:`specs/`,requires:[`proposal`],template:`## 目的
|
|
955
|
+
|
|
956
|
+
<!-- 这个能力的领域职责,一两句话 -->
|
|
957
|
+
|
|
958
|
+
### 需求: <!-- 需求名称 -->
|
|
959
|
+
|
|
960
|
+
<!-- 系统 SHALL/MUST 做什么(必须有描述文本) -->
|
|
961
|
+
|
|
962
|
+
#### 场景: <!-- 场景名称 -->
|
|
963
|
+
|
|
964
|
+
- **GIVEN** <!-- 前置条件/上下文 -->
|
|
965
|
+
- **WHEN** <!-- 触发动作 -->
|
|
966
|
+
- **THEN** <!-- 期望结果 -->
|
|
967
|
+
- **AND** <!-- 追加条件或结果 -->
|
|
968
|
+
`,instruction:`根据 proposal 中列出的能力,为每个能力创建 specs/<name>/spec.md。
|
|
969
|
+
|
|
970
|
+
格式规则:
|
|
971
|
+
- 以 '## 目的' 开头,一两句话说明这个能力的领域职责
|
|
972
|
+
- 每个需求用 '### 需求:' 开头,标题后必须有描述文本(用 SHALL/MUST 表述行为)
|
|
973
|
+
- 每个场景用 '#### 场景:' 开头(必须 4 个 #)
|
|
974
|
+
- 场景内容用列表格式,关键字加粗:- **GIVEN** / - **WHEN** / - **THEN** / - **AND**
|
|
975
|
+
- GIVEN 描述前置条件(可省略,但推荐写明)
|
|
976
|
+
- 每个需求至少一个场景
|
|
977
|
+
- 场景应该是可测试的(每个场景对应一个潜在的 test case)
|
|
978
|
+
|
|
979
|
+
内容约束:
|
|
980
|
+
- 只描述可观察行为,不描述实现方式
|
|
981
|
+
- 不包含内部类名、函数名、框架/库选择等实现细节(这些属于 design.md)
|
|
982
|
+
- 使用 RFC 2119 关键字表达需求强度:SHALL/MUST(必须)、SHOULD(推荐)、MAY(可选)`},{id:`design`,generates:`design.md`,requires:[`proposal`],template:`## 背景
|
|
983
|
+
|
|
984
|
+
<!-- 背景和当前状态 -->
|
|
985
|
+
|
|
986
|
+
## 目标与非目标
|
|
987
|
+
|
|
988
|
+
**目标:**
|
|
989
|
+
<!-- 这个设计要达成什么 -->
|
|
990
|
+
|
|
991
|
+
**非目标:**
|
|
992
|
+
<!-- 明确排除在范围之外的内容 -->
|
|
993
|
+
|
|
994
|
+
## 决策
|
|
995
|
+
|
|
996
|
+
<!-- 关键技术决策和理由 -->
|
|
997
|
+
|
|
998
|
+
## 风险与权衡
|
|
999
|
+
|
|
1000
|
+
<!-- 已知风险和权衡 -->
|
|
1001
|
+
`,instruction:`根据 proposal 的动机和 specs 的需求,设计技术实现方案。
|
|
1002
|
+
- 背景:当前状态和约束
|
|
1003
|
+
- 目标与非目标:明确范围
|
|
1004
|
+
- 决策:关键技术选择和理由(为什么选 X 而不是 Y)
|
|
1005
|
+
- 风险与权衡:已知限制和可能出错的地方
|
|
1006
|
+
聚焦架构和方案,不要写逐行实现细节。`},{id:`tasks`,generates:`tasks.md`,requires:[`specs`,`design`],template:`## 1. <!-- 任务组名称 -->
|
|
1007
|
+
|
|
1008
|
+
- [ ] 1.1 <!-- 任务描述 -->
|
|
1009
|
+
- [ ] 1.2 <!-- 任务描述 -->
|
|
1010
|
+
`,instruction:`根据 specs 的需求和 design 的技术方案,拆分实现任务。
|
|
1011
|
+
- 按任务组分组,每组用 ## 标题
|
|
1012
|
+
- 每个任务用 checkbox 格式:- [ ] X.Y 描述
|
|
1013
|
+
- 任务粒度要小到一个会话内能完成
|
|
1014
|
+
- 按依赖顺序排列(先做的在前)`}]},lite:{name:`lite`,artifacts:[{id:`tasks`,generates:`tasks.md`,requires:[],template:`## 背景
|
|
1015
|
+
|
|
1016
|
+
<!-- 简要说明这个变更的目的和方案 -->
|
|
1017
|
+
|
|
1018
|
+
## 1. <!-- 任务组名称 -->
|
|
1019
|
+
|
|
1020
|
+
- [ ] 1.1 <!-- 任务描述 -->
|
|
1021
|
+
- [ ] 1.2 <!-- 任务描述 -->
|
|
1022
|
+
`,instruction:`根据背景描述,拆分实现任务。
|
|
1023
|
+
- 按任务组分组,每组用 ## 标题
|
|
1024
|
+
- 每个任务用 checkbox 格式:- [ ] X.Y 描述
|
|
1025
|
+
- 任务粒度要小到一个会话内能完成
|
|
1026
|
+
- 按依赖顺序排列(先做的在前)`}]}};function w(e){let t=C[e];if(!t)throw new _(`Schema "${e}" 不存在,可用的 schema: ${Object.keys(C).join(`, `)}`);return t}const T={apply:{dirName:`marchen-apply`,content:`---
|
|
1027
|
+
name: marchen-apply
|
|
1028
|
+
description: 按变更的 tasks.md 逐个实现任务。适用于用户想按任务清单逐步实现代码。
|
|
1029
|
+
---
|
|
1030
|
+
|
|
1031
|
+
按变更的 tasks.md 逐个实现任务,完成后勾选 checkbox。
|
|
1032
|
+
|
|
1033
|
+
---
|
|
1034
|
+
|
|
1035
|
+
**输入**:用户的请求应包含变更名称,或可从上下文推断。
|
|
1036
|
+
|
|
1037
|
+
**流程**
|
|
1038
|
+
|
|
1039
|
+
1. **选择变更**
|
|
1040
|
+
|
|
1041
|
+
有名称就用,没有则:
|
|
1042
|
+
- 从对话上下文推断
|
|
1043
|
+
- 只有一个 open 变更时自动选择
|
|
1044
|
+
- 多个变更时 \`marchen list --json\` + **AskUserQuestion** 让用户选
|
|
1045
|
+
|
|
1046
|
+
显示:"使用变更: \`<name>\`"
|
|
1047
|
+
|
|
1048
|
+
2. **获取实现指令**
|
|
1049
|
+
|
|
1050
|
+
\`\`\`bash
|
|
1051
|
+
marchen instructions <name> apply --json
|
|
1052
|
+
\`\`\`
|
|
1053
|
+
|
|
1054
|
+
返回 JSON 包含:
|
|
1055
|
+
- \`state\`:\`"ready"\` / \`"blocked"\` / \`"all_done"\`
|
|
1056
|
+
- \`progress\`:\`{ total, completed, remaining }\`
|
|
1057
|
+
- \`context\`:所有 artifact 的信息数组,每项包含 \`id\`、\`status\`、\`path\`、\`content\`
|
|
1058
|
+
- \`instruction\`:实现指引
|
|
1059
|
+
- \`changeDir\`:变更目录绝对路径
|
|
1060
|
+
|
|
1061
|
+
根据 \`state\` 处理:
|
|
1062
|
+
- \`"blocked"\` → 提示先完成 artifacts
|
|
1063
|
+
- \`"all_done"\` → 提示归档
|
|
1064
|
+
- \`"ready"\` → 继续
|
|
1065
|
+
|
|
1066
|
+
3. **显示进度**
|
|
1067
|
+
|
|
1068
|
+
从返回的 JSON 读取并显示:
|
|
1069
|
+
"变更: \`<name>\` | 进度: N/M | 下一个: <第一个未完成任务的描述>"
|
|
1070
|
+
|
|
1071
|
+
4. **逐个实现任务**
|
|
1072
|
+
|
|
1073
|
+
从 \`context\` 中读取所有 artifact 内容作为上下文。
|
|
1074
|
+
|
|
1075
|
+
对每个未完成任务:
|
|
1076
|
+
- 显示 "任务 N/M: <描述>"
|
|
1077
|
+
- 实现代码改动
|
|
1078
|
+
- 在 tasks.md 中勾选:\`- [ ]\` → \`- [x]\`
|
|
1079
|
+
文件路径:\`<changeDir>/tasks.md\`
|
|
1080
|
+
- 显示 "✓ 完成"
|
|
1081
|
+
- 继续下一个
|
|
1082
|
+
|
|
1083
|
+
**暂停条件:**
|
|
1084
|
+
- 任务不清晰 → 询问用户
|
|
1085
|
+
- 发现设计问题 → 建议更新 artifact
|
|
1086
|
+
- 遇到错误或阻塞 → 报告并等待
|
|
1087
|
+
- 用户中断
|
|
1088
|
+
|
|
1089
|
+
5. **显示结果**
|
|
1090
|
+
|
|
1091
|
+
全部完成时:
|
|
1092
|
+
"全部完成 (N/N),可以用 \`marchen review <name>\` 检查实现,或直接 \`marchen archive <name>\` 归档。"
|
|
1093
|
+
|
|
1094
|
+
暂停时:
|
|
1095
|
+
"暂停于任务 N/M: <原因>"
|
|
1096
|
+
|
|
1097
|
+
**护栏**
|
|
1098
|
+
|
|
1099
|
+
- 实现前必须读 context 中的 artifact 内容
|
|
1100
|
+
- 每完成一个任务立即勾选 checkbox,不要攒着
|
|
1101
|
+
- 改动最小化,只做任务要求的事
|
|
1102
|
+
- 不确定就暂停问,不要猜
|
|
1103
|
+
- 如果实现过程中遇到不确定的设计决策,可以用 \`marchen search "<关键词>" --json\` 搜索历史变更中的相关方案
|
|
1104
|
+
- \`instruction\` 是给你的指引,不要原样复制到代码注释中
|
|
1105
|
+
`},archive:{dirName:`marchen-archive`,content:`---
|
|
1106
|
+
name: marchen-archive
|
|
1107
|
+
description: 归档已完成的变更。检查完成度后执行归档,适用于实现完毕后收尾。
|
|
1108
|
+
---
|
|
1109
|
+
|
|
1110
|
+
归档已完成的变更,检查完成度后执行归档。
|
|
1111
|
+
|
|
1112
|
+
---
|
|
1113
|
+
|
|
1114
|
+
**输入**:用户的请求可包含变更名称(如 \`/marchen:archive add-auth\`),也可不带。
|
|
1115
|
+
|
|
1116
|
+
**流程**
|
|
1117
|
+
|
|
1118
|
+
1. **确定变更名称**
|
|
1119
|
+
|
|
1120
|
+
有名称就用,没有则:
|
|
1121
|
+
- 从对话上下文推断
|
|
1122
|
+
- 只有一个 open 变更时自动选择
|
|
1123
|
+
- 多个变更时 \`marchen list --json\` + **AskUserQuestion** 让用户选
|
|
1124
|
+
|
|
1125
|
+
**重要**:不要猜测或自动选择,必须让用户确认。
|
|
1126
|
+
|
|
1127
|
+
2. **检查完成度**
|
|
1128
|
+
|
|
1129
|
+
\`\`\`bash
|
|
1130
|
+
marchen status <name> --json
|
|
1131
|
+
\`\`\`
|
|
1132
|
+
|
|
1133
|
+
解析 JSON,检查:
|
|
1134
|
+
- \`artifacts\`:每个 artifact 的 \`status\` 是否为 \`filled\`
|
|
1135
|
+
- \`tasks.completed\` vs \`tasks.total\`(\`tasks\` 为 null 时视为无任务,跳过检查)
|
|
1136
|
+
|
|
1137
|
+
**如果全部完成:** 直接进入下一步。
|
|
1138
|
+
|
|
1139
|
+
**如果有未完成的 artifact 或 task:**
|
|
1140
|
+
- 显示警告,列出未完成项
|
|
1141
|
+
- 用 **AskUserQuestion** 确认是否继续
|
|
1142
|
+
- 用户确认后继续,不阻塞
|
|
1143
|
+
|
|
1144
|
+
3. **生成摘要**
|
|
1145
|
+
|
|
1146
|
+
读取 \`marchen/changes/<name>/proposal.md\`,从中生成一句话中文摘要(≤50字),概括这次变更做了什么。摘要应包含关键语义词,便于后续 AI 检索。
|
|
1147
|
+
|
|
1148
|
+
4. **执行归档**
|
|
1149
|
+
|
|
1150
|
+
\`\`\`bash
|
|
1151
|
+
marchen archive <name> --summary "<生成的摘要>" --json
|
|
1152
|
+
\`\`\`
|
|
1153
|
+
|
|
1154
|
+
解析返回的 JSON 获取归档结果。
|
|
1155
|
+
|
|
1156
|
+
5. **显示结果**
|
|
1157
|
+
|
|
1158
|
+
\`\`\`
|
|
1159
|
+
变更 "<name>" 已归档
|
|
1160
|
+
Schema: <schema>
|
|
1161
|
+
归档到: <archivedTo>
|
|
1162
|
+
\`\`\`
|
|
1163
|
+
|
|
1164
|
+
如有警告(未完成项),一并显示。
|
|
1165
|
+
|
|
1166
|
+
**护栏**
|
|
1167
|
+
|
|
1168
|
+
- 未提供名称时必须用 AskUserQuestion 让用户选择
|
|
1169
|
+
- 用 \`status --json\` 检查完成度,不要自己读文件判断
|
|
1170
|
+
- 警告不阻塞归档,只提醒 + 确认
|
|
1171
|
+
- 使用 AskUserQuestion 时,选项不超过 4 个
|
|
1172
|
+
`},explore:{dirName:`marchen-explore`,content:`---
|
|
1173
|
+
name: marchen-explore
|
|
1174
|
+
description: 进入探索模式 — 思考想法、调查问题、厘清需求。适用于用户想在动手之前先理清思路。
|
|
1175
|
+
---
|
|
1176
|
+
|
|
1177
|
+
进入探索模式。深入思考,自由可视化,跟随对话走向任何方向。
|
|
1178
|
+
|
|
1179
|
+
**重要:探索模式只用于思考,不用于实现。** 可以读文件、搜索代码、调查代码库,但绝不能写代码或实现功能。如果用户要求实现,提醒他们先退出探索模式并用 \`/marchen:propose\` 创建变更。可以创建 Marchen artifact(proposal、design、spec)——那是捕获思考,不是实现。
|
|
1180
|
+
|
|
1181
|
+
**这是一种姿态,不是工作流。** 没有固定步骤、没有必须的顺序、没有强制输出。你是帮助用户探索的思考伙伴。
|
|
1182
|
+
|
|
1183
|
+
**输入**:\`/marchen:explore\` 后面可以跟任何内容:
|
|
1184
|
+
- 模糊的想法:"实时协作"
|
|
1185
|
+
- 具体的问题:"auth 系统越来越难维护了"
|
|
1186
|
+
- 变更名称:"add-dark-mode"(在该变更上下文中探索)
|
|
1187
|
+
- 方案比较:"postgres vs sqlite"
|
|
1188
|
+
- 什么都不带也行
|
|
1189
|
+
|
|
1190
|
+
---
|
|
1191
|
+
|
|
1192
|
+
## 姿态
|
|
1193
|
+
|
|
1194
|
+
- **好奇,不武断** — 提出自然涌现的问题,不按脚本走
|
|
1195
|
+
- **开放线索,不审问** — 展示多个有趣方向,让用户跟随感兴趣的。不要把他们引导到单一路径上
|
|
1196
|
+
- **视觉化** — 大量使用 ASCII 图表来辅助思考
|
|
1197
|
+
- **自适应** — 跟随有趣的线索,有新信息时及时转向
|
|
1198
|
+
- **耐心** — 不急于下结论,让问题的形状自然浮现
|
|
1199
|
+
- **接地气** — 在相关时探索实际代码库,不要空谈理论
|
|
1200
|
+
|
|
1201
|
+
---
|
|
1202
|
+
|
|
1203
|
+
## 你可以做什么
|
|
1204
|
+
|
|
1205
|
+
根据用户带来的内容,你可能会:
|
|
1206
|
+
|
|
1207
|
+
**探索问题空间**
|
|
1208
|
+
- 提出从用户所说内容中自然涌现的澄清问题
|
|
1209
|
+
- 挑战假设
|
|
1210
|
+
- 重新框定问题
|
|
1211
|
+
- 寻找类比
|
|
1212
|
+
|
|
1213
|
+
**调查代码库**
|
|
1214
|
+
- 映射与讨论相关的现有架构
|
|
1215
|
+
- 找到集成点
|
|
1216
|
+
- 识别已有的模式
|
|
1217
|
+
- 发现隐藏的复杂性
|
|
1218
|
+
|
|
1219
|
+
**比较方案**
|
|
1220
|
+
- 头脑风暴多种方案
|
|
1221
|
+
- 构建比较表
|
|
1222
|
+
- 勾勒权衡
|
|
1223
|
+
- 推荐路径(如果被问到)
|
|
1224
|
+
|
|
1225
|
+
**可视化**
|
|
1226
|
+
\`\`\`
|
|
1227
|
+
┌─────────────────────────────────────────┐
|
|
1228
|
+
│ Use ASCII diagrams liberally │
|
|
1229
|
+
├─────────────────────────────────────────┤
|
|
1230
|
+
│ │
|
|
1231
|
+
│ ┌────────┐ ┌────────┐ │
|
|
1232
|
+
│ │ State │────────▶│ State │ │
|
|
1233
|
+
│ │ A │ │ B │ │
|
|
1234
|
+
│ └────────┘ └────────┘ │
|
|
1235
|
+
│ │
|
|
1236
|
+
│ System diagrams, state machines, │
|
|
1237
|
+
│ data flows, architecture sketches, │
|
|
1238
|
+
│ dependency graphs, comparison tables │
|
|
1239
|
+
│ │
|
|
1240
|
+
└─────────────────────────────────────────┘
|
|
1241
|
+
\`\`\`
|
|
1242
|
+
|
|
1243
|
+
**发现风险和未知**
|
|
1244
|
+
- 识别可能出错的地方
|
|
1245
|
+
- 找到理解上的空白
|
|
1246
|
+
- 建议 spike 或调查
|
|
1247
|
+
|
|
1248
|
+
---
|
|
1249
|
+
|
|
1250
|
+
## Marchen 感知
|
|
1251
|
+
|
|
1252
|
+
你了解 Marchen 系统。自然地使用它,不要强制。
|
|
1253
|
+
|
|
1254
|
+
### 检查上下文
|
|
1255
|
+
|
|
1256
|
+
开始时快速检查现有状态和历史:
|
|
1257
|
+
|
|
1258
|
+
1. 当前变更:
|
|
1259
|
+
\`\`\`bash
|
|
1260
|
+
marchen list --json
|
|
1261
|
+
\`\`\`
|
|
1262
|
+
|
|
1263
|
+
2. 变更历史概览:
|
|
1264
|
+
\`\`\`bash
|
|
1265
|
+
cat marchen/changelog.md
|
|
1266
|
+
\`\`\`
|
|
1267
|
+
这是所有已归档变更的索引,每条包含日期、变更名和一句话摘要。先扫一遍找到与用户话题相关的条目。如果找到,直接读对应 archive 目录下的 proposal.md 或 design.md 了解详情。
|
|
1268
|
+
|
|
1269
|
+
3. 语义搜索:
|
|
1270
|
+
|
|
1271
|
+
这是 RAG 搜索,不是 grep——构造语义完整的短语,不要用单个泛词。
|
|
1272
|
+
|
|
1273
|
+
\`\`\`bash
|
|
1274
|
+
marchen search "<语义完整的查询短语>" --json
|
|
1275
|
+
\`\`\`
|
|
1276
|
+
|
|
1277
|
+
**查询构造指引:**
|
|
1278
|
+
- 用描述性短语,不用单个词:
|
|
1279
|
+
"初始化适配多个 agent 客户端" → \`"multi-agent provider 初始化"\`
|
|
1280
|
+
"之前怎么处理错误的" → \`"错误处理 error handling 重构"\`
|
|
1281
|
+
"暗色模式的设计决策" → \`"dark mode 设计方案"\`
|
|
1282
|
+
- 中英文混合效果更好(归档内容里中英都有)
|
|
1283
|
+
- 如果结果不理想,换个角度重新构造查询
|
|
1284
|
+
|
|
1285
|
+
如果有匹配结果(score >= 0.4),读取对应 archive 目录下的 design.md 或 proposal.md 了解详细决策。
|
|
1286
|
+
|
|
1287
|
+
如果 \`marchen search\` 不可用(命令报错),回退到 changelog.md + 手动读 archive 目录。
|
|
1288
|
+
|
|
1289
|
+
这告诉你:
|
|
1290
|
+
- 是否有进行中的变更
|
|
1291
|
+
- 它们的名称、schema 和状态
|
|
1292
|
+
- 项目过去做过哪些变更
|
|
1293
|
+
- 用户可能在做什么
|
|
1294
|
+
|
|
1295
|
+
如果用户提到了特定变更名称,读取它的 artifact 作为上下文。
|
|
1296
|
+
|
|
1297
|
+
### 没有变更时
|
|
1298
|
+
|
|
1299
|
+
自由思考。当洞察结晶时,根据复杂度推荐下一步:
|
|
1300
|
+
|
|
1301
|
+
**判断标准:**
|
|
1302
|
+
- \`/marchen:lite\` — bug 修复、小改动、单一任务组、不需要设计文档
|
|
1303
|
+
- \`/marchen:propose\` — 新功能、多步骤、需要 design/specs、涉及多模块
|
|
1304
|
+
|
|
1305
|
+
**推荐方式:** 直接在回复中输出推荐,说明理由,让用户自行输入命令。示例:
|
|
1306
|
+
|
|
1307
|
+
> 想法差不多成型了。这个改动比较简单(只涉及一个文件的小调整),建议用 \`/marchen:lite\` 直接走轻量流程。
|
|
1308
|
+
>
|
|
1309
|
+
> 如果你觉得需要更完整的设计文档,也可以用 \`/marchen:propose\`。
|
|
1310
|
+
|
|
1311
|
+
根据讨论内容给出你的推荐和理由,但让用户自己决定输入哪个命令。
|
|
1312
|
+
|
|
1313
|
+
### 有变更时
|
|
1314
|
+
|
|
1315
|
+
如果用户提到了变更或你发现某个变更相关:
|
|
1316
|
+
|
|
1317
|
+
1. **读取已有 artifact 作为上下文**
|
|
1318
|
+
- \`marchen/changes/<name>/proposal.md\`
|
|
1319
|
+
- \`marchen/changes/<name>/design.md\`
|
|
1320
|
+
- \`marchen/changes/<name>/tasks.md\`
|
|
1321
|
+
- 等
|
|
1322
|
+
|
|
1323
|
+
2. **在对话中自然引用**
|
|
1324
|
+
- "你的 design 提到用 Redis,但我们刚发现 SQLite 更合适……"
|
|
1325
|
+
- "proposal 把范围限定在付费用户,但我们现在觉得应该面向所有人……"
|
|
1326
|
+
|
|
1327
|
+
3. **在做出决策时提议捕获**
|
|
1328
|
+
|
|
1329
|
+
| 洞察类型 | 捕获到哪里 |
|
|
1330
|
+
|---------|-----------|
|
|
1331
|
+
| 发现新需求 | \`specs/<capability>/spec.md\` |
|
|
1332
|
+
| 需求变更 | \`specs/<capability>/spec.md\` |
|
|
1333
|
+
| 做出设计决策 | \`design.md\` |
|
|
1334
|
+
| 范围变更 | \`proposal.md\` |
|
|
1335
|
+
| 发现新工作 | \`tasks.md\` |
|
|
1336
|
+
| 假设被推翻 | 相关 artifact |
|
|
1337
|
+
|
|
1338
|
+
示例:
|
|
1339
|
+
- "这是一个设计决策。要记录到 design.md 吗?"
|
|
1340
|
+
- "这是新需求。要加到 specs 里吗?"
|
|
1341
|
+
- "这改变了范围。要更新 proposal 吗?"
|
|
1342
|
+
|
|
1343
|
+
4. **用户决定** — 提议后继续。不施压,不自动捕获。
|
|
1344
|
+
|
|
1345
|
+
---
|
|
1346
|
+
|
|
1347
|
+
## 不必做的事
|
|
1348
|
+
|
|
1349
|
+
- 按脚本走
|
|
1350
|
+
- 每次问同样的问题
|
|
1351
|
+
- 产出特定 artifact
|
|
1352
|
+
- 得出结论
|
|
1353
|
+
- 如果有价值的岔路就不必守住话题
|
|
1354
|
+
- 简短(这是思考时间)
|
|
1355
|
+
|
|
1356
|
+
---
|
|
1357
|
+
|
|
1358
|
+
## 结束探索
|
|
1359
|
+
|
|
1360
|
+
没有固定的结束方式。探索可能:
|
|
1361
|
+
|
|
1362
|
+
- **流向下一阶段**:用 **AskUserQuestion** 提供 \`/marchen:lite\` 和 \`/marchen:propose\` 选项,附带推荐理由
|
|
1363
|
+
- **更新 artifact**:"已将这些决策更新到 design.md"
|
|
1364
|
+
- **只是提供清晰度**:用户得到了需要的,继续前进
|
|
1365
|
+
- **稍后继续**:"随时可以继续"
|
|
1366
|
+
|
|
1367
|
+
当想法结晶时,你可以提供总结——但不是必须的。有时候思考过程本身就是价值。
|
|
1368
|
+
|
|
1369
|
+
---
|
|
1370
|
+
|
|
1371
|
+
## 护栏
|
|
1372
|
+
|
|
1373
|
+
- **不实现** — 绝不写代码或实现功能。创建 Marchen artifact 可以,写应用代码不行
|
|
1374
|
+
- **不伪装理解** — 不清楚就深挖
|
|
1375
|
+
- **不催促** — 探索是思考时间,不是任务时间
|
|
1376
|
+
- **不强制结构** — 让模式自然浮现
|
|
1377
|
+
- **不自动捕获** — 提议保存洞察,不要直接做
|
|
1378
|
+
- **要可视化** — 一张好图胜过千言万语
|
|
1379
|
+
- **要探索代码库** — 让讨论扎根于现实
|
|
1380
|
+
- **要质疑假设** — 包括用户的和你自己的
|
|
1381
|
+
`},lite:{dirName:`marchen-lite`,content:`---
|
|
1382
|
+
name: marchen-lite
|
|
1383
|
+
description: 一键式轻量变更流程。创建 lite 变更、实现任务、询问归档,一气呵成。适合 bug 修复、小改动。
|
|
1384
|
+
---
|
|
1385
|
+
|
|
1386
|
+
一键式轻量变更 — 使用 lite schema 创建变更,自动实现任务,完成后询问归档。
|
|
1387
|
+
适合 bug 修复、小改动、explore 之后的快速执行。
|
|
1388
|
+
|
|
1389
|
+
---
|
|
1390
|
+
|
|
1391
|
+
**输入**:用户的请求应包含变更名称(kebab-case)或变更描述。
|
|
1392
|
+
|
|
1393
|
+
**流程**
|
|
1394
|
+
|
|
1395
|
+
1. **确定变更名称**
|
|
1396
|
+
|
|
1397
|
+
如果提供了输入,直接使用或从描述中提取 kebab-case 名称(如"修复登录 bug" → \`fix-login-bug\`)。
|
|
1398
|
+
|
|
1399
|
+
如果没有输入,用 **AskUserQuestion** 工具询问:
|
|
1400
|
+
> "你想做什么变更?描述一下你要构建或修复的内容。"
|
|
1401
|
+
|
|
1402
|
+
从回答中提取 kebab-case 名称。
|
|
1403
|
+
|
|
1404
|
+
**重要**:必须理解用户想做什么才能继续。
|
|
1405
|
+
|
|
1406
|
+
2. **创建变更目录**
|
|
1407
|
+
|
|
1408
|
+
\`\`\`bash
|
|
1409
|
+
marchen new <name> --schema lite
|
|
1410
|
+
\`\`\`
|
|
1411
|
+
|
|
1412
|
+
创建 \`marchen/changes/<name>/\` 目录,包含 \`.metadata.yaml\` 和 \`tasks.md\` 骨架。
|
|
1413
|
+
|
|
1414
|
+
如果同名变更已存在,用 **AskUserQuestion** 询问用户是继续已有变更还是换个名称。
|
|
1415
|
+
|
|
1416
|
+
3. **获取 tasks 指令**
|
|
1417
|
+
|
|
1418
|
+
\`\`\`bash
|
|
1419
|
+
marchen status <name> --json
|
|
1420
|
+
\`\`\`
|
|
1421
|
+
|
|
1422
|
+
确认变更创建成功,然后获取 tasks 的创建指令:
|
|
1423
|
+
|
|
1424
|
+
\`\`\`bash
|
|
1425
|
+
marchen instructions <name> tasks --json
|
|
1426
|
+
\`\`\`
|
|
1427
|
+
|
|
1428
|
+
返回 JSON 包含:
|
|
1429
|
+
- \`template\`:tasks.md 的骨架结构(含 \`## 背景\` 章节)
|
|
1430
|
+
- \`instruction\`:如何填充 tasks 的指导文本
|
|
1431
|
+
- \`outputPath\`:写入路径(\`tasks.md\`)
|
|
1432
|
+
- \`context\`:上下文信息(lite schema 下为空数组)
|
|
1433
|
+
|
|
1434
|
+
4. **填充 tasks.md**
|
|
1435
|
+
|
|
1436
|
+
根据用户描述 + \`instruction\` 指引 + \`template\` 结构,填充 tasks.md:
|
|
1437
|
+
- \`## 背景\`:简要说明变更目的和方案
|
|
1438
|
+
- 任务列表:按组分类,checkbox 格式
|
|
1439
|
+
|
|
1440
|
+
写入 \`marchen/changes/<name>/tasks.md\`。
|
|
1441
|
+
|
|
1442
|
+
如果用户描述太模糊,用 **AskUserQuestion** 澄清关键信息。
|
|
1443
|
+
|
|
1444
|
+
5. **开始实现**
|
|
1445
|
+
|
|
1446
|
+
获取实现指令:
|
|
1447
|
+
|
|
1448
|
+
\`\`\`bash
|
|
1449
|
+
marchen instructions <name> apply --json
|
|
1450
|
+
\`\`\`
|
|
1451
|
+
|
|
1452
|
+
返回 JSON 包含:
|
|
1453
|
+
- \`state\`:\`"ready"\` / \`"blocked"\` / \`"all_done"\`
|
|
1454
|
+
- \`progress\`:\`{ total, completed, remaining }\`
|
|
1455
|
+
- \`context\`:所有 artifact 的信息数组
|
|
1456
|
+
- \`instruction\`:实现指引
|
|
1457
|
+
- \`changeDir\`:变更目录绝对路径
|
|
1458
|
+
|
|
1459
|
+
显示:"变更: \`<name>\` | 任务: 0/N | 开始实现..."
|
|
1460
|
+
|
|
1461
|
+
对每个未完成任务:
|
|
1462
|
+
- 显示 "任务 N/M: <描述>"
|
|
1463
|
+
- 实现代码改动
|
|
1464
|
+
- 在 tasks.md 中勾选:\`- [ ]\` → \`- [x]\`
|
|
1465
|
+
文件路径:\`<changeDir>/tasks.md\`
|
|
1466
|
+
- 显示 "✓ 完成"
|
|
1467
|
+
- 继续下一个
|
|
1468
|
+
|
|
1469
|
+
**暂停条件:**
|
|
1470
|
+
- 任务不清晰 → 询问用户
|
|
1471
|
+
- 发现设计问题 → 建议更新 artifact
|
|
1472
|
+
- 遇到错误或阻塞 → 报告并等待
|
|
1473
|
+
- 用户中断
|
|
1474
|
+
|
|
1475
|
+
暂停时显示:"暂停于任务 N/M: <原因>",流程结束。
|
|
1476
|
+
|
|
1477
|
+
6. **全部完成 → 询问归档**
|
|
1478
|
+
|
|
1479
|
+
所有任务完成后,用 **AskUserQuestion** 询问:
|
|
1480
|
+
|
|
1481
|
+
> "全部任务已完成 (N/N),是否归档这个变更?"
|
|
1482
|
+
> - 归档
|
|
1483
|
+
> - 暂不归档
|
|
1484
|
+
|
|
1485
|
+
**如果用户选择归档:**
|
|
1486
|
+
|
|
1487
|
+
读取 \`marchen/changes/<name>/tasks.md\` 的背景段,生成一句话中文摘要(≤50字)。
|
|
1488
|
+
|
|
1489
|
+
\`\`\`bash
|
|
1490
|
+
marchen archive <name> --summary "<摘要>" --json
|
|
1491
|
+
\`\`\`
|
|
1492
|
+
|
|
1493
|
+
显示:
|
|
1494
|
+
\`\`\`
|
|
1495
|
+
变更 "<name>" 已归档
|
|
1496
|
+
归档到: <archivedTo>
|
|
1497
|
+
\`\`\`
|
|
1498
|
+
|
|
1499
|
+
**如果用户选择暂不归档:**
|
|
1500
|
+
|
|
1501
|
+
显示:"好的,后续可以用 \`/marchen:archive <name>\` 归档。"
|
|
1502
|
+
|
|
1503
|
+
**护栏**
|
|
1504
|
+
|
|
1505
|
+
- 必须使用 \`--schema lite\` 创建变更
|
|
1506
|
+
- tasks.md 的 \`## 背景\` 章节必须填写,不能留空
|
|
1507
|
+
- 任务粒度要小到一个会话内能完成
|
|
1508
|
+
- 如果上下文关键信息不清楚,询问用户;但小疑问优先做合理判断,保持节奏
|
|
1509
|
+
- 已存在同名变更时必须询问用户,不要覆盖
|
|
1510
|
+
- 实现前必须读 context 中的 artifact 内容
|
|
1511
|
+
- 每完成一个任务立即勾选 checkbox,不要攒着
|
|
1512
|
+
- 改动最小化,只做任务要求的事
|
|
1513
|
+
- 不确定就暂停问,不要猜
|
|
1514
|
+
- \`instruction\` 是给你的指引,不要把它原样复制到代码注释或 tasks.md 中
|
|
1515
|
+
- 使用 AskUserQuestion 时,选项不超过 4 个
|
|
1516
|
+
`},"propose-preview":{dirName:`marchen-propose-preview`,content:`---
|
|
1517
|
+
name: marchen-propose-preview
|
|
1518
|
+
description: 预览 propose 生成的变更摘要。从 proposal/design/specs/tasks 浓缩为终端卡片,便于人快速 review,决定下一步是 apply 还是改 propose。
|
|
1519
|
+
disable-model-invocation: true
|
|
1520
|
+
argument-hint: <change-name>
|
|
1521
|
+
---
|
|
1522
|
+
|
|
1523
|
+
预览一个变更的浓缩摘要 — 纯终端输出,不写文件。
|
|
1524
|
+
|
|
1525
|
+
适用于 propose 完成后人快速 review,避免来回翻 4~7 个 artifact 文件。
|
|
1526
|
+
|
|
1527
|
+
---
|
|
1528
|
+
|
|
1529
|
+
**输入**:用户的请求应包含变更名称,或可从上下文推断。
|
|
1530
|
+
|
|
1531
|
+
**流程**
|
|
1532
|
+
|
|
1533
|
+
1. **选择变更**
|
|
1534
|
+
|
|
1535
|
+
有名称就用,没有则:
|
|
1536
|
+
- 从对话上下文推断
|
|
1537
|
+
- 只有一个 open 变更时自动选择
|
|
1538
|
+
- 多个变更时 \`marchen list --json\` + **AskUserQuestion** 让用户选
|
|
1539
|
+
|
|
1540
|
+
显示:"Preview 变更: \`<name>\`"
|
|
1541
|
+
|
|
1542
|
+
2. **获取 artifact 内容**
|
|
1543
|
+
|
|
1544
|
+
\`\`\`bash
|
|
1545
|
+
marchen instructions <name> apply --json
|
|
1546
|
+
\`\`\`
|
|
1547
|
+
|
|
1548
|
+
返回 JSON 包含:
|
|
1549
|
+
- \`schemaName\`:\`"full"\` / \`"lite"\`
|
|
1550
|
+
- \`state\`:\`"ready"\` / \`"blocked"\` / \`"all_done"\`
|
|
1551
|
+
- \`progress\`:\`{ total, completed, remaining }\`
|
|
1552
|
+
- \`context\`:所有 artifact 的内容数组(\`id\` / \`status\` / \`content\`)
|
|
1553
|
+
|
|
1554
|
+
**如果 state 为 \`blocked\`**:打印 "变更未填完,先用 /marchen:propose 补齐 artifact 再预览",结束。不要强行摘要半成品。
|
|
1555
|
+
|
|
1556
|
+
3. **生成卡片并直接打印**
|
|
1557
|
+
|
|
1558
|
+
根据 \`schemaName\` 选模板:
|
|
1559
|
+
- \`full\` → 四段:改了什么 / 关键决策 / 影响范围 / 风险
|
|
1560
|
+
- \`lite\` → 两段:改了什么 / 任务概览
|
|
1561
|
+
|
|
1562
|
+
严格按下面的"摘要规则"生成,输出为单个卡片,禁止加任何解释段落。
|
|
1563
|
+
|
|
1564
|
+
4. **末尾追加一行下一步提示**
|
|
1565
|
+
|
|
1566
|
+
\`\`\`
|
|
1567
|
+
/marchen:apply <name> 开始实现
|
|
1568
|
+
/marchen:propose <name> 修改提案
|
|
1569
|
+
\`\`\`
|
|
1570
|
+
|
|
1571
|
+
---
|
|
1572
|
+
|
|
1573
|
+
## 摘要规则(必须严格遵守)
|
|
1574
|
+
|
|
1575
|
+
**通用约束:**
|
|
1576
|
+
|
|
1577
|
+
- 卡片框宽 70 字符(含 \`│\` 边框),便于主流终端整齐显示
|
|
1578
|
+
- 每行内容(去掉边框后)≤ 60 字符;超出必须截断/合并/重写,不许折行
|
|
1579
|
+
- 禁止粘贴 artifact 原文片段,所有内容必须重新组织、压缩
|
|
1580
|
+
- 中文为主,技术名词保留英文(如 \`SearchManager\`、\`SDK\`)
|
|
1581
|
+
- 宁可少写,不要堆。摘不下就合并或截断,在框底加 \`更多详情见 marchen/changes/<name>/\`
|
|
1582
|
+
|
|
1583
|
+
**full schema 段落上限:**
|
|
1584
|
+
|
|
1585
|
+
| 段落 | 上限 | 来源 |
|
|
1586
|
+
|---------|------------------|---------------------------------|
|
|
1587
|
+
| 改了什么 | 6 条 bullet | proposal 的"能力"小节 |
|
|
1588
|
+
| 关键决策 | 5 条 bullet | design 的"决策"小节 |
|
|
1589
|
+
| 影响范围 | 8 节点 ASCII 图 | proposal 的"影响范围" + design |
|
|
1590
|
+
| 风险 | 3 条 bullet | design 的"风险与权衡" |
|
|
1591
|
+
|
|
1592
|
+
**lite schema 段落上限:**
|
|
1593
|
+
|
|
1594
|
+
| 段落 | 上限 | 来源 |
|
|
1595
|
+
|---------|------------------|-------------------------------|
|
|
1596
|
+
| 改了什么 | 6 条 bullet | tasks.md 任务组标题 + 推断 |
|
|
1597
|
+
| 任务概览 | 1 行/任务组 | tasks.md 一级标题 + 完成进度 |
|
|
1598
|
+
|
|
1599
|
+
**影响范围图退化策略:**
|
|
1600
|
+
|
|
1601
|
+
如果节点数超 8 个 / 关系不清晰 / 没有明显依赖结构 → 改用 bullet 列模块名,不要硬画歪斜的 ASCII。
|
|
1602
|
+
|
|
1603
|
+
**任务进度条规则(lite):**
|
|
1604
|
+
|
|
1605
|
+
进度条固定 10 格宽,按 \`completed/total\` 比例画。例如 \`8/8\` → \`██████████\`,\`3/5\` → \`██████░░░░\`。
|
|
1606
|
+
|
|
1607
|
+
---
|
|
1608
|
+
|
|
1609
|
+
## 输出样例
|
|
1610
|
+
|
|
1611
|
+
**full schema:**
|
|
1612
|
+
|
|
1613
|
+
\`\`\`
|
|
1614
|
+
╭─ <name> ────────────────────────────── full · <N> 任务 ─╮
|
|
1615
|
+
│ │
|
|
1616
|
+
│ <一句话动机,从 proposal "动机" 段提炼,≤ 55 字> │
|
|
1617
|
+
│ │
|
|
1618
|
+
│ ━━ 改了什么 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
1619
|
+
│ ✚ <capability-name> <一句话能力描述> │
|
|
1620
|
+
│ ... │
|
|
1621
|
+
│ │
|
|
1622
|
+
│ ━━ 关键决策 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
1623
|
+
│ 1. <决策内容> │
|
|
1624
|
+
│ ... │
|
|
1625
|
+
│ │
|
|
1626
|
+
│ ━━ 影响范围 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
1627
|
+
│ <ASCII 图 或 bullet 模块列表> │
|
|
1628
|
+
│ │
|
|
1629
|
+
│ ━━ 风险 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
1630
|
+
│ • <风险> │
|
|
1631
|
+
│ │
|
|
1632
|
+
╰──────────────────────────────────────────────────────────╯
|
|
1633
|
+
\`\`\`
|
|
1634
|
+
|
|
1635
|
+
**lite schema:**
|
|
1636
|
+
|
|
1637
|
+
\`\`\`
|
|
1638
|
+
╭─ <name> ──────────────────────────── lite · <N> 任务 ─╮
|
|
1639
|
+
│ │
|
|
1640
|
+
│ <一句话动机,从 tasks.md "背景" 段提炼> │
|
|
1641
|
+
│ │
|
|
1642
|
+
│ ━━ 改了什么 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
1643
|
+
│ • <推断的核心变更> │
|
|
1644
|
+
│ │
|
|
1645
|
+
│ ━━ 任务概览 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
1646
|
+
│ 1. <任务组标题> ██████████ N/M ✓ │
|
|
1647
|
+
│ │
|
|
1648
|
+
╰───────────────────────────────────────────────────────╯
|
|
1649
|
+
\`\`\`
|
|
1650
|
+
|
|
1651
|
+
**护栏**
|
|
1652
|
+
|
|
1653
|
+
- 不写文件,纯 stdout 输出
|
|
1654
|
+
- 不调用 AskUserQuestion 询问"要打印什么",遵守摘要规则即可
|
|
1655
|
+
- 不展开补全 artifact 信息——artifact 里没有的,摘要里就不该有
|
|
1656
|
+
- state 为 \`blocked\` 时拒绝生成,不强行摘要半成品
|
|
1657
|
+
- 不要在卡片外加解释段落,只打印卡片 + 下一步提示
|
|
1658
|
+
- \`instruction\` 字段(apply JSON 里有)是给 apply 用的,不是给 preview 的,忽略它
|
|
1659
|
+
`},propose:{dirName:`marchen-propose`,content:`---
|
|
1660
|
+
name: marchen-propose
|
|
1661
|
+
description: 提出新变更,创建并填充所有 artifact。适用于用户想快速描述需求并生成完整的 proposal、specs、design、tasks。
|
|
1662
|
+
---
|
|
1663
|
+
|
|
1664
|
+
提出新变更 — 创建变更目录并按依赖顺序生成所有 artifact。
|
|
1665
|
+
|
|
1666
|
+
将创建以下 artifact:
|
|
1667
|
+
- proposal.md(动机和变更内容)
|
|
1668
|
+
- specs/(每个能力的需求规格)
|
|
1669
|
+
- design.md(技术方案)
|
|
1670
|
+
- tasks.md(实现任务清单)
|
|
1671
|
+
|
|
1672
|
+
完成后可用 /marchen:apply 开始实现。
|
|
1673
|
+
|
|
1674
|
+
---
|
|
1675
|
+
|
|
1676
|
+
**输入**:用户的请求应包含变更名称(kebab-case)或变更描述。
|
|
1677
|
+
|
|
1678
|
+
**流程**
|
|
1679
|
+
|
|
1680
|
+
1. **确定变更名称**
|
|
1681
|
+
|
|
1682
|
+
如果提供了输入,直接使用或从描述中提取 kebab-case 名称(如"添加用户认证" → \`add-user-auth\`)。
|
|
1683
|
+
|
|
1684
|
+
如果没有输入,用 **AskUserQuestion** 工具询问:
|
|
1685
|
+
> "你想做什么变更?描述一下你要构建或修复的内容。"
|
|
1686
|
+
|
|
1687
|
+
从回答中提取 kebab-case 名称。
|
|
1688
|
+
|
|
1689
|
+
**重要**:必须理解用户想做什么才能继续。
|
|
1690
|
+
|
|
1691
|
+
2. **创建变更目录**
|
|
1692
|
+
|
|
1693
|
+
\`\`\`bash
|
|
1694
|
+
marchen new <name>
|
|
1695
|
+
\`\`\`
|
|
1696
|
+
|
|
1697
|
+
创建 \`marchen/changes/<name>/\` 目录和 \`.metadata.yaml\`。
|
|
1698
|
+
|
|
1699
|
+
如果同名变更已存在,用 **AskUserQuestion** 询问用户是继续已有变更还是换个名称。
|
|
1700
|
+
|
|
1701
|
+
3. **循环创建 artifact**
|
|
1702
|
+
|
|
1703
|
+
用 **TaskCreate** 工具创建任务列表追踪进度。
|
|
1704
|
+
|
|
1705
|
+
循环执行以下步骤:
|
|
1706
|
+
|
|
1707
|
+
a. **查询当前状态**
|
|
1708
|
+
\`\`\`bash
|
|
1709
|
+
marchen status <name> --json
|
|
1710
|
+
\`\`\`
|
|
1711
|
+
返回 JSON 包含:
|
|
1712
|
+
- \`workflow.next\`:下一个应该创建的 artifact ID,全部完成时为 \`null\`
|
|
1713
|
+
- \`workflow.ready\`:当前可以创建的 artifact 列表
|
|
1714
|
+
- \`workflow.blocked\`:被阻塞的 artifact 列表
|
|
1715
|
+
- \`artifacts\`:每个 artifact 的状态详情(\`id\`、\`status\`、\`path\`)
|
|
1716
|
+
|
|
1717
|
+
如果 \`workflow.next\` 为 \`null\` → 全部完成,跳到第 4 步。
|
|
1718
|
+
|
|
1719
|
+
b. **获取创建指令**
|
|
1720
|
+
\`\`\`bash
|
|
1721
|
+
marchen instructions <name> <workflow.next> --json
|
|
1722
|
+
\`\`\`
|
|
1723
|
+
返回 JSON 包含:
|
|
1724
|
+
- \`template\`:artifact 的 markdown 骨架结构,用它作为输出文件的框架
|
|
1725
|
+
- \`instruction\`:如何填充该 artifact 的指导文本
|
|
1726
|
+
- \`outputPath\`:写入路径(相对于变更目录)
|
|
1727
|
+
- \`context\`:上下文 artifact 的信息数组,每项包含 \`id\`、\`status\`、\`content\`(已填充的内容直接在这里,不需要额外读文件)
|
|
1728
|
+
- \`unlocks\`:完成此 artifact 后解锁的 artifact 列表
|
|
1729
|
+
|
|
1730
|
+
c. **创建 artifact**
|
|
1731
|
+
|
|
1732
|
+
根据 artifact 类型处理:
|
|
1733
|
+
|
|
1734
|
+
**普通 artifact(proposal / design / tasks):**
|
|
1735
|
+
- 读取 \`context\` 中 \`status\` 为 \`filled\` 的 \`content\` 作为上下文
|
|
1736
|
+
- 按 \`instruction\` 指引 + \`template\` 结构生成内容
|
|
1737
|
+
- 写入 \`marchen/changes/<name>/<outputPath>\`
|
|
1738
|
+
- 写入后验证文件存在
|
|
1739
|
+
|
|
1740
|
+
**specs(目录型 artifact,outputPath 为 \`specs/\`):**
|
|
1741
|
+
- 读取 proposal 内容(在 \`context\` 中,\`id\` 为 \`proposal\` 的 \`content\`)
|
|
1742
|
+
- 从 proposal 的"能力"章节提取能力列表(kebab-case 名称)
|
|
1743
|
+
- 为每个能力:
|
|
1744
|
+
- 创建目录 \`marchen/changes/<name>/specs/<capability>/\`
|
|
1745
|
+
- 按 \`template\` 结构 + \`instruction\` 指引生成 spec 内容
|
|
1746
|
+
- 写入 \`specs/<capability>/spec.md\`
|
|
1747
|
+
- 写入后验证每个 spec 文件存在
|
|
1748
|
+
|
|
1749
|
+
**如果 proposal 的上下文不够清晰**(用户描述太模糊):
|
|
1750
|
+
- 用 **AskUserQuestion** 澄清关键信息
|
|
1751
|
+
- 然后继续创建
|
|
1752
|
+
|
|
1753
|
+
d. 显示进度:"已创建 \`<artifact-id>\`",标记任务完成,回到步骤 a。
|
|
1754
|
+
|
|
1755
|
+
4. **显示最终状态**
|
|
1756
|
+
|
|
1757
|
+
\`\`\`bash
|
|
1758
|
+
marchen status <name>
|
|
1759
|
+
\`\`\`
|
|
1760
|
+
|
|
1761
|
+
**输出**
|
|
1762
|
+
|
|
1763
|
+
完成后显示:
|
|
1764
|
+
- 变更名称和目录位置
|
|
1765
|
+
- 已创建的 artifact 列表及简要说明
|
|
1766
|
+
- 用纯文字(不调用 AskUserQuestion,不自动执行)提示下一步两个并列选项,由用户自行决定:
|
|
1767
|
+
|
|
1768
|
+
\`\`\`
|
|
1769
|
+
下一步:
|
|
1770
|
+
/marchen:apply <name> 直接开始实现
|
|
1771
|
+
/marchen:propose-preview <name> 先看一眼浓缩摘要再决定
|
|
1772
|
+
\`\`\`
|
|
1773
|
+
|
|
1774
|
+
**护栏**
|
|
1775
|
+
|
|
1776
|
+
- 按依赖顺序创建,不跳过 artifact
|
|
1777
|
+
- 每次循环创建一个 artifact(specs 算一个,但包含多个文件)
|
|
1778
|
+
- 写入后验证文件存在再继续下一个
|
|
1779
|
+
- 如果上下文关键信息不清楚,询问用户;但小疑问优先做合理判断,保持节奏
|
|
1780
|
+
- 已存在同名变更时必须询问用户,不要覆盖
|
|
1781
|
+
- \`instruction\` 是给你的指引,不要把它原样复制到 artifact 文件中
|
|
1782
|
+
- 使用 AskUserQuestion 时,选项不超过 4 个;需要更多选项时合并或分步询问
|
|
1783
|
+
`},review:{dirName:`marchen-review`,content:`---
|
|
1784
|
+
name: marchen-review
|
|
1785
|
+
description: 对照变更意图检查代码实现的完整性和一致性,支持基于 chrome-devtools MCP 的 UI 场景验证。
|
|
1786
|
+
---
|
|
1787
|
+
|
|
1788
|
+
对照变更的 artifact 检查代码改动,必要时驱动浏览器验证 UI 行为,报告遗漏、偏差和阻塞。
|
|
1789
|
+
|
|
1790
|
+
---
|
|
1791
|
+
|
|
1792
|
+
**输入**:用户的请求应包含变更名称,或可从上下文推断。
|
|
1793
|
+
|
|
1794
|
+
**流程**
|
|
1795
|
+
|
|
1796
|
+
1. **选择变更**
|
|
1797
|
+
|
|
1798
|
+
有名称就用,没有则:
|
|
1799
|
+
- 从对话上下文推断
|
|
1800
|
+
- 只有一个 open 变更时自动选择
|
|
1801
|
+
- 多个变更时 \`marchen list --json\` + **AskUserQuestion** 让用户选
|
|
1802
|
+
|
|
1803
|
+
显示:"Review 变更: \`<name>\`"
|
|
1804
|
+
|
|
1805
|
+
2. **嗅探 diff 中的 UI 改动**
|
|
1806
|
+
|
|
1807
|
+
执行 \`git diff --name-only HEAD\`,判断改动是否涉及前端 UI(组件、样式、模板、静态资源等)。命中则记下命中文件,用于下一步的提示。
|
|
1808
|
+
|
|
1809
|
+
3. **AskUserQuestion 选择 review 模式**
|
|
1810
|
+
|
|
1811
|
+
用 **AskUserQuestion** 让用户三选一:
|
|
1812
|
+
|
|
1813
|
+
- **代码 review**(默认):对照 artifact 检查 git diff,不跑代码
|
|
1814
|
+
- **UI 验证**:用 chrome-devtools MCP 实际打开页面验证 specs 中的场景
|
|
1815
|
+
- **两者都做**:先代码、再 UI
|
|
1816
|
+
|
|
1817
|
+
若上一步命中 UI 文件,在问题描述里附一句:"检测到 diff 涉及 UI 文件(<列出命中文件>),建议选 UI 验证或两者都做。"
|
|
1818
|
+
未命中时不附加提示,默认推荐"代码 review"。
|
|
1819
|
+
|
|
1820
|
+
保存用户选择为 \`<mode>\`。
|
|
1821
|
+
|
|
1822
|
+
4. **获取意图与改动(公共前置)**
|
|
1823
|
+
|
|
1824
|
+
- 执行 \`marchen instructions <name> apply --json\`,从返回 JSON 的 \`context\` 数组里读取 \`status\` 为 "filled" 的 artifact(proposal/specs/design/tasks)作为变更意图
|
|
1825
|
+
- 执行 \`git diff HEAD\`;为空则 \`git diff HEAD~1\`;仍为空则报告 "未检测到代码改动" 并结束
|
|
1826
|
+
|
|
1827
|
+
diff 大到可能挤占 context 时,先 \`git diff --stat HEAD\` 看摘要,再按需读单个文件 diff。
|
|
1828
|
+
|
|
1829
|
+
5. **代码 review**(mode 为 "代码 review" 或 "两者都做" 时执行)
|
|
1830
|
+
|
|
1831
|
+
逐条对照变更意图和代码改动,输出报告:
|
|
1832
|
+
|
|
1833
|
+
**任务完成度** — tasks 中每个任务是否有对应改动:
|
|
1834
|
+
- ✅ 任务: <描述> — 已实现
|
|
1835
|
+
- ❌ 任务: <描述> — 未找到对应改动
|
|
1836
|
+
|
|
1837
|
+
**一致性检查** — 实现是否符合 design 决策:
|
|
1838
|
+
- ✅ <决策> — 已遵守
|
|
1839
|
+
- ⚠️ <决策> — 实现有偏差:<说明>
|
|
1840
|
+
|
|
1841
|
+
**需求覆盖** — specs 中需求是否被覆盖:
|
|
1842
|
+
- ✅ <需求> — 已覆盖
|
|
1843
|
+
- ❌ <需求> — 未覆盖
|
|
1844
|
+
|
|
1845
|
+
**发现的问题(如有)**
|
|
1846
|
+
- <文件:行号> <问题描述>
|
|
1847
|
+
|
|
1848
|
+
全部通过:输出 "✅ 代码 review 通过。"
|
|
1849
|
+
|
|
1850
|
+
遇到无法判断的情况(tasks 描述与 diff 对不上但可能是改名了、spec 表述含糊等),直接用 **AskUserQuestion** 就地问用户,不要积攒到最后。
|
|
1851
|
+
|
|
1852
|
+
6. **UI 验证**(mode 为 "UI 验证" 或 "两者都做" 时执行)
|
|
1853
|
+
|
|
1854
|
+
### a. 检测 chrome-devtools MCP 可用性
|
|
1855
|
+
|
|
1856
|
+
尝试调用 chrome-devtools MCP 的只读探测工具(例如列出已打开的页面)。工具不存在或调用失败 → 在报告里写:
|
|
1857
|
+
|
|
1858
|
+
> ⏭ chrome-devtools MCP 不可用,跳过 UI 验证。
|
|
1859
|
+
> 安装方法:\`npx chrome-devtools-mcp@latest\`,并参考所用 AI 工具的 MCP 配置文档将其注册。
|
|
1860
|
+
|
|
1861
|
+
然后跳过本节剩余步骤。
|
|
1862
|
+
|
|
1863
|
+
### b. 提取待验证场景
|
|
1864
|
+
|
|
1865
|
+
优先从变更的 specs 文件中提取所有 \`#### 场景:\` 块(按 spec 文件分组)。
|
|
1866
|
+
若 specs 不存在或不含场景 → 退化到从 tasks/proposal 推断 UI 相关验证点,并在报告中标注 "基于 tasks 推断,覆盖度可能不完整"。
|
|
1867
|
+
两者都没有可提取场景 → 报告 "未找到可验证的 UI 场景" 并跳过本节。
|
|
1868
|
+
|
|
1869
|
+
### c. 推断 dev server URL
|
|
1870
|
+
|
|
1871
|
+
从项目文件(脚本、框架配置、环境变量、README 等)推断 dev server 地址和启动命令,不要硬猜端口。
|
|
1872
|
+
|
|
1873
|
+
推出候选 URL 后用 chrome-devtools MCP 的导航工具打开,再用快照工具确认页面像被测应用(合理 title、含框架 root 节点或变更描述涉及的标志性内容)。看着不像被测应用 → 当作未能推断。
|
|
1874
|
+
|
|
1875
|
+
推不出 URL → 全部场景 ⏭ "URL unknown",在报告里说明推断依据,提示用户确认地址后重新运行 review,跳过本节执行步骤。
|
|
1876
|
+
|
|
1877
|
+
### d. 推断到 URL 但 dev server 未启动 → 询问是否帮忙起
|
|
1878
|
+
|
|
1879
|
+
推出来 URL 但导航失败(连接拒绝)→ 主会话 **AskUserQuestion**,附上推断依据和启动命令:
|
|
1880
|
+
|
|
1881
|
+
- **帮我起 dev server 然后继续**
|
|
1882
|
+
- **我手动起,起完告诉你**
|
|
1883
|
+
- **跳过 UI 验证**
|
|
1884
|
+
|
|
1885
|
+
选"帮我起":
|
|
1886
|
+
1. 用 Bash 的 \`run_in_background\` 执行推断到的启动命令(如 \`pnpm dev\`、\`npm run dev\`)
|
|
1887
|
+
2. 轮询端口直到可访问;出现明显异常(进程退出 / 长时间无任何输出 / 报错日志)才判定启动失败,让用户手动检查
|
|
1888
|
+
3. 启动成功后继续执行后续步骤,并在 review 结束时显式告知用户后台进程 PID 和端口,提示 "用完请自行 kill <PID>"——MUST NOT 自动 kill
|
|
1889
|
+
|
|
1890
|
+
选"我手动起":等用户告知启动完成后继续;用户可以新会话执行 \`/marchen:review\` 重跑。
|
|
1891
|
+
|
|
1892
|
+
选"跳过 UI":所有场景 ⏭ "dev server 未启动",跳过后续步骤。
|
|
1893
|
+
|
|
1894
|
+
### e. 乐观执行场景
|
|
1895
|
+
|
|
1896
|
+
对每个待验证场景:
|
|
1897
|
+
|
|
1898
|
+
1. 用 chrome-devtools MCP 的导航工具打开对应路径
|
|
1899
|
+
2. 用快照工具观察页面状态
|
|
1900
|
+
3. 把场景的 GIVEN/WHEN/THEN 翻译成具体交互(点击、填写、读取文本)并执行
|
|
1901
|
+
4. 比对页面状态与场景预期
|
|
1902
|
+
|
|
1903
|
+
遇到阻塞(登录墙、权限不足、缺数据、表单需要真实输入等):**主会话可以就地 AskUserQuestion**——例如"需要登录账号才能继续,提供测试账号?/ 跳过这个场景 / 暂停 review",而不必积攒到最后。
|
|
1904
|
+
|
|
1905
|
+
仍无法继续的场景 → 记录 ⏭ + 阻塞原因,跳到下一个。
|
|
1906
|
+
场景翻译不出具体操作(例如纯后端行为描述)→ ⏭ "不适合 UI 验证"。
|
|
1907
|
+
|
|
1908
|
+
### f. UI 报告格式
|
|
1909
|
+
|
|
1910
|
+
\`\`\`
|
|
1911
|
+
## UI 验证(chrome-devtools MCP)
|
|
1912
|
+
|
|
1913
|
+
测试目标:<URL 或 "未确定">
|
|
1914
|
+
|
|
1915
|
+
✅ <场景标题> — 通过
|
|
1916
|
+
说明:<可选简述>
|
|
1917
|
+
❌ <场景标题> — 失败
|
|
1918
|
+
证据:<console error 摘要 / 页面文案 / 关键截图路径>
|
|
1919
|
+
⏭ <场景标题> — 跳过:<阻塞原因>
|
|
1920
|
+
\`\`\`
|
|
1921
|
+
|
|
1922
|
+
7. **展示报告并按结果分支**
|
|
1923
|
+
|
|
1924
|
+
- **全部通过且无 ⏭**:提示 "可以用 \`marchen archive <name>\` 归档。"
|
|
1925
|
+
- **有 ❌ 失败**:提示用户修复后可以再次 review。
|
|
1926
|
+
- **有 ⏭ 跳过**:用 **AskUserQuestion** 让用户选择:
|
|
1927
|
+
- **补信息后继续**:收集所需信息(账号、数据等),就地再跑被跳过的场景
|
|
1928
|
+
- **跳过这些场景,继续归档**:保留报告,提示 \`marchen archive <name>\`
|
|
1929
|
+
- **暂停去修复**:什么都不做
|
|
1930
|
+
|
|
1931
|
+
如本次 review 启动了后台 dev server,再次提醒用户进程信息(PID/端口)。
|
|
1932
|
+
|
|
1933
|
+
**护栏**
|
|
1934
|
+
|
|
1935
|
+
- 不要修改任何代码,只报告(review 不是 apply)
|
|
1936
|
+
- UI 验证阻塞即停,不要硬闯(不登录、不猜数据、不绕权限);要继续就向用户索取信息
|
|
1937
|
+
- 起 dev server 必须先获得用户授权(AskUserQuestion);起完不自动 kill,告知 PID 让用户自行管理
|
|
1938
|
+
- 用户提供的凭据/账号等敏感数据:不写入任何 artifact,不在报告里复述明文,截图前对密码/token/邮箱等敏感字段脱敏或回避
|
|
1939
|
+
- 大 diff 先看 \`git diff --stat\`,按需读单个文件,避免灌爆 context
|
|
1940
|
+
- 使用 AskUserQuestion 时,选项不超过 4 个
|
|
1941
|
+
`}};async function E(e){try{await p.mkdir(e,{recursive:!0})}catch(e){if(e.code!==`EEXIST`)throw e}}async function D(e){try{return await p.access(e),!0}catch{return!1}}async function O(e,t){try{await p.rename(e,t)}catch(t){throw t.code===`ENOENT`?new y(`目录不存在`,e):t}}async function k(e){try{return await p.readdir(e)}catch(t){throw t.code===`ENOENT`?new y(`目录不存在`,e):t}}async function A(e){try{return await p.readFile(e,`utf-8`)}catch(t){throw t.code===`ENOENT`?new y(`文件不存在`,e):t}}async function j(e,t){await E(u(e)),await p.writeFile(e,t,`utf-8`)}async function M(e,t){await E(u(e)),await p.appendFile(e,t,`utf-8`)}function N(e=process.cwd()){return f(e)}function P(e=process.cwd()){return f(e,t)}function F(e=process.cwd()){return f(P(e),n)}function I(e=process.cwd()){return f(P(e),i)}async function L(e){let t=await A(e);try{return m.load(t)}catch(t){throw new y(`YAML 解析失败`,e,t instanceof Error?t:void 0)}}async function R(e,t){await j(e,m.dump(t,{indent:2}))}const ee=/^[a-z0-9]+(?:-[a-z0-9]+)*$/;var te=class e{constructor(e){this.workspace=e}static isValidName(e){return ee.test(e)}async create(t,n){if(await this.ensureInitialized(),!e.isValidName(t))throw new _(`变更名称 "${t}" 不合法,请使用 kebab-case 格式(如 add-dark-mode)`);let r=d(this.workspace.changeDir,t);if(await D(r))throw new _(`变更 "${t}" 已存在`);let i=n??`full`,o=w(i);await E(r);for(let e of o.artifacts)e.generates.endsWith(`/`)&&await E(d(r,e.generates));let s={name:t,schema:i,createdAt:new Date().toISOString(),status:`open`};await R(d(r,a),s)}async archive(e,t){await this.ensureInitialized();let n=d(this.workspace.changeDir,e);if(!await D(n))throw new _(`变更 "${e}" 不存在`);let r=d(n,a),i=await L(r),o=new Date().toISOString(),s=o.slice(0,10),c=d(this.workspace.archiveDir,`${s}-${e}`);if(await D(c))throw new _(`归档目标 "${s}-${e}" 已存在`);return await R(r,{...i,status:`archived`,archivedAt:o}),await O(n,c),await this.appendChangelog(e,s,t?.summary),await this.updateSearchIndex(),{name:e,schema:i.schema,archivedTo:c,archivedAt:o}}async appendChangelog(e,t,n){let r=this.workspace.changelogPath;await D(r)||await j(r,`# 变更日志
|
|
1942
|
+
`);let i=`[${e}](./archive/${t}-${e}/)`,a=n?.trim().replace(/[\r\n]+/g,` `);await M(r,`${a?`- ${t}: ${i} — ${a}`:`- ${t}: ${i}`}\n`)}async updateSearchIndex(){try{await Promise.race([this.doUpdateSearchIndex(),new Promise(e=>setTimeout(e,3e4))])}catch{}}async doUpdateSearchIndex(){let{SearchManager:e}=await import(`./search-manager-zPFpsFnI.mjs`).then(e=>e.n),t=new e(this.workspace);await t.isAvailable()&&(await t.indexChange(),await t.close())}async list(){await this.ensureInitialized();let e=await k(this.workspace.changeDir),t=[];for(let n of e){let e=d(this.workspace.changeDir,n,a);if(await D(e))try{let n=await L(e);t.push(n)}catch{continue}}return t.sort((e,t)=>new Date(t.createdAt).getTime()-new Date(e.createdAt).getTime()),t}async status(e){await this.ensureInitialized();let t=d(this.workspace.changeDir,e);if(!await D(t))throw new _(`变更 "${e}" 不存在`);let n=await L(d(t,a)),r=w(n.schema),i=[];for(let e of r.artifacts){let n=d(t,e.generates);if(e.id===`specs`){let t=await this.detectSpecsStatus(n);i.push({id:e.id,path:e.generates,...t})}else{let t=await this.detectContentStatus(n);i.push({id:e.id,status:t,path:e.generates})}}let o=new Map(i.map(e=>[e.id,e.status])),s=this.computeWorkflow(r,o),c=i.find(e=>e.id===`tasks`),l=null;if(c&&c.status===`filled`){let e=await A(d(t,`tasks.md`)),n=this.parseTaskItems(e);l={total:n.length,completed:n.filter(e=>e.completed).length,items:n}}return{name:e,schema:n.schema,artifacts:i,workflow:s,tasks:l}}async getInstructions(e,t){await this.ensureInitialized();let n=d(this.workspace.changeDir,e);if(!await D(n))throw new _(`变更 "${e}" 不存在`);let r=await L(d(n,a)),i=w(r.schema),o=i.artifacts.find(e=>e.id===t);if(!o)throw new _(`Artifact "${t}" 不存在,可用的 artifact: ${i.artifacts.map(e=>e.id).join(`, `)}`);let s=o.template??``,c=o.instruction,l=[];for(let e of o.requires){let t=i.artifacts.find(t=>t.id===e);if(!t)continue;let r=d(n,t.generates);if(e===`specs`){let n=await this.detectSpecsStatus(r),i=n.status===`filled`?await this.readSpecsContent(r):null;l.push({id:e,status:n.status,path:t.generates,content:i})}else{let n=await this.detectContentStatus(r),i=n===`filled`?await A(r):null;l.push({id:e,status:n,path:t.generates,content:i})}}let u=i.artifacts.filter(e=>e.requires.includes(t)).map(e=>e.id);return{changeName:e,artifactId:t,schemaName:r.schema,changeDir:n,outputPath:o.generates,template:s,instruction:c,context:l,unlocks:u,state:null,progress:null}}async detectContentStatus(e){return await D(e)?(await A(e)).replace(/<!--[\s\S]*?-->/g,``).split(`
|
|
1943
|
+
`).filter(e=>e.trim()!==``).filter(e=>!/^#{1,6}\s/.test(e.trim())).join(`
|
|
1944
|
+
`).trim().length>20?`filled`:`empty`:`missing`}async detectSpecsStatus(e){if(!await D(e))return{status:`missing`,capabilities:[]};let t=await k(e);if(t.length===0)return{status:`no-content`,capabilities:[]};let n=!0;for(let r of t){let t=d(e,r,`spec.md`);if(!await D(t)){n=!1;continue}await this.detectContentStatus(t)!==`filled`&&(n=!1)}return{status:n?`filled`:`no-content`,capabilities:t}}async readSpecsContent(e){let t=await k(e),n=[];for(let r of t){let t=d(e,r,`spec.md`);if(await D(t)){let e=await A(t);n.push(`--- specs/${r}/spec.md ---\n${e}`)}}return n.join(`
|
|
1945
|
+
|
|
1946
|
+
`)}computeWorkflow(e,t){let n=e=>t.get(e)===`filled`,r=[],i=[];for(let t of e.artifacts)n(t.id)||(t.requires.every(n)?r.push(t.id):i.push(t.id));return{next:r[0]??null,ready:r,blocked:i}}parseTaskItems(e){return[...e.matchAll(/^- \[([ x])\] (.+)$/gm)].map(e=>({description:e[2],completed:e[1]===`x`}))}parseTaskProgress(e){let t=this.parseTaskItems(e),n=t.filter(e=>e.completed).length;return{total:t.length,completed:n,remaining:t.length-n}}async getApplyInstructions(e){await this.ensureInitialized();let t=d(this.workspace.changeDir,e);if(!await D(t))throw new _(`变更 "${e}" 不存在`);let n=await L(d(t,a)),r=w(n.schema),i=[];for(let e of r.artifacts){let n=d(t,e.generates);if(e.id===`specs`){let t=await this.detectSpecsStatus(n),r=t.status===`filled`?await this.readSpecsContent(n):null;i.push({id:e.id,status:t.status,path:e.generates,content:r})}else{let t=await this.detectContentStatus(n),r=t===`filled`?await A(n):null;i.push({id:e.id,status:t,path:e.generates,content:r})}}let o=i.find(e=>e.id===`tasks`),s,c;return!o||!o.content||o.status!==`filled`?(s=`blocked`,c={total:0,completed:0,remaining:0}):(c=this.parseTaskProgress(o.content),s=c.remaining===0?`all_done`:`ready`),{changeName:e,artifactId:`apply`,schemaName:n.schema,changeDir:t,outputPath:null,template:null,instruction:`按 tasks.md 逐个实现任务,完成后勾选 checkbox。
|
|
1947
|
+
- 每完成一个任务立即将 - [ ] 改为 - [x]
|
|
1948
|
+
- 改动最小化,只做任务要求的事
|
|
1949
|
+
- 不确定就暂停问,不要猜
|
|
1950
|
+
- 如果发现设计问题,暂停并建议更新 artifact`,context:i,unlocks:null,state:s,progress:c}}async ensureInitialized(){if(!await this.workspace.isInitialized())throw new v(`Marchen 尚未初始化`,`运行 marchen init 初始化`)}},z=class{root;specDir;changeDir;archiveDir;changelogPath;searchDbPath;packageBoundaries=[{name:`@marchen/shared`,dependsOn:[]},{name:`@marchen/config`,dependsOn:[`@marchen/shared`]},{name:`@marchen/fs`,dependsOn:[`@marchen/shared`]},{name:`@marchen/core`,dependsOn:[`@marchen/config`,`@marchen/fs`,`@marchen/shared`]}];constructor(e){this.root=N(e),this.specDir=P(this.root),this.changeDir=F(this.root),this.archiveDir=I(this.root),this.changelogPath=d(this.specDir,`changelog.md`),this.searchDbPath=d(this.specDir,`.search`,`index.sqlite`)}async isInitialized(){return await D(this.specDir)}async initialize(t){let n=t?.providers??S,i=n.map(e=>x[e]).filter(e=>e!=null);await E(this.specDir),await E(this.changeDir),await E(this.archiveDir),await E(d(this.specDir,`.search`));let a=d(this.specDir,e),o={schema:`full`,providers:[...n]};t?.version&&(o.version=t.version),o.search={enabled:t?.searchEnabled??!1},o.models={endpoint:r},await R(a,o),await j(d(this.changeDir,`.gitkeep`),``),await j(d(this.archiveDir,`.gitkeep`),``),await D(this.changelogPath)||await j(this.changelogPath,`# 变更日志
|
|
1951
|
+
`);for(let e of i)await this.generateSkills(e.skillDir),e.commandDir&&await this.generateCommands(e.commandDir)}async readConfig(){return await L(d(this.specDir,e))}async update(t){let n=d(this.specDir,e),i=await L(n),a=i.version??null;if(a===t.version)return{previousVersion:a,currentVersion:t.version,providersUpdated:[],skillCount:0,commandCount:0};let o=i.providers,s=o&&o.length>0?o:[...S],c=s.map(e=>x[e]).filter(e=>e!=null),l=[],u=0,f=0,p=Object.keys(T).length,m=Object.keys(b).length;for(let e of c)await this.generateSkills(e.skillDir),u+=p,e.commandDir&&(await this.generateCommands(e.commandDir),f+=m),l.push(e.name);i.version=t.version,i.providers||=[...s];let h=i.search;h&&`mode`in h&&(i.search={enabled:h.mode===`semantic`}),i.search||={enabled:!1};let g=i.models;return g?g.endpoint??(i.models={...g,endpoint:r}):i.models={endpoint:r},await R(n,i),{previousVersion:a,currentVersion:t.version,providersUpdated:l,skillCount:u,commandCount:f}}async generateSkills(e){for(let t of Object.values(T)){let n=d(this.root,e,t.dirName);await E(n),await j(d(n,`SKILL.md`),t.content)}}async generateCommands(e){let t=d(this.root,e);await E(t);for(let e of Object.values(b))await j(d(t,e.fileName),e.content)}};function B(){let e=new z;return{workspace:e,changes:new te(e)}}function V(e){e instanceof v?(l.log.error(e.message),e.hint&&l.log.info(e.hint)):e instanceof _?l.log.warn(e.message):e instanceof y?l.log.error(`${e.message}: ${e.path}`):e instanceof g?l.log.error(e.message):l.log.error(`未知错误: ${e}`),process.exit(1)}function H(e){e.command(`archive`).description(`归档一个已完成的变更`).argument(`<name>`,`变更名称`).option(`--json`,`输出 JSON 格式`).option(`--summary <text>`,`变更摘要(写入 changelog)`).action(async(e,t)=>{try{let{changes:n}=B(),r=await n.archive(e,{summary:t.summary});if(t.json){console.log(JSON.stringify(r,null,2));return}l.intro(`Marchen CLI`),l.log.success(`变更 "${e}" 归档成功`),l.outro(`运行 marchen list 查看剩余变更`)}catch(e){V(e)}})}const U={embed:`Embedding`,generate:`Query Expansion`,rerank:`Reranker`};function W(e){let t=U[e.model]??e.model;switch(e.stage){case`checking`:return`检查模型 ${t}...`;case`downloading`:if(e.downloadedBytes&&e.totalBytes){let n=Math.round(e.downloadedBytes/e.totalBytes*100);return`下载模型 ${t}... ${(e.downloadedBytes/1024/1024).toFixed(1)}/${(e.totalBytes/1024/1024).toFixed(0)} MB (${n}%)`}return`下载模型 ${t}...`;case`verifying`:return`校验模型 ${t}...`;case`ready`:return`模型 ${t} 就绪`;default:return`准备模型 ${t}...`}}function G(e){e.command(`init`).description(`初始化 Marchen 目录结构`).option(`--force`,`强制覆盖已存在的目录`).action(async t=>{l.intro(`Marchen CLI`);let{workspace:n}=B();if(await n.isInitialized()&&!t.force){let e=await l.confirm({message:`Marchen 目录已存在,是否覆盖?`});(l.isCancel(e)||!e)&&(l.cancel(`操作已取消`),process.exit(0))}let r=Object.values(x).map(e=>({value:e.id,label:e.name})),i=await l.multiselect({message:`选择要安装的 AI 工具集成`,options:r,initialValues:[`claude-code`],required:!0});l.isCancel(i)&&(l.cancel(`操作已取消`),process.exit(0));let a=e.version(),s=await l.confirm({message:`是否启用搜索?(需下载约 2GB 模型)`,initialValue:!1});l.isCancel(s)&&(l.cancel(`操作已取消`),process.exit(0)),await n.initialize({providers:i,version:a,searchEnabled:s});let c=i.map(e=>x[e]?.name??e).join(`, `);if(l.log.success(`已为 ${c} 生成 skills 文件`),s){let e=new o(n),t=l.spinner();t.start(`下载搜索模型...`),await e.ensureModels({onProgress:e=>{t.message(W(e))}}),t.stop(`Hybrid Search 已启用`)}l.outro(`Marchen 初始化成功!`)})}function K(e){e.command(`instructions`).description(`获取 artifact 的创建指令`).argument(`<name>`,`变更名称`).argument(`<artifact-id>`,`artifact 标识符 (proposal/specs/design/tasks/apply)`).option(`--json`,`输出 JSON 格式(默认行为)`).action(async(e,t)=>{try{let{changes:n}=B(),r=t===`apply`?await n.getApplyInstructions(e):await n.getInstructions(e,t);console.log(JSON.stringify(r,null,2))}catch(e){V(e)}})}function q(e){let t=Math.floor((Date.now()-new Date(e).getTime())/1e3);return t<60?`刚刚`:t<3600?`${Math.floor(t/60)} 分钟前`:t<86400?`${Math.floor(t/3600)} 小时前`:`${Math.floor(t/86400)} 天前`}function J(e){e.command(`list`).description(`列出所有 open 状态的变更`).option(`--json`,`输出 JSON 格式`).action(async e=>{try{let{changes:t}=B(),n=await t.list();if(e.json){console.log(JSON.stringify(n,null,2));return}if(l.intro(`Marchen CLI`),n.length===0){l.log.info(`暂无 open 状态的变更`),l.outro(`运行 marchen new <name> 创建一个变更`);return}let r=Math.max(4,...n.map(e=>e.name.length)),i=Math.max(6,...n.map(e=>e.schema.length)),a=[`${`名称`.padEnd(r-2)} ${`Schema`.padEnd(i)} 创建时间`,`${`─`.repeat(r)} ${`─`.repeat(i)} ${`─`.repeat(8)}`,...n.map(e=>`${e.name.padEnd(r)} ${e.schema.padEnd(i)} ${q(e.createdAt)}`)].join(`
|
|
1952
|
+
`);l.log.info(`共 ${n.length} 个 open 变更:\n\n${a}`),l.outro(`运行 marchen status <name> 查看变更详情`)}catch(e){V(e)}})}function Y(e){e.command(`new`).description(`创建一个新的变更`).argument(`<name>`,`变更名称(kebab-case,如 add-dark-mode)`).option(`--schema <name>`,`工作流 schema`,`full`).action(async(e,t)=>{if(l.intro(`Marchen CLI`),!C[t.schema]){let e=Object.keys(C).join(`, `);l.log.error(`Schema "${t.schema}" 不存在,可用的 schema: ${e}`),process.exit(1)}let n=l.spinner();n.start(`正在创建变更 "${e}"...`);try{let{changes:r}=B();await r.create(e,t.schema),n.stop(`变更 "${e}" 创建成功(schema: ${t.schema})`)}catch(e){n.stop(`创建失败`),V(e)}l.outro(`运行 marchen list 查看所有变更`)})}function X(e){e.command(`search <query>`).description(`搜索归档变更历史`).option(`-n, --limit <number>`,`结果数量`,`5`).option(`--min-score <number>`,`最低分数阈值`,`0.3`).option(`--json`,`输出 JSON 格式`).option(`--rebuild`,`重建索引后搜索`).action(async(e,t)=>{let n=new z,r=new o(n),i=t.json?null:l.spinner();try{await r.isAvailable()||(l.log.error(`搜索功能不可用(qmd 加载失败)`),process.exit(1));let a=!1;try{a=(await n.readConfig()).search?.enabled??!1}catch{}a||(l.log.error(`搜索未启用。请在 config.yaml 中设置 search.enabled: true 后运行 marchen update`),process.exit(1)),i?.start(`准备搜索引擎...`),await r.prepare(),i?.stop(`搜索引擎就绪`),t.rebuild&&(i?.start(`正在重建索引...`),await r.index(),i?.stop(`索引重建完成`)),i?.start(`搜索中...`);let o=await r.search(e,{limit:Number(t.limit),minScore:Number(t.minScore)});if(i?.stop(`搜索完成`),t.json){console.log(JSON.stringify(o,null,2)),await r.close();return}if(o.length===0){l.log.info(`未找到匹配结果`),await r.close();return}for(let e of o){let t=Math.round(e.score*100),n=t>=70?h.green(`${t}%`):t>=40?h.yellow(`${t}%`):h.dim(`${t}%`);l.log.info(`${h.bold(e.path)} ${n}`),e.snippet&&l.log.message(h.dim(e.snippet))}await r.close()}catch(e){i?.stop(`搜索失败`),V(e)}})}const Z={filled:`✅`,empty:`⬜`,missing:`⬜`,"no-content":`⬜`};function ne(e){switch(e){case`filled`:return h.green(e);case`empty`:return h.yellow(e);case`blocked`:return h.red(e);default:return h.dim(e)}}function Q(e,t){let n=t>0?Math.round(e/t*10):0,r=`[${`█`.repeat(n)+`░`.repeat(10-n)}] ${e}/${t} 完成`;return e===t?h.green(r):e>0?h.yellow(r):h.dim(r)}function re(e){e.command(`status`).description(`查看变更的 artifact 状态和工作流建议`).argument(`<name>`,`变更名称`).option(`--json`,`输出 JSON 格式`).action(async(e,t)=>{try{let{changes:n}=B(),r=await n.status(e);if(t.json){console.log(JSON.stringify(r,null,2));return}l.intro(`Marchen CLI`),l.log.info(`${h.bold(r.name)} · ${h.dim(r.schema)}`);let i=r.artifacts.map(e=>{let t=r.workflow.blocked.includes(e.id),n=t?`🔒`:Z[e.status],i=t?`blocked`:e.status,a=`${n} ${e.id.padEnd(12)} ${ne(i)}`;if(e.capabilities&&(a+=` (${e.capabilities.length} capabilities)`),t){let t=ie(e.id).filter(e=>r.artifacts.find(t=>t.id===e)?.status!==`filled`);t.length>0&&(a+=` (等待 ${t.join(`, `)})`)}return a});l.log.info(`Artifacts\n${i.join(`
|
|
1953
|
+
`)}`);let a=r.artifacts.filter(e=>e.status===`filled`).length,o=r.artifacts.length,s=`Artifacts: ${Q(a,o)}`;r.tasks&&(s+=`\nTasks: ${Q(r.tasks.completed,r.tasks.total)}`),l.log.info(s),r.workflow.next?l.outro(`下一步: ${h.cyan(r.workflow.next)}`):l.outro(`所有 artifact 已就绪`)}catch(e){V(e)}})}function ie(e){return{proposal:[],specs:[`proposal`],design:[`proposal`],tasks:[`specs`,`design`]}[e]??[]}function ae(e){e.command(`update`).description(`更新 skill/command 文件到最新版本`).action(async()=>{l.intro(`Marchen CLI`);try{let{workspace:t}=B(),n=e.version();if(!await t.isInitialized())throw new v(`Marchen 尚未初始化`,`请先运行 marchen init`);let r=await t.update({version:n});if(r.providersUpdated.length===0)l.log.info(`已是最新版本 (${r.currentVersion})`);else{let e=r.previousVersion??`unknown`;l.log.info(`版本: ${e} → ${r.currentVersion}`);for(let e of r.providersUpdated)l.log.success(`已更新 ${e} skills`);l.log.success(`config.yaml 版本已更新`)}if((await t.readConfig()).search?.enabled??!1){let e=new o(t),n=!1,r=l.spinner();r.start(`检查搜索模型...`),await e.ensureModels({onProgress:e=>{e.stage===`downloading`&&(n=!0),r.message(W(e))}}),r.stop(n?`Hybrid Search 已启用`:`Hybrid Search 已就绪`)}l.outro(`更新完成!`)}catch(e){V(e)}})}const{version:oe}=s(import.meta.url)(`../package.json`);function $(){let e=new c;return e.name(`marchen`).description(`Workflow harness for AI coding agents`).version(oe,`-v, --version`),H(e),G(e),K(e),J(e),Y(e),X(e),re(e),ae(e),e}$().parse(process.argv);export{$ as buildCliProgram};
|
|
1954
|
+
//# sourceMappingURL=index.mjs.map
|