mcp-probe-kit 3.0.17 → 3.0.19

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 (37) hide show
  1. package/README.md +77 -50
  2. package/build/lib/__tests__/agents-md-template.unit.test.d.ts +1 -0
  3. package/build/lib/__tests__/agents-md-template.unit.test.js +25 -0
  4. package/build/lib/__tests__/project-context-layout.unit.test.d.ts +1 -0
  5. package/build/lib/__tests__/project-context-layout.unit.test.js +80 -0
  6. package/build/lib/agents-md-template.d.ts +25 -0
  7. package/build/lib/agents-md-template.js +55 -0
  8. package/build/lib/memory-orchestration.d.ts +3 -1
  9. package/build/lib/memory-orchestration.js +71 -5
  10. package/build/lib/merge-agents-md.d.ts +6 -0
  11. package/build/lib/merge-agents-md.js +51 -0
  12. package/build/lib/project-context-layout.d.ts +78 -0
  13. package/build/lib/project-context-layout.js +350 -0
  14. package/build/lib/workspace-root.js +6 -1
  15. package/build/resources/ui-ux-data/metadata.json +1 -1
  16. package/build/schemas/index.d.ts +25 -3
  17. package/build/schemas/memory-tools.d.ts +1 -1
  18. package/build/schemas/memory-tools.js +1 -1
  19. package/build/schemas/project-tools.d.ts +24 -2
  20. package/build/schemas/project-tools.js +24 -2
  21. package/build/tools/__tests__/code_insight.unit.test.js +3 -3
  22. package/build/tools/__tests__/init_project_context.unit.test.js +32 -21
  23. package/build/tools/__tests__/start_feature.unit.test.js +2 -1
  24. package/build/tools/code_insight.js +11 -9
  25. package/build/tools/init_project_context.js +563 -506
  26. package/build/tools/start_bugfix.js +254 -248
  27. package/build/tools/start_feature.js +137 -131
  28. package/build/tools/start_ui.js +402 -402
  29. package/docs/.mcp-probe/layout.json +11 -0
  30. package/docs/i18n/en.json +36 -5
  31. package/docs/i18n/ja.json +9 -2
  32. package/docs/i18n/ko.json +9 -2
  33. package/docs/i18n/zh-CN.json +36 -5
  34. package/docs/memory-local-setup.md +314 -0
  35. package/docs/memory-local-setup.zh-CN.md +280 -0
  36. package/docs/pages/getting-started.html +249 -31
  37. package/package.json +1 -1
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ # mcp-probe-kit — Know the Context, Feed the Moment
2
+
1
3
  <div align="center">
2
4
  <img src="docs/assets/logo.png" alt="知时MCP Logo" width="160"/>
3
5
  <h1>知时MCP | mcp-probe-kit</h1>
@@ -39,6 +41,7 @@ A powerful MCP (Model Context Protocol) server providing **28 tools** covering t
39
41
  **👉 [https://mcp-probe-kit.bytezonex.com](https://mcp-probe-kit.bytezonex.com/)**
40
42
 
41
43
  - [Quick Start](https://mcp-probe-kit.bytezonex.com/pages/getting-started.html) - Setup in 5 minutes
44
+ - [Local Memory Stack (Qdrant + Nomic Embed)](docs/memory-local-setup.md) - Docker Compose, ports `50008` / `50012`, MCP env
42
45
  - [All Tools](https://mcp-probe-kit.bytezonex.com/pages/all-tools.html) - Complete list of 28 tools
43
46
  - [Best Practices](https://mcp-probe-kit.bytezonex.com/pages/examples.html) - Full development workflow guide
44
47
  - [v3.0 Migration Guide](https://mcp-probe-kit.bytezonex.com/pages/migration.html) - Upgrade from v2.x to v3.0
@@ -103,10 +106,10 @@ A powerful MCP (Model Context Protocol) server providing **28 tools** covering t
103
106
 
104
107
  **Memory backend and embedding configuration:**
105
108
  - Vector database: **Qdrant**
106
- - Recommended local setup: `Qdrant + Ollama`
109
+ - **Recommended local setup:** `Qdrant (port 50008) + Infinity / nomic-embed (port 50012)` — lighter than Ollama; see **[Local Memory Stack guide](docs/memory-local-setup.md)** (中文: [memory-local-setup.zh-CN.md](docs/memory-local-setup.zh-CN.md))
107
110
  - Supported embedding providers:
108
111
  - `ollama`
109
- - `openai-compatible`
112
+ - `openai-compatible` (Infinity, OpenAI, etc.)
110
113
  - Required environment variables for memory write/search:
111
114
  - `MEMORY_QDRANT_URL`
112
115
  - `MEMORY_EMBEDDING_URL`
@@ -123,11 +126,9 @@ A powerful MCP (Model Context Protocol) server providing **28 tools** covering t
123
126
  - Memory write is enabled only when `MEMORY_QDRANT_URL`, `MEMORY_EMBEDDING_URL`, and `MEMORY_EMBEDDING_MODEL` are all configured
124
127
  - The Qdrant collection is auto-created on first write, and vector dimension is inferred from the first embedding response
125
128
 
126
- **Recommended local memory setup (Qdrant + Ollama):**
127
- ```bash
128
- docker run -d --name mcp-qdrant -p 6333:6333 qdrant/qdrant
129
- ollama pull nomic-embed-text
130
- ```
129
+ **Recommended local memory setup (Qdrant + Nomic Embed / Infinity):**
130
+
131
+ Full Docker Compose, ports, and troubleshooting: **[docs/memory-local-setup.md](docs/memory-local-setup.md)**
131
132
 
132
133
  ```json
133
134
  {
@@ -136,11 +137,13 @@ ollama pull nomic-embed-text
136
137
  "command": "npx",
137
138
  "args": ["-y", "mcp-probe-kit@latest"],
138
139
  "env": {
139
- "MEMORY_QDRANT_URL": "http://127.0.0.1:6333",
140
+ "MEMORY_QDRANT_URL": "http://127.0.0.1:50008",
141
+ "MEMORY_QDRANT_API_KEY": "your-qdrant-api-key",
140
142
  "MEMORY_QDRANT_COLLECTION": "mcp_probe_memory",
141
- "MEMORY_EMBEDDING_PROVIDER": "ollama",
142
- "MEMORY_EMBEDDING_URL": "http://127.0.0.1:11434/api/embeddings",
143
- "MEMORY_EMBEDDING_MODEL": "nomic-embed-text",
143
+ "MEMORY_EMBEDDING_PROVIDER": "openai-compatible",
144
+ "MEMORY_EMBEDDING_URL": "http://127.0.0.1:50012/embeddings",
145
+ "MEMORY_EMBEDDING_MODEL": "nomic-ai/nomic-embed-text-v1.5",
146
+ "MEMORY_EMBEDDING_API_KEY": "your-infinity-api-key",
144
147
  "MEMORY_SEARCH_LIMIT": "3",
145
148
  "MEMORY_SUMMARY_MAX_CHARS": "280"
146
149
  }
@@ -149,7 +152,21 @@ ollama pull nomic-embed-text
149
152
  }
150
153
  ```
151
154
 
152
- **OpenAI-compatible embedding setup:**
155
+ **Alternative: Qdrant + Ollama** (if you already run Ollama):
156
+
157
+ ```bash
158
+ docker run -d --name mcp-qdrant -p 6333:6333 qdrant/qdrant
159
+ ollama pull nomic-embed-text
160
+ ```
161
+
162
+ ```json
163
+ "MEMORY_QDRANT_URL": "http://127.0.0.1:6333",
164
+ "MEMORY_EMBEDDING_PROVIDER": "ollama",
165
+ "MEMORY_EMBEDDING_URL": "http://127.0.0.1:11434/api/embeddings",
166
+ "MEMORY_EMBEDDING_MODEL": "nomic-embed-text"
167
+ ```
168
+
169
+ **OpenAI-compatible embedding (hosted API):**
153
170
  ```json
154
171
  {
155
172
  "mcpServers": {
@@ -414,6 +431,27 @@ No installation needed, use the latest version directly.
414
431
  }
415
432
  ```
416
433
 
434
+ #### OpenCode Configuration
435
+
436
+ **Config file location:**
437
+ - Project-level: `opencode.json` (in project root)
438
+ - Global: `~/.config/opencode/opencode.json`
439
+
440
+ **Config content:**
441
+ ```json
442
+ {
443
+ "mcp": {
444
+ "mcp-probe-kit": {
445
+ "type": "local",
446
+ "command": ["npx", "-y", "mcp-probe-kit@latest"],
447
+ "enabled": true
448
+ }
449
+ }
450
+ }
451
+ ```
452
+
453
+ > **Note:** OpenCode uses `opencode.json` with a different schema from Cursor/Claude Desktop. The key `mcp` replaces `mcpServers`, `command` is an array, `type: "local"` is required, and environment variables use `environment` instead of `env`. See [OpenCode MCP docs](https://opencode.ai/docs/mcp) for details.
454
+
417
455
  ### Method 2: Global Installation
418
456
 
419
457
  ```bash
@@ -438,21 +476,14 @@ If you want to use `memorize_asset`, `read_memory_asset`, and `scan_and_extract_
438
476
  1. A **Qdrant** vector database
439
477
  2. An **embedding service** in either `ollama` or `openai-compatible` mode
440
478
 
441
- #### Option A: Qdrant + Ollama
479
+ **Full guide (Docker Compose for Qdrant + Infinity, ports `50008` / `50012`, MCP env, smoke tests):**
442
480
 
443
- Start Qdrant with Docker:
481
+ - English: [docs/memory-local-setup.md](docs/memory-local-setup.md)
482
+ - 中文: [docs/memory-local-setup.zh-CN.md](docs/memory-local-setup.zh-CN.md)
444
483
 
445
- ```bash
446
- docker run -d --name mcp-qdrant -p 6333:6333 qdrant/qdrant
447
- ```
448
-
449
- Start Ollama and pull the default embedding model:
450
-
451
- ```bash
452
- ollama pull nomic-embed-text
453
- ```
484
+ #### Option A: Qdrant + Nomic Embed / Infinity (recommended)
454
485
 
455
- Recommended MCP config env:
486
+ Lightweight local stack; no Ollama. Deploy Qdrant and `nomic-embed` via Docker Compose (see guide), then:
456
487
 
457
488
  ```json
458
489
  {
@@ -461,11 +492,13 @@ Recommended MCP config env:
461
492
  "command": "npx",
462
493
  "args": ["-y", "mcp-probe-kit@latest"],
463
494
  "env": {
464
- "MEMORY_QDRANT_URL": "http://127.0.0.1:6333",
495
+ "MEMORY_QDRANT_URL": "http://127.0.0.1:50008",
496
+ "MEMORY_QDRANT_API_KEY": "your-qdrant-api-key",
465
497
  "MEMORY_QDRANT_COLLECTION": "mcp_probe_memory",
466
- "MEMORY_EMBEDDING_PROVIDER": "ollama",
467
- "MEMORY_EMBEDDING_URL": "http://127.0.0.1:11434/api/embeddings",
468
- "MEMORY_EMBEDDING_MODEL": "nomic-embed-text",
498
+ "MEMORY_EMBEDDING_PROVIDER": "openai-compatible",
499
+ "MEMORY_EMBEDDING_URL": "http://127.0.0.1:50012/embeddings",
500
+ "MEMORY_EMBEDDING_MODEL": "nomic-ai/nomic-embed-text-v1.5",
501
+ "MEMORY_EMBEDDING_API_KEY": "your-infinity-api-key",
469
502
  "MEMORY_SEARCH_LIMIT": "3",
470
503
  "MEMORY_SUMMARY_MAX_CHARS": "280"
471
504
  }
@@ -474,36 +507,30 @@ Recommended MCP config env:
474
507
  }
475
508
  ```
476
509
 
477
- #### Option B: Qdrant + OpenAI-Compatible Embedding API
510
+ > Embedding URL must be `/embeddings` (not `/v1/embeddings`). Qdrant requires `api-key` when `QDRANT__SERVICE__API_KEY` is set.
478
511
 
479
- Start Qdrant with Docker:
512
+ #### Option B: Qdrant + Ollama
480
513
 
481
514
  ```bash
482
515
  docker run -d --name mcp-qdrant -p 6333:6333 qdrant/qdrant
516
+ ollama pull nomic-embed-text
517
+ ```
518
+
519
+ ```json
520
+ "MEMORY_QDRANT_URL": "http://127.0.0.1:6333",
521
+ "MEMORY_EMBEDDING_PROVIDER": "ollama",
522
+ "MEMORY_EMBEDDING_URL": "http://127.0.0.1:11434/api/embeddings",
523
+ "MEMORY_EMBEDDING_MODEL": "nomic-embed-text"
483
524
  ```
484
525
 
485
- Then point the embedding config to an OpenAI-compatible `/embeddings` endpoint:
526
+ #### Option C: Qdrant + hosted OpenAI-compatible API
486
527
 
487
528
  ```json
488
- {
489
- "mcpServers": {
490
- "mcp-probe-kit": {
491
- "command": "npx",
492
- "args": ["-y", "mcp-probe-kit@latest"],
493
- "env": {
494
- "MEMORY_QDRANT_URL": "http://127.0.0.1:6333",
495
- "MEMORY_QDRANT_COLLECTION": "mcp_probe_memory",
496
- "MEMORY_QDRANT_API_KEY": "",
497
- "MEMORY_EMBEDDING_PROVIDER": "openai-compatible",
498
- "MEMORY_EMBEDDING_URL": "https://your-embedding-endpoint/v1/embeddings",
499
- "MEMORY_EMBEDDING_API_KEY": "your-api-key",
500
- "MEMORY_EMBEDDING_MODEL": "text-embedding-3-small",
501
- "MEMORY_SEARCH_LIMIT": "3",
502
- "MEMORY_SUMMARY_MAX_CHARS": "280"
503
- }
504
- }
505
- }
506
- }
529
+ "MEMORY_QDRANT_URL": "http://127.0.0.1:50008",
530
+ "MEMORY_EMBEDDING_PROVIDER": "openai-compatible",
531
+ "MEMORY_EMBEDDING_URL": "https://your-embedding-endpoint/v1/embeddings",
532
+ "MEMORY_EMBEDDING_API_KEY": "your-api-key",
533
+ "MEMORY_EMBEDDING_MODEL": "text-embedding-3-small"
507
534
  ```
508
535
 
509
536
  #### Memory Environment Variables
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { generateAgentsMdInner } from '../agents-md-template.js';
3
+ import { resolveProjectContextLayout } from '../project-context-layout.js';
4
+ const layout = resolveProjectContextLayout(process.cwd());
5
+ const baseInput = {
6
+ layout,
7
+ locale: 'zh-CN',
8
+ projectName: 'demo',
9
+ projectVersion: '1.0.0',
10
+ description: 'test',
11
+ language: 'TypeScript',
12
+ category: 'library',
13
+ docs: [],
14
+ projectRootPosix: '/repo',
15
+ graphReady: false,
16
+ };
17
+ describe('generateAgentsMdInner', () => {
18
+ it('includes memory workflow in zh-CN template', () => {
19
+ const md = generateAgentsMdInner({ ...baseInput });
20
+ expect(md).toContain('记忆');
21
+ expect(md).toContain('start_bugfix');
22
+ expect(md).toContain('memorize_asset');
23
+ expect(md).toContain('bugfix');
24
+ });
25
+ });
@@ -0,0 +1,80 @@
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 { detectDocumentLocale, discoverProjectRootFromLayout, layoutAbsPath, readLayoutManifest, relativeLink, resolveProjectContextLayout, writeLayoutManifest, } from "../project-context-layout.js";
6
+ import { mergeAgentsMdBlock } from "../merge-agents-md.js";
7
+ describe("project-context-layout", () => {
8
+ test("relativeLink from AGENTS.md to docs paths", () => {
9
+ const link = relativeLink("AGENTS.md", "docs/project-context/tech-stack.md");
10
+ expect(link === "docs/project-context/tech-stack.md" || link === "./docs/project-context/tech-stack.md").toBe(true);
11
+ });
12
+ test("default layout uses AGENTS.md and docs/", () => {
13
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "layout-"));
14
+ const layout = resolveProjectContextLayout(root, {});
15
+ expect(layout.indexPath).toBe("AGENTS.md");
16
+ expect(layout.contextRoot).toBe("docs");
17
+ expect(layout.modularDir).toBe("docs/project-context");
18
+ expect(layout.latestMarkdownPath).toBe("docs/graph-insights/latest.md");
19
+ });
20
+ test("writeLayoutManifest is portable (no absolute projectRoot)", () => {
21
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "layout-manifest-"));
22
+ const layout = resolveProjectContextLayout(root, {});
23
+ const rel = writeLayoutManifest(root, layout);
24
+ expect(rel).toBe("docs/.mcp-probe/layout.json");
25
+ const manifest = readLayoutManifest(root);
26
+ expect(manifest?.indexPath).toBe("AGENTS.md");
27
+ expect(manifest?.projectRoot).toBeUndefined();
28
+ expect(manifest?.projectRootEnv).toBe("MCP_PROJECT_ROOT");
29
+ expect(discoverProjectRootFromLayout(root)).toBe(path.resolve(root));
30
+ });
31
+ test("ignores stale absolute projectRoot in manifest", () => {
32
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "layout-stale-root-"));
33
+ const layout = resolveProjectContextLayout(root, {});
34
+ writeLayoutManifest(root, layout);
35
+ const manifestPath = path.join(root, "docs", ".mcp-probe", "layout.json");
36
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
37
+ manifest.projectRoot = "Z:/nonexistent/wrong-root";
38
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
39
+ expect(discoverProjectRootFromLayout(path.join(root, "src"))).toBe(path.resolve(root));
40
+ });
41
+ test("infers root for custom contextRoot manifest path", () => {
42
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "layout-custom-docs-"));
43
+ const layout = resolveProjectContextLayout(root, { docs_dir: "documentation" });
44
+ writeLayoutManifest(root, layout);
45
+ expect(fs.existsSync(path.join(root, "documentation", ".mcp-probe", "layout.json"))).toBe(true);
46
+ const fromSub = resolveProjectContextLayout(path.join(root, "src"), {});
47
+ expect(fromSub.projectRoot).toBe(path.resolve(root));
48
+ expect(fromSub.contextRoot).toBe("documentation");
49
+ });
50
+ test("discovers projectRoot from layout.json when cwd is subdirectory", () => {
51
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "layout-discover-"));
52
+ const layout = resolveProjectContextLayout(root, {});
53
+ writeLayoutManifest(root, layout);
54
+ const subDir = path.join(root, "src", "tools");
55
+ fs.mkdirSync(subDir, { recursive: true });
56
+ expect(discoverProjectRootFromLayout(subDir)).toBe(path.resolve(root));
57
+ const fromSub = resolveProjectContextLayout(subDir, {});
58
+ expect(fromSub.projectRoot).toBe(path.resolve(root));
59
+ expect(layoutAbsPath(fromSub, "docs/project-context.md")).toBe(path.join(root, "docs", "project-context.md"));
60
+ });
61
+ test("detectDocumentLocale zh from README", () => {
62
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "layout-zh-"));
63
+ fs.writeFileSync(path.join(root, "README.md"), "# 测试项目\n\n这是一个中文项目说明,用于单元测试。\n".repeat(5), "utf8");
64
+ expect(detectDocumentLocale(root)).toBe("zh-CN");
65
+ });
66
+ });
67
+ describe("mergeAgentsMdBlock", () => {
68
+ test("prepends block when no marker exists", () => {
69
+ const { content, mergeMode } = mergeAgentsMdBlock("# User rules\n", "## MCP block");
70
+ expect(mergeMode).toBe("prepended");
71
+ expect(content.startsWith("<!-- mcp-probe:context begin")).toBe(true);
72
+ expect(content).toContain("# User rules");
73
+ });
74
+ test("replaces and moves block to top", () => {
75
+ const existing = "# Footer\n\n<!-- mcp-probe:context begin -->\nold\n<!-- mcp-probe:context end -->\n";
76
+ const { content, mergeMode } = mergeAgentsMdBlock(existing, "new inner");
77
+ expect(mergeMode).toBe("replaced-and-moved-to-top");
78
+ expect(content.indexOf("new inner")).toBeLessThan(content.indexOf("# Footer"));
79
+ });
80
+ });
@@ -0,0 +1,25 @@
1
+ import type { DocumentLocale, ProjectContextLayout } from "./project-context-layout.js";
2
+ export interface AgentsMdTemplateInput {
3
+ layout: ProjectContextLayout;
4
+ locale: DocumentLocale;
5
+ projectName: string;
6
+ projectVersion: string;
7
+ description: string;
8
+ language: string;
9
+ framework?: string;
10
+ category: string;
11
+ docs: Array<{
12
+ file: string;
13
+ title: string;
14
+ purpose: string;
15
+ }>;
16
+ projectRootPosix: string;
17
+ graphReady: boolean;
18
+ /** @deprecated kept for callers; memory rules are always included in AGENTS.md */
19
+ memoryEnabled?: boolean;
20
+ }
21
+ /**
22
+ * Ultra-compact AGENTS.md body (inside mcp-probe block).
23
+ */
24
+ export declare function generateAgentsMdInner(input: AgentsMdTemplateInput): string;
25
+ export declare function generateAgentsMdTemplate(input: AgentsMdTemplateInput): string;
@@ -0,0 +1,55 @@
1
+ import { relativeLink } from "./project-context-layout.js";
2
+ function link(layout, targetRel) {
3
+ return relativeLink(layout.indexPath, targetRel);
4
+ }
5
+ function memorySection(locale) {
6
+ if (locale === "zh-CN") {
7
+ return `
8
+ 记忆(需 MEMORY_QDRANT_URL 等已配置):
9
+ - 检索:\`start_feature\` / \`start_bugfix\` / \`start_ui\` 会自动搜记忆;命中后用 \`read_memory_asset\` 读全文再动手
10
+ - Bug 修完验证通过后 → **必须** \`memorize_asset\` type=\`bugfix\` tags=\`bugfix,root-cause\`(现象+根因+改法)
11
+ - 功能/UI 有可复用产出 → \`memorize_asset\` type=\`pattern\`/\`component\``;
12
+ }
13
+ return `
14
+ Memory (requires MEMORY_* env):
15
+ - Search: \`start_feature\` / \`start_bugfix\` / \`start_ui\` auto-search; use \`read_memory_asset\` on hits before coding
16
+ - After verified bugfix → MUST \`memorize_asset\` type=\`bugfix\` tags=\`bugfix,root-cause\` (symptom + root cause + fix)
17
+ - Reusable feature/UI → \`memorize_asset\` type=\`pattern\`/\`component\``;
18
+ }
19
+ /**
20
+ * Ultra-compact AGENTS.md body (inside mcp-probe block).
21
+ */
22
+ export function generateAgentsMdInner(input) {
23
+ const { layout, locale } = input;
24
+ const graph = link(layout, layout.latestMarkdownPath);
25
+ const ctxIndex = link(layout, layout.legacyIndexPath);
26
+ if (locale === "zh-CN") {
27
+ return `## MCP(必须先调)
28
+ 需已配置 mcp-probe-kit。\`start_*\` 若返回 delegated plan,逐步执行完再结束。
29
+
30
+ - 新功能 → \`start_feature\`(会先搜记忆)
31
+ - Bug → \`start_bugfix\`(会先搜记忆)
32
+ - UI → \`start_ui\`(会先搜记忆)
33
+ - 不熟代码 / 影响面 → \`code_insight\`(context / impact / auto)
34
+ - 缺上下文 → \`init_project_context\`
35
+ - 提交 → \`gencommit\`
36
+
37
+ 上下文:写代码前先读 [project-context](${ctxIndex})(链到 \`${layout.modularDir}/\` 各文档)
38
+ 图谱:大改前读 [latest](${graph});过期 \`code_insight\` mode=auto save_to_docs=true${memorySection(locale)}`;
39
+ }
40
+ return `## MCP (call first)
41
+ Requires mcp-probe-kit. Complete every \`start_*\` delegated plan step before done.
42
+
43
+ - Feature → \`start_feature\` (searches memory first)
44
+ - Bug → \`start_bugfix\` (searches memory first)
45
+ - UI → \`start_ui\` (searches memory first)
46
+ - Unfamiliar code / impact → \`code_insight\` (context / impact / auto)
47
+ - Missing context → \`init_project_context\`
48
+ - Commit → \`gencommit\`
49
+
50
+ Context: before coding read [project-context](${ctxIndex}) (links to \`${layout.modularDir}/\`)
51
+ Graph: read [latest](${graph}) before large changes; refresh \`code_insight\` mode=auto save_to_docs=true${memorySection(locale)}`;
52
+ }
53
+ export function generateAgentsMdTemplate(input) {
54
+ return generateAgentsMdInner(input);
55
+ }
@@ -1,4 +1,5 @@
1
1
  import type { MemorySearchResult } from './memory-client.js';
2
+ export type MemoryPlanKind = 'feature' | 'bugfix' | 'ui' | 'default';
2
3
  export interface MemoryInjectionContext {
3
4
  enabled: boolean;
4
5
  available: boolean;
@@ -9,7 +10,7 @@ export interface MemoryInjectionContext {
9
10
  }
10
11
  export declare function loadMemoryInjectionContext(query: string): Promise<MemoryInjectionContext>;
11
12
  export declare function renderMemoryGuideSection(context: MemoryInjectionContext): string;
12
- export declare function buildMemoryPlanStep(): {
13
+ export declare function buildMemoryPlanStep(kind?: MemoryPlanKind): {
13
14
  id: string;
14
15
  tool: string;
15
16
  when: string;
@@ -20,6 +21,7 @@ export declare function buildMemoryPlanStep(): {
20
21
  summary: string;
21
22
  content: string;
22
23
  usage: string;
24
+ tags: string[];
23
25
  confidence: number;
24
26
  };
25
27
  outputs: never[];
@@ -31,6 +31,14 @@ export async function loadMemoryInjectionContext(query) {
31
31
  };
32
32
  }
33
33
  }
34
+ function formatMemoryResultLabel(item) {
35
+ const kind = item.type === 'bugfix' || item.tags.includes('bugfix')
36
+ ? '历史 Bug 修复'
37
+ : item.type === 'pattern' || item.type === 'component'
38
+ ? '可复用模式'
39
+ : '历史资产';
40
+ return `${item.name} [${item.type}] (${kind})`;
41
+ }
34
42
  export function renderMemoryGuideSection(context) {
35
43
  if (!context.enabled) {
36
44
  return '';
@@ -39,25 +47,83 @@ export function renderMemoryGuideSection(context) {
39
47
  return `\n\n## 🧠 记忆系统\n- 状态: 已配置但本次检索降级\n- 原因: ${context.error || '未知错误'}\n- 处理: 忽略记忆注入,继续主流程\n`;
40
48
  }
41
49
  if (context.results.length === 0) {
42
- return `\n\n## 🧠 记忆系统\n- 状态: 已启用\n- 检索结果: 未找到高相关历史资产\n- 处理: 继续主流程;若本次产出存在高价值通用资产,结束后调用 \`memorize_asset\` 沉淀\n`;
50
+ return `\n\n## 🧠 记忆系统\n- 状态: 已启用\n- 检索结果: 未找到高相关记录(含历史 Bug 修复与可复用模式)\n- 处理: 继续主流程;Bug 修复验证通过后必须 \`memorize_asset\` 沉淀;功能/UI 有可复用产出再沉淀\n`;
43
51
  }
44
52
  const items = context.results
45
- .map((item, index) => `${index + 1}. ${item.name} [${item.type}] score=${item.score.toFixed(3)}\n - 摘要: ${item.summary}\n - 读取: read_memory_asset {\"asset_id\": \"${item.id}\"}${item.sourcePath ? `\n - 来源: ${item.sourcePath}` : ''}`)
53
+ .map((item, index) => {
54
+ const label = formatMemoryResultLabel(item);
55
+ return `${index + 1}. ${label} score=${item.score.toFixed(3)}\n - 摘要: ${item.summary}\n - 读取: read_memory_asset {\"asset_id\": \"${item.id}\"}${item.sourcePath ? `\n - 来源: ${item.sourcePath}` : ''}`;
56
+ })
46
57
  .join('\n');
47
- return `\n\n## 🧠 记忆系统\n- 状态: 已启用\n- 指令: 优先复用以下历史成功经验;如果某条相关,再用 \`read_memory_asset\` 拉取完整内容,不要直接重写\n- 检索结果:\n${items}\n`;
58
+ return `\n\n## 🧠 记忆系统\n- 状态: 已启用\n- 指令: 开干前先复用下列记录(含历史 Bug 现象/根因/改法);相关条目用 \`read_memory_asset\` 读全文,避免重复踩坑\n- 检索结果:\n${items}\n`;
48
59
  }
49
- export function buildMemoryPlanStep() {
60
+ export function buildMemoryPlanStep(kind = 'default') {
61
+ if (kind === 'bugfix') {
62
+ return {
63
+ id: 'memorize-bugfix',
64
+ tool: 'memorize_asset',
65
+ when: 'Bug 已修复且验证通过后(必须沉淀,便于下次同类问题检索)',
66
+ args: {
67
+ name: '[问题简述,如 登录超时-Redis连接池]',
68
+ type: 'bugfix',
69
+ description: '[现象、报错信息、复现条件]',
70
+ summary: '[检索用:关键词 + 根因 + 修复要点,一句话]',
71
+ content: '【现象】...\n【根因】...\n【修复】具体改动文件与关键代码/配置\n【验证】如何确认已修好',
72
+ usage: '[再次遇到何种症状时可参考]',
73
+ tags: ['bugfix', 'root-cause'],
74
+ confidence: 0.85,
75
+ },
76
+ outputs: [],
77
+ };
78
+ }
79
+ if (kind === 'ui') {
80
+ return {
81
+ id: 'memorize-ui',
82
+ tool: 'memorize_asset',
83
+ when: 'UI 实现完成且存在可复用组件/布局/交互模式',
84
+ args: {
85
+ name: '[UI 资产名称]',
86
+ type: 'component',
87
+ description: '[该 UI 模式解决什么问题]',
88
+ summary: '[检索用摘要]',
89
+ content: '[组件结构、样式约定或可复用片段]',
90
+ usage: '[适用页面/场景]',
91
+ tags: ['ui', 'pattern'],
92
+ confidence: 0.75,
93
+ },
94
+ outputs: [],
95
+ };
96
+ }
97
+ if (kind === 'feature') {
98
+ return {
99
+ id: 'memorize-feature',
100
+ tool: 'memorize_asset',
101
+ when: '功能完成且存在可复用实现/规范',
102
+ args: {
103
+ name: '[功能/模式名称]',
104
+ type: 'pattern',
105
+ description: '[该资产解决什么问题]',
106
+ summary: '[检索用摘要]',
107
+ content: '[可复用代码或流程]',
108
+ usage: '[适用场景与限制]',
109
+ tags: ['feature', 'pattern'],
110
+ confidence: 0.75,
111
+ },
112
+ outputs: [],
113
+ };
114
+ }
50
115
  return {
51
116
  id: 'memorize',
52
117
  tool: 'memorize_asset',
53
118
  when: '本次实现完成且确认存在可复用资产',
54
119
  args: {
55
120
  name: '[资产名称]',
56
- type: 'code',
121
+ type: 'pattern',
57
122
  description: '[该资产解决了什么问题]',
58
123
  summary: '[用于后续检索的简洁摘要]',
59
124
  content: '[可复用代码或规范内容]',
60
125
  usage: '[适用场景与限制]',
126
+ tags: ['pattern'],
61
127
  confidence: 0.7,
62
128
  },
63
129
  outputs: [],
@@ -0,0 +1,6 @@
1
+ export type AgentsMdMergeMode = "created" | "prepended" | "replaced-and-moved-to-top" | "skipped-empty";
2
+ export declare function wrapMcpProbeBlock(innerMarkdown: string): string;
3
+ export declare function mergeAgentsMdBlock(existingContent: string | null | undefined, generatedInner: string): {
4
+ content: string;
5
+ mergeMode: AgentsMdMergeMode;
6
+ };
@@ -0,0 +1,51 @@
1
+ const BLOCK_BEGIN = "<!-- mcp-probe:context begin — auto-generated; re-run init_project_context updates this block only -->";
2
+ const BLOCK_END = "<!-- mcp-probe:context end -->";
3
+ export function wrapMcpProbeBlock(innerMarkdown) {
4
+ return `${BLOCK_BEGIN}\n${innerMarkdown.trim()}\n${BLOCK_END}`;
5
+ }
6
+ function stripExistingBlock(content) {
7
+ const beginIdx = content.indexOf(BLOCK_BEGIN);
8
+ if (beginIdx === -1) {
9
+ const legacyBegin = content.indexOf("<!-- mcp-probe:context begin");
10
+ if (legacyBegin === -1) {
11
+ return content.trim();
12
+ }
13
+ const legacyEnd = content.indexOf(BLOCK_END);
14
+ if (legacyEnd === -1) {
15
+ return content.trim();
16
+ }
17
+ const legacyLineEnd = content.indexOf("-->", legacyBegin);
18
+ const blockStart = legacyLineEnd === -1 ? legacyBegin : legacyLineEnd + 3;
19
+ const before = content.slice(0, legacyBegin).trimEnd();
20
+ const after = content.slice(legacyEnd + BLOCK_END.length).trimStart();
21
+ return [before, after].filter(Boolean).join("\n\n").trim();
22
+ }
23
+ const endIdx = content.indexOf(BLOCK_END);
24
+ if (endIdx === -1) {
25
+ return content.trim();
26
+ }
27
+ const before = content.slice(0, beginIdx).trimEnd();
28
+ const after = content.slice(endIdx + BLOCK_END.length).trimStart();
29
+ return [before, after].filter(Boolean).join("\n\n").trim();
30
+ }
31
+ export function mergeAgentsMdBlock(existingContent, generatedInner) {
32
+ const block = wrapMcpProbeBlock(generatedInner);
33
+ if (!existingContent?.trim()) {
34
+ return { content: `${block}\n`, mergeMode: "created" };
35
+ }
36
+ const userBody = stripExistingBlock(existingContent);
37
+ const hadBlock = existingContent.includes(BLOCK_BEGIN) || existingContent.includes("<!-- mcp-probe:context begin");
38
+ if (!userBody) {
39
+ return { content: `${block}\n`, mergeMode: hadBlock ? "replaced-and-moved-to-top" : "created" };
40
+ }
41
+ if (!hadBlock) {
42
+ return {
43
+ content: `${block}\n\n${userBody}\n`,
44
+ mergeMode: "prepended",
45
+ };
46
+ }
47
+ return {
48
+ content: `${block}\n\n${userBody}\n`,
49
+ mergeMode: "replaced-and-moved-to-top",
50
+ };
51
+ }