mcp-probe-kit 3.0.18 → 3.0.21
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 +87 -55
- package/build/index.js +3 -1
- package/build/lib/__tests__/agents-md-template.unit.test.d.ts +1 -0
- package/build/lib/__tests__/agents-md-template.unit.test.js +27 -0
- package/build/lib/__tests__/memory-config.unit.test.js +9 -0
- package/build/lib/__tests__/memory-injection.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-injection.unit.test.js +51 -0
- package/build/lib/__tests__/memory-orchestration.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-orchestration.unit.test.js +84 -0
- package/build/lib/__tests__/memory-payload.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-payload.unit.test.js +35 -0
- package/build/lib/__tests__/project-context-layout.unit.test.d.ts +1 -0
- package/build/lib/__tests__/project-context-layout.unit.test.js +80 -0
- package/build/lib/agents-md-template.d.ts +25 -0
- package/build/lib/agents-md-template.js +57 -0
- package/build/lib/memory-client.d.ts +8 -1
- package/build/lib/memory-client.js +53 -44
- package/build/lib/memory-config.d.ts +8 -0
- package/build/lib/memory-config.js +19 -0
- package/build/lib/memory-orchestration.d.ts +10 -3
- package/build/lib/memory-orchestration.js +146 -7
- package/build/lib/memory-payload.d.ts +21 -0
- package/build/lib/memory-payload.js +65 -0
- package/build/lib/merge-agents-md.d.ts +6 -0
- package/build/lib/merge-agents-md.js +51 -0
- package/build/lib/project-context-layout.d.ts +78 -0
- package/build/lib/project-context-layout.js +350 -0
- package/build/lib/workspace-root.js +6 -1
- package/build/resources/ui-ux-data/metadata.json +1 -1
- package/build/schemas/index.d.ts +62 -11
- package/build/schemas/memory-tools.d.ts +38 -9
- package/build/schemas/memory-tools.js +24 -9
- package/build/schemas/project-tools.d.ts +24 -2
- package/build/schemas/project-tools.js +24 -2
- package/build/tools/__tests__/code_insight.unit.test.js +3 -3
- package/build/tools/__tests__/init_project_context.unit.test.js +32 -21
- package/build/tools/__tests__/start_feature.unit.test.js +2 -1
- package/build/tools/code_insight.js +11 -9
- package/build/tools/index.d.ts +1 -0
- package/build/tools/index.js +1 -0
- package/build/tools/init_project_context.js +563 -506
- package/build/tools/memorize_asset.js +12 -0
- package/build/tools/scan_and_extract_patterns.js +7 -7
- package/build/tools/search_memory.d.ts +7 -0
- package/build/tools/search_memory.js +57 -0
- package/build/tools/start_bugfix.js +257 -251
- package/build/tools/start_feature.js +140 -134
- package/build/tools/start_ui.js +405 -405
- package/docs/.mcp-probe/layout.json +11 -0
- package/docs/data/tools.js +18 -0
- package/docs/i18n/all-tools/en.json +6 -1
- package/docs/i18n/all-tools/ja.json +2 -1
- package/docs/i18n/all-tools/ko.json +2 -1
- package/docs/i18n/all-tools/zh-CN.json +7 -2
- package/docs/i18n/en.json +38 -7
- package/docs/i18n/ja.json +9 -2
- package/docs/i18n/ko.json +9 -2
- package/docs/i18n/zh-CN.json +40 -9
- package/docs/memory-local-setup.md +314 -0
- package/docs/memory-local-setup.zh-CN.md +283 -0
- package/docs/pages/getting-started.html +252 -33
- package/package.json +2 -2
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export declare const DEFAULT_CONTEXT_ROOT = "docs";
|
|
2
|
+
export declare const DEFAULT_INDEX_PATH = "AGENTS.md";
|
|
3
|
+
/** Relative path to layout manifest under a given context root */
|
|
4
|
+
export declare function layoutManifestRel(contextRoot?: string): string;
|
|
5
|
+
/** Default manifest location when contextRoot is `docs` */
|
|
6
|
+
export declare const LAYOUT_MANIFEST_REL: string;
|
|
7
|
+
export type IndexStyle = "auto" | "agents" | "legacy";
|
|
8
|
+
export type LayoutSource = "args" | "manifest" | "detect" | "default";
|
|
9
|
+
export type DocumentLocale = "en" | "zh-CN";
|
|
10
|
+
export interface ProjectContextLayout {
|
|
11
|
+
/** Absolute project root (platform path) */
|
|
12
|
+
projectRoot: string;
|
|
13
|
+
/** Absolute project root (POSIX, stored in layout.json) */
|
|
14
|
+
projectRootPosix: string;
|
|
15
|
+
indexPath: string;
|
|
16
|
+
contextRoot: string;
|
|
17
|
+
modularDir: string;
|
|
18
|
+
graphDir: string;
|
|
19
|
+
latestMarkdownPath: string;
|
|
20
|
+
latestJsonPath: string;
|
|
21
|
+
manifestPath: string;
|
|
22
|
+
indexStyle: "agents" | "legacy";
|
|
23
|
+
source: LayoutSource;
|
|
24
|
+
legacyIndexPath: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ProjectContextLayoutArgs {
|
|
27
|
+
docs_dir?: string;
|
|
28
|
+
output?: string;
|
|
29
|
+
output_dir?: string;
|
|
30
|
+
filename?: string;
|
|
31
|
+
index_style?: IndexStyle;
|
|
32
|
+
}
|
|
33
|
+
/** Primary env key recorded in layout.json (optional local override via projectRoot) */
|
|
34
|
+
export declare const LAYOUT_PROJECT_ROOT_ENV = "MCP_PROJECT_ROOT";
|
|
35
|
+
export interface LayoutManifestV1 {
|
|
36
|
+
version: 1;
|
|
37
|
+
/** Ignored if present in old files — project root is always inferred from manifest path */
|
|
38
|
+
projectRoot?: string;
|
|
39
|
+
/** Env fallback when manifest path cannot be resolved (default MCP_PROJECT_ROOT) */
|
|
40
|
+
projectRootEnv?: string;
|
|
41
|
+
indexPath: string;
|
|
42
|
+
contextRoot: string;
|
|
43
|
+
modularDir: string;
|
|
44
|
+
graphDir: string;
|
|
45
|
+
indexStyle: "agents" | "legacy";
|
|
46
|
+
generatedBy: string;
|
|
47
|
+
generatedAt: string;
|
|
48
|
+
}
|
|
49
|
+
export declare function toPosixPath(value: string): string;
|
|
50
|
+
export declare function relativeLink(fromRel: string, toRel: string): string;
|
|
51
|
+
export declare function assertPathInsideProject(projectRoot: string, relativePath: string): void;
|
|
52
|
+
export declare function projectRootToManifestValue(projectRoot: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Resolve project root: infer from manifest file path → env → fallback (never reads manifest.projectRoot).
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveManifestProjectRoot(manifest: LayoutManifestV1, fallbackProjectRoot: string, manifestFilePath?: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* `{projectRoot}/{contextRoot}/.mcp-probe/layout.json` → walk up from manifest file to project root.
|
|
59
|
+
*/
|
|
60
|
+
export declare function inferProjectRootFromManifestPath(manifestFilePath: string, contextRoot?: string): string;
|
|
61
|
+
export declare function findLayoutManifestPath(startDir: string): string | null;
|
|
62
|
+
export declare function discoverProjectRootFromLayout(startDir: string): string | null;
|
|
63
|
+
export declare function readLayoutManifestFromPath(manifestFilePath: string): LayoutManifestV1 | null;
|
|
64
|
+
export declare function readLayoutManifest(projectRoot: string): LayoutManifestV1 | null;
|
|
65
|
+
export declare function manifestPathRelativeToProject(projectRoot: string, absoluteManifestPath: string): string;
|
|
66
|
+
export declare function layoutAbsPath(layout: ProjectContextLayout, relativePath: string): string;
|
|
67
|
+
type ProjectContextLayoutCore = Omit<ProjectContextLayout, "projectRoot" | "projectRootPosix">;
|
|
68
|
+
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;
|
|
71
|
+
export declare function layoutFromManifest(manifest: LayoutManifestV1, fallbackProjectRoot: string, manifestFilePath?: string): ProjectContextLayout;
|
|
72
|
+
export declare function resolveProjectContextLayout(projectRoot: string, args?: ProjectContextLayoutArgs): ProjectContextLayout;
|
|
73
|
+
export declare function countCjkChars(text: string): number;
|
|
74
|
+
export declare function detectDocumentLocale(projectRoot: string, existingAgentsContent?: string): DocumentLocale;
|
|
75
|
+
export declare function legacyProjectContextExists(projectRoot: string, layout: ProjectContextLayout): boolean;
|
|
76
|
+
export declare function agentsMdExists(projectRoot: string, layout: ProjectContextLayout): boolean;
|
|
77
|
+
export declare function parseLayoutArgsFromRecord(args: Record<string, unknown>): ProjectContextLayoutArgs;
|
|
78
|
+
export {};
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
export const DEFAULT_CONTEXT_ROOT = "docs";
|
|
4
|
+
export const DEFAULT_INDEX_PATH = "AGENTS.md";
|
|
5
|
+
/** Relative path to layout manifest under a given context root */
|
|
6
|
+
export function layoutManifestRel(contextRoot = DEFAULT_CONTEXT_ROOT) {
|
|
7
|
+
return joinRel(normalizeRelativePath(contextRoot), ".mcp-probe/layout.json");
|
|
8
|
+
}
|
|
9
|
+
/** Default manifest location when contextRoot is `docs` */
|
|
10
|
+
export const LAYOUT_MANIFEST_REL = layoutManifestRel(DEFAULT_CONTEXT_ROOT);
|
|
11
|
+
const MANIFEST_SCAN_SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", "coverage"]);
|
|
12
|
+
/** Primary env key recorded in layout.json (optional local override via projectRoot) */
|
|
13
|
+
export const LAYOUT_PROJECT_ROOT_ENV = "MCP_PROJECT_ROOT";
|
|
14
|
+
const LAYOUT_PROJECT_ROOT_ENV_FALLBACKS = [
|
|
15
|
+
LAYOUT_PROJECT_ROOT_ENV,
|
|
16
|
+
"MCP_WORKSPACE_ROOT",
|
|
17
|
+
"CURSOR_WORKSPACE_ROOT",
|
|
18
|
+
"WORKSPACE_ROOT",
|
|
19
|
+
"PROJECT_ROOT",
|
|
20
|
+
];
|
|
21
|
+
export function toPosixPath(value) {
|
|
22
|
+
return value.replace(/\\/g, "/");
|
|
23
|
+
}
|
|
24
|
+
export function relativeLink(fromRel, toRel) {
|
|
25
|
+
const fromDir = path.posix.dirname(toPosixPath(fromRel).replace(/^\.\//, "") || ".");
|
|
26
|
+
const toPath = toPosixPath(toRel).replace(/^\.\//, "");
|
|
27
|
+
let rel = path.posix.relative(fromDir, toPath);
|
|
28
|
+
if (!rel || rel === ".") {
|
|
29
|
+
return toPath;
|
|
30
|
+
}
|
|
31
|
+
if (!rel.startsWith(".")) {
|
|
32
|
+
rel = `./${rel}`;
|
|
33
|
+
}
|
|
34
|
+
return rel;
|
|
35
|
+
}
|
|
36
|
+
function normalizeRelativePath(raw) {
|
|
37
|
+
const cleaned = toPosixPath(raw.trim()).replace(/^\/+/, "").replace(/\/+$/, "");
|
|
38
|
+
if (!cleaned || cleaned === ".") {
|
|
39
|
+
return cleaned || ".";
|
|
40
|
+
}
|
|
41
|
+
if (cleaned.split("/").includes("..")) {
|
|
42
|
+
throw new Error(`路径不允许包含 '..': ${raw}`);
|
|
43
|
+
}
|
|
44
|
+
return cleaned;
|
|
45
|
+
}
|
|
46
|
+
function joinRel(...segments) {
|
|
47
|
+
return normalizeRelativePath(path.posix.join(...segments.filter(Boolean)));
|
|
48
|
+
}
|
|
49
|
+
function isExistingDirectory(target) {
|
|
50
|
+
try {
|
|
51
|
+
return fs.existsSync(target) && fs.statSync(target).isDirectory();
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function assertPathInsideProject(projectRoot, relativePath) {
|
|
58
|
+
const resolved = path.resolve(projectRoot, relativePath);
|
|
59
|
+
const root = path.resolve(projectRoot);
|
|
60
|
+
const relative = path.relative(root, resolved);
|
|
61
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
62
|
+
throw new Error(`路径必须在项目根目录内: ${relativePath}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function fileExists(projectRoot, relativePath) {
|
|
66
|
+
return fs.existsSync(path.join(projectRoot, relativePath));
|
|
67
|
+
}
|
|
68
|
+
export function projectRootToManifestValue(projectRoot) {
|
|
69
|
+
return toPosixPath(path.resolve(projectRoot));
|
|
70
|
+
}
|
|
71
|
+
function resolveProjectRootFromEnv(envKey) {
|
|
72
|
+
const keys = envKey?.trim()
|
|
73
|
+
? [envKey.trim(), ...LAYOUT_PROJECT_ROOT_ENV_FALLBACKS]
|
|
74
|
+
: [...LAYOUT_PROJECT_ROOT_ENV_FALLBACKS];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
for (const key of keys) {
|
|
77
|
+
if (seen.has(key)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
seen.add(key);
|
|
81
|
+
const raw = process.env[key]?.trim();
|
|
82
|
+
if (!raw) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const resolved = path.resolve(raw.replace(/\//g, path.sep));
|
|
86
|
+
if (isExistingDirectory(resolved)) {
|
|
87
|
+
return resolved;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Resolve project root: infer from manifest file path → env → fallback (never reads manifest.projectRoot).
|
|
94
|
+
*/
|
|
95
|
+
export function resolveManifestProjectRoot(manifest, fallbackProjectRoot, manifestFilePath) {
|
|
96
|
+
if (manifestFilePath) {
|
|
97
|
+
const inferred = inferProjectRootFromManifestPath(manifestFilePath, manifest.contextRoot);
|
|
98
|
+
if (isExistingDirectory(inferred)) {
|
|
99
|
+
return inferred;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const fromEnv = resolveProjectRootFromEnv(manifest.projectRootEnv);
|
|
103
|
+
if (fromEnv) {
|
|
104
|
+
return fromEnv;
|
|
105
|
+
}
|
|
106
|
+
return path.resolve(fallbackProjectRoot);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* `{projectRoot}/{contextRoot}/.mcp-probe/layout.json` → walk up from manifest file to project root.
|
|
110
|
+
*/
|
|
111
|
+
export function inferProjectRootFromManifestPath(manifestFilePath, contextRoot = DEFAULT_CONTEXT_ROOT) {
|
|
112
|
+
let dir = path.dirname(path.resolve(manifestFilePath));
|
|
113
|
+
const segments = layoutManifestRel(contextRoot).split("/").filter(Boolean);
|
|
114
|
+
for (let i = 1; i < segments.length; i++) {
|
|
115
|
+
dir = path.dirname(dir);
|
|
116
|
+
}
|
|
117
|
+
return dir;
|
|
118
|
+
}
|
|
119
|
+
function findLayoutManifestInTree(dir) {
|
|
120
|
+
const defaultCandidate = path.join(dir, LAYOUT_MANIFEST_REL);
|
|
121
|
+
if (fs.existsSync(defaultCandidate)) {
|
|
122
|
+
return defaultCandidate;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
126
|
+
if (!entry.isDirectory() || MANIFEST_SCAN_SKIP_DIRS.has(entry.name)) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const candidate = path.join(dir, entry.name, ".mcp-probe", "layout.json");
|
|
130
|
+
if (fs.existsSync(candidate)) {
|
|
131
|
+
return candidate;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
export function findLayoutManifestPath(startDir) {
|
|
141
|
+
let current = path.resolve(startDir);
|
|
142
|
+
while (true) {
|
|
143
|
+
const found = findLayoutManifestInTree(current);
|
|
144
|
+
if (found) {
|
|
145
|
+
return found;
|
|
146
|
+
}
|
|
147
|
+
const parent = path.dirname(current);
|
|
148
|
+
if (parent === current) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
current = parent;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export function discoverProjectRootFromLayout(startDir) {
|
|
155
|
+
const manifestPath = findLayoutManifestPath(startDir);
|
|
156
|
+
if (!manifestPath) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const manifest = readLayoutManifestFromPath(manifestPath);
|
|
160
|
+
if (!manifest) {
|
|
161
|
+
return inferProjectRootFromManifestPath(manifestPath);
|
|
162
|
+
}
|
|
163
|
+
return resolveManifestProjectRoot(manifest, startDir, manifestPath);
|
|
164
|
+
}
|
|
165
|
+
export function readLayoutManifestFromPath(manifestFilePath) {
|
|
166
|
+
if (!fs.existsSync(manifestFilePath)) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const parsed = JSON.parse(fs.readFileSync(manifestFilePath, "utf8"));
|
|
171
|
+
if (parsed?.version !== 1) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
return parsed;
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export function readLayoutManifest(projectRoot) {
|
|
181
|
+
const manifestPath = findLayoutManifestPath(projectRoot);
|
|
182
|
+
return manifestPath ? readLayoutManifestFromPath(manifestPath) : null;
|
|
183
|
+
}
|
|
184
|
+
export function manifestPathRelativeToProject(projectRoot, absoluteManifestPath) {
|
|
185
|
+
const rel = path.relative(path.resolve(projectRoot), path.resolve(absoluteManifestPath));
|
|
186
|
+
return toPosixPath(rel);
|
|
187
|
+
}
|
|
188
|
+
export function layoutAbsPath(layout, relativePath) {
|
|
189
|
+
return path.join(layout.projectRoot, normalizeRelativePath(relativePath));
|
|
190
|
+
}
|
|
191
|
+
export function attachProjectRoot(layout, projectRoot) {
|
|
192
|
+
const resolved = path.resolve(projectRoot);
|
|
193
|
+
return {
|
|
194
|
+
...layout,
|
|
195
|
+
projectRoot: resolved,
|
|
196
|
+
projectRootPosix: projectRootToManifestValue(resolved),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export function buildLayoutManifest(layout) {
|
|
200
|
+
return {
|
|
201
|
+
version: 1,
|
|
202
|
+
projectRootEnv: LAYOUT_PROJECT_ROOT_ENV,
|
|
203
|
+
indexPath: layout.indexPath,
|
|
204
|
+
contextRoot: layout.contextRoot,
|
|
205
|
+
modularDir: layout.modularDir,
|
|
206
|
+
graphDir: layout.graphDir,
|
|
207
|
+
indexStyle: layout.indexStyle,
|
|
208
|
+
generatedBy: "init_project_context",
|
|
209
|
+
generatedAt: new Date().toISOString(),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
export function writeLayoutManifest(projectRoot, layout) {
|
|
213
|
+
const resolvedRoot = path.resolve(projectRoot);
|
|
214
|
+
const manifestRel = layoutManifestRel(layout.contextRoot);
|
|
215
|
+
const manifest = buildLayoutManifest(attachProjectRoot(layout, resolvedRoot));
|
|
216
|
+
const absoluteManifest = path.join(resolvedRoot, ...manifestRel.split("/"));
|
|
217
|
+
fs.mkdirSync(path.dirname(absoluteManifest), { recursive: true });
|
|
218
|
+
fs.writeFileSync(absoluteManifest, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
219
|
+
return manifestRel;
|
|
220
|
+
}
|
|
221
|
+
export function layoutFromManifest(manifest, fallbackProjectRoot, manifestFilePath) {
|
|
222
|
+
const contextRoot = normalizeRelativePath(manifest.contextRoot);
|
|
223
|
+
const projectRoot = resolveManifestProjectRoot(manifest, fallbackProjectRoot, manifestFilePath);
|
|
224
|
+
const graphDir = normalizeRelativePath(manifest.graphDir);
|
|
225
|
+
const manifestRel = manifestFilePath
|
|
226
|
+
? manifestPathRelativeToProject(projectRoot, manifestFilePath)
|
|
227
|
+
: layoutManifestRel(contextRoot);
|
|
228
|
+
return attachProjectRoot({
|
|
229
|
+
indexPath: normalizeRelativePath(manifest.indexPath),
|
|
230
|
+
contextRoot,
|
|
231
|
+
modularDir: normalizeRelativePath(manifest.modularDir),
|
|
232
|
+
graphDir,
|
|
233
|
+
latestMarkdownPath: joinRel(graphDir, "latest.md"),
|
|
234
|
+
latestJsonPath: joinRel(graphDir, "latest.json"),
|
|
235
|
+
manifestPath: manifestRel,
|
|
236
|
+
indexStyle: manifest.indexStyle,
|
|
237
|
+
source: "manifest",
|
|
238
|
+
legacyIndexPath: joinRel(contextRoot, "project-context.md"),
|
|
239
|
+
}, projectRoot);
|
|
240
|
+
}
|
|
241
|
+
function buildLayoutFromParts(projectRoot, contextRoot, indexPath, indexStyle, source) {
|
|
242
|
+
const modularDir = joinRel(contextRoot, "project-context");
|
|
243
|
+
const graphDir = joinRel(contextRoot, "graph-insights");
|
|
244
|
+
return attachProjectRoot({
|
|
245
|
+
indexPath,
|
|
246
|
+
contextRoot,
|
|
247
|
+
modularDir,
|
|
248
|
+
graphDir,
|
|
249
|
+
latestMarkdownPath: joinRel(graphDir, "latest.md"),
|
|
250
|
+
latestJsonPath: joinRel(graphDir, "latest.json"),
|
|
251
|
+
manifestPath: layoutManifestRel(contextRoot),
|
|
252
|
+
indexStyle,
|
|
253
|
+
source,
|
|
254
|
+
legacyIndexPath: joinRel(contextRoot, "project-context.md"),
|
|
255
|
+
}, projectRoot);
|
|
256
|
+
}
|
|
257
|
+
export function resolveProjectContextLayout(projectRoot, args = {}) {
|
|
258
|
+
const startRoot = path.resolve(projectRoot);
|
|
259
|
+
const hasExplicitArgs = Boolean(args.docs_dir?.trim() ||
|
|
260
|
+
args.output?.trim() ||
|
|
261
|
+
args.output_dir?.trim() ||
|
|
262
|
+
args.filename?.trim() ||
|
|
263
|
+
args.index_style);
|
|
264
|
+
const manifestPath = findLayoutManifestPath(startRoot);
|
|
265
|
+
const manifest = manifestPath ? readLayoutManifestFromPath(manifestPath) : readLayoutManifest(startRoot);
|
|
266
|
+
let canonicalRoot = startRoot;
|
|
267
|
+
if (!hasExplicitArgs && manifest) {
|
|
268
|
+
canonicalRoot = resolveManifestProjectRoot(manifest, startRoot, manifestPath ?? undefined);
|
|
269
|
+
}
|
|
270
|
+
if (!hasExplicitArgs && manifest) {
|
|
271
|
+
const fromManifest = layoutFromManifest(manifest, canonicalRoot, manifestPath ?? undefined);
|
|
272
|
+
assertPathInsideProject(fromManifest.projectRoot, fromManifest.indexPath);
|
|
273
|
+
assertPathInsideProject(fromManifest.projectRoot, fromManifest.contextRoot);
|
|
274
|
+
return fromManifest;
|
|
275
|
+
}
|
|
276
|
+
const contextRoot = normalizeRelativePath(args.docs_dir?.trim() || DEFAULT_CONTEXT_ROOT);
|
|
277
|
+
let indexPath;
|
|
278
|
+
let indexStyle;
|
|
279
|
+
if (args.output?.trim()) {
|
|
280
|
+
indexPath = normalizeRelativePath(args.output.trim());
|
|
281
|
+
indexStyle = indexPath.endsWith("AGENTS.md") ? "agents" : "legacy";
|
|
282
|
+
}
|
|
283
|
+
else if (args.output_dir?.trim()) {
|
|
284
|
+
indexPath = joinRel(args.output_dir.trim(), args.filename?.trim() || "project-context.md");
|
|
285
|
+
indexStyle = "legacy";
|
|
286
|
+
}
|
|
287
|
+
else if (args.index_style === "legacy") {
|
|
288
|
+
indexPath = joinRel(contextRoot, "project-context.md");
|
|
289
|
+
indexStyle = "legacy";
|
|
290
|
+
}
|
|
291
|
+
else if (args.index_style === "agents") {
|
|
292
|
+
indexPath = DEFAULT_INDEX_PATH;
|
|
293
|
+
indexStyle = "agents";
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
indexPath = DEFAULT_INDEX_PATH;
|
|
297
|
+
indexStyle = "agents";
|
|
298
|
+
}
|
|
299
|
+
const layout = buildLayoutFromParts(canonicalRoot, contextRoot, indexPath, indexStyle, hasExplicitArgs ? "args" : manifest ? "manifest" : "default");
|
|
300
|
+
assertPathInsideProject(layout.projectRoot, layout.indexPath);
|
|
301
|
+
assertPathInsideProject(layout.projectRoot, layout.contextRoot);
|
|
302
|
+
assertPathInsideProject(layout.projectRoot, layout.modularDir);
|
|
303
|
+
assertPathInsideProject(layout.projectRoot, layout.graphDir);
|
|
304
|
+
return layout;
|
|
305
|
+
}
|
|
306
|
+
export function countCjkChars(text) {
|
|
307
|
+
return (text.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []).length;
|
|
308
|
+
}
|
|
309
|
+
export function detectDocumentLocale(projectRoot, existingAgentsContent) {
|
|
310
|
+
if (existingAgentsContent) {
|
|
311
|
+
const begin = existingAgentsContent.indexOf("<!-- mcp-probe:context begin");
|
|
312
|
+
const end = existingAgentsContent.indexOf("<!-- mcp-probe:context end -->");
|
|
313
|
+
if (begin !== -1 && end > begin) {
|
|
314
|
+
const block = existingAgentsContent.slice(begin, end);
|
|
315
|
+
if (countCjkChars(block) >= 8) {
|
|
316
|
+
return "zh-CN";
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const readmePath = path.join(projectRoot, "README.md");
|
|
321
|
+
if (fs.existsSync(readmePath)) {
|
|
322
|
+
const sample = fs.readFileSync(readmePath, "utf8").slice(0, 4000);
|
|
323
|
+
const cjk = countCjkChars(sample);
|
|
324
|
+
const letters = (sample.match(/[a-zA-Z]/g) || []).length;
|
|
325
|
+
if (cjk >= 20 && cjk / Math.max(letters, 1) > 0.12) {
|
|
326
|
+
return "zh-CN";
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (fs.existsSync(path.join(projectRoot, "i18n", "README.zh-CN.md"))) {
|
|
330
|
+
return "zh-CN";
|
|
331
|
+
}
|
|
332
|
+
return "en";
|
|
333
|
+
}
|
|
334
|
+
export function legacyProjectContextExists(projectRoot, layout) {
|
|
335
|
+
return fileExists(layout.projectRoot, layout.legacyIndexPath);
|
|
336
|
+
}
|
|
337
|
+
export function agentsMdExists(projectRoot, layout) {
|
|
338
|
+
return fileExists(layout.projectRoot, layout.indexPath);
|
|
339
|
+
}
|
|
340
|
+
export function parseLayoutArgsFromRecord(args) {
|
|
341
|
+
return {
|
|
342
|
+
docs_dir: typeof args.docs_dir === "string" ? args.docs_dir : undefined,
|
|
343
|
+
output: typeof args.output === "string" ? args.output : undefined,
|
|
344
|
+
output_dir: typeof args.output_dir === "string" ? args.output_dir : undefined,
|
|
345
|
+
filename: typeof args.filename === "string" ? args.filename : undefined,
|
|
346
|
+
index_style: args.index_style === "auto" || args.index_style === "agents" || args.index_style === "legacy"
|
|
347
|
+
? args.index_style
|
|
348
|
+
: undefined,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { discoverProjectRootFromLayout } from "./project-context-layout.js";
|
|
4
5
|
const WORKSPACE_ENV_KEYS = [
|
|
5
6
|
"MCP_PROJECT_ROOT",
|
|
6
7
|
"MCP_WORKSPACE_ROOT",
|
|
@@ -131,10 +132,14 @@ export function buildProjectRootRetryHint(inputPath) {
|
|
|
131
132
|
export function resolveWorkspaceRoot(explicitProjectRoot) {
|
|
132
133
|
const explicit = safeResolve(explicitProjectRoot || "");
|
|
133
134
|
if (isExistingDirectory(explicit)) {
|
|
134
|
-
return explicit;
|
|
135
|
+
return discoverProjectRootFromLayout(explicit) ?? explicit;
|
|
135
136
|
}
|
|
136
137
|
const packageRoot = getRuntimePackageRoot();
|
|
137
138
|
const cwd = safeResolve(process.cwd()) || packageRoot;
|
|
139
|
+
const fromLayout = discoverProjectRootFromLayout(cwd);
|
|
140
|
+
if (fromLayout) {
|
|
141
|
+
return fromLayout;
|
|
142
|
+
}
|
|
138
143
|
for (const key of WORKSPACE_ENV_KEYS) {
|
|
139
144
|
const candidate = safeResolve(process.env[key] || "");
|
|
140
145
|
const resolved = findWorkspaceAncestor(candidate, packageRoot);
|
package/build/schemas/index.d.ts
CHANGED
|
@@ -195,13 +195,35 @@ export declare const allToolSchemas: ({
|
|
|
195
195
|
};
|
|
196
196
|
} | {
|
|
197
197
|
readonly name: "init_project_context";
|
|
198
|
-
readonly description: "
|
|
198
|
+
readonly description: "生成/更新项目上下文:默认写入 AGENTS.md(含 MCP 与 GitNexus 触发规则)及 docs/project-context/。新功能请先 start_feature,修 bug 请先 start_bugfix。完成后 Agent 应阅读 AGENTS.md。";
|
|
199
199
|
readonly inputSchema: {
|
|
200
200
|
readonly type: "object";
|
|
201
201
|
readonly properties: {
|
|
202
202
|
readonly docs_dir: {
|
|
203
203
|
readonly type: "string";
|
|
204
|
-
readonly description: "
|
|
204
|
+
readonly description: "附属文档根目录(project-context、graph-insights)。默认 docs";
|
|
205
|
+
};
|
|
206
|
+
readonly index_style: {
|
|
207
|
+
readonly type: "string";
|
|
208
|
+
readonly enum: readonly ["auto", "agents", "legacy"];
|
|
209
|
+
readonly description: "索引风格:auto(默认 AGENTS.md)、agents、legacy(docs/project-context.md)";
|
|
210
|
+
};
|
|
211
|
+
readonly output: {
|
|
212
|
+
readonly type: "string";
|
|
213
|
+
readonly description: "高级:索引文件相对路径,如 AGENTS.md";
|
|
214
|
+
};
|
|
215
|
+
readonly output_dir: {
|
|
216
|
+
readonly type: "string";
|
|
217
|
+
readonly description: "高级:索引所在目录,如 .claude/rules";
|
|
218
|
+
};
|
|
219
|
+
readonly filename: {
|
|
220
|
+
readonly type: "string";
|
|
221
|
+
readonly description: "高级:与 output_dir 合用,默认 project-context.md";
|
|
222
|
+
};
|
|
223
|
+
readonly locale: {
|
|
224
|
+
readonly type: "string";
|
|
225
|
+
readonly enum: readonly ["en", "zh-CN"];
|
|
226
|
+
readonly description: "AGENTS.md 语言;默认根据 README 探测";
|
|
205
227
|
};
|
|
206
228
|
};
|
|
207
229
|
readonly required: readonly [];
|
|
@@ -666,6 +688,35 @@ export declare const allToolSchemas: ({
|
|
|
666
688
|
};
|
|
667
689
|
required: never[];
|
|
668
690
|
};
|
|
691
|
+
} | {
|
|
692
|
+
readonly name: "search_memory";
|
|
693
|
+
readonly description: "按语义检索共享记忆库。适合在 start_* 之外主动查找历史 Bug 修复或可复用模式;命中后用 read_memory_asset 读取全文。";
|
|
694
|
+
readonly inputSchema: {
|
|
695
|
+
readonly type: "object";
|
|
696
|
+
readonly properties: {
|
|
697
|
+
readonly query: {
|
|
698
|
+
readonly type: "string";
|
|
699
|
+
readonly description: "检索 query(现象、报错、关键词、功能描述等)";
|
|
700
|
+
};
|
|
701
|
+
readonly type: {
|
|
702
|
+
readonly type: "string";
|
|
703
|
+
readonly description: "优先匹配的资产类型,如 bugfix、pattern、component";
|
|
704
|
+
};
|
|
705
|
+
readonly tags: {
|
|
706
|
+
readonly type: "array";
|
|
707
|
+
readonly items: {
|
|
708
|
+
readonly type: "string";
|
|
709
|
+
};
|
|
710
|
+
readonly description: "优先匹配的标签";
|
|
711
|
+
};
|
|
712
|
+
readonly limit: {
|
|
713
|
+
readonly type: "number";
|
|
714
|
+
readonly description: "返回条数,默认 MEMORY_SEARCH_LIMIT";
|
|
715
|
+
};
|
|
716
|
+
};
|
|
717
|
+
readonly required: readonly ["query"];
|
|
718
|
+
readonly additionalProperties: true;
|
|
719
|
+
};
|
|
669
720
|
} | {
|
|
670
721
|
readonly name: "read_memory_asset";
|
|
671
722
|
readonly description: "当编排阶段已检索到记忆摘要,且 AI 需要查看完整沉淀代码或详细规范时使用。根据 asset_id 读取记忆资产详情。";
|
|
@@ -682,7 +733,7 @@ export declare const allToolSchemas: ({
|
|
|
682
733
|
};
|
|
683
734
|
} | {
|
|
684
735
|
readonly name: "memorize_asset";
|
|
685
|
-
readonly description: "
|
|
736
|
+
readonly description: "沉淀可检索资产到共享记忆库。Bug 修复后必须 type=bugfix,content 含【现象】【根因】【修复】【验证】。跨仓库共享时勿填 source_project/source_path,路径写入 content 即可。";
|
|
686
737
|
readonly inputSchema: {
|
|
687
738
|
readonly type: "object";
|
|
688
739
|
readonly properties: {
|
|
@@ -692,7 +743,7 @@ export declare const allToolSchemas: ({
|
|
|
692
743
|
};
|
|
693
744
|
readonly type: {
|
|
694
745
|
readonly type: "string";
|
|
695
|
-
readonly description: "
|
|
746
|
+
readonly description: "资产类型:bugfix / pattern / component / code 等";
|
|
696
747
|
};
|
|
697
748
|
readonly description: {
|
|
698
749
|
readonly type: "string";
|
|
@@ -700,11 +751,11 @@ export declare const allToolSchemas: ({
|
|
|
700
751
|
};
|
|
701
752
|
readonly summary: {
|
|
702
753
|
readonly type: "string";
|
|
703
|
-
readonly description: "
|
|
754
|
+
readonly description: "检索用一句话摘要(关键词 + 根因/要点)";
|
|
704
755
|
};
|
|
705
756
|
readonly content: {
|
|
706
757
|
readonly type: "string";
|
|
707
|
-
readonly description: "
|
|
758
|
+
readonly description: "完整内容(bugfix 建议结构化四段)";
|
|
708
759
|
};
|
|
709
760
|
readonly code_snippet: {
|
|
710
761
|
readonly type: "string";
|
|
@@ -712,15 +763,15 @@ export declare const allToolSchemas: ({
|
|
|
712
763
|
};
|
|
713
764
|
readonly file_path: {
|
|
714
765
|
readonly type: "string";
|
|
715
|
-
readonly description: "
|
|
766
|
+
readonly description: "已废弃:勿用于跨仓库沉淀,路径写入 content";
|
|
716
767
|
};
|
|
717
768
|
readonly source_project: {
|
|
718
769
|
readonly type: "string";
|
|
719
|
-
readonly description: "
|
|
770
|
+
readonly description: "已废弃:仅同仓库追溯时可选";
|
|
720
771
|
};
|
|
721
772
|
readonly source_path: {
|
|
722
773
|
readonly type: "string";
|
|
723
|
-
readonly description: "
|
|
774
|
+
readonly description: "已废弃:仅同仓库追溯时可选";
|
|
724
775
|
};
|
|
725
776
|
readonly usage: {
|
|
726
777
|
readonly type: "string";
|
|
@@ -735,7 +786,7 @@ export declare const allToolSchemas: ({
|
|
|
735
786
|
readonly items: {
|
|
736
787
|
readonly type: "string";
|
|
737
788
|
};
|
|
738
|
-
readonly description: "
|
|
789
|
+
readonly description: "标签列表,如 bugfix, root-cause";
|
|
739
790
|
};
|
|
740
791
|
};
|
|
741
792
|
readonly required: readonly ["name", "description", "summary"];
|
|
@@ -757,7 +808,7 @@ export declare const allToolSchemas: ({
|
|
|
757
808
|
};
|
|
758
809
|
readonly project_name: {
|
|
759
810
|
readonly type: "string";
|
|
760
|
-
readonly description: "
|
|
811
|
+
readonly description: "已废弃,扫描结果不再写入 source_project";
|
|
761
812
|
};
|
|
762
813
|
readonly directory_path: {
|
|
763
814
|
readonly type: "string";
|