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.
- package/README.md +3 -1
- package/build/index.js +1 -0
- package/build/lib/__tests__/agents-md-template.unit.test.js +2 -0
- package/build/lib/__tests__/agents-skill-ref.unit.test.d.ts +1 -0
- package/build/lib/__tests__/agents-skill-ref.unit.test.js +32 -0
- package/build/lib/__tests__/harness-adapters.unit.test.d.ts +1 -0
- package/build/lib/__tests__/harness-adapters.unit.test.js +68 -0
- package/build/lib/__tests__/harness-skill-targets.unit.test.d.ts +1 -0
- package/build/lib/__tests__/harness-skill-targets.unit.test.js +59 -0
- 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/__tests__/project-mcp-resources.unit.test.js +1 -1
- package/build/lib/__tests__/workflow-skill-frontmatter.unit.test.d.ts +1 -0
- package/build/lib/__tests__/workflow-skill-frontmatter.unit.test.js +31 -0
- package/build/lib/__tests__/workflow-skill-installer.unit.test.js +21 -6
- package/build/lib/agents-md-template.d.ts +1 -0
- package/build/lib/agents-md-template.js +21 -6
- package/build/lib/agents-skill-ref.d.ts +6 -0
- package/build/lib/agents-skill-ref.js +45 -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/harness-adapters.d.ts +19 -0
- package/build/lib/harness-adapters.js +166 -0
- package/build/lib/harness-skill-targets.d.ts +38 -0
- package/build/lib/harness-skill-targets.js +95 -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-layout.d.ts +15 -2
- package/build/lib/project-context-layout.js +27 -3
- 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/lib/workflow-skill-installer.d.ts +2 -0
- package/build/lib/workflow-skill-installer.js +19 -5
- package/build/lib/workflow-skill-template.d.ts +3 -0
- package/build/lib/workflow-skill-template.js +12 -5
- package/build/lib/workflow-skill-version.d.ts +7 -1
- package/build/lib/workflow-skill-version.js +45 -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 +83 -46
- 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:
|
|
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 }, //
|
|
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,
|
|
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 ?
|
|
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
|
|
150
|
-
agentsMd
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 @@
|
|
|
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 {
|