openmatrix 0.1.50 → 0.1.52
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/dist/agents/agent-runner.d.ts +1 -26
- package/dist/cli/commands/meeting.js +55 -13
- package/dist/cli/commands/start.js +233 -82
- package/dist/orchestrator/executor.d.ts +2 -1
- package/dist/orchestrator/phase-executor.d.ts +2 -1
- package/dist/orchestrator/task-parser.d.ts +22 -0
- package/dist/orchestrator/task-parser.js +117 -17
- package/dist/orchestrator/task-planner.d.ts +18 -26
- package/dist/orchestrator/task-planner.js +190 -165
- package/dist/storage/state-manager.js +9 -9
- package/package.json +1 -1
- package/skills/approve.md +10 -10
- package/skills/auto.md +137 -187
- package/skills/meeting.md +3 -1
- package/skills/start.md +240 -626
|
@@ -4,4 +4,26 @@ export declare class TaskParser {
|
|
|
4
4
|
private extractTitle;
|
|
5
5
|
private extractDescription;
|
|
6
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;
|
|
7
29
|
}
|
|
@@ -8,12 +8,20 @@ class TaskParser {
|
|
|
8
8
|
const constraints = this.extractSection(content, '约束');
|
|
9
9
|
const deliverables = this.extractSection(content, '交付物');
|
|
10
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} 的实现`);
|
|
11
19
|
return {
|
|
12
20
|
title,
|
|
13
|
-
description,
|
|
14
|
-
goals: [...new Set(
|
|
15
|
-
constraints: [...new Set(constraints)],
|
|
16
|
-
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)],
|
|
17
25
|
rawContent: content
|
|
18
26
|
};
|
|
19
27
|
}
|
|
@@ -22,34 +30,39 @@ class TaskParser {
|
|
|
22
30
|
return match ? match[1].trim() : 'Untitled Task';
|
|
23
31
|
}
|
|
24
32
|
extractDescription(content) {
|
|
25
|
-
// Remove title
|
|
26
|
-
let desc = content
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
.trim();
|
|
31
|
-
// Get first non-empty line
|
|
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
|
|
32
38
|
const lines = desc.split('\n').filter(l => l.trim());
|
|
33
|
-
|
|
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 '';
|
|
34
51
|
}
|
|
35
52
|
extractSection(content, sectionName) {
|
|
36
|
-
// Split content into lines and find the section
|
|
37
53
|
const lines = content.split('\n');
|
|
38
54
|
const items = [];
|
|
39
55
|
let inSection = false;
|
|
40
56
|
for (const line of lines) {
|
|
41
|
-
// Check if we're entering the target section
|
|
42
57
|
if (line.match(new RegExp(`^##\\s+${sectionName}\\s*$`))) {
|
|
43
58
|
inSection = true;
|
|
44
59
|
continue;
|
|
45
60
|
}
|
|
46
|
-
// Check if we've hit another section
|
|
47
61
|
if (line.match(/^##\s+/)) {
|
|
48
62
|
if (inSection)
|
|
49
|
-
break;
|
|
63
|
+
break;
|
|
50
64
|
continue;
|
|
51
65
|
}
|
|
52
|
-
// Extract list items if we're in the target section
|
|
53
66
|
if (inSection) {
|
|
54
67
|
const itemMatch = line.match(/^-\s+(.+)$/);
|
|
55
68
|
if (itemMatch) {
|
|
@@ -59,5 +72,92 @@ class TaskParser {
|
|
|
59
72
|
}
|
|
60
73
|
return items;
|
|
61
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
|
+
}
|
|
62
162
|
}
|
|
63
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;
|
|
@@ -21,6 +21,8 @@ export interface UserAnswers {
|
|
|
21
21
|
e2eTests?: boolean;
|
|
22
22
|
/** E2E 测试类型 (web/mobile/gui) */
|
|
23
23
|
e2eType?: 'web' | 'mobile' | 'gui';
|
|
24
|
+
/** 质量级别 */
|
|
25
|
+
qualityLevel?: 'fast' | 'balanced' | 'strict';
|
|
24
26
|
}
|
|
25
27
|
/**
|
|
26
28
|
* TaskPlanner - 任务拆解器
|
|
@@ -31,9 +33,12 @@ export interface UserAnswers {
|
|
|
31
33
|
* 3. 验收标准注入 (从用户回答中提取)
|
|
32
34
|
* 4. 用户上下文注入 (将用户回答注入任务描述)
|
|
33
35
|
* 5. 依赖关系分析 (自动分析任务间依赖)
|
|
36
|
+
* 6. 并行执行 (独立任务互不依赖)
|
|
37
|
+
* 7. 质量级别感知 (根据配置调整测试覆盖率)
|
|
34
38
|
*/
|
|
35
39
|
export declare class TaskPlanner {
|
|
36
40
|
private userAnswers;
|
|
41
|
+
private taskCounter;
|
|
37
42
|
constructor(userAnswers?: UserAnswers);
|
|
38
43
|
/**
|
|
39
44
|
* 设置用户回答
|
|
@@ -41,45 +46,32 @@ export declare class TaskPlanner {
|
|
|
41
46
|
setUserAnswers(answers: UserAnswers): void;
|
|
42
47
|
/**
|
|
43
48
|
* Break down a parsed task into sub-tasks
|
|
49
|
+
*/
|
|
50
|
+
breakdown(parsedTask: ParsedTask, answers: Record<string, string>, qualityConfig?: QualityConfig, plan?: string): TaskBreakdown[];
|
|
51
|
+
/**
|
|
52
|
+
* 判断是否需要设计阶段
|
|
44
53
|
*
|
|
45
|
-
*
|
|
54
|
+
* 条件: 多个 goal,或 goal 包含复杂关键词
|
|
46
55
|
*/
|
|
47
|
-
|
|
56
|
+
private needsDesignPhase;
|
|
48
57
|
/**
|
|
49
|
-
*
|
|
58
|
+
* 获取测试覆盖率目标
|
|
50
59
|
*/
|
|
51
|
-
private
|
|
60
|
+
private getCoverageTarget;
|
|
52
61
|
/**
|
|
53
|
-
*
|
|
62
|
+
* 提取用户上下文
|
|
54
63
|
*/
|
|
64
|
+
private extractUserContext;
|
|
55
65
|
private parseArrayAnswer;
|
|
56
66
|
/**
|
|
57
|
-
*
|
|
67
|
+
* 构建全局上下文块(注入到每个子任务描述中)
|
|
58
68
|
*/
|
|
69
|
+
private buildGlobalContext;
|
|
59
70
|
private buildTaskDescription;
|
|
60
|
-
/**
|
|
61
|
-
* 构建测试任务描述
|
|
62
|
-
*/
|
|
63
71
|
private buildTestDescription;
|
|
64
|
-
/**
|
|
65
|
-
* 构建 E2E 测试任务描述
|
|
66
|
-
*/
|
|
67
72
|
private buildE2ETestDescription;
|
|
68
|
-
/**
|
|
69
|
-
* 获取 E2E 测试类型配置
|
|
70
|
-
*/
|
|
71
73
|
private getE2ETypeConfig;
|
|
72
|
-
/**
|
|
73
|
-
* 生成用户流程测试用例
|
|
74
|
-
*/
|
|
75
74
|
private generateUserFlows;
|
|
76
|
-
/**
|
|
77
|
-
* 解析覆盖率数值
|
|
78
|
-
*/
|
|
79
|
-
private parseCoverage;
|
|
80
|
-
/**
|
|
81
|
-
* 生成验收标准
|
|
82
|
-
*/
|
|
83
75
|
private generateAcceptanceCriteria;
|
|
84
76
|
private generateTaskId;
|
|
85
77
|
private determinePriority;
|