openmatrix 0.1.61 → 0.1.63
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/cli/commands/approve.js +26 -15
- package/dist/cli/commands/brainstorm.js +169 -2
- package/dist/orchestrator/answer-mapper.d.ts +24 -0
- package/dist/orchestrator/answer-mapper.js +108 -0
- package/dist/orchestrator/executor.d.ts +19 -0
- package/dist/orchestrator/executor.js +76 -1
- package/dist/orchestrator/interactive-question-generator.js +77 -51
- package/dist/orchestrator/scheduler.d.ts +15 -1
- package/dist/orchestrator/scheduler.js +90 -17
- package/dist/orchestrator/smart-question-analyzer.d.ts +8 -0
- package/dist/orchestrator/smart-question-analyzer.js +72 -0
- package/dist/orchestrator/state-machine.js +2 -0
- package/dist/orchestrator/task-planner.d.ts +1 -1
- package/dist/orchestrator/task-planner.js +13 -7
- package/dist/storage/state-manager.d.ts +5 -0
- package/dist/storage/state-manager.js +81 -53
- package/package.json +1 -1
- package/scripts/install-skills.js +35 -29
- package/skills/approve.md +11 -14
- package/skills/brainstorm.md +34 -14
|
@@ -193,18 +193,18 @@ class InteractiveQuestionGenerator {
|
|
|
193
193
|
*/
|
|
194
194
|
generateBaseQuestions(task) {
|
|
195
195
|
const questions = [];
|
|
196
|
-
// 1.
|
|
196
|
+
// 1. 任务目标
|
|
197
197
|
questions.push({
|
|
198
198
|
id: 'objective',
|
|
199
|
-
question: '
|
|
199
|
+
question: '这个任务的核心目标是什么?想要解决什么问题?',
|
|
200
200
|
type: 'single',
|
|
201
201
|
required: true,
|
|
202
202
|
category: 'objective',
|
|
203
203
|
priority: 1,
|
|
204
204
|
options: [
|
|
205
|
-
{ key: 'new_feature', label: '实现新功能', description: '
|
|
206
|
-
{ key: 'bug_fix', label: '
|
|
207
|
-
{ key: 'refactor', label: '重构优化', description: '
|
|
205
|
+
{ key: 'new_feature', label: '实现新功能', description: '添加新的功能特性,扩展系统能力' },
|
|
206
|
+
{ key: 'bug_fix', label: '修复问题', description: '修复 Bug 或解决已知问题' },
|
|
207
|
+
{ key: 'refactor', label: '重构优化', description: '改进代码结构、性能或可维护性' },
|
|
208
208
|
{ key: 'other', label: '其他', description: '其他类型任务' }
|
|
209
209
|
],
|
|
210
210
|
followUpCondition: {
|
|
@@ -221,14 +221,28 @@ class InteractiveQuestionGenerator {
|
|
|
221
221
|
]
|
|
222
222
|
}
|
|
223
223
|
});
|
|
224
|
-
// 2.
|
|
224
|
+
// 2. 质量级别
|
|
225
|
+
questions.push({
|
|
226
|
+
id: 'quality_level',
|
|
227
|
+
question: '选择质量门禁级别(决定测试覆盖、Lint、安全扫描等要求)',
|
|
228
|
+
type: 'single',
|
|
229
|
+
required: true,
|
|
230
|
+
category: 'quality',
|
|
231
|
+
priority: 2,
|
|
232
|
+
options: [
|
|
233
|
+
{ key: 'strict', label: 'strict', description: 'TDD + 80% 覆盖率 + 严格 Lint + 安全扫描 — 生产级代码' },
|
|
234
|
+
{ key: 'balanced', label: 'balanced (推荐)', description: '60% 覆盖率 + Lint + 安全扫描 — 日常开发' },
|
|
235
|
+
{ key: 'fast', label: 'fast', description: '无质量门禁 — 快速原型/验证' }
|
|
236
|
+
]
|
|
237
|
+
});
|
|
238
|
+
// 3. 技术栈
|
|
225
239
|
questions.push({
|
|
226
240
|
id: 'tech_stack',
|
|
227
241
|
question: '使用什么技术栈?',
|
|
228
242
|
type: 'multiple',
|
|
229
243
|
required: true,
|
|
230
244
|
category: 'technical',
|
|
231
|
-
priority:
|
|
245
|
+
priority: 3,
|
|
232
246
|
options: [
|
|
233
247
|
{ key: 'typescript', label: 'TypeScript', description: '类型安全的 JavaScript' },
|
|
234
248
|
{ key: 'javascript', label: 'JavaScript', description: '标准 JavaScript' },
|
|
@@ -247,64 +261,33 @@ class InteractiveQuestionGenerator {
|
|
|
247
261
|
type: 'text',
|
|
248
262
|
required: true,
|
|
249
263
|
category: 'technical',
|
|
250
|
-
priority:
|
|
264
|
+
priority: 3.5
|
|
251
265
|
}
|
|
252
266
|
]
|
|
253
267
|
}
|
|
254
268
|
});
|
|
255
|
-
//
|
|
256
|
-
questions.push({
|
|
257
|
-
id: 'data_storage',
|
|
258
|
-
question: '需要什么类型的存储?',
|
|
259
|
-
type: 'single',
|
|
260
|
-
required: true,
|
|
261
|
-
category: 'technical',
|
|
262
|
-
priority: 3,
|
|
263
|
-
options: [
|
|
264
|
-
{ key: 'postgresql', label: 'PostgreSQL', description: '关系型数据库' },
|
|
265
|
-
{ key: 'mongodb', label: 'MongoDB', description: '文档数据库' },
|
|
266
|
-
{ key: 'sqlite', label: 'SQLite', description: '本地数据库' },
|
|
267
|
-
{ key: 'none', label: '无需存储', description: '不需要数据持久化' }
|
|
268
|
-
]
|
|
269
|
-
});
|
|
270
|
-
// 4. 认证方式
|
|
269
|
+
// 4. 执行模式
|
|
271
270
|
questions.push({
|
|
272
|
-
id: '
|
|
273
|
-
question: '
|
|
271
|
+
id: 'execution_mode',
|
|
272
|
+
question: '选择执行模式(控制 AI 执行过程中的审批节点)',
|
|
274
273
|
type: 'single',
|
|
275
274
|
required: true,
|
|
276
|
-
category: '
|
|
275
|
+
category: 'quality',
|
|
277
276
|
priority: 4,
|
|
278
277
|
options: [
|
|
279
|
-
{ key: '
|
|
280
|
-
{ key: '
|
|
281
|
-
{ key: '
|
|
282
|
-
{ key: 'none', label: '无需认证', description: '无用户系统' }
|
|
278
|
+
{ key: 'auto', label: 'auto (推荐)', description: '全自动执行,无需人工审批,遇到阻塞自动 Meeting' },
|
|
279
|
+
{ key: 'confirm-key', label: 'confirm-key', description: '关键节点审批(计划、合并、部署)' },
|
|
280
|
+
{ key: 'confirm-all', label: 'confirm-all', description: '每个阶段都需人工确认' }
|
|
283
281
|
]
|
|
284
282
|
});
|
|
285
|
-
// 5.
|
|
286
|
-
questions.push({
|
|
287
|
-
id: 'api_style',
|
|
288
|
-
question: 'API 采用什么风格?',
|
|
289
|
-
type: 'single',
|
|
290
|
-
required: true,
|
|
291
|
-
category: 'technical',
|
|
292
|
-
priority: 5,
|
|
293
|
-
options: [
|
|
294
|
-
{ key: 'rest', label: 'RESTful', description: 'REST API' },
|
|
295
|
-
{ key: 'graphql', label: 'GraphQL', description: 'GraphQL API' },
|
|
296
|
-
{ key: 'grpc', label: 'gRPC', description: '高性能 RPC' },
|
|
297
|
-
{ key: 'mixed', label: '混合', description: '多种风格混合' }
|
|
298
|
-
]
|
|
299
|
-
});
|
|
300
|
-
// 6. 测试要求
|
|
283
|
+
// 5. 测试覆盖率
|
|
301
284
|
questions.push({
|
|
302
285
|
id: 'test_coverage',
|
|
303
286
|
question: '测试覆盖率要求?',
|
|
304
287
|
type: 'single',
|
|
305
288
|
required: true,
|
|
306
289
|
category: 'quality',
|
|
307
|
-
priority:
|
|
290
|
+
priority: 5,
|
|
308
291
|
options: [
|
|
309
292
|
{ key: 'high', label: '>80% (严格)', description: '完整单元测试和集成测试' },
|
|
310
293
|
{ key: 'medium', label: '>60% (标准)', description: '核心功能测试' },
|
|
@@ -312,14 +295,14 @@ class InteractiveQuestionGenerator {
|
|
|
312
295
|
{ key: 'none', label: '无要求', description: '不需要测试' }
|
|
313
296
|
]
|
|
314
297
|
});
|
|
315
|
-
//
|
|
298
|
+
// 6. 文档要求
|
|
316
299
|
questions.push({
|
|
317
|
-
id: '
|
|
300
|
+
id: 'documentation_level',
|
|
318
301
|
question: '需要什么级别的文档?',
|
|
319
302
|
type: 'single',
|
|
320
303
|
required: true,
|
|
321
304
|
category: 'quality',
|
|
322
|
-
priority:
|
|
305
|
+
priority: 6,
|
|
323
306
|
options: [
|
|
324
307
|
{ key: 'full', label: '完整文档', description: 'API + 使用指南 + 架构' },
|
|
325
308
|
{ key: 'basic', label: '基础文档', description: 'README + API' },
|
|
@@ -327,6 +310,49 @@ class InteractiveQuestionGenerator {
|
|
|
327
310
|
{ key: 'none', label: '无需文档', description: '不生成文档' }
|
|
328
311
|
]
|
|
329
312
|
});
|
|
313
|
+
// 7. E2E 测试
|
|
314
|
+
questions.push({
|
|
315
|
+
id: 'e2e_tests',
|
|
316
|
+
question: '是否启用端到端 (E2E) 测试?(适用于 Web/Mobile/GUI 项目,耗时较长)',
|
|
317
|
+
type: 'single',
|
|
318
|
+
required: false,
|
|
319
|
+
category: 'quality',
|
|
320
|
+
priority: 7,
|
|
321
|
+
options: [
|
|
322
|
+
{ key: 'true', label: '启用 E2E 测试', description: '使用 Playwright/Cypress 等框架进行端到端测试' },
|
|
323
|
+
{ key: 'false', label: '不启用 (推荐)', description: '仅进行单元测试和集成测试,节省时间' }
|
|
324
|
+
]
|
|
325
|
+
});
|
|
326
|
+
// 8. 风险评估
|
|
327
|
+
questions.push({
|
|
328
|
+
id: 'risks',
|
|
329
|
+
question: '这个任务可能面临哪些风险或挑战?',
|
|
330
|
+
type: 'multiple',
|
|
331
|
+
required: true,
|
|
332
|
+
category: 'risk',
|
|
333
|
+
priority: 8,
|
|
334
|
+
options: [
|
|
335
|
+
{ key: 'technical', label: '技术风险', description: '技术实现存在不确定性' },
|
|
336
|
+
{ key: 'time', label: '时间风险', description: '需要在短时间内完成' },
|
|
337
|
+
{ key: 'compatibility', label: '兼容性风险', description: '可能影响现有功能' },
|
|
338
|
+
{ key: 'none', label: '无明显风险', description: '任务清晰,风险可控' }
|
|
339
|
+
]
|
|
340
|
+
});
|
|
341
|
+
// 9. 验收标准
|
|
342
|
+
questions.push({
|
|
343
|
+
id: 'acceptance',
|
|
344
|
+
question: '如何判断任务完成?有哪些验收标准?',
|
|
345
|
+
type: 'multiple',
|
|
346
|
+
required: true,
|
|
347
|
+
category: 'scope',
|
|
348
|
+
priority: 9,
|
|
349
|
+
options: [
|
|
350
|
+
{ key: 'functional', label: '功能完整', description: '所有功能按预期工作' },
|
|
351
|
+
{ key: 'tested', label: '测试覆盖', description: '有足够的测试覆盖' },
|
|
352
|
+
{ key: 'performance', label: '性能达标', description: '满足性能要求' },
|
|
353
|
+
{ key: 'documented', label: '文档完善', description: '有完整的使用文档' }
|
|
354
|
+
]
|
|
355
|
+
});
|
|
330
356
|
return questions;
|
|
331
357
|
}
|
|
332
358
|
/**
|
|
@@ -12,6 +12,7 @@ export interface TaskAssignment {
|
|
|
12
12
|
}
|
|
13
13
|
export declare class Scheduler {
|
|
14
14
|
private stateManager;
|
|
15
|
+
private stateMachine;
|
|
15
16
|
private config;
|
|
16
17
|
private runningTasks;
|
|
17
18
|
constructor(stateManager: StateManager, config?: Partial<SchedulerConfig>);
|
|
@@ -27,6 +28,10 @@ export declare class Scheduler {
|
|
|
27
28
|
* 获取优先级权重
|
|
28
29
|
*/
|
|
29
30
|
private getPriorityWeight;
|
|
31
|
+
/**
|
|
32
|
+
* 通过状态机验证并执行状态转换
|
|
33
|
+
*/
|
|
34
|
+
private transitionTask;
|
|
30
35
|
/**
|
|
31
36
|
* 标记任务开始执行
|
|
32
37
|
*/
|
|
@@ -48,9 +53,18 @@ export declare class Scheduler {
|
|
|
48
53
|
*/
|
|
49
54
|
markTaskBlocked(taskId: string, reason: string): Promise<void>;
|
|
50
55
|
/**
|
|
51
|
-
*
|
|
56
|
+
* 获取所有可并行执行的任务(按优先级排序)
|
|
52
57
|
*/
|
|
53
58
|
getParallelTasks(): Promise<Task[]>;
|
|
59
|
+
/**
|
|
60
|
+
* 检测循环依赖(DFS)
|
|
61
|
+
* 返回所有检测到的循环路径
|
|
62
|
+
*/
|
|
63
|
+
detectCircularDependencies(tasks: Task[]): string[][];
|
|
64
|
+
/**
|
|
65
|
+
* 检测循环依赖并将相关任务标记为 blocked
|
|
66
|
+
*/
|
|
67
|
+
private handleCircularDependencies;
|
|
54
68
|
/**
|
|
55
69
|
* 获取调度状态
|
|
56
70
|
*/
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Scheduler = void 0;
|
|
4
|
+
const state_machine_js_1 = require("./state-machine.js");
|
|
4
5
|
class Scheduler {
|
|
5
6
|
stateManager;
|
|
7
|
+
stateMachine;
|
|
6
8
|
config;
|
|
7
9
|
runningTasks = new Set();
|
|
8
10
|
constructor(stateManager, config) {
|
|
9
11
|
this.stateManager = stateManager;
|
|
12
|
+
this.stateMachine = new state_machine_js_1.StateMachine();
|
|
10
13
|
this.config = {
|
|
11
14
|
maxConcurrentTasks: 3,
|
|
12
15
|
taskTimeout: 120000,
|
|
@@ -18,6 +21,8 @@ class Scheduler {
|
|
|
18
21
|
*/
|
|
19
22
|
async getNextTask() {
|
|
20
23
|
const tasks = await this.stateManager.listTasks();
|
|
24
|
+
// 检测并处理循环依赖
|
|
25
|
+
await this.handleCircularDependencies(tasks);
|
|
21
26
|
// 筛选可执行任务
|
|
22
27
|
const executable = tasks.filter(task => this.canExecute(task, tasks));
|
|
23
28
|
if (executable.length === 0) {
|
|
@@ -62,13 +67,28 @@ class Scheduler {
|
|
|
62
67
|
};
|
|
63
68
|
return weights[priority] || 0;
|
|
64
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* 通过状态机验证并执行状态转换
|
|
72
|
+
*/
|
|
73
|
+
async transitionTask(taskId, event, extraUpdates) {
|
|
74
|
+
const task = await this.stateManager.getTask(taskId);
|
|
75
|
+
if (!task)
|
|
76
|
+
throw new Error(`Task ${taskId} not found`);
|
|
77
|
+
const result = this.stateMachine.transition(task, event);
|
|
78
|
+
if (!result.success) {
|
|
79
|
+
throw new Error(`状态转换失败 [${task.id}]: ${result.error}`);
|
|
80
|
+
}
|
|
81
|
+
await this.stateManager.updateTask(taskId, {
|
|
82
|
+
status: result.toStatus,
|
|
83
|
+
...extraUpdates
|
|
84
|
+
});
|
|
85
|
+
}
|
|
65
86
|
/**
|
|
66
87
|
* 标记任务开始执行
|
|
67
88
|
*/
|
|
68
89
|
async markTaskStarted(taskId) {
|
|
69
90
|
this.runningTasks.add(taskId);
|
|
70
|
-
await this.
|
|
71
|
-
status: 'in_progress',
|
|
91
|
+
await this.transitionTask(taskId, 'start', {
|
|
72
92
|
phases: {
|
|
73
93
|
develop: { status: 'in_progress', duration: null, startedAt: new Date().toISOString() },
|
|
74
94
|
verify: { status: 'pending', duration: null },
|
|
@@ -83,8 +103,7 @@ class Scheduler {
|
|
|
83
103
|
this.runningTasks.delete(taskId);
|
|
84
104
|
const task = await this.stateManager.getTask(taskId);
|
|
85
105
|
if (task) {
|
|
86
|
-
await this.
|
|
87
|
-
status: 'completed',
|
|
106
|
+
await this.transitionTask(taskId, 'accept_done', {
|
|
88
107
|
phases: {
|
|
89
108
|
...task.phases,
|
|
90
109
|
accept: { status: 'completed', duration: 0, completedAt: new Date().toISOString() }
|
|
@@ -97,42 +116,96 @@ class Scheduler {
|
|
|
97
116
|
*/
|
|
98
117
|
async markTaskFailed(taskId, error) {
|
|
99
118
|
this.runningTasks.delete(taskId);
|
|
100
|
-
await this.stateManager.
|
|
101
|
-
|
|
119
|
+
const task = await this.stateManager.getTask(taskId);
|
|
120
|
+
const retryCount = task?.retryCount || 0;
|
|
121
|
+
await this.transitionTask(taskId, 'fail', {
|
|
102
122
|
error,
|
|
103
|
-
retryCount
|
|
123
|
+
retryCount
|
|
104
124
|
});
|
|
105
125
|
}
|
|
106
126
|
/**
|
|
107
127
|
* 标记任务等待确认
|
|
108
128
|
*/
|
|
109
129
|
async markTaskWaiting(taskId) {
|
|
110
|
-
await this.
|
|
111
|
-
status: 'waiting'
|
|
112
|
-
});
|
|
130
|
+
await this.transitionTask(taskId, 'wait');
|
|
113
131
|
}
|
|
114
132
|
/**
|
|
115
133
|
* 标记任务阻塞(需要 Meeting)
|
|
116
134
|
*/
|
|
117
135
|
async markTaskBlocked(taskId, reason) {
|
|
118
136
|
this.runningTasks.delete(taskId);
|
|
119
|
-
await this.
|
|
120
|
-
status: 'blocked',
|
|
137
|
+
await this.transitionTask(taskId, 'block', {
|
|
121
138
|
error: reason
|
|
122
139
|
});
|
|
123
140
|
}
|
|
124
141
|
/**
|
|
125
|
-
*
|
|
142
|
+
* 获取所有可并行执行的任务(按优先级排序)
|
|
126
143
|
*/
|
|
127
144
|
async getParallelTasks() {
|
|
128
145
|
const tasks = await this.stateManager.listTasks();
|
|
129
|
-
|
|
146
|
+
// 检测并处理循环依赖
|
|
147
|
+
await this.handleCircularDependencies(tasks);
|
|
148
|
+
// 筛选可执行任务
|
|
149
|
+
const executable = tasks.filter(task => this.canExecute(task, tasks));
|
|
150
|
+
// 按优先级排序(高优先级优先)
|
|
151
|
+
executable.sort((a, b) => this.getPriorityWeight(b.priority) - this.getPriorityWeight(a.priority));
|
|
152
|
+
// 限制并发数
|
|
153
|
+
return executable.slice(0, this.config.maxConcurrentTasks);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 检测循环依赖(DFS)
|
|
157
|
+
* 返回所有检测到的循环路径
|
|
158
|
+
*/
|
|
159
|
+
detectCircularDependencies(tasks) {
|
|
160
|
+
const cycles = [];
|
|
161
|
+
const visited = new Set();
|
|
162
|
+
const recursionStack = new Set();
|
|
163
|
+
const dfs = (taskId, path) => {
|
|
164
|
+
visited.add(taskId);
|
|
165
|
+
recursionStack.add(taskId);
|
|
166
|
+
const task = tasks.find(t => t.id === taskId);
|
|
167
|
+
if (!task || !task.dependencies) {
|
|
168
|
+
recursionStack.delete(taskId);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
for (const depId of task.dependencies) {
|
|
172
|
+
if (!visited.has(depId)) {
|
|
173
|
+
dfs(depId, [...path, depId]);
|
|
174
|
+
}
|
|
175
|
+
else if (recursionStack.has(depId)) {
|
|
176
|
+
// 找到循环
|
|
177
|
+
const cycleStart = path.indexOf(depId);
|
|
178
|
+
const cycle = cycleStart >= 0
|
|
179
|
+
? [...path.slice(cycleStart), depId]
|
|
180
|
+
: [taskId, depId];
|
|
181
|
+
cycles.push(cycle);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
recursionStack.delete(taskId);
|
|
185
|
+
};
|
|
130
186
|
for (const task of tasks) {
|
|
131
|
-
if (
|
|
132
|
-
|
|
187
|
+
if (!visited.has(task.id)) {
|
|
188
|
+
dfs(task.id, [task.id]);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return cycles;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 检测循环依赖并将相关任务标记为 blocked
|
|
195
|
+
*/
|
|
196
|
+
async handleCircularDependencies(tasks) {
|
|
197
|
+
const cycles = this.detectCircularDependencies(tasks);
|
|
198
|
+
for (const cycle of cycles) {
|
|
199
|
+
for (const taskId of cycle) {
|
|
200
|
+
const task = tasks.find(t => t.id === taskId);
|
|
201
|
+
if (task && (task.status === 'pending' || task.status === 'retry_queue')) {
|
|
202
|
+
await this.stateManager.updateTask(taskId, {
|
|
203
|
+
status: 'blocked',
|
|
204
|
+
error: `循环依赖检测: ${cycle.join(' → ')}`
|
|
205
|
+
});
|
|
206
|
+
}
|
|
133
207
|
}
|
|
134
208
|
}
|
|
135
|
-
return available;
|
|
136
209
|
}
|
|
137
210
|
/**
|
|
138
211
|
* 获取调度状态
|
|
@@ -176,6 +176,10 @@ class SmartQuestionAnalyzer {
|
|
|
176
176
|
inferences.push(this.inferE2ETest(desc, context));
|
|
177
177
|
// 5. 推断执行模式
|
|
178
178
|
inferences.push(this.inferExecutionMode(desc, context));
|
|
179
|
+
// 6. 推断任务目标类型
|
|
180
|
+
inferences.push(this.inferObjective(desc, context));
|
|
181
|
+
// 7. 推断测试覆盖率级别
|
|
182
|
+
inferences.push(this.inferTestCoverage(desc, context));
|
|
179
183
|
return inferences;
|
|
180
184
|
}
|
|
181
185
|
/**
|
|
@@ -384,6 +388,74 @@ class SmartQuestionAnalyzer {
|
|
|
384
388
|
inference.reason = '无法确定执行模式';
|
|
385
389
|
return inference;
|
|
386
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* 推断任务目标类型
|
|
393
|
+
*/
|
|
394
|
+
inferObjective(desc, context) {
|
|
395
|
+
const inference = {
|
|
396
|
+
questionId: 'objective',
|
|
397
|
+
confidence: 'low',
|
|
398
|
+
reason: ''
|
|
399
|
+
};
|
|
400
|
+
if (/(fix|bug|修复|hotfix|patch)/i.test(desc)) {
|
|
401
|
+
inference.inferredAnswer = 'bug_fix';
|
|
402
|
+
inference.confidence = 'medium';
|
|
403
|
+
inference.reason = '任务描述包含修复关键词';
|
|
404
|
+
}
|
|
405
|
+
else if (/(implement|add|新功能|实现|开发|feature|新增)/i.test(desc)) {
|
|
406
|
+
inference.inferredAnswer = 'new_feature';
|
|
407
|
+
inference.confidence = 'medium';
|
|
408
|
+
inference.reason = '任务描述包含新功能关键词';
|
|
409
|
+
}
|
|
410
|
+
else if (/(refactor|优化|重构|improve|性能)/i.test(desc)) {
|
|
411
|
+
inference.inferredAnswer = 'refactor';
|
|
412
|
+
inference.confidence = 'medium';
|
|
413
|
+
inference.reason = '任务描述包含重构关键词';
|
|
414
|
+
}
|
|
415
|
+
else if (/(test|测试|coverage|覆盖)/i.test(desc)) {
|
|
416
|
+
inference.inferredAnswer = 'test';
|
|
417
|
+
inference.confidence = 'medium';
|
|
418
|
+
inference.reason = '任务描述包含测试关键词';
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
inference.reason = '无法确定任务目标类型';
|
|
422
|
+
}
|
|
423
|
+
return inference;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* 推断测试覆盖率级别
|
|
427
|
+
*/
|
|
428
|
+
inferTestCoverage(desc, context) {
|
|
429
|
+
const inference = {
|
|
430
|
+
questionId: 'test_coverage',
|
|
431
|
+
confidence: 'low',
|
|
432
|
+
reason: ''
|
|
433
|
+
};
|
|
434
|
+
if (context.hasTests && /(test|测试)/i.test(desc)) {
|
|
435
|
+
inference.inferredAnswer = 'high';
|
|
436
|
+
inference.confidence = 'medium';
|
|
437
|
+
inference.reason = '项目已有测试,且任务涉及测试';
|
|
438
|
+
}
|
|
439
|
+
else if (/(implement|add|新功能|实现|feature)/i.test(desc)) {
|
|
440
|
+
inference.inferredAnswer = 'medium';
|
|
441
|
+
inference.confidence = 'low';
|
|
442
|
+
inference.reason = '新功能任务,中等测试覆盖率';
|
|
443
|
+
}
|
|
444
|
+
else if (/(fix|bug|修复)/i.test(desc)) {
|
|
445
|
+
inference.inferredAnswer = 'low';
|
|
446
|
+
inference.confidence = 'medium';
|
|
447
|
+
inference.reason = 'Bug 修复,需要回归测试但覆盖率要求不高';
|
|
448
|
+
}
|
|
449
|
+
else if (/(prototype|poc|demo|快速|原型)/i.test(desc)) {
|
|
450
|
+
inference.inferredAnswer = 'none';
|
|
451
|
+
inference.confidence = 'medium';
|
|
452
|
+
inference.reason = '原型任务,不需要测试';
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
inference.reason = '无法确定测试覆盖率级别';
|
|
456
|
+
}
|
|
457
|
+
return inference;
|
|
458
|
+
}
|
|
387
459
|
/**
|
|
388
460
|
* 生成推断摘要
|
|
389
461
|
*/
|
|
@@ -7,6 +7,8 @@ const TRANSITIONS = [
|
|
|
7
7
|
{ from: 'pending', to: 'scheduled', event: 'schedule' },
|
|
8
8
|
// scheduled → in_progress
|
|
9
9
|
{ from: 'scheduled', to: 'in_progress', event: 'start' },
|
|
10
|
+
// pending → in_progress (跳过 scheduled,直接开始)
|
|
11
|
+
{ from: 'pending', to: 'in_progress', event: 'start' },
|
|
10
12
|
// in_progress → verify
|
|
11
13
|
{ from: 'in_progress', to: 'verify', event: 'develop_done' },
|
|
12
14
|
// verify → accept
|
|
@@ -16,7 +16,7 @@ export interface UserAnswers {
|
|
|
16
16
|
techStack?: string[];
|
|
17
17
|
testCoverage?: string;
|
|
18
18
|
documentationLevel?: string;
|
|
19
|
-
additionalContext?: Record<string, string>;
|
|
19
|
+
additionalContext?: Record<string, string | string[]>;
|
|
20
20
|
/** 是否启用 E2E 测试 */
|
|
21
21
|
e2eTests?: boolean;
|
|
22
22
|
/** E2E 测试类型 (web/mobile/gui) */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TaskPlanner = void 0;
|
|
4
|
+
const answer_mapper_js_1 = require("./answer-mapper.js");
|
|
4
5
|
/**
|
|
5
6
|
* TaskPlanner - 任务拆解器
|
|
6
7
|
*
|
|
@@ -261,16 +262,21 @@ ${userContext.documentationLevel}
|
|
|
261
262
|
* 提取用户上下文
|
|
262
263
|
*/
|
|
263
264
|
extractUserContext(answers) {
|
|
264
|
-
|
|
265
|
-
const
|
|
265
|
+
// 先翻译 brainstorm 规范键为 planner 期望的键
|
|
266
|
+
const translated = (0, answer_mapper_js_1.translateBrainstormAnswers)(answers);
|
|
267
|
+
const merged = { ...answers, ...translated };
|
|
268
|
+
// 辅助函数:提取字符串值(处理 string[] 情况)
|
|
269
|
+
const str = (v) => Array.isArray(v) ? v.join(', ') : v;
|
|
270
|
+
const e2eAnswer = str(merged['E2E测试'] || merged['e2eTests'] || merged['e2e']);
|
|
271
|
+
const e2eTypeAnswer = str(merged['E2E类型'] || merged['e2eType']);
|
|
266
272
|
return {
|
|
267
|
-
objective:
|
|
268
|
-
techStack: this.parseArrayAnswer(
|
|
269
|
-
testCoverage:
|
|
270
|
-
documentationLevel:
|
|
273
|
+
objective: str(merged['目标'] || merged['objective']),
|
|
274
|
+
techStack: this.parseArrayAnswer(str(merged['技术栈'] || merged['techStack']) || ''),
|
|
275
|
+
testCoverage: str(merged['测试'] || merged['testCoverage']),
|
|
276
|
+
documentationLevel: str(merged['文档'] || merged['documentationLevel']),
|
|
271
277
|
e2eTests: e2eAnswer === 'true' || e2eAnswer === '✅ 启用 E2E 测试' || e2eAnswer === '是',
|
|
272
278
|
e2eType: e2eTypeAnswer || 'web',
|
|
273
|
-
additionalContext:
|
|
279
|
+
additionalContext: merged
|
|
274
280
|
};
|
|
275
281
|
}
|
|
276
282
|
parseArrayAnswer(answer) {
|
|
@@ -2,7 +2,12 @@ import type { GlobalState, Task, Approval, ApprovalStatus } from '../types/index
|
|
|
2
2
|
export declare class StateManager {
|
|
3
3
|
private store;
|
|
4
4
|
private stateCache;
|
|
5
|
+
private writeLock;
|
|
5
6
|
constructor(basePath: string);
|
|
7
|
+
/**
|
|
8
|
+
* 串行化异步写操作,防止并发 read-modify-write 竞态
|
|
9
|
+
*/
|
|
10
|
+
private withLock;
|
|
6
11
|
initialize(): Promise<void>;
|
|
7
12
|
getState(): Promise<GlobalState>;
|
|
8
13
|
updateState(updates: Partial<GlobalState>): Promise<void>;
|