mcp-probe-kit 3.6.3 → 3.6.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/build/lib/__tests__/project-context-writer.unit.test.d.ts +1 -0
- package/build/lib/__tests__/project-context-writer.unit.test.js +28 -0
- package/build/lib/feature-spec-writer.d.ts +12 -0
- package/build/lib/feature-spec-writer.js +14 -0
- package/build/lib/file-delivery.d.ts +17 -0
- package/build/lib/file-delivery.js +42 -0
- package/build/lib/graph-insights-writer.d.ts +10 -0
- package/build/lib/graph-insights-writer.js +65 -0
- package/build/lib/init-project-writer.d.ts +7 -0
- package/build/lib/init-project-writer.js +161 -0
- package/build/lib/project-context-writer.d.ts +41 -0
- package/build/lib/project-context-writer.js +120 -0
- package/build/lib/tool-annotations.js +2 -2
- package/build/schemas/output/project-tools.d.ts +136 -0
- package/build/schemas/output/project-tools.js +65 -1
- package/build/tools/__tests__/add_feature.write.unit.test.d.ts +1 -0
- package/build/tools/__tests__/add_feature.write.unit.test.js +30 -0
- package/build/tools/__tests__/code_insight.unit.test.js +2 -4
- package/build/tools/__tests__/init_project.write.unit.test.d.ts +1 -0
- package/build/tools/__tests__/init_project.write.unit.test.js +22 -0
- package/build/tools/__tests__/init_project_context.unit.test.js +49 -45
- package/build/tools/add_feature.js +23 -6
- package/build/tools/code_insight.js +3 -3
- package/build/tools/init_project.js +25 -22
- package/build/tools/init_project_context.js +66 -44
- package/package.json +1 -1
|
@@ -70,6 +70,50 @@ export declare const ProjectInitSchema: {
|
|
|
70
70
|
readonly items: {
|
|
71
71
|
readonly type: "string";
|
|
72
72
|
};
|
|
73
|
+
readonly description: "已废弃,请用 writtenFiles / plannedFiles";
|
|
74
|
+
};
|
|
75
|
+
readonly writtenFiles: {
|
|
76
|
+
readonly type: "array";
|
|
77
|
+
readonly items: {
|
|
78
|
+
readonly type: "string";
|
|
79
|
+
};
|
|
80
|
+
readonly description: "已写入磁盘的路径";
|
|
81
|
+
};
|
|
82
|
+
readonly plannedFiles: {
|
|
83
|
+
readonly type: "array";
|
|
84
|
+
readonly items: {
|
|
85
|
+
readonly type: "string";
|
|
86
|
+
};
|
|
87
|
+
readonly description: "规划中尚未创建的路径";
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
readonly writtenFiles: {
|
|
92
|
+
readonly type: "array";
|
|
93
|
+
readonly items: {
|
|
94
|
+
readonly type: "object";
|
|
95
|
+
readonly properties: {
|
|
96
|
+
readonly path: {
|
|
97
|
+
readonly type: "string";
|
|
98
|
+
};
|
|
99
|
+
readonly action: {
|
|
100
|
+
readonly type: "string";
|
|
101
|
+
readonly enum: readonly ["created", "updated", "skipped"];
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
readonly pendingFiles: {
|
|
107
|
+
readonly type: "array";
|
|
108
|
+
readonly items: {
|
|
109
|
+
readonly type: "object";
|
|
110
|
+
readonly properties: {
|
|
111
|
+
readonly path: {
|
|
112
|
+
readonly type: "string";
|
|
113
|
+
};
|
|
114
|
+
readonly reason: {
|
|
115
|
+
readonly type: "string";
|
|
116
|
+
};
|
|
73
117
|
};
|
|
74
118
|
};
|
|
75
119
|
};
|
|
@@ -174,6 +218,36 @@ export declare const ProjectContextSchema: {
|
|
|
174
218
|
};
|
|
175
219
|
};
|
|
176
220
|
};
|
|
221
|
+
readonly description: "文档索引(含已写入与待生成)";
|
|
222
|
+
};
|
|
223
|
+
readonly writtenFiles: {
|
|
224
|
+
readonly type: "array";
|
|
225
|
+
readonly items: {
|
|
226
|
+
readonly type: "object";
|
|
227
|
+
readonly properties: {
|
|
228
|
+
readonly path: {
|
|
229
|
+
readonly type: "string";
|
|
230
|
+
};
|
|
231
|
+
readonly action: {
|
|
232
|
+
readonly type: "string";
|
|
233
|
+
readonly enum: readonly ["created", "updated", "skipped"];
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
readonly pendingFiles: {
|
|
239
|
+
readonly type: "array";
|
|
240
|
+
readonly items: {
|
|
241
|
+
readonly type: "object";
|
|
242
|
+
readonly properties: {
|
|
243
|
+
readonly path: {
|
|
244
|
+
readonly type: "string";
|
|
245
|
+
};
|
|
246
|
+
readonly reason: {
|
|
247
|
+
readonly type: "string";
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
};
|
|
177
251
|
};
|
|
178
252
|
readonly nextSteps: {
|
|
179
253
|
readonly type: "array";
|
|
@@ -268,6 +342,41 @@ export declare const FeatureSpecSchema: {
|
|
|
268
342
|
};
|
|
269
343
|
};
|
|
270
344
|
};
|
|
345
|
+
readonly writtenFiles: {
|
|
346
|
+
readonly type: "array";
|
|
347
|
+
readonly items: {
|
|
348
|
+
readonly type: "object";
|
|
349
|
+
readonly properties: {
|
|
350
|
+
readonly path: {
|
|
351
|
+
readonly type: "string";
|
|
352
|
+
};
|
|
353
|
+
readonly action: {
|
|
354
|
+
readonly type: "string";
|
|
355
|
+
readonly enum: readonly ["created", "updated", "skipped"];
|
|
356
|
+
};
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
readonly pendingFiles: {
|
|
361
|
+
readonly type: "array";
|
|
362
|
+
readonly items: {
|
|
363
|
+
readonly type: "object";
|
|
364
|
+
readonly properties: {
|
|
365
|
+
readonly path: {
|
|
366
|
+
readonly type: "string";
|
|
367
|
+
};
|
|
368
|
+
readonly reason: {
|
|
369
|
+
readonly type: "string";
|
|
370
|
+
};
|
|
371
|
+
};
|
|
372
|
+
};
|
|
373
|
+
};
|
|
374
|
+
readonly specPaths: {
|
|
375
|
+
readonly type: "array";
|
|
376
|
+
readonly items: {
|
|
377
|
+
readonly type: "string";
|
|
378
|
+
};
|
|
379
|
+
};
|
|
271
380
|
};
|
|
272
381
|
readonly required: readonly ["summary", "featureName", "requirements", "tasks"];
|
|
273
382
|
};
|
|
@@ -459,7 +568,17 @@ export interface ProjectInit {
|
|
|
459
568
|
structure: {
|
|
460
569
|
directories?: string[];
|
|
461
570
|
files?: string[];
|
|
571
|
+
writtenFiles?: string[];
|
|
572
|
+
plannedFiles?: string[];
|
|
462
573
|
};
|
|
574
|
+
writtenFiles?: Array<{
|
|
575
|
+
path: string;
|
|
576
|
+
action: 'created' | 'updated' | 'skipped';
|
|
577
|
+
}>;
|
|
578
|
+
pendingFiles?: Array<{
|
|
579
|
+
path: string;
|
|
580
|
+
reason: string;
|
|
581
|
+
}>;
|
|
463
582
|
techStack?: string[];
|
|
464
583
|
dependencies?: Record<string, string>;
|
|
465
584
|
scripts?: Record<string, string>;
|
|
@@ -486,6 +605,14 @@ export interface ProjectContext {
|
|
|
486
605
|
}>;
|
|
487
606
|
nextSteps?: string[];
|
|
488
607
|
metadata?: Record<string, any>;
|
|
608
|
+
writtenFiles?: Array<{
|
|
609
|
+
path: string;
|
|
610
|
+
action: 'created' | 'updated' | 'skipped';
|
|
611
|
+
}>;
|
|
612
|
+
pendingFiles?: Array<{
|
|
613
|
+
path: string;
|
|
614
|
+
reason: string;
|
|
615
|
+
}>;
|
|
489
616
|
}
|
|
490
617
|
export interface FeatureSpec {
|
|
491
618
|
summary: string;
|
|
@@ -509,6 +636,15 @@ export interface FeatureSpec {
|
|
|
509
636
|
normal?: string;
|
|
510
637
|
pessimistic?: string;
|
|
511
638
|
};
|
|
639
|
+
writtenFiles?: Array<{
|
|
640
|
+
path: string;
|
|
641
|
+
action: 'created' | 'updated' | 'skipped';
|
|
642
|
+
}>;
|
|
643
|
+
pendingFiles?: Array<{
|
|
644
|
+
path: string;
|
|
645
|
+
reason: string;
|
|
646
|
+
}>;
|
|
647
|
+
specPaths?: string[];
|
|
512
648
|
}
|
|
513
649
|
export interface Estimate {
|
|
514
650
|
summary: string;
|
|
@@ -32,7 +32,29 @@ export const ProjectInitSchema = {
|
|
|
32
32
|
description: '项目结构',
|
|
33
33
|
properties: {
|
|
34
34
|
directories: { type: 'array', items: { type: 'string' } },
|
|
35
|
-
files: { type: 'array', items: { type: 'string' } },
|
|
35
|
+
files: { type: 'array', items: { type: 'string' }, description: '已废弃,请用 writtenFiles / plannedFiles' },
|
|
36
|
+
writtenFiles: { type: 'array', items: { type: 'string' }, description: '已写入磁盘的路径' },
|
|
37
|
+
plannedFiles: { type: 'array', items: { type: 'string' }, description: '规划中尚未创建的路径' },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
writtenFiles: {
|
|
41
|
+
type: 'array',
|
|
42
|
+
items: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {
|
|
45
|
+
path: { type: 'string' },
|
|
46
|
+
action: { type: 'string', enum: ['created', 'updated', 'skipped'] },
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
pendingFiles: {
|
|
51
|
+
type: 'array',
|
|
52
|
+
items: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
path: { type: 'string' },
|
|
56
|
+
reason: { type: 'string' },
|
|
57
|
+
},
|
|
36
58
|
},
|
|
37
59
|
},
|
|
38
60
|
techStack: {
|
|
@@ -100,6 +122,27 @@ export const ProjectContextSchema = {
|
|
|
100
122
|
purpose: { type: 'string' },
|
|
101
123
|
},
|
|
102
124
|
},
|
|
125
|
+
description: '文档索引(含已写入与待生成)',
|
|
126
|
+
},
|
|
127
|
+
writtenFiles: {
|
|
128
|
+
type: 'array',
|
|
129
|
+
items: {
|
|
130
|
+
type: 'object',
|
|
131
|
+
properties: {
|
|
132
|
+
path: { type: 'string' },
|
|
133
|
+
action: { type: 'string', enum: ['created', 'updated', 'skipped'] },
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
pendingFiles: {
|
|
138
|
+
type: 'array',
|
|
139
|
+
items: {
|
|
140
|
+
type: 'object',
|
|
141
|
+
properties: {
|
|
142
|
+
path: { type: 'string' },
|
|
143
|
+
reason: { type: 'string' },
|
|
144
|
+
},
|
|
145
|
+
},
|
|
103
146
|
},
|
|
104
147
|
nextSteps: {
|
|
105
148
|
type: 'array',
|
|
@@ -156,6 +199,27 @@ export const FeatureSpecSchema = {
|
|
|
156
199
|
pessimistic: { type: 'string' },
|
|
157
200
|
},
|
|
158
201
|
},
|
|
202
|
+
writtenFiles: {
|
|
203
|
+
type: 'array',
|
|
204
|
+
items: {
|
|
205
|
+
type: 'object',
|
|
206
|
+
properties: {
|
|
207
|
+
path: { type: 'string' },
|
|
208
|
+
action: { type: 'string', enum: ['created', 'updated', 'skipped'] },
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
pendingFiles: {
|
|
213
|
+
type: 'array',
|
|
214
|
+
items: {
|
|
215
|
+
type: 'object',
|
|
216
|
+
properties: {
|
|
217
|
+
path: { type: 'string' },
|
|
218
|
+
reason: { type: 'string' },
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
specPaths: { type: 'array', items: { type: 'string' } },
|
|
159
223
|
},
|
|
160
224
|
required: ['summary', 'featureName', 'requirements', 'tasks'],
|
|
161
225
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { describe, expect, test } from "vitest";
|
|
5
|
+
import { addFeature } from "../add_feature.js";
|
|
6
|
+
describe("add_feature 不落盘", () => {
|
|
7
|
+
test("仅返回模板与 pendingFiles,不写 specs 文件", async () => {
|
|
8
|
+
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "mcp-probe-add-feature-"));
|
|
9
|
+
const cwd = process.cwd();
|
|
10
|
+
process.chdir(projectRoot);
|
|
11
|
+
try {
|
|
12
|
+
const result = await addFeature({
|
|
13
|
+
feature_name: "user-auth",
|
|
14
|
+
description: "用户登录与注册",
|
|
15
|
+
});
|
|
16
|
+
expect(result.isError).toBeFalsy();
|
|
17
|
+
const structured = result.structuredContent;
|
|
18
|
+
expect(structured.pendingFiles).toHaveLength(3);
|
|
19
|
+
expect(structured.writtenFiles).toBeUndefined();
|
|
20
|
+
expect(fs.existsSync(path.join(projectRoot, "docs", "specs", "user-auth", "requirements.md"))).toBe(false);
|
|
21
|
+
const text = String(result.content[0].text);
|
|
22
|
+
expect(text).toMatch(/由 Agent 落盘/);
|
|
23
|
+
expect(text).toMatch(/MCP \*\*不会\*\*写入磁盘/);
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
process.chdir(cwd);
|
|
27
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -128,7 +128,7 @@ describe("code_insight 单元测试", () => {
|
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
});
|
|
131
|
-
test("
|
|
131
|
+
test("save_to_docs 时返回 delegated plan,由 Agent 落盘图谱文件", async () => {
|
|
132
132
|
const prev = process.env.MCP_ENABLE_GITNEXUS_BRIDGE;
|
|
133
133
|
process.env.MCP_ENABLE_GITNEXUS_BRIDGE = "0";
|
|
134
134
|
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "code-insight-docs-"));
|
|
@@ -146,13 +146,11 @@ describe("code_insight 单元测试", () => {
|
|
|
146
146
|
expect(text).toMatch(/使用场景指南/);
|
|
147
147
|
expect(structured.projectDocs.latestMarkdownFilePath).toMatch(/docs\/graph-insights\/latest\.md$/);
|
|
148
148
|
expect(structured.projectDocs.archiveMarkdownFilePath).toContain("/docs/graph-insights/");
|
|
149
|
-
expect(structured.projectDocs.projectContextFilePath).toMatch(/\/(AGENTS\.md|docs\/project-context\.md)$/);
|
|
150
|
-
expect(structured.projectDocs.navigationSnippet).toMatch(/代码图谱洞察/);
|
|
151
149
|
expect(structured.plan.mode).toBe("delegated");
|
|
152
150
|
expect(structured.plan.steps).toHaveLength(2);
|
|
153
151
|
expect(structured.plan.steps[0].id).toBe("consume-result");
|
|
154
152
|
expect(structured.plan.steps[1].id).toBe("optional-save");
|
|
155
|
-
expect(structured.
|
|
153
|
+
expect(structured.writtenFiles).toBeUndefined();
|
|
156
154
|
expect(fs.existsSync(path.join(projectRoot, "docs", "graph-insights", "latest.md"))).toBe(false);
|
|
157
155
|
}
|
|
158
156
|
finally {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { describe, expect, test } from "vitest";
|
|
5
|
+
import { initProject } from "../init_project.js";
|
|
6
|
+
describe("init_project 落盘边界", () => {
|
|
7
|
+
test("仅写入 Skill 与 AGENTS.md,docs 由 Agent 创建", async () => {
|
|
8
|
+
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "mcp-probe-init-project-"));
|
|
9
|
+
const result = await initProject({
|
|
10
|
+
project_name: "Demo App",
|
|
11
|
+
project_root: projectRoot,
|
|
12
|
+
input: "一个简单的演示应用",
|
|
13
|
+
});
|
|
14
|
+
expect(result.isError).toBeFalsy();
|
|
15
|
+
const structured = result.structuredContent;
|
|
16
|
+
expect(structured.writtenFiles.length).toBe(2);
|
|
17
|
+
expect(structured.pendingFiles.length).toBeGreaterThan(0);
|
|
18
|
+
expect(fs.existsSync(path.join(projectRoot, "AGENTS.md"))).toBe(true);
|
|
19
|
+
expect(fs.existsSync(path.join(projectRoot, "docs", "project-context.md"))).toBe(false);
|
|
20
|
+
expect(fs.existsSync(path.join(projectRoot, "docs", "specs", "demo-app", "requirements.md"))).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -1,74 +1,78 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import os from
|
|
3
|
-
import path from
|
|
4
|
-
import { describe, expect, test } from
|
|
5
|
-
import { initProjectContext } from
|
|
6
|
-
describe(
|
|
7
|
-
test(
|
|
8
|
-
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(),
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { describe, expect, test } from "vitest";
|
|
5
|
+
import { initProjectContext } from "../init_project_context.js";
|
|
6
|
+
describe("init_project_context 单元测试", () => {
|
|
7
|
+
test("MCP 仅写入 AGENTS.md 与 layout.json", async () => {
|
|
8
|
+
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "mcp-probe-kit-init-"));
|
|
9
9
|
const result = await initProjectContext({
|
|
10
|
-
docs_dir:
|
|
10
|
+
docs_dir: "docs",
|
|
11
11
|
project_root: projectRoot,
|
|
12
12
|
});
|
|
13
13
|
expect(result.isError).toBeFalsy();
|
|
14
|
-
expect(
|
|
15
|
-
if (!(
|
|
16
|
-
throw new Error(
|
|
14
|
+
expect("structuredContent" in result).toBe(true);
|
|
15
|
+
if (!("structuredContent" in result)) {
|
|
16
|
+
throw new Error("structuredContent 缺失");
|
|
17
17
|
}
|
|
18
18
|
const structured = result.structuredContent;
|
|
19
|
-
expect(structured.
|
|
20
|
-
expect(structured.
|
|
21
|
-
expect(structured.
|
|
22
|
-
expect(
|
|
23
|
-
expect(
|
|
24
|
-
expect(fs.existsSync(path.join(projectRoot,
|
|
19
|
+
expect(structured.writtenFiles.some((f) => f.path === "AGENTS.md")).toBe(true);
|
|
20
|
+
expect(structured.writtenFiles.some((f) => f.path === "docs/.mcp-probe/layout.json")).toBe(true);
|
|
21
|
+
expect(structured.pendingFiles.some((f) => f.path === "docs/project-context.md")).toBe(true);
|
|
22
|
+
expect(fs.existsSync(path.join(projectRoot, "docs", ".mcp-probe", "layout.json"))).toBe(true);
|
|
23
|
+
expect(fs.existsSync(path.join(projectRoot, "AGENTS.md"))).toBe(true);
|
|
24
|
+
expect(fs.existsSync(path.join(projectRoot, "docs", "project-context.md"))).toBe(false);
|
|
25
|
+
expect(fs.existsSync(path.join(projectRoot, "docs", "project-context", "tech-stack.md"))).toBe(false);
|
|
25
26
|
const plan = structured.metadata?.plan;
|
|
26
|
-
expect(plan?.mode).toBe(
|
|
27
|
+
expect(plan?.mode).toBe("delegated");
|
|
27
28
|
expect(plan.steps.map((step) => step.id)).toEqual([
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
'finalize-agents-md',
|
|
29
|
+
"write-modular-docs",
|
|
30
|
+
"bootstrap-code-insight",
|
|
31
|
+
"persist-graph-docs",
|
|
32
32
|
]);
|
|
33
|
-
|
|
34
|
-
expect(
|
|
33
|
+
const text = result.content[0].text;
|
|
34
|
+
expect(text).toMatch(/文件落盘状态/);
|
|
35
|
+
expect(text).toMatch(/尚未创建/);
|
|
35
36
|
});
|
|
36
|
-
test(
|
|
37
|
-
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(),
|
|
37
|
+
test("输出文本包含 AGENTS.md 与 MCP 触发规则", async () => {
|
|
38
|
+
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "mcp-probe-kit-init-"));
|
|
38
39
|
const result = await initProjectContext({
|
|
39
40
|
project_root: projectRoot,
|
|
40
41
|
});
|
|
41
42
|
const text = result.content[0].text;
|
|
42
43
|
expect(text).toMatch(/AGENTS\.md/);
|
|
43
|
-
expect(text).toMatch(
|
|
44
|
-
expect(text).toMatch(/finalize-agents-md/);
|
|
44
|
+
expect(text).toMatch(/文件落盘状态/);
|
|
45
45
|
});
|
|
46
|
-
test(
|
|
47
|
-
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(),
|
|
48
|
-
fs.mkdirSync(path.join(projectRoot,
|
|
49
|
-
fs.writeFileSync(path.join(projectRoot,
|
|
46
|
+
test("已存在 project-context 分类文档时跳过重写 modular", async () => {
|
|
47
|
+
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "mcp-probe-kit-context-"));
|
|
48
|
+
fs.mkdirSync(path.join(projectRoot, "docs", "project-context"), { recursive: true });
|
|
49
|
+
fs.writeFileSync(path.join(projectRoot, "docs", "project-context.md"), "# existing context\n", "utf8");
|
|
50
50
|
const result = await initProjectContext({
|
|
51
|
-
docs_dir:
|
|
51
|
+
docs_dir: "docs",
|
|
52
52
|
project_root: projectRoot,
|
|
53
53
|
});
|
|
54
54
|
expect(result.isError).toBeFalsy();
|
|
55
55
|
const structured = result.structuredContent;
|
|
56
56
|
expect(structured.metadata?.legacyProjectContextExists).toBe(true);
|
|
57
57
|
expect(structured.metadata?.plan?.steps.map((step) => step.id)).toEqual([
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
'finalize-agents-md',
|
|
58
|
+
"bootstrap-code-insight",
|
|
59
|
+
"persist-graph-docs",
|
|
61
60
|
]);
|
|
61
|
+
expect(structured.pendingFiles.every((f) => !f.path.startsWith("docs/project-context"))).toBe(true);
|
|
62
|
+
expect(fs.readFileSync(path.join(projectRoot, "docs", "project-context.md"), "utf8")).toBe("# existing context\n");
|
|
63
|
+
expect(fs.existsSync(path.join(projectRoot, "AGENTS.md"))).toBe(true);
|
|
62
64
|
const text = result.content[0].text;
|
|
63
65
|
expect(text).toMatch(/保留/);
|
|
64
66
|
});
|
|
65
|
-
test(
|
|
66
|
-
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(),
|
|
67
|
-
fs.writeFileSync(path.join(projectRoot,
|
|
68
|
-
const result = await initProjectContext({
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
expect(
|
|
67
|
+
test("已有 AGENTS.md 时 merge 而非覆盖", async () => {
|
|
68
|
+
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "mcp-probe-kit-agents-"));
|
|
69
|
+
fs.writeFileSync(path.join(projectRoot, "AGENTS.md"), "# Custom rules\n", "utf8");
|
|
70
|
+
const result = await initProjectContext({
|
|
71
|
+
project_root: projectRoot,
|
|
72
|
+
});
|
|
73
|
+
const agents = fs.readFileSync(path.join(projectRoot, "AGENTS.md"), "utf8");
|
|
74
|
+
expect(agents).toContain("# Custom rules");
|
|
75
|
+
expect(agents).toMatch(/mcp-probe:context/);
|
|
76
|
+
expect(result.structuredContent.metadata?.agentsMdMergeMode).not.toBe("replaced");
|
|
73
77
|
});
|
|
74
78
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { parseArgs, getString } from "../utils/parseArgs.js";
|
|
2
|
-
import {
|
|
2
|
+
import { okStructured } from "../lib/response.js";
|
|
3
3
|
import { loadTemplate, normalizeTemplateProfile } from "../lib/template-loader.js";
|
|
4
4
|
import { handleToolError } from "../utils/error-handler.js";
|
|
5
5
|
/**
|
|
6
6
|
* add_feature 工具
|
|
7
7
|
*
|
|
8
8
|
* 功能:为已有项目添加新功能的规格文档
|
|
9
|
-
*
|
|
9
|
+
* 模式:指令生成器 — MCP 返回模板与要求,由调用方 Agent 按模板落盘
|
|
10
10
|
*
|
|
11
11
|
* 输出文件:
|
|
12
12
|
* - docs/specs/{feature_name}/requirements.md - 需求文档
|
|
@@ -236,9 +236,9 @@ ${combinedValidation.warnings.length > 0 ? `- 其他警告: ${combinedValidation
|
|
|
236
236
|
|
|
237
237
|
---
|
|
238
238
|
|
|
239
|
-
## 📝
|
|
239
|
+
## 📝 创建文档(由 Agent 落盘)
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
MCP **不会**写入磁盘。请根据下方模板,在 \`${docsDir}/specs/${featureName}/\` 创建三个文件并替换占位符。
|
|
242
242
|
|
|
243
243
|
### 文件 1: requirements.md
|
|
244
244
|
|
|
@@ -286,9 +286,26 @@ ${fenceClose}
|
|
|
286
286
|
*指南版本: 1.1.0*
|
|
287
287
|
*工具: MCP Probe Kit - add_feature*
|
|
288
288
|
`;
|
|
289
|
-
|
|
289
|
+
const specPaths = [
|
|
290
|
+
`${docsDir}/specs/${featureName}/requirements.md`,
|
|
291
|
+
`${docsDir}/specs/${featureName}/design.md`,
|
|
292
|
+
`${docsDir}/specs/${featureName}/tasks.md`,
|
|
293
|
+
];
|
|
294
|
+
const pendingFiles = specPaths.map((specPath) => ({
|
|
295
|
+
path: specPath,
|
|
296
|
+
reason: "由 Agent 根据本工具返回的模板写入",
|
|
297
|
+
}));
|
|
298
|
+
const structuredData = {
|
|
299
|
+
summary: `功能规格:${featureName}`,
|
|
300
|
+
featureName,
|
|
301
|
+
requirements: ["见 requirements.md 模板"],
|
|
302
|
+
tasks: [{ id: "1", title: "按模板创建 specs 文档", description: "将下方模板落盘并补充真实内容" }],
|
|
303
|
+
pendingFiles,
|
|
304
|
+
specPaths,
|
|
305
|
+
};
|
|
306
|
+
return okStructured(guide, structuredData, {
|
|
290
307
|
schema: (await import("../schemas/output/project-tools.js")).FeatureSpecSchema,
|
|
291
|
-
note: "
|
|
308
|
+
note: "本工具返回模板与要求,不代写文件;Agent 须按指南创建 specs",
|
|
292
309
|
template: {
|
|
293
310
|
profile: templateProfile,
|
|
294
311
|
requested: profileDecision.requested,
|
|
@@ -197,7 +197,7 @@ function renderUsageGuide() {
|
|
|
197
197
|
## 下一步建议
|
|
198
198
|
- 查询不精确: 增加 \`goal\`(例如“理解登录认证流程”)
|
|
199
199
|
- 出现歧义: 传入 \`uid\` 或 \`file_path\` 重新执行
|
|
200
|
-
- 需要落盘: 传 \`save_to_docs: true\`,再按 delegated plan 写入 docs/graph-insights`;
|
|
200
|
+
- 需要落盘: 传 \`save_to_docs: true\`,再按 delegated plan 由 Agent 写入 docs/graph-insights`;
|
|
201
201
|
}
|
|
202
202
|
export function resolveCodeInsightQuery(input) {
|
|
203
203
|
const finalTarget = input.target || ((input.mode === "context" || input.mode === "impact") ? input.input : "");
|
|
@@ -369,7 +369,7 @@ ${result.warnings.length > 0 ? `警告: ${result.warnings.join(", ")}` : ""}`.tr
|
|
|
369
369
|
structured.nextAction = delegatedPlan?.kind === "ambiguity"
|
|
370
370
|
? "请先选择 uid 或 file_path 重新调用 code_insight 完成消歧"
|
|
371
371
|
: projectDocs
|
|
372
|
-
? `请按 delegated plan 落盘图谱文档,并更新 ${projectDocs.projectContextFilePath} 的索引入口`
|
|
372
|
+
? `请按 delegated plan 由 Agent 落盘图谱文档,并更新 ${projectDocs.projectContextFilePath} 的索引入口`
|
|
373
373
|
: null;
|
|
374
374
|
const structuredWithHandles = attachHandles(structured, {
|
|
375
375
|
graph_resource: DEFAULT_GRAPH_RESOURCE_URI,
|
|
@@ -379,7 +379,7 @@ ${result.warnings.length > 0 ? `警告: ${result.warnings.join(", ")}` : ""}`.tr
|
|
|
379
379
|
tool: "code_insight",
|
|
380
380
|
goal: delegatedPlan.kind === "ambiguity"
|
|
381
381
|
? "先完成符号消歧,再继续图谱分析"
|
|
382
|
-
: "
|
|
382
|
+
: "完成图谱分析后,由 Agent 按 delegated plan 落盘到 docs/graph-insights",
|
|
383
383
|
tasks: delegatedPlan.kind === "ambiguity"
|
|
384
384
|
? [
|
|
385
385
|
"先阅读本次 code_insight 返回的 candidates",
|
|
@@ -37,6 +37,21 @@ export async function initProject(args) {
|
|
|
37
37
|
: "";
|
|
38
38
|
const featureSlug = projectName.toLowerCase().replace(/\s+/g, '-');
|
|
39
39
|
const agentsRel = toPosixPath(bootstrap.agentsMd.path);
|
|
40
|
+
const bootstrapWritten = [
|
|
41
|
+
{ path: MCP_PROBE_SKILL_REL_PATH, action: bootstrap.skill.created ? "created" : bootstrap.skill.updated ? "updated" : "skipped" },
|
|
42
|
+
{ path: agentsRel, action: bootstrap.agentsMd.created ? "created" : bootstrap.agentsMd.updated ? "updated" : "skipped" },
|
|
43
|
+
];
|
|
44
|
+
const pendingFiles = [
|
|
45
|
+
{ path: "docs/project-context.md", reason: "由 Agent 按指南创建" },
|
|
46
|
+
{ path: "docs/constitution.md", reason: "由 Agent 按指南创建" },
|
|
47
|
+
{ path: `docs/specs/${featureSlug}/requirements.md`, reason: "由 Agent 按指南创建" },
|
|
48
|
+
{ path: `docs/specs/${featureSlug}/design.md`, reason: "由 Agent 按指南创建" },
|
|
49
|
+
{ path: `docs/specs/${featureSlug}/tasks.md`, reason: "由 Agent 按指南创建" },
|
|
50
|
+
{ path: `docs/specs/${featureSlug}/research.md`, reason: "由 Agent 按指南创建" },
|
|
51
|
+
{ path: "scripts/check-prerequisites.sh", reason: "由 Agent 按指南创建" },
|
|
52
|
+
{ path: "scripts/setup.sh", reason: "由 Agent 按指南创建" },
|
|
53
|
+
{ path: "src/", reason: "由 Agent 按项目类型创建源代码目录" },
|
|
54
|
+
];
|
|
40
55
|
const message = `你需要按照 Spec-Driven Development(规范驱动开发)的方式初始化项目,参考 https://github.com/github/spec-kit 的工作流程。
|
|
41
56
|
|
|
42
57
|
📋 **项目需求**:
|
|
@@ -208,7 +223,7 @@ ${warningBlock}
|
|
|
208
223
|
7. 识别可以并行执行的任务以优化开发效率
|
|
209
224
|
|
|
210
225
|
🚀 **开始执行**:
|
|
211
|
-
|
|
226
|
+
MCP 已写入 Skill 与 AGENTS.md。请按上述步骤由 Agent 创建 docs、scripts、src 及 specs 文档。`;
|
|
212
227
|
// 创建结构化数据对象
|
|
213
228
|
const structuredData = {
|
|
214
229
|
summary: `初始化项目:${projectName}`,
|
|
@@ -234,29 +249,17 @@ ${warningBlock}
|
|
|
234
249
|
'scripts/',
|
|
235
250
|
'src/'
|
|
236
251
|
],
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
agentsRel,
|
|
240
|
-
'docs/project-context.md',
|
|
241
|
-
'docs/constitution.md',
|
|
242
|
-
`docs/specs/${featureSlug}/requirements.md`,
|
|
243
|
-
`docs/specs/${featureSlug}/design.md`,
|
|
244
|
-
`docs/specs/${featureSlug}/tasks.md`,
|
|
245
|
-
`docs/specs/${featureSlug}/research.md`,
|
|
246
|
-
'scripts/check-prerequisites.sh',
|
|
247
|
-
'scripts/setup.sh'
|
|
248
|
-
]
|
|
252
|
+
writtenFiles: bootstrapWritten.map((file) => file.path),
|
|
253
|
+
plannedFiles: pendingFiles.map((file) => file.path),
|
|
249
254
|
},
|
|
255
|
+
writtenFiles: bootstrapWritten,
|
|
256
|
+
pendingFiles,
|
|
250
257
|
nextSteps: [
|
|
251
|
-
'确认
|
|
252
|
-
'
|
|
253
|
-
'
|
|
254
|
-
'
|
|
255
|
-
'
|
|
256
|
-
'生成设计文档 design.md',
|
|
257
|
-
'生成任务清单 tasks.md',
|
|
258
|
-
'生成技术调研 research.md',
|
|
259
|
-
'创建辅助脚本'
|
|
258
|
+
'确认 Skill 与 AGENTS.md 已落盘',
|
|
259
|
+
'按指南创建 docs/ 文档与 specs',
|
|
260
|
+
'创建 scripts/ 辅助脚本',
|
|
261
|
+
'创建 src/ 源代码目录',
|
|
262
|
+
'运行 init_project_context 生成完整上下文与图谱',
|
|
260
263
|
]
|
|
261
264
|
};
|
|
262
265
|
return okStructured(message, structuredData, {
|