openmatrix 0.2.24 → 0.2.26
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/README.md +155 -595
- package/dist/agents/agent-runner.d.ts +5 -1
- package/dist/agents/agent-runner.js +53 -3
- package/dist/cli/commands/complete.js +13 -1
- package/dist/cli/commands/start.js +20 -16
- package/dist/orchestrator/task-planner.d.ts +5 -104
- package/dist/orchestrator/task-planner.js +99 -799
- package/dist/storage/file-store.d.ts +8 -0
- package/dist/storage/file-store.js +30 -0
- package/dist/storage/state-manager.d.ts +7 -0
- package/dist/storage/state-manager.js +101 -46
- package/dist/test/generator.js +39 -11
- package/dist/types/index.d.ts +3 -1
- package/package.json +61 -61
- package/skills/auto.md +383 -415
- package/skills/brainstorm.md +27 -29
- package/skills/om.md +3 -3
- package/skills/plan.md +261 -0
- package/skills/start.md +621 -707
|
@@ -72,9 +72,13 @@ export declare class AgentRunner {
|
|
|
72
72
|
*/
|
|
73
73
|
private buildAmbiguityDetectionInstruction;
|
|
74
74
|
/**
|
|
75
|
-
* 构建累积上下文 -
|
|
75
|
+
* 构建累积上下文 - 从当前运行的 context.md 读取前序 Agent 的决策和知识
|
|
76
76
|
*/
|
|
77
77
|
private buildAccumulatedContext;
|
|
78
|
+
/**
|
|
79
|
+
* 构建测试项目上下文 — 注入原始扫描数据供 AI 自行分析
|
|
80
|
+
*/
|
|
81
|
+
private buildTestContext;
|
|
78
82
|
/**
|
|
79
83
|
* 构建阶段上下文
|
|
80
84
|
*/
|
|
@@ -137,6 +137,9 @@ class AgentRunner {
|
|
|
137
137
|
const phaseContext = this.buildPhaseContext(task);
|
|
138
138
|
const accumulatedContext = await this.buildAccumulatedContext(task);
|
|
139
139
|
const ambiguityInstruction = this.buildAmbiguityDetectionInstruction(task);
|
|
140
|
+
const testContext = task.assignedAgent === 'tester' ? await this.buildTestContext() : '';
|
|
141
|
+
const state = await this.stateManager.getState();
|
|
142
|
+
const runId = state.runId;
|
|
140
143
|
return `# 任务执行
|
|
141
144
|
|
|
142
145
|
${ambiguityInstruction}
|
|
@@ -148,6 +151,15 @@ ${ambiguityInstruction}
|
|
|
148
151
|
- 优先级: ${task.priority}
|
|
149
152
|
- 超时: ${task.timeout / 1000} 秒
|
|
150
153
|
|
|
154
|
+
## 工作目录隔离
|
|
155
|
+
|
|
156
|
+
本次运行 ID: ${runId}
|
|
157
|
+
所有中间产物必须写入运行专属目录: \`.openmatrix/${runId}/tasks/${task.id}/artifacts/\`
|
|
158
|
+
- 中间产物: \`.openmatrix/${runId}/tasks/${task.id}/artifacts/\`
|
|
159
|
+
- 结果报告: \`.openmatrix/${runId}/tasks/${task.id}/artifacts/result.md\`
|
|
160
|
+
|
|
161
|
+
**注意**: 业务代码(src/、tests/ 等)写入项目正常目录,仅中间产物和报告写入运行目录。
|
|
162
|
+
|
|
151
163
|
## 当前阶段
|
|
152
164
|
${phaseContext}
|
|
153
165
|
|
|
@@ -157,6 +169,7 @@ ${task.dependencies.length > 0
|
|
|
157
169
|
: '无依赖'}
|
|
158
170
|
|
|
159
171
|
${accumulatedContext}
|
|
172
|
+
${testContext}
|
|
160
173
|
|
|
161
174
|
---
|
|
162
175
|
|
|
@@ -168,7 +181,7 @@ ${agentPrompt.instructions}
|
|
|
168
181
|
|
|
169
182
|
## 完成要求
|
|
170
183
|
|
|
171
|
-
1. 将执行结果写入: \`.openmatrix/tasks/${task.id}/artifacts/result.md\`
|
|
184
|
+
1. 将执行结果写入: \`.openmatrix/${runId}/tasks/${task.id}/artifacts/result.md\`
|
|
172
185
|
(任务状态由 openmatrix complete 命令管理,请勿直接修改 task.json)
|
|
173
186
|
2. 如需审批,创建审批请求: \`.openmatrix/approvals/\` 目录
|
|
174
187
|
|
|
@@ -260,11 +273,21 @@ ${agentPrompt.instructions}
|
|
|
260
273
|
**重要**: 如果无法解析歧义检测结果,视为无歧义继续执行。`;
|
|
261
274
|
}
|
|
262
275
|
/**
|
|
263
|
-
* 构建累积上下文 -
|
|
276
|
+
* 构建累积上下文 - 从当前运行的 context.md 读取前序 Agent 的决策和知识
|
|
264
277
|
*/
|
|
265
278
|
async buildAccumulatedContext(currentTask) {
|
|
266
279
|
const omPath = path.join(process.cwd(), '.openmatrix');
|
|
267
|
-
|
|
280
|
+
// 读取 current.json 获取当前 runId
|
|
281
|
+
let contextFile = path.join(omPath, 'context.md');
|
|
282
|
+
try {
|
|
283
|
+
const currentJson = await fs.readFile(path.join(omPath, 'current.json'), 'utf-8');
|
|
284
|
+
const { runId } = JSON.parse(currentJson);
|
|
285
|
+
if (runId)
|
|
286
|
+
contextFile = path.join(omPath, runId, 'context.md');
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// fallback to root context.md
|
|
290
|
+
}
|
|
268
291
|
try {
|
|
269
292
|
const content = await fs.readFile(contextFile, 'utf-8');
|
|
270
293
|
const trimmed = content.trim();
|
|
@@ -277,6 +300,33 @@ ${agentPrompt.instructions}
|
|
|
277
300
|
你应该基于这些信息来工作,避免重复犯错或与已有决策冲突。
|
|
278
301
|
|
|
279
302
|
${trimmed}
|
|
303
|
+
`;
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
return '';
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* 构建测试项目上下文 — 注入原始扫描数据供 AI 自行分析
|
|
311
|
+
*/
|
|
312
|
+
async buildTestContext() {
|
|
313
|
+
try {
|
|
314
|
+
const { performFullScan } = await import('../test/context-analyzer.js');
|
|
315
|
+
const scanResult = performFullScan(process.cwd());
|
|
316
|
+
return `
|
|
317
|
+
## 项目测试扫描数据(原始数据,由 AI 自行分析决策)
|
|
318
|
+
|
|
319
|
+
\`\`\`json
|
|
320
|
+
${JSON.stringify({
|
|
321
|
+
frameworks: scanResult.frameworks,
|
|
322
|
+
testStyle: scanResult.testStyle,
|
|
323
|
+
existingTests: scanResult.existingTests.slice(0, 10),
|
|
324
|
+
projectType: scanResult.projectType,
|
|
325
|
+
isFrontend: scanResult.isFrontend,
|
|
326
|
+
hasUIComponents: scanResult.hasUIComponents,
|
|
327
|
+
summary: scanResult.summary
|
|
328
|
+
}, null, 2)}
|
|
329
|
+
\`\`\`
|
|
280
330
|
`;
|
|
281
331
|
}
|
|
282
332
|
catch {
|
|
@@ -106,7 +106,18 @@ exports.completeCommand = new commander_1.Command('complete')
|
|
|
106
106
|
});
|
|
107
107
|
// 5. 追加写入全局 context.md (Agent Memory)
|
|
108
108
|
if (isSuccess) {
|
|
109
|
-
|
|
109
|
+
// 读取 current.json 获取当前 runId,context.md 写入运行目录
|
|
110
|
+
let contextDir = omPath;
|
|
111
|
+
try {
|
|
112
|
+
const currentJson = await fs.readFile(path.join(omPath, 'current.json'), 'utf-8');
|
|
113
|
+
const { runId } = JSON.parse(currentJson);
|
|
114
|
+
if (runId)
|
|
115
|
+
contextDir = path.join(omPath, runId);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// fallback to omPath
|
|
119
|
+
}
|
|
120
|
+
const contextFile = path.join(contextDir, 'context.md');
|
|
110
121
|
const timestamp = new Date().toISOString();
|
|
111
122
|
// 构建上下文内容
|
|
112
123
|
const summary = options.summary || '任务已完成';
|
|
@@ -116,6 +127,7 @@ exports.completeCommand = new commander_1.Command('complete')
|
|
|
116
127
|
|
|
117
128
|
`;
|
|
118
129
|
try {
|
|
130
|
+
await fs.mkdir(contextDir, { recursive: true });
|
|
119
131
|
// 原子追加写入全局 context.md(O_APPEND flag 保证并发安全)
|
|
120
132
|
await fs.appendFile(contextFile, contextEntry, 'utf-8');
|
|
121
133
|
}
|
|
@@ -66,7 +66,6 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
66
66
|
const omPath = path.join(basePath, '.openmatrix');
|
|
67
67
|
// 确保目录存在
|
|
68
68
|
await fs.mkdir(omPath, { recursive: true });
|
|
69
|
-
await fs.mkdir(path.join(omPath, 'tasks'), { recursive: true });
|
|
70
69
|
await fs.mkdir(path.join(omPath, 'approvals'), { recursive: true });
|
|
71
70
|
// 确保 .openmatrix 被 git 忽略
|
|
72
71
|
await (0, gitignore_js_1.ensureOpenmatrixGitignore)(basePath);
|
|
@@ -88,6 +87,10 @@ exports.startCommand = new commander_1.Command('start')
|
|
|
88
87
|
const stateManager = new state_manager_js_1.StateManager(omPath);
|
|
89
88
|
await stateManager.initialize();
|
|
90
89
|
const state = await stateManager.getState();
|
|
90
|
+
// 如果上次运行已结束(completed/failed),自动清理旧数据开始新运行
|
|
91
|
+
if (state.status === 'completed' || state.status === 'failed') {
|
|
92
|
+
await stateManager.reset();
|
|
93
|
+
}
|
|
91
94
|
// 检查是否已有运行中的任务
|
|
92
95
|
if (state.status === 'running') {
|
|
93
96
|
if (options.json) {
|
|
@@ -166,7 +169,7 @@ async function loadResearchContext(researchContextPath, basePath, omPath) {
|
|
|
166
169
|
/**
|
|
167
170
|
* 合并研究上下文到 AI 解析的输入
|
|
168
171
|
*/
|
|
169
|
-
function mergeResearchContext(tasksInput, researchContext
|
|
172
|
+
function mergeResearchContext(tasksInput, researchContext) {
|
|
170
173
|
const merged = { ...tasksInput };
|
|
171
174
|
// goals: 研究的基础 goals + AI 补充的 goals(去重)
|
|
172
175
|
const baseGoals = researchContext.goals || [];
|
|
@@ -189,14 +192,6 @@ function mergeResearchContext(tasksInput, researchContext, researchReport) {
|
|
|
189
192
|
if (mergedDeliverables.length > 0) {
|
|
190
193
|
merged.deliverables = mergedDeliverables;
|
|
191
194
|
}
|
|
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
195
|
return merged;
|
|
201
196
|
}
|
|
202
197
|
/**
|
|
@@ -238,17 +233,25 @@ async function handleTasksJson(options, stateManager, state, omPath, basePath) {
|
|
|
238
233
|
return;
|
|
239
234
|
}
|
|
240
235
|
// 加载并合并研究上下文
|
|
241
|
-
const { context: researchContext
|
|
236
|
+
const { context: researchContext } = await loadResearchContext(options.researchContext, basePath, omPath);
|
|
242
237
|
let resolvedInput = tasksInput;
|
|
243
238
|
if (researchContext) {
|
|
244
|
-
resolvedInput = mergeResearchContext(tasksInput, researchContext
|
|
239
|
+
resolvedInput = mergeResearchContext(tasksInput, researchContext);
|
|
245
240
|
if (!options.json) {
|
|
246
241
|
console.log(`🔬 已加载研究领域: ${researchContext.domain}`);
|
|
247
242
|
}
|
|
248
243
|
}
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
244
|
+
// 读取独立的技术方案文档(plan.md)
|
|
245
|
+
let planContent;
|
|
246
|
+
const planPath = path.join(omPath, 'plan.md');
|
|
247
|
+
try {
|
|
248
|
+
planContent = await fs.readFile(planPath, 'utf-8');
|
|
249
|
+
if (!options.json) {
|
|
250
|
+
console.log(`📄 已加载技术方案: plan.md`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// plan.md 不存在时继续(可能由 AI 后续生成或无 plan)
|
|
252
255
|
}
|
|
253
256
|
// 构建 ParsedTask
|
|
254
257
|
const parsedTask = {
|
|
@@ -256,6 +259,7 @@ async function handleTasksJson(options, stateManager, state, omPath, basePath) {
|
|
|
256
259
|
description: resolvedInput.description || '',
|
|
257
260
|
goals: resolvedInput.goals,
|
|
258
261
|
goalTypes: resolvedInput.goalTypes,
|
|
262
|
+
goalComplexity: resolvedInput.goalComplexity,
|
|
259
263
|
constraints: resolvedInput.constraints || [],
|
|
260
264
|
deliverables: resolvedInput.deliverables || [],
|
|
261
265
|
rawContent: ''
|
|
@@ -281,7 +285,7 @@ async function handleTasksJson(options, stateManager, state, omPath, basePath) {
|
|
|
281
285
|
}
|
|
282
286
|
// 使用 TaskPlanner 拆分(保持原有拆分逻辑)
|
|
283
287
|
const planner = new task_planner_js_1.TaskPlanner();
|
|
284
|
-
const subTasks = planner.breakdown(parsedTask, extraAnswers, qualityConfig,
|
|
288
|
+
const subTasks = planner.breakdown(parsedTask, extraAnswers, qualityConfig, planContent);
|
|
285
289
|
// 创建任务到状态管理器,并建立 ID 映射
|
|
286
290
|
// TaskPlanner 生成的 taskId 和 StateManager 创建的 id 不同,
|
|
287
291
|
// 需要映射后才能正确设置 dependencies
|
|
@@ -1,43 +1,4 @@
|
|
|
1
1
|
import type { ParsedTask, QualityConfig } from '../types/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* 从 plan 中解析出的结构化模块信息
|
|
4
|
-
*/
|
|
5
|
-
export interface PlanModule {
|
|
6
|
-
/** 模块名称,如 "用户域" */
|
|
7
|
-
name: string;
|
|
8
|
-
/** 模块描述 */
|
|
9
|
-
description: string;
|
|
10
|
-
/** 关联的表,如 ["users", "user_profiles"] */
|
|
11
|
-
tables: string[];
|
|
12
|
-
/** 模块类型 */
|
|
13
|
-
type: 'domain' | 'feature' | 'infra' | 'system';
|
|
14
|
-
/** 依赖的其他模块名称 */
|
|
15
|
-
dependsOn: string[];
|
|
16
|
-
/** 预估复杂度 */
|
|
17
|
-
complexity: 'low' | 'medium' | 'high';
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* 从 plan 中解析出的结构化信息
|
|
21
|
-
*/
|
|
22
|
-
export interface ParsedPlan {
|
|
23
|
-
/** 核心模块列表 */
|
|
24
|
-
modules: PlanModule[];
|
|
25
|
-
/** 技术栈摘要 */
|
|
26
|
-
techStack: string[];
|
|
27
|
-
/** 原始 plan 文本 */
|
|
28
|
-
raw: string;
|
|
29
|
-
}
|
|
30
|
-
/** 从 plan 中提取的结构化信息(用于 fallback 时注入任务描述) */
|
|
31
|
-
export interface PlanMetadata {
|
|
32
|
-
/** 技术栈 */
|
|
33
|
-
techStack: string[];
|
|
34
|
-
/** 接口/API 定义 */
|
|
35
|
-
interfaces: string[];
|
|
36
|
-
/** 数据模型/表 */
|
|
37
|
-
dataModels: string[];
|
|
38
|
-
/** 关键决策 */
|
|
39
|
-
keyDecisions: string[];
|
|
40
|
-
}
|
|
41
2
|
export interface TaskBreakdown {
|
|
42
3
|
taskId: string;
|
|
43
4
|
title: string;
|
|
@@ -66,14 +27,8 @@ export interface UserAnswers {
|
|
|
66
27
|
/**
|
|
67
28
|
* TaskPlanner - 任务拆解器
|
|
68
29
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* 2. 测试任务配对 (每个开发任务自动生成对应测试任务)
|
|
72
|
-
* 3. 验收标准注入 (从用户回答中提取)
|
|
73
|
-
* 4. 用户上下文注入 (将用户回答注入任务描述)
|
|
74
|
-
* 5. 依赖关系分析 (自动分析任务间依赖)
|
|
75
|
-
* 6. 并行执行 (独立任务互不依赖)
|
|
76
|
-
* 7. 质量级别感知 (根据配置调整测试覆盖率)
|
|
30
|
+
* Plan 原文作为原始上下文透传给 AI Agent,由 AI 自行理解和提取
|
|
31
|
+
* 技术栈、数据模型、依赖关系等信息。CLI 层不做语义解析。
|
|
77
32
|
*/
|
|
78
33
|
export declare class TaskPlanner {
|
|
79
34
|
private userAnswers;
|
|
@@ -85,66 +40,20 @@ export declare class TaskPlanner {
|
|
|
85
40
|
*/
|
|
86
41
|
setUserAnswers(answers: UserAnswers): void;
|
|
87
42
|
/**
|
|
88
|
-
*
|
|
89
|
-
*/
|
|
90
|
-
parsePlan(planText: string): ParsedPlan;
|
|
91
|
-
/**
|
|
92
|
-
* 从 plan 中提取结构化信息(用于 fallback 时注入任务描述)
|
|
93
|
-
* 即使无法解析模块,也能保留关键技术信息
|
|
94
|
-
*/
|
|
95
|
-
extractPlanMetadata(plan: string): PlanMetadata;
|
|
96
|
-
/**
|
|
97
|
-
* 从 goals 推断模块结构(当 plan 无法解析模块时使用)
|
|
98
|
-
*/
|
|
99
|
-
inferModulesFromGoals(parsedTask: ParsedTask): PlanModule[];
|
|
100
|
-
/**
|
|
101
|
-
* 分析模块间依赖关系
|
|
102
|
-
*
|
|
103
|
-
* 策略:从 plan 文本中提取 AI 明确写的依赖信息,不做架构猜测。
|
|
104
|
-
* 如果 plan 中没有指定依赖,模块之间并行执行。
|
|
105
|
-
*/
|
|
106
|
-
private analyzeModuleDependencies;
|
|
107
|
-
/**
|
|
108
|
-
* 从 plan 文本中提取某个模块相关的上下文段落
|
|
109
|
-
*/
|
|
110
|
-
private extractModuleContext;
|
|
111
|
-
/**
|
|
112
|
-
* 预估模块复杂度 — 基于通用架构特征,不依赖具体业务领域
|
|
43
|
+
* 任务拆解 — 唯一路径:按目标拆分,plan 作为原始上下文透传
|
|
113
44
|
*/
|
|
114
|
-
private estimateModuleComplexity;
|
|
115
45
|
breakdown(parsedTask: ParsedTask, answers: Record<string, string>, qualityConfig?: QualityConfig, plan?: string): TaskBreakdown[];
|
|
116
46
|
/**
|
|
117
|
-
*
|
|
118
|
-
*/
|
|
119
|
-
private breakdownByModules;
|
|
120
|
-
/**
|
|
121
|
-
* 为模块任务添加 phase 级别的跨阶段依赖
|
|
122
|
-
*/
|
|
123
|
-
private enforcePhaseDependenciesForModule;
|
|
124
|
-
/**
|
|
125
|
-
* 确定模块任务优先级
|
|
126
|
-
*/
|
|
127
|
-
private determineModulePriority;
|
|
128
|
-
/**
|
|
129
|
-
* 为模块生成验收标准
|
|
130
|
-
*/
|
|
131
|
-
private generateModuleAcceptanceCriteria;
|
|
132
|
-
/**
|
|
133
|
-
* 按目标拆分的传统方式(fallback)
|
|
47
|
+
* 按目标拆分子任务
|
|
134
48
|
*/
|
|
135
49
|
private breakdownByGoals;
|
|
136
|
-
/**
|
|
137
|
-
* 判断是否需要设计阶段
|
|
138
|
-
*
|
|
139
|
-
* 条件: 多个 goal,或 goal 包含复杂关键词
|
|
140
|
-
*/
|
|
141
50
|
private needsDesignPhase;
|
|
142
51
|
/**
|
|
143
52
|
* 获取测试覆盖率目标
|
|
144
53
|
*/
|
|
145
54
|
private getCoverageTarget;
|
|
146
55
|
/**
|
|
147
|
-
* 提取用户上下文
|
|
56
|
+
* 提取用户上下文 — 使用 translateBrainstormAnswers 映射后的规范化键
|
|
148
57
|
*/
|
|
149
58
|
private extractUserContext;
|
|
150
59
|
private parseArrayAnswer;
|
|
@@ -161,14 +70,6 @@ export declare class TaskPlanner {
|
|
|
161
70
|
private generateTaskId;
|
|
162
71
|
private determinePriority;
|
|
163
72
|
private estimateComplexity;
|
|
164
|
-
/**
|
|
165
|
-
* 分类目标类型
|
|
166
|
-
* - development: 需要编写代码的功能实现 → 拆分为实现+测试对
|
|
167
|
-
* - testing: 已明确是测试任务 → 单个测试任务
|
|
168
|
-
* - documentation: 文档编写 → 单个文档任务
|
|
169
|
-
* - other: 其他类型(配置、优化、部署等) → 单个任务
|
|
170
|
-
*/
|
|
171
|
-
private classifyGoal;
|
|
172
73
|
/**
|
|
173
74
|
* 检测顺序阶段目标(如 "Phase 1 基础架构"、"Phase 2 AI创作核心")
|
|
174
75
|
* 返回按阶段排序的索引列表
|