openmatrix 0.1.14 → 0.1.16
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 +14 -10
- package/dist/cli/commands/start.js +74 -9
- package/dist/orchestrator/phase-executor.d.ts +6 -0
- package/dist/orchestrator/phase-executor.js +51 -5
- package/dist/orchestrator/task-planner.d.ts +16 -0
- package/dist/orchestrator/task-planner.js +129 -1
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.js +3 -0
- package/package.json +1 -1
- package/skills/openmatrix.md +2 -0
- package/skills/start.md +62 -13
package/README.md
CHANGED
|
@@ -161,15 +161,15 @@ ls ~/.claude/commands/om/
|
|
|
161
161
|
|
|
162
162
|
### 1️⃣ 三级质量配置 (第一个问题就让你选)
|
|
163
163
|
|
|
164
|
-
| 级别 | TDD | 覆盖率 | Lint | 安全 | AI验收 | 适用场景 |
|
|
165
|
-
|
|
166
|
-
| **strict** | ✅ | >80% | ✅ 严格 | ✅ | ✅ | 🏭 **生产代码** |
|
|
167
|
-
| **balanced** | ❌ | >60% | ✅ | ✅ | ✅ | 📦 日常开发 |
|
|
168
|
-
| **fast** | ❌ | >20% | ❌ | ❌ | ❌ | 🏃 快速原型 |
|
|
164
|
+
| 级别 | TDD | 覆盖率 | Lint | 安全 | E2E测试 | AI验收 | 适用场景 |
|
|
165
|
+
|:----:|:---:|:------:|:----:|:----:|:-------:|:------:|---------|
|
|
166
|
+
| **strict** | ✅ | >80% | ✅ 严格 | ✅ | ❓ 可选 | ✅ | 🏭 **生产代码** |
|
|
167
|
+
| **balanced** | ❌ | >60% | ✅ | ✅ | ❓ 可选 | ✅ | 📦 日常开发 |
|
|
168
|
+
| **fast** | ❌ | >20% | ❌ | ❌ | ❌ | ❌ | 🏃 快速原型 |
|
|
169
169
|
|
|
170
|
-
> strict 可配置为 100%。默认 >80% 覆盖核心业务逻辑。
|
|
170
|
+
> E2E 测试耗时较长,建议根据项目需要选择。strict 可配置为 100%。默认 >80% 覆盖核心业务逻辑。
|
|
171
171
|
|
|
172
|
-
### 2️⃣
|
|
172
|
+
### 2️⃣ 七道质量门禁 (Verify 阶段)
|
|
173
173
|
|
|
174
174
|
```
|
|
175
175
|
┌─────────────────────────────────────────────────────────────┐
|
|
@@ -180,7 +180,8 @@ ls ~/.claude/commands/om/
|
|
|
180
180
|
│ 🚪 Gate 3: 覆盖率检查 >20%/60%/80% → 可配置 │
|
|
181
181
|
│ 🚪 Gate 4: Lint 检查 无 error → 可配置 │
|
|
182
182
|
│ 🚪 Gate 5: 安全扫描 npm audit → 无高危漏洞 │
|
|
183
|
-
│ 🚪 Gate 6:
|
|
183
|
+
│ 🚪 Gate 6: E2E 测试 Playwright等 → 可选 │
|
|
184
|
+
│ 🚪 Gate 7: 验收标准 用户定义 → 必须全部满足 │
|
|
184
185
|
└─────────────────────────────────────────────────────────────┘
|
|
185
186
|
```
|
|
186
187
|
|
|
@@ -378,6 +379,7 @@ Accept 阶段由 Reviewer Agent 执行:
|
|
|
378
379
|
"build": { "success": true },
|
|
379
380
|
"lint": { "errors": 0, "warnings": 3 },
|
|
380
381
|
"security": { "vulnerabilities": [] },
|
|
382
|
+
"e2e": { "passed": 5, "failed": 0, "skipped": 0 },
|
|
381
383
|
"acceptance": { "met": 5, "total": 5 }
|
|
382
384
|
}
|
|
383
385
|
```
|
|
@@ -431,7 +433,8 @@ OpenMatrix 通过 Claude Code Agent 工具**原生支持所有主流编程语言
|
|
|
431
433
|
"tdd": false,
|
|
432
434
|
"minCoverage": 60,
|
|
433
435
|
"strictLint": true,
|
|
434
|
-
"securityScan": true
|
|
436
|
+
"securityScan": true,
|
|
437
|
+
"e2eTests": false
|
|
435
438
|
},
|
|
436
439
|
"approvalPoints": ["plan", "merge"],
|
|
437
440
|
"agents": { "maxConcurrent": 3 }
|
|
@@ -452,12 +455,13 @@ cd openmatrix && npm install && npm run build && npm test
|
|
|
452
455
|
## Roadmap
|
|
453
456
|
|
|
454
457
|
- [x] TDD 模式
|
|
455
|
-
- [x]
|
|
458
|
+
- [x] 7 道质量门禁
|
|
456
459
|
- [x] Meeting 机制
|
|
457
460
|
- [x] 质量报告
|
|
458
461
|
- [x] AI 验收
|
|
459
462
|
- [x] `/om:auto` 全自动模式
|
|
460
463
|
- [x] 多语言支持 (Python/Go/Java/TypeScript 等)
|
|
464
|
+
- [x] E2E 测试支持 (Web/Mobile/GUI)
|
|
461
465
|
- [ ] VSCode 扩展
|
|
462
466
|
- [ ] CI/CD 集成
|
|
463
467
|
|
|
@@ -42,15 +42,22 @@ const task_planner_js_1 = require("../../orchestrator/task-planner.js");
|
|
|
42
42
|
const approval_manager_js_1 = require("../../orchestrator/approval-manager.js");
|
|
43
43
|
const executor_js_1 = require("../../orchestrator/executor.js");
|
|
44
44
|
const gitignore_js_1 = require("../../utils/gitignore.js");
|
|
45
|
+
const index_js_1 = require("../../types/index.js");
|
|
45
46
|
const fs = __importStar(require("fs/promises"));
|
|
46
47
|
const path = __importStar(require("path"));
|
|
47
48
|
exports.startCommand = new commander_1.Command('start')
|
|
48
49
|
.description('启动新的任务执行周期')
|
|
49
50
|
.argument('[input]', '任务文件路径或描述')
|
|
50
51
|
.option('-c, --config <path>', '配置文件路径')
|
|
52
|
+
.option('--init-only', '仅初始化 .openmatrix 目录,不执行任务')
|
|
51
53
|
.option('--skip-questions', '跳过澄清问题')
|
|
52
54
|
.option('--mode <mode>', '执行模式 (confirm-all|confirm-key|auto)')
|
|
53
55
|
.option('--json', '输出 JSON 格式 (供 Skill 解析)')
|
|
56
|
+
.option('--title <title>', '任务标题')
|
|
57
|
+
.option('--description <desc>', '任务描述')
|
|
58
|
+
.option('-q, --quality <level>', '质量级别 (strict|balanced|fast)')
|
|
59
|
+
.option('-t, --tech-stack <stack>', '技术栈 (逗号分隔,如 "TypeScript,Vue.js,PostgreSQL")')
|
|
60
|
+
.option('--docs <level>', '文档级别 (full|basic|minimal|none)')
|
|
54
61
|
.action(async (input, options) => {
|
|
55
62
|
const basePath = process.cwd();
|
|
56
63
|
const omPath = path.join(basePath, '.openmatrix');
|
|
@@ -60,6 +67,21 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
60
67
|
await fs.mkdir(path.join(omPath, 'approvals'), { recursive: true });
|
|
61
68
|
// 确保 .openmatrix 被 git 忽略
|
|
62
69
|
await (0, gitignore_js_1.ensureOpenmatrixGitignore)(basePath);
|
|
70
|
+
// --init-only 模式:仅初始化目录后返回
|
|
71
|
+
if (options.initOnly) {
|
|
72
|
+
if (options.json) {
|
|
73
|
+
console.log(JSON.stringify({
|
|
74
|
+
status: 'initialized',
|
|
75
|
+
message: '.openmatrix 目录已初始化',
|
|
76
|
+
path: omPath
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.log('✅ .openmatrix 目录已初始化');
|
|
81
|
+
console.log(` 路径: ${omPath}`);
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
63
85
|
const stateManager = new state_manager_js_1.StateManager(omPath);
|
|
64
86
|
await stateManager.initialize();
|
|
65
87
|
const state = await stateManager.getState();
|
|
@@ -79,10 +101,18 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
79
101
|
}
|
|
80
102
|
return;
|
|
81
103
|
}
|
|
82
|
-
//
|
|
104
|
+
// 构建任务内容
|
|
83
105
|
let taskContent = input;
|
|
106
|
+
// 如果提供了 --title 和 --description,构建任务内容
|
|
107
|
+
if (options.title || options.description) {
|
|
108
|
+
const title = options.title || '未命名任务';
|
|
109
|
+
const description = options.description || '';
|
|
110
|
+
const techStack = options.techStack ? `\n\n技术栈: ${options.techStack}` : '';
|
|
111
|
+
const docs = options.docs ? `\n文档要求: ${options.docs}` : '';
|
|
112
|
+
taskContent = `# ${title}\n\n${description}${techStack}${docs}`;
|
|
113
|
+
}
|
|
114
|
+
// 如果没有任务内容,尝试读取默认文件
|
|
84
115
|
if (!taskContent) {
|
|
85
|
-
// 尝试读取默认任务文件
|
|
86
116
|
const defaultPath = path.join(basePath, 'TASK.md');
|
|
87
117
|
try {
|
|
88
118
|
taskContent = await fs.readFile(defaultPath, 'utf-8');
|
|
@@ -101,6 +131,7 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
101
131
|
console.log('❌ 请提供任务文件路径或描述');
|
|
102
132
|
console.log(' 用法: openmatrix start <task.md>');
|
|
103
133
|
console.log(' 或创建 TASK.md 文件');
|
|
134
|
+
console.log(' 或使用 --title 和 --description 选项');
|
|
104
135
|
}
|
|
105
136
|
return;
|
|
106
137
|
}
|
|
@@ -158,9 +189,6 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
158
189
|
const executionMode = options.mode || 'confirm-key';
|
|
159
190
|
let approvalPoints = [];
|
|
160
191
|
// 根据模式设置审批点
|
|
161
|
-
// auto 模式: 空数组,不暂停任何审批点
|
|
162
|
-
// confirm-key 模式: 仅在关键节点暂停
|
|
163
|
-
// confirm-all 模式: 每个阶段都暂停
|
|
164
192
|
switch (executionMode) {
|
|
165
193
|
case 'confirm-all':
|
|
166
194
|
approvalPoints = ['plan', 'phase', 'merge', 'deploy'];
|
|
@@ -169,18 +197,27 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
169
197
|
approvalPoints = ['plan', 'merge', 'deploy'];
|
|
170
198
|
break;
|
|
171
199
|
case 'auto':
|
|
172
|
-
approvalPoints = [];
|
|
200
|
+
approvalPoints = [];
|
|
173
201
|
break;
|
|
174
202
|
default:
|
|
175
203
|
approvalPoints = ['plan', 'merge'];
|
|
176
204
|
}
|
|
205
|
+
// 处理质量配置
|
|
206
|
+
let qualityConfig;
|
|
207
|
+
if (options.quality) {
|
|
208
|
+
const qualityLevel = options.quality.toLowerCase();
|
|
209
|
+
if (['strict', 'balanced', 'fast'].includes(qualityLevel)) {
|
|
210
|
+
qualityConfig = index_js_1.QUALITY_PRESETS[qualityLevel];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
177
213
|
// 更新状态
|
|
178
214
|
await stateManager.updateState({
|
|
179
215
|
status: 'running',
|
|
180
216
|
currentPhase: 'execution',
|
|
181
217
|
config: {
|
|
182
218
|
...state.config,
|
|
183
|
-
approvalPoints: approvalPoints
|
|
219
|
+
approvalPoints: approvalPoints,
|
|
220
|
+
quality: qualityConfig || state.config.quality
|
|
184
221
|
}
|
|
185
222
|
});
|
|
186
223
|
// 创建审批请求(如果有审批点)
|
|
@@ -198,7 +235,15 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
198
235
|
index: i + 1,
|
|
199
236
|
title: t.title,
|
|
200
237
|
priority: t.priority
|
|
201
|
-
}))
|
|
238
|
+
})),
|
|
239
|
+
// 额外信息供 Skill 使用
|
|
240
|
+
taskInfo: {
|
|
241
|
+
title: options.title || parsedTask.title,
|
|
242
|
+
description: options.description,
|
|
243
|
+
quality: options.quality,
|
|
244
|
+
techStack: options.techStack,
|
|
245
|
+
docs: options.docs
|
|
246
|
+
}
|
|
202
247
|
}));
|
|
203
248
|
}
|
|
204
249
|
else {
|
|
@@ -208,6 +253,12 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
208
253
|
});
|
|
209
254
|
console.log(`\n🎯 执行模式: ${executionMode}`);
|
|
210
255
|
console.log(` 审批点: ${approvalPoints.join(', ')}`);
|
|
256
|
+
if (options.quality) {
|
|
257
|
+
console.log(` 质量级别: ${options.quality}`);
|
|
258
|
+
}
|
|
259
|
+
if (options.techStack) {
|
|
260
|
+
console.log(` 技术栈: ${options.techStack}`);
|
|
261
|
+
}
|
|
211
262
|
console.log(`\n⏸️ 等待计划审批`);
|
|
212
263
|
console.log(` 审批ID: ${approval.id}`);
|
|
213
264
|
console.log(` 使用 /om:approve ${approval.id} 审批`);
|
|
@@ -242,7 +293,15 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
242
293
|
taskId: t.taskId,
|
|
243
294
|
agentType: t.agentType,
|
|
244
295
|
timeout: t.timeout
|
|
245
|
-
}))
|
|
296
|
+
})),
|
|
297
|
+
// 额外信息供 Skill 使用
|
|
298
|
+
taskInfo: {
|
|
299
|
+
title: options.title || parsedTask.title,
|
|
300
|
+
description: options.description,
|
|
301
|
+
quality: options.quality,
|
|
302
|
+
techStack: options.techStack,
|
|
303
|
+
docs: options.docs
|
|
304
|
+
}
|
|
246
305
|
}));
|
|
247
306
|
}
|
|
248
307
|
else {
|
|
@@ -252,6 +311,12 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
252
311
|
});
|
|
253
312
|
console.log(`\n🎯 执行模式: ${executionMode}`);
|
|
254
313
|
console.log(` 审批点: ${approvalPoints.length > 0 ? approvalPoints.join(', ') : '无 (全自动)'}`);
|
|
314
|
+
if (options.quality) {
|
|
315
|
+
console.log(` 质量级别: ${options.quality}`);
|
|
316
|
+
}
|
|
317
|
+
if (options.techStack) {
|
|
318
|
+
console.log(` 技术栈: ${options.techStack}`);
|
|
319
|
+
}
|
|
255
320
|
console.log('\n🚀 开始执行...');
|
|
256
321
|
console.log(' 使用 /om:status 查看进度');
|
|
257
322
|
}
|
|
@@ -39,6 +39,7 @@ class PhaseExecutor {
|
|
|
39
39
|
minCoverage: 60,
|
|
40
40
|
strictLint: true,
|
|
41
41
|
securityScan: true,
|
|
42
|
+
e2eTests: false,
|
|
42
43
|
level: 'balanced'
|
|
43
44
|
};
|
|
44
45
|
constructor(stateManager, approvalManager) {
|
|
@@ -86,9 +87,9 @@ class PhaseExecutor {
|
|
|
86
87
|
*/
|
|
87
88
|
setQualityLevel(level) {
|
|
88
89
|
const presets = {
|
|
89
|
-
fast: { tdd: false, minCoverage: 0, strictLint: false, securityScan: false, level: 'fast' },
|
|
90
|
-
balanced: { tdd: false, minCoverage: 60, strictLint: true, securityScan: true, level: 'balanced' },
|
|
91
|
-
strict: { tdd: true, minCoverage: 80, strictLint: true, securityScan: true, level: 'strict' }
|
|
90
|
+
fast: { tdd: false, minCoverage: 0, strictLint: false, securityScan: false, e2eTests: false, level: 'fast' },
|
|
91
|
+
balanced: { tdd: false, minCoverage: 60, strictLint: true, securityScan: true, e2eTests: false, level: 'balanced' },
|
|
92
|
+
strict: { tdd: true, minCoverage: 80, strictLint: true, securityScan: true, e2eTests: false, level: 'strict' }
|
|
92
93
|
};
|
|
93
94
|
this.qualityConfig = presets[level];
|
|
94
95
|
this.minTestCoverage = this.qualityConfig.minCoverage;
|
|
@@ -376,6 +377,7 @@ ${isTDDMode ? '- [ ] 所有测试通过 (GREEN)' : ''}
|
|
|
376
377
|
| 覆盖率 | >= ${qc.minCoverage}% | ${qc.minCoverage > 0 ? '❌ 阻止通过' : '⚠️ 仅警告'} |
|
|
377
378
|
| Lint | ${qc.strictLint ? '无 error' : '无严重 error'} | ${qc.strictLint ? '❌ 阻止通过' : '⚠️ 仅警告'} |
|
|
378
379
|
| 安全 | 无高危漏洞 | ${qc.securityScan ? '❌ 阻止通过' : '⏭️ 跳过'} |
|
|
380
|
+
| E2E | 全部通过 | ${qc.e2eTests ? '❌ 阻止通过' : '⏭️ 跳过'} |
|
|
379
381
|
| 验收标准 | 全部满足 | ❌ 阻止通过 |
|
|
380
382
|
|
|
381
383
|
## 自动化验证命令
|
|
@@ -415,7 +417,21 @@ npm audit --audit-level=high || echo "Security scan skipped"
|
|
|
415
417
|
**要求**: 无 high/critical 漏洞
|
|
416
418
|
**失败后果**: ❌ VERIFY_FAILED` : '⏭️ 已禁用'}
|
|
417
419
|
|
|
418
|
-
### 6.
|
|
420
|
+
### 6. E2E 测试 (端到端测试)
|
|
421
|
+
${qc.e2eTests ? `\`\`\`bash
|
|
422
|
+
# Web 应用: Playwright / Cypress
|
|
423
|
+
npx playwright test || npx cypress run
|
|
424
|
+
|
|
425
|
+
# 移动端: Appium / Detox
|
|
426
|
+
npx appium ... || npx detox test
|
|
427
|
+
|
|
428
|
+
# GUI 桌面应用: 根据项目配置
|
|
429
|
+
npm run test:e2e
|
|
430
|
+
\`\`\`
|
|
431
|
+
**要求**: 所有 E2E 测试通过
|
|
432
|
+
**失败后果**: ❌ VERIFY_FAILED` : '⏭️ 已禁用'}
|
|
433
|
+
|
|
434
|
+
### 7. 验收标准验证`);
|
|
419
435
|
// 注入验收标准
|
|
420
436
|
if (task.acceptanceCriteria && task.acceptanceCriteria.length > 0) {
|
|
421
437
|
parts.push(`
|
|
@@ -458,6 +474,13 @@ ${task.acceptanceCriteria.map((c, i) => `${i + 1}. [ ] ${c}`).join('\n')}
|
|
|
458
474
|
"vulnerabilities": [],
|
|
459
475
|
"status": "pass|fail"
|
|
460
476
|
},
|
|
477
|
+
"e2e": {
|
|
478
|
+
"passed": 0,
|
|
479
|
+
"failed": 0,
|
|
480
|
+
"skipped": 0,
|
|
481
|
+
"duration": 0,
|
|
482
|
+
"status": "pass|fail|skipped"
|
|
483
|
+
},
|
|
461
484
|
"acceptance": {
|
|
462
485
|
"total": ${task.acceptanceCriteria?.length || 0},
|
|
463
486
|
"met": 0,
|
|
@@ -478,6 +501,7 @@ Quality Score: [A/B/C/D/F]
|
|
|
478
501
|
- Build: ✅ Success
|
|
479
502
|
- Lint: ✅ No errors
|
|
480
503
|
- Security: ✅ No vulnerabilities
|
|
504
|
+
${qc.e2eTests ? '- E2E: ✅ X/X passed' : ''}
|
|
481
505
|
- Acceptance: ✅ N/M criteria met
|
|
482
506
|
\`\`\`
|
|
483
507
|
|
|
@@ -712,6 +736,7 @@ ACCEPT_FAILED
|
|
|
712
736
|
build: { success: false, errors: [] },
|
|
713
737
|
lint: { errors: 0, warnings: 0 },
|
|
714
738
|
security: { vulnerabilities: 0 },
|
|
739
|
+
e2e: { passed: 0, failed: 0, skipped: 0, duration: 0 },
|
|
715
740
|
acceptance: { met: 0, total: 0 }
|
|
716
741
|
};
|
|
717
742
|
// 解析测试结果
|
|
@@ -738,13 +763,27 @@ ACCEPT_FAILED
|
|
|
738
763
|
const vulnMatch = output.match(/(\d+)\s*(?:vulnerabilities|vulnerable)/i);
|
|
739
764
|
if (vulnMatch)
|
|
740
765
|
result.security.vulnerabilities = parseInt(vulnMatch[1], 10);
|
|
766
|
+
// 解析 E2E 测试结果
|
|
767
|
+
const e2ePassedMatch = output.match(/(?:e2e|playwright|cypress|appium|detox).*?(\d+)\s*(?:passed|passing)/i);
|
|
768
|
+
if (e2ePassedMatch)
|
|
769
|
+
result.e2e.passed = parseInt(e2ePassedMatch[1], 10);
|
|
770
|
+
const e2eFailedMatch = output.match(/(?:e2e|playwright|cypress|appium|detox).*?(\d+)\s*(?:failed|failing)/i);
|
|
771
|
+
if (e2eFailedMatch)
|
|
772
|
+
result.e2e.failed = parseInt(e2eFailedMatch[1], 10);
|
|
773
|
+
const e2eSkippedMatch = output.match(/(?:e2e|playwright|cypress|appium|detox).*?(\d+)\s*skipped/i);
|
|
774
|
+
if (e2eSkippedMatch)
|
|
775
|
+
result.e2e.skipped = parseInt(e2eSkippedMatch[1], 10);
|
|
776
|
+
const e2eDurationMatch = output.match(/(?:e2e|playwright|cypress|appium|detox).*?(\d+)\s*(?:ms|s|min)/i);
|
|
777
|
+
if (e2eDurationMatch)
|
|
778
|
+
result.e2e.duration = parseInt(e2eDurationMatch[1], 10);
|
|
741
779
|
// 判断是否通过
|
|
742
780
|
const qc = this.qualityConfig;
|
|
743
781
|
const testsPassed = result.tests.failed === 0;
|
|
744
782
|
const coverageOk = result.tests.coverage >= qc.minCoverage;
|
|
745
783
|
const lintOk = qc.strictLint ? result.lint.errors === 0 : true;
|
|
746
784
|
const buildOk = result.build.success;
|
|
747
|
-
|
|
785
|
+
const e2eOk = qc.e2eTests ? result.e2e.failed === 0 : true;
|
|
786
|
+
result.passed = testsPassed && coverageOk && lintOk && buildOk && e2eOk;
|
|
748
787
|
return result;
|
|
749
788
|
}
|
|
750
789
|
/**
|
|
@@ -777,6 +816,13 @@ ACCEPT_FAILED
|
|
|
777
816
|
vulnerabilities: [],
|
|
778
817
|
status: gateResult.security.vulnerabilities === 0 ? 'pass' : 'fail'
|
|
779
818
|
},
|
|
819
|
+
e2e: {
|
|
820
|
+
passed: gateResult.e2e.passed,
|
|
821
|
+
failed: gateResult.e2e.failed,
|
|
822
|
+
skipped: gateResult.e2e.skipped,
|
|
823
|
+
duration: gateResult.e2e.duration,
|
|
824
|
+
status: qc.e2eTests ? (gateResult.e2e.failed === 0 ? 'pass' : 'fail') : 'skipped'
|
|
825
|
+
},
|
|
780
826
|
acceptance: {
|
|
781
827
|
total: task.acceptanceCriteria?.length || 0,
|
|
782
828
|
met: gateResult.acceptance.met,
|
|
@@ -17,6 +17,10 @@ export interface UserAnswers {
|
|
|
17
17
|
testCoverage?: string;
|
|
18
18
|
documentationLevel?: string;
|
|
19
19
|
additionalContext?: Record<string, string>;
|
|
20
|
+
/** 是否启用 E2E 测试 */
|
|
21
|
+
e2eTests?: boolean;
|
|
22
|
+
/** E2E 测试类型 (web/mobile/gui) */
|
|
23
|
+
e2eType?: 'web' | 'mobile' | 'gui';
|
|
20
24
|
}
|
|
21
25
|
/**
|
|
22
26
|
* TaskPlanner - 任务拆解器
|
|
@@ -57,6 +61,18 @@ export declare class TaskPlanner {
|
|
|
57
61
|
* 构建测试任务描述
|
|
58
62
|
*/
|
|
59
63
|
private buildTestDescription;
|
|
64
|
+
/**
|
|
65
|
+
* 构建 E2E 测试任务描述
|
|
66
|
+
*/
|
|
67
|
+
private buildE2ETestDescription;
|
|
68
|
+
/**
|
|
69
|
+
* 获取 E2E 测试类型配置
|
|
70
|
+
*/
|
|
71
|
+
private getE2ETypeConfig;
|
|
72
|
+
/**
|
|
73
|
+
* 生成用户流程测试用例
|
|
74
|
+
*/
|
|
75
|
+
private generateUserFlows;
|
|
60
76
|
/**
|
|
61
77
|
* 解析覆盖率数值
|
|
62
78
|
*/
|
|
@@ -164,7 +164,36 @@ ${parsedTask.deliverables.map(d => `- ${d}`).join('\n')}
|
|
|
164
164
|
]
|
|
165
165
|
});
|
|
166
166
|
}
|
|
167
|
-
// 4.
|
|
167
|
+
// 4. E2E 测试任务 (如果启用,作为 verify 阶段的一部分)
|
|
168
|
+
if (userContext.e2eTests) {
|
|
169
|
+
const e2eTaskId = this.generateTaskId();
|
|
170
|
+
const e2eType = userContext.e2eType || 'web';
|
|
171
|
+
// E2E 测试依赖所有开发任务和单元测试任务
|
|
172
|
+
const allTestDependencies = [...devTaskIds];
|
|
173
|
+
breakdowns.forEach(b => {
|
|
174
|
+
if (b.phase === 'verify' && b.title.startsWith('测试:')) {
|
|
175
|
+
allTestDependencies.push(b.taskId);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
breakdowns.push({
|
|
179
|
+
taskId: e2eTaskId,
|
|
180
|
+
title: '端到端(E2E)测试',
|
|
181
|
+
description: this.buildE2ETestDescription(e2eType, parsedTask, userContext),
|
|
182
|
+
priority: 'P0', // E2E 测试是关键任务
|
|
183
|
+
dependencies: allTestDependencies, // 依赖所有开发任务和单元测试
|
|
184
|
+
estimatedComplexity: 'high',
|
|
185
|
+
assignedAgent: 'tester',
|
|
186
|
+
phase: 'verify',
|
|
187
|
+
acceptanceCriteria: [
|
|
188
|
+
'所有 E2E 测试用例通过',
|
|
189
|
+
'关键用户流程验证完成',
|
|
190
|
+
'跨浏览器/设备兼容性验证',
|
|
191
|
+
'E2E 测试报告已生成',
|
|
192
|
+
'无阻塞级别的缺陷'
|
|
193
|
+
]
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
// 5. 文档任务 (如果需要)
|
|
168
197
|
if (userContext.documentationLevel && userContext.documentationLevel !== '无需文档') {
|
|
169
198
|
breakdowns.push({
|
|
170
199
|
taskId: this.generateTaskId(),
|
|
@@ -196,11 +225,15 @@ ${userContext.documentationLevel}
|
|
|
196
225
|
* 提取用户上下文
|
|
197
226
|
*/
|
|
198
227
|
extractUserContext(answers) {
|
|
228
|
+
const e2eAnswer = answers['E2E测试'] || answers['e2eTests'] || answers['e2e'];
|
|
229
|
+
const e2eTypeAnswer = answers['E2E类型'] || answers['e2eType'];
|
|
199
230
|
return {
|
|
200
231
|
objective: answers['目标'] || answers['objective'],
|
|
201
232
|
techStack: this.parseArrayAnswer(answers['技术栈'] || answers['techStack']),
|
|
202
233
|
testCoverage: answers['测试'] || answers['testCoverage'],
|
|
203
234
|
documentationLevel: answers['文档'] || answers['documentationLevel'],
|
|
235
|
+
e2eTests: e2eAnswer === 'true' || e2eAnswer === '✅ 启用 E2E 测试' || e2eAnswer === '是',
|
|
236
|
+
e2eType: e2eTypeAnswer || 'web',
|
|
204
237
|
additionalContext: answers
|
|
205
238
|
};
|
|
206
239
|
}
|
|
@@ -263,6 +296,101 @@ ${devTaskId}
|
|
|
263
296
|
- 测试报告
|
|
264
297
|
- 覆盖率报告`;
|
|
265
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* 构建 E2E 测试任务描述
|
|
301
|
+
*/
|
|
302
|
+
buildE2ETestDescription(e2eType, parsedTask, userContext) {
|
|
303
|
+
const typeConfig = this.getE2ETypeConfig(e2eType);
|
|
304
|
+
return `## E2E 测试目标
|
|
305
|
+
执行完整的端到端测试,验证关键用户流程
|
|
306
|
+
|
|
307
|
+
## 应用类型
|
|
308
|
+
${typeConfig.description}
|
|
309
|
+
|
|
310
|
+
## 测试框架
|
|
311
|
+
${typeConfig.frameworks.map(f => `- ${f}`).join('\n')}
|
|
312
|
+
|
|
313
|
+
## 测试范围
|
|
314
|
+
${parsedTask.goals.map((g, i) => `${i + 1}. ${g}`).join('\n')}
|
|
315
|
+
|
|
316
|
+
## 关键用户流程
|
|
317
|
+
根据应用功能,测试以下流程:
|
|
318
|
+
${this.generateUserFlows(parsedTask, e2eType)}
|
|
319
|
+
|
|
320
|
+
## 测试环境
|
|
321
|
+
${typeConfig.environments.map(e => `- ${e}`).join('\n')}
|
|
322
|
+
|
|
323
|
+
## 测试要求
|
|
324
|
+
1. **关键路径覆盖**: 所有核心用户流程必须有 E2E 测试
|
|
325
|
+
2. **断言完整**: 每个测试步骤必须有明确的断言
|
|
326
|
+
3. **等待策略**: 使用合理的等待机制,避免硬编码延迟
|
|
327
|
+
4. **数据隔离**: 测试数据独立,不影响其他测试
|
|
328
|
+
5. **清理机制**: 测试后清理创建的数据
|
|
329
|
+
|
|
330
|
+
## 输出要求
|
|
331
|
+
- E2E 测试文件 (tests/e2e/*.spec.ts)
|
|
332
|
+
- 测试执行报告
|
|
333
|
+
- 截图/录像 (失败时)
|
|
334
|
+
- 测试覆盖率报告 (如有)
|
|
335
|
+
|
|
336
|
+
## 运行命令
|
|
337
|
+
\`\`\`bash
|
|
338
|
+
${typeConfig.runCommand}
|
|
339
|
+
\`\`\`
|
|
340
|
+
|
|
341
|
+
## 验收标准
|
|
342
|
+
- [ ] 所有 E2E 测试用例通过
|
|
343
|
+
- [ ] 关键用户流程验证完成
|
|
344
|
+
- [ ] 跨浏览器/设备兼容性验证
|
|
345
|
+
- [ ] E2E 测试报告已生成
|
|
346
|
+
- [ ] 无阻塞级别的缺陷`;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* 获取 E2E 测试类型配置
|
|
350
|
+
*/
|
|
351
|
+
getE2ETypeConfig(type) {
|
|
352
|
+
const configs = {
|
|
353
|
+
web: {
|
|
354
|
+
description: 'Web 应用 (浏览器)',
|
|
355
|
+
frameworks: ['Playwright (推荐)', 'Cypress', 'Selenium WebDriver', 'Puppeteer'],
|
|
356
|
+
environments: ['Chrome', 'Firefox', 'Safari', 'Edge', 'Mobile Viewports'],
|
|
357
|
+
runCommand: 'npx playwright test --reporter=html'
|
|
358
|
+
},
|
|
359
|
+
mobile: {
|
|
360
|
+
description: '移动应用 (iOS/Android)',
|
|
361
|
+
frameworks: ['Appium', 'Detox (React Native)', 'XCUITest (iOS)', 'Espresso (Android)'],
|
|
362
|
+
environments: ['iOS Simulator', 'Android Emulator', 'Real Devices'],
|
|
363
|
+
runCommand: 'npx appium --base-path /wd/hub && npm run test:e2e'
|
|
364
|
+
},
|
|
365
|
+
gui: {
|
|
366
|
+
description: 'GUI 桌面应用 (Electron/Native)',
|
|
367
|
+
frameworks: ['Playwright for Electron', 'Spectron (Electron)', 'Robot Framework', 'PyAutoGUI'],
|
|
368
|
+
environments: ['Windows', 'macOS', 'Linux'],
|
|
369
|
+
runCommand: 'npm run test:e2e'
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
return configs[type];
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* 生成用户流程测试用例
|
|
376
|
+
*/
|
|
377
|
+
generateUserFlows(parsedTask, type) {
|
|
378
|
+
const flows = [];
|
|
379
|
+
const goals = parsedTask.goals;
|
|
380
|
+
// 根据目标生成用户流程
|
|
381
|
+
goals.forEach((goal, i) => {
|
|
382
|
+
flows.push(`\n### 流程 ${i + 1}: ${goal}`);
|
|
383
|
+
flows.push('```gherkin');
|
|
384
|
+
flows.push(`Feature: ${goal}`);
|
|
385
|
+
flows.push('');
|
|
386
|
+
flows.push(' Scenario: 正常流程');
|
|
387
|
+
flows.push(` Given 用户已启动应用`);
|
|
388
|
+
flows.push(` When 用户执行 "${goal}" 操作`);
|
|
389
|
+
flows.push(` Then 操作成功完成`);
|
|
390
|
+
flows.push('```');
|
|
391
|
+
});
|
|
392
|
+
return flows.join('\n');
|
|
393
|
+
}
|
|
266
394
|
/**
|
|
267
395
|
* 解析覆盖率数值
|
|
268
396
|
*/
|
package/dist/types/index.d.ts
CHANGED
|
@@ -107,6 +107,8 @@ export interface QualityConfig {
|
|
|
107
107
|
strictLint: boolean;
|
|
108
108
|
/** 安全扫描 */
|
|
109
109
|
securityScan: boolean;
|
|
110
|
+
/** 端到端测试 (适用于 Web 项目等需要 E2E 测试的场景) */
|
|
111
|
+
e2eTests: boolean;
|
|
110
112
|
/** 质量级别 */
|
|
111
113
|
level: 'fast' | 'balanced' | 'strict';
|
|
112
114
|
}
|
|
@@ -141,6 +143,14 @@ export interface QualityReport {
|
|
|
141
143
|
vulnerabilities: SecurityVulnerability[];
|
|
142
144
|
status: 'pass' | 'fail';
|
|
143
145
|
};
|
|
146
|
+
/** E2E 测试结果 */
|
|
147
|
+
e2e: {
|
|
148
|
+
passed: number;
|
|
149
|
+
failed: number;
|
|
150
|
+
skipped: number;
|
|
151
|
+
duration: number;
|
|
152
|
+
status: 'pass' | 'fail' | 'skipped';
|
|
153
|
+
};
|
|
144
154
|
/** 验收标准检查 */
|
|
145
155
|
acceptance: {
|
|
146
156
|
total: number;
|
package/dist/types/index.js
CHANGED
|
@@ -11,6 +11,7 @@ exports.QUALITY_PRESETS = {
|
|
|
11
11
|
minCoverage: 0,
|
|
12
12
|
strictLint: false,
|
|
13
13
|
securityScan: false,
|
|
14
|
+
e2eTests: false,
|
|
14
15
|
level: 'fast'
|
|
15
16
|
},
|
|
16
17
|
balanced: {
|
|
@@ -18,6 +19,7 @@ exports.QUALITY_PRESETS = {
|
|
|
18
19
|
minCoverage: 60,
|
|
19
20
|
strictLint: true,
|
|
20
21
|
securityScan: true,
|
|
22
|
+
e2eTests: false,
|
|
21
23
|
level: 'balanced'
|
|
22
24
|
},
|
|
23
25
|
strict: {
|
|
@@ -25,6 +27,7 @@ exports.QUALITY_PRESETS = {
|
|
|
25
27
|
minCoverage: 80,
|
|
26
28
|
strictLint: true,
|
|
27
29
|
securityScan: true,
|
|
30
|
+
e2eTests: false, // 让用户选择,因为 E2E 测试耗时
|
|
28
31
|
level: 'strict'
|
|
29
32
|
}
|
|
30
33
|
};
|
package/package.json
CHANGED
package/skills/openmatrix.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openmatrix
|
|
3
3
|
description: "Invoke /om:start for any implementation task - adding features, fixing bugs, refactoring, creating commands, writing code, building systems. Use this when user describes work to be done, not for questions or information requests."
|
|
4
|
+
priority: high
|
|
5
|
+
always_load: true
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
<objective>
|
package/skills/start.md
CHANGED
|
@@ -19,6 +19,7 @@ description: 启动新的任务执行周期
|
|
|
19
19
|
openmatrix start --init-only
|
|
20
20
|
```
|
|
21
21
|
- 这会创建 `.openmatrix/`、`.openmatrix/tasks/`、`.openmatrix/approvals/` 目录
|
|
22
|
+
- 同时自动将 `.openmatrix/` 添加到 `.gitignore`
|
|
22
23
|
|
|
23
24
|
2. **检查当前状态**
|
|
24
25
|
- 读取 `.openmatrix/state.json`
|
|
@@ -40,7 +41,7 @@ description: 启动新的任务执行周期
|
|
|
40
41
|
|
|
41
42
|
5. **⚠️ 交互式问答 (必须执行)**
|
|
42
43
|
|
|
43
|
-
**重要**: 除非用户明确指定 `--skip-questions
|
|
44
|
+
**重要**: 除非用户明确指定 `--skip-questions` 或提供了 `--quality` 等选项,否则必须执行交互式问答。
|
|
44
45
|
|
|
45
46
|
使用 `AskUserQuestion` 工具,逐个提出以下问题:
|
|
46
47
|
|
|
@@ -53,11 +54,11 @@ description: 启动新的任务执行周期
|
|
|
53
54
|
options: [
|
|
54
55
|
{
|
|
55
56
|
label: "🚀 strict (推荐生产代码)",
|
|
56
|
-
description: "TDD + 80%覆盖率 + 严格Lint + 安全扫描 + AI验收"
|
|
57
|
+
description: "TDD + 80%覆盖率 + 严格Lint + 安全扫描 + AI验收 (E2E可选)"
|
|
57
58
|
},
|
|
58
59
|
{
|
|
59
60
|
label: "⚖️ balanced (日常开发)",
|
|
60
|
-
description: "60%覆盖率 + Lint + 安全扫描 + AI验收"
|
|
61
|
+
description: "60%覆盖率 + Lint + 安全扫描 + AI验收 (E2E可选)"
|
|
61
62
|
},
|
|
62
63
|
{
|
|
63
64
|
label: "⚡ fast (快速原型)",
|
|
@@ -69,6 +70,52 @@ description: 启动新的任务执行周期
|
|
|
69
70
|
})
|
|
70
71
|
```
|
|
71
72
|
|
|
73
|
+
**⚠️ 如果用户选择 "strict" 或 "balanced",追问 E2E 测试:**
|
|
74
|
+
```typescript
|
|
75
|
+
AskUserQuestion({
|
|
76
|
+
questions: [{
|
|
77
|
+
question: "是否需要端到端(E2E)测试?(适用于 Web、移动端、GUI 桌面应用等需要完整用户流程测试的场景,较耗时)",
|
|
78
|
+
header: "E2E测试",
|
|
79
|
+
options: [
|
|
80
|
+
{
|
|
81
|
+
label: "✅ 启用 E2E 测试",
|
|
82
|
+
description: "添加端到端测试,验证完整用户流程(Playwright/Cypress/Appium等)"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
label: "❌ 不需要",
|
|
86
|
+
description: "仅运行单元测试和集成测试"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
multiSelect: false
|
|
90
|
+
}]
|
|
91
|
+
})
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**⚠️ 如果用户启用 E2E 测试,追问应用类型:**
|
|
95
|
+
```typescript
|
|
96
|
+
AskUserQuestion({
|
|
97
|
+
questions: [{
|
|
98
|
+
question: "选择应用类型以确定 E2E 测试方案:",
|
|
99
|
+
header: "应用类型",
|
|
100
|
+
options: [
|
|
101
|
+
{
|
|
102
|
+
label: "🌐 Web 应用",
|
|
103
|
+
description: "浏览器应用 - Playwright/Cypress/Selenium"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
label: "📱 移动应用",
|
|
107
|
+
description: "iOS/Android - Appium/Detox/XCUITest/Espresso"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
label: "🖥️ GUI 桌面应用",
|
|
111
|
+
description: "Electron/原生桌面 - Playwright/Spectron/Robot Framework"
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
multiSelect: false
|
|
115
|
+
}]
|
|
116
|
+
})
|
|
117
|
+
```
|
|
118
|
+
|
|
72
119
|
**问题 1: 任务目标**
|
|
73
120
|
```typescript
|
|
74
121
|
AskUserQuestion({
|
|
@@ -389,36 +436,38 @@ $ARGUMENTS
|
|
|
389
436
|
<notes>
|
|
390
437
|
## 质量级别详解
|
|
391
438
|
|
|
392
|
-
| 级别 | TDD | 覆盖率 | Lint | 安全扫描 | AI验收 | 适用场景 |
|
|
393
|
-
|
|
394
|
-
| **strict** | ✅ | >80% | ✅ 严格 | ✅ | ✅ | 生产代码、核心功能 |
|
|
395
|
-
| **balanced** | ❌ | >60% | ✅ | ✅ | ✅ | 日常开发 (默认) |
|
|
396
|
-
| **fast** | ❌ | >20% | ❌ | ❌ | ❌ | 快速原型、POC |
|
|
439
|
+
| 级别 | TDD | 覆盖率 | Lint | 安全扫描 | E2E测试 | AI验收 | 适用场景 |
|
|
440
|
+
|------|:---:|:------:|:----:|:--------:|:-------:|:------:|---------|
|
|
441
|
+
| **strict** | ✅ | >80% | ✅ 严格 | ✅ | ❓ 可选 | ✅ | 生产代码、核心功能 |
|
|
442
|
+
| **balanced** | ❌ | >60% | ✅ | ✅ | ❓ 可选 | ✅ | 日常开发 (默认) |
|
|
443
|
+
| **fast** | ❌ | >20% | ❌ | ❌ | ❌ | ❌ | 快速原型、POC |
|
|
397
444
|
|
|
398
|
-
> strict 可配置为 100%。80% 覆盖核心业务逻辑,100% 成本高收益低。
|
|
445
|
+
> E2E 测试耗时较长,即使在严格模式下也建议根据项目需要选择。strict 可配置为 100%。80% 覆盖核心业务逻辑,100% 成本高收益低。
|
|
399
446
|
|
|
400
447
|
### strict 模式 (推荐生产代码)
|
|
401
448
|
```
|
|
402
449
|
🧪 TDD 阶段: 先写测试 (RED) → 测试必须失败
|
|
403
450
|
✨ 开发阶段: 再写代码 (GREEN) → 测试必须通过
|
|
404
|
-
✅ 验证阶段:
|
|
451
|
+
✅ 验证阶段: 7道质量门禁
|
|
405
452
|
├── Gate 1: 编译检查 (必须通过)
|
|
406
453
|
├── Gate 2: 测试运行 (必须通过)
|
|
407
454
|
├── Gate 3: 覆盖率 >= 80% (必须达标)
|
|
408
455
|
├── Gate 4: Lint 无 error (必须通过)
|
|
409
456
|
├── Gate 5: 安全扫描无高危 (必须通过)
|
|
410
|
-
|
|
457
|
+
├── Gate 6: E2E 测试通过 (必须通过,Web项目)
|
|
458
|
+
└── Gate 7: 验收标准全部满足
|
|
411
459
|
🎉 验收阶段: AI Reviewer 最终确认
|
|
412
460
|
```
|
|
413
461
|
|
|
414
462
|
### balanced 模式 (日常开发)
|
|
415
463
|
```
|
|
416
464
|
✨ 开发阶段: 编写代码
|
|
417
|
-
✅ 验证阶段: 4道质量门禁
|
|
465
|
+
✅ 验证阶段: 4-5道质量门禁
|
|
418
466
|
├── Gate 1: 编译检查
|
|
419
467
|
├── Gate 2: 测试运行
|
|
420
468
|
├── Gate 3: 覆盖率 >= 60%
|
|
421
|
-
|
|
469
|
+
├── Gate 4: E2E 测试 (可选,Web项目)
|
|
470
|
+
└── Gate 5: 验收标准
|
|
422
471
|
🎉 验收阶段: AI Reviewer 确认
|
|
423
472
|
```
|
|
424
473
|
|