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.
- package/README.md +136 -0
- package/bin/cli.js +188 -0
- package/docs/ai-workflow-tutorial.md +462 -0
- package/docs/official-site-tutorial.md +391 -0
- package/package.json +34 -0
- package/templates/.github/HARNESS-ENGINEERING-GUIDE.md +407 -0
- package/templates/.github/agents/vela-knowledge.agent.md +45 -0
- package/templates/.github/agents/vela-s1-prd.agent.md +69 -0
- package/templates/.github/agents/vela-s2-tech.agent.md +66 -0
- package/templates/.github/agents/vela-s3-coding.agent.md +301 -0
- package/templates/.github/agents/vela-workflow.agent.md +110 -0
- package/templates/.github/copilot-instructions.md +64 -0
- package/templates/.github/prompts/vela-apis.prompt.md +98 -0
- package/templates/.github/prompts/vela-best-practices.prompt.md +93 -0
- package/templates/.github/prompts/vela-components.prompt.md +118 -0
- package/templates/.github/prompts/vela-dev-guide.prompt.md +622 -0
- package/templates/.github/rules/project-init.md +45 -0
- package/templates/.github/rules/vela-coding-convention.md +324 -0
- package/templates/.github/rules/vela-css.md +217 -0
- package/templates/.github/rules/vela-design-driven.md +306 -0
- package/templates/.github/rules/vela-figma-mcp.md +198 -0
- package/templates/.github/rules/vela-format.md +119 -0
- package/templates/.github/rules/vela-layout.md +67 -0
- package/templates/.github/rules/vela-platform.md +46 -0
- package/templates/.github/rules/vela-quality.md +109 -0
- package/templates/.kiro/hooks/figma-design-check.kiro.hook +14 -0
- package/templates/.kiro/hooks/post-coding-validation.kiro.hook +13 -0
- package/templates/.kiro/hooks/validate-ux-files.kiro.hook +16 -0
- package/templates/.kiro/settings/mcp.json +7 -0
- package/templates/.kiro/skills/vela-js-app/SKILL.md +1072 -0
- package/templates/.kiro/steering/workflow-conventions.md +110 -0
- package/templates/.workflow/resource-paths.json +62 -0
- package/templates/.workflow/scripts/.gitkeep +0 -0
- package/templates/.workflow/scripts/checkpoint_manager.js +284 -0
- package/templates/.workflow/scripts/context_loader.js +841 -0
- package/templates/.workflow/scripts/figma_export.js +346 -0
- package/templates/.workflow/scripts/session_manager.js +438 -0
- package/templates/.workflow/stages/.gitkeep +0 -0
- package/templates/.workflow/stages/commands.md +171 -0
- package/templates/.workflow/stages/s1_prd.md +286 -0
- package/templates/.workflow/stages/s2_tech_design.md +302 -0
- package/templates/.workflow/stages/s3_coding.md +699 -0
- package/templates/.workflow/stages/s4_simulator.md +259 -0
- package/templates/.workflow/workflow-config.json +46 -0
- package/templates/.workflow/workflow_starter.md +912 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: auto
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Vela 快应用工作流规范
|
|
6
|
+
|
|
7
|
+
## 知识来源
|
|
8
|
+
|
|
9
|
+
- 核心知识文件: #[[file:.kiro/skills/vela-js-app/SKILL.md]]
|
|
10
|
+
- 官方文档站点: https://iot.mi.com/vela/quickapp/
|
|
11
|
+
- 当 SKILL.md 中的信息不够详细时(如某个组件的完整属性列表),使用 webFetch 访问 SKILL.md 中标注的官网链接按需获取
|
|
12
|
+
|
|
13
|
+
## 项目约束
|
|
14
|
+
|
|
15
|
+
- 目标平台: VelaOS 智能手表/手环
|
|
16
|
+
- 默认屏幕规格: 480×480 圆屏(可在 Session 中自定义)
|
|
17
|
+
- 快应用页面文件格式: `.ux`(模板 + 脚本 + 样式合一)
|
|
18
|
+
- 布局方式: Flexbox 为主,默认 flex-direction: column
|
|
19
|
+
- 禁止使用第三方 npm 包(仅使用 VelaOS 内置 API 和组件)
|
|
20
|
+
- 禁止使用 antd、echarts、element-ui、vant、axios、lodash 等外部库
|
|
21
|
+
- 图表需求使用 Vela 内置 chart 组件,不用 echarts/d3
|
|
22
|
+
- 组件只能用知识库 knowledge/components/ 中列出的内置组件
|
|
23
|
+
- API 只能用 @system.xxx 系统 API(参考 knowledge/api-reference/)
|
|
24
|
+
- 所有功能优先用原生 JS + CSS 实现
|
|
25
|
+
|
|
26
|
+
## 代码风格
|
|
27
|
+
|
|
28
|
+
- 变量/函数: camelCase
|
|
29
|
+
- 组件名: PascalCase
|
|
30
|
+
- 常量: UPPER_SNAKE_CASE
|
|
31
|
+
- 缩进: 2 空格
|
|
32
|
+
- 字符串: 单引号优先
|
|
33
|
+
- 每个文件顶部必须有文件说明注释
|
|
34
|
+
- 所有异步操作必须包含错误处理
|
|
35
|
+
- 禁止使用 `@import` 引入 CSS,外部 CSS 必须用 `<style src="./path"></style>` 标签引入
|
|
36
|
+
|
|
37
|
+
## 工作流使用提示
|
|
38
|
+
|
|
39
|
+
- 启动工作流: 在对话框中输入 `#vela-workflow`(或引用 `@.workflow/workflow_starter.md`)
|
|
40
|
+
- 快捷命令: y(确认) / e(编辑) / n(重做) / q(退出) / status(进度) / back(返回)
|
|
41
|
+
- Session 数据保存在 `.ai-workspace/sessions/` 目录
|
|
42
|
+
- 代码产出物直接写入项目工程目录(非 Session 目录)
|
|
43
|
+
- `package.json` 必须基于 `knowledge/examples/settings/package.json` 模板生成
|
|
44
|
+
|
|
45
|
+
## 项目初始化规范
|
|
46
|
+
|
|
47
|
+
- 全新项目必须使用 `npx create-aiot ux --name <项目名>` 创建
|
|
48
|
+
- 脚手架创建后必须补全缺失的产物:
|
|
49
|
+
- `package.json` 的 `devDependencies`(aiot-toolkit)
|
|
50
|
+
- `package.json` 的 `scripts`(start, build, release, watch)
|
|
51
|
+
- `README.md`(项目说明、开发/构建命令、目标平台)
|
|
52
|
+
- `.gitignore`(node_modules/, build/, dist/, .DS_Store, *.log)
|
|
53
|
+
|
|
54
|
+
## Figma 设计稿对接规范
|
|
55
|
+
|
|
56
|
+
### 获取设计信息流程
|
|
57
|
+
|
|
58
|
+
1. 使用 `get_metadata` 获取整体页面结构和节点 ID
|
|
59
|
+
2. 对每个关键页面/frame 调用 `get_design_context` 获取详细设计参数(颜色、尺寸、字体)
|
|
60
|
+
3. 使用 `get_screenshot` 获取视觉参考截图
|
|
61
|
+
4. 从设计上下文中提取图片资源 URL 并下载到 `src/common/`
|
|
62
|
+
|
|
63
|
+
### 图片资源导出规则
|
|
64
|
+
|
|
65
|
+
- **格式要求**:Vela 快应用仅支持 PNG 格式,禁止使用 SVG
|
|
66
|
+
- **下载方式**:从 Figma Desktop MCP 的 localhost 资源 URL 下载(`http://localhost:3845/assets/xxx.png`)
|
|
67
|
+
- **文件验证**:下载后用 `file` 命令验证是否为有效 PNG,检查文件大小(19字节 = 下载失败)
|
|
68
|
+
- **尺寸控制**:图标不超过 200KB,背景图用 `sips -Z <maxDimension>` 压缩到屏幕尺寸
|
|
69
|
+
- **命名规范**:`icon-<name>.png`(图标)、`bg-<name>.png`(背景)、`screenshot-<n>.png`(截图)
|
|
70
|
+
|
|
71
|
+
### 尺寸还原规则
|
|
72
|
+
|
|
73
|
+
- `manifest.json` 的 `config.designWidth` 必须与 Figma 设计稿宽度一致(如 466px 屏幕设为 466)
|
|
74
|
+
- 所有列表项、卡片、按钮使用**固定宽高**(从 Figma 节点尺寸直接取值),不用百分比
|
|
75
|
+
- border-radius 直接使用 Figma 中的数值
|
|
76
|
+
|
|
77
|
+
### Scroll 容器必备设置
|
|
78
|
+
|
|
79
|
+
Vela 快应用的 scroll 组件需要内部容器有明确尺寸才能正确滚动:
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
<scroll class="page" scroll-y="true">
|
|
83
|
+
<div class="container">
|
|
84
|
+
<!-- 内容 -->
|
|
85
|
+
</div>
|
|
86
|
+
</scroll>
|
|
87
|
+
```
|
|
88
|
+
```css
|
|
89
|
+
.page {
|
|
90
|
+
width: 100%;
|
|
91
|
+
height: 100%;
|
|
92
|
+
}
|
|
93
|
+
.container {
|
|
94
|
+
flex-direction: column;
|
|
95
|
+
width: 100%;
|
|
96
|
+
height: 1000px; /* 必须设置固定高度,大于内容实际高度 */
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 设计对比检查清单
|
|
101
|
+
|
|
102
|
+
代码生成后自动执行以下检查(由 `figma-design-check` hook 触发):
|
|
103
|
+
|
|
104
|
+
- [ ] 布局结构(flex-direction、对齐方式)与设计稿一致
|
|
105
|
+
- [ ] 关键元素尺寸(宽高、圆角、间距)使用固定值且与设计稿匹配
|
|
106
|
+
- [ ] 颜色值、字号与设计稿一致
|
|
107
|
+
- [ ] 所有图片引用指向 `src/common/` 下的有效 PNG 文件
|
|
108
|
+
- [ ] 无 SVG 文件引用
|
|
109
|
+
- [ ] 超出一屏的页面使用 scroll 包裹且 container 有固定高度
|
|
110
|
+
- [ ] 圆形屏幕安全区域 padding 正确(上下 ~50px,左右 ~24-36px)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2.0.0",
|
|
3
|
+
"paths": {
|
|
4
|
+
"agents_root": "agents",
|
|
5
|
+
"knowledge_root": "knowledge",
|
|
6
|
+
"skill_file": ".kiro/skills/vela-js-app/SKILL.md"
|
|
7
|
+
},
|
|
8
|
+
"knowledge_mode": "skill-first",
|
|
9
|
+
"skill_mappings": {
|
|
10
|
+
"prd/generate": {
|
|
11
|
+
"path": "agents/prd_agent.prompt.md",
|
|
12
|
+
"description": "S1: PRD 生成"
|
|
13
|
+
},
|
|
14
|
+
"tech/design": {
|
|
15
|
+
"path": "agents/tech_design_agent.prompt.md",
|
|
16
|
+
"description": "S2: 技术方案"
|
|
17
|
+
},
|
|
18
|
+
"coding/implement": {
|
|
19
|
+
"path": "agents/coding_agent.prompt.md",
|
|
20
|
+
"description": "S3: 功能研发"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"knowledge_mappings": {
|
|
24
|
+
"vela/dev-paradigm": {
|
|
25
|
+
"path": "knowledge/dev-paradigm",
|
|
26
|
+
"index_file": "INDEX.md",
|
|
27
|
+
"key_files": ["README.md", "quickapp-lifecycle.md", "page-routing.md", "manifest-config.md", "project-structure.md", "app-entry.md", "page-file-spec.md"],
|
|
28
|
+
"description": "Vela 快应用开发范式(生命周期、页面路由、manifest 配置、项目结构、脚本/样式规范)"
|
|
29
|
+
},
|
|
30
|
+
"vela/api-reference": {
|
|
31
|
+
"path": "knowledge/api-reference",
|
|
32
|
+
"index_file": "INDEX.md",
|
|
33
|
+
"key_files": ["README.md", "router-api.md", "app-api.md", "device-api.md", "storage-api.md", "fetch-api.md", "interconnect-api.md"],
|
|
34
|
+
"description": "Vela 快应用 API 接口(router、app、device、storage、fetch、audio、sensor 等 24 个系统 API)"
|
|
35
|
+
},
|
|
36
|
+
"vela/components": {
|
|
37
|
+
"path": "knowledge/components",
|
|
38
|
+
"index_file": "INDEX.md",
|
|
39
|
+
"key_files": ["README.md", "div.md", "list.md", "text.md", "image.md", "input.md", "scroll.md"],
|
|
40
|
+
"description": "Vela 快应用组件规范(容器组件 6 个、基础组件 10 个、表单组件 4 个、通用参考 4 个)"
|
|
41
|
+
},
|
|
42
|
+
"vela/examples": {
|
|
43
|
+
"path": "knowledge/examples",
|
|
44
|
+
"index_file": "INDEX.md",
|
|
45
|
+
"key_files": ["README.md", "todo-app.md", "album-app.md", "calculator-app.md"],
|
|
46
|
+
"description": "Vela 快应用代码示例(chart、todolist、player、settings、输入法等项目模板)"
|
|
47
|
+
},
|
|
48
|
+
"vela/best-practices": {
|
|
49
|
+
"path": "knowledge/best-practices",
|
|
50
|
+
"index_file": "INDEX.md",
|
|
51
|
+
"key_files": ["README.md", "start.md", "memory.md", "business.md"],
|
|
52
|
+
"description": "Vela 快应用最佳实践(启动优化、内存管理、业务实践)"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"stage_knowledge": {
|
|
56
|
+
"S1": ["skill"],
|
|
57
|
+
"S2": ["skill"],
|
|
58
|
+
"S3": ["skill", "vela/examples"],
|
|
59
|
+
"S4": ["skill"]
|
|
60
|
+
},
|
|
61
|
+
"skill_knowledge_note": "所有阶段优先使用 SKILL.md 作为基础知识。当需要某个组件/API 的完整属性列表时,Agent 应使用 webFetch 访问 SKILL.md 中标注的官网链接按需获取详细文档。vela/examples 仅在 S3 阶段加载,提供完整项目模板参考。"
|
|
62
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpoint 管理模块
|
|
3
|
+
*
|
|
4
|
+
* 负责阶段间的人工审核(Checkpoint)机制和阶段流转逻辑。
|
|
5
|
+
* 提供产出物审核状态管理、y/e/n 命令处理、阶段推进和前置条件校验。
|
|
6
|
+
*
|
|
7
|
+
* Requirements: 6.1, 6.2, 6.3, 6.4, 6.5, 10.2, 10.5
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const {
|
|
13
|
+
resumeSession,
|
|
14
|
+
updateStageStatus,
|
|
15
|
+
safeWriteSessionJson,
|
|
16
|
+
SESSIONS_DIR,
|
|
17
|
+
VALID_STAGE_IDS
|
|
18
|
+
} = require('./session_manager');
|
|
19
|
+
|
|
20
|
+
// 工作流配置文件路径
|
|
21
|
+
const WORKFLOW_CONFIG_PATH = path.resolve(__dirname, '../workflow-config.json');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 加载工作流配置
|
|
25
|
+
* @returns {{ success: boolean, config?: object, error?: string }}
|
|
26
|
+
*/
|
|
27
|
+
function loadWorkflowConfig() {
|
|
28
|
+
try {
|
|
29
|
+
const raw = fs.readFileSync(WORKFLOW_CONFIG_PATH, 'utf-8');
|
|
30
|
+
return { success: true, config: JSON.parse(raw) };
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return { success: false, error: `加载工作流配置失败: ${err.message}` };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 处理 Checkpoint(阶段审核点)
|
|
38
|
+
*
|
|
39
|
+
* 当阶段产出物生成完成时调用,将阶段状态设为 pending_review,
|
|
40
|
+
* 返回描述 checkpoint 状态的对象供工作流编排器使用。
|
|
41
|
+
*
|
|
42
|
+
* @param {string} sessionId - Session ID
|
|
43
|
+
* @param {string} stageId - 阶段 ID(S1/S2/S3)
|
|
44
|
+
* @param {string} output - 产出物内容路径
|
|
45
|
+
* @returns {{ success: boolean, checkpoint?: object, error?: string }}
|
|
46
|
+
*/
|
|
47
|
+
function handleCheckpoint(sessionId, stageId, output) {
|
|
48
|
+
// 参数校验
|
|
49
|
+
if (!sessionId || typeof sessionId !== 'string') {
|
|
50
|
+
return { success: false, error: 'Session ID 不能为空' };
|
|
51
|
+
}
|
|
52
|
+
if (!VALID_STAGE_IDS.includes(stageId)) {
|
|
53
|
+
return { success: false, error: `无效的阶段 ID: ${stageId},应为 S1/S2/S3` };
|
|
54
|
+
}
|
|
55
|
+
if (!output || typeof output !== 'string') {
|
|
56
|
+
return { success: false, error: '产出物路径不能为空' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 加载工作流配置获取阶段名称
|
|
60
|
+
const configResult = loadWorkflowConfig();
|
|
61
|
+
if (!configResult.success) {
|
|
62
|
+
return configResult;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const stageConfig = configResult.config.stages[stageId];
|
|
66
|
+
if (!stageConfig) {
|
|
67
|
+
return { success: false, error: `工作流配置中未找到阶段: ${stageId}` };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 更新阶段状态为 pending_review
|
|
71
|
+
const updateResult = updateStageStatus(sessionId, stageId, 'pending_review', output);
|
|
72
|
+
if (!updateResult.success) {
|
|
73
|
+
return updateResult;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
success: true,
|
|
78
|
+
checkpoint: {
|
|
79
|
+
stageId,
|
|
80
|
+
stageName: stageConfig.name,
|
|
81
|
+
outputPath: output,
|
|
82
|
+
status: 'pending_review'
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 处理 Checkpoint 命令(y/e/n)
|
|
89
|
+
*
|
|
90
|
+
* 根据用户的审核命令执行对应操作:
|
|
91
|
+
* - y: 标记阶段为 completed,推进到下一阶段
|
|
92
|
+
* - e: 保持阶段为 in_progress,返回编辑指示
|
|
93
|
+
* - n: 重置阶段为 in_progress,清除产出物
|
|
94
|
+
*
|
|
95
|
+
* @param {string} sessionId - Session ID
|
|
96
|
+
* @param {string} stageId - 阶段 ID(S1/S2/S3)
|
|
97
|
+
* @param {string} command - 用户命令(y/e/n)
|
|
98
|
+
* @param {string} outputPath - 当前产出物路径
|
|
99
|
+
* @returns {{ success: boolean, result?: object, error?: string }}
|
|
100
|
+
*/
|
|
101
|
+
function processCheckpointCommand(sessionId, stageId, command, outputPath) {
|
|
102
|
+
// 参数校验
|
|
103
|
+
if (!sessionId || typeof sessionId !== 'string') {
|
|
104
|
+
return { success: false, error: 'Session ID 不能为空' };
|
|
105
|
+
}
|
|
106
|
+
if (!VALID_STAGE_IDS.includes(stageId)) {
|
|
107
|
+
return { success: false, error: `无效的阶段 ID: ${stageId},应为 S1/S2/S3` };
|
|
108
|
+
}
|
|
109
|
+
if (!['y', 'e', 'n'].includes(command)) {
|
|
110
|
+
return { success: false, error: `无效的命令: ${command},应为 y/e/n` };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (command === 'y') {
|
|
114
|
+
// 确认通过:标记为 completed,推进到下一阶段
|
|
115
|
+
const updateResult = updateStageStatus(sessionId, stageId, 'completed', outputPath);
|
|
116
|
+
if (!updateResult.success) {
|
|
117
|
+
return updateResult;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const advanceResult = advanceToNextStage(sessionId, stageId);
|
|
121
|
+
if (!advanceResult.success) {
|
|
122
|
+
return advanceResult;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
success: true,
|
|
127
|
+
result: {
|
|
128
|
+
action: 'advance',
|
|
129
|
+
nextStage: advanceResult.nextStage
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (command === 'e') {
|
|
135
|
+
// 编辑修改:保持为 in_progress,等待用户反馈后重新生成
|
|
136
|
+
const updateResult = updateStageStatus(sessionId, stageId, 'in_progress', outputPath);
|
|
137
|
+
if (!updateResult.success) {
|
|
138
|
+
return updateResult;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
success: true,
|
|
143
|
+
result: {
|
|
144
|
+
action: 'edit'
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// command === 'n'
|
|
150
|
+
// 放弃重新生成:重置为 in_progress,清除产出物
|
|
151
|
+
const updateResult = updateStageStatus(sessionId, stageId, 'in_progress', null);
|
|
152
|
+
if (!updateResult.success) {
|
|
153
|
+
return updateResult;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
success: true,
|
|
158
|
+
result: {
|
|
159
|
+
action: 'redo'
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 推进到下一阶段
|
|
166
|
+
*
|
|
167
|
+
* 根据 workflow-config.json 中的 next_stage 配置,更新 session.json 的 current_stage。
|
|
168
|
+
* 如果当前阶段是最终阶段(next_stage 为 null),返回工作流完成指示。
|
|
169
|
+
*
|
|
170
|
+
* @param {string} sessionId - Session ID
|
|
171
|
+
* @param {string} currentStageId - 当前阶段 ID(S1/S2/S3)
|
|
172
|
+
* @returns {{ success: boolean, nextStage: string|null, isComplete: boolean, error?: string }}
|
|
173
|
+
*/
|
|
174
|
+
function advanceToNextStage(sessionId, currentStageId) {
|
|
175
|
+
// 参数校验
|
|
176
|
+
if (!sessionId || typeof sessionId !== 'string') {
|
|
177
|
+
return { success: false, nextStage: null, isComplete: false, error: 'Session ID 不能为空' };
|
|
178
|
+
}
|
|
179
|
+
if (!VALID_STAGE_IDS.includes(currentStageId)) {
|
|
180
|
+
return { success: false, nextStage: null, isComplete: false, error: `无效的阶段 ID: ${currentStageId},应为 S1/S2/S3` };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 加载工作流配置
|
|
184
|
+
const configResult = loadWorkflowConfig();
|
|
185
|
+
if (!configResult.success) {
|
|
186
|
+
return { success: false, nextStage: null, isComplete: false, error: configResult.error };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const stageConfig = configResult.config.stages[currentStageId];
|
|
190
|
+
if (!stageConfig) {
|
|
191
|
+
return { success: false, nextStage: null, isComplete: false, error: `工作流配置中未找到阶段: ${currentStageId}` };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const nextStage = stageConfig.next_stage;
|
|
195
|
+
|
|
196
|
+
// 如果没有下一阶段,工作流完成
|
|
197
|
+
if (!nextStage) {
|
|
198
|
+
return { success: true, nextStage: null, isComplete: true };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 更新 session.json 的 current_stage
|
|
202
|
+
const sessionResult = resumeSession(sessionId);
|
|
203
|
+
if (!sessionResult.success) {
|
|
204
|
+
return { success: false, nextStage: null, isComplete: false, error: sessionResult.error };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const sessionData = sessionResult.session;
|
|
208
|
+
sessionData.current_stage = nextStage;
|
|
209
|
+
sessionData.updated_at = new Date().toISOString();
|
|
210
|
+
|
|
211
|
+
const writeResult = safeWriteSessionJson(sessionId, sessionData);
|
|
212
|
+
if (!writeResult.success) {
|
|
213
|
+
return { success: false, nextStage: null, isComplete: false, error: `更新 Session 失败: ${writeResult.error}` };
|
|
214
|
+
}
|
|
215
|
+
return { success: true, nextStage, isComplete: false };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 校验前置条件
|
|
220
|
+
*
|
|
221
|
+
* 检查指定阶段的所有前置阶段是否已完成。
|
|
222
|
+
* 根据 workflow-config.json 中的 prerequisites 配置进行校验。
|
|
223
|
+
*
|
|
224
|
+
* @param {string} sessionId - Session ID
|
|
225
|
+
* @param {string} stageId - 要校验的阶段 ID(S1/S2/S3)
|
|
226
|
+
* @returns {{ success: boolean, valid?: boolean, missingPrerequisites?: string[], error?: string }}
|
|
227
|
+
*/
|
|
228
|
+
function validatePrerequisites(sessionId, stageId) {
|
|
229
|
+
// 参数校验
|
|
230
|
+
if (!sessionId || typeof sessionId !== 'string') {
|
|
231
|
+
return { success: false, error: 'Session ID 不能为空' };
|
|
232
|
+
}
|
|
233
|
+
if (!VALID_STAGE_IDS.includes(stageId)) {
|
|
234
|
+
return { success: false, error: `无效的阶段 ID: ${stageId},应为 S1/S2/S3` };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 加载工作流配置
|
|
238
|
+
const configResult = loadWorkflowConfig();
|
|
239
|
+
if (!configResult.success) {
|
|
240
|
+
return { success: false, error: configResult.error };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const stageConfig = configResult.config.stages[stageId];
|
|
244
|
+
if (!stageConfig) {
|
|
245
|
+
return { success: false, error: `工作流配置中未找到阶段: ${stageId}` };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const prerequisites = stageConfig.prerequisites || [];
|
|
249
|
+
|
|
250
|
+
// 没有前置条件,直接通过
|
|
251
|
+
if (prerequisites.length === 0) {
|
|
252
|
+
return { success: true, valid: true, missingPrerequisites: [] };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 恢复 Session 数据
|
|
256
|
+
const sessionResult = resumeSession(sessionId);
|
|
257
|
+
if (!sessionResult.success) {
|
|
258
|
+
return { success: false, error: sessionResult.error };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const stages = sessionResult.session.stages;
|
|
262
|
+
const missing = [];
|
|
263
|
+
|
|
264
|
+
for (const prereq of prerequisites) {
|
|
265
|
+
if (!stages[prereq] || stages[prereq].status !== 'completed') {
|
|
266
|
+
missing.push(prereq);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
success: true,
|
|
272
|
+
valid: missing.length === 0,
|
|
273
|
+
missingPrerequisites: missing
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
module.exports = {
|
|
278
|
+
handleCheckpoint,
|
|
279
|
+
processCheckpointCommand,
|
|
280
|
+
advanceToNextStage,
|
|
281
|
+
validatePrerequisites,
|
|
282
|
+
loadWorkflowConfig,
|
|
283
|
+
WORKFLOW_CONFIG_PATH
|
|
284
|
+
};
|