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,19 @@
|
|
|
1
|
+
import { type HarnessAdapterKind, type HarnessDetectionResult, type LayoutHarnessManifest } from "./harness-skill-targets.js";
|
|
2
|
+
export interface HarnessAdapterWriteResult {
|
|
3
|
+
id: string;
|
|
4
|
+
path: string;
|
|
5
|
+
kind: HarnessAdapterKind;
|
|
6
|
+
created: boolean;
|
|
7
|
+
updated: boolean;
|
|
8
|
+
skipped: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface HarnessAdapterEnsureResult {
|
|
11
|
+
detection: HarnessDetectionResult;
|
|
12
|
+
adapters: HarnessAdapterWriteResult[];
|
|
13
|
+
layoutHarness: LayoutHarnessManifest;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Write optional harness adapters. AGENTS.md and canonical Skill are unchanged.
|
|
17
|
+
* Canonical Skill must already exist at `.agents/skills/mcp-probe-kit/SKILL.md`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function ensureHarnessAdapters(projectRoot: string, skillContent: string, agentsIndexPath?: string): HarnessAdapterEnsureResult;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { CANONICAL_SKILL_REL_PATH, detectHarnessContext, toLayoutHarnessManifest, } from "./harness-skill-targets.js";
|
|
4
|
+
import { getMcpProbeSkillVersion } from "./workflow-skill-version.js";
|
|
5
|
+
const RULES_POINTER_VERSION_KEY = "mcp-probe-kit-harness-adapter-version";
|
|
6
|
+
const CLAUDE_BLOCK_BEGIN = "<!-- mcp-probe:harness begin — auto-generated; do not edit -->";
|
|
7
|
+
const CLAUDE_BLOCK_END = "<!-- mcp-probe:harness end -->";
|
|
8
|
+
function generateRulesPointerContent(skillCanonical, version, agentsIndexPath) {
|
|
9
|
+
return `# mcp-probe-kit MCP
|
|
10
|
+
|
|
11
|
+
> ${RULES_POINTER_VERSION_KEY}: ${version}
|
|
12
|
+
|
|
13
|
+
配置 **mcp-probe-kit** MCP 后,写代码 / 改文件前:
|
|
14
|
+
|
|
15
|
+
1. 阅读项目根 \`${agentsIndexPath}\` 中 \`<!-- mcp-probe:context -->\` 块(MCP 触发规则)
|
|
16
|
+
2. 需要完整工具表时阅读 \`${skillCanonical}\`
|
|
17
|
+
|
|
18
|
+
**不要**跳过 MCP 直接改业务代码。拿不准先调 \`workflow\`。
|
|
19
|
+
`;
|
|
20
|
+
}
|
|
21
|
+
function generateComateRulesContent(skillCanonical, version, agentsIndexPath) {
|
|
22
|
+
return `# mcp-probe-kit-mcp
|
|
23
|
+
|
|
24
|
+
> ${RULES_POINTER_VERSION_KEY}: ${version}
|
|
25
|
+
|
|
26
|
+
配置 mcp-probe-kit MCP 后,写代码前阅读 ${agentsIndexPath} 的 mcp-probe 块或 ${skillCanonical}。拿不准先调 workflow。
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
function generateClaudePointerBlock(skillCanonical, agentsPath) {
|
|
30
|
+
return `${CLAUDE_BLOCK_BEGIN}
|
|
31
|
+
## MCP (mcp-probe-kit)
|
|
32
|
+
|
|
33
|
+
Before coding, read the \`mcp-probe:context\` block in \`${agentsPath}\` or Skill \`${skillCanonical}\`.
|
|
34
|
+
${CLAUDE_BLOCK_END}`;
|
|
35
|
+
}
|
|
36
|
+
function stripClaudeHarnessBlock(content) {
|
|
37
|
+
const beginIdx = content.indexOf(CLAUDE_BLOCK_BEGIN);
|
|
38
|
+
if (beginIdx === -1) {
|
|
39
|
+
return content.trim();
|
|
40
|
+
}
|
|
41
|
+
const endIdx = content.indexOf(CLAUDE_BLOCK_END);
|
|
42
|
+
if (endIdx === -1) {
|
|
43
|
+
return content.trim();
|
|
44
|
+
}
|
|
45
|
+
const before = content.slice(0, beginIdx).trimEnd();
|
|
46
|
+
const after = content.slice(endIdx + CLAUDE_BLOCK_END.length).trimStart();
|
|
47
|
+
return [before, after].filter(Boolean).join("\n\n").trim();
|
|
48
|
+
}
|
|
49
|
+
function mergeClaudePointer(existing, block) {
|
|
50
|
+
const userBody = existing ? stripClaudeHarnessBlock(existing) : "";
|
|
51
|
+
if (!userBody) {
|
|
52
|
+
return `${block}\n`;
|
|
53
|
+
}
|
|
54
|
+
return `${block}\n\n${userBody}\n`;
|
|
55
|
+
}
|
|
56
|
+
function adapterNeedsUpdate(existing, nextContent, kind, agentsIndexPath) {
|
|
57
|
+
if (!existing?.trim()) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (kind === "skill-mirror") {
|
|
61
|
+
return existing !== nextContent;
|
|
62
|
+
}
|
|
63
|
+
const version = getMcpProbeSkillVersion();
|
|
64
|
+
if (!existing.includes(RULES_POINTER_VERSION_KEY) || !existing.includes(version)) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
if (kind === "claude-pointer") {
|
|
68
|
+
return (!existing.includes(CANONICAL_SKILL_REL_PATH) ||
|
|
69
|
+
Boolean(agentsIndexPath && !existing.includes(agentsIndexPath)));
|
|
70
|
+
}
|
|
71
|
+
if (kind === "rules-pointer" && agentsIndexPath && !existing.includes(agentsIndexPath)) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return existing !== nextContent;
|
|
75
|
+
}
|
|
76
|
+
function resolveAdapterContent(adapter, skillContent, agentsIndexPath) {
|
|
77
|
+
const version = getMcpProbeSkillVersion();
|
|
78
|
+
switch (adapter.kind) {
|
|
79
|
+
case "skill-mirror":
|
|
80
|
+
return skillContent;
|
|
81
|
+
case "rules-pointer":
|
|
82
|
+
return adapter.relPath.endsWith(".mdr")
|
|
83
|
+
? generateComateRulesContent(CANONICAL_SKILL_REL_PATH, version, agentsIndexPath)
|
|
84
|
+
: generateRulesPointerContent(CANONICAL_SKILL_REL_PATH, version, agentsIndexPath);
|
|
85
|
+
case "claude-pointer":
|
|
86
|
+
return mergeClaudePointer(null, generateClaudePointerBlock(CANONICAL_SKILL_REL_PATH, agentsIndexPath));
|
|
87
|
+
default:
|
|
88
|
+
return skillContent;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function writeAdapterFile(projectRoot, adapter, content, agentsIndexPath) {
|
|
92
|
+
const absolute = path.join(projectRoot, adapter.relPath);
|
|
93
|
+
const existing = fs.existsSync(absolute) ? fs.readFileSync(absolute, "utf8") : null;
|
|
94
|
+
if (adapter.kind === "claude-pointer" && existing) {
|
|
95
|
+
const block = generateClaudePointerBlock(CANONICAL_SKILL_REL_PATH, agentsIndexPath);
|
|
96
|
+
const merged = mergeClaudePointer(existing, block);
|
|
97
|
+
if (!adapterNeedsUpdate(existing, merged, adapter.kind, agentsIndexPath)) {
|
|
98
|
+
return {
|
|
99
|
+
id: adapter.id,
|
|
100
|
+
path: adapter.relPath,
|
|
101
|
+
kind: adapter.kind,
|
|
102
|
+
created: false,
|
|
103
|
+
updated: false,
|
|
104
|
+
skipped: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
fs.mkdirSync(path.dirname(absolute), { recursive: true });
|
|
108
|
+
fs.writeFileSync(absolute, merged, "utf8");
|
|
109
|
+
return {
|
|
110
|
+
id: adapter.id,
|
|
111
|
+
path: adapter.relPath,
|
|
112
|
+
kind: adapter.kind,
|
|
113
|
+
created: false,
|
|
114
|
+
updated: true,
|
|
115
|
+
skipped: false,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (!adapterNeedsUpdate(existing, content, adapter.kind, agentsIndexPath)) {
|
|
119
|
+
return {
|
|
120
|
+
id: adapter.id,
|
|
121
|
+
path: adapter.relPath,
|
|
122
|
+
kind: adapter.kind,
|
|
123
|
+
created: false,
|
|
124
|
+
updated: false,
|
|
125
|
+
skipped: true,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
fs.mkdirSync(path.dirname(absolute), { recursive: true });
|
|
129
|
+
fs.writeFileSync(absolute, content, "utf8");
|
|
130
|
+
const hadContent = Boolean(existing?.trim());
|
|
131
|
+
return {
|
|
132
|
+
id: adapter.id,
|
|
133
|
+
path: adapter.relPath,
|
|
134
|
+
kind: adapter.kind,
|
|
135
|
+
created: !hadContent,
|
|
136
|
+
updated: hadContent,
|
|
137
|
+
skipped: false,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function collectInstalledAdapterManifest(projectRoot, detection) {
|
|
141
|
+
return detection.adaptersToWrite
|
|
142
|
+
.filter((adapter) => fs.existsSync(path.join(projectRoot, adapter.relPath)))
|
|
143
|
+
.map((adapter) => ({
|
|
144
|
+
id: adapter.id,
|
|
145
|
+
kind: adapter.kind,
|
|
146
|
+
path: adapter.relPath,
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Write optional harness adapters. AGENTS.md and canonical Skill are unchanged.
|
|
151
|
+
* Canonical Skill must already exist at `.agents/skills/mcp-probe-kit/SKILL.md`.
|
|
152
|
+
*/
|
|
153
|
+
export function ensureHarnessAdapters(projectRoot, skillContent, agentsIndexPath = "AGENTS.md") {
|
|
154
|
+
const root = path.resolve(projectRoot);
|
|
155
|
+
const detection = detectHarnessContext(root);
|
|
156
|
+
const adapters = [];
|
|
157
|
+
for (const adapter of detection.adaptersToWrite) {
|
|
158
|
+
const content = resolveAdapterContent(adapter, skillContent, agentsIndexPath);
|
|
159
|
+
adapters.push(writeAdapterFile(root, adapter, content, agentsIndexPath));
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
detection,
|
|
163
|
+
adapters,
|
|
164
|
+
layoutHarness: toLayoutHarnessManifest(detection, collectInstalledAdapterManifest(root, detection)),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Canonical Skill path — never changes per harness */
|
|
2
|
+
export declare const CANONICAL_SKILL_REL_PATH = ".agents/skills/mcp-probe-kit/SKILL.md";
|
|
3
|
+
export type HarnessId = "agents" | "cursor" | "claude" | "codex" | "opencode" | "trae" | "traecli" | "codebuddy" | "lingma" | "comate";
|
|
4
|
+
export type HarnessAdapterKind = "skill-mirror" | "rules-pointer" | "claude-pointer";
|
|
5
|
+
export interface HarnessAdapterTarget {
|
|
6
|
+
id: string;
|
|
7
|
+
harnessId: HarnessId;
|
|
8
|
+
relPath: string;
|
|
9
|
+
kind: HarnessAdapterKind;
|
|
10
|
+
/** 项目内已有该目录时才写适配层(零配置) */
|
|
11
|
+
markerDir: string;
|
|
12
|
+
}
|
|
13
|
+
export interface HarnessDetectionResult {
|
|
14
|
+
markerHarnesses: HarnessId[];
|
|
15
|
+
detected: HarnessId[];
|
|
16
|
+
skillCanonical: string;
|
|
17
|
+
adaptersToWrite: HarnessAdapterTarget[];
|
|
18
|
+
}
|
|
19
|
+
export declare const HARNESS_ADAPTER_TARGETS: HarnessAdapterTarget[];
|
|
20
|
+
/**
|
|
21
|
+
* 零配置 harness 检测:项目里已有工具目录(如 `.trae/`)则写对应薄适配。
|
|
22
|
+
* AGENTS.md 与 canonical Skill 路径始终不变。
|
|
23
|
+
*/
|
|
24
|
+
export declare function detectHarnessContext(projectRoot: string): HarnessDetectionResult;
|
|
25
|
+
export interface LayoutHarnessManifest {
|
|
26
|
+
detected: HarnessId[];
|
|
27
|
+
skillCanonical: string;
|
|
28
|
+
adapters: Array<{
|
|
29
|
+
id: string;
|
|
30
|
+
kind: HarnessAdapterKind;
|
|
31
|
+
path: string;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
export declare function toLayoutHarnessManifest(detection: HarnessDetectionResult, writtenAdapters: Array<{
|
|
35
|
+
id: string;
|
|
36
|
+
kind: HarnessAdapterKind;
|
|
37
|
+
path: string;
|
|
38
|
+
}>): LayoutHarnessManifest;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { MCP_PROBE_SKILL_REL_PATH } from "./workflow-skill-template.js";
|
|
4
|
+
/** Canonical Skill path — never changes per harness */
|
|
5
|
+
export const CANONICAL_SKILL_REL_PATH = MCP_PROBE_SKILL_REL_PATH;
|
|
6
|
+
export const HARNESS_ADAPTER_TARGETS = [
|
|
7
|
+
{
|
|
8
|
+
id: "trae-skill",
|
|
9
|
+
harnessId: "trae",
|
|
10
|
+
relPath: ".trae/skills/mcp-probe-kit/SKILL.md",
|
|
11
|
+
kind: "skill-mirror",
|
|
12
|
+
markerDir: ".trae",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "traecli-skill",
|
|
16
|
+
harnessId: "traecli",
|
|
17
|
+
relPath: ".traecli/skills/mcp-probe-kit/SKILL.md",
|
|
18
|
+
kind: "skill-mirror",
|
|
19
|
+
markerDir: ".traecli",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "codebuddy-skill",
|
|
23
|
+
harnessId: "codebuddy",
|
|
24
|
+
relPath: ".codebuddy/skills/mcp-probe-kit/SKILL.md",
|
|
25
|
+
kind: "skill-mirror",
|
|
26
|
+
markerDir: ".codebuddy",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "lingma-rules",
|
|
30
|
+
harnessId: "lingma",
|
|
31
|
+
relPath: ".lingma/rules/mcp-probe-kit-mcp.md",
|
|
32
|
+
kind: "rules-pointer",
|
|
33
|
+
markerDir: ".lingma",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: "comate-rules",
|
|
37
|
+
harnessId: "comate",
|
|
38
|
+
relPath: ".comate/rules/mcp-probe-kit-mcp.mdr",
|
|
39
|
+
kind: "rules-pointer",
|
|
40
|
+
markerDir: ".comate",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "claude-pointer",
|
|
44
|
+
harnessId: "claude",
|
|
45
|
+
relPath: "CLAUDE.md",
|
|
46
|
+
kind: "claude-pointer",
|
|
47
|
+
markerDir: ".claude",
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
const MARKER_DIR_TO_HARNESS = {
|
|
51
|
+
".trae": "trae",
|
|
52
|
+
".traecli": "traecli",
|
|
53
|
+
".codebuddy": "codebuddy",
|
|
54
|
+
".lingma": "lingma",
|
|
55
|
+
".comate": "comate",
|
|
56
|
+
".cursor": "cursor",
|
|
57
|
+
".claude": "claude",
|
|
58
|
+
".codex": "codex",
|
|
59
|
+
".opencode": "opencode",
|
|
60
|
+
};
|
|
61
|
+
function detectMarkerHarnesses(projectRoot) {
|
|
62
|
+
const found = [];
|
|
63
|
+
for (const [markerDir, harnessId] of Object.entries(MARKER_DIR_TO_HARNESS)) {
|
|
64
|
+
if (fs.existsSync(path.join(projectRoot, markerDir))) {
|
|
65
|
+
found.push(harnessId);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return found;
|
|
69
|
+
}
|
|
70
|
+
function shouldWriteAdapter(projectRoot, adapter) {
|
|
71
|
+
return fs.existsSync(path.join(projectRoot, adapter.markerDir));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 零配置 harness 检测:项目里已有工具目录(如 `.trae/`)则写对应薄适配。
|
|
75
|
+
* AGENTS.md 与 canonical Skill 路径始终不变。
|
|
76
|
+
*/
|
|
77
|
+
export function detectHarnessContext(projectRoot) {
|
|
78
|
+
const root = path.resolve(projectRoot);
|
|
79
|
+
const markerHarnesses = detectMarkerHarnesses(root);
|
|
80
|
+
const detected = markerHarnesses.length > 0 ? markerHarnesses : ["agents"];
|
|
81
|
+
const adaptersToWrite = HARNESS_ADAPTER_TARGETS.filter((adapter) => shouldWriteAdapter(root, adapter));
|
|
82
|
+
return {
|
|
83
|
+
markerHarnesses,
|
|
84
|
+
detected,
|
|
85
|
+
skillCanonical: CANONICAL_SKILL_REL_PATH,
|
|
86
|
+
adaptersToWrite,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export function toLayoutHarnessManifest(detection, writtenAdapters) {
|
|
90
|
+
return {
|
|
91
|
+
detected: detection.detected,
|
|
92
|
+
skillCanonical: detection.skillCanonical,
|
|
93
|
+
adapters: writtenAdapters,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { mergeDeliveryReports, writeProjectFile, } from "./file-delivery.js";
|
|
2
|
+
export function writeInitProjectSkeletons(input) {
|
|
3
|
+
const { projectRoot, projectName, featureSlug, requirement } = input;
|
|
4
|
+
const specBase = `docs/specs/${featureSlug}`;
|
|
5
|
+
const projectContext = `# ${projectName} - 项目上下文
|
|
6
|
+
|
|
7
|
+
> 由 init_project 自动生成的骨架,请根据仓库实际情况补充。
|
|
8
|
+
|
|
9
|
+
## 项目概览
|
|
10
|
+
|
|
11
|
+
| 属性 | 值 |
|
|
12
|
+
|------|-----|
|
|
13
|
+
| 项目名称 | ${projectName} |
|
|
14
|
+
| 描述 | ${requirement || "待补充"} |
|
|
15
|
+
|
|
16
|
+
## 技术栈
|
|
17
|
+
|
|
18
|
+
- 待补充
|
|
19
|
+
|
|
20
|
+
## 架构
|
|
21
|
+
|
|
22
|
+
- 待补充
|
|
23
|
+
|
|
24
|
+
## 开发流程
|
|
25
|
+
|
|
26
|
+
- 待补充
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
*生成工具: mcp-probe-kit init_project*
|
|
30
|
+
`;
|
|
31
|
+
const constitution = `# ${projectName} - 项目宪法
|
|
32
|
+
|
|
33
|
+
> 定义项目核心原则与约束。
|
|
34
|
+
|
|
35
|
+
## 代码风格
|
|
36
|
+
|
|
37
|
+
- 待补充
|
|
38
|
+
|
|
39
|
+
## 架构原则
|
|
40
|
+
|
|
41
|
+
- 待补充
|
|
42
|
+
|
|
43
|
+
## 安全准则
|
|
44
|
+
|
|
45
|
+
- 待补充
|
|
46
|
+
|
|
47
|
+
## 测试要求
|
|
48
|
+
|
|
49
|
+
- 待补充
|
|
50
|
+
|
|
51
|
+
## 文档标准
|
|
52
|
+
|
|
53
|
+
- 待补充
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
*生成工具: mcp-probe-kit init_project*
|
|
57
|
+
`;
|
|
58
|
+
const requirements = `# 需求文档:${projectName}
|
|
59
|
+
|
|
60
|
+
## 功能概述
|
|
61
|
+
|
|
62
|
+
${requirement || "待补充"}
|
|
63
|
+
|
|
64
|
+
## 术语定义
|
|
65
|
+
|
|
66
|
+
- 待补充
|
|
67
|
+
|
|
68
|
+
## 需求列表(EARS)
|
|
69
|
+
|
|
70
|
+
### 用户故事
|
|
71
|
+
|
|
72
|
+
- As a ... I want ... So that ...
|
|
73
|
+
|
|
74
|
+
### 验收标准
|
|
75
|
+
|
|
76
|
+
- WHEN ... THEN THE system SHALL ...
|
|
77
|
+
|
|
78
|
+
## 非功能需求
|
|
79
|
+
|
|
80
|
+
- 性能、安全、兼容性:待补充
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
*生成工具: mcp-probe-kit init_project*
|
|
84
|
+
`;
|
|
85
|
+
const design = `# 设计文档:${projectName}
|
|
86
|
+
|
|
87
|
+
## 技术方案
|
|
88
|
+
|
|
89
|
+
- 待补充
|
|
90
|
+
|
|
91
|
+
## 架构设计
|
|
92
|
+
|
|
93
|
+
- 待补充
|
|
94
|
+
|
|
95
|
+
## 数据模型
|
|
96
|
+
|
|
97
|
+
- 待补充
|
|
98
|
+
|
|
99
|
+
## API 设计
|
|
100
|
+
|
|
101
|
+
- 待补充
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
*生成工具: mcp-probe-kit init_project*
|
|
105
|
+
`;
|
|
106
|
+
const tasks = `# 任务清单:${projectName}
|
|
107
|
+
|
|
108
|
+
## 概述
|
|
109
|
+
|
|
110
|
+
实现 ${projectName} 的任务分解。
|
|
111
|
+
|
|
112
|
+
## 任务列表
|
|
113
|
+
|
|
114
|
+
### 阶段 1: 项目初始化
|
|
115
|
+
|
|
116
|
+
- [ ] 1.1 搭建项目骨架
|
|
117
|
+
|
|
118
|
+
### 阶段 2: 核心功能
|
|
119
|
+
|
|
120
|
+
- [ ] 2.1 实现核心逻辑
|
|
121
|
+
|
|
122
|
+
### 阶段 3: 测试
|
|
123
|
+
|
|
124
|
+
- [ ] 3.1 编写测试
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
*生成工具: mcp-probe-kit init_project*
|
|
128
|
+
`;
|
|
129
|
+
const research = `# 技术调研:${projectName}
|
|
130
|
+
|
|
131
|
+
## 技术栈调研
|
|
132
|
+
|
|
133
|
+
- 待补充
|
|
134
|
+
|
|
135
|
+
## 版本兼容性
|
|
136
|
+
|
|
137
|
+
- 待补充
|
|
138
|
+
|
|
139
|
+
## 风险与最佳实践
|
|
140
|
+
|
|
141
|
+
- 待补充
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
*生成工具: mcp-probe-kit init_project*
|
|
145
|
+
`;
|
|
146
|
+
return mergeDeliveryReports({
|
|
147
|
+
writtenFiles: [
|
|
148
|
+
writeProjectFile(projectRoot, "docs/project-context.md", projectContext, "ifMissing"),
|
|
149
|
+
writeProjectFile(projectRoot, "docs/constitution.md", constitution, "ifMissing"),
|
|
150
|
+
writeProjectFile(projectRoot, `${specBase}/requirements.md`, requirements, "ifMissing"),
|
|
151
|
+
writeProjectFile(projectRoot, `${specBase}/design.md`, design, "ifMissing"),
|
|
152
|
+
writeProjectFile(projectRoot, `${specBase}/tasks.md`, tasks, "ifMissing"),
|
|
153
|
+
writeProjectFile(projectRoot, `${specBase}/research.md`, research, "ifMissing"),
|
|
154
|
+
],
|
|
155
|
+
pendingFiles: [
|
|
156
|
+
{ path: "scripts/check-prerequisites.sh", reason: "需 Agent 按指南创建辅助脚本" },
|
|
157
|
+
{ path: "scripts/setup.sh", reason: "需 Agent 按指南创建辅助脚本" },
|
|
158
|
+
{ path: "src/", reason: "源代码目录需按项目类型创建" },
|
|
159
|
+
],
|
|
160
|
+
});
|
|
161
|
+
}
|
|
@@ -32,6 +32,15 @@ export interface ProjectContextLayoutArgs {
|
|
|
32
32
|
}
|
|
33
33
|
/** Primary env key recorded in layout.json (optional local override via projectRoot) */
|
|
34
34
|
export declare const LAYOUT_PROJECT_ROOT_ENV = "MCP_PROJECT_ROOT";
|
|
35
|
+
export interface LayoutManifestHarnessV1 {
|
|
36
|
+
detected: string[];
|
|
37
|
+
skillCanonical: string;
|
|
38
|
+
adapters: Array<{
|
|
39
|
+
id: string;
|
|
40
|
+
kind: string;
|
|
41
|
+
path: string;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
35
44
|
export interface LayoutManifestV1 {
|
|
36
45
|
version: 1;
|
|
37
46
|
/** Ignored if present in old files — project root is always inferred from manifest path */
|
|
@@ -45,6 +54,8 @@ export interface LayoutManifestV1 {
|
|
|
45
54
|
indexStyle: "agents" | "legacy";
|
|
46
55
|
generatedBy: string;
|
|
47
56
|
generatedAt: string;
|
|
57
|
+
/** Harness detection snapshot (AGENTS.md + canonical Skill stay harness-agnostic) */
|
|
58
|
+
harness?: LayoutManifestHarnessV1;
|
|
48
59
|
}
|
|
49
60
|
export declare function toPosixPath(value: string): string;
|
|
50
61
|
export declare function relativeLink(fromRel: string, toRel: string): string;
|
|
@@ -66,8 +77,10 @@ export declare function manifestPathRelativeToProject(projectRoot: string, absol
|
|
|
66
77
|
export declare function layoutAbsPath(layout: ProjectContextLayout, relativePath: string): string;
|
|
67
78
|
type ProjectContextLayoutCore = Omit<ProjectContextLayout, "projectRoot" | "projectRootPosix">;
|
|
68
79
|
export declare function attachProjectRoot(layout: ProjectContextLayoutCore, projectRoot: string): ProjectContextLayout;
|
|
69
|
-
export declare function buildLayoutManifest(layout: ProjectContextLayout): LayoutManifestV1;
|
|
70
|
-
export declare function writeLayoutManifest(projectRoot: string, layout: ProjectContextLayout): string;
|
|
80
|
+
export declare function buildLayoutManifest(layout: ProjectContextLayout, harness?: LayoutManifestHarnessV1): LayoutManifestV1;
|
|
81
|
+
export declare function writeLayoutManifest(projectRoot: string, layout: ProjectContextLayout, harness?: LayoutManifestHarnessV1): string;
|
|
82
|
+
/** Patch harness section on an existing layout.json (e.g. after bootstrap adapters). */
|
|
83
|
+
export declare function patchLayoutManifestHarness(projectRoot: string, harness: LayoutManifestHarnessV1): string | null;
|
|
71
84
|
export declare function layoutFromManifest(manifest: LayoutManifestV1, fallbackProjectRoot: string, manifestFilePath?: string): ProjectContextLayout;
|
|
72
85
|
export declare function resolveProjectContextLayout(projectRoot: string, args?: ProjectContextLayoutArgs): ProjectContextLayout;
|
|
73
86
|
export declare function countCjkChars(text: string): number;
|
|
@@ -196,7 +196,7 @@ export function attachProjectRoot(layout, projectRoot) {
|
|
|
196
196
|
projectRootPosix: projectRootToManifestValue(resolved),
|
|
197
197
|
};
|
|
198
198
|
}
|
|
199
|
-
export function buildLayoutManifest(layout) {
|
|
199
|
+
export function buildLayoutManifest(layout, harness) {
|
|
200
200
|
return {
|
|
201
201
|
version: 1,
|
|
202
202
|
projectRootEnv: LAYOUT_PROJECT_ROOT_ENV,
|
|
@@ -207,17 +207,41 @@ export function buildLayoutManifest(layout) {
|
|
|
207
207
|
indexStyle: layout.indexStyle,
|
|
208
208
|
generatedBy: "init_project_context",
|
|
209
209
|
generatedAt: new Date().toISOString(),
|
|
210
|
+
...(harness ? { harness } : {}),
|
|
210
211
|
};
|
|
211
212
|
}
|
|
212
|
-
export function writeLayoutManifest(projectRoot, layout) {
|
|
213
|
+
export function writeLayoutManifest(projectRoot, layout, harness) {
|
|
213
214
|
const resolvedRoot = path.resolve(projectRoot);
|
|
214
215
|
const manifestRel = layoutManifestRel(layout.contextRoot);
|
|
215
|
-
const manifest = buildLayoutManifest(attachProjectRoot(layout, resolvedRoot));
|
|
216
|
+
const manifest = buildLayoutManifest(attachProjectRoot(layout, resolvedRoot), harness);
|
|
216
217
|
const absoluteManifest = path.join(resolvedRoot, ...manifestRel.split("/"));
|
|
217
218
|
fs.mkdirSync(path.dirname(absoluteManifest), { recursive: true });
|
|
218
219
|
fs.writeFileSync(absoluteManifest, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
219
220
|
return manifestRel;
|
|
220
221
|
}
|
|
222
|
+
function layoutHarnessEquals(left, right) {
|
|
223
|
+
return JSON.stringify(left ?? null) === JSON.stringify(right);
|
|
224
|
+
}
|
|
225
|
+
/** Patch harness section on an existing layout.json (e.g. after bootstrap adapters). */
|
|
226
|
+
export function patchLayoutManifestHarness(projectRoot, harness) {
|
|
227
|
+
const resolvedRoot = path.resolve(projectRoot);
|
|
228
|
+
const existing = readLayoutManifest(resolvedRoot);
|
|
229
|
+
if (!existing) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
if (layoutHarnessEquals(existing.harness, harness)) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const layout = layoutFromManifest(existing, resolvedRoot);
|
|
236
|
+
const manifestRel = layout.manifestPath;
|
|
237
|
+
const absoluteManifest = path.join(resolvedRoot, ...manifestRel.split("/"));
|
|
238
|
+
const updated = {
|
|
239
|
+
...existing,
|
|
240
|
+
harness,
|
|
241
|
+
};
|
|
242
|
+
fs.writeFileSync(absoluteManifest, `${JSON.stringify(updated, null, 2)}\n`, "utf8");
|
|
243
|
+
return manifestRel;
|
|
244
|
+
}
|
|
221
245
|
export function layoutFromManifest(manifest, fallbackProjectRoot, manifestFilePath) {
|
|
222
246
|
const contextRoot = normalizeRelativePath(manifest.contextRoot);
|
|
223
247
|
const projectRoot = resolveManifestProjectRoot(manifest, fallbackProjectRoot, manifestFilePath);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ProjectContextLayout } from "./project-context-layout.js";
|
|
2
|
+
export interface WrittenFileRecord {
|
|
3
|
+
path: string;
|
|
4
|
+
action: "created" | "updated" | "skipped";
|
|
5
|
+
}
|
|
6
|
+
export interface ProjectContextWriteResult {
|
|
7
|
+
agentsMd: WrittenFileRecord;
|
|
8
|
+
projectContextIndex: WrittenFileRecord | null;
|
|
9
|
+
modularDocs: WrittenFileRecord[];
|
|
10
|
+
}
|
|
11
|
+
type DocEntry = {
|
|
12
|
+
file: string;
|
|
13
|
+
title: string;
|
|
14
|
+
purpose: string;
|
|
15
|
+
};
|
|
16
|
+
type ProjectInfo = {
|
|
17
|
+
name: string;
|
|
18
|
+
version: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
type Detection = {
|
|
22
|
+
language: string;
|
|
23
|
+
framework?: string;
|
|
24
|
+
category: string;
|
|
25
|
+
confidence?: number;
|
|
26
|
+
};
|
|
27
|
+
export declare function buildProjectContextIndexMarkdown(projectInfo: ProjectInfo, detection: Detection, docs: DocEntry[], docsDir: string): string;
|
|
28
|
+
export declare function buildModularDocSkeleton(doc: DocEntry, projectInfo: ProjectInfo, detection: Detection): string;
|
|
29
|
+
/**
|
|
30
|
+
* 由 init_project_context 服务端直接落盘(勿依赖 Agent fsWrite)。
|
|
31
|
+
*/
|
|
32
|
+
export declare function writeProjectContextArtifacts(input: {
|
|
33
|
+
layout: ProjectContextLayout;
|
|
34
|
+
mergedAgentsContent: string;
|
|
35
|
+
skipModularDocs: boolean;
|
|
36
|
+
projectInfo: ProjectInfo;
|
|
37
|
+
detection: Detection;
|
|
38
|
+
docs: DocEntry[];
|
|
39
|
+
}): ProjectContextWriteResult;
|
|
40
|
+
export declare function formatWriteSummary(result: ProjectContextWriteResult): string;
|
|
41
|
+
export {};
|