mcp-probe-kit 3.3.0 → 3.6.0
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 +15 -7
- package/build/index.js +45 -79
- package/build/lib/__tests__/agents-md-template.unit.test.js +1 -0
- package/build/lib/__tests__/dev-workflow.unit.test.d.ts +1 -0
- package/build/lib/__tests__/dev-workflow.unit.test.js +22 -0
- package/build/lib/__tests__/handles.unit.test.d.ts +1 -0
- package/build/lib/__tests__/handles.unit.test.js +19 -0
- package/build/lib/__tests__/mcp-apps.unit.test.d.ts +1 -0
- package/build/lib/__tests__/mcp-apps.unit.test.js +52 -0
- package/build/lib/__tests__/mcp-tool-skill-registry.unit.test.d.ts +1 -0
- package/build/lib/__tests__/mcp-tool-skill-registry.unit.test.js +18 -0
- package/build/lib/__tests__/memory-client.unit.test.js +105 -0
- package/build/lib/__tests__/spec-gate.unit.test.d.ts +1 -0
- package/build/lib/__tests__/spec-gate.unit.test.js +54 -0
- package/build/lib/__tests__/task-defaults.unit.test.d.ts +1 -0
- package/build/lib/__tests__/task-defaults.unit.test.js +27 -0
- package/build/lib/__tests__/workflow-skill-installer.unit.test.d.ts +1 -0
- package/build/lib/__tests__/workflow-skill-installer.unit.test.js +110 -0
- package/build/lib/agents-md-template.js +10 -2
- package/build/lib/dev-workflow.d.ts +36 -0
- package/build/lib/dev-workflow.js +497 -0
- package/build/lib/handles.d.ts +31 -0
- package/build/lib/handles.js +36 -0
- package/build/lib/mcp-apps.d.ts +14 -0
- package/build/lib/mcp-apps.js +234 -0
- package/build/lib/mcp-tool-skill-registry.d.ts +46 -0
- package/build/lib/mcp-tool-skill-registry.js +256 -0
- package/build/lib/memory-client.d.ts +19 -0
- package/build/lib/memory-client.js +69 -0
- package/build/lib/memory-orchestration.d.ts +5 -0
- package/build/lib/memory-orchestration.js +20 -0
- package/build/lib/merge-agents-md.d.ts +2 -2
- package/build/lib/merge-agents-md.js +7 -4
- package/build/lib/output-schema-registry.d.ts +11 -0
- package/build/lib/output-schema-registry.js +80 -0
- package/build/lib/spec-gate.d.ts +32 -0
- package/build/lib/spec-gate.js +83 -0
- package/build/lib/task-defaults.d.ts +6 -0
- package/build/lib/task-defaults.js +20 -0
- package/build/lib/tool-annotations.js +3 -0
- package/build/lib/toolset-manager.js +2 -0
- package/build/lib/workflow-skill-installer.d.ts +38 -0
- package/build/lib/workflow-skill-installer.js +158 -0
- package/build/lib/workflow-skill-template.d.ts +9 -0
- package/build/lib/workflow-skill-template.js +85 -0
- package/build/lib/workflow-skill-version.d.ts +15 -0
- package/build/lib/workflow-skill-version.js +68 -0
- package/build/schemas/index.d.ts +110 -0
- package/build/schemas/index.js +1 -0
- package/build/schemas/memory-tools.d.ts +83 -0
- package/build/schemas/memory-tools.js +43 -0
- package/build/schemas/orchestration-tools.d.ts +8 -0
- package/build/schemas/orchestration-tools.js +8 -0
- package/build/schemas/output/code-insight-tools.d.ts +65 -0
- package/build/schemas/output/code-insight-tools.js +42 -0
- package/build/schemas/output/memory-tools.d.ts +576 -0
- package/build/schemas/output/memory-tools.js +148 -0
- package/build/schemas/project-tools.d.ts +19 -0
- package/build/schemas/project-tools.js +20 -0
- package/build/tools/__tests__/delete_memory_asset.unit.test.d.ts +1 -0
- package/build/tools/__tests__/delete_memory_asset.unit.test.js +87 -0
- package/build/tools/__tests__/search_memory.unit.test.js +1 -0
- package/build/tools/__tests__/start_bugfix.unit.test.js +34 -1
- package/build/tools/__tests__/update_memory_asset.unit.test.d.ts +1 -0
- package/build/tools/__tests__/update_memory_asset.unit.test.js +82 -0
- package/build/tools/__tests__/workflow.unit.test.d.ts +1 -0
- package/build/tools/__tests__/workflow.unit.test.js +24 -0
- package/build/tools/code_insight.js +5 -1
- package/build/tools/delete_memory_asset.d.ts +7 -0
- package/build/tools/delete_memory_asset.js +57 -0
- package/build/tools/index.d.ts +3 -0
- package/build/tools/index.js +3 -0
- package/build/tools/search_memory.js +5 -2
- package/build/tools/start_bugfix.js +37 -4
- package/build/tools/start_feature.js +4 -3
- package/build/tools/start_ui.js +5 -4
- package/build/tools/update_memory_asset.d.ts +7 -0
- package/build/tools/update_memory_asset.js +98 -0
- package/build/tools/workflow.d.ts +13 -0
- package/build/tools/workflow.js +69 -0
- package/package.json +5 -3
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 记忆工具结构化输出 Schema
|
|
3
|
+
*/
|
|
4
|
+
const MemoryAssetHandleSchema = {
|
|
5
|
+
type: 'object',
|
|
6
|
+
properties: {
|
|
7
|
+
id: { type: 'string', description: '记忆资产 ID' },
|
|
8
|
+
tool: {
|
|
9
|
+
type: 'string',
|
|
10
|
+
enum: ['read_memory_asset', 'update_memory_asset', 'delete_memory_asset'],
|
|
11
|
+
description: '建议调用的 MCP 工具',
|
|
12
|
+
},
|
|
13
|
+
name: { type: 'string' },
|
|
14
|
+
type: { type: 'string' },
|
|
15
|
+
summary: { type: 'string' },
|
|
16
|
+
},
|
|
17
|
+
required: ['id', 'tool'],
|
|
18
|
+
};
|
|
19
|
+
const ToolHandlesSchema = {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
memory_assets: {
|
|
23
|
+
type: 'array',
|
|
24
|
+
items: MemoryAssetHandleSchema,
|
|
25
|
+
},
|
|
26
|
+
graph_snapshot: { type: 'string', description: '图谱快照 URI(probe://graph/...)' },
|
|
27
|
+
graph_resource: { type: 'string', description: '图谱资源 URI,通常为 probe://graph/latest' },
|
|
28
|
+
},
|
|
29
|
+
additionalProperties: false,
|
|
30
|
+
};
|
|
31
|
+
const MemoryAssetSchema = {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
id: { type: 'string' },
|
|
35
|
+
name: { type: 'string' },
|
|
36
|
+
type: { type: 'string' },
|
|
37
|
+
description: { type: 'string' },
|
|
38
|
+
summary: { type: 'string' },
|
|
39
|
+
content: { type: 'string' },
|
|
40
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
41
|
+
confidence: { type: 'number' },
|
|
42
|
+
createdAt: { type: 'string' },
|
|
43
|
+
updatedAt: { type: 'string' },
|
|
44
|
+
},
|
|
45
|
+
required: ['id', 'name'],
|
|
46
|
+
};
|
|
47
|
+
export const MemorySearchSchema = {
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {
|
|
50
|
+
enabled: { type: 'boolean' },
|
|
51
|
+
query: { type: 'string' },
|
|
52
|
+
count: { type: 'number' },
|
|
53
|
+
results: {
|
|
54
|
+
type: 'array',
|
|
55
|
+
items: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
id: { type: 'string' },
|
|
59
|
+
score: { type: 'number' },
|
|
60
|
+
name: { type: 'string' },
|
|
61
|
+
type: { type: 'string' },
|
|
62
|
+
description: { type: 'string' },
|
|
63
|
+
summary: { type: 'string' },
|
|
64
|
+
content: { type: 'string' },
|
|
65
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
66
|
+
sourcePath: { type: 'string' },
|
|
67
|
+
},
|
|
68
|
+
required: ['id'],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
handles: ToolHandlesSchema,
|
|
72
|
+
},
|
|
73
|
+
required: ['enabled', 'results'],
|
|
74
|
+
};
|
|
75
|
+
export const MemoryAssetDetailSchema = {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {
|
|
78
|
+
enabled: { type: 'boolean' },
|
|
79
|
+
asset: MemoryAssetSchema,
|
|
80
|
+
handles: ToolHandlesSchema,
|
|
81
|
+
},
|
|
82
|
+
required: ['enabled'],
|
|
83
|
+
};
|
|
84
|
+
export const MemorizeResultSchema = {
|
|
85
|
+
type: 'object',
|
|
86
|
+
properties: {
|
|
87
|
+
enabled: { type: 'boolean' },
|
|
88
|
+
stored: { type: 'boolean' },
|
|
89
|
+
asset: MemoryAssetSchema,
|
|
90
|
+
warnings: { type: 'array', items: { type: 'string' } },
|
|
91
|
+
handles: ToolHandlesSchema,
|
|
92
|
+
},
|
|
93
|
+
required: ['enabled', 'stored'],
|
|
94
|
+
};
|
|
95
|
+
export const DeleteMemoryResultSchema = {
|
|
96
|
+
type: 'object',
|
|
97
|
+
properties: {
|
|
98
|
+
enabled: { type: 'boolean' },
|
|
99
|
+
deleted: { type: 'boolean' },
|
|
100
|
+
requires_confirmation: { type: 'boolean', description: '为 true 时表示仅预览,需 confirm=true 再删' },
|
|
101
|
+
preview: {
|
|
102
|
+
type: 'object',
|
|
103
|
+
properties: {
|
|
104
|
+
id: { type: 'string' },
|
|
105
|
+
name: { type: 'string' },
|
|
106
|
+
type: { type: 'string' },
|
|
107
|
+
summary: { type: 'string' },
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
asset: MemoryAssetSchema,
|
|
111
|
+
handles: ToolHandlesSchema,
|
|
112
|
+
},
|
|
113
|
+
required: ['enabled', 'deleted'],
|
|
114
|
+
};
|
|
115
|
+
export const UpdateMemoryResultSchema = {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
enabled: { type: 'boolean' },
|
|
119
|
+
updated: { type: 'boolean' },
|
|
120
|
+
asset: MemoryAssetSchema,
|
|
121
|
+
warnings: { type: 'array', items: { type: 'string' } },
|
|
122
|
+
handles: ToolHandlesSchema,
|
|
123
|
+
},
|
|
124
|
+
required: ['enabled', 'updated'],
|
|
125
|
+
};
|
|
126
|
+
export const PatternExtractionSchema = {
|
|
127
|
+
type: 'object',
|
|
128
|
+
properties: {
|
|
129
|
+
summary: { type: 'string' },
|
|
130
|
+
scannedFiles: { type: 'number' },
|
|
131
|
+
candidates: {
|
|
132
|
+
type: 'array',
|
|
133
|
+
items: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {
|
|
136
|
+
name: { type: 'string' },
|
|
137
|
+
type: { type: 'string' },
|
|
138
|
+
filePath: { type: 'string' },
|
|
139
|
+
summary: { type: 'string' },
|
|
140
|
+
tags: { type: 'array', items: { type: 'string' } },
|
|
141
|
+
},
|
|
142
|
+
required: ['name', 'filePath'],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
handles: ToolHandlesSchema,
|
|
146
|
+
},
|
|
147
|
+
required: ['summary', 'candidates'],
|
|
148
|
+
};
|
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
* 项目管理工具的 Schema 定义
|
|
3
3
|
*/
|
|
4
4
|
export declare const projectToolSchemas: readonly [{
|
|
5
|
+
readonly name: "workflow";
|
|
6
|
+
readonly description: "当不确定该用哪个 MCP 工具时使用。根据意图返回分阶段 MCP 指南(firstTool + phases)。同时确保用户项目已存在 .agents/skills/mcp-probe-kit/SKILL.md 与 AGENTS.md 中的 Skill 引用(缺失则自动创建/更新)。";
|
|
7
|
+
readonly inputSchema: {
|
|
8
|
+
readonly type: "object";
|
|
9
|
+
readonly properties: {
|
|
10
|
+
readonly intent: {
|
|
11
|
+
readonly type: "string";
|
|
12
|
+
readonly description: "用户目标或任务描述(自然语言)";
|
|
13
|
+
};
|
|
14
|
+
readonly scenario: {
|
|
15
|
+
readonly type: "string";
|
|
16
|
+
readonly enum: readonly ["auto", "feature", "bugfix", "ui", "explore", "commit", "review", "refactor", "onboard", "spec", "memory"];
|
|
17
|
+
readonly description: "可选:显式场景;默认 auto 从 intent 推断";
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
readonly required: readonly [];
|
|
21
|
+
readonly additionalProperties: true;
|
|
22
|
+
};
|
|
23
|
+
}, {
|
|
5
24
|
readonly name: "init_project_context";
|
|
6
25
|
readonly description: "生成/更新项目上下文:默认写入 AGENTS.md(含 MCP 与 GitNexus 触发规则)及 docs/project-context/。新功能请先 start_feature,修 bug 请先 start_bugfix。完成后 Agent 应阅读 AGENTS.md。";
|
|
7
26
|
readonly inputSchema: {
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
* 项目管理工具的 Schema 定义
|
|
3
3
|
*/
|
|
4
4
|
export const projectToolSchemas = [
|
|
5
|
+
{
|
|
6
|
+
name: "workflow",
|
|
7
|
+
description: "当不确定该用哪个 MCP 工具时使用。根据意图返回分阶段 MCP 指南(firstTool + phases)。同时确保用户项目已存在 .agents/skills/mcp-probe-kit/SKILL.md 与 AGENTS.md 中的 Skill 引用(缺失则自动创建/更新)。",
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
intent: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: "用户目标或任务描述(自然语言)",
|
|
14
|
+
},
|
|
15
|
+
scenario: {
|
|
16
|
+
type: "string",
|
|
17
|
+
enum: ["auto", "feature", "bugfix", "ui", "explore", "commit", "review", "refactor", "onboard", "spec", "memory"],
|
|
18
|
+
description: "可选:显式场景;默认 auto 从 intent 推断",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
required: [],
|
|
22
|
+
additionalProperties: true,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
5
25
|
{
|
|
6
26
|
name: "init_project_context",
|
|
7
27
|
description: "生成/更新项目上下文:默认写入 AGENTS.md(含 MCP 与 GitNexus 触发规则)及 docs/project-context/。新功能请先 start_feature,修 bug 请先 start_bugfix。完成后 Agent 应阅读 AGENTS.md。",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
const deleteAssetMock = vi.fn();
|
|
3
|
+
const getAssetMock = vi.fn();
|
|
4
|
+
const isReadEnabledMock = vi.fn();
|
|
5
|
+
vi.mock('../../lib/memory-client.js', () => ({
|
|
6
|
+
createMemoryClient: () => ({
|
|
7
|
+
isReadEnabled: isReadEnabledMock,
|
|
8
|
+
getAsset: getAssetMock,
|
|
9
|
+
deleteAsset: deleteAssetMock,
|
|
10
|
+
}),
|
|
11
|
+
}));
|
|
12
|
+
import { deleteMemoryAsset } from '../delete_memory_asset.js';
|
|
13
|
+
const sampleAsset = {
|
|
14
|
+
id: 'asset-1',
|
|
15
|
+
name: 'obsolete-pattern',
|
|
16
|
+
type: 'pattern',
|
|
17
|
+
description: 'Outdated pattern',
|
|
18
|
+
summary: 'no longer used',
|
|
19
|
+
content: 'old code',
|
|
20
|
+
tags: ['pattern'],
|
|
21
|
+
confidence: 0.8,
|
|
22
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
23
|
+
updatedAt: '2026-01-01T00:00:00.000Z',
|
|
24
|
+
};
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
isReadEnabledMock.mockReset();
|
|
27
|
+
deleteAssetMock.mockReset();
|
|
28
|
+
getAssetMock.mockReset();
|
|
29
|
+
});
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
vi.clearAllMocks();
|
|
32
|
+
});
|
|
33
|
+
describe('delete_memory_asset 单元测试', () => {
|
|
34
|
+
test('记忆服务未开启时返回跳过结果', async () => {
|
|
35
|
+
isReadEnabledMock.mockReturnValue(false);
|
|
36
|
+
const result = await deleteMemoryAsset({ asset_id: 'asset-1' });
|
|
37
|
+
expect(result.isError).toBe(false);
|
|
38
|
+
expect(result.content[0].text).toContain('记忆服务未开启');
|
|
39
|
+
expect(deleteAssetMock).not.toHaveBeenCalled();
|
|
40
|
+
expect(getAssetMock).not.toHaveBeenCalled();
|
|
41
|
+
});
|
|
42
|
+
test('未 confirm 时返回预览并要求确认', async () => {
|
|
43
|
+
isReadEnabledMock.mockReturnValue(true);
|
|
44
|
+
getAssetMock.mockResolvedValue(sampleAsset);
|
|
45
|
+
const result = await deleteMemoryAsset({ asset_id: 'asset-1' });
|
|
46
|
+
expect(result.isError).toBe(false);
|
|
47
|
+
if (!('structuredContent' in result) || !result.structuredContent) {
|
|
48
|
+
throw new Error('structuredContent 缺失');
|
|
49
|
+
}
|
|
50
|
+
expect(result.content[0].text).toContain('待确认删除');
|
|
51
|
+
expect(result.structuredContent?.requires_confirmation).toBe(true);
|
|
52
|
+
expect(result.structuredContent?.preview?.name).toBe('obsolete-pattern');
|
|
53
|
+
expect(result.structuredContent?.handles?.memory_assets?.[0]?.tool).toBe('delete_memory_asset');
|
|
54
|
+
expect(deleteAssetMock).not.toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
test('资产不存在时返回未找到', async () => {
|
|
57
|
+
isReadEnabledMock.mockReturnValue(true);
|
|
58
|
+
getAssetMock.mockResolvedValue(null);
|
|
59
|
+
const result = await deleteMemoryAsset({ asset_id: 'missing-id' });
|
|
60
|
+
expect(result.isError).toBe(false);
|
|
61
|
+
if (!('structuredContent' in result) || !result.structuredContent) {
|
|
62
|
+
throw new Error('structuredContent 缺失');
|
|
63
|
+
}
|
|
64
|
+
expect(result.content[0].text).toContain('未找到记忆资产');
|
|
65
|
+
expect(result.structuredContent.deleted).toBe(false);
|
|
66
|
+
expect(deleteAssetMock).not.toHaveBeenCalled();
|
|
67
|
+
});
|
|
68
|
+
test('confirm=true 删除成功时返回资产信息', async () => {
|
|
69
|
+
isReadEnabledMock.mockReturnValue(true);
|
|
70
|
+
deleteAssetMock.mockResolvedValue({ deleted: true, asset: sampleAsset });
|
|
71
|
+
const result = await deleteMemoryAsset({ asset_id: 'asset-1', confirm: true });
|
|
72
|
+
expect(result.isError).toBe(false);
|
|
73
|
+
if (!('structuredContent' in result) || !result.structuredContent) {
|
|
74
|
+
throw new Error('structuredContent 缺失');
|
|
75
|
+
}
|
|
76
|
+
expect(result.content[0].text).toContain('已删除记忆资产: obsolete-pattern');
|
|
77
|
+
expect(result.structuredContent.deleted).toBe(true);
|
|
78
|
+
expect(result.structuredContent.requires_confirmation).toBe(false);
|
|
79
|
+
expect(result.structuredContent.asset.name).toBe('obsolete-pattern');
|
|
80
|
+
expect(getAssetMock).not.toHaveBeenCalled();
|
|
81
|
+
});
|
|
82
|
+
test('缺少 asset_id 时返回错误', async () => {
|
|
83
|
+
const result = await deleteMemoryAsset({});
|
|
84
|
+
expect(result.isError).toBe(true);
|
|
85
|
+
expect(result.content[0].text).toContain('asset_id');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -85,5 +85,6 @@ describe('search_memory 单元测试', () => {
|
|
|
85
85
|
description: 'Feishu proxy mismatch',
|
|
86
86
|
summary: 'proxy caused 400 on HTTPS',
|
|
87
87
|
}));
|
|
88
|
+
expect(result.structuredContent.handles.memory_assets[0]).toEqual(expect.objectContaining({ id: 'asset-1', tool: 'read_memory_asset' }));
|
|
88
89
|
});
|
|
89
90
|
});
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 单元测试:start_bugfix 工具(委托式编排)
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as os from 'node:os';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import { afterEach, describe, test, expect } from 'vitest';
|
|
5
8
|
import { startBugfix } from '../start_bugfix.js';
|
|
9
|
+
const tempDirs = [];
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
while (tempDirs.length > 0) {
|
|
12
|
+
const dir = tempDirs.pop();
|
|
13
|
+
if (dir) {
|
|
14
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
});
|
|
6
18
|
describe('start_bugfix 单元测试', () => {
|
|
7
19
|
test('缺少必填参数时返回错误', async () => {
|
|
8
20
|
const result = await startBugfix({});
|
|
@@ -38,6 +50,27 @@ describe('start_bugfix 单元测试', () => {
|
|
|
38
50
|
expect(structured?.metadata?.graphDocs?.latestMarkdownPath).toBe('docs/graph-insights/latest.md');
|
|
39
51
|
expect(structured?.metadata?.graphContext?.summary).toMatch(/GitNexus|图谱|降级/);
|
|
40
52
|
});
|
|
53
|
+
test('关联规格存在时计划包含 check_spec 闸门', async () => {
|
|
54
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'bugfix-spec-'));
|
|
55
|
+
tempDirs.push(root);
|
|
56
|
+
const featureName = 'checkout-flow';
|
|
57
|
+
const specDir = path.join(root, 'docs', 'specs', featureName);
|
|
58
|
+
fs.mkdirSync(specDir, { recursive: true });
|
|
59
|
+
fs.writeFileSync(path.join(specDir, 'requirements.md'), '# requirements\n');
|
|
60
|
+
const result = await startBugfix({
|
|
61
|
+
error_message: 'checkout total wrong',
|
|
62
|
+
feature_name: featureName,
|
|
63
|
+
docs_dir: 'docs',
|
|
64
|
+
project_root: root,
|
|
65
|
+
});
|
|
66
|
+
expect(result.isError).toBe(false);
|
|
67
|
+
const structured = result.structuredContent;
|
|
68
|
+
const checkStep = structured?.metadata?.plan?.steps?.find((step) => step.tool === 'check_spec');
|
|
69
|
+
expect(checkStep).toBeTruthy();
|
|
70
|
+
expect(checkStep.args.feature_name).toBe(featureName);
|
|
71
|
+
expect(structured.metadata.specGate.featureName).toBe(featureName);
|
|
72
|
+
expect(result.content[0].text).toMatch(/check_spec|规格闸门/);
|
|
73
|
+
});
|
|
41
74
|
test('loop 模式返回需求循环结构', async () => {
|
|
42
75
|
const result = await startBugfix({
|
|
43
76
|
error_message: 'NullReference',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
const updateAssetMock = vi.fn();
|
|
3
|
+
const isEnabledMock = vi.fn();
|
|
4
|
+
vi.mock('../../lib/memory-client.js', () => ({
|
|
5
|
+
createMemoryClient: () => ({
|
|
6
|
+
isEnabled: isEnabledMock,
|
|
7
|
+
updateAsset: updateAssetMock,
|
|
8
|
+
}),
|
|
9
|
+
}));
|
|
10
|
+
import { updateMemoryAsset } from '../update_memory_asset.js';
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
isEnabledMock.mockReset();
|
|
13
|
+
updateAssetMock.mockReset();
|
|
14
|
+
});
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
vi.clearAllMocks();
|
|
17
|
+
});
|
|
18
|
+
describe('update_memory_asset 单元测试', () => {
|
|
19
|
+
test('记忆服务未开启时返回跳过结果', async () => {
|
|
20
|
+
isEnabledMock.mockReturnValue(false);
|
|
21
|
+
const result = await updateMemoryAsset({ asset_id: 'asset-1', summary: 'new summary' });
|
|
22
|
+
expect(result.isError).toBe(false);
|
|
23
|
+
expect('structuredContent' in result).toBe(true);
|
|
24
|
+
if (!('structuredContent' in result)) {
|
|
25
|
+
throw new Error('structuredContent 缺失');
|
|
26
|
+
}
|
|
27
|
+
expect(result.content[0].text).toContain('记忆服务未开启');
|
|
28
|
+
expect(updateAssetMock).not.toHaveBeenCalled();
|
|
29
|
+
});
|
|
30
|
+
test('资产不存在时返回未找到', async () => {
|
|
31
|
+
isEnabledMock.mockReturnValue(true);
|
|
32
|
+
updateAssetMock.mockResolvedValue({ updated: false, asset: null });
|
|
33
|
+
const result = await updateMemoryAsset({ asset_id: 'missing-id', summary: 'new summary' });
|
|
34
|
+
expect(result.isError).toBe(false);
|
|
35
|
+
expect('structuredContent' in result).toBe(true);
|
|
36
|
+
if (!('structuredContent' in result)) {
|
|
37
|
+
throw new Error('structuredContent 缺失');
|
|
38
|
+
}
|
|
39
|
+
expect(result.content[0].text).toContain('未找到记忆资产');
|
|
40
|
+
expect(result.structuredContent.updated).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
test('更新成功时返回资产信息', async () => {
|
|
43
|
+
isEnabledMock.mockReturnValue(true);
|
|
44
|
+
updateAssetMock.mockResolvedValue({
|
|
45
|
+
updated: true,
|
|
46
|
+
asset: {
|
|
47
|
+
id: 'asset-1',
|
|
48
|
+
name: 'feishu-proxy-bug',
|
|
49
|
+
type: 'bugfix',
|
|
50
|
+
description: 'Feishu proxy mismatch',
|
|
51
|
+
summary: 'updated summary',
|
|
52
|
+
content: '【现象】submit 成功\n【根因】HTTP_PROXY 污染\n【修复】proxy:false',
|
|
53
|
+
tags: ['bugfix'],
|
|
54
|
+
confidence: 0.9,
|
|
55
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
56
|
+
updatedAt: '2026-01-02T00:00:00.000Z',
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
const result = await updateMemoryAsset({
|
|
60
|
+
asset_id: 'asset-1',
|
|
61
|
+
summary: 'updated summary',
|
|
62
|
+
});
|
|
63
|
+
expect(result.isError).toBe(false);
|
|
64
|
+
expect('structuredContent' in result).toBe(true);
|
|
65
|
+
if (!('structuredContent' in result)) {
|
|
66
|
+
throw new Error('structuredContent 缺失');
|
|
67
|
+
}
|
|
68
|
+
expect(result.content[0].text).toContain('已更新记忆资产: feishu-proxy-bug');
|
|
69
|
+
expect(result.structuredContent.updated).toBe(true);
|
|
70
|
+
expect(updateAssetMock).toHaveBeenCalledWith('asset-1', { summary: 'updated summary' });
|
|
71
|
+
});
|
|
72
|
+
test('缺少 asset_id 时返回错误', async () => {
|
|
73
|
+
const result = await updateMemoryAsset({ summary: 'only summary' });
|
|
74
|
+
expect(result.isError).toBe(true);
|
|
75
|
+
expect(result.content[0].text).toContain('asset_id');
|
|
76
|
+
});
|
|
77
|
+
test('未提供任何更新字段时返回错误', async () => {
|
|
78
|
+
const result = await updateMemoryAsset({ asset_id: 'asset-1' });
|
|
79
|
+
expect(result.isError).toBe(true);
|
|
80
|
+
expect(result.content[0].text).toContain('至少提供一个待更新字段');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { workflow } from '../workflow.js';
|
|
3
|
+
describe('workflow 工具', () => {
|
|
4
|
+
test('新功能意图返回 start_feature 为首工具', async () => {
|
|
5
|
+
const result = await workflow({ intent: '实现订单导出功能' });
|
|
6
|
+
expect(result.isError).toBe(false);
|
|
7
|
+
expect('structuredContent' in result).toBe(true);
|
|
8
|
+
if (!('structuredContent' in result) || !result.structuredContent) {
|
|
9
|
+
throw new Error('missing structuredContent');
|
|
10
|
+
}
|
|
11
|
+
expect(result.structuredContent.firstTool).toBe('start_feature');
|
|
12
|
+
expect(result.structuredContent.scenario).toBe('feature');
|
|
13
|
+
expect(result.content[0].text).toContain('start_feature');
|
|
14
|
+
expect(result.structuredContent.handles?.next_tool).toBe('start_feature');
|
|
15
|
+
});
|
|
16
|
+
test('Bug 意图返回 start_bugfix', async () => {
|
|
17
|
+
const result = await workflow({ intent: '修复支付接口 500 错误', scenario: 'bugfix' });
|
|
18
|
+
expect(result.isError).toBe(false);
|
|
19
|
+
if (!('structuredContent' in result) || !result.structuredContent) {
|
|
20
|
+
throw new Error('missing structuredContent');
|
|
21
|
+
}
|
|
22
|
+
expect(result.structuredContent.firstTool).toBe('start_bugfix');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -4,6 +4,7 @@ import { parseArgs, getString, getNumber, getBoolean } from "../utils/parseArgs.
|
|
|
4
4
|
import { okStructured } from "../lib/response.js";
|
|
5
5
|
import { renderOrchestrationHeader } from "../lib/orchestration-guidance.js";
|
|
6
6
|
import { runCodeInsightBridge, } from "../lib/gitnexus-bridge.js";
|
|
7
|
+
import { attachHandles, DEFAULT_GRAPH_RESOURCE_URI } from "../lib/handles.js";
|
|
7
8
|
import { throwIfAborted, } from "../lib/tool-execution-context.js";
|
|
8
9
|
import { isLikelyProjectNamedRelativePath, buildProjectRootRetryHint, resolveWorkspaceRoot } from "../lib/workspace-root.js";
|
|
9
10
|
import { layoutAbsPath, parseLayoutArgsFromRecord, resolveProjectContextLayout, } from "../lib/project-context-layout.js";
|
|
@@ -370,6 +371,9 @@ ${result.warnings.length > 0 ? `警告: ${result.warnings.join(", ")}` : ""}`.tr
|
|
|
370
371
|
: projectDocs
|
|
371
372
|
? `请按 delegated plan 落盘图谱文档,并更新 ${projectDocs.projectContextFilePath} 的索引入口`
|
|
372
373
|
: null;
|
|
374
|
+
const structuredWithHandles = attachHandles(structured, {
|
|
375
|
+
graph_resource: DEFAULT_GRAPH_RESOURCE_URI,
|
|
376
|
+
});
|
|
373
377
|
return okStructured(delegatedPlan
|
|
374
378
|
? `${renderOrchestrationHeader({
|
|
375
379
|
tool: "code_insight",
|
|
@@ -405,7 +409,7 @@ ${delegatedPlan.kind === "docs" && projectDocs ? `后续操作:
|
|
|
405
409
|
${usageGuide}`
|
|
406
410
|
: `${message}
|
|
407
411
|
|
|
408
|
-
${usageGuide}`,
|
|
412
|
+
${usageGuide}`, structuredWithHandles);
|
|
409
413
|
}
|
|
410
414
|
catch (error) {
|
|
411
415
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { okStructured } from '../lib/response.js';
|
|
2
|
+
import { createMemoryClient } from '../lib/memory-client.js';
|
|
3
|
+
import { handleToolError } from '../utils/error-handler.js';
|
|
4
|
+
import { attachHandles, buildMemoryAssetHandles } from '../lib/handles.js';
|
|
5
|
+
function isConfirmTrue(value) {
|
|
6
|
+
return value === true || value === 'true' || value === 1 || value === '1';
|
|
7
|
+
}
|
|
8
|
+
export async function deleteMemoryAsset(args) {
|
|
9
|
+
try {
|
|
10
|
+
const assetId = typeof args?.asset_id === 'string' ? args.asset_id.trim() : '';
|
|
11
|
+
if (!assetId) {
|
|
12
|
+
throw new Error('缺少必填参数: asset_id');
|
|
13
|
+
}
|
|
14
|
+
const confirm = isConfirmTrue(args?.confirm);
|
|
15
|
+
const client = createMemoryClient();
|
|
16
|
+
if (!client.isReadEnabled()) {
|
|
17
|
+
return okStructured('记忆服务未开启,已跳过删除。', { enabled: false, deleted: false, asset: null });
|
|
18
|
+
}
|
|
19
|
+
if (!confirm) {
|
|
20
|
+
const existing = await client.getAsset(assetId);
|
|
21
|
+
if (!existing) {
|
|
22
|
+
return okStructured(`未找到记忆资产: ${assetId}`, attachHandles({ enabled: true, deleted: false, requires_confirmation: false, asset: null }, { memory_assets: buildMemoryAssetHandles([{ id: assetId }], 'delete_memory_asset') }));
|
|
23
|
+
}
|
|
24
|
+
return okStructured(`待确认删除记忆资产: ${existing.name}。请先用 read_memory_asset 核对内容,确认后带 confirm: true 重新调用 delete_memory_asset。`, attachHandles({
|
|
25
|
+
enabled: true,
|
|
26
|
+
deleted: false,
|
|
27
|
+
requires_confirmation: true,
|
|
28
|
+
preview: {
|
|
29
|
+
id: existing.id,
|
|
30
|
+
name: existing.name,
|
|
31
|
+
type: existing.type,
|
|
32
|
+
summary: existing.summary,
|
|
33
|
+
},
|
|
34
|
+
asset: null,
|
|
35
|
+
}, {
|
|
36
|
+
memory_assets: buildMemoryAssetHandles([{ id: existing.id, name: existing.name, type: existing.type, summary: existing.summary }], 'delete_memory_asset'),
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
const { deleted, asset } = await client.deleteAsset(assetId);
|
|
40
|
+
if (!deleted) {
|
|
41
|
+
return okStructured(`未找到记忆资产: ${assetId}`, attachHandles({ enabled: true, deleted: false, requires_confirmation: false, asset: null }, { memory_assets: buildMemoryAssetHandles([{ id: assetId }], 'delete_memory_asset') }));
|
|
42
|
+
}
|
|
43
|
+
return okStructured(`已删除记忆资产: ${asset?.name ?? assetId}`, attachHandles({
|
|
44
|
+
enabled: true,
|
|
45
|
+
deleted: true,
|
|
46
|
+
requires_confirmation: false,
|
|
47
|
+
asset,
|
|
48
|
+
}, asset
|
|
49
|
+
? {
|
|
50
|
+
memory_assets: buildMemoryAssetHandles([{ id: asset.id, name: asset.name, type: asset.type, summary: asset.summary }], 'delete_memory_asset'),
|
|
51
|
+
}
|
|
52
|
+
: {}));
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return handleToolError(error, 'delete_memory_asset');
|
|
56
|
+
}
|
|
57
|
+
}
|
package/build/tools/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { codeInsight } from "./code_insight.js";
|
|
|
5
5
|
export { gentest } from "./gentest.js";
|
|
6
6
|
export { refactor } from "./refactor.js";
|
|
7
7
|
export { initProjectContext } from "./init_project_context.js";
|
|
8
|
+
export { workflow } from "./workflow.js";
|
|
8
9
|
export { addFeature } from "./add_feature.js";
|
|
9
10
|
export { checkSpec } from "./check_spec.js";
|
|
10
11
|
export { fixBug } from "./fix_bug.js";
|
|
@@ -20,6 +21,8 @@ export { startUi } from "./start_ui.js";
|
|
|
20
21
|
export { searchMemory } from "./search_memory.js";
|
|
21
22
|
export { readMemoryAsset } from "./read_memory_asset.js";
|
|
22
23
|
export { memorizeAsset } from "./memorize_asset.js";
|
|
24
|
+
export { deleteMemoryAsset } from "./delete_memory_asset.js";
|
|
25
|
+
export { updateMemoryAsset } from "./update_memory_asset.js";
|
|
23
26
|
export { scanAndExtractPatterns } from "./scan_and_extract_patterns.js";
|
|
24
27
|
export { startProduct } from "./start_product.js";
|
|
25
28
|
export { gitWorkReport } from "./git_work_report.js";
|
package/build/tools/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export { codeInsight } from "./code_insight.js";
|
|
|
5
5
|
export { gentest } from "./gentest.js";
|
|
6
6
|
export { refactor } from "./refactor.js";
|
|
7
7
|
export { initProjectContext } from "./init_project_context.js";
|
|
8
|
+
export { workflow } from "./workflow.js";
|
|
8
9
|
export { addFeature } from "./add_feature.js";
|
|
9
10
|
export { checkSpec } from "./check_spec.js";
|
|
10
11
|
export { fixBug } from "./fix_bug.js";
|
|
@@ -24,6 +25,8 @@ export { startUi } from "./start_ui.js";
|
|
|
24
25
|
export { searchMemory } from "./search_memory.js";
|
|
25
26
|
export { readMemoryAsset } from "./read_memory_asset.js";
|
|
26
27
|
export { memorizeAsset } from "./memorize_asset.js";
|
|
28
|
+
export { deleteMemoryAsset } from "./delete_memory_asset.js";
|
|
29
|
+
export { updateMemoryAsset } from "./update_memory_asset.js";
|
|
27
30
|
export { scanAndExtractPatterns } from "./scan_and_extract_patterns.js";
|
|
28
31
|
// 产品设计工作流
|
|
29
32
|
export { startProduct } from "./start_product.js";
|
|
@@ -2,6 +2,7 @@ import { parseArgs, getString, getNumber } from '../utils/parseArgs.js';
|
|
|
2
2
|
import { okStructured } from '../lib/response.js';
|
|
3
3
|
import { createMemoryClient } from '../lib/memory-client.js';
|
|
4
4
|
import { formatSearchMemoryResultsText, shouldShowSourceInSearch, } from '../lib/memory-orchestration.js';
|
|
5
|
+
import { attachHandles, buildMemoryAssetHandles } from '../lib/handles.js';
|
|
5
6
|
import { getMemoryConfig } from '../lib/memory-config.js';
|
|
6
7
|
import { handleToolError } from '../utils/error-handler.js';
|
|
7
8
|
export async function searchMemory(args) {
|
|
@@ -46,12 +47,14 @@ export async function searchMemory(args) {
|
|
|
46
47
|
tags: item.tags,
|
|
47
48
|
sourcePath: shouldShowSourceInSearch(item, config) ? item.sourcePath : undefined,
|
|
48
49
|
}));
|
|
49
|
-
return okStructured(formatSearchMemoryResultsText(results, config), {
|
|
50
|
+
return okStructured(formatSearchMemoryResultsText(results, config), attachHandles({
|
|
50
51
|
enabled: true,
|
|
51
52
|
query,
|
|
52
53
|
count: results.length,
|
|
53
54
|
results: items,
|
|
54
|
-
}
|
|
55
|
+
}, {
|
|
56
|
+
memory_assets: buildMemoryAssetHandles(items),
|
|
57
|
+
}));
|
|
55
58
|
}
|
|
56
59
|
catch (error) {
|
|
57
60
|
return handleToolError(error, 'search_memory');
|