openmatrix 0.1.99 → 0.2.2
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/impl/coder-agent.js +42 -42
- package/dist/agents/impl/executor-agent.js +75 -75
- package/dist/agents/impl/planner-agent.js +63 -63
- package/dist/agents/impl/reviewer-agent.js +66 -66
- package/dist/agents/impl/tester-agent.js +56 -56
- package/dist/cli/commands/auto.js +20 -6
- package/dist/cli/commands/complete.js +1 -1
- package/dist/cli/commands/meeting.js +0 -1
- package/dist/cli/commands/report.js +45 -45
- package/dist/cli/commands/start.js +168 -34
- package/dist/cli/commands/status.js +0 -1
- package/dist/cli/commands/step.js +62 -35
- package/dist/orchestrator/ai-reviewer.d.ts +30 -2
- package/dist/orchestrator/ai-reviewer.js +314 -209
- package/dist/orchestrator/answer-mapper.d.ts +1 -0
- package/dist/orchestrator/answer-mapper.js +5 -2
- package/dist/orchestrator/approval-manager.js +14 -13
- package/dist/orchestrator/executor.d.ts +10 -1
- package/dist/orchestrator/executor.js +62 -2
- package/dist/orchestrator/interactive-question-generator.js +4 -3
- package/dist/orchestrator/meeting-manager.js +32 -31
- package/dist/orchestrator/phase-executor.js +9 -6
- package/dist/orchestrator/scheduler.d.ts +8 -6
- package/dist/orchestrator/scheduler.js +53 -22
- package/dist/orchestrator/state-machine.js +2 -2
- package/dist/orchestrator/task-planner.d.ts +83 -4
- package/dist/orchestrator/task-planner.js +702 -124
- package/dist/storage/state-manager.d.ts +6 -0
- package/dist/storage/state-manager.js +28 -0
- package/package.json +55 -55
- package/scripts/build-check.js +19 -19
- package/scripts/install-skills.js +57 -57
- package/skills/approve.md +250 -250
- package/skills/auto.md +298 -298
- package/skills/meeting.md +324 -324
- package/skills/om.md +112 -112
- package/skills/openmatrix.md +112 -112
- package/skills/start.md +38 -6
- package/dist/cli/commands/upgrade.d.ts +0 -2
- package/dist/cli/commands/upgrade.js +0 -329
- package/dist/orchestrator/task-planner.old.d.ts +0 -87
- package/dist/orchestrator/task-planner.old.js +0 -444
|
@@ -135,68 +135,68 @@ exports.reportCommand = new commander_1.Command('report')
|
|
|
135
135
|
});
|
|
136
136
|
function generateMarkdownReport(state, tasks, approvals, stats, efficiency, options) {
|
|
137
137
|
const duration = Math.round(efficiency.totalDuration / 1000 / 60); // 转为分钟
|
|
138
|
-
let report = `
|
|
139
|
-
# 📊 OpenMatrix 执行报告
|
|
140
|
-
|
|
141
|
-
**Run ID:** ${state.runId}
|
|
142
|
-
**时间:** ${state.startedAt}
|
|
143
|
-
**状态:** ${state.status}
|
|
144
|
-
|
|
145
|
-
## 统计概览
|
|
146
|
-
|
|
147
|
-
| 状态 | 数量 | 占比 |
|
|
148
|
-
|------|------|------|
|
|
149
|
-
| ✅ 完成 | ${stats.completed} | ${stats.total > 0 ? Math.round(stats.completed / stats.total * 100) : 0}% |
|
|
150
|
-
| ❌ 失败 | ${stats.failed} | ${stats.total > 0 ? Math.round(stats.failed / stats.total * 100) : 0}% |
|
|
151
|
-
| 🔄 进行中 | ${stats.inProgress} | ${stats.total > 0 ? Math.round(stats.inProgress / stats.total * 100) : 0}% |
|
|
152
|
-
| 🔴 阻塞 | ${stats.blocked} | ${stats.total > 0 ? Math.round(stats.blocked / stats.total * 100) : 0}% |
|
|
153
|
-
| ⏳ 待处理 | ${stats.pending} | ${stats.total > 0 ? Math.round(stats.pending / stats.total * 100) : 0}% |
|
|
154
|
-
|
|
155
|
-
## 任务详情
|
|
156
|
-
|
|
157
|
-
### ✅ 已完成
|
|
138
|
+
let report = `
|
|
139
|
+
# 📊 OpenMatrix 执行报告
|
|
140
|
+
|
|
141
|
+
**Run ID:** ${state.runId}
|
|
142
|
+
**时间:** ${state.startedAt}
|
|
143
|
+
**状态:** ${state.status}
|
|
144
|
+
|
|
145
|
+
## 统计概览
|
|
146
|
+
|
|
147
|
+
| 状态 | 数量 | 占比 |
|
|
148
|
+
|------|------|------|
|
|
149
|
+
| ✅ 完成 | ${stats.completed} | ${stats.total > 0 ? Math.round(stats.completed / stats.total * 100) : 0}% |
|
|
150
|
+
| ❌ 失败 | ${stats.failed} | ${stats.total > 0 ? Math.round(stats.failed / stats.total * 100) : 0}% |
|
|
151
|
+
| 🔄 进行中 | ${stats.inProgress} | ${stats.total > 0 ? Math.round(stats.inProgress / stats.total * 100) : 0}% |
|
|
152
|
+
| 🔴 阻塞 | ${stats.blocked} | ${stats.total > 0 ? Math.round(stats.blocked / stats.total * 100) : 0}% |
|
|
153
|
+
| ⏳ 待处理 | ${stats.pending} | ${stats.total > 0 ? Math.round(stats.pending / stats.total * 100) : 0}% |
|
|
154
|
+
|
|
155
|
+
## 任务详情
|
|
156
|
+
|
|
157
|
+
### ✅ 已完成
|
|
158
158
|
${tasks
|
|
159
159
|
.filter(t => t.status === 'completed')
|
|
160
160
|
.map(t => `- ${t.id}: ${t.title}`)
|
|
161
|
-
.join('\n') || '_无_'}
|
|
162
|
-
|
|
163
|
-
### ❌ 失败
|
|
161
|
+
.join('\n') || '_无_'}
|
|
162
|
+
|
|
163
|
+
### ❌ 失败
|
|
164
164
|
${tasks
|
|
165
165
|
.filter(t => t.status === 'failed')
|
|
166
166
|
.map(t => `- ${t.id}: ${t.title}\n 原因: ${t.error || '未知'}`)
|
|
167
|
-
.join('\n') || '_无_'}
|
|
168
|
-
|
|
169
|
-
### 🔴 阻塞
|
|
167
|
+
.join('\n') || '_无_'}
|
|
168
|
+
|
|
169
|
+
### 🔴 阻塞
|
|
170
170
|
${tasks
|
|
171
171
|
.filter(t => t.status === 'blocked')
|
|
172
172
|
.map(t => `- ${t.id}: ${t.title}\n 原因: ${t.error || '未知'}`)
|
|
173
|
-
.join('\n') || '_无_'}
|
|
173
|
+
.join('\n') || '_无_'}
|
|
174
174
|
`;
|
|
175
175
|
// 效率分析
|
|
176
176
|
if (options.efficiency) {
|
|
177
|
-
report += `
|
|
178
|
-
## 🏆 效率分析
|
|
179
|
-
|
|
180
|
-
| 指标 | 值 |
|
|
181
|
-
|------|-----|
|
|
182
|
-
| 总耗时 | ${duration} 分钟 |
|
|
183
|
-
| Agent 调用 | ${efficiency.agentCalls} 次 |
|
|
184
|
-
| 重试次数 | ${efficiency.retryCount} 次 |
|
|
185
|
-
| 并行度 | ${efficiency.parallelism} / ${efficiency.targetParallelism} |
|
|
177
|
+
report += `
|
|
178
|
+
## 🏆 效率分析
|
|
179
|
+
|
|
180
|
+
| 指标 | 值 |
|
|
181
|
+
|------|-----|
|
|
182
|
+
| 总耗时 | ${duration} 分钟 |
|
|
183
|
+
| Agent 调用 | ${efficiency.agentCalls} 次 |
|
|
184
|
+
| 重试次数 | ${efficiency.retryCount} 次 |
|
|
185
|
+
| 并行度 | ${efficiency.parallelism} / ${efficiency.targetParallelism} |
|
|
186
186
|
`;
|
|
187
187
|
}
|
|
188
188
|
// 审批记录
|
|
189
|
-
report += `
|
|
190
|
-
## 🔔 审批记录
|
|
191
|
-
|
|
192
|
-
| ID | 类型 | 决策 | 状态 |
|
|
193
|
-
|----|------|------|------|
|
|
189
|
+
report += `
|
|
190
|
+
## 🔔 审批记录
|
|
191
|
+
|
|
192
|
+
| ID | 类型 | 决策 | 状态 |
|
|
193
|
+
|----|------|------|------|
|
|
194
194
|
${approvals.length > 0
|
|
195
195
|
? approvals.map(a => `| ${a.id} | ${a.type} | ${a.decision || '-'} | ${a.status} |`).join('\n')
|
|
196
|
-
: '| _无_ | | | |'}
|
|
197
|
-
|
|
198
|
-
---
|
|
199
|
-
*报告生成时间: ${new Date().toISOString()}*
|
|
196
|
+
: '| _无_ | | | |'}
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
*报告生成时间: ${new Date().toISOString()}*
|
|
200
200
|
`;
|
|
201
201
|
return report.trim();
|
|
202
202
|
}
|
|
@@ -59,6 +59,8 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
59
59
|
.option('--docs <level>', '文档级别 (full|basic|minimal|none)')
|
|
60
60
|
.option('--tasks-json <json>', 'AI 已拆分的任务 JSON (跳过自动解析)')
|
|
61
61
|
.option('--e2e-tests', '启用 E2E 测试')
|
|
62
|
+
.option('--e2e-type <type>', 'E2E 测试类型 (web|visual)')
|
|
63
|
+
.option('--research-context <path>', '研究上下文 JSON 路径 (来自 /om:research 的 context.json)')
|
|
62
64
|
.action(async (input, options) => {
|
|
63
65
|
const basePath = process.cwd();
|
|
64
66
|
const omPath = path.join(basePath, '.openmatrix');
|
|
@@ -112,8 +114,91 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
112
114
|
// ============================
|
|
113
115
|
// 路径 B: 自动解析 (fallback)
|
|
114
116
|
// ============================
|
|
115
|
-
await handleAutoParse(input, options, stateManager, state);
|
|
117
|
+
await handleAutoParse(input, options, stateManager, state, basePath, omPath);
|
|
116
118
|
});
|
|
119
|
+
/**
|
|
120
|
+
* 加载研究上下文
|
|
121
|
+
*/
|
|
122
|
+
async function loadResearchContext(researchContextPath, basePath, omPath) {
|
|
123
|
+
let contextPath = researchContextPath;
|
|
124
|
+
if (!contextPath) {
|
|
125
|
+
// 自动检测默认路径
|
|
126
|
+
const defaultPath = path.join(omPath, 'research', 'context.json');
|
|
127
|
+
try {
|
|
128
|
+
await fs.access(defaultPath);
|
|
129
|
+
contextPath = `@${defaultPath}`;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return { context: null, report: null };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (!contextPath) {
|
|
136
|
+
return { context: null, report: null };
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
// 支持 @file 语法
|
|
140
|
+
let jsonStr = contextPath;
|
|
141
|
+
if (jsonStr.startsWith('@')) {
|
|
142
|
+
const filePath = jsonStr.slice(1);
|
|
143
|
+
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.join(basePath, filePath);
|
|
144
|
+
jsonStr = await fs.readFile(resolvedPath, 'utf-8');
|
|
145
|
+
}
|
|
146
|
+
const context = JSON.parse(jsonStr);
|
|
147
|
+
// 读取研究报告
|
|
148
|
+
let report = null;
|
|
149
|
+
if (context.reportPath) {
|
|
150
|
+
const reportPath = path.isAbsolute(context.reportPath)
|
|
151
|
+
? context.reportPath
|
|
152
|
+
: path.join(basePath, context.reportPath);
|
|
153
|
+
try {
|
|
154
|
+
report = await fs.readFile(reportPath, 'utf-8');
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// 研究报告不存在不影响流程
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return { context, report };
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return { context: null, report: null };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* 合并研究上下文到 AI 解析的输入
|
|
168
|
+
*/
|
|
169
|
+
function mergeResearchContext(tasksInput, researchContext, researchReport) {
|
|
170
|
+
const merged = { ...tasksInput };
|
|
171
|
+
// goals: 研究的基础 goals + AI 补充的 goals(去重)
|
|
172
|
+
const baseGoals = researchContext.goals || [];
|
|
173
|
+
const aiGoals = tasksInput.goals || [];
|
|
174
|
+
const mergedGoals = [...new Set([...baseGoals, ...aiGoals])];
|
|
175
|
+
if (mergedGoals.length > 0) {
|
|
176
|
+
merged.goals = mergedGoals;
|
|
177
|
+
}
|
|
178
|
+
// constraints: 合并
|
|
179
|
+
const baseConstraints = researchContext.constraints || [];
|
|
180
|
+
const aiConstraints = tasksInput.constraints || [];
|
|
181
|
+
const mergedConstraints = [...new Set([...baseConstraints, ...aiConstraints])];
|
|
182
|
+
if (mergedConstraints.length > 0) {
|
|
183
|
+
merged.constraints = mergedConstraints;
|
|
184
|
+
}
|
|
185
|
+
// deliverables: 合并
|
|
186
|
+
const baseDeliverables = researchContext.deliverables || [];
|
|
187
|
+
const aiDeliverables = tasksInput.deliverables || [];
|
|
188
|
+
const mergedDeliverables = [...new Set([...baseDeliverables, ...aiDeliverables])];
|
|
189
|
+
if (mergedDeliverables.length > 0) {
|
|
190
|
+
merged.deliverables = mergedDeliverables;
|
|
191
|
+
}
|
|
192
|
+
// plan: 如果 AI 未提供 plan,使用研究报告内容
|
|
193
|
+
if (!merged.plan && researchReport) {
|
|
194
|
+
merged.plan = `# 领域研究: ${researchContext.domain}\n\n## 研究主题\n${researchContext.topic}\n\n${researchReport}`;
|
|
195
|
+
}
|
|
196
|
+
else if (researchReport) {
|
|
197
|
+
// AI 已有 plan,将研究作为附录追加
|
|
198
|
+
merged.plan = `${merged.plan}\n\n---\n\n# 领域研究背景: ${researchContext.domain}\n\n## 研究主题\n${researchContext.topic}\n\n${researchReport}`;
|
|
199
|
+
}
|
|
200
|
+
return merged;
|
|
201
|
+
}
|
|
117
202
|
/**
|
|
118
203
|
* 处理 AI 解析的任务 (--tasks-json)
|
|
119
204
|
* AI 提供 ParsedTask 格式的结构化数据,仍由 TaskPlanner 做拆分
|
|
@@ -152,18 +237,27 @@ async function handleTasksJson(options, stateManager, state, omPath, basePath) {
|
|
|
152
237
|
}
|
|
153
238
|
return;
|
|
154
239
|
}
|
|
240
|
+
// 加载并合并研究上下文
|
|
241
|
+
const { context: researchContext, report: researchReport } = await loadResearchContext(options.researchContext, basePath, omPath);
|
|
242
|
+
let resolvedInput = tasksInput;
|
|
243
|
+
if (researchContext) {
|
|
244
|
+
resolvedInput = mergeResearchContext(tasksInput, researchContext, researchReport);
|
|
245
|
+
if (!options.json) {
|
|
246
|
+
console.log(`🔬 已加载研究领域: ${researchContext.domain}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
155
249
|
// 保存 AI 生成的执行计划
|
|
156
|
-
if (
|
|
157
|
-
await fs.writeFile(path.join(omPath, 'plan.md'),
|
|
250
|
+
if (resolvedInput.plan) {
|
|
251
|
+
await fs.writeFile(path.join(omPath, 'plan.md'), resolvedInput.plan, 'utf-8');
|
|
158
252
|
}
|
|
159
253
|
// 构建 ParsedTask
|
|
160
254
|
const parsedTask = {
|
|
161
|
-
title: tasksInput.title,
|
|
162
|
-
description:
|
|
163
|
-
goals:
|
|
164
|
-
goalTypes:
|
|
165
|
-
constraints:
|
|
166
|
-
deliverables:
|
|
255
|
+
title: resolvedInput.title || tasksInput.title,
|
|
256
|
+
description: resolvedInput.description || '',
|
|
257
|
+
goals: resolvedInput.goals,
|
|
258
|
+
goalTypes: resolvedInput.goalTypes,
|
|
259
|
+
constraints: resolvedInput.constraints || [],
|
|
260
|
+
deliverables: resolvedInput.deliverables || [],
|
|
167
261
|
rawContent: ''
|
|
168
262
|
};
|
|
169
263
|
// 解析质量配置
|
|
@@ -173,15 +267,19 @@ async function handleTasksJson(options, stateManager, state, omPath, basePath) {
|
|
|
173
267
|
if (tasksInput.e2eTests || options.e2eTests) {
|
|
174
268
|
qualityConfig.e2eTests = true;
|
|
175
269
|
}
|
|
270
|
+
// E2E 类型(通过 answers 传递给 TaskPlanner)
|
|
271
|
+
const extraAnswers = { ...(resolvedInput.answers || {}) };
|
|
272
|
+
if (options.e2eType) {
|
|
273
|
+
extraAnswers.e2eType = options.e2eType;
|
|
274
|
+
}
|
|
176
275
|
if (!options.json) {
|
|
177
276
|
console.log(`\n📋 任务: ${parsedTask.title}`);
|
|
178
277
|
console.log(` 目标: ${parsedTask.goals.join(', ')}`);
|
|
179
278
|
console.log('\n🔧 拆解任务...');
|
|
180
279
|
}
|
|
181
280
|
// 使用 TaskPlanner 拆分(保持原有拆分逻辑)
|
|
182
|
-
const answers = tasksInput.answers || {};
|
|
183
281
|
const planner = new task_planner_js_1.TaskPlanner();
|
|
184
|
-
const subTasks = planner.breakdown(parsedTask,
|
|
282
|
+
const subTasks = planner.breakdown(parsedTask, extraAnswers, qualityConfig, resolvedInput.plan);
|
|
185
283
|
// 创建任务到状态管理器,并建立 ID 映射
|
|
186
284
|
// TaskPlanner 生成的 taskId 和 StateManager 创建的 id 不同,
|
|
187
285
|
// 需要映射后才能正确设置 dependencies
|
|
@@ -238,14 +336,15 @@ async function handleTasksJson(options, stateManager, state, omPath, basePath) {
|
|
|
238
336
|
assignedAgent: t.assignedAgent
|
|
239
337
|
})),
|
|
240
338
|
taskInfo: {
|
|
241
|
-
title:
|
|
242
|
-
description:
|
|
243
|
-
quality: qualityLevel
|
|
339
|
+
title: resolvedInput.title,
|
|
340
|
+
description: resolvedInput.description,
|
|
341
|
+
quality: qualityLevel,
|
|
342
|
+
domain: researchContext?.domain
|
|
244
343
|
}
|
|
245
344
|
}));
|
|
246
345
|
}
|
|
247
346
|
else {
|
|
248
|
-
console.log(`\n📋 ${
|
|
347
|
+
console.log(`\n📋 ${resolvedInput.title} - ${subTasks.length} 个子任务:\n`);
|
|
249
348
|
subTasks.forEach((task, i) => {
|
|
250
349
|
console.log(` ${i + 1}. ${task.title} (${task.priority}, ${task.assignedAgent})`);
|
|
251
350
|
});
|
|
@@ -275,14 +374,15 @@ async function handleTasksJson(options, stateManager, state, omPath, basePath) {
|
|
|
275
374
|
},
|
|
276
375
|
subagentTasks,
|
|
277
376
|
taskInfo: {
|
|
278
|
-
title:
|
|
279
|
-
description:
|
|
280
|
-
quality: qualityLevel
|
|
377
|
+
title: resolvedInput.title,
|
|
378
|
+
description: resolvedInput.description,
|
|
379
|
+
quality: qualityLevel,
|
|
380
|
+
domain: researchContext?.domain
|
|
281
381
|
}
|
|
282
382
|
}));
|
|
283
383
|
}
|
|
284
384
|
else {
|
|
285
|
-
console.log(`\n📋 ${
|
|
385
|
+
console.log(`\n📋 ${resolvedInput.title} - ${subTasks.length} 个子任务已创建`);
|
|
286
386
|
console.log(`🎯 执行模式:${executionMode}`);
|
|
287
387
|
console.log(` 质量级别:${qualityLevel}`);
|
|
288
388
|
console.log('\n🚀 等待 Skill 执行任务...');
|
|
@@ -292,7 +392,7 @@ async function handleTasksJson(options, stateManager, state, omPath, basePath) {
|
|
|
292
392
|
/**
|
|
293
393
|
* 处理自动解析 (fallback,无 AI 时使用)
|
|
294
394
|
*/
|
|
295
|
-
async function handleAutoParse(input, options, stateManager, state) {
|
|
395
|
+
async function handleAutoParse(input, options, stateManager, state, basePath, omPath) {
|
|
296
396
|
// 构建任务内容
|
|
297
397
|
let taskContent = input;
|
|
298
398
|
if (options.title || options.description) {
|
|
@@ -302,7 +402,46 @@ async function handleAutoParse(input, options, stateManager, state) {
|
|
|
302
402
|
const docs = options.docs ? `\n文档要求: ${options.docs}` : '';
|
|
303
403
|
taskContent = `# ${title}\n\n${description}${techStack}${docs}`;
|
|
304
404
|
}
|
|
405
|
+
// 如果没有提供输入,先检测研究上下文,再检测 TASK.md
|
|
305
406
|
if (!taskContent) {
|
|
407
|
+
// 优先检测研究上下文
|
|
408
|
+
const { context: researchContext, report: researchReport } = await loadResearchContext(options.researchContext, basePath, omPath);
|
|
409
|
+
if (researchContext) {
|
|
410
|
+
// 基于研究上下文构建 ParsedTask
|
|
411
|
+
const parsedTitle = researchContext.topic || researchContext.domain || '未命名任务';
|
|
412
|
+
const researchGoals = researchContext.goals || [];
|
|
413
|
+
if (!options.json) {
|
|
414
|
+
console.log(`\n🔬 检测到研究领域: ${researchContext.domain}`);
|
|
415
|
+
console.log(` 研究目标: ${researchGoals.length > 0 ? researchGoals.join(', ') : '待拆分'}`);
|
|
416
|
+
console.log('\n🔍 基于研究结果解析任务...');
|
|
417
|
+
}
|
|
418
|
+
const parsedTask = {
|
|
419
|
+
title: parsedTitle,
|
|
420
|
+
description: researchGoals.length > 0 ? researchGoals.join('\n') : '',
|
|
421
|
+
goals: researchGoals,
|
|
422
|
+
goalTypes: researchGoals.map(() => 'development'),
|
|
423
|
+
constraints: researchContext.constraints || [],
|
|
424
|
+
deliverables: researchContext.deliverables || [],
|
|
425
|
+
rawContent: researchReport || ''
|
|
426
|
+
};
|
|
427
|
+
if (!options.json) {
|
|
428
|
+
console.log(`\n📋 任务: ${parsedTask.title}`);
|
|
429
|
+
console.log(` 目标: ${parsedTask.goals.join(', ') || '(待 AI 补充)'}`);
|
|
430
|
+
console.log('\n🔧 拆解任务...');
|
|
431
|
+
}
|
|
432
|
+
let qualityConfig;
|
|
433
|
+
if (options.quality) {
|
|
434
|
+
const qualityLevel = options.quality.toLowerCase();
|
|
435
|
+
if (['strict', 'balanced', 'fast'].includes(qualityLevel)) {
|
|
436
|
+
qualityConfig = index_js_1.QUALITY_PRESETS[qualityLevel];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
const planner = new task_planner_js_1.TaskPlanner();
|
|
440
|
+
const subTasks = planner.breakdown(parsedTask, {}, qualityConfig, researchReport || undefined);
|
|
441
|
+
await finalizeAndOutput(subTasks, options, stateManager, state, qualityConfig, parsedTask.title);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
// 无研究上下文,回退到 TASK.md
|
|
306
445
|
const defaultPath = path.join(process.cwd(), 'TASK.md');
|
|
307
446
|
try {
|
|
308
447
|
taskContent = await fs.readFile(defaultPath, 'utf-8');
|
|
@@ -355,6 +494,13 @@ async function handleAutoParse(input, options, stateManager, state) {
|
|
|
355
494
|
}
|
|
356
495
|
const planner = new task_planner_js_1.TaskPlanner();
|
|
357
496
|
const subTasks = planner.breakdown(parsedTask, {}, qualityConfig);
|
|
497
|
+
await finalizeAndOutput(subTasks, options, stateManager, state, qualityConfig, parsedTask.title);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* 公共函数:创建任务、更新状态、处理审批、输出结果
|
|
501
|
+
* 被 handleTasksJson 和 handleAutoParse 共用
|
|
502
|
+
*/
|
|
503
|
+
async function finalizeAndOutput(subTasks, options, stateManager, state, qualityConfig, taskTitle) {
|
|
358
504
|
// 创建任务,并建立 TaskPlanner ID → StateManager ID 的映射
|
|
359
505
|
const taskIdMap = new Map();
|
|
360
506
|
for (const subTask of subTasks) {
|
|
@@ -401,13 +547,7 @@ async function handleAutoParse(input, options, stateManager, state) {
|
|
|
401
547
|
approvalType: 'plan',
|
|
402
548
|
message: '等待计划审批',
|
|
403
549
|
tasks: subTasks.map((t, i) => ({ index: i + 1, title: t.title, priority: t.priority })),
|
|
404
|
-
taskInfo: {
|
|
405
|
-
title: options.title || parsedTask.title,
|
|
406
|
-
description: options.description,
|
|
407
|
-
quality: options.quality,
|
|
408
|
-
techStack: options.techStack,
|
|
409
|
-
docs: options.docs
|
|
410
|
-
}
|
|
550
|
+
taskInfo: { title: taskTitle, quality: options.quality }
|
|
411
551
|
}));
|
|
412
552
|
}
|
|
413
553
|
else {
|
|
@@ -440,13 +580,7 @@ async function handleAutoParse(input, options, stateManager, state) {
|
|
|
440
580
|
pending: allTasks.filter(t => t.status === 'pending').length
|
|
441
581
|
},
|
|
442
582
|
subagentTasks,
|
|
443
|
-
taskInfo: {
|
|
444
|
-
title: options.title || parsedTask.title,
|
|
445
|
-
description: options.description,
|
|
446
|
-
quality: options.quality,
|
|
447
|
-
techStack: options.techStack,
|
|
448
|
-
docs: options.docs
|
|
449
|
-
}
|
|
583
|
+
taskInfo: { title: taskTitle, quality: options.quality }
|
|
450
584
|
}));
|
|
451
585
|
}
|
|
452
586
|
else {
|
|
@@ -38,9 +38,12 @@ exports.stepCommand = void 0;
|
|
|
38
38
|
// 循环状态持久化 - 防止上下文压缩导致循环丢失
|
|
39
39
|
const commander_1 = require("commander");
|
|
40
40
|
const state_manager_js_1 = require("../../storage/state-manager.js");
|
|
41
|
+
const scheduler_js_1 = require("../../orchestrator/scheduler.js");
|
|
42
|
+
const agent_runner_js_1 = require("../../agents/agent-runner.js");
|
|
43
|
+
const approval_manager_js_1 = require("../../orchestrator/approval-manager.js");
|
|
41
44
|
const path = __importStar(require("path"));
|
|
42
45
|
exports.stepCommand = new commander_1.Command('step')
|
|
43
|
-
.description('
|
|
46
|
+
.description('获取下一个待执行任务(自动转换状态并准备 Subagent 配置)')
|
|
44
47
|
.option('--json', '输出 JSON 格式')
|
|
45
48
|
.action(async (options) => {
|
|
46
49
|
const basePath = process.cwd();
|
|
@@ -53,30 +56,15 @@ exports.stepCommand = new commander_1.Command('step')
|
|
|
53
56
|
console.log(JSON.stringify({ status: 'done', message: '所有任务已完成' }));
|
|
54
57
|
return;
|
|
55
58
|
}
|
|
56
|
-
//
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
// 使用 Scheduler 获取下一个可执行任务(处理依赖、并发等)
|
|
60
|
+
const scheduler = new scheduler_js_1.Scheduler(stateManager, {
|
|
61
|
+
maxConcurrentTasks: state.config.maxConcurrentAgents || 3
|
|
62
|
+
});
|
|
63
|
+
const nextTask = await scheduler.getNextTask();
|
|
61
64
|
if (!nextTask) {
|
|
62
|
-
// 所有任务都完成了或被阻塞
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
// 自动更新统计
|
|
66
|
-
const stats = {
|
|
67
|
-
totalTasks: total,
|
|
68
|
-
completed: completed,
|
|
69
|
-
inProgress: allTasks.filter(t => t.status === 'in_progress').length,
|
|
70
|
-
failed: allTasks.filter(t => t.status === 'failed').length,
|
|
71
|
-
pending: allTasks.filter(t => t.status === 'pending').length,
|
|
72
|
-
scheduled: allTasks.filter(t => t.status === 'scheduled').length,
|
|
73
|
-
blocked: allTasks.filter(t => t.status === 'blocked').length,
|
|
74
|
-
waiting: allTasks.filter(t => t.status === 'waiting').length,
|
|
75
|
-
verify: allTasks.filter(t => t.status === 'verify').length,
|
|
76
|
-
accept: allTasks.filter(t => t.status === 'accept').length,
|
|
77
|
-
retry_queue: allTasks.filter(t => t.status === 'retry_queue').length
|
|
78
|
-
};
|
|
79
|
-
const allDone = completed + stats.failed === total;
|
|
65
|
+
// 所有任务都完成了或被阻塞 — 刷新统计后返回
|
|
66
|
+
const stats = await refreshStatistics(stateManager);
|
|
67
|
+
const allDone = stats.completed + stats.failed === stats.totalTasks;
|
|
80
68
|
await stateManager.updateState({
|
|
81
69
|
statistics: stats,
|
|
82
70
|
status: allDone ? 'completed' : 'running',
|
|
@@ -86,30 +74,46 @@ exports.stepCommand = new commander_1.Command('step')
|
|
|
86
74
|
status: allDone ? 'done' : 'blocked',
|
|
87
75
|
statistics: stats,
|
|
88
76
|
message: allDone
|
|
89
|
-
? `所有任务已完成 (${completed}/${
|
|
90
|
-
: `没有可执行任务 (${completed}/${
|
|
77
|
+
? `所有任务已完成 (${stats.completed}/${stats.totalTasks})`
|
|
78
|
+
: `没有可执行任务 (${stats.completed}/${stats.totalTasks} 完成, ${stats.failed} 失败, ${stats.pending} 等待)`
|
|
91
79
|
}));
|
|
92
80
|
return;
|
|
93
81
|
}
|
|
94
|
-
//
|
|
82
|
+
// 转换任务状态为 in_progress(通过 Scheduler)
|
|
83
|
+
await scheduler.markTaskStarted(nextTask.id);
|
|
84
|
+
// 准备 Subagent 任务配置(包含完整的 prompt)
|
|
85
|
+
const approvalManager = new approval_manager_js_1.ApprovalManager(stateManager);
|
|
86
|
+
const agentRunner = new agent_runner_js_1.AgentRunner(stateManager, approvalManager, {
|
|
87
|
+
maxConcurrent: state.config.maxConcurrentAgents || 3,
|
|
88
|
+
taskTimeout: state.config.timeout * 1000 || 120000
|
|
89
|
+
});
|
|
90
|
+
const subagentTask = await agentRunner.prepareSubagentTask(nextTask);
|
|
91
|
+
// 重新读取统计信息
|
|
92
|
+
const stats = await refreshStatistics(stateManager);
|
|
95
93
|
const result = {
|
|
96
94
|
status: 'next',
|
|
97
95
|
task: {
|
|
98
96
|
id: nextTask.id,
|
|
99
97
|
title: nextTask.title,
|
|
100
98
|
description: nextTask.description,
|
|
101
|
-
status:
|
|
99
|
+
status: 'in_progress',
|
|
102
100
|
assignedAgent: nextTask.assignedAgent,
|
|
103
101
|
dependencies: nextTask.dependencies,
|
|
104
102
|
acceptanceCriteria: nextTask.acceptanceCriteria
|
|
105
103
|
},
|
|
104
|
+
// 完整的 Subagent 配置(可直接用于 Agent 工具调用)
|
|
105
|
+
subagent: {
|
|
106
|
+
subagent_type: subagentTask.subagent_type,
|
|
107
|
+
description: subagentTask.description,
|
|
108
|
+
prompt: subagentTask.prompt,
|
|
109
|
+
isolation: subagentTask.isolation,
|
|
110
|
+
timeout: subagentTask.timeout
|
|
111
|
+
},
|
|
106
112
|
statistics: {
|
|
107
|
-
total:
|
|
108
|
-
completed:
|
|
109
|
-
remaining:
|
|
110
|
-
|
|
111
|
-
t.status === 'in_progress').length,
|
|
112
|
-
failed: allTasks.filter(t => t.status === 'failed').length
|
|
113
|
+
total: stats.totalTasks,
|
|
114
|
+
completed: stats.completed,
|
|
115
|
+
remaining: stats.pending + stats.inProgress + stats.scheduled + stats.verify + stats.accept,
|
|
116
|
+
failed: stats.failed
|
|
113
117
|
}
|
|
114
118
|
};
|
|
115
119
|
if (options.json) {
|
|
@@ -117,6 +121,29 @@ exports.stepCommand = new commander_1.Command('step')
|
|
|
117
121
|
}
|
|
118
122
|
else {
|
|
119
123
|
console.log(`📌 下一个任务: ${nextTask.id} - ${nextTask.title}`);
|
|
120
|
-
console.log(`
|
|
124
|
+
console.log(` 状态已转换为 in_progress`);
|
|
125
|
+
console.log(` Agent 类型: ${nextTask.assignedAgent}`);
|
|
126
|
+
console.log(` 进度: ${stats.completed}/${stats.totalTasks} 完成, ${result.statistics.remaining} 剩余`);
|
|
121
127
|
}
|
|
122
128
|
});
|
|
129
|
+
/**
|
|
130
|
+
* 从当前任务状态重新计算统计信息(保证准确性)
|
|
131
|
+
*/
|
|
132
|
+
async function refreshStatistics(stateManager) {
|
|
133
|
+
const tasks = await stateManager.listTasks();
|
|
134
|
+
const stats = {
|
|
135
|
+
totalTasks: tasks.length,
|
|
136
|
+
completed: tasks.filter(t => t.status === 'completed').length,
|
|
137
|
+
inProgress: tasks.filter(t => t.status === 'in_progress').length,
|
|
138
|
+
failed: tasks.filter(t => t.status === 'failed').length,
|
|
139
|
+
pending: tasks.filter(t => t.status === 'pending').length,
|
|
140
|
+
scheduled: tasks.filter(t => t.status === 'scheduled').length,
|
|
141
|
+
blocked: tasks.filter(t => t.status === 'blocked').length,
|
|
142
|
+
waiting: tasks.filter(t => t.status === 'waiting').length,
|
|
143
|
+
verify: tasks.filter(t => t.status === 'verify').length,
|
|
144
|
+
accept: tasks.filter(t => t.status === 'accept').length,
|
|
145
|
+
retry_queue: tasks.filter(t => t.status === 'retry_queue').length
|
|
146
|
+
};
|
|
147
|
+
await stateManager.updateState({ statistics: stats });
|
|
148
|
+
return stats;
|
|
149
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Task } from '../types/index.js';
|
|
1
|
+
import type { Task, TaskPriority, AgentType } from '../types/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* AI Reviewer 验收报告
|
|
4
4
|
*/
|
|
@@ -24,6 +24,18 @@ export interface ReviewIssue {
|
|
|
24
24
|
description: string;
|
|
25
25
|
suggestion?: string;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Review 修复任务配置
|
|
29
|
+
*/
|
|
30
|
+
export interface ReviewFixTask {
|
|
31
|
+
taskId: string;
|
|
32
|
+
title: string;
|
|
33
|
+
description: string;
|
|
34
|
+
priority: TaskPriority;
|
|
35
|
+
assignedAgent: AgentType;
|
|
36
|
+
dependencies: string[];
|
|
37
|
+
timeout: number;
|
|
38
|
+
}
|
|
27
39
|
/**
|
|
28
40
|
* AIReviewer - AI 代码审查器
|
|
29
41
|
*
|
|
@@ -42,9 +54,25 @@ export declare class AIReviewer {
|
|
|
42
54
|
/**
|
|
43
55
|
* 解析 AI Review 结果
|
|
44
56
|
*/
|
|
45
|
-
parseReviewResult(output: string): ReviewReport;
|
|
57
|
+
parseReviewResult(taskId: string, output: string): ReviewReport;
|
|
46
58
|
/**
|
|
47
59
|
* 生成验收阶段提示词 (包含 AI Review)
|
|
48
60
|
*/
|
|
49
61
|
buildAcceptPrompt(task: Task): string;
|
|
62
|
+
/**
|
|
63
|
+
* 根据 Review 报告生成修复任务
|
|
64
|
+
*
|
|
65
|
+
* 对每个 critical/major 问题生成独立的修复任务,
|
|
66
|
+
* 这些任务会被加入执行队列并自动执行。
|
|
67
|
+
*
|
|
68
|
+
* @param originalTask 被审查的原始任务
|
|
69
|
+
* @param report Review 报告
|
|
70
|
+
* @param reviewTaskId 审查任务本身的 ID(用于依赖)
|
|
71
|
+
* @returns 修复任务列表
|
|
72
|
+
*/
|
|
73
|
+
generateFixTasks(originalTask: Task, report: ReviewReport, reviewTaskId: string): ReviewFixTask[];
|
|
74
|
+
/**
|
|
75
|
+
* 判断 Review 报告是否需要自动修复
|
|
76
|
+
*/
|
|
77
|
+
needsAutoFix(report: ReviewReport): boolean;
|
|
50
78
|
}
|