openmatrix 0.1.49 → 0.1.51
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 +4 -8
- package/dist/agents/agent-runner.d.ts +1 -26
- package/dist/cli/commands/analyze.js +3 -4
- package/dist/cli/commands/brainstorm.js +52 -74
- package/dist/cli/commands/meeting.js +55 -13
- package/dist/cli/commands/start.js +243 -224
- package/dist/cli/index.js +0 -4
- package/dist/orchestrator/executor.d.ts +2 -1
- package/dist/orchestrator/phase-executor.d.ts +2 -1
- package/dist/orchestrator/smart-question-analyzer.d.ts +0 -38
- package/dist/orchestrator/smart-question-analyzer.js +7 -257
- package/dist/orchestrator/task-parser.d.ts +22 -8
- package/dist/orchestrator/task-parser.js +118 -31
- package/dist/orchestrator/task-planner.d.ts +39 -12
- package/dist/orchestrator/task-planner.js +290 -171
- package/dist/storage/state-manager.d.ts +0 -1
- package/dist/storage/state-manager.js +10 -10
- package/package.json +55 -56
- package/skills/approve.md +10 -10
- package/skills/auto.md +117 -21
- package/skills/brainstorm.md +238 -105
- package/skills/check.md +199 -199
- package/skills/meeting.md +3 -1
- package/skills/openmatrix.md +190 -175
- package/skills/start.md +666 -171
|
@@ -20,24 +20,10 @@ export interface ProjectContext {
|
|
|
20
20
|
packageManager: 'npm' | 'yarn' | 'pnpm' | 'pip' | 'go-mod' | 'cargo' | 'unknown';
|
|
21
21
|
dependencies: Record<string, string>;
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
24
|
-
* 任务理解
|
|
25
|
-
*/
|
|
26
|
-
export interface TaskUnderstanding {
|
|
27
|
-
taskType: 'feature' | 'bugfix' | 'refactor' | 'docs' | 'test' | 'config' | 'unknown';
|
|
28
|
-
taskTypeLabel: string;
|
|
29
|
-
scope: string[];
|
|
30
|
-
scopeLabel: string;
|
|
31
|
-
complexity: 'simple' | 'medium' | 'complex';
|
|
32
|
-
complexityLabel: string;
|
|
33
|
-
estimatedCommits: string;
|
|
34
|
-
suggestions: string[];
|
|
35
|
-
}
|
|
36
23
|
/**
|
|
37
24
|
* 分析结果
|
|
38
25
|
*/
|
|
39
26
|
export interface AnalysisResult {
|
|
40
|
-
understanding: TaskUnderstanding;
|
|
41
27
|
inferences: QuestionInference[];
|
|
42
28
|
questionsToAsk: string[];
|
|
43
29
|
skippedQuestions: string[];
|
|
@@ -65,26 +51,6 @@ export declare class SmartQuestionAnalyzer {
|
|
|
65
51
|
* 获取项目上下文
|
|
66
52
|
*/
|
|
67
53
|
getProjectContext(): Promise<ProjectContext>;
|
|
68
|
-
/**
|
|
69
|
-
* 分析任务理解
|
|
70
|
-
*/
|
|
71
|
-
private analyzeTaskUnderstanding;
|
|
72
|
-
/**
|
|
73
|
-
* 检测任务类型
|
|
74
|
-
*/
|
|
75
|
-
private detectTaskType;
|
|
76
|
-
/**
|
|
77
|
-
* 检测影响范围
|
|
78
|
-
*/
|
|
79
|
-
private detectScope;
|
|
80
|
-
/**
|
|
81
|
-
* 检测复杂度
|
|
82
|
-
*/
|
|
83
|
-
private detectComplexity;
|
|
84
|
-
/**
|
|
85
|
-
* 生成建议
|
|
86
|
-
*/
|
|
87
|
-
private generateSuggestions;
|
|
88
54
|
/**
|
|
89
55
|
* 推断问题答案
|
|
90
56
|
*/
|
|
@@ -113,8 +79,4 @@ export declare class SmartQuestionAnalyzer {
|
|
|
113
79
|
* 生成推断摘要
|
|
114
80
|
*/
|
|
115
81
|
generateSummary(result: AnalysisResult): string;
|
|
116
|
-
/**
|
|
117
|
-
* 生成 Markdown 格式输出
|
|
118
|
-
*/
|
|
119
|
-
generateMarkdown(result: AnalysisResult): string;
|
|
120
82
|
}
|
|
@@ -58,11 +58,9 @@ class SmartQuestionAnalyzer {
|
|
|
58
58
|
async analyze(taskDescription, parsedTask) {
|
|
59
59
|
// 1. 获取项目上下文
|
|
60
60
|
const projectContext = await this.getProjectContext();
|
|
61
|
-
// 2.
|
|
62
|
-
const understanding = this.analyzeTaskUnderstanding(taskDescription, projectContext);
|
|
63
|
-
// 3. 执行推断
|
|
61
|
+
// 2. 执行推断
|
|
64
62
|
const inferences = this.inferAnswers(taskDescription, projectContext);
|
|
65
|
-
//
|
|
63
|
+
// 3. 筛选需要提问的问题
|
|
66
64
|
const questionsToAsk = inferences
|
|
67
65
|
.filter(i => i.confidence === 'low' || !i.inferredAnswer)
|
|
68
66
|
.map(i => i.questionId);
|
|
@@ -70,7 +68,6 @@ class SmartQuestionAnalyzer {
|
|
|
70
68
|
.filter(i => i.confidence !== 'low' && i.inferredAnswer)
|
|
71
69
|
.map(i => i.questionId);
|
|
72
70
|
return {
|
|
73
|
-
understanding,
|
|
74
71
|
inferences,
|
|
75
72
|
questionsToAsk,
|
|
76
73
|
skippedQuestions,
|
|
@@ -163,199 +160,6 @@ class SmartQuestionAnalyzer {
|
|
|
163
160
|
this.cachedContext = context;
|
|
164
161
|
return context;
|
|
165
162
|
}
|
|
166
|
-
/**
|
|
167
|
-
* 分析任务理解
|
|
168
|
-
*/
|
|
169
|
-
analyzeTaskUnderstanding(taskDescription, context) {
|
|
170
|
-
const desc = taskDescription.toLowerCase();
|
|
171
|
-
// 1. 判断任务类型
|
|
172
|
-
const taskType = this.detectTaskType(desc);
|
|
173
|
-
// 2. 判断影响范围
|
|
174
|
-
const scope = this.detectScope(desc, context);
|
|
175
|
-
// 3. 判断复杂度
|
|
176
|
-
const complexity = this.detectComplexity(desc, taskType, scope);
|
|
177
|
-
// 4. 生成建议
|
|
178
|
-
const suggestions = this.generateSuggestions(taskType, complexity, desc);
|
|
179
|
-
return {
|
|
180
|
-
taskType: taskType.type,
|
|
181
|
-
taskTypeLabel: taskType.label,
|
|
182
|
-
scope: scope.areas,
|
|
183
|
-
scopeLabel: scope.label,
|
|
184
|
-
complexity: complexity.level,
|
|
185
|
-
complexityLabel: complexity.label,
|
|
186
|
-
estimatedCommits: complexity.estimatedCommits,
|
|
187
|
-
suggestions
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* 检测任务类型
|
|
192
|
-
*/
|
|
193
|
-
detectTaskType(desc) {
|
|
194
|
-
// Bug 修复
|
|
195
|
-
if (/(fix|bug|修复|hotfix|patch|问题|报错|错误|异常)/i.test(desc)) {
|
|
196
|
-
return { type: 'bugfix', label: 'Bug 修复' };
|
|
197
|
-
}
|
|
198
|
-
// 重构
|
|
199
|
-
if (/(refactor|重构|优化|improve|改进)/i.test(desc)) {
|
|
200
|
-
return { type: 'refactor', label: '重构优化' };
|
|
201
|
-
}
|
|
202
|
-
// 文档
|
|
203
|
-
if (/(doc|文档|readme|文档|注释)/i.test(desc)) {
|
|
204
|
-
return { type: 'docs', label: '文档编写' };
|
|
205
|
-
}
|
|
206
|
-
// 测试
|
|
207
|
-
if (/(test|测试|单元|unit|coverage|覆盖率)/i.test(desc)) {
|
|
208
|
-
return { type: 'test', label: '测试编写' };
|
|
209
|
-
}
|
|
210
|
-
// 配置
|
|
211
|
-
if (/(config|配置|设置|setup|初始化)/i.test(desc)) {
|
|
212
|
-
return { type: 'config', label: '配置修改' };
|
|
213
|
-
}
|
|
214
|
-
// 新功能 (默认)
|
|
215
|
-
if (/(implement|add|新功能|实现|开发|feature|添加|创建)/i.test(desc)) {
|
|
216
|
-
return { type: 'feature', label: '新功能开发' };
|
|
217
|
-
}
|
|
218
|
-
return { type: 'unknown', label: '待确认' };
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* 检测影响范围
|
|
222
|
-
*/
|
|
223
|
-
detectScope(desc, context) {
|
|
224
|
-
const areas = [];
|
|
225
|
-
// 从描述中检测
|
|
226
|
-
if (/(前端|页面|ui|样式|css|html|react|vue|component)/i.test(desc)) {
|
|
227
|
-
areas.push('前端');
|
|
228
|
-
}
|
|
229
|
-
if (/(后端|api|服务|server|接口|数据库|database)/i.test(desc)) {
|
|
230
|
-
areas.push('后端');
|
|
231
|
-
}
|
|
232
|
-
if (/(cli|命令行|终端|terminal)/i.test(desc)) {
|
|
233
|
-
areas.push('CLI');
|
|
234
|
-
}
|
|
235
|
-
if (/(测试|test|spec)/i.test(desc)) {
|
|
236
|
-
areas.push('测试');
|
|
237
|
-
}
|
|
238
|
-
if (/(文档|doc|readme)/i.test(desc)) {
|
|
239
|
-
areas.push('文档');
|
|
240
|
-
}
|
|
241
|
-
if (/(配置|config)/i.test(desc)) {
|
|
242
|
-
areas.push('配置');
|
|
243
|
-
}
|
|
244
|
-
// 从项目上下文补充
|
|
245
|
-
if (areas.length === 0) {
|
|
246
|
-
if (context.hasFrontend)
|
|
247
|
-
areas.push('前端');
|
|
248
|
-
if (context.hasBackend)
|
|
249
|
-
areas.push('后端');
|
|
250
|
-
}
|
|
251
|
-
// 默认
|
|
252
|
-
if (areas.length === 0) {
|
|
253
|
-
areas.push('代码');
|
|
254
|
-
}
|
|
255
|
-
const label = areas.join('、');
|
|
256
|
-
return { areas, label };
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* 检测复杂度
|
|
260
|
-
*/
|
|
261
|
-
detectComplexity(desc, taskType, scope) {
|
|
262
|
-
// 简单任务关键词
|
|
263
|
-
const simpleKeywords = /(fix|bug|修复|改|调整|样式|变量名|文案|按钮|颜色|字体)/i;
|
|
264
|
-
// 复杂任务关键词 - 需要更精确
|
|
265
|
-
const complexKeywords = /(系统|架构|集成|完整|从零|优化整体|多模块|全栈)/i;
|
|
266
|
-
const multiScope = scope.areas.length >= 3;
|
|
267
|
-
// Bug 修复
|
|
268
|
-
if (taskType.type === 'bugfix') {
|
|
269
|
-
if (simpleKeywords.test(desc) || desc.length < 50) {
|
|
270
|
-
return { level: 'simple', label: '简单', estimatedCommits: '预计 1 个 commit' };
|
|
271
|
-
}
|
|
272
|
-
return { level: 'medium', label: '中等', estimatedCommits: '预计 1-2 个 commit' };
|
|
273
|
-
}
|
|
274
|
-
// 文档、配置
|
|
275
|
-
if (taskType.type === 'docs' || taskType.type === 'config') {
|
|
276
|
-
return { level: 'simple', label: '简单', estimatedCommits: '预计 1 个 commit' };
|
|
277
|
-
}
|
|
278
|
-
// 测试
|
|
279
|
-
if (taskType.type === 'test') {
|
|
280
|
-
if (multiScope) {
|
|
281
|
-
return { level: 'medium', label: '中等', estimatedCommits: '预计 2-3 个 commit' };
|
|
282
|
-
}
|
|
283
|
-
return { level: 'simple', label: '简单', estimatedCommits: '预计 1 个 commit' };
|
|
284
|
-
}
|
|
285
|
-
// 复杂关键词检测
|
|
286
|
-
if (complexKeywords.test(desc) || multiScope) {
|
|
287
|
-
return { level: 'complex', label: '复杂', estimatedCommits: '预计 3+ 个 commit' };
|
|
288
|
-
}
|
|
289
|
-
// 新功能 - 默认 medium
|
|
290
|
-
if (taskType.type === 'feature') {
|
|
291
|
-
// 只有非常简单的单文件修改才判断为 simple
|
|
292
|
-
if (desc.length < 20 && scope.areas.length === 1 && /(按钮|链接|文案|颜色)/.test(desc)) {
|
|
293
|
-
return { level: 'simple', label: '简单', estimatedCommits: '预计 1 个 commit' };
|
|
294
|
-
}
|
|
295
|
-
if (desc.length > 100 || scope.areas.length >= 2) {
|
|
296
|
-
return { level: 'medium', label: '中等', estimatedCommits: '预计 2-3 个 commit' };
|
|
297
|
-
}
|
|
298
|
-
return { level: 'medium', label: '中等', estimatedCommits: '预计 2-3 个 commit' };
|
|
299
|
-
}
|
|
300
|
-
// 重构 - 默认 medium
|
|
301
|
-
if (taskType.type === 'refactor') {
|
|
302
|
-
if (multiScope || /(整体|全部|系统)/.test(desc)) {
|
|
303
|
-
return { level: 'complex', label: '复杂', estimatedCommits: '预计 3+ 个 commit' };
|
|
304
|
-
}
|
|
305
|
-
return { level: 'medium', label: '中等', estimatedCommits: '预计 2-3 个 commit' };
|
|
306
|
-
}
|
|
307
|
-
// 默认中等
|
|
308
|
-
return { level: 'medium', label: '中等', estimatedCommits: '预计 1-2 个 commit' };
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* 生成建议
|
|
312
|
-
*/
|
|
313
|
-
generateSuggestions(taskType, complexity, desc) {
|
|
314
|
-
const suggestions = [];
|
|
315
|
-
// 复杂任务优先建议头脑风暴
|
|
316
|
-
if (complexity.level === 'complex') {
|
|
317
|
-
suggestions.push('建议先头脑风暴,明确需求');
|
|
318
|
-
}
|
|
319
|
-
// 根据任务类型建议
|
|
320
|
-
switch (taskType.type) {
|
|
321
|
-
case 'bugfix':
|
|
322
|
-
suggestions.push('直接修复,无需 TDD');
|
|
323
|
-
suggestions.push('使用 fast 模式');
|
|
324
|
-
break;
|
|
325
|
-
case 'feature':
|
|
326
|
-
if (complexity.level === 'complex') {
|
|
327
|
-
suggestions.push('使用 strict 模式保证质量');
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
suggestions.push('使用 balanced 模式');
|
|
331
|
-
}
|
|
332
|
-
break;
|
|
333
|
-
case 'refactor':
|
|
334
|
-
suggestions.push('确保测试覆盖');
|
|
335
|
-
suggestions.push('使用 balanced 模式');
|
|
336
|
-
break;
|
|
337
|
-
case 'test':
|
|
338
|
-
suggestions.push('使用 fast 或 balanced 模式');
|
|
339
|
-
break;
|
|
340
|
-
case 'docs':
|
|
341
|
-
suggestions.push('无需测试');
|
|
342
|
-
suggestions.push('使用 fast 模式');
|
|
343
|
-
break;
|
|
344
|
-
case 'config':
|
|
345
|
-
suggestions.push('注意配置安全');
|
|
346
|
-
suggestions.push('使用 fast 模式');
|
|
347
|
-
break;
|
|
348
|
-
default:
|
|
349
|
-
if (complexity.level !== 'complex') {
|
|
350
|
-
suggestions.push('根据实际情况选择模式');
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
// 确保至少有一个建议
|
|
354
|
-
if (suggestions.length === 0) {
|
|
355
|
-
suggestions.push('建议先确认任务需求');
|
|
356
|
-
}
|
|
357
|
-
return suggestions;
|
|
358
|
-
}
|
|
359
163
|
/**
|
|
360
164
|
* 推断问题答案
|
|
361
165
|
*/
|
|
@@ -584,21 +388,7 @@ class SmartQuestionAnalyzer {
|
|
|
584
388
|
* 生成推断摘要
|
|
585
389
|
*/
|
|
586
390
|
generateSummary(result) {
|
|
587
|
-
const lines = [];
|
|
588
|
-
// 任务理解
|
|
589
|
-
lines.push('🔍 我理解的任务:\n');
|
|
590
|
-
lines.push(`📋 类型: ${result.understanding.taskTypeLabel}`);
|
|
591
|
-
lines.push(`📁 范围: ${result.understanding.scopeLabel}`);
|
|
592
|
-
lines.push(`⚡ 复杂度: ${result.understanding.complexityLabel} (${result.understanding.estimatedCommits})`);
|
|
593
|
-
// 建议
|
|
594
|
-
if (result.understanding.suggestions.length > 0) {
|
|
595
|
-
lines.push('\n💡 建议执行方式:');
|
|
596
|
-
for (const suggestion of result.understanding.suggestions) {
|
|
597
|
-
lines.push(` • ${suggestion}`);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
// 推断结果
|
|
601
|
-
lines.push('\n📊 配置推断:');
|
|
391
|
+
const lines = ['📊 AI 推断结果:\n'];
|
|
602
392
|
for (const inference of result.inferences) {
|
|
603
393
|
const icon = inference.confidence === 'high' ? '✅' :
|
|
604
394
|
inference.confidence === 'medium' ? '🤔' : '❓';
|
|
@@ -606,55 +396,15 @@ class SmartQuestionAnalyzer {
|
|
|
606
396
|
? inference.inferredAnswer.join(', ')
|
|
607
397
|
: inference.inferredAnswer || '待确认';
|
|
608
398
|
lines.push(`${icon} ${inference.questionId}: ${answer}`);
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
lines.push(`\n❓ 需要确认: ${result.questionsToAsk.join(', ')}`);
|
|
612
|
-
}
|
|
613
|
-
else {
|
|
614
|
-
lines.push('\n✅ 配置已推断,可直接执行');
|
|
615
|
-
}
|
|
616
|
-
return lines.join('\n');
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* 生成 Markdown 格式输出
|
|
620
|
-
*/
|
|
621
|
-
generateMarkdown(result) {
|
|
622
|
-
const lines = [];
|
|
623
|
-
lines.push('## 🔍 我理解的任务\n');
|
|
624
|
-
lines.push(`| 属性 | 值 |`);
|
|
625
|
-
lines.push(`|------|-----|`);
|
|
626
|
-
lines.push(`| 📋 类型 | ${result.understanding.taskTypeLabel} |`);
|
|
627
|
-
lines.push(`| 📁 范围 | ${result.understanding.scopeLabel} |`);
|
|
628
|
-
lines.push(`| ⚡ 复杂度 | ${result.understanding.complexityLabel} |`);
|
|
629
|
-
lines.push(`| ⏱️ 预计 | ${result.understanding.estimatedCommits} |`);
|
|
630
|
-
// 建议
|
|
631
|
-
if (result.understanding.suggestions.length > 0) {
|
|
632
|
-
lines.push('\n## 💡 建议执行方式\n');
|
|
633
|
-
for (const suggestion of result.understanding.suggestions) {
|
|
634
|
-
lines.push(`- ${suggestion}`);
|
|
399
|
+
if (inference.reason) {
|
|
400
|
+
lines.push(` └─ ${inference.reason}`);
|
|
635
401
|
}
|
|
636
402
|
}
|
|
637
|
-
// 推断的配置
|
|
638
|
-
lines.push('\n## 📊 推荐配置\n');
|
|
639
|
-
lines.push(`| 配置项 | 推荐值 | 置信度 |`);
|
|
640
|
-
lines.push(`|--------|--------|--------|`);
|
|
641
|
-
for (const inference of result.inferences) {
|
|
642
|
-
const answer = Array.isArray(inference.inferredAnswer)
|
|
643
|
-
? inference.inferredAnswer.join(', ')
|
|
644
|
-
: inference.inferredAnswer || '待确认';
|
|
645
|
-
const confidenceIcon = inference.confidence === 'high' ? '✅' :
|
|
646
|
-
inference.confidence === 'medium' ? '🤔' : '❓';
|
|
647
|
-
lines.push(`| ${inference.questionId} | ${answer} | ${confidenceIcon} |`);
|
|
648
|
-
}
|
|
649
|
-
// 需要确认的问题
|
|
650
403
|
if (result.questionsToAsk.length > 0) {
|
|
651
|
-
lines.push(
|
|
652
|
-
for (const q of result.questionsToAsk) {
|
|
653
|
-
lines.push(`- ${q}`);
|
|
654
|
-
}
|
|
404
|
+
lines.push(`\n❓ 需要确认的问题: ${result.questionsToAsk.join(', ')}`);
|
|
655
405
|
}
|
|
656
406
|
else {
|
|
657
|
-
lines.push('\n✅
|
|
407
|
+
lines.push('\n✅ 所有问题已推断,无需额外确认');
|
|
658
408
|
}
|
|
659
409
|
return lines.join('\n');
|
|
660
410
|
}
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import type { ParsedTask } from '../types/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* TaskParser - 任务解析器
|
|
4
|
-
*
|
|
5
|
-
* 职责:解析用户输入的任务描述,提取结构化信息
|
|
6
|
-
*
|
|
7
|
-
* 注意:TaskParser 只负责格式解析,不做业务逻辑判断
|
|
8
|
-
* 任务拆解的智能逻辑应该由 TaskPlanner (调用 AI) 来完成
|
|
9
|
-
*/
|
|
10
2
|
export declare class TaskParser {
|
|
11
3
|
parse(content: string): ParsedTask;
|
|
12
4
|
private extractTitle;
|
|
13
5
|
private extractDescription;
|
|
14
6
|
private extractSection;
|
|
7
|
+
/**
|
|
8
|
+
* 从纯文本中智能提取 goals
|
|
9
|
+
*
|
|
10
|
+
* 策略优先级:
|
|
11
|
+
* 1. 有序列表 (1. xxx, 2. xxx)
|
|
12
|
+
* 2. 无序列表 (- xxx, * xxx)
|
|
13
|
+
* 3. 分号/换行分隔的多行描述
|
|
14
|
+
* 4. 将标题作为唯一 goal
|
|
15
|
+
*/
|
|
16
|
+
private extractGoalsFromPlainText;
|
|
17
|
+
/**
|
|
18
|
+
* 提取有序列表项
|
|
19
|
+
*/
|
|
20
|
+
private extractOrderedItems;
|
|
21
|
+
/**
|
|
22
|
+
* 提取无序列表项
|
|
23
|
+
*/
|
|
24
|
+
private extractUnorderedItems;
|
|
25
|
+
/**
|
|
26
|
+
* 从文本中提取句子作为 goals
|
|
27
|
+
*/
|
|
28
|
+
private extractSentences;
|
|
15
29
|
}
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TaskParser = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* TaskParser - 任务解析器
|
|
6
|
-
*
|
|
7
|
-
* 职责:解析用户输入的任务描述,提取结构化信息
|
|
8
|
-
*
|
|
9
|
-
* 注意:TaskParser 只负责格式解析,不做业务逻辑判断
|
|
10
|
-
* 任务拆解的智能逻辑应该由 TaskPlanner (调用 AI) 来完成
|
|
11
|
-
*/
|
|
12
4
|
class TaskParser {
|
|
13
5
|
parse(content) {
|
|
14
6
|
const title = this.extractTitle(content);
|
|
@@ -16,53 +8,61 @@ class TaskParser {
|
|
|
16
8
|
const constraints = this.extractSection(content, '约束');
|
|
17
9
|
const deliverables = this.extractSection(content, '交付物');
|
|
18
10
|
const description = this.extractDescription(content);
|
|
11
|
+
// Fallback: 当 markdown section 未提取到 goals 时,智能提取
|
|
12
|
+
const finalGoals = goals.length > 0
|
|
13
|
+
? goals
|
|
14
|
+
: this.extractGoalsFromPlainText(content, title);
|
|
15
|
+
// Fallback: 当无 deliverables 时,从 goals 推断
|
|
16
|
+
const finalDeliverables = deliverables.length > 0
|
|
17
|
+
? deliverables
|
|
18
|
+
: finalGoals.map(g => `${g} 的实现`);
|
|
19
19
|
return {
|
|
20
20
|
title,
|
|
21
|
-
description,
|
|
22
|
-
goals: [...new Set(
|
|
23
|
-
constraints: [...new Set(constraints)],
|
|
24
|
-
deliverables: [...new Set(
|
|
21
|
+
description: description || (finalGoals.length > 0 ? finalGoals[0] : title),
|
|
22
|
+
goals: [...new Set(finalGoals)],
|
|
23
|
+
constraints: [...new Set(constraints)],
|
|
24
|
+
deliverables: [...new Set(finalDeliverables)],
|
|
25
25
|
rawContent: content
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
extractTitle(content) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (headerMatch) {
|
|
32
|
-
return headerMatch[1].trim();
|
|
33
|
-
}
|
|
34
|
-
// 2. 没有标题,取第一行非空内容(截断到 50 字符)
|
|
35
|
-
const firstLine = content.split('\n').find(l => l.trim());
|
|
36
|
-
return firstLine ? firstLine.trim().slice(0, 50) : 'Untitled Task';
|
|
29
|
+
const match = content.match(/^#\s+(.+)$/m);
|
|
30
|
+
return match ? match[1].trim() : 'Untitled Task';
|
|
37
31
|
}
|
|
38
32
|
extractDescription(content) {
|
|
39
|
-
//
|
|
40
|
-
let desc = content
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
.trim();
|
|
45
|
-
// 获取第一个非空行
|
|
33
|
+
// Remove title line
|
|
34
|
+
let desc = content.replace(/^#\s+.+$/m, '').trim();
|
|
35
|
+
// Remove section headers
|
|
36
|
+
desc = desc.replace(/^##\s+.+$/gm, '').trim();
|
|
37
|
+
// Get first non-empty, non-list line as description
|
|
46
38
|
const lines = desc.split('\n').filter(l => l.trim());
|
|
47
|
-
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
const trimmed = line.trim();
|
|
41
|
+
// Skip list items, section headers
|
|
42
|
+
if (/^[-*]\s+/.test(trimmed))
|
|
43
|
+
continue;
|
|
44
|
+
if (/^\d+\.\s+/.test(trimmed))
|
|
45
|
+
continue;
|
|
46
|
+
if (trimmed.length > 0) {
|
|
47
|
+
return trimmed;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return '';
|
|
48
51
|
}
|
|
49
52
|
extractSection(content, sectionName) {
|
|
50
53
|
const lines = content.split('\n');
|
|
51
54
|
const items = [];
|
|
52
55
|
let inSection = false;
|
|
53
56
|
for (const line of lines) {
|
|
54
|
-
// 检查是否进入目标 section
|
|
55
57
|
if (line.match(new RegExp(`^##\\s+${sectionName}\\s*$`))) {
|
|
56
58
|
inSection = true;
|
|
57
59
|
continue;
|
|
58
60
|
}
|
|
59
|
-
// 检查是否进入其他 section
|
|
60
61
|
if (line.match(/^##\s+/)) {
|
|
61
62
|
if (inSection)
|
|
62
63
|
break;
|
|
63
64
|
continue;
|
|
64
65
|
}
|
|
65
|
-
// 提取列表项
|
|
66
66
|
if (inSection) {
|
|
67
67
|
const itemMatch = line.match(/^-\s+(.+)$/);
|
|
68
68
|
if (itemMatch) {
|
|
@@ -72,5 +72,92 @@ class TaskParser {
|
|
|
72
72
|
}
|
|
73
73
|
return items;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* 从纯文本中智能提取 goals
|
|
77
|
+
*
|
|
78
|
+
* 策略优先级:
|
|
79
|
+
* 1. 有序列表 (1. xxx, 2. xxx)
|
|
80
|
+
* 2. 无序列表 (- xxx, * xxx)
|
|
81
|
+
* 3. 分号/换行分隔的多行描述
|
|
82
|
+
* 4. 将标题作为唯一 goal
|
|
83
|
+
*/
|
|
84
|
+
extractGoalsFromPlainText(content, title) {
|
|
85
|
+
const goals = [];
|
|
86
|
+
const cleanContent = content
|
|
87
|
+
.replace(/^#\s+.+$/m, '') // 去标题
|
|
88
|
+
.replace(/^##\s+.+$/gm, '') // 去section标题
|
|
89
|
+
.trim();
|
|
90
|
+
// 策略1: 有序列表 (1. xxx, 2. xxx, ...)
|
|
91
|
+
const orderedItems = this.extractOrderedItems(cleanContent);
|
|
92
|
+
if (orderedItems.length > 0) {
|
|
93
|
+
return orderedItems;
|
|
94
|
+
}
|
|
95
|
+
// 策略2: 无序列表 (- xxx, * xxx)
|
|
96
|
+
const unorderedItems = this.extractUnorderedItems(cleanContent);
|
|
97
|
+
if (unorderedItems.length > 0) {
|
|
98
|
+
return unorderedItems;
|
|
99
|
+
}
|
|
100
|
+
// 策略3: 多行文本,按句号/分号拆分为多个 goal
|
|
101
|
+
const sentences = this.extractSentences(cleanContent);
|
|
102
|
+
if (sentences.length > 1) {
|
|
103
|
+
return sentences;
|
|
104
|
+
}
|
|
105
|
+
// 策略4: 单个句子作为唯一 goal
|
|
106
|
+
if (sentences.length === 1 && sentences[0].length > 0) {
|
|
107
|
+
return sentences;
|
|
108
|
+
}
|
|
109
|
+
// 策略5: 将标题作为唯一 goal
|
|
110
|
+
if (title && title !== 'Untitled Task') {
|
|
111
|
+
return [title];
|
|
112
|
+
}
|
|
113
|
+
return goals;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 提取有序列表项
|
|
117
|
+
*/
|
|
118
|
+
extractOrderedItems(text) {
|
|
119
|
+
const items = [];
|
|
120
|
+
const lines = text.split('\n');
|
|
121
|
+
for (const line of lines) {
|
|
122
|
+
const match = line.trim().match(/^\d+[.、)\s]+(.+)$/);
|
|
123
|
+
if (match) {
|
|
124
|
+
const item = match[1].trim();
|
|
125
|
+
if (item.length > 0) {
|
|
126
|
+
items.push(item);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return items;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 提取无序列表项
|
|
134
|
+
*/
|
|
135
|
+
extractUnorderedItems(text) {
|
|
136
|
+
const items = [];
|
|
137
|
+
const lines = text.split('\n');
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
const match = line.trim().match(/^[-*]\s+(.+)$/);
|
|
140
|
+
if (match) {
|
|
141
|
+
const item = match[1].trim();
|
|
142
|
+
if (item.length > 0) {
|
|
143
|
+
items.push(item);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return items;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* 从文本中提取句子作为 goals
|
|
151
|
+
*/
|
|
152
|
+
extractSentences(text) {
|
|
153
|
+
if (!text || text.trim().length === 0)
|
|
154
|
+
return [];
|
|
155
|
+
// 按中文句号、英文句号、分号拆分
|
|
156
|
+
const sentences = text
|
|
157
|
+
.split(/[。;\n]/)
|
|
158
|
+
.map(s => s.replace(/[.;]/g, '').trim())
|
|
159
|
+
.filter(s => s.length > 2); // 过滤太短的片段
|
|
160
|
+
return sentences;
|
|
161
|
+
}
|
|
75
162
|
}
|
|
76
163
|
exports.TaskParser = TaskParser;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ParsedTask } from '../types/index.js';
|
|
1
|
+
import type { ParsedTask, QualityConfig } from '../types/index.js';
|
|
2
2
|
export interface TaskBreakdown {
|
|
3
3
|
taskId: string;
|
|
4
4
|
title: string;
|
|
@@ -17,34 +17,61 @@ 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';
|
|
24
|
+
/** 质量级别 */
|
|
25
|
+
qualityLevel?: 'fast' | 'balanced' | 'strict';
|
|
20
26
|
}
|
|
21
27
|
/**
|
|
22
28
|
* TaskPlanner - 任务拆解器
|
|
23
29
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
30
|
+
* 增强版特性:
|
|
31
|
+
* 1. 更细粒度的任务拆分 (每个目标拆分为设计+实现+测试)
|
|
32
|
+
* 2. 测试任务配对 (每个开发任务自动生成对应测试任务)
|
|
33
|
+
* 3. 验收标准注入 (从用户回答中提取)
|
|
34
|
+
* 4. 用户上下文注入 (将用户回答注入任务描述)
|
|
35
|
+
* 5. 依赖关系分析 (自动分析任务间依赖)
|
|
36
|
+
* 6. 并行执行 (独立任务互不依赖)
|
|
37
|
+
* 7. 质量级别感知 (根据配置调整测试覆盖率)
|
|
27
38
|
*/
|
|
28
39
|
export declare class TaskPlanner {
|
|
40
|
+
private userAnswers;
|
|
41
|
+
private taskCounter;
|
|
42
|
+
constructor(userAnswers?: UserAnswers);
|
|
43
|
+
/**
|
|
44
|
+
* 设置用户回答
|
|
45
|
+
*/
|
|
46
|
+
setUserAnswers(answers: UserAnswers): void;
|
|
29
47
|
/**
|
|
30
48
|
* Break down a parsed task into sub-tasks
|
|
31
49
|
*/
|
|
32
|
-
breakdown(parsedTask: ParsedTask, answers: Record<string, string
|
|
50
|
+
breakdown(parsedTask: ParsedTask, answers: Record<string, string>, qualityConfig?: QualityConfig, plan?: string): TaskBreakdown[];
|
|
51
|
+
/**
|
|
52
|
+
* 判断是否需要设计阶段
|
|
53
|
+
*
|
|
54
|
+
* 条件: 多个 goal,或 goal 包含复杂关键词
|
|
55
|
+
*/
|
|
56
|
+
private needsDesignPhase;
|
|
33
57
|
/**
|
|
34
|
-
*
|
|
58
|
+
* 获取测试覆盖率目标
|
|
35
59
|
*/
|
|
36
|
-
private
|
|
60
|
+
private getCoverageTarget;
|
|
37
61
|
/**
|
|
38
|
-
*
|
|
39
|
-
* 把 rawContent 整体交给 planner 分析,生成 PLAN.md
|
|
40
|
-
* coder 基于 PLAN.md 实现
|
|
62
|
+
* 提取用户上下文
|
|
41
63
|
*/
|
|
42
|
-
private breakdownFallback;
|
|
43
64
|
private extractUserContext;
|
|
44
65
|
private parseArrayAnswer;
|
|
66
|
+
/**
|
|
67
|
+
* 构建全局上下文块(注入到每个子任务描述中)
|
|
68
|
+
*/
|
|
69
|
+
private buildGlobalContext;
|
|
45
70
|
private buildTaskDescription;
|
|
46
71
|
private buildTestDescription;
|
|
47
|
-
private
|
|
72
|
+
private buildE2ETestDescription;
|
|
73
|
+
private getE2ETypeConfig;
|
|
74
|
+
private generateUserFlows;
|
|
48
75
|
private generateAcceptanceCriteria;
|
|
49
76
|
private generateTaskId;
|
|
50
77
|
private determinePriority;
|