create-vela-workflow 1.0.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.
Files changed (45) hide show
  1. package/README.md +136 -0
  2. package/bin/cli.js +188 -0
  3. package/docs/ai-workflow-tutorial.md +462 -0
  4. package/docs/official-site-tutorial.md +391 -0
  5. package/package.json +34 -0
  6. package/templates/.github/HARNESS-ENGINEERING-GUIDE.md +407 -0
  7. package/templates/.github/agents/vela-knowledge.agent.md +45 -0
  8. package/templates/.github/agents/vela-s1-prd.agent.md +69 -0
  9. package/templates/.github/agents/vela-s2-tech.agent.md +66 -0
  10. package/templates/.github/agents/vela-s3-coding.agent.md +301 -0
  11. package/templates/.github/agents/vela-workflow.agent.md +110 -0
  12. package/templates/.github/copilot-instructions.md +64 -0
  13. package/templates/.github/prompts/vela-apis.prompt.md +98 -0
  14. package/templates/.github/prompts/vela-best-practices.prompt.md +93 -0
  15. package/templates/.github/prompts/vela-components.prompt.md +118 -0
  16. package/templates/.github/prompts/vela-dev-guide.prompt.md +622 -0
  17. package/templates/.github/rules/project-init.md +45 -0
  18. package/templates/.github/rules/vela-coding-convention.md +324 -0
  19. package/templates/.github/rules/vela-css.md +217 -0
  20. package/templates/.github/rules/vela-design-driven.md +306 -0
  21. package/templates/.github/rules/vela-figma-mcp.md +198 -0
  22. package/templates/.github/rules/vela-format.md +119 -0
  23. package/templates/.github/rules/vela-layout.md +67 -0
  24. package/templates/.github/rules/vela-platform.md +46 -0
  25. package/templates/.github/rules/vela-quality.md +109 -0
  26. package/templates/.kiro/hooks/figma-design-check.kiro.hook +14 -0
  27. package/templates/.kiro/hooks/post-coding-validation.kiro.hook +13 -0
  28. package/templates/.kiro/hooks/validate-ux-files.kiro.hook +16 -0
  29. package/templates/.kiro/settings/mcp.json +7 -0
  30. package/templates/.kiro/skills/vela-js-app/SKILL.md +1072 -0
  31. package/templates/.kiro/steering/workflow-conventions.md +110 -0
  32. package/templates/.workflow/resource-paths.json +62 -0
  33. package/templates/.workflow/scripts/.gitkeep +0 -0
  34. package/templates/.workflow/scripts/checkpoint_manager.js +284 -0
  35. package/templates/.workflow/scripts/context_loader.js +841 -0
  36. package/templates/.workflow/scripts/figma_export.js +346 -0
  37. package/templates/.workflow/scripts/session_manager.js +438 -0
  38. package/templates/.workflow/stages/.gitkeep +0 -0
  39. package/templates/.workflow/stages/commands.md +171 -0
  40. package/templates/.workflow/stages/s1_prd.md +286 -0
  41. package/templates/.workflow/stages/s2_tech_design.md +302 -0
  42. package/templates/.workflow/stages/s3_coding.md +699 -0
  43. package/templates/.workflow/stages/s4_simulator.md +259 -0
  44. package/templates/.workflow/workflow-config.json +46 -0
  45. package/templates/.workflow/workflow_starter.md +912 -0
@@ -0,0 +1,438 @@
1
+ /**
2
+ * Session 管理工具
3
+ *
4
+ * 负责工作流 Session 的创建、恢复、状态更新和 Checkpoint 管理。
5
+ * 每个 Session 对应一次完整的工作流执行实例,包含三个阶段(S1/S2/S3)的状态信息。
6
+ *
7
+ * Requirements: 2.1, 2.2, 2.3, 2.4, 2.5
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ // Session 存储根目录
14
+ const SESSIONS_DIR = path.resolve(__dirname, '../../.ai-workspace/sessions');
15
+
16
+ // 有效的阶段状态
17
+ const VALID_STATUSES = ['not_started', 'in_progress', 'pending_review', 'completed'];
18
+
19
+ // 有效的阶段 ID
20
+ const VALID_STAGE_IDS = ['S1', 'S2', 'S3', 'S4'];
21
+
22
+ /**
23
+ * 生成 Session ID
24
+ * 格式: VELA-{YYYYMMDD}-{HHmmss}-{random4chars}
25
+ * @returns {string} 生成的 Session ID
26
+ */
27
+ function generateSessionId() {
28
+ const now = new Date();
29
+ const datePart = now.getFullYear().toString()
30
+ + String(now.getMonth() + 1).padStart(2, '0')
31
+ + String(now.getDate()).padStart(2, '0');
32
+ const timePart = String(now.getHours()).padStart(2, '0')
33
+ + String(now.getMinutes()).padStart(2, '0')
34
+ + String(now.getSeconds()).padStart(2, '0');
35
+ const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
36
+ let random = '';
37
+ for (let i = 0; i < 4; i++) {
38
+ random += chars.charAt(Math.floor(Math.random() * chars.length));
39
+ }
40
+ return `VELA-${datePart}-${timePart}-${random}`;
41
+ }
42
+
43
+ /**
44
+ * 创建新的 Session
45
+ *
46
+ * 生成唯一的 session_id,创建 session 目录,并初始化 session.json。
47
+ * session.json 包含所有必需字段和三个阶段的初始状态。
48
+ *
49
+ * @param {string} requirementName - 需求名称(如 "天气快应用")
50
+ * @returns {{ success: boolean, session?: object, error?: string }}
51
+ */
52
+ function createSession(requirementName) {
53
+ // 参数校验
54
+ if (!requirementName || typeof requirementName !== 'string' || requirementName.trim() === '') {
55
+ return { success: false, error: '需求名称不能为空' };
56
+ }
57
+
58
+ const sessionId = generateSessionId();
59
+ const sessionDir = path.join(SESSIONS_DIR, sessionId);
60
+ const now = new Date().toISOString();
61
+
62
+ // 构建 session.json 数据结构
63
+ const sessionData = {
64
+ session_id: sessionId,
65
+ requirement_name: requirementName.trim(),
66
+ created_at: now,
67
+ updated_at: now,
68
+ current_stage: 'S1',
69
+ stages: {
70
+ S1: {
71
+ status: 'not_started',
72
+ output: null,
73
+ completed_at: null
74
+ },
75
+ S2: {
76
+ status: 'not_started',
77
+ output: null,
78
+ completed_at: null
79
+ },
80
+ S3: {
81
+ status: 'not_started',
82
+ output: null,
83
+ completed_at: null
84
+ },
85
+ S4: {
86
+ status: 'not_started',
87
+ output: null,
88
+ completed_at: null
89
+ }
90
+ },
91
+ inputs: {
92
+ figma_urls: [],
93
+ requirement_source: 'manual',
94
+ requirement_description: '',
95
+ screen_spec: null, // { width: number, height: number, shape: "round"|"oval"|"square" }
96
+ project_path: null // 项目工程目录路径(相对于工作区根目录),如 'apps/packages_apps/wearable/myapp'
97
+ },
98
+ checkpoint: {
99
+ active_context: null
100
+ },
101
+ workflow_mode: 'full'
102
+ };
103
+
104
+ try {
105
+ // 创建 session 目录(递归创建,确保父目录存在)
106
+ fs.mkdirSync(sessionDir, { recursive: true });
107
+
108
+ // 安全写入 session.json
109
+ const writeResult = safeWriteSessionJson(sessionId, sessionData);
110
+ if (!writeResult.success) {
111
+ return { success: false, error: `创建 Session 失败: ${writeResult.error}` };
112
+ }
113
+
114
+ return { success: true, session: sessionData };
115
+ } catch (err) {
116
+ return { success: false, error: `创建 Session 失败: ${err.message}` };
117
+ }
118
+ }
119
+
120
+ /**
121
+ * 恢复已有 Session
122
+ *
123
+ * 读取指定 Session 的 session.json 文件,校验格式完整性,返回 Session 对象。
124
+ * 如果文件损坏或格式非法,返回错误信息。
125
+ *
126
+ * @param {string} sessionId - Session ID(如 "VELA-20260301-100000-ab12")
127
+ * @returns {{ success: boolean, session?: object, error?: string }}
128
+ */
129
+ /**
130
+ * 恢复已有 Session
131
+ *
132
+ * 读取指定 Session 的 session.json 文件,校验格式完整性,返回 Session 对象。
133
+ * 如果文件损坏或格式非法,尝试自动修复常见问题(如非法字符),否则返回错误信息。
134
+ *
135
+ * @param {string} sessionId - Session ID(如 "VELA-20260301-100000-ab12")
136
+ * @returns {{ success: boolean, session?: object, error?: string }}
137
+ */
138
+ function resumeSession(sessionId) {
139
+ // 参数校验
140
+ if (!sessionId || typeof sessionId !== 'string' || sessionId.trim() === '') {
141
+ return { success: false, error: 'Session ID 不能为空' };
142
+ }
143
+
144
+ const sessionDir = path.join(SESSIONS_DIR, sessionId);
145
+ const sessionFilePath = path.join(sessionDir, 'session.json');
146
+
147
+ // 检查 session 目录是否存在
148
+ if (!fs.existsSync(sessionDir)) {
149
+ return { success: false, error: `Session 目录不存在: ${sessionId}` };
150
+ }
151
+
152
+ // 检查 session.json 是否存在
153
+ if (!fs.existsSync(sessionFilePath)) {
154
+ return { success: false, error: `session.json 文件不存在: ${sessionId}` };
155
+ }
156
+
157
+ let rawData;
158
+ try {
159
+ rawData = fs.readFileSync(sessionFilePath, 'utf-8');
160
+ } catch (err) {
161
+ return { success: false, error: `读取 session.json 失败: ${err.message}` };
162
+ }
163
+
164
+ let sessionData;
165
+ try {
166
+ sessionData = JSON.parse(rawData);
167
+ } catch (parseErr) {
168
+ // JSON 解析失败,尝试自动修复常见问题
169
+ const repaired = tryRepairJson(rawData);
170
+ if (repaired !== null) {
171
+ try {
172
+ sessionData = JSON.parse(repaired);
173
+ // 修复成功,回写修复后的文件
174
+ fs.writeFileSync(sessionFilePath, JSON.stringify(sessionData, null, 2), 'utf-8');
175
+ } catch (_) {
176
+ return { success: false, error: `session.json 文件损坏且无法自动修复,JSON 解析失败: ${parseErr.message}` };
177
+ }
178
+ } else {
179
+ return { success: false, error: `session.json 文件损坏,JSON 解析失败: ${parseErr.message}` };
180
+ }
181
+ }
182
+
183
+ // 校验必需字段
184
+ const validationError = validateSessionData(sessionData);
185
+ if (validationError) {
186
+ return { success: false, error: `session.json 格式非法: ${validationError}` };
187
+ }
188
+
189
+ return { success: true, session: sessionData };
190
+ }
191
+ /**
192
+ * 尝试修复常见的 JSON 损坏问题
193
+ *
194
+ * 处理以下情况:
195
+ * - 中文引号(\u201c \u201d \u2018 \u2019)未转义导致字符串提前截断
196
+ * - 尾部多余逗号
197
+ * - BOM 头
198
+ *
199
+ * @param {string} raw - 原始 JSON 字符串
200
+ * @returns {string|null} 修复后的字符串,无法修复返回 null
201
+ */
202
+ function tryRepairJson(raw) {
203
+ if (!raw || typeof raw !== 'string') return null;
204
+
205
+ try {
206
+ let repaired = raw;
207
+
208
+ // 移除 BOM 头
209
+ if (repaired.charCodeAt(0) === 0xFEFF) {
210
+ repaired = repaired.slice(1);
211
+ }
212
+
213
+ // 替换中文引号为转义的 Unicode 序列(在 JSON 字符串值内部)
214
+ // 这些字符在 JSON 字符串内是合法的,但如果被错误地当作裸字符写入会破坏结构
215
+ repaired = repaired.replace(/\u201c/g, '\\u201c');
216
+ repaired = repaired.replace(/\u201d/g, '\\u201d');
217
+ repaired = repaired.replace(/\u2018/g, '\\u2018');
218
+ repaired = repaired.replace(/\u2019/g, '\\u2019');
219
+
220
+ // 移除尾部逗号(对象和数组中最后一个元素后的逗号)
221
+ repaired = repaired.replace(/,\s*([\]}])/g, '$1');
222
+
223
+ // 尝试解析验证
224
+ JSON.parse(repaired);
225
+ return repaired;
226
+ } catch (_) {
227
+ return null;
228
+ }
229
+ }
230
+
231
+ /**
232
+ * 安全写入 session.json
233
+ *
234
+ * 使用 JSON.stringify 确保所有内容正确序列化,避免手动拼接导致的格式问题。
235
+ * 写入前先验证数据可以被正确序列化和反序列化。
236
+ *
237
+ * @param {string} sessionId - Session ID
238
+ * @param {object} sessionData - 要写入的 Session 数据
239
+ * @returns {{ success: boolean, error?: string }}
240
+ */
241
+ function safeWriteSessionJson(sessionId, sessionData) {
242
+ if (!sessionId || !sessionData) {
243
+ return { success: false, error: '参数不能为空' };
244
+ }
245
+
246
+ const sessionFilePath = path.join(SESSIONS_DIR, sessionId, 'session.json');
247
+ const tmpFilePath = sessionFilePath + '.tmp';
248
+
249
+ try {
250
+ // 序列化并验证往返一致性
251
+ const serialized = JSON.stringify(sessionData, null, 2);
252
+ JSON.parse(serialized); // 验证序列化结果可以被正确解析
253
+
254
+ // 原子写入:先写临时文件,再 rename,防止写入中断导致 JSON 截断
255
+ fs.writeFileSync(tmpFilePath, serialized, 'utf-8');
256
+ fs.renameSync(tmpFilePath, sessionFilePath);
257
+ return { success: true };
258
+ } catch (err) {
259
+ // 清理可能残留的临时文件
260
+ try { fs.unlinkSync(tmpFilePath); } catch (_) { /* ignore */ }
261
+ return { success: false, error: `写入 session.json 失败: ${err.message}` };
262
+ }
263
+ }
264
+
265
+
266
+ /**
267
+ * 校验 session.json 数据格式
268
+ *
269
+ * 确保所有必需字段存在且类型正确。
270
+ *
271
+ * @param {object} data - session.json 解析后的对象
272
+ * @returns {string|null} 错误信息,null 表示校验通过
273
+ */
274
+ function validateSessionData(data) {
275
+ if (!data || typeof data !== 'object') {
276
+ return '数据不是有效的 JSON 对象';
277
+ }
278
+
279
+ // 校验顶层必需字段
280
+ const requiredFields = ['session_id', 'requirement_name', 'created_at', 'updated_at', 'current_stage', 'stages'];
281
+ for (const field of requiredFields) {
282
+ if (!(field in data)) {
283
+ return `缺少必需字段: ${field}`;
284
+ }
285
+ }
286
+
287
+ // 校验 session_id 格式
288
+ if (typeof data.session_id !== 'string' || !data.session_id.startsWith('VELA-')) {
289
+ return 'session_id 格式无效,应以 VELA- 开头';
290
+ }
291
+
292
+ // 校验 current_stage 是否为有效阶段
293
+ if (!VALID_STAGE_IDS.includes(data.current_stage)) {
294
+ return `current_stage 无效: ${data.current_stage},应为 S1/S2/S3`;
295
+ }
296
+
297
+ // 校验 stages 结构
298
+ if (!data.stages || typeof data.stages !== 'object') {
299
+ return 'stages 字段不是有效的对象';
300
+ }
301
+
302
+ for (const stageId of VALID_STAGE_IDS) {
303
+ if (!(stageId in data.stages)) {
304
+ return `stages 中缺少阶段: ${stageId}`;
305
+ }
306
+ const stage = data.stages[stageId];
307
+ if (!stage || typeof stage !== 'object') {
308
+ return `阶段 ${stageId} 不是有效的对象`;
309
+ }
310
+ if (!('status' in stage)) {
311
+ return `阶段 ${stageId} 缺少 status 字段`;
312
+ }
313
+ if (!VALID_STATUSES.includes(stage.status)) {
314
+ return `阶段 ${stageId} 的 status 无效: ${stage.status}`;
315
+ }
316
+ }
317
+
318
+ return null;
319
+ }
320
+
321
+ /**
322
+ * 更新阶段状态
323
+ *
324
+ * 更新指定 Session 中某个阶段的状态和产出物路径。
325
+ * 当状态为 completed 时,自动记录 completed_at 时间戳。
326
+ *
327
+ * @param {string} sessionId - Session ID
328
+ * @param {string} stageId - 阶段 ID(S1/S2/S3)
329
+ * @param {string} status - 新状态(not_started/in_progress/pending_review/completed)
330
+ * @param {string|null} outputPath - 产出物文件路径(可选)
331
+ * @returns {{ success: boolean, session?: object, error?: string }}
332
+ */
333
+ function updateStageStatus(sessionId, stageId, status, outputPath) {
334
+ // 参数校验
335
+ if (!sessionId || typeof sessionId !== 'string') {
336
+ return { success: false, error: 'Session ID 不能为空' };
337
+ }
338
+ if (!VALID_STAGE_IDS.includes(stageId)) {
339
+ return { success: false, error: `无效的阶段 ID: ${stageId},应为 S1/S2/S3` };
340
+ }
341
+ if (!VALID_STATUSES.includes(status)) {
342
+ return { success: false, error: `无效的状态: ${status},应为 ${VALID_STATUSES.join('/')}` };
343
+ }
344
+
345
+ // 先恢复 Session 以确保数据有效
346
+ const result = resumeSession(sessionId);
347
+ if (!result.success) {
348
+ return result;
349
+ }
350
+
351
+ const sessionData = result.session;
352
+ const now = new Date().toISOString();
353
+
354
+ // 更新阶段状态
355
+ sessionData.stages[stageId].status = status;
356
+ sessionData.updated_at = now;
357
+
358
+ // 当状态为 completed 时,记录完成时间和产出物路径
359
+ if (status === 'completed') {
360
+ sessionData.stages[stageId].completed_at = now;
361
+ if (outputPath) {
362
+ sessionData.stages[stageId].output = outputPath;
363
+ }
364
+ }
365
+
366
+ // 非 completed 状态下也可以更新产出物路径
367
+ if (outputPath && status !== 'completed') {
368
+ sessionData.stages[stageId].output = outputPath;
369
+ }
370
+
371
+ // 安全写回 session.json
372
+ const writeResult = safeWriteSessionJson(sessionId, sessionData);
373
+ if (!writeResult.success) {
374
+ return { success: false, error: `更新阶段状态失败: ${writeResult.error}` };
375
+ }
376
+
377
+ return { success: true, session: sessionData };
378
+ }
379
+
380
+ /**
381
+ * 获取阶段 Checkpoint 信息
382
+ *
383
+ * 读取指定 Session 中某个阶段的 checkpoint 信息,
384
+ * 包含阶段状态、产出物路径和完成时间。
385
+ *
386
+ * @param {string} sessionId - Session ID
387
+ * @param {string} stageId - 阶段 ID(S1/S2/S3)
388
+ * @returns {{ success: boolean, checkpoint?: object, error?: string }}
389
+ */
390
+ function getCheckpoint(sessionId, stageId) {
391
+ // 参数校验
392
+ if (!sessionId || typeof sessionId !== 'string') {
393
+ return { success: false, error: 'Session ID 不能为空' };
394
+ }
395
+ if (!VALID_STAGE_IDS.includes(stageId)) {
396
+ return { success: false, error: `无效的阶段 ID: ${stageId},应为 S1/S2/S3` };
397
+ }
398
+
399
+ // 恢复 Session 数据
400
+ const result = resumeSession(sessionId);
401
+ if (!result.success) {
402
+ return result;
403
+ }
404
+
405
+ const sessionData = result.session;
406
+ const stage = sessionData.stages[stageId];
407
+
408
+ // 构建 checkpoint 信息
409
+ const checkpoint = {
410
+ stage_id: stageId,
411
+ status: stage.status,
412
+ output: stage.output,
413
+ completed_at: stage.completed_at
414
+ };
415
+
416
+ // 如果 session.json 中有该阶段的详细 checkpoint 数据,也一并返回
417
+ if (sessionData.checkpoint && sessionData.checkpoint[stageId]) {
418
+ checkpoint.details = sessionData.checkpoint[stageId];
419
+ }
420
+
421
+ return { success: true, checkpoint: checkpoint };
422
+ }
423
+
424
+ // 导出所有函数
425
+ module.exports = {
426
+ createSession,
427
+ resumeSession,
428
+ updateStageStatus,
429
+ getCheckpoint,
430
+ // 导出辅助函数和常量,便于测试
431
+ generateSessionId,
432
+ validateSessionData,
433
+ tryRepairJson,
434
+ safeWriteSessionJson,
435
+ SESSIONS_DIR,
436
+ VALID_STATUSES,
437
+ VALID_STAGE_IDS
438
+ };
File without changes
@@ -0,0 +1,171 @@
1
+ # 快捷命令处理
2
+
3
+ > 本文件由 `workflow_starter.md` 引用,定义所有快捷命令的处理逻辑。
4
+
5
+ ---
6
+
7
+ ## 命令一览
8
+
9
+ | 命令 | 说明 |
10
+ |------|------|
11
+ | `y` | 确认通过当前阶段产出 |
12
+ | `e` | 编辑修改,基于反馈迭代生成 |
13
+ | `n` | 放弃当前产出,重新生成 |
14
+ | `q` | 退出并保存当前进度 |
15
+ | `status` | 查看三阶段完成进度 |
16
+ | `back` | 返回上一阶段 |
17
+
18
+ ---
19
+
20
+ ## `y` — 确认通过
21
+
22
+ 当用户输入 `y` 时,执行以下步骤:
23
+
24
+ 1. 调用 `updateStageStatus(sessionId, stageId, 'completed', outputPath)` 将当前阶段标记为 `completed`
25
+ 2. 保存产出物文件路径到 `session.json` 对应阶段的 `output` 字段
26
+ 3. 读取 `workflow-config.json` 中当前阶段的 `next_stage`
27
+ 4. **若 `next_stage` 存在**(S1 → S2,S2 → S3):
28
+ - 更新 `session.json` 的 `current_stage` 为 `next_stage`
29
+ - 输出:
30
+ ```
31
+ ✅ 阶段 {stageId} - {stageName} 已完成
32
+ ➡️ 自动进入下一阶段: {next_stageId} - {next_stageName}
33
+ ```
34
+ - 返回 workflow_starter.md 的 Step 3 执行下一阶段
35
+ 5. **若 `next_stage` 为 `null`**(S3 完成):
36
+ - 输出工作流完成总结:
37
+ ```
38
+ 🎉 Vela 快应用开发工作流已全部完成!
39
+
40
+ 📊 完成总结:
41
+ ✅ S1 PRD 生成 — 01-prd.md
42
+ ✅ S2 技术方案 — 02-tech-design.md
43
+ ✅ S3 功能研发 — 代码已写入项目工程目录
44
+ ✅ S4 模拟器调试 — 调试通过(或 ⏭️ 已跳过)
45
+
46
+ 📂 PRD 和技术方案保存在: .ai-workspace/sessions/{session_id}/
47
+ ```
48
+
49
+ ---
50
+
51
+ ## `e` — 编辑修改
52
+
53
+ 当用户输入 `e` 时,进入迭代编辑模式:
54
+
55
+ 1. 提示用户输入修改意见:
56
+ ```
57
+ ✏️ 请输入修改意见(描述需要调整的内容):
58
+ ```
59
+ 2. 接收用户反馈后,将修改意见追加到当前阶段 Agent 的上下文中
60
+ 3. 重新执行当前阶段的 Agent(携带修改意见上下文)
61
+ 4. 展示新的产出物,再次进入 Step 4 结果展示
62
+ 5. 用户可继续选择 `y`(确认)、`e`(再次编辑)或 `n`(放弃)
63
+ 6. **支持多轮迭代**,直到用户选择 `y` 或 `n` 退出编辑模式
64
+
65
+ > 每轮编辑的修改意见会累积传递给 Agent,确保迭代上下文连贯。
66
+
67
+ ### 编辑上下文管理策略
68
+
69
+ 为防止多轮编辑导致上下文过长(超出 token 限制或分散 Agent 注意力),采用以下策略:
70
+
71
+ - **保留最近 3 轮**的完整修改意见
72
+ - **第 4 轮及更早**的修改意见,仅保留一句话摘要(由 Agent 在每轮结束时自动生成)
73
+ - 上下文格式:
74
+ ```
75
+ [历史修改摘要]
76
+ - 第1轮: {一句话摘要}
77
+ - 第2轮: {一句话摘要}
78
+
79
+ [最近修改意见]
80
+ --- 第3轮 ---
81
+ {完整修改意见}
82
+ --- 第4轮 ---
83
+ {完整修改意见}
84
+ --- 第5轮(当前)---
85
+ {完整修改意见}
86
+ ```
87
+ - 若累计编辑超过 **5 轮**,提示用户考虑使用 `n` 放弃重新生成:
88
+ ```
89
+ 💡 已迭代 {round} 轮,若调整较大建议输入 n 重新生成。
90
+ ```
91
+
92
+ ---
93
+
94
+ ## `n` — 放弃重新生成
95
+
96
+ 当用户输入 `n` 时:
97
+
98
+ 1. 丢弃当前阶段的产出物
99
+ 2. 调用 `updateStageStatus(sessionId, stageId, 'in_progress')` 重置阶段状态
100
+ 3. 清除 Agent 上下文中的历史修改意见(若有)
101
+ 4. 从头重新执行当前阶段,返回 workflow_starter.md 的 Step 3
102
+ 5. 输出:
103
+ ```
104
+ 🔄 已放弃当前产出,正在重新生成阶段 {stageId} - {stageName}...
105
+ ```
106
+
107
+ ---
108
+
109
+ ## `q` — 退出保存进度
110
+
111
+ 当用户输入 `q` 时:
112
+
113
+ 1. 保存当前 Session 状态到 `session.json`(包括当前阶段、各阶段状态)
114
+ 2. 更新 `user-config.json` 的 `last_session.session_id` 为当前 Session ID
115
+ 3. 输出保存确认:
116
+ ```
117
+ 💾 进度已保存
118
+ 📝 Session ID: {session_id}
119
+ 🎯 当前阶段: {stageId} - {stageName}
120
+ 👋 下次启动时将自动恢复到当前进度。
121
+ ```
122
+
123
+ ---
124
+
125
+ ## `status` — 查看进度
126
+
127
+ 当用户输入 `status` 时:
128
+
129
+ 1. 读取 `session.json` 中的 `stages` 和 `current_stage`
130
+ 2. 展示三阶段进度表:
131
+ ```
132
+ 📊 工作流进度:
133
+ {图标} S1 PRD 生成 — {status}
134
+ {图标} S2 技术方案 — {status}
135
+ {图标} S3 功能研发 — {status}
136
+ {图标} S4 模拟器调试 — {status}
137
+ 🎯 当前阶段: {current_stage} - {current_stage_name}
138
+ ```
139
+
140
+ **状态图标映射**:
141
+
142
+ | 状态 | 图标 |
143
+ |------|------|
144
+ | `completed` | ✅ |
145
+ | `in_progress` | 🔄 |
146
+ | `pending_review` | ⏳ |
147
+ | `not_started` | ⬜ |
148
+ | `skipped` | ⏭️ |
149
+
150
+ 当前阶段额外标注 `← 当前` 指示符。
151
+
152
+ ---
153
+
154
+ ## `back` — 返回上一阶段
155
+
156
+ 当用户输入 `back` 时,根据当前阶段判断:
157
+
158
+ | 当前阶段 | 行为 |
159
+ |---------|------|
160
+ | S1 | 无法返回,输出:`⚠️ 当前已是第一个阶段,无法返回。` |
161
+ | S2 | 返回 S1,更新 `session.json` 的 `current_stage` 为 `S1` |
162
+ | S3 | 返回 S2,更新 `session.json` 的 `current_stage` 为 `S2` |
163
+
164
+ 返回成功时输出:
165
+ ```
166
+ ⬅️ 已返回阶段 {prev_stageId} - {prev_stageName}
167
+ ```
168
+
169
+ 然后返回 workflow_starter.md 的 Step 3 执行该阶段。
170
+
171
+ > 返回上一阶段不会清除已完成阶段的产出物,用户可重新审核或修改。