openmatrix 0.1.40 → 0.1.41
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/brainstorm.js +89 -202
- package/dist/cli/commands/start.js +74 -29
- package/dist/orchestrator/task-parser.d.ts +8 -0
- package/dist/orchestrator/task-parser.js +22 -9
- package/dist/orchestrator/task-planner.d.ts +34 -1
- package/dist/orchestrator/task-planner.js +144 -4
- package/package.json +1 -1
|
@@ -79,6 +79,12 @@ exports.brainstormCommand = new commander_1.Command('brainstorm')
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
await fs.writeFile(brainstormPath, JSON.stringify(session, null, 2));
|
|
82
|
+
// 构建下一个步骤的命令
|
|
83
|
+
const brainstormAnswers = JSON.stringify({
|
|
84
|
+
answers: session.answers,
|
|
85
|
+
insights: session.insights,
|
|
86
|
+
designNotes: session.designNotes
|
|
87
|
+
});
|
|
82
88
|
if (options.json) {
|
|
83
89
|
console.log(JSON.stringify({
|
|
84
90
|
status: 'ready_to_start',
|
|
@@ -88,7 +94,23 @@ exports.brainstormCommand = new commander_1.Command('brainstorm')
|
|
|
88
94
|
answers: session.answers,
|
|
89
95
|
insights: session.insights,
|
|
90
96
|
designNotes: session.designNotes,
|
|
91
|
-
|
|
97
|
+
// 关键:告诉 Agent 下一步必须执行什么
|
|
98
|
+
nextStep: {
|
|
99
|
+
required: true,
|
|
100
|
+
description: '必须调用此命令开始任务执行',
|
|
101
|
+
command: 'openmatrix',
|
|
102
|
+
args: ['start', session.taskInput.slice(0, 100)],
|
|
103
|
+
options: {
|
|
104
|
+
'brainstorm-answers': brainstormAnswers
|
|
105
|
+
},
|
|
106
|
+
hint: '使用 /om:start 开始执行任务'
|
|
107
|
+
},
|
|
108
|
+
// 强制执行标记 - Agent 必须执行 nextStep
|
|
109
|
+
_meta: {
|
|
110
|
+
requiresAction: true,
|
|
111
|
+
actionType: 'cli_command',
|
|
112
|
+
blocking: true
|
|
113
|
+
}
|
|
92
114
|
}));
|
|
93
115
|
}
|
|
94
116
|
else {
|
|
@@ -216,206 +238,71 @@ exports.brainstormCommand = new commander_1.Command('brainstorm')
|
|
|
216
238
|
});
|
|
217
239
|
/**
|
|
218
240
|
* 根据任务内容生成头脑风暴问题
|
|
241
|
+
*
|
|
242
|
+
* 基于"底层逻辑"方法论,不使用固定模板
|
|
219
243
|
*/
|
|
220
244
|
function generateBrainstormQuestions(taskContent, taskTitle) {
|
|
221
245
|
const questions = [];
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
why: '核心功能决定开发优先级和工作量'
|
|
283
|
-
});
|
|
284
|
-
questions.push({
|
|
285
|
-
id: 'art_style',
|
|
286
|
-
question: '美术风格是什么?素材如何获取?',
|
|
287
|
-
header: '美术',
|
|
288
|
-
options: [
|
|
289
|
-
{ label: '程序化生成', description: '用代码绘制图形' },
|
|
290
|
-
{ label: '像素风', description: '复古像素艺术' },
|
|
291
|
-
{ label: '使用免费素材', description: 'OpenGameArt 等资源' },
|
|
292
|
-
{ label: '需要美术设计', description: '需要定制美术资源' }
|
|
293
|
-
],
|
|
294
|
-
multiSelect: false,
|
|
295
|
-
why: '美术方案影响开发成本和视觉效果'
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
else if (isAPI) {
|
|
299
|
-
// ========== API 专用问题 ==========
|
|
300
|
-
questions.push({
|
|
301
|
-
id: 'api_consumer',
|
|
302
|
-
question: 'API 的调用方是谁?',
|
|
303
|
-
header: '调用方',
|
|
304
|
-
options: [
|
|
305
|
-
{ label: '前端应用', description: 'Web/Mobile 前端调用' },
|
|
306
|
-
{ label: '第三方开发者', description: '公开 API 供外部使用' },
|
|
307
|
-
{ label: '内部服务', description: '微服务间调用' },
|
|
308
|
-
{ label: '混合场景', description: '多种调用方' }
|
|
309
|
-
],
|
|
310
|
-
multiSelect: false,
|
|
311
|
-
why: '调用方决定 API 设计和文档要求'
|
|
312
|
-
});
|
|
313
|
-
questions.push({
|
|
314
|
-
id: 'api_auth',
|
|
315
|
-
question: '需要什么样的认证授权?',
|
|
316
|
-
header: '认证',
|
|
317
|
-
options: [
|
|
318
|
-
{ label: '无认证', description: '公开 API,无需认证' },
|
|
319
|
-
{ label: 'API Key', description: '简单的密钥认证' },
|
|
320
|
-
{ label: 'JWT/OAuth', description: '用户身份认证' },
|
|
321
|
-
{ label: '需要调研', description: '认证方案待定' }
|
|
322
|
-
],
|
|
323
|
-
multiSelect: false,
|
|
324
|
-
why: '认证方式影响安全架构'
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
else if (isCLI) {
|
|
328
|
-
// ========== CLI 专用问题 ==========
|
|
329
|
-
questions.push({
|
|
330
|
-
id: 'cli_user',
|
|
331
|
-
question: 'CLI 工具的主要使用者是谁?',
|
|
332
|
-
header: '使用者',
|
|
333
|
-
options: [
|
|
334
|
-
{ label: '开发者', description: '开发流程辅助工具' },
|
|
335
|
-
{ label: '运维人员', description: '部署/运维自动化' },
|
|
336
|
-
{ label: '普通用户', description: '非技术用户也能使用' },
|
|
337
|
-
{ label: 'CI/CD', description: '自动化流程中使用' }
|
|
338
|
-
],
|
|
339
|
-
multiSelect: true,
|
|
340
|
-
why: '使用者决定交互设计和文档要求'
|
|
341
|
-
});
|
|
342
|
-
questions.push({
|
|
343
|
-
id: 'cli_output',
|
|
344
|
-
question: 'CLI 的输出格式要求?',
|
|
345
|
-
header: '输出',
|
|
346
|
-
options: [
|
|
347
|
-
{ label: '人类可读', description: '彩色、表格、友好提示' },
|
|
348
|
-
{ label: 'JSON 机器读', description: '结构化输出,便于解析' },
|
|
349
|
-
{ label: '两种都支持', description: '通过参数切换' },
|
|
350
|
-
{ label: '静默模式', description: '最少输出,仅错误' }
|
|
351
|
-
],
|
|
352
|
-
multiSelect: false,
|
|
353
|
-
why: '输出格式影响用户体验和自动化集成'
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
// ========== 通用问题 ==========
|
|
358
|
-
questions.push({
|
|
359
|
-
id: 'core_objective',
|
|
360
|
-
question: '这个任务的核心目标是什么?想要解决什么问题?',
|
|
361
|
-
header: '核心目标',
|
|
362
|
-
options: [
|
|
363
|
-
{ label: '实现新功能', description: '添加新的功能特性,扩展系统能力' },
|
|
364
|
-
{ label: '修复问题', description: '修复 Bug 或解决已知问题' },
|
|
365
|
-
{ label: '重构优化', description: '改进代码结构、性能或可维护性' },
|
|
366
|
-
{ label: '技术探索', description: '探索新技术方案,验证可行性' }
|
|
367
|
-
],
|
|
368
|
-
multiSelect: false,
|
|
369
|
-
why: '明确核心目标有助于选择正确的实现策略和质量标准'
|
|
370
|
-
});
|
|
371
|
-
questions.push({
|
|
372
|
-
id: 'user_value',
|
|
373
|
-
question: '这个任务为用户带来什么价值?最终用户是谁?',
|
|
374
|
-
header: '用户价值',
|
|
375
|
-
options: [
|
|
376
|
-
{ label: '开发者', description: '主要用户是开发者,需要清晰的 API 和文档' },
|
|
377
|
-
{ label: '终端用户', description: '主要用户是终端用户,需要良好的用户体验' },
|
|
378
|
-
{ label: '运维人员', description: '主要用户是运维,需要稳定性和可观测性' },
|
|
379
|
-
{ label: '内部团队', description: '主要用户是内部团队,需要高效协作支持' }
|
|
380
|
-
],
|
|
381
|
-
multiSelect: false,
|
|
382
|
-
why: '了解目标用户有助于设计合适的接口和交互方式'
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
// ========== 通用问题(所有类型都有) ==========
|
|
386
|
-
// 实现复杂度 - 如果任务内容包含复杂关键词
|
|
387
|
-
if (content.includes('架构') || content.includes('系统') || content.includes('集成') || content.includes('多个')) {
|
|
388
|
-
questions.push({
|
|
389
|
-
id: 'complexity',
|
|
390
|
-
question: '这个任务的实现复杂度如何?需要哪些关键组件?',
|
|
391
|
-
header: '复杂度',
|
|
392
|
-
options: [
|
|
393
|
-
{ label: '简单', description: '单一功能,少量代码修改' },
|
|
394
|
-
{ label: '中等', description: '需要多个组件协作,有依赖关系' },
|
|
395
|
-
{ label: '复杂', description: '涉及架构调整,需要仔细规划' },
|
|
396
|
-
{ label: '非常复杂', description: '大型重构或新系统,需要分阶段实施' }
|
|
397
|
-
],
|
|
398
|
-
multiSelect: false,
|
|
399
|
-
why: '复杂度评估有助于决定是否需要分阶段实施和额外的设计审查'
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
// 技术约束 - 如果涉及技术选型
|
|
403
|
-
if (content.includes('技术') || content.includes('框架') || content.includes('库') || content.includes('技术栈')) {
|
|
404
|
-
questions.push({
|
|
405
|
-
id: 'tech_constraints',
|
|
406
|
-
question: '有哪些技术约束或偏好?需要使用/避免什么技术?',
|
|
407
|
-
header: '技术约束',
|
|
408
|
-
options: [
|
|
409
|
-
{ label: '使用现有技术栈', description: '复用项目已有的技术选择' },
|
|
410
|
-
{ label: '引入新技术', description: '需要引入新的库或框架' },
|
|
411
|
-
{ label: '保持技术中立', description: '不引入新依赖,使用原生方案' },
|
|
412
|
-
{ label: '需要技术调研', description: '技术选型不确定,需要先调研' }
|
|
413
|
-
],
|
|
414
|
-
multiSelect: false,
|
|
415
|
-
why: '技术约束影响实现方案和后续维护成本'
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
// 风险评估
|
|
246
|
+
// ========== 第一层:价值逻辑 ==========
|
|
247
|
+
// 1. 核心目标
|
|
248
|
+
questions.push({
|
|
249
|
+
id: 'core_objective',
|
|
250
|
+
question: '这个任务的核心目标是什么?想要解决什么问题?',
|
|
251
|
+
header: '核心目标',
|
|
252
|
+
options: [
|
|
253
|
+
{ label: '创建新系统', description: '从零开始构建新的系统或应用' },
|
|
254
|
+
{ label: '添加新功能', description: '在现有系统上增加新能力' },
|
|
255
|
+
{ label: '修复问题', description: '解决 Bug 或已知问题' },
|
|
256
|
+
{ label: '优化改进', description: '提升性能、体验或代码质量' }
|
|
257
|
+
],
|
|
258
|
+
multiSelect: false,
|
|
259
|
+
why: '明确核心目标有助于选择正确的实现策略'
|
|
260
|
+
});
|
|
261
|
+
// 2. 用户价值
|
|
262
|
+
questions.push({
|
|
263
|
+
id: 'user_value',
|
|
264
|
+
question: '这个任务为谁创造价值?最终用户是谁?',
|
|
265
|
+
header: '用户价值',
|
|
266
|
+
options: [
|
|
267
|
+
{ label: '终端用户', description: '产品的最终使用者,需要良好体验' },
|
|
268
|
+
{ label: '开发者', description: '技术用户,需要清晰的 API 和文档' },
|
|
269
|
+
{ label: '团队内部', description: '内部工具,追求效率' },
|
|
270
|
+
{ label: '业务方', description: '满足业务需求,关注指标' }
|
|
271
|
+
],
|
|
272
|
+
multiSelect: false,
|
|
273
|
+
why: '了解目标用户有助于设计合适的方案'
|
|
274
|
+
});
|
|
275
|
+
// ========== 第二层:架构逻辑 ==========
|
|
276
|
+
// 3. 复杂度评估
|
|
277
|
+
questions.push({
|
|
278
|
+
id: 'complexity',
|
|
279
|
+
question: '这个任务的复杂度如何?涉及哪些关键组件?',
|
|
280
|
+
header: '复杂度',
|
|
281
|
+
options: [
|
|
282
|
+
{ label: '简单', description: '单一功能,少量代码' },
|
|
283
|
+
{ label: '中等', description: '多个组件协作,有依赖关系' },
|
|
284
|
+
{ label: '复杂', description: '涉及架构设计,需要仔细规划' },
|
|
285
|
+
{ label: '非常复杂', description: '大型系统,需要分阶段实施' }
|
|
286
|
+
],
|
|
287
|
+
multiSelect: false,
|
|
288
|
+
why: '复杂度决定是否需要分阶段实施'
|
|
289
|
+
});
|
|
290
|
+
// 4. 技术约束
|
|
291
|
+
questions.push({
|
|
292
|
+
id: 'tech_constraints',
|
|
293
|
+
question: '有哪些技术约束或偏好?',
|
|
294
|
+
header: '技术约束',
|
|
295
|
+
options: [
|
|
296
|
+
{ label: '使用现有技术栈', description: '不引入新依赖' },
|
|
297
|
+
{ label: '可以引入新技术', description: '允许引入新的库或框架' },
|
|
298
|
+
{ label: '需要技术调研', description: '技术选型待定,先调研' },
|
|
299
|
+
{ label: '无特殊约束', description: '自由选择最合适的方案' }
|
|
300
|
+
],
|
|
301
|
+
multiSelect: false,
|
|
302
|
+
why: '技术约束影响实现方案选择'
|
|
303
|
+
});
|
|
304
|
+
// ========== 第三层:风险逻辑 ==========
|
|
305
|
+
// 5. 风险识别
|
|
419
306
|
questions.push({
|
|
420
307
|
id: 'risks',
|
|
421
308
|
question: '这个任务可能面临哪些风险或挑战?',
|
|
@@ -429,7 +316,7 @@ function generateBrainstormQuestions(taskContent, taskTitle) {
|
|
|
429
316
|
multiSelect: true,
|
|
430
317
|
why: '识别风险有助于提前规划应对策略'
|
|
431
318
|
});
|
|
432
|
-
// 验收标准
|
|
319
|
+
// 6. 验收标准
|
|
433
320
|
questions.push({
|
|
434
321
|
id: 'acceptance',
|
|
435
322
|
question: '如何判断任务完成?有哪些验收标准?',
|
|
@@ -443,15 +330,15 @@ function generateBrainstormQuestions(taskContent, taskTitle) {
|
|
|
443
330
|
multiSelect: true,
|
|
444
331
|
why: '明确的验收标准有助于判断任务完成度'
|
|
445
332
|
});
|
|
446
|
-
//
|
|
333
|
+
// 7. 优先级
|
|
447
334
|
questions.push({
|
|
448
335
|
id: 'priority',
|
|
449
|
-
question: '
|
|
336
|
+
question: '这个任务的优先级如何?实施策略是什么?',
|
|
450
337
|
header: '优先级',
|
|
451
338
|
options: [
|
|
452
339
|
{ label: '高优先级', description: '需要尽快完成,影响其他工作' },
|
|
453
340
|
{ label: '中优先级', description: '计划内任务,按正常节奏推进' },
|
|
454
|
-
{ label: '低优先级', description: '
|
|
341
|
+
{ label: '低优先级', description: '可延后处理' },
|
|
455
342
|
{ label: '需要 MVP', description: '先实现最小可用版本,再迭代' }
|
|
456
343
|
],
|
|
457
344
|
multiSelect: false,
|
|
@@ -145,65 +145,110 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
145
145
|
return;
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
+
else if (brainstormSession.status === 'ready_to_start' || brainstormSession.status === 'completed') {
|
|
149
|
+
// 头脑风暴已完成,自动使用保存的答案(如果没有显式传递)
|
|
150
|
+
if (!options.brainstormAnswers && brainstormSession.answers) {
|
|
151
|
+
// 自动使用保存的 brainstorm 答案
|
|
152
|
+
options.brainstormAnswers = JSON.stringify({
|
|
153
|
+
answers: brainstormSession.answers,
|
|
154
|
+
insights: brainstormSession.insights || [],
|
|
155
|
+
designNotes: brainstormSession.designNotes || []
|
|
156
|
+
});
|
|
157
|
+
if (!options.json) {
|
|
158
|
+
console.log('📋 自动使用已完成的头脑风暴结果');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
148
162
|
}
|
|
149
163
|
catch {
|
|
150
164
|
// brainstorm 文件不存在,继续执行
|
|
151
165
|
}
|
|
152
166
|
// 构建任务内容
|
|
153
167
|
let taskContent = input;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const description = options.description || '';
|
|
158
|
-
const techStack = options.techStack ? `\n\n技术栈: ${options.techStack}` : '';
|
|
159
|
-
const docs = options.docs ? `\n文档要求: ${options.docs}` : '';
|
|
160
|
-
taskContent = `# ${title}\n\n${description}${techStack}${docs}`;
|
|
161
|
-
}
|
|
162
|
-
// 如果没有任务内容,尝试读取默认文件
|
|
163
|
-
if (!taskContent) {
|
|
164
|
-
const defaultPath = path.join(basePath, 'TASK.md');
|
|
168
|
+
let titleOverride = options.title; // 保存标题覆盖选项
|
|
169
|
+
// 如果输入是文件路径,先读取文件内容
|
|
170
|
+
if (taskContent && taskContent.endsWith('.md')) {
|
|
165
171
|
try {
|
|
166
|
-
taskContent = await fs.readFile(
|
|
172
|
+
taskContent = await fs.readFile(taskContent, 'utf-8');
|
|
167
173
|
if (!options.json) {
|
|
168
|
-
console.log(`📄 读取任务文件: ${
|
|
174
|
+
console.log(`📄 读取任务文件: ${input}`);
|
|
169
175
|
}
|
|
170
176
|
}
|
|
171
177
|
catch {
|
|
172
178
|
if (options.json) {
|
|
173
179
|
console.log(JSON.stringify({
|
|
174
180
|
status: 'error',
|
|
175
|
-
message:
|
|
181
|
+
message: `无法读取文件: ${input}`
|
|
176
182
|
}));
|
|
177
183
|
}
|
|
178
184
|
else {
|
|
179
|
-
console.log(
|
|
180
|
-
console.log(' 用法: openmatrix start <task.md>');
|
|
181
|
-
console.log(' 或创建 TASK.md 文件');
|
|
182
|
-
console.log(' 或使用 --title 和 --description 选项');
|
|
185
|
+
console.log(`❌ 无法读取文件: ${input}`);
|
|
183
186
|
}
|
|
184
187
|
return;
|
|
185
188
|
}
|
|
186
189
|
}
|
|
187
|
-
else if (taskContent
|
|
188
|
-
//
|
|
190
|
+
else if (!taskContent) {
|
|
191
|
+
// 如果没有任务内容,尝试读取默认文件
|
|
192
|
+
const defaultPath = path.join(basePath, 'TASK.md');
|
|
189
193
|
try {
|
|
190
|
-
taskContent = await fs.readFile(
|
|
194
|
+
taskContent = await fs.readFile(defaultPath, 'utf-8');
|
|
191
195
|
if (!options.json) {
|
|
192
|
-
console.log(`📄 读取任务文件: ${
|
|
196
|
+
console.log(`📄 读取任务文件: ${defaultPath}`);
|
|
193
197
|
}
|
|
194
198
|
}
|
|
195
199
|
catch {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
200
|
+
// 如果没有默认文件,使用 --title 和 --description 构建
|
|
201
|
+
if (options.title || options.description) {
|
|
202
|
+
const title = options.title || '未命名任务';
|
|
203
|
+
const description = options.description || '';
|
|
204
|
+
const techStack = options.techStack ? `\n\n技术栈: ${options.techStack}` : '';
|
|
205
|
+
const docs = options.docs ? `\n文档要求: ${options.docs}` : '';
|
|
206
|
+
taskContent = `# ${title}\n\n${description}${techStack}${docs}`;
|
|
207
|
+
titleOverride = undefined; // 内容已包含标题,不需要覆盖
|
|
201
208
|
}
|
|
202
209
|
else {
|
|
203
|
-
|
|
210
|
+
if (options.json) {
|
|
211
|
+
console.log(JSON.stringify({
|
|
212
|
+
status: 'error',
|
|
213
|
+
message: '请提供任务文件路径或描述'
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
console.log('❌ 请提供任务文件路径或描述');
|
|
218
|
+
console.log(' 用法: openmatrix start <task.md>');
|
|
219
|
+
console.log(' 或创建 TASK.md 文件');
|
|
220
|
+
console.log(' 或使用 --title 和 --description 选项');
|
|
221
|
+
}
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else if (!taskContent.endsWith('.md') && !taskContent.includes('\n')) {
|
|
227
|
+
// 如果输入是单行文本,可能是文件路径或简短描述
|
|
228
|
+
// 检查是否是文件
|
|
229
|
+
const possiblePath = path.join(basePath, taskContent);
|
|
230
|
+
try {
|
|
231
|
+
taskContent = await fs.readFile(possiblePath, 'utf-8');
|
|
232
|
+
if (!options.json) {
|
|
233
|
+
console.log(`📄 读取任务文件: ${possiblePath}`);
|
|
204
234
|
}
|
|
205
|
-
return;
|
|
206
235
|
}
|
|
236
|
+
catch {
|
|
237
|
+
// 不是文件,作为描述处理
|
|
238
|
+
if (options.title || options.description) {
|
|
239
|
+
const title = options.title || '未命名任务';
|
|
240
|
+
const description = options.description || taskContent;
|
|
241
|
+
const techStack = options.techStack ? `\n\n技术栈: ${options.techStack}` : '';
|
|
242
|
+
const docs = options.docs ? `\n文档要求: ${options.docs}` : '';
|
|
243
|
+
taskContent = `# ${title}\n\n${description}${techStack}${docs}`;
|
|
244
|
+
titleOverride = undefined;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// 如果有标题覆盖,更新任务内容中的标题
|
|
249
|
+
if (titleOverride && taskContent) {
|
|
250
|
+
// 替换第一个 # 标题
|
|
251
|
+
taskContent = taskContent.replace(/^#\s+.+$/m, `# ${titleOverride}`);
|
|
207
252
|
}
|
|
208
253
|
// 解析任务
|
|
209
254
|
if (!options.json) {
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { ParsedTask } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* TaskParser - 任务解析器
|
|
4
|
+
*
|
|
5
|
+
* 职责:解析用户输入的任务描述,提取结构化信息
|
|
6
|
+
*
|
|
7
|
+
* 注意:TaskParser 只负责格式解析,不做业务逻辑判断
|
|
8
|
+
* 任务拆解的智能逻辑应该由 TaskPlanner (调用 AI) 来完成
|
|
9
|
+
*/
|
|
2
10
|
export declare class TaskParser {
|
|
3
11
|
parse(content: string): ParsedTask;
|
|
4
12
|
private extractTitle;
|
|
@@ -1,6 +1,14 @@
|
|
|
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
|
+
*/
|
|
4
12
|
class TaskParser {
|
|
5
13
|
parse(content) {
|
|
6
14
|
const title = this.extractTitle(content);
|
|
@@ -18,38 +26,43 @@ class TaskParser {
|
|
|
18
26
|
};
|
|
19
27
|
}
|
|
20
28
|
extractTitle(content) {
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
// 1. 从第一个 # 标题提取
|
|
30
|
+
const headerMatch = content.match(/^#\s+(.+)$/m);
|
|
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';
|
|
23
37
|
}
|
|
24
38
|
extractDescription(content) {
|
|
25
|
-
//
|
|
39
|
+
// 移除标题和 sections,获取第一段
|
|
26
40
|
let desc = content
|
|
27
41
|
.replace(/^#\s+.+$/m, '')
|
|
28
42
|
.replace(/^##\s+.+$/gm, '')
|
|
29
43
|
.replace(/^- .+$/gm, '')
|
|
30
44
|
.trim();
|
|
31
|
-
//
|
|
45
|
+
// 获取第一个非空行
|
|
32
46
|
const lines = desc.split('\n').filter(l => l.trim());
|
|
33
47
|
return lines.length > 0 ? lines[0].trim() : '';
|
|
34
48
|
}
|
|
35
49
|
extractSection(content, sectionName) {
|
|
36
|
-
// Split content into lines and find the section
|
|
37
50
|
const lines = content.split('\n');
|
|
38
51
|
const items = [];
|
|
39
52
|
let inSection = false;
|
|
40
53
|
for (const line of lines) {
|
|
41
|
-
//
|
|
54
|
+
// 检查是否进入目标 section
|
|
42
55
|
if (line.match(new RegExp(`^##\\s+${sectionName}\\s*$`))) {
|
|
43
56
|
inSection = true;
|
|
44
57
|
continue;
|
|
45
58
|
}
|
|
46
|
-
//
|
|
59
|
+
// 检查是否进入其他 section
|
|
47
60
|
if (line.match(/^##\s+/)) {
|
|
48
61
|
if (inSection)
|
|
49
|
-
break;
|
|
62
|
+
break;
|
|
50
63
|
continue;
|
|
51
64
|
}
|
|
52
|
-
//
|
|
65
|
+
// 提取列表项
|
|
53
66
|
if (inSection) {
|
|
54
67
|
const itemMatch = line.match(/^-\s+(.+)$/);
|
|
55
68
|
if (itemMatch) {
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { ParsedTask } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* TaskBreakdown - 计划/任务拆解单元
|
|
4
|
+
*
|
|
5
|
+
* 术语区分:
|
|
6
|
+
* - Plan (计划): planner agent 输出,宏观层面,包含 ARCHITECTURE.md 和 PLAN.md
|
|
7
|
+
* - Task (任务): 具体执行单元,2-4 小时,有明确的验收标准
|
|
8
|
+
*
|
|
9
|
+
* 当 assignedAgent 为 'planner' 时,这是一个规划任务,输出是 Plan
|
|
10
|
+
* 当 assignedAgent 为其他时,这是具体的执行 Task
|
|
11
|
+
*/
|
|
2
12
|
export interface TaskBreakdown {
|
|
3
13
|
taskId: string;
|
|
4
14
|
title: string;
|
|
@@ -42,13 +52,36 @@ export declare class TaskPlanner {
|
|
|
42
52
|
/**
|
|
43
53
|
* Break down a parsed task into sub-tasks
|
|
44
54
|
*
|
|
45
|
-
*
|
|
55
|
+
* 智能拆解策略:
|
|
56
|
+
* 1. 如果有明确的 goals (来自 ## 目标 section),按目标拆解
|
|
57
|
+
* 2. 如果没有 goals 但有复杂内容,创建规划任务交给 AI 拆解
|
|
58
|
+
* 3. 如果是简单任务,直接创建单个开发任务
|
|
46
59
|
*/
|
|
47
60
|
breakdown(parsedTask: ParsedTask, answers: Record<string, string>): TaskBreakdown[];
|
|
61
|
+
/**
|
|
62
|
+
* 判断是否为复杂任务
|
|
63
|
+
*/
|
|
64
|
+
private isComplexTask;
|
|
65
|
+
/**
|
|
66
|
+
* 创建规划任务 (由 AI 智能拆解)
|
|
67
|
+
*/
|
|
68
|
+
private createPlanningTask;
|
|
69
|
+
/**
|
|
70
|
+
* 创建单个开发任务
|
|
71
|
+
*/
|
|
72
|
+
private createSingleTask;
|
|
73
|
+
/**
|
|
74
|
+
* 按目标拆解任务 (原有逻辑)
|
|
75
|
+
*/
|
|
76
|
+
private breakdownByGoals;
|
|
48
77
|
/**
|
|
49
78
|
* 提取用户上下文
|
|
50
79
|
*/
|
|
51
80
|
private extractUserContext;
|
|
81
|
+
/**
|
|
82
|
+
* 格式化用户上下文为字符串
|
|
83
|
+
*/
|
|
84
|
+
private formatUserContext;
|
|
52
85
|
/**
|
|
53
86
|
* 解析数组类型的回答
|
|
54
87
|
*/
|
|
@@ -25,12 +25,129 @@ class TaskPlanner {
|
|
|
25
25
|
/**
|
|
26
26
|
* Break down a parsed task into sub-tasks
|
|
27
27
|
*
|
|
28
|
-
*
|
|
28
|
+
* 智能拆解策略:
|
|
29
|
+
* 1. 如果有明确的 goals (来自 ## 目标 section),按目标拆解
|
|
30
|
+
* 2. 如果没有 goals 但有复杂内容,创建规划任务交给 AI 拆解
|
|
31
|
+
* 3. 如果是简单任务,直接创建单个开发任务
|
|
29
32
|
*/
|
|
30
33
|
breakdown(parsedTask, answers) {
|
|
31
34
|
const breakdowns = [];
|
|
32
|
-
const seenTitles = new Set();
|
|
33
35
|
const userContext = this.extractUserContext(answers);
|
|
36
|
+
// 判断任务复杂度
|
|
37
|
+
const isComplexTask = this.isComplexTask(parsedTask);
|
|
38
|
+
// 情况1: 没有 goals,但有复杂内容 → 创建规划任务
|
|
39
|
+
if (parsedTask.goals.length === 0 && isComplexTask) {
|
|
40
|
+
return this.createPlanningTask(parsedTask, userContext, answers);
|
|
41
|
+
}
|
|
42
|
+
// 情况2: 没有 goals,但有简单内容 → 创建单个开发任务
|
|
43
|
+
if (parsedTask.goals.length === 0 && parsedTask.description) {
|
|
44
|
+
return this.createSingleTask(parsedTask, userContext, answers);
|
|
45
|
+
}
|
|
46
|
+
// 情况3: 没有 goals,也没有内容 → 返回空(无效任务)
|
|
47
|
+
if (parsedTask.goals.length === 0) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
// 情况4: 有明确的 goals → 按目标拆解
|
|
51
|
+
return this.breakdownByGoals(parsedTask, userContext, answers);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 判断是否为复杂任务
|
|
55
|
+
*/
|
|
56
|
+
isComplexTask(parsedTask) {
|
|
57
|
+
const content = parsedTask.rawContent || '';
|
|
58
|
+
// 检查是否有多个 markdown sections
|
|
59
|
+
const sections = content.match(/^#{1,2}\s+/gm) || [];
|
|
60
|
+
if (sections.length >= 3)
|
|
61
|
+
return true;
|
|
62
|
+
// 检查内容长度
|
|
63
|
+
if (content.length > 500)
|
|
64
|
+
return true;
|
|
65
|
+
// 检查是否有列表项
|
|
66
|
+
const listItems = content.match(/^-\s+/gm) || [];
|
|
67
|
+
if (listItems.length >= 5)
|
|
68
|
+
return true;
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 创建规划任务 (由 AI 智能拆解)
|
|
73
|
+
*/
|
|
74
|
+
createPlanningTask(parsedTask, userContext, answers) {
|
|
75
|
+
const planningTaskId = this.generateTaskId();
|
|
76
|
+
return [{
|
|
77
|
+
taskId: planningTaskId,
|
|
78
|
+
title: `${parsedTask.title} - 需求分析与架构设计`,
|
|
79
|
+
description: `# 需求分析与架构设计
|
|
80
|
+
|
|
81
|
+
## 任务背景
|
|
82
|
+
${parsedTask.description || parsedTask.title}
|
|
83
|
+
|
|
84
|
+
## 原始需求
|
|
85
|
+
\`\`\`
|
|
86
|
+
${parsedTask.rawContent}
|
|
87
|
+
\`\`\`
|
|
88
|
+
|
|
89
|
+
## 用户上下文
|
|
90
|
+
${this.formatUserContext(userContext, answers)}
|
|
91
|
+
|
|
92
|
+
## 你的任务
|
|
93
|
+
作为架构师,请:
|
|
94
|
+
1. 深入分析需求,理解核心功能
|
|
95
|
+
2. 设计系统架构
|
|
96
|
+
3. 将需求拆解为可执行的子任务
|
|
97
|
+
4. 为每个子任务定义验收标准
|
|
98
|
+
|
|
99
|
+
## 输出要求
|
|
100
|
+
- 架构设计文档 (ARCHITECTURE.md)
|
|
101
|
+
- 执行计划 (PLAN.md) - 宏观层面的计划,包含子任务清单
|
|
102
|
+
- 每个子计划包含: 标题、描述、优先级、依赖关系、验收标准
|
|
103
|
+
|
|
104
|
+
## 注意事项
|
|
105
|
+
- 任务拆解要细粒度,每个任务应该在 2-4 小时内完成
|
|
106
|
+
- 任务之间要有清晰的依赖关系
|
|
107
|
+
- 考虑技术栈和约束条件`,
|
|
108
|
+
priority: 'P0',
|
|
109
|
+
dependencies: [],
|
|
110
|
+
estimatedComplexity: 'high',
|
|
111
|
+
assignedAgent: 'planner',
|
|
112
|
+
phase: 'design',
|
|
113
|
+
acceptanceCriteria: [
|
|
114
|
+
'架构设计文档完整',
|
|
115
|
+
'执行计划 (PLAN.md) 清晰',
|
|
116
|
+
'每个子计划有明确的验收标准',
|
|
117
|
+
'依赖关系明确',
|
|
118
|
+
'技术方案可行'
|
|
119
|
+
]
|
|
120
|
+
}];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* 创建单个开发任务
|
|
124
|
+
*/
|
|
125
|
+
createSingleTask(parsedTask, userContext, answers) {
|
|
126
|
+
const taskId = this.generateTaskId();
|
|
127
|
+
return [{
|
|
128
|
+
taskId,
|
|
129
|
+
title: parsedTask.title,
|
|
130
|
+
description: this.buildTaskDescription(parsedTask.description || parsedTask.title, userContext, answers),
|
|
131
|
+
priority: 'P1',
|
|
132
|
+
dependencies: [],
|
|
133
|
+
estimatedComplexity: 'medium',
|
|
134
|
+
assignedAgent: 'coder',
|
|
135
|
+
phase: 'develop',
|
|
136
|
+
acceptanceCriteria: [
|
|
137
|
+
'功能实现完整',
|
|
138
|
+
'代码可编译',
|
|
139
|
+
'无严重 bug',
|
|
140
|
+
'代码符合规范'
|
|
141
|
+
]
|
|
142
|
+
}];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 按目标拆解任务 (原有逻辑)
|
|
146
|
+
*/
|
|
147
|
+
breakdownByGoals(parsedTask, userContext, answers) {
|
|
148
|
+
const breakdowns = [];
|
|
149
|
+
const seenTitles = new Set();
|
|
150
|
+
const extractedContext = this.extractUserContext(answers);
|
|
34
151
|
// 0. 设计阶段任务
|
|
35
152
|
if (parsedTask.goals.length > 1) {
|
|
36
153
|
breakdowns.push({
|
|
@@ -39,10 +156,10 @@ class TaskPlanner {
|
|
|
39
156
|
description: `分析需求,设计整体架构,规划实现方案
|
|
40
157
|
|
|
41
158
|
## 用户需求
|
|
42
|
-
${
|
|
159
|
+
${extractedContext.objective || '未指定'}
|
|
43
160
|
|
|
44
161
|
## 技术栈
|
|
45
|
-
${
|
|
162
|
+
${extractedContext.techStack?.join(', ') || '未指定'}
|
|
46
163
|
|
|
47
164
|
## 输出
|
|
48
165
|
- 架构设计文档
|
|
@@ -237,6 +354,29 @@ ${userContext.documentationLevel}
|
|
|
237
354
|
additionalContext: answers
|
|
238
355
|
};
|
|
239
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* 格式化用户上下文为字符串
|
|
359
|
+
*/
|
|
360
|
+
formatUserContext(userContext, answers) {
|
|
361
|
+
const parts = [];
|
|
362
|
+
if (answers['brainstormAnswers']) {
|
|
363
|
+
parts.push('### 头脑风暴答案');
|
|
364
|
+
parts.push(answers['brainstormAnswers']);
|
|
365
|
+
}
|
|
366
|
+
if (userContext.objective) {
|
|
367
|
+
parts.push(`### 目标: ${userContext.objective}`);
|
|
368
|
+
}
|
|
369
|
+
if (userContext.techStack && userContext.techStack.length > 0) {
|
|
370
|
+
parts.push(`### 技术栈: ${userContext.techStack.join(', ')}`);
|
|
371
|
+
}
|
|
372
|
+
if (answers['insights']) {
|
|
373
|
+
parts.push(`### 洞察: ${answers['insights']}`);
|
|
374
|
+
}
|
|
375
|
+
if (answers['designNotes']) {
|
|
376
|
+
parts.push(`### 设计笔记: ${answers['designNotes']}`);
|
|
377
|
+
}
|
|
378
|
+
return parts.join('\n\n') || '无额外上下文';
|
|
379
|
+
}
|
|
240
380
|
/**
|
|
241
381
|
* 解析数组类型的回答
|
|
242
382
|
*/
|