mcp-probe-kit 3.6.3 → 3.6.8

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.
Files changed (55) hide show
  1. package/README.md +3 -1
  2. package/build/index.js +1 -0
  3. package/build/lib/__tests__/agents-md-template.unit.test.js +2 -0
  4. package/build/lib/__tests__/agents-skill-ref.unit.test.d.ts +1 -0
  5. package/build/lib/__tests__/agents-skill-ref.unit.test.js +32 -0
  6. package/build/lib/__tests__/harness-adapters.unit.test.d.ts +1 -0
  7. package/build/lib/__tests__/harness-adapters.unit.test.js +68 -0
  8. package/build/lib/__tests__/harness-skill-targets.unit.test.d.ts +1 -0
  9. package/build/lib/__tests__/harness-skill-targets.unit.test.js +59 -0
  10. package/build/lib/__tests__/project-context-writer.unit.test.d.ts +1 -0
  11. package/build/lib/__tests__/project-context-writer.unit.test.js +28 -0
  12. package/build/lib/__tests__/project-mcp-resources.unit.test.js +1 -1
  13. package/build/lib/__tests__/workflow-skill-frontmatter.unit.test.d.ts +1 -0
  14. package/build/lib/__tests__/workflow-skill-frontmatter.unit.test.js +31 -0
  15. package/build/lib/__tests__/workflow-skill-installer.unit.test.js +21 -6
  16. package/build/lib/agents-md-template.d.ts +1 -0
  17. package/build/lib/agents-md-template.js +21 -6
  18. package/build/lib/agents-skill-ref.d.ts +6 -0
  19. package/build/lib/agents-skill-ref.js +45 -0
  20. package/build/lib/feature-spec-writer.d.ts +12 -0
  21. package/build/lib/feature-spec-writer.js +14 -0
  22. package/build/lib/file-delivery.d.ts +17 -0
  23. package/build/lib/file-delivery.js +42 -0
  24. package/build/lib/graph-insights-writer.d.ts +10 -0
  25. package/build/lib/graph-insights-writer.js +65 -0
  26. package/build/lib/harness-adapters.d.ts +19 -0
  27. package/build/lib/harness-adapters.js +166 -0
  28. package/build/lib/harness-skill-targets.d.ts +38 -0
  29. package/build/lib/harness-skill-targets.js +95 -0
  30. package/build/lib/init-project-writer.d.ts +7 -0
  31. package/build/lib/init-project-writer.js +161 -0
  32. package/build/lib/project-context-layout.d.ts +15 -2
  33. package/build/lib/project-context-layout.js +27 -3
  34. package/build/lib/project-context-writer.d.ts +41 -0
  35. package/build/lib/project-context-writer.js +120 -0
  36. package/build/lib/tool-annotations.js +2 -2
  37. package/build/lib/workflow-skill-installer.d.ts +2 -0
  38. package/build/lib/workflow-skill-installer.js +19 -5
  39. package/build/lib/workflow-skill-template.d.ts +3 -0
  40. package/build/lib/workflow-skill-template.js +12 -5
  41. package/build/lib/workflow-skill-version.d.ts +7 -1
  42. package/build/lib/workflow-skill-version.js +45 -2
  43. package/build/schemas/output/project-tools.d.ts +136 -0
  44. package/build/schemas/output/project-tools.js +65 -1
  45. package/build/tools/__tests__/add_feature.write.unit.test.d.ts +1 -0
  46. package/build/tools/__tests__/add_feature.write.unit.test.js +30 -0
  47. package/build/tools/__tests__/code_insight.unit.test.js +2 -4
  48. package/build/tools/__tests__/init_project.write.unit.test.d.ts +1 -0
  49. package/build/tools/__tests__/init_project.write.unit.test.js +22 -0
  50. package/build/tools/__tests__/init_project_context.unit.test.js +49 -45
  51. package/build/tools/add_feature.js +23 -6
  52. package/build/tools/code_insight.js +3 -3
  53. package/build/tools/init_project.js +25 -22
  54. package/build/tools/init_project_context.js +83 -46
  55. package/package.json +84 -84
@@ -0,0 +1,120 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { layoutAbsPath } from "./project-context-layout.js";
4
+ function writeFileEnsuringDir(absPath, content) {
5
+ fs.mkdirSync(path.dirname(absPath), { recursive: true });
6
+ fs.writeFileSync(absPath, content, "utf8");
7
+ }
8
+ function writeIfMissing(layout, relPath, content) {
9
+ const absPath = layoutAbsPath(layout, relPath);
10
+ if (fs.existsSync(absPath)) {
11
+ return { path: relPath, action: "skipped" };
12
+ }
13
+ writeFileEnsuringDir(absPath, content);
14
+ return { path: relPath, action: "created" };
15
+ }
16
+ function writeAlways(layout, relPath, content) {
17
+ const absPath = layoutAbsPath(layout, relPath);
18
+ const existed = fs.existsSync(absPath);
19
+ writeFileEnsuringDir(absPath, content);
20
+ return { path: relPath, action: existed ? "updated" : "created" };
21
+ }
22
+ export function buildProjectContextIndexMarkdown(projectInfo, detection, docs, docsDir) {
23
+ const timestamp = new Date().toISOString();
24
+ const docNav = docs
25
+ .map((doc) => `### [${doc.title}](./project-context/${doc.file})
26
+ ${doc.purpose}
27
+ `)
28
+ .join("\n");
29
+ return `# ${projectInfo.name} - 项目上下文
30
+
31
+ > 本文档是项目上下文的索引文件,提供项目概览和文档导航。
32
+
33
+ ## 📊 项目概览
34
+
35
+ | 属性 | 值 |
36
+ |------|-----|
37
+ | 项目名称 | ${projectInfo.name} |
38
+ | 版本 | ${projectInfo.version} |
39
+ | 语言 | ${detection.language} |
40
+ | 框架 | ${detection.framework || "无"} |
41
+ | 类型 | ${detection.category} |
42
+ | 描述 | ${projectInfo.description || "待补充"} |
43
+
44
+ ## 📚 文档导航
45
+
46
+ ${docNav}
47
+ ### [代码图谱洞察](./graph-insights/latest.md)
48
+ 最近一次 code_insight 分析结果,包含模块依赖、调用链和影响面摘要
49
+
50
+ ## 🚀 快速开始
51
+
52
+ 1. 阅读 [技术栈](./project-context/tech-stack.md) 了解项目使用的技术
53
+ 2. 阅读 [架构设计](./project-context/architecture.md) 了解项目结构
54
+ 3. 阅读 [代码图谱洞察](./graph-insights/latest.md) 快速理解模块依赖与调用链
55
+ 4. 根据需要查看具体的操作指南
56
+
57
+ ---
58
+ *生成时间: ${timestamp}*
59
+ *生成工具: mcp-probe-kit init_project_context*
60
+ `;
61
+ }
62
+ export function buildModularDocSkeleton(doc, projectInfo, detection) {
63
+ return `# ${doc.title}
64
+
65
+ > ${doc.purpose}
66
+
67
+ ## 概述
68
+
69
+ 本文档描述 **${projectInfo.name}** 的${doc.title}。请根据仓库实际代码补充下方各节(勿留空占位)。
70
+
71
+ ## 项目信息
72
+
73
+ | 属性 | 值 |
74
+ |------|-----|
75
+ | 语言 | ${detection.language} |
76
+ | 框架 | ${detection.framework || "无"} |
77
+ | 类型 | ${detection.category} |
78
+
79
+ ## 待补充
80
+
81
+ - 从项目中提取真实路径与代码示例
82
+ - 步骤需具体可操作
83
+
84
+ ---
85
+ *返回索引: [../project-context.md](../project-context.md)*
86
+ `;
87
+ }
88
+ /**
89
+ * 由 init_project_context 服务端直接落盘(勿依赖 Agent fsWrite)。
90
+ */
91
+ export function writeProjectContextArtifacts(input) {
92
+ const { layout, mergedAgentsContent, skipModularDocs, projectInfo, detection, docs } = input;
93
+ const docsDir = layout.contextRoot;
94
+ const agentsMd = writeAlways(layout, layout.indexPath, mergedAgentsContent);
95
+ let projectContextIndex = null;
96
+ const modularDocs = [];
97
+ if (!skipModularDocs) {
98
+ projectContextIndex = writeIfMissing(layout, layout.legacyIndexPath, buildProjectContextIndexMarkdown(projectInfo, detection, docs, docsDir));
99
+ for (const doc of docs) {
100
+ const rel = `${layout.modularDir}/${doc.file}`;
101
+ modularDocs.push(writeIfMissing(layout, rel, buildModularDocSkeleton(doc, projectInfo, detection)));
102
+ }
103
+ }
104
+ return { agentsMd, projectContextIndex, modularDocs };
105
+ }
106
+ export function formatWriteSummary(result) {
107
+ const lines = ["## 已写入磁盘(MCP 服务端)"];
108
+ const push = (record) => {
109
+ const verb = record.action === "created" ? "已创建" : record.action === "updated" ? "已更新" : "已跳过(已存在)";
110
+ lines.push(`- \`${record.path}\` — ${verb}`);
111
+ };
112
+ push(result.agentsMd);
113
+ if (result.projectContextIndex) {
114
+ push(result.projectContextIndex);
115
+ }
116
+ for (const doc of result.modularDocs) {
117
+ push(doc);
118
+ }
119
+ return lines.join("\n");
120
+ }
@@ -14,7 +14,7 @@
14
14
  */
15
15
  export const TOOL_ANNOTATIONS = {
16
16
  // —— 只读指南型(可安全自动放行)——
17
- init_project: { title: '初始化项目', readOnlyHint: true, idempotentHint: true, openWorldHint: false },
17
+ init_project: { title: '初始化项目', readOnlyHint: false, idempotentHint: true, openWorldHint: false }, // 仅写 Skill + AGENTS.md
18
18
  workflow: { title: '开发工作流路由', readOnlyHint: true, idempotentHint: true, openWorldHint: false },
19
19
  gencommit: { title: '生成提交信息', readOnlyHint: true, idempotentHint: true, openWorldHint: false },
20
20
  code_review: { title: '代码审查', readOnlyHint: true, idempotentHint: true, openWorldHint: false },
@@ -43,7 +43,7 @@ export const TOOL_ANNOTATIONS = {
43
43
  start_feature: { title: '新功能开发编排', readOnlyHint: true, idempotentHint: false, openWorldHint: true },
44
44
  start_bugfix: { title: 'Bug 修复编排', readOnlyHint: true, idempotentHint: false, openWorldHint: true },
45
45
  // —— 写型(工具自身落盘 / 写记忆 / 写缓存,非破坏)——
46
- init_project_context: { title: '生成项目上下文', readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }, // 自己写 docs/.mcp-probe/layout.json(其余为指令)
46
+ init_project_context: { title: '生成项目上下文', readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }, // 仅写 AGENTS.md + layout.json
47
47
  memorize_asset: { title: '沉淀记忆资产', readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }, // 写 Qdrant
48
48
  delete_memory_asset: { title: '删除记忆资产', readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }, // 删 Qdrant
49
49
  update_memory_asset: { title: '更新记忆资产', readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }, // 写 Qdrant
@@ -1,3 +1,4 @@
1
+ import { type HarnessAdapterEnsureResult } from "./harness-adapters.js";
1
2
  export interface SkillEnsureResult {
2
3
  skillPath: string;
3
4
  skillRelPath: string;
@@ -17,6 +18,7 @@ export interface McpProbeKitBootstrapResult {
17
18
  projectRoot: string;
18
19
  skill: SkillEnsureResult;
19
20
  agentsMd: AgentsMdEnsureResult;
21
+ harness?: HarnessAdapterEnsureResult;
20
22
  /** 工作区可能解析失败(写到了 mcp-probe-kit 安装目录) */
21
23
  workspaceWarning?: string;
22
24
  }
@@ -1,10 +1,12 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import { agentsSkillReferenceSatisfied, resolveAgentsSkillRefMode, } from "./agents-skill-ref.js";
3
4
  import { generateAgentsMdInner } from "./agents-md-template.js";
4
5
  import { mergeAgentsMdBlock } from "./merge-agents-md.js";
5
- import { detectDocumentLocale, resolveProjectContextLayout, toPosixPath, } from "./project-context-layout.js";
6
6
  import { generateWorkflowSkillContent, LEGACY_WORKFLOW_SKILL_REL_PATH, MCP_PROBE_SKILL_REL_PATH, } from "./workflow-skill-template.js";
7
- import { agentsContextNeedsUpgrade, getMcpProbeSkillVersion, parseSkillVersionMarker, skillContentNeedsUpgrade, } from "./workflow-skill-version.js";
7
+ import { agentsContextNeedsUpgrade, getMcpProbeSkillVersion, parseSkillInstalledVersion, skillContentNeedsUpgrade, } from "./workflow-skill-version.js";
8
+ import { ensureHarnessAdapters } from "./harness-adapters.js";
9
+ import { detectDocumentLocale, patchLayoutManifestHarness, resolveProjectContextLayout, toPosixPath, } from "./project-context-layout.js";
8
10
  import { isLikelyProjectNamedRelativePath, getMcpPackageInstallRoot, resolveWorkspaceRoot, } from "./workspace-root.js";
9
11
  function buildWorkspaceWarning(projectRoot) {
10
12
  const normalizedRoot = path.resolve(projectRoot);
@@ -42,6 +44,7 @@ function buildAgentsMdInner(projectRoot, existingAgentsContent) {
42
44
  const layout = resolveProjectContextLayout(projectRoot);
43
45
  const locale = detectDocumentLocale(projectRoot, existingAgentsContent);
44
46
  const graphReady = fs.existsSync(path.join(projectRoot, layout.latestMarkdownPath));
47
+ const contextReady = fs.existsSync(path.join(projectRoot, layout.legacyIndexPath));
45
48
  return generateAgentsMdInner({
46
49
  layout,
47
50
  locale,
@@ -53,6 +56,7 @@ function buildAgentsMdInner(projectRoot, existingAgentsContent) {
53
56
  docs: [],
54
57
  projectRootPosix: toPosixPath(projectRoot),
55
58
  graphReady,
59
+ contextReady,
56
60
  });
57
61
  }
58
62
  function agentsMdNeedsUpdate(content, skillRelPath, targetVersion) {
@@ -68,6 +72,9 @@ function agentsMdNeedsUpdate(content, skillRelPath, targetVersion) {
68
72
  if (!content.includes(skillRelPath)) {
69
73
  return true;
70
74
  }
75
+ if (!agentsSkillReferenceSatisfied(content, resolveAgentsSkillRefMode())) {
76
+ return true;
77
+ }
71
78
  if (agentsContextNeedsUpgrade(content, targetVersion)) {
72
79
  return true;
73
80
  }
@@ -85,7 +92,7 @@ export function ensureMcpProbeSkill(projectRoot) {
85
92
  const skillPath = path.join(root, MCP_PROBE_SKILL_REL_PATH);
86
93
  const targetVersion = getMcpProbeSkillVersion();
87
94
  const existing = fs.existsSync(skillPath) ? fs.readFileSync(skillPath, "utf8") : null;
88
- const previousVersion = existing ? parseSkillVersionMarker(existing) : null;
95
+ const previousVersion = existing ? parseSkillInstalledVersion(existing) : null;
89
96
  if (!skillContentNeedsUpgrade(existing, targetVersion)) {
90
97
  return {
91
98
  skillPath,
@@ -144,10 +151,17 @@ export function ensureAgentsMdSkillReference(projectRoot) {
144
151
  export function ensureMcpProbeKitBootstrap(projectRoot) {
145
152
  const root = path.resolve(projectRoot);
146
153
  const workspaceWarning = buildWorkspaceWarning(root);
154
+ const skill = ensureMcpProbeSkill(root);
155
+ const agentsMd = ensureAgentsMdSkillReference(root);
156
+ const skillContent = fs.readFileSync(skill.skillPath, "utf8");
157
+ const layout = resolveProjectContextLayout(root);
158
+ const harness = ensureHarnessAdapters(root, skillContent, layout.indexPath);
159
+ patchLayoutManifestHarness(root, harness.layoutHarness);
147
160
  return {
148
161
  projectRoot: root,
149
- skill: ensureMcpProbeSkill(root),
150
- agentsMd: ensureAgentsMdSkillReference(root),
162
+ skill,
163
+ agentsMd,
164
+ harness,
151
165
  workspaceWarning,
152
166
  };
153
167
  }
@@ -6,4 +6,7 @@ export declare const MCP_PROBE_SKILL_REL_PATH = ".agents/skills/mcp-probe-kit/SK
6
6
  /** @deprecated 旧路径,仅用于检测并升级 AGENTS.md 引用 */
7
7
  export declare const LEGACY_WORKFLOW_SKILL_REL_PATH = ".agents/skills/workflow/SKILL.md";
8
8
  export declare function generateWorkflowSkillBody(skillVersion?: string): string;
9
+ export declare const MCP_PROBE_SKILL_NAME = "mcp-probe-kit";
10
+ export declare const MCP_PROBE_SKILL_DESCRIPTION = "\u5C06\u7528\u6237\u610F\u56FE\u8DEF\u7531\u5230 mcp-probe-kit MCP \u5DE5\u5177\uFF08start_feature\u3001start_bugfix\u3001code_insight\u3001workflow\u3001gencommit \u7B49\uFF09\u3002\u5728\u5DF2\u914D\u7F6E MCP \u4E14\u51C6\u5907\u5199\u4EE3\u7801\u524D\u8BFB\u53D6\uFF1B\u4EC5\u8BF4\u660E\u8C03\u54EA\u4E2A MCP\uFF0C\u4E0D\u662F\u9879\u76EE\u7814\u53D1\u6D41\u7A0B\u672C\u8EAB\u3002Routes intent to mcp-probe-kit MCP tools; read before coding when MCP is configured.";
11
+ export declare function formatSkillFrontmatter(skillVersion?: string): string;
9
12
  export declare function generateWorkflowSkillContent(skillVersion?: string): string;
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { VERSION } from "../version.js";
6
6
  import { MCP_INTENT_QUICK_LOOKUP, MCP_SKILL_AVOID_RULES, MCP_SKILL_COMMON_FLOWS, MCP_TOOL_SKILL_GROUPS, } from "./mcp-tool-skill-registry.js";
7
- import { formatSkillVersionMarker } from "./workflow-skill-version.js";
7
+ import { SKILL_VERSION_FRONTMATTER_KEY } from "./workflow-skill-version.js";
8
8
  export const MCP_PROBE_SKILL_REL_PATH = ".agents/skills/mcp-probe-kit/SKILL.md";
9
9
  /** @deprecated 旧路径,仅用于检测并升级 AGENTS.md 引用 */
10
10
  export const LEGACY_WORKFLOW_SKILL_REL_PATH = ".agents/skills/workflow/SKILL.md";
@@ -74,12 +74,19 @@ ${renderAvoidRules()}
74
74
  *mcp-probe-kit 按版本自动同步(当前 \`${skillVersion}\`)。路径:\`${MCP_PROBE_SKILL_REL_PATH}\`*
75
75
  `;
76
76
  }
77
+ export const MCP_PROBE_SKILL_NAME = "mcp-probe-kit";
78
+ export const MCP_PROBE_SKILL_DESCRIPTION = `将用户意图路由到 mcp-probe-kit MCP 工具(start_feature、start_bugfix、code_insight、workflow、gencommit 等)。在已配置 MCP 且准备写代码前读取;仅说明调哪个 MCP,不是项目研发流程本身。Routes intent to mcp-probe-kit MCP tools; read before coding when MCP is configured.`;
79
+ export function formatSkillFrontmatter(skillVersion = VERSION) {
80
+ return `---
81
+ name: ${MCP_PROBE_SKILL_NAME}
82
+ description: >-
83
+ ${MCP_PROBE_SKILL_DESCRIPTION}
84
+ ${SKILL_VERSION_FRONTMATTER_KEY}: "${skillVersion}"
85
+ ---`;
86
+ }
77
87
  export function generateWorkflowSkillContent(skillVersion = VERSION) {
78
- const versionMarker = formatSkillVersionMarker(skillVersion);
79
- return `${versionMarker}
88
+ return `${formatSkillFrontmatter(skillVersion)}
80
89
 
81
90
  ${generateWorkflowSkillBody(skillVersion)}
82
-
83
- ${versionMarker}
84
91
  `;
85
92
  }
@@ -1,4 +1,6 @@
1
- /** Skill 文件首行版本标记:`<!-- mcp-probe-kit-skill-version: 3.5.0 -->` */
1
+ /** Skill frontmatter 版本字段 */
2
+ export declare const SKILL_VERSION_FRONTMATTER_KEY = "mcp-probe-kit-version";
3
+ /** @deprecated 旧版 HTML 版本标记,仍用于解析历史文件 */
2
4
  export declare const SKILL_VERSION_MARKER = "mcp-probe-kit-skill-version";
3
5
  /** AGENTS.md mcp-probe 块内版本标记 */
4
6
  export declare const AGENTS_CONTEXT_VERSION_MARKER = "mcp-probe:context-version";
@@ -6,6 +8,10 @@ export declare function getMcpProbeSkillVersion(): string;
6
8
  export declare function formatSkillVersionMarker(version?: string): string;
7
9
  export declare function formatAgentsContextVersionMarker(version?: string): string;
8
10
  export declare function parseSkillVersionMarker(content: string): string | null;
11
+ /** 从 Skill 正文解析已安装版本(frontmatter 优先,HTML 注释兜底) */
12
+ export declare function parseSkillInstalledVersion(content: string): string | null;
13
+ /** 是否缺少 Agent Skill 发现所需的 YAML frontmatter */
14
+ export declare function skillFrontmatterNeedsUpgrade(content: string): boolean;
9
15
  export declare function parseAgentsContextVersion(content: string): string | null;
10
16
  /** 比较 semver(仅主版本号段,忽略 prerelease 后缀) */
11
17
  export declare function compareSemver(a: string, b: string): number;
@@ -1,5 +1,7 @@
1
1
  import { VERSION } from "../version.js";
2
- /** Skill 文件首行版本标记:`<!-- mcp-probe-kit-skill-version: 3.5.0 -->` */
2
+ /** Skill frontmatter 版本字段 */
3
+ export const SKILL_VERSION_FRONTMATTER_KEY = "mcp-probe-kit-version";
4
+ /** @deprecated 旧版 HTML 版本标记,仍用于解析历史文件 */
3
5
  export const SKILL_VERSION_MARKER = "mcp-probe-kit-skill-version";
4
6
  /** AGENTS.md mcp-probe 块内版本标记 */
5
7
  export const AGENTS_CONTEXT_VERSION_MARKER = "mcp-probe:context-version";
@@ -17,6 +19,44 @@ export function parseSkillVersionMarker(content) {
17
19
  const match = content.match(pattern);
18
20
  return match?.[1]?.trim() ?? null;
19
21
  }
22
+ function parseSkillVersionFromFrontmatter(content) {
23
+ if (!content.trimStart().startsWith("---")) {
24
+ return null;
25
+ }
26
+ const end = content.indexOf("\n---", 3);
27
+ if (end === -1) {
28
+ return null;
29
+ }
30
+ const header = content.slice(0, end + 4);
31
+ const pattern = new RegExp(`^${SKILL_VERSION_FRONTMATTER_KEY}:\\s*["']?([^"'\\n]+)["']?\\s*$`, "m");
32
+ const match = header.match(pattern);
33
+ return match?.[1]?.trim().replace(/^["']|["']$/g, "") ?? null;
34
+ }
35
+ /** 从 Skill 正文解析已安装版本(frontmatter 优先,HTML 注释兜底) */
36
+ export function parseSkillInstalledVersion(content) {
37
+ return parseSkillVersionFromFrontmatter(content) ?? parseSkillVersionMarker(content);
38
+ }
39
+ /** 是否缺少 Agent Skill 发现所需的 YAML frontmatter */
40
+ export function skillFrontmatterNeedsUpgrade(content) {
41
+ if (!content.trimStart().startsWith("---")) {
42
+ return true;
43
+ }
44
+ const end = content.indexOf("\n---", 3);
45
+ if (end === -1) {
46
+ return true;
47
+ }
48
+ const header = content.slice(0, end + 4);
49
+ if (!/^name:\s*mcp-probe-kit\s*$/m.test(header)) {
50
+ return true;
51
+ }
52
+ if (!/^description:\s*>?-?\s*\S/m.test(header)) {
53
+ return true;
54
+ }
55
+ if (!new RegExp(`^${SKILL_VERSION_FRONTMATTER_KEY}:\\s*`, "m").test(header)) {
56
+ return true;
57
+ }
58
+ return false;
59
+ }
20
60
  export function parseAgentsContextVersion(content) {
21
61
  const pattern = new RegExp(`<!--\\s*${AGENTS_CONTEXT_VERSION_MARKER}:\\s*([^\\s>]+)\\s*-->`, "i");
22
62
  const match = content.match(pattern);
@@ -49,7 +89,10 @@ export function skillContentNeedsUpgrade(existing, targetVersion = VERSION) {
49
89
  if (!existing?.trim()) {
50
90
  return true;
51
91
  }
52
- const installed = parseSkillVersionMarker(existing);
92
+ if (skillFrontmatterNeedsUpgrade(existing)) {
93
+ return true;
94
+ }
95
+ const installed = parseSkillInstalledVersion(existing);
53
96
  if (!installed) {
54
97
  return true;
55
98
  }
@@ -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,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("返回 docs 保存指引而不直接代写文件", async () => {
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.plan.steps[1].outputs[0]).toMatch(/docs\/graph-insights\/latest\.md$/);
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 {