mcp-probe-kit 3.6.8 → 3.6.10

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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,62 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { afterEach, describe, expect, test } from "vitest";
5
+ import { buildFileStatusEntries } from "../file-delivery.js";
6
+ const tempDirs = [];
7
+ afterEach(() => {
8
+ while (tempDirs.length > 0) {
9
+ const dir = tempDirs.pop();
10
+ if (dir) {
11
+ fs.rmSync(dir, { recursive: true, force: true });
12
+ }
13
+ }
14
+ });
15
+ describe("buildFileStatusEntries", () => {
16
+ test("marks MCP-created files as written", () => {
17
+ const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "file-delivery-"));
18
+ tempDirs.push(projectRoot);
19
+ const result = buildFileStatusEntries(projectRoot, [{ path: "docs/guide.md", purpose: "User guide" }], [{ path: "docs/guide.md", action: "created" }], []);
20
+ expect(result).toEqual([
21
+ {
22
+ path: "docs/guide.md",
23
+ purpose: "User guide",
24
+ exists: false,
25
+ written: true,
26
+ agent_action_required: false,
27
+ },
28
+ ]);
29
+ });
30
+ test("treats pre-existing on-disk files as written when not pending", () => {
31
+ const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "file-delivery-"));
32
+ tempDirs.push(projectRoot);
33
+ const relPath = "docs/existing.md";
34
+ fs.mkdirSync(path.join(projectRoot, "docs"), { recursive: true });
35
+ fs.writeFileSync(path.join(projectRoot, relPath), "# existing", "utf8");
36
+ const result = buildFileStatusEntries(projectRoot, [{ path: relPath }], [], []);
37
+ expect(result).toEqual([
38
+ {
39
+ path: relPath,
40
+ exists: true,
41
+ written: true,
42
+ agent_action_required: false,
43
+ },
44
+ ]);
45
+ });
46
+ test("pending files require agent action and are not written", () => {
47
+ const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "file-delivery-"));
48
+ tempDirs.push(projectRoot);
49
+ const relPath = "docs/pending.md";
50
+ fs.mkdirSync(path.join(projectRoot, "docs"), { recursive: true });
51
+ fs.writeFileSync(path.join(projectRoot, relPath), "stub", "utf8");
52
+ const result = buildFileStatusEntries(projectRoot, [{ path: relPath }], [{ path: relPath, action: "skipped" }], [{ path: relPath, reason: "Agent must fill template" }]);
53
+ expect(result).toEqual([
54
+ {
55
+ path: relPath,
56
+ exists: true,
57
+ written: false,
58
+ agent_action_required: true,
59
+ },
60
+ ]);
61
+ });
62
+ });
@@ -3,7 +3,7 @@ import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import spawn from "cross-spawn";
5
5
  import { afterEach, describe, expect, test } from "vitest";
6
- import { extractResolvedSymbolIdFromContext, prepareBridgeWorkspace, resolveExecutableCommand, resolveGitNexusBridgeCommand, resolveSpawnCommand, rerankQueryStructuredContent, } from "../gitnexus-bridge.js";
6
+ import { extractResolvedSymbolIdFromContext, prepareBridgeWorkspace, resolveExecutableCommand, resolveGitNexusBridgeCommand, resolveSpawnCommand, rerankQueryStructuredContent, tryRefreshWorkspaceIndex, } from "../gitnexus-bridge.js";
7
7
  const tempRoots = [];
8
8
  afterEach(() => {
9
9
  while (tempRoots.length > 0) {
@@ -123,6 +123,33 @@ describe("gitnexus-bridge workspace preparation", () => {
123
123
  expect(resolved.endsWith(".exe") || resolved === "git" || resolved === "git.cmd").toBe(true);
124
124
  expect(spawned.command.toLowerCase()).toContain("git");
125
125
  });
126
+ test("index refresh 失败时返回错误信息而不抛错", async () => {
127
+ const repoRoot = makeTempDir("gitnexus-index-fail-");
128
+ fs.mkdirSync(path.join(repoRoot, ".git"));
129
+ const prevCommand = process.env.MCP_GITNEXUS_COMMAND;
130
+ const prevPath = process.env.PATH;
131
+ process.env.MCP_GITNEXUS_COMMAND = path.join(repoRoot, "missing-gitnexus.cmd");
132
+ process.env.PATH = repoRoot;
133
+ try {
134
+ const workspace = await prepareBridgeWorkspace(repoRoot);
135
+ const error = await tryRefreshWorkspaceIndex(workspace);
136
+ expect(error).toBeTruthy();
137
+ }
138
+ finally {
139
+ if (prevCommand === undefined) {
140
+ delete process.env.MCP_GITNEXUS_COMMAND;
141
+ }
142
+ else {
143
+ process.env.MCP_GITNEXUS_COMMAND = prevCommand;
144
+ }
145
+ if (prevPath === undefined) {
146
+ delete process.env.PATH;
147
+ }
148
+ else {
149
+ process.env.PATH = prevPath;
150
+ }
151
+ }
152
+ });
126
153
  test("git 目录直接使用源仓库", async () => {
127
154
  const repoRoot = makeTempDir("gitnexus-direct-");
128
155
  fs.mkdirSync(path.join(repoRoot, ".git"));
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, test, afterEach } from 'vitest';
2
2
  import { isAutoTaskTool, shouldAutoEscalateToTask } from '../task-defaults.js';
3
3
  const originalDisable = process.env.MCP_DISABLE_AUTO_TASK;
4
+ const originalEnable = process.env.MCP_ENABLE_AUTO_TASK;
4
5
  afterEach(() => {
5
6
  if (originalDisable === undefined) {
6
7
  delete process.env.MCP_DISABLE_AUTO_TASK;
@@ -8,6 +9,12 @@ afterEach(() => {
8
9
  else {
9
10
  process.env.MCP_DISABLE_AUTO_TASK = originalDisable;
10
11
  }
12
+ if (originalEnable === undefined) {
13
+ delete process.env.MCP_ENABLE_AUTO_TASK;
14
+ }
15
+ else {
16
+ process.env.MCP_ENABLE_AUTO_TASK = originalEnable;
17
+ }
11
18
  });
12
19
  describe('task-defaults', () => {
13
20
  test('code_insight 与 scan 属于自动 Task 工具', () => {
@@ -15,12 +22,19 @@ describe('task-defaults', () => {
15
22
  expect(isAutoTaskTool('scan_and_extract_patterns')).toBe(true);
16
23
  expect(isAutoTaskTool('search_memory')).toBe(false);
17
24
  });
18
- test('默认对长耗时工具自动升级为 Task', () => {
25
+ test('默认不自动升级为 Task,避免 Agent 客户端拿不到同步结果', () => {
19
26
  delete process.env.MCP_DISABLE_AUTO_TASK;
27
+ delete process.env.MCP_ENABLE_AUTO_TASK;
28
+ expect(shouldAutoEscalateToTask('code_insight', false)).toBe(false);
29
+ expect(shouldAutoEscalateToTask('code_insight', true)).toBe(false);
30
+ });
31
+ test('MCP_ENABLE_AUTO_TASK=1 时开启自动 Task', () => {
32
+ process.env.MCP_ENABLE_AUTO_TASK = '1';
20
33
  expect(shouldAutoEscalateToTask('code_insight', false)).toBe(true);
21
34
  expect(shouldAutoEscalateToTask('code_insight', true)).toBe(false);
22
35
  });
23
- test('MCP_DISABLE_AUTO_TASK=1 时关闭自动 Task', () => {
36
+ test('MCP_DISABLE_AUTO_TASK=1 时强制关闭自动 Task', () => {
37
+ process.env.MCP_ENABLE_AUTO_TASK = '1';
24
38
  process.env.MCP_DISABLE_AUTO_TASK = '1';
25
39
  expect(shouldAutoEscalateToTask('code_insight', false)).toBe(false);
26
40
  });
@@ -11,7 +11,18 @@ export interface FileDeliveryReport {
11
11
  writtenFiles: DeliveredFile[];
12
12
  pendingFiles: PendingFile[];
13
13
  }
14
+ export interface FileStatusEntry {
15
+ path: string;
16
+ exists: boolean;
17
+ written: boolean;
18
+ agent_action_required: boolean;
19
+ purpose?: string;
20
+ }
14
21
  export declare function toPosixRel(relPath: string): string;
15
22
  export declare function writeProjectFile(projectRoot: string, relPath: string, content: string, policy: "ifMissing" | "always"): DeliveredFile;
23
+ export declare function buildFileStatusEntries(projectRoot: string, entries: Array<{
24
+ path: string;
25
+ purpose?: string;
26
+ }>, writtenFiles: DeliveredFile[], pendingFiles: PendingFile[]): FileStatusEntry[];
16
27
  export declare function mergeDeliveryReports(...reports: FileDeliveryReport[]): FileDeliveryReport;
17
28
  export declare function formatFileDeliverySection(report: FileDeliveryReport): string;
@@ -14,6 +14,26 @@ export function writeProjectFile(projectRoot, relPath, content, policy) {
14
14
  fs.writeFileSync(absPath, content, "utf8");
15
15
  return { path: posixPath, action: existed ? "updated" : "created" };
16
16
  }
17
+ export function buildFileStatusEntries(projectRoot, entries, writtenFiles, pendingFiles) {
18
+ const writtenByMcp = new Set(writtenFiles
19
+ .filter((file) => file.action === "created" || file.action === "updated")
20
+ .map((file) => toPosixRel(file.path)));
21
+ const pendingPaths = new Set(pendingFiles.map((file) => toPosixRel(file.path)));
22
+ return entries.map(({ path: relPath, purpose }) => {
23
+ const posixPath = toPosixRel(relPath);
24
+ const absPath = path.join(path.resolve(projectRoot), ...posixPath.split("/"));
25
+ const exists = fs.existsSync(absPath);
26
+ const agent_action_required = pendingPaths.has(posixPath);
27
+ const written = writtenByMcp.has(posixPath) || (exists && !agent_action_required);
28
+ return {
29
+ path: posixPath,
30
+ ...(purpose !== undefined ? { purpose } : {}),
31
+ exists,
32
+ written,
33
+ agent_action_required,
34
+ };
35
+ });
36
+ }
17
37
  export function mergeDeliveryReports(...reports) {
18
38
  return {
19
39
  writtenFiles: reports.flatMap((report) => report.writtenFiles),
@@ -98,6 +98,7 @@ export interface BridgeWorkspace {
98
98
  export declare function prepareBridgeWorkspace(cwd?: string, signal?: AbortSignal, options?: {
99
99
  bootstrap?: boolean;
100
100
  }): Promise<BridgeWorkspace>;
101
+ export declare function tryRefreshWorkspaceIndex(workspace: BridgeWorkspace, signal?: AbortSignal): Promise<string | undefined>;
101
102
  export declare function runCodeInsightBridge(request: CodeInsightRequest): Promise<CodeInsightBridgeResult>;
102
103
  export declare function buildFeatureGraphContext(input: {
103
104
  featureName: string;
@@ -786,12 +786,18 @@ export async function prepareBridgeWorkspace(cwd = process.cwd(), signal, option
786
786
  }
787
787
  return createTempAnalysisWorkspace(resolvedCwd, signal, options);
788
788
  }
789
- async function ensureWorkspaceIndexed(workspace, signal) {
789
+ export async function tryRefreshWorkspaceIndex(workspace, signal) {
790
790
  if (workspace.workspaceMode !== "direct") {
791
- return;
791
+ return undefined;
792
+ }
793
+ try {
794
+ const analyzeCli = resolveGitNexusCliCommand("analyze");
795
+ await runProcess(analyzeCli.command, analyzeCli.args, workspace.analysisRoot, signal);
796
+ return undefined;
797
+ }
798
+ catch (error) {
799
+ return normalizeError(error);
792
800
  }
793
- const analyzeCli = resolveGitNexusCliCommand("analyze");
794
- await runProcess(analyzeCli.command, analyzeCli.args, workspace.analysisRoot, signal);
795
801
  }
796
802
  function isUnsafeHomeRoot(sourceRoot) {
797
803
  return path.resolve(sourceRoot) === path.resolve(os.homedir());
@@ -967,12 +973,15 @@ export async function runCodeInsightBridge(request) {
967
973
  };
968
974
  }
969
975
  const workspace = await prepareBridgeWorkspace(requestedProjectRoot, request.signal);
970
- await ensureWorkspaceIndexed(workspace, request.signal);
976
+ const warnings = [];
977
+ const indexRefreshError = await tryRefreshWorkspaceIndex(workspace, request.signal);
978
+ if (indexRefreshError) {
979
+ warnings.push("index_refresh_failed");
980
+ }
971
981
  const effectiveRepo = workspace.workspaceMode === "temp-repo"
972
982
  ? workspace.repoName
973
983
  : resolvePreferredRepoName(request.repo) || workspace.repoName;
974
984
  const { command, args } = launcher;
975
- const warnings = [];
976
985
  if (workspace.workspaceMode === "temp-repo") {
977
986
  warnings.push("temp_repo_workspace");
978
987
  }
@@ -1,5 +1,6 @@
1
1
  /**
2
- * 长耗时工具默认走 MCP Task,避免阻塞 stdio 宿主。
2
+ * 长耗时工具可选走 MCP Task,避免阻塞 stdio 宿主。
3
+ * 默认关闭:多数 Agent 客户端不会轮询 task 结果,自动升级会导致 code_insight 等“报错/无结果”。
3
4
  */
4
5
  export declare function isAutoTaskTool(toolName: string): boolean;
5
6
  export declare function isAutoTaskEnabled(): boolean;
@@ -1,16 +1,19 @@
1
1
  /**
2
- * 长耗时工具默认走 MCP Task,避免阻塞 stdio 宿主。
2
+ * 长耗时工具可选走 MCP Task,避免阻塞 stdio 宿主。
3
+ * 默认关闭:多数 Agent 客户端不会轮询 task 结果,自动升级会导致 code_insight 等“报错/无结果”。
3
4
  */
4
5
  const AUTO_TASK_TOOLS = new Set(['code_insight', 'scan_and_extract_patterns']);
5
6
  export function isAutoTaskTool(toolName) {
6
7
  return AUTO_TASK_TOOLS.has(toolName);
7
8
  }
9
+ function isTruthyEnv(value) {
10
+ return Boolean(value && /^(1|true|yes|on)$/i.test(value.trim()));
11
+ }
8
12
  export function isAutoTaskEnabled() {
9
- const raw = process.env.MCP_DISABLE_AUTO_TASK?.trim();
10
- if (raw && /^(1|true|yes|on)$/i.test(raw)) {
13
+ if (isTruthyEnv(process.env.MCP_DISABLE_AUTO_TASK)) {
11
14
  return false;
12
15
  }
13
- return true;
16
+ return isTruthyEnv(process.env.MCP_ENABLE_AUTO_TASK);
14
17
  }
15
18
  export function shouldAutoEscalateToTask(toolName, hasExplicitTaskRequest) {
16
19
  if (hasExplicitTaskRequest) {
@@ -218,7 +218,7 @@ export declare const allToolSchemas: ({
218
218
  };
219
219
  } | {
220
220
  readonly name: "init_project_context";
221
- readonly description: "生成/更新项目上下文:默认写入 AGENTS.md(含 MCP GitNexus 触发规则)及 docs/project-context/。新功能请先 start_feature,修 bug 请先 start_bugfix。完成后 Agent 应阅读 AGENTS.md。";
221
+ readonly description: "生成/更新项目上下文写作计划(delegated):MCP 写入 AGENTS.md 与 layout.json;project-context 分类文档与 graph-insights 由 Agent 按返回的 plan 落盘。新功能请先 start_feature,修 bug 请先 start_bugfix。";
222
222
  readonly inputSchema: {
223
223
  readonly type: "object";
224
224
  readonly properties: {
@@ -216,9 +216,21 @@ export declare const ProjectContextSchema: {
216
216
  readonly purpose: {
217
217
  readonly type: "string";
218
218
  };
219
+ readonly exists: {
220
+ readonly type: "boolean";
221
+ readonly description: "文件是否已存在于磁盘";
222
+ };
223
+ readonly written: {
224
+ readonly type: "boolean";
225
+ readonly description: "内容是否已落盘(MCP 已写或 Agent 无需再写)";
226
+ };
227
+ readonly agent_action_required: {
228
+ readonly type: "boolean";
229
+ readonly description: "是否仍需 Agent 按 plan 手动写入";
230
+ };
219
231
  };
220
232
  };
221
- readonly description: "文档索引(含已写入与待生成)";
233
+ readonly description: "文档索引(含 exists / written / agent_action_required 状态)";
222
234
  };
223
235
  readonly writtenFiles: {
224
236
  readonly type: "array";
@@ -602,6 +614,9 @@ export interface ProjectContext {
602
614
  documentation?: Array<{
603
615
  path?: string;
604
616
  purpose?: string;
617
+ exists?: boolean;
618
+ written?: boolean;
619
+ agent_action_required?: boolean;
605
620
  }>;
606
621
  nextSteps?: string[];
607
622
  metadata?: Record<string, any>;
@@ -120,9 +120,15 @@ export const ProjectContextSchema = {
120
120
  properties: {
121
121
  path: { type: 'string' },
122
122
  purpose: { type: 'string' },
123
+ exists: { type: 'boolean', description: '文件是否已存在于磁盘' },
124
+ written: { type: 'boolean', description: '内容是否已落盘(MCP 已写或 Agent 无需再写)' },
125
+ agent_action_required: {
126
+ type: 'boolean',
127
+ description: '是否仍需 Agent 按 plan 手动写入',
128
+ },
123
129
  },
124
130
  },
125
- description: '文档索引(含已写入与待生成)',
131
+ description: '文档索引(含 exists / written / agent_action_required 状态)',
126
132
  },
127
133
  writtenFiles: {
128
134
  type: 'array',
@@ -22,7 +22,7 @@ export declare const projectToolSchemas: readonly [{
22
22
  };
23
23
  }, {
24
24
  readonly name: "init_project_context";
25
- readonly description: "生成/更新项目上下文:默认写入 AGENTS.md(含 MCP GitNexus 触发规则)及 docs/project-context/。新功能请先 start_feature,修 bug 请先 start_bugfix。完成后 Agent 应阅读 AGENTS.md。";
25
+ readonly description: "生成/更新项目上下文写作计划(delegated):MCP 写入 AGENTS.md 与 layout.json;project-context 分类文档与 graph-insights 由 Agent 按返回的 plan 落盘。新功能请先 start_feature,修 bug 请先 start_bugfix。";
26
26
  readonly inputSchema: {
27
27
  readonly type: "object";
28
28
  readonly properties: {
@@ -25,7 +25,7 @@ export const projectToolSchemas = [
25
25
  },
26
26
  {
27
27
  name: "init_project_context",
28
- description: "生成/更新项目上下文:默认写入 AGENTS.md(含 MCP GitNexus 触发规则)及 docs/project-context/。新功能请先 start_feature,修 bug 请先 start_bugfix。完成后 Agent 应阅读 AGENTS.md。",
28
+ description: "生成/更新项目上下文写作计划(delegated):MCP 写入 AGENTS.md 与 layout.json;project-context 分类文档与 graph-insights 由 Agent 按返回的 plan 落盘。新功能请先 start_feature,修 bug 请先 start_bugfix。",
29
29
  inputSchema: {
30
30
  type: "object",
31
31
  properties: {
@@ -15,8 +15,11 @@ describe("add_feature 不落盘", () => {
15
15
  });
16
16
  expect(result.isError).toBeFalsy();
17
17
  const structured = result.structuredContent;
18
+ expect(structured.summary).toBe("已生成功能规格写作计划:user-auth");
18
19
  expect(structured.pendingFiles).toHaveLength(3);
20
+ expect(structured.specPaths).toHaveLength(3);
19
21
  expect(structured.writtenFiles).toBeUndefined();
22
+ expect(result._meta?.note).toMatch(/不代写文件/);
20
23
  expect(fs.existsSync(path.join(projectRoot, "docs", "specs", "user-auth", "requirements.md"))).toBe(false);
21
24
  const text = String(result.content[0].text);
22
25
  expect(text).toMatch(/由 Agent 落盘/);
@@ -13,6 +13,9 @@ describe("init_project 落盘边界", () => {
13
13
  });
14
14
  expect(result.isError).toBeFalsy();
15
15
  const structured = result.structuredContent;
16
+ expect(structured.summary).toBe("已生成项目初始化写作计划,请 Agent 按指南落盘 docs/specs/scripts/src");
17
+ expect(structured.nextSteps[0]).toContain("MCP 仅写入 Skill 与 AGENTS.md");
18
+ expect(structured.nextSteps[0]).toContain("Agent 须按指南手动落盘");
16
19
  expect(structured.writtenFiles.length).toBe(2);
17
20
  expect(structured.pendingFiles.length).toBeGreaterThan(0);
18
21
  expect(fs.existsSync(path.join(projectRoot, "AGENTS.md"))).toBe(true);
@@ -25,6 +25,21 @@ describe("init_project_context 单元测试", () => {
25
25
  expect(fs.existsSync(path.join(projectRoot, "docs", "project-context", "tech-stack.md"))).toBe(false);
26
26
  const plan = structured.metadata?.plan;
27
27
  expect(plan?.mode).toBe("delegated");
28
+ expect(structured.summary).toMatch(/写作计划/);
29
+ expect(structured.summary).not.toMatch(/^生成 .*项目上下文/);
30
+ expect(structured.nextSteps?.[0]).toMatch(/不会自动生成完整/);
31
+ const legacyDoc = structured.documentation?.find((doc) => doc.path === "docs/project-context.md");
32
+ expect(legacyDoc).toMatchObject({
33
+ exists: false,
34
+ written: false,
35
+ agent_action_required: true,
36
+ });
37
+ const agentsDoc = structured.documentation?.find((doc) => doc.path === "AGENTS.md");
38
+ expect(agentsDoc).toMatchObject({
39
+ exists: true,
40
+ written: true,
41
+ agent_action_required: false,
42
+ });
28
43
  expect(plan.steps.map((step) => step.id)).toEqual([
29
44
  "write-modular-docs",
30
45
  "bootstrap-code-insight",
@@ -59,6 +74,19 @@ describe("init_project_context 单元测试", () => {
59
74
  "persist-graph-docs",
60
75
  ]);
61
76
  expect(structured.pendingFiles.every((f) => !f.path.startsWith("docs/project-context"))).toBe(true);
77
+ expect(structured.summary).toMatch(/保留现有分类文档/);
78
+ const legacyDoc = structured.documentation?.find((doc) => doc.path === "docs/project-context.md");
79
+ expect(legacyDoc).toMatchObject({
80
+ exists: true,
81
+ written: true,
82
+ agent_action_required: false,
83
+ });
84
+ const graphDoc = structured.documentation?.find((doc) => doc.path === "docs/graph-insights/latest.md");
85
+ expect(graphDoc).toMatchObject({
86
+ exists: false,
87
+ written: false,
88
+ agent_action_required: true,
89
+ });
62
90
  expect(fs.readFileSync(path.join(projectRoot, "docs", "project-context.md"), "utf8")).toBe("# existing context\n");
63
91
  expect(fs.existsSync(path.join(projectRoot, "AGENTS.md"))).toBe(true);
64
92
  const text = result.content[0].text;
@@ -296,7 +296,7 @@ ${fenceClose}
296
296
  reason: "由 Agent 根据本工具返回的模板写入",
297
297
  }));
298
298
  const structuredData = {
299
- summary: `功能规格:${featureName}`,
299
+ summary: `已生成功能规格写作计划:${featureName}`,
300
300
  featureName,
301
301
  requirements: ["见 requirements.md 模板"],
302
302
  tasks: [{ id: "1", title: "按模板创建 specs 文档", description: "将下方模板落盘并补充真实内容" }],
@@ -4,6 +4,7 @@ import { ensureMcpProbeKitBootstrap } from "../lib/workflow-skill-installer.js";
4
4
  import { MCP_PROBE_SKILL_REL_PATH } from "../lib/workflow-skill-template.js";
5
5
  import { resolveWorkspaceRootWithMeta } from "../lib/workspace-root.js";
6
6
  import { toPosixPath } from "../lib/project-context-layout.js";
7
+ const AGENT_MANUAL_WRITE_NOTICE = "MCP 仅写入 Skill 与 AGENTS.md;Agent 须按指南手动落盘 pendingFiles 中的 docs、specs、scripts、src。";
7
8
  /**
8
9
  * init_project 工具
9
10
  *
@@ -226,7 +227,7 @@ ${warningBlock}
226
227
  MCP 已写入 Skill 与 AGENTS.md。请按上述步骤由 Agent 创建 docs、scripts、src 及 specs 文档。`;
227
228
  // 创建结构化数据对象
228
229
  const structuredData = {
229
- summary: `初始化项目:${projectName}`,
230
+ summary: "已生成项目初始化写作计划,请 Agent 按指南落盘 docs/specs/scripts/src",
230
231
  projectName: projectName,
231
232
  projectRoot: toPosixPath(projectRoot),
232
233
  bootstrap: {
@@ -255,11 +256,12 @@ MCP 已写入 Skill 与 AGENTS.md。请按上述步骤由 Agent 创建 docs、sc
255
256
  writtenFiles: bootstrapWritten,
256
257
  pendingFiles,
257
258
  nextSteps: [
258
- '确认 Skill 与 AGENTS.md 已落盘',
259
- '按指南创建 docs/ 文档与 specs',
260
- '创建 scripts/ 辅助脚本',
261
- '创建 src/ 源代码目录',
262
- '运行 init_project_context 生成完整上下文与图谱',
259
+ AGENT_MANUAL_WRITE_NOTICE,
260
+ "确认 Skill AGENTS.md 已落盘",
261
+ "按指南创建 docs/ 文档与 specs",
262
+ "创建 scripts/ 辅助脚本",
263
+ "创建 src/ 源代码目录",
264
+ "运行 init_project_context 生成完整上下文与图谱",
263
265
  ]
264
266
  };
265
267
  return okStructured(message, structuredData, {
@@ -9,9 +9,10 @@ import { generateAgentsMdInner } from "../lib/agents-md-template.js";
9
9
  import { ensureHarnessAdapters } from "../lib/harness-adapters.js";
10
10
  import { generateWorkflowSkillContent, MCP_PROBE_SKILL_REL_PATH } from "../lib/workflow-skill-template.js";
11
11
  import { getMcpProbeSkillVersion } from "../lib/workflow-skill-version.js";
12
- import { formatFileDeliverySection, writeProjectFile, } from "../lib/file-delivery.js";
12
+ import { buildFileStatusEntries, formatFileDeliverySection, writeProjectFile, } from "../lib/file-delivery.js";
13
13
  import * as fs from "fs";
14
14
  import * as path from "path";
15
+ const AGENT_MANUAL_WRITE_NOTICE = "本工具不会自动生成完整 project-context 与图谱内容,Agent 必须根据 metadata.plan 与 pendingFiles 手动写入这些文件。";
15
16
  /**
16
17
  * init_project_context 工具
17
18
  *
@@ -275,22 +276,43 @@ async function generateProjectContext(layout, projectRoot) {
275
276
  const header = renderOrchestrationHeader({
276
277
  tool: "init_project_context",
277
278
  goal: modularExists
278
- ? "补齐图谱(保留现有分类文档;AGENTS.md 已由 MCP 写入)"
279
- : "写入 AGENTS.md 与 layout,由 Agent 生成 project-context 与图谱",
279
+ ? "已生成 delegated 写作计划(保留现有分类文档;AGENTS.md 已由 MCP 写入)"
280
+ : "已生成 delegated 写作计划(AGENTS.md 与 layout 已由 MCP 写入)",
280
281
  tasks: modularExists
281
- ? ["保留现有分类文档", "code_insight + Agent 落盘图谱"]
282
- : ["MCP 已写 AGENTS.md layout", "Agent 写分类文档", "code_insight + Agent 落盘图谱"],
282
+ ? ["保留现有分类文档", "Agent plan 落盘图谱"]
283
+ : ["Agent plan 落盘 project-context 分类文档", "code_insight + Agent 落盘图谱"],
283
284
  notes: [
284
285
  `项目根目录: ${toPosixPath(resolvedRoot)}`,
285
286
  `上下文目录: ${docsDir}`,
286
287
  `索引: ${layout.indexPath}`,
287
288
  `layout: ${manifestWritten}(已服务端写入)`,
289
+ AGENT_MANUAL_WRITE_NOTICE,
288
290
  ],
289
291
  });
292
+ const documentationDefs = [
293
+ { path: layout.indexPath, purpose: "Harness 入口(MCP 触发规则,省 token)" },
294
+ {
295
+ path: layout.legacyIndexPath,
296
+ purpose: "项目上下文索引(写代码前优先读,链到分类文档)",
297
+ },
298
+ ...docs.map((doc) => ({
299
+ path: `${layout.modularDir}/${doc.file}`,
300
+ purpose: doc.purpose,
301
+ })),
302
+ {
303
+ path: layout.latestMarkdownPath,
304
+ purpose: "最新代码图谱洞察(由 code_insight 维护)",
305
+ },
306
+ {
307
+ path: layout.latestJsonPath,
308
+ purpose: "最新代码图谱结构化结果(由 code_insight 维护)",
309
+ },
310
+ { path: layout.manifestPath, purpose: "layout manifest(工具链路径发现)" },
311
+ ];
290
312
  const structuredData = {
291
313
  summary: modularExists
292
- ? `检测到现有项目上下文,补齐图谱与 ${layout.indexPath}`
293
- : `生成 ${detection.category} 项目上下文与 ${layout.indexPath}`,
314
+ ? "已生成上下文写作计划(保留现有分类文档),请 Agent 按 plan 落盘图谱文件"
315
+ : "已生成上下文写作计划,请 Agent plan 落盘文件",
294
316
  mode: "modular",
295
317
  projectOverview: {
296
318
  name: projectInfo.name,
@@ -298,31 +320,16 @@ async function generateProjectContext(layout, projectRoot) {
298
320
  techStack: detection.framework ? [detection.framework] : [],
299
321
  architecture: detection.category,
300
322
  },
301
- documentation: [
302
- { path: layout.indexPath, purpose: "Harness 入口(MCP 触发规则,省 token)" },
303
- {
304
- path: layout.legacyIndexPath,
305
- purpose: "项目上下文索引(写代码前优先读,链到分类文档)",
306
- },
307
- ...docs.map((doc) => ({
308
- path: `${layout.modularDir}/${doc.file}`,
309
- purpose: doc.purpose,
310
- })),
311
- {
312
- path: layout.latestMarkdownPath,
313
- purpose: "最新代码图谱洞察(由 code_insight 维护)",
314
- },
315
- {
316
- path: layout.latestJsonPath,
317
- purpose: "最新代码图谱结构化结果(由 code_insight 维护)",
318
- },
319
- { path: layout.manifestPath, purpose: "layout manifest(工具链路径发现)" },
320
- ],
323
+ documentation: buildFileStatusEntries(projectRootAbs, documentationDefs, writtenFiles, pendingFiles).map((entry) => ({
324
+ ...entry,
325
+ purpose: entry.purpose ?? "",
326
+ })),
321
327
  nextSteps: [
328
+ AGENT_MANUAL_WRITE_NOTICE,
322
329
  ...(modularExists
323
330
  ? [`保留 ${layout.legacyIndexPath} 与分类文档`]
324
331
  : [`由 Agent 按模板创建 ${layout.modularDir}/ 分类文档`]),
325
- "调用 code_insight,由 Agent 落盘图谱",
332
+ "调用 code_insight,由 Agent 按 metadata.plan 落盘图谱",
326
333
  `${layout.indexPath} 与 ${manifestWritten} 已由 MCP 写入(mergeMode: ${mergedAgents.mergeMode})`,
327
334
  ],
328
335
  writtenFiles,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-probe-kit",
3
- "version": "3.6.8",
3
+ "version": "3.6.10",
4
4
  "description": "AI-Powered Development Toolkit - MCP Server with 30 tools covering code quality, development efficiency, project management, and UI/UX design. Features: Structured Output, Workflow Orchestration, MCP Skill auto-sync, UI/UX Pro Max, and Requirements Interview.",
5
5
  "mcpName": "io.github.mybolide/mcp-probe-kit",
6
6
  "type": "module",