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.
Files changed (62) hide show
  1. package/README.md +87 -55
  2. package/build/index.js +3 -1
  3. package/build/lib/__tests__/agents-md-template.unit.test.d.ts +1 -0
  4. package/build/lib/__tests__/agents-md-template.unit.test.js +27 -0
  5. package/build/lib/__tests__/memory-config.unit.test.js +9 -0
  6. package/build/lib/__tests__/memory-injection.unit.test.d.ts +1 -0
  7. package/build/lib/__tests__/memory-injection.unit.test.js +51 -0
  8. package/build/lib/__tests__/memory-orchestration.unit.test.d.ts +1 -0
  9. package/build/lib/__tests__/memory-orchestration.unit.test.js +84 -0
  10. package/build/lib/__tests__/memory-payload.unit.test.d.ts +1 -0
  11. package/build/lib/__tests__/memory-payload.unit.test.js +35 -0
  12. package/build/lib/__tests__/project-context-layout.unit.test.d.ts +1 -0
  13. package/build/lib/__tests__/project-context-layout.unit.test.js +80 -0
  14. package/build/lib/agents-md-template.d.ts +25 -0
  15. package/build/lib/agents-md-template.js +57 -0
  16. package/build/lib/memory-client.d.ts +8 -1
  17. package/build/lib/memory-client.js +53 -44
  18. package/build/lib/memory-config.d.ts +8 -0
  19. package/build/lib/memory-config.js +19 -0
  20. package/build/lib/memory-orchestration.d.ts +10 -3
  21. package/build/lib/memory-orchestration.js +146 -7
  22. package/build/lib/memory-payload.d.ts +21 -0
  23. package/build/lib/memory-payload.js +65 -0
  24. package/build/lib/merge-agents-md.d.ts +6 -0
  25. package/build/lib/merge-agents-md.js +51 -0
  26. package/build/lib/project-context-layout.d.ts +78 -0
  27. package/build/lib/project-context-layout.js +350 -0
  28. package/build/lib/workspace-root.js +6 -1
  29. package/build/resources/ui-ux-data/metadata.json +1 -1
  30. package/build/schemas/index.d.ts +62 -11
  31. package/build/schemas/memory-tools.d.ts +38 -9
  32. package/build/schemas/memory-tools.js +24 -9
  33. package/build/schemas/project-tools.d.ts +24 -2
  34. package/build/schemas/project-tools.js +24 -2
  35. package/build/tools/__tests__/code_insight.unit.test.js +3 -3
  36. package/build/tools/__tests__/init_project_context.unit.test.js +32 -21
  37. package/build/tools/__tests__/start_feature.unit.test.js +2 -1
  38. package/build/tools/code_insight.js +11 -9
  39. package/build/tools/index.d.ts +1 -0
  40. package/build/tools/index.js +1 -0
  41. package/build/tools/init_project_context.js +563 -506
  42. package/build/tools/memorize_asset.js +12 -0
  43. package/build/tools/scan_and_extract_patterns.js +7 -7
  44. package/build/tools/search_memory.d.ts +7 -0
  45. package/build/tools/search_memory.js +57 -0
  46. package/build/tools/start_bugfix.js +257 -251
  47. package/build/tools/start_feature.js +140 -134
  48. package/build/tools/start_ui.js +405 -405
  49. package/docs/.mcp-probe/layout.json +11 -0
  50. package/docs/data/tools.js +18 -0
  51. package/docs/i18n/all-tools/en.json +6 -1
  52. package/docs/i18n/all-tools/ja.json +2 -1
  53. package/docs/i18n/all-tools/ko.json +2 -1
  54. package/docs/i18n/all-tools/zh-CN.json +7 -2
  55. package/docs/i18n/en.json +38 -7
  56. package/docs/i18n/ja.json +9 -2
  57. package/docs/i18n/ko.json +9 -2
  58. package/docs/i18n/zh-CN.json +40 -9
  59. package/docs/memory-local-setup.md +314 -0
  60. package/docs/memory-local-setup.zh-CN.md +283 -0
  61. package/docs/pages/getting-started.html +252 -33
  62. 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);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "2.2.3",
3
- "syncedAt": "2026-05-20T09:05:15.322Z",
3
+ "syncedAt": "2026-05-27T08:58:13.736Z",
4
4
  "source": "uipro-cli",
5
5
  "format": "json"
6
6
  }
@@ -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: "文档目录。可选,默认 docs";
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: " AI 完成开发并确认存在可复用代码、模式或规范时使用。将高价值资产沉淀到记忆系统。";
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: "资产类型,如 code/component/pattern/spec";
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";