openmatrix 0.2.3 → 0.2.5
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/complete.js +4 -2
- package/dist/orchestrator/executor.js +15 -3
- package/dist/orchestrator/git-commit-manager.js +24 -8
- package/dist/orchestrator/meeting-manager.d.ts +1 -27
- package/dist/orchestrator/meeting-manager.js +1 -0
- package/dist/orchestrator/retry-manager.d.ts +20 -1
- package/dist/orchestrator/retry-manager.js +52 -3
- package/dist/orchestrator/scheduler.d.ts +19 -0
- package/dist/orchestrator/scheduler.js +51 -1
- package/dist/orchestrator/task-planner.d.ts +20 -0
- package/dist/orchestrator/task-planner.js +195 -7
- package/dist/storage/file-store.d.ts +13 -0
- package/dist/storage/file-store.js +41 -4
- package/dist/storage/state-manager.d.ts +4 -4
- package/dist/storage/state-manager.js +8 -5
- package/dist/types/index.d.ts +20 -0
- package/dist/utils/error-handler.d.ts +46 -0
- package/dist/utils/error-handler.js +123 -0
- package/dist/utils/logger.d.ts +33 -0
- package/dist/utils/logger.js +110 -2
- package/package.json +6 -1
- package/skills/approve.md +1 -1
- package/skills/auto.md +20 -13
- package/skills/brainstorm.md +3 -1
- package/skills/meeting.md +19 -30
- package/skills/om.md +20 -29
- package/skills/openmatrix.md +17 -0
- package/skills/report.md +1 -1
- package/skills/research.md +3 -1
- package/skills/resume.md +1 -1
- package/skills/retry.md +1 -1
- package/skills/start.md +51 -12
- package/skills/status.md +1 -1
|
@@ -108,8 +108,173 @@ class TaskPlanner {
|
|
|
108
108
|
this.analyzeModuleDependencies(modules, planText);
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
+
// 模式3: 英文格式 "N modules: A, B, C" 或 "N components: A, B, C"
|
|
112
|
+
if (modules.length === 0) {
|
|
113
|
+
const enModuleMatch = planText.match(/(\d+)\s*(?:modules?|components?|domains?|features?)\s*[::]\s*(.+)/i);
|
|
114
|
+
if (enModuleMatch) {
|
|
115
|
+
const moduleNames = enModuleMatch[2]
|
|
116
|
+
.split(/[,,、]/)
|
|
117
|
+
.map(s => s.trim())
|
|
118
|
+
.filter(s => s.length > 0 && s.length < 30);
|
|
119
|
+
for (const modName of moduleNames) {
|
|
120
|
+
modules.push({
|
|
121
|
+
name: modName,
|
|
122
|
+
description: `${modName} module`,
|
|
123
|
+
tables: [],
|
|
124
|
+
type: 'domain',
|
|
125
|
+
dependsOn: [],
|
|
126
|
+
complexity: this.estimateModuleComplexity(modName, [])
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
this.analyzeModuleDependencies(modules, planText);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// 模式4: Markdown 标题下的列表 "## Modules\n- A\n- B" 或 "## Architecture\n1. A\n2. B"
|
|
133
|
+
if (modules.length === 0) {
|
|
134
|
+
const mdPatterns = [
|
|
135
|
+
/##\s*(?:模块|Modules?|架构|Architecture|领域|Domains?|功能|Features?)[\s\S]*?((?:[-*\d]+\.\s*.+(?:\n|$))+)/i,
|
|
136
|
+
/##\s*(?:实现|Implementation|开发|Development)[\s\S]*?((?:[-*\d]+\.\s*.+(?:\n|$))+)/i,
|
|
137
|
+
];
|
|
138
|
+
for (const pattern of mdPatterns) {
|
|
139
|
+
const mdMatch = planText.match(pattern);
|
|
140
|
+
if (mdMatch) {
|
|
141
|
+
const lines = mdMatch[1].split('\n').filter(l => l.trim());
|
|
142
|
+
for (const line of lines) {
|
|
143
|
+
// 匹配 "- Name" 或 "1. Name" 或 "1. Name: Description"
|
|
144
|
+
const itemMatch = line.match(/[-*\d]+\.\s*(.+?)(?:[::\s](.*))?/);
|
|
145
|
+
if (itemMatch) {
|
|
146
|
+
const name = itemMatch[1].trim();
|
|
147
|
+
const desc = itemMatch[2]?.trim() || `${name} module`;
|
|
148
|
+
if (name.length > 0 && name.length < 30 && !modules.some(m => m.name === name)) {
|
|
149
|
+
modules.push({
|
|
150
|
+
name,
|
|
151
|
+
description: desc,
|
|
152
|
+
tables: [],
|
|
153
|
+
type: 'domain',
|
|
154
|
+
dependsOn: [],
|
|
155
|
+
complexity: this.estimateModuleComplexity(name, [])
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (modules.length > 0) {
|
|
162
|
+
this.analyzeModuleDependencies(modules, planText);
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
111
167
|
return { modules, techStack, raw: planText };
|
|
112
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* 从 plan 中提取结构化信息(用于 fallback 时注入任务描述)
|
|
171
|
+
* 即使无法解析模块,也能保留关键技术信息
|
|
172
|
+
*/
|
|
173
|
+
extractPlanMetadata(plan) {
|
|
174
|
+
const techStack = [];
|
|
175
|
+
const interfaces = [];
|
|
176
|
+
const dataModels = [];
|
|
177
|
+
const keyDecisions = [];
|
|
178
|
+
// 1. 提取技术栈
|
|
179
|
+
const techMatch = plan.match(/(?:技术栈|Technology|Tech Stack)[\s\S]*?((?:[-*]\s*.+(?:\n|$))+)/i);
|
|
180
|
+
if (techMatch) {
|
|
181
|
+
techMatch[1].split('\n').forEach(line => {
|
|
182
|
+
const trimmed = line.replace(/^[-*]\s*/, '').trim();
|
|
183
|
+
if (trimmed && trimmed.length < 50)
|
|
184
|
+
techStack.push(trimmed);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
// 2. 提取接口/API
|
|
188
|
+
const interfacePatterns = [
|
|
189
|
+
/(?:接口|API|Endpoints?|接口定义)[\s\S]*?((?:[-*]\s*.+(?:\n|$))+)/i,
|
|
190
|
+
/(?:##\s*(?:接口|API|Endpoints?))[\s\S]*?((?:[-*\d]+\.\s*.+(?:\n|$))+)/i,
|
|
191
|
+
];
|
|
192
|
+
for (const pattern of interfacePatterns) {
|
|
193
|
+
const match = plan.match(pattern);
|
|
194
|
+
if (match) {
|
|
195
|
+
match[1].split('\n').forEach(line => {
|
|
196
|
+
const trimmed = line.replace(/^[-*\d]+\.\s*/, '').trim();
|
|
197
|
+
if (trimmed && trimmed.length < 80 && !interfaces.includes(trimmed)) {
|
|
198
|
+
interfaces.push(trimmed);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
if (interfaces.length > 0)
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// 3. 提取数据模型/表
|
|
206
|
+
const dataPatterns = [
|
|
207
|
+
/(?:数据模型|Database|Tables?|Schema|实体|模型)[\s\S]*?((?:[-*]\s*.+(?:\n|$))+)/i,
|
|
208
|
+
/(?:##\s*(?:数据模型|Database|实体))[\s\S]*?((?:[-*\d]+\.\s*.+(?:\n|$))+)/i,
|
|
209
|
+
];
|
|
210
|
+
for (const pattern of dataPatterns) {
|
|
211
|
+
const match = plan.match(pattern);
|
|
212
|
+
if (match) {
|
|
213
|
+
match[1].split('\n').forEach(line => {
|
|
214
|
+
const trimmed = line.replace(/^[-*\d]+\.\s*/, '').trim();
|
|
215
|
+
if (trimmed && trimmed.length < 50 && !dataModels.includes(trimmed)) {
|
|
216
|
+
dataModels.push(trimmed);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
if (dataModels.length > 0)
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// 4. 提取关键决策
|
|
224
|
+
const decisionPatterns = [
|
|
225
|
+
/(?:关键决策|Key Decisions|重要决策|决策点)[\s\S]*?((?:[-*]\s*.+(?:\n|$))+)/i,
|
|
226
|
+
/(?:##\s*(?:关键决策|Key Decisions|决策))[\s\S]*?((?:[-*\d]+\.\s*.+(?:\n|$))+)/i,
|
|
227
|
+
];
|
|
228
|
+
for (const pattern of decisionPatterns) {
|
|
229
|
+
const match = plan.match(pattern);
|
|
230
|
+
if (match) {
|
|
231
|
+
match[1].split('\n').forEach(line => {
|
|
232
|
+
const trimmed = line.replace(/^[-*\d]+\.\s*/, '').trim();
|
|
233
|
+
if (trimmed && trimmed.length < 100 && !keyDecisions.includes(trimmed)) {
|
|
234
|
+
keyDecisions.push(trimmed);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
if (keyDecisions.length > 0)
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return { techStack, interfaces, dataModels, keyDecisions };
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* 从 goals 推断模块结构(当 plan 无法解析模块时使用)
|
|
245
|
+
*/
|
|
246
|
+
inferModulesFromGoals(parsedTask) {
|
|
247
|
+
const modules = [];
|
|
248
|
+
if (!parsedTask.goalTypes || parsedTask.goals.length < 3) {
|
|
249
|
+
return modules;
|
|
250
|
+
}
|
|
251
|
+
for (let i = 0; i < parsedTask.goals.length; i++) {
|
|
252
|
+
const goal = parsedTask.goals[i];
|
|
253
|
+
const goalType = parsedTask.goalTypes[i];
|
|
254
|
+
// 只将 development 类型的 goal 作为模块
|
|
255
|
+
if (goalType === 'development') {
|
|
256
|
+
modules.push({
|
|
257
|
+
name: goal.replace(/^(实现|开发|完成|Develop|Implement)\s*:?\s*/i, '').trim(),
|
|
258
|
+
description: goal,
|
|
259
|
+
tables: [],
|
|
260
|
+
type: 'domain',
|
|
261
|
+
dependsOn: [],
|
|
262
|
+
complexity: this.estimateComplexity(goal)
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// 添加基础依赖:第一个模块无依赖,后续模块依赖前一模块
|
|
267
|
+
for (let i = 1; i < modules.length; i++) {
|
|
268
|
+
// 默认并行,除非 goal 描述中明确提到依赖
|
|
269
|
+
if (parsedTask.goals[i].toLowerCase().includes('基于') ||
|
|
270
|
+
parsedTask.goals[i].toLowerCase().includes('依赖') ||
|
|
271
|
+
parsedTask.goals[i].toLowerCase().includes('需要') ||
|
|
272
|
+
parsedTask.goals[i].toLowerCase().includes('after')) {
|
|
273
|
+
modules[i].dependsOn.push(modules[i - 1].name);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return modules;
|
|
277
|
+
}
|
|
113
278
|
/**
|
|
114
279
|
* 分析模块间依赖关系
|
|
115
280
|
*
|
|
@@ -190,15 +355,26 @@ class TaskPlanner {
|
|
|
190
355
|
return 'medium';
|
|
191
356
|
}
|
|
192
357
|
breakdown(parsedTask, answers, qualityConfig, plan) {
|
|
193
|
-
// 如果提供了 plan
|
|
358
|
+
// 如果提供了 plan,尝试解析模块
|
|
194
359
|
if (plan) {
|
|
195
360
|
const parsed = this.parsePlan(plan);
|
|
196
361
|
if (parsed.modules.length > 0) {
|
|
197
362
|
return this.breakdownByModules(parsedTask, answers, qualityConfig, parsed, plan);
|
|
198
363
|
}
|
|
364
|
+
// plan 无法解析模块时,尝试从 goals 推断
|
|
365
|
+
const inferredModules = this.inferModulesFromGoals(parsedTask);
|
|
366
|
+
if (inferredModules.length > 0) {
|
|
367
|
+
const inferredPlan = {
|
|
368
|
+
modules: inferredModules,
|
|
369
|
+
techStack: parsed.techStack,
|
|
370
|
+
raw: plan
|
|
371
|
+
};
|
|
372
|
+
return this.breakdownByModules(parsedTask, answers, qualityConfig, inferredPlan, plan);
|
|
373
|
+
}
|
|
199
374
|
}
|
|
200
|
-
// fallback:
|
|
201
|
-
|
|
375
|
+
// fallback: 按目标拆分,但保留 plan metadata
|
|
376
|
+
const planMetadata = plan ? this.extractPlanMetadata(plan) : undefined;
|
|
377
|
+
return this.breakdownByGoals(parsedTask, answers, qualityConfig, plan, planMetadata);
|
|
202
378
|
}
|
|
203
379
|
/**
|
|
204
380
|
* 基于 plan 解析出的模块做细粒度任务拆分
|
|
@@ -502,7 +678,7 @@ ${userContext.documentationLevel}
|
|
|
502
678
|
/**
|
|
503
679
|
* 按目标拆分的传统方式(fallback)
|
|
504
680
|
*/
|
|
505
|
-
breakdownByGoals(parsedTask, answers, qualityConfig, plan) {
|
|
681
|
+
breakdownByGoals(parsedTask, answers, qualityConfig, plan, planMetadata) {
|
|
506
682
|
const breakdowns = [];
|
|
507
683
|
const seenTitles = new Set();
|
|
508
684
|
const userContext = this.extractUserContext(answers);
|
|
@@ -567,7 +743,7 @@ ${globalContext}
|
|
|
567
743
|
breakdowns.push({
|
|
568
744
|
taskId: devTaskId,
|
|
569
745
|
title: `实现: ${goal}`,
|
|
570
|
-
description: this.buildTaskDescription(goal, globalContext),
|
|
746
|
+
description: this.buildTaskDescription(goal, globalContext, planMetadata),
|
|
571
747
|
priority: this.determinePriority(i),
|
|
572
748
|
dependencies: deps,
|
|
573
749
|
estimatedComplexity: this.estimateComplexity(goal),
|
|
@@ -909,12 +1085,24 @@ ${userContext.documentationLevel}
|
|
|
909
1085
|
}
|
|
910
1086
|
return parts.join('\n');
|
|
911
1087
|
}
|
|
912
|
-
buildTaskDescription(goal, globalContext) {
|
|
913
|
-
|
|
1088
|
+
buildTaskDescription(goal, globalContext, planMetadata) {
|
|
1089
|
+
let desc = `## 当前子任务目标\n${goal}\n\n${globalContext}`;
|
|
1090
|
+
// 注入 plan 提取的关键信息
|
|
1091
|
+
if (planMetadata && planMetadata.interfaces.length > 0) {
|
|
1092
|
+
desc += `\n\n## 相关接口/API\n${planMetadata.interfaces.slice(0, 10).map(i => `- ${i}`).join('\n')}`;
|
|
1093
|
+
}
|
|
1094
|
+
if (planMetadata && planMetadata.dataModels.length > 0) {
|
|
1095
|
+
desc += `\n\n## 相关数据模型\n${planMetadata.dataModels.slice(0, 10).map(d => `- ${d}`).join('\n')}`;
|
|
1096
|
+
}
|
|
1097
|
+
if (planMetadata && planMetadata.keyDecisions.length > 0) {
|
|
1098
|
+
desc += `\n\n## 关键决策参考\n${planMetadata.keyDecisions.slice(0, 5).map(d => `- ${d}`).join('\n')}`;
|
|
1099
|
+
}
|
|
1100
|
+
desc += `\n\n## 输出要求
|
|
914
1101
|
- 完成功能实现
|
|
915
1102
|
- 代码可编译
|
|
916
1103
|
- 遵循项目规范
|
|
917
1104
|
- 添加必要注释`;
|
|
1105
|
+
return desc;
|
|
918
1106
|
}
|
|
919
1107
|
buildTestDescription(goal, devTaskId, coverageTarget, globalContext) {
|
|
920
1108
|
return `## 测试目标
|
|
@@ -22,4 +22,17 @@ export declare class FileStore {
|
|
|
22
22
|
exists(path: string): Promise<boolean>;
|
|
23
23
|
listFiles(dir: string): Promise<string[]>;
|
|
24
24
|
listDirs(dir: string): Promise<string[]>;
|
|
25
|
+
/**
|
|
26
|
+
* 同步写入 JSON 文件
|
|
27
|
+
*/
|
|
28
|
+
writeJsonSync<T>(path: string, data: T): void;
|
|
29
|
+
/**
|
|
30
|
+
* 同步读取 JSON 文件
|
|
31
|
+
* @returns 文件内容,如果文件不存在则返回 null
|
|
32
|
+
*/
|
|
33
|
+
readJsonSync<T>(path: string): T | null;
|
|
34
|
+
/**
|
|
35
|
+
* 同步检查文件是否存在
|
|
36
|
+
*/
|
|
37
|
+
existsSync(path: string): boolean;
|
|
25
38
|
}
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FileStore = void 0;
|
|
4
4
|
const promises_1 = require("fs/promises");
|
|
5
|
-
const path_1 = require("path");
|
|
6
5
|
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const fs_2 = require("fs");
|
|
8
|
+
const error_handler_js_1 = require("../utils/error-handler.js");
|
|
7
9
|
class FileStore {
|
|
8
10
|
basePath;
|
|
9
11
|
constructor(basePath) {
|
|
@@ -71,7 +73,7 @@ class FileStore {
|
|
|
71
73
|
async exists(path) {
|
|
72
74
|
const fullPath = (0, path_1.join)(this.basePath, path);
|
|
73
75
|
try {
|
|
74
|
-
await (0, promises_1.access)(fullPath,
|
|
76
|
+
await (0, promises_1.access)(fullPath, fs_2.constants.F_OK);
|
|
75
77
|
return true;
|
|
76
78
|
}
|
|
77
79
|
catch {
|
|
@@ -86,7 +88,8 @@ class FileStore {
|
|
|
86
88
|
.filter((f) => f.isFile())
|
|
87
89
|
.map((f) => f.name);
|
|
88
90
|
}
|
|
89
|
-
catch {
|
|
91
|
+
catch (error) {
|
|
92
|
+
(0, error_handler_js_1.logError)(error, { operation: 'listFiles', file: fullPath });
|
|
90
93
|
return [];
|
|
91
94
|
}
|
|
92
95
|
}
|
|
@@ -98,9 +101,43 @@ class FileStore {
|
|
|
98
101
|
.filter((f) => f.isDirectory())
|
|
99
102
|
.map((f) => f.name);
|
|
100
103
|
}
|
|
101
|
-
catch {
|
|
104
|
+
catch (error) {
|
|
105
|
+
(0, error_handler_js_1.logError)(error, { operation: 'listDirs', file: fullPath });
|
|
102
106
|
return [];
|
|
103
107
|
}
|
|
104
108
|
}
|
|
109
|
+
// ============ 同步方法(用于构造器等场景)============
|
|
110
|
+
/**
|
|
111
|
+
* 同步写入 JSON 文件
|
|
112
|
+
*/
|
|
113
|
+
writeJsonSync(path, data) {
|
|
114
|
+
const fullPath = (0, path_1.join)(this.basePath, path);
|
|
115
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(fullPath), { recursive: true });
|
|
116
|
+
(0, fs_1.writeFileSync)(fullPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 同步读取 JSON 文件
|
|
120
|
+
* @returns 文件内容,如果文件不存在则返回 null
|
|
121
|
+
*/
|
|
122
|
+
readJsonSync(path) {
|
|
123
|
+
const fullPath = (0, path_1.join)(this.basePath, path);
|
|
124
|
+
try {
|
|
125
|
+
const content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
|
|
126
|
+
return JSON.parse(content);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (error.code === 'ENOENT' || error.code === 'EISDIR') {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* 同步检查文件是否存在
|
|
137
|
+
*/
|
|
138
|
+
existsSync(path) {
|
|
139
|
+
const fullPath = (0, path_1.join)(this.basePath, path);
|
|
140
|
+
return (0, fs_1.existsSync)(fullPath);
|
|
141
|
+
}
|
|
105
142
|
}
|
|
106
143
|
exports.FileStore = FileStore;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GlobalState, Task, Approval, ApprovalStatus } from '../types/index.js';
|
|
1
|
+
import type { GlobalState, Task, Approval, ApprovalStatus, Meeting, MeetingStatus } from '../types/index.js';
|
|
2
2
|
export declare class StateManager {
|
|
3
3
|
private store;
|
|
4
4
|
private stateCache;
|
|
@@ -55,7 +55,7 @@ export declare class StateManager {
|
|
|
55
55
|
updateApproval(approval: Approval): Promise<void>;
|
|
56
56
|
getApprovalsByStatus(status: ApprovalStatus): Promise<Approval[]>;
|
|
57
57
|
getAllApprovals(): Promise<Approval[]>;
|
|
58
|
-
saveMeeting(meeting:
|
|
59
|
-
getMeeting(meetingId: string): Promise<
|
|
60
|
-
getMeetingsByStatus(status:
|
|
58
|
+
saveMeeting(meeting: Meeting): Promise<void>;
|
|
59
|
+
getMeeting(meetingId: string): Promise<Meeting | null>;
|
|
60
|
+
getMeetingsByStatus(status: MeetingStatus): Promise<Meeting[]>;
|
|
61
61
|
}
|
|
@@ -6,6 +6,7 @@ const promises_1 = require("fs/promises");
|
|
|
6
6
|
const path_1 = require("path");
|
|
7
7
|
const DEFAULT_CONFIG = {
|
|
8
8
|
timeout: 120,
|
|
9
|
+
taskTimeout: 600000, // 10 分钟(毫秒)
|
|
9
10
|
maxRetries: 3,
|
|
10
11
|
approvalPoints: ['plan', 'merge'],
|
|
11
12
|
maxConcurrentAgents: 3,
|
|
@@ -164,6 +165,9 @@ class StateManager {
|
|
|
164
165
|
}
|
|
165
166
|
async createTask(input) {
|
|
166
167
|
return this.withFileLock(async () => {
|
|
168
|
+
// 先从磁盘读取最新状态,避免使用过期缓存
|
|
169
|
+
const latestState = await this.store.readJson('state.json') ?? await this.getState();
|
|
170
|
+
this.stateCache = latestState;
|
|
167
171
|
const taskId = this.generateTaskId();
|
|
168
172
|
const now = new Date().toISOString();
|
|
169
173
|
const task = {
|
|
@@ -189,13 +193,12 @@ class StateManager {
|
|
|
189
193
|
// Create artifacts subdirectory
|
|
190
194
|
await this.store.ensureDir(`tasks/${taskId}/artifacts`);
|
|
191
195
|
// Update statistics
|
|
192
|
-
const state = await this.getState();
|
|
193
196
|
const newState = {
|
|
194
|
-
...
|
|
197
|
+
...latestState,
|
|
195
198
|
statistics: {
|
|
196
|
-
...
|
|
197
|
-
totalTasks:
|
|
198
|
-
pending:
|
|
199
|
+
...latestState.statistics,
|
|
200
|
+
totalTasks: latestState.statistics.totalTasks + 1,
|
|
201
|
+
pending: latestState.statistics.pending + 1
|
|
199
202
|
}
|
|
200
203
|
};
|
|
201
204
|
await this.store.writeJson('state.json', newState);
|
package/dist/types/index.d.ts
CHANGED
|
@@ -94,7 +94,10 @@ export interface GlobalState {
|
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
96
|
export interface AppConfig {
|
|
97
|
+
/** 单个任务超时时间(秒) */
|
|
97
98
|
timeout: number;
|
|
99
|
+
/** 任务执行超时时间(毫秒) */
|
|
100
|
+
taskTimeout?: number;
|
|
98
101
|
maxRetries: number;
|
|
99
102
|
approvalPoints: ('plan' | 'merge' | 'deploy')[];
|
|
100
103
|
maxConcurrentAgents: number;
|
|
@@ -200,6 +203,23 @@ export interface ApprovalOption {
|
|
|
200
203
|
key: string;
|
|
201
204
|
label: string;
|
|
202
205
|
}
|
|
206
|
+
export type MeetingStatus = 'pending' | 'in_progress' | 'resolved' | 'cancelled';
|
|
207
|
+
export type MeetingType = 'blocking' | 'decision' | 'review' | 'planning';
|
|
208
|
+
export interface Meeting {
|
|
209
|
+
id: string;
|
|
210
|
+
type: MeetingType;
|
|
211
|
+
status: MeetingStatus;
|
|
212
|
+
taskId: string;
|
|
213
|
+
title: string;
|
|
214
|
+
description: string;
|
|
215
|
+
blockingReason?: string;
|
|
216
|
+
impactScope: string[];
|
|
217
|
+
participants: string[];
|
|
218
|
+
resolution?: string;
|
|
219
|
+
createdAt: string;
|
|
220
|
+
startedAt?: string;
|
|
221
|
+
resolvedAt?: string;
|
|
222
|
+
}
|
|
203
223
|
export interface ParsedTask {
|
|
204
224
|
title: string;
|
|
205
225
|
description: string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 错误上下文信息
|
|
3
|
+
*/
|
|
4
|
+
export interface ErrorContext {
|
|
5
|
+
operation: string;
|
|
6
|
+
file?: string;
|
|
7
|
+
taskId?: string;
|
|
8
|
+
metadata?: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 将任意错误转换为 Error 对象并保留 stack trace
|
|
12
|
+
*/
|
|
13
|
+
export declare function toError(error: unknown): Error;
|
|
14
|
+
/**
|
|
15
|
+
* 记录错误日志(保留 stack trace)
|
|
16
|
+
*/
|
|
17
|
+
export declare function logError(error: unknown, context: ErrorContext): void;
|
|
18
|
+
/**
|
|
19
|
+
* 包装错误,添加上下文信息到错误消息
|
|
20
|
+
*/
|
|
21
|
+
export declare function wrapError(error: unknown, context: ErrorContext): Error;
|
|
22
|
+
/**
|
|
23
|
+
* 包装并抛出错误
|
|
24
|
+
*/
|
|
25
|
+
export declare function throwWrapped(error: unknown, context: ErrorContext): never;
|
|
26
|
+
/**
|
|
27
|
+
* 安全执行函数,捕获并记录错误但不抛出
|
|
28
|
+
* @returns 返回值或 null(如果发生错误)
|
|
29
|
+
*/
|
|
30
|
+
export declare function safeExecute<T>(fn: () => Promise<T>, context: ErrorContext): Promise<T | null>;
|
|
31
|
+
/**
|
|
32
|
+
* 安全执行同步函数
|
|
33
|
+
*/
|
|
34
|
+
export declare function safeExecuteSync<T>(fn: () => T, context: ErrorContext): T | null;
|
|
35
|
+
/**
|
|
36
|
+
* 执行函数,捕获错误并包装后抛出
|
|
37
|
+
*/
|
|
38
|
+
export declare function executeWithError<T>(fn: () => Promise<T>, context: ErrorContext): Promise<T>;
|
|
39
|
+
/**
|
|
40
|
+
* 判断错误是否可恢复(如网络超时、临时文件锁等)
|
|
41
|
+
*/
|
|
42
|
+
export declare function isRecoverableError(error: unknown): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* 判断错误是否为文件系统错误
|
|
45
|
+
*/
|
|
46
|
+
export declare function isFileSystemError(error: unknown): boolean;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toError = toError;
|
|
4
|
+
exports.logError = logError;
|
|
5
|
+
exports.wrapError = wrapError;
|
|
6
|
+
exports.throwWrapped = throwWrapped;
|
|
7
|
+
exports.safeExecute = safeExecute;
|
|
8
|
+
exports.safeExecuteSync = safeExecuteSync;
|
|
9
|
+
exports.executeWithError = executeWithError;
|
|
10
|
+
exports.isRecoverableError = isRecoverableError;
|
|
11
|
+
exports.isFileSystemError = isFileSystemError;
|
|
12
|
+
// src/utils/error-handler.ts
|
|
13
|
+
const logger_js_1 = require("./logger.js");
|
|
14
|
+
/**
|
|
15
|
+
* 将任意错误转换为 Error 对象并保留 stack trace
|
|
16
|
+
*/
|
|
17
|
+
function toError(error) {
|
|
18
|
+
if (error instanceof Error) {
|
|
19
|
+
return error;
|
|
20
|
+
}
|
|
21
|
+
if (typeof error === 'string') {
|
|
22
|
+
return new Error(error);
|
|
23
|
+
}
|
|
24
|
+
return new Error(JSON.stringify(error));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 记录错误日志(保留 stack trace)
|
|
28
|
+
*/
|
|
29
|
+
function logError(error, context) {
|
|
30
|
+
const err = toError(error);
|
|
31
|
+
const logger = (0, logger_js_1.getLogger)();
|
|
32
|
+
logger.error({
|
|
33
|
+
message: err.message,
|
|
34
|
+
stack: err.stack,
|
|
35
|
+
operation: context.operation,
|
|
36
|
+
file: context.file,
|
|
37
|
+
taskId: context.taskId,
|
|
38
|
+
...context.metadata,
|
|
39
|
+
timestamp: new Date().toISOString()
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 包装错误,添加上下文信息到错误消息
|
|
44
|
+
*/
|
|
45
|
+
function wrapError(error, context) {
|
|
46
|
+
const err = toError(error);
|
|
47
|
+
const wrapped = new Error(`${context.operation}: ${err.message}`);
|
|
48
|
+
wrapped.stack = err.stack;
|
|
49
|
+
wrapped.cause = err;
|
|
50
|
+
return wrapped;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 包装并抛出错误
|
|
54
|
+
*/
|
|
55
|
+
function throwWrapped(error, context) {
|
|
56
|
+
throw wrapError(error, context);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 安全执行函数,捕获并记录错误但不抛出
|
|
60
|
+
* @returns 返回值或 null(如果发生错误)
|
|
61
|
+
*/
|
|
62
|
+
async function safeExecute(fn, context) {
|
|
63
|
+
try {
|
|
64
|
+
return await fn();
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
logError(error, context);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 安全执行同步函数
|
|
73
|
+
*/
|
|
74
|
+
function safeExecuteSync(fn, context) {
|
|
75
|
+
try {
|
|
76
|
+
return fn();
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
logError(error, context);
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 执行函数,捕获错误并包装后抛出
|
|
85
|
+
*/
|
|
86
|
+
async function executeWithError(fn, context) {
|
|
87
|
+
try {
|
|
88
|
+
return await fn();
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
throwWrapped(error, context);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 判断错误是否可恢复(如网络超时、临时文件锁等)
|
|
96
|
+
*/
|
|
97
|
+
function isRecoverableError(error) {
|
|
98
|
+
const err = toError(error);
|
|
99
|
+
const recoverablePatterns = [
|
|
100
|
+
/timeout/i,
|
|
101
|
+
/temporarily unavailable/i,
|
|
102
|
+
/ECONNRESET/i,
|
|
103
|
+
/ENOTFOUND/i,
|
|
104
|
+
/lock/i,
|
|
105
|
+
/retry/i
|
|
106
|
+
];
|
|
107
|
+
return recoverablePatterns.some(p => p.test(err.message));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 判断错误是否为文件系统错误
|
|
111
|
+
*/
|
|
112
|
+
function isFileSystemError(error) {
|
|
113
|
+
const err = toError(error);
|
|
114
|
+
const fsPatterns = [
|
|
115
|
+
/ENOENT/i,
|
|
116
|
+
/EACCES/i,
|
|
117
|
+
/EPERM/i,
|
|
118
|
+
/EISDIR/i,
|
|
119
|
+
/ENOTDIR/i,
|
|
120
|
+
/EMFILE/i
|
|
121
|
+
];
|
|
122
|
+
return fsPatterns.some(p => p.test(err.message));
|
|
123
|
+
}
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -3,7 +3,25 @@ export interface LoggerOptions {
|
|
|
3
3
|
level?: string;
|
|
4
4
|
logDir?: string;
|
|
5
5
|
console?: boolean;
|
|
6
|
+
runId?: string;
|
|
6
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* 结构化日志格式
|
|
10
|
+
*/
|
|
11
|
+
export interface StructuredLog {
|
|
12
|
+
level: 'info' | 'warn' | 'error' | 'debug';
|
|
13
|
+
runId?: string;
|
|
14
|
+
taskId?: string;
|
|
15
|
+
operation: string;
|
|
16
|
+
message: string;
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
timestamp: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 持久化日志到文件(追加模式)
|
|
22
|
+
* 用于关键操作的审计追踪
|
|
23
|
+
*/
|
|
24
|
+
export declare function persistLog(log: StructuredLog, omPath: string): void;
|
|
7
25
|
/**
|
|
8
26
|
* 创建或获取默认 Logger
|
|
9
27
|
*/
|
|
@@ -16,6 +34,10 @@ export declare function getLogger(): winston.Logger;
|
|
|
16
34
|
* 设置默认 logger
|
|
17
35
|
*/
|
|
18
36
|
export declare function setLogger(logger: winston.Logger): void;
|
|
37
|
+
/**
|
|
38
|
+
* 使用 runId 初始化 logger
|
|
39
|
+
*/
|
|
40
|
+
export declare function initLoggerWithRunId(runId: string, omPath?: string): void;
|
|
19
41
|
/**
|
|
20
42
|
* 便捷方法: 直接记录日志
|
|
21
43
|
*/
|
|
@@ -24,11 +46,17 @@ export declare const logger: {
|
|
|
24
46
|
error: (message: string, meta?: any) => winston.Logger;
|
|
25
47
|
warn: (message: string, meta?: any) => winston.Logger;
|
|
26
48
|
debug: (message: string, meta?: any) => winston.Logger;
|
|
49
|
+
structured: {
|
|
50
|
+
info: (operation: string, message: string, meta?: Record<string, unknown>, omPath?: string) => void;
|
|
51
|
+
error: (operation: string, message: string, meta?: Record<string, unknown>, omPath?: string) => void;
|
|
52
|
+
warn: (operation: string, message: string, meta?: Record<string, unknown>, omPath?: string) => void;
|
|
53
|
+
};
|
|
27
54
|
task: {
|
|
28
55
|
start: (taskId: string, title: string) => void;
|
|
29
56
|
complete: (taskId: string, duration?: number) => void;
|
|
30
57
|
fail: (taskId: string, error: string) => void;
|
|
31
58
|
retry: (taskId: string, attempt: number) => void;
|
|
59
|
+
timeout: (taskId: string, timeout: number, phase?: string, omPath?: string) => void;
|
|
32
60
|
};
|
|
33
61
|
agent: {
|
|
34
62
|
call: (agentType: string, taskId: string) => void;
|
|
@@ -38,4 +66,9 @@ export declare const logger: {
|
|
|
38
66
|
request: (approvalId: string, type: string) => void;
|
|
39
67
|
decision: (approvalId: string, decision: string) => void;
|
|
40
68
|
};
|
|
69
|
+
orchestration: {
|
|
70
|
+
breakdown: (moduleCount: number, moduleNames: string[], omPath?: string) => void;
|
|
71
|
+
schedule: (taskId: string, dependencies: string[]) => void;
|
|
72
|
+
dependencyResolved: (taskId: string) => void;
|
|
73
|
+
};
|
|
41
74
|
};
|