mcp-probe-kit 3.0.18 → 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 +75 -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
@@ -41,6 +41,7 @@ A powerful MCP (Model Context Protocol) server providing **28 tools** covering t
41
41
  **👉 [https://mcp-probe-kit.bytezonex.com](https://mcp-probe-kit.bytezonex.com/)**
42
42
 
43
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
44
45
  - [All Tools](https://mcp-probe-kit.bytezonex.com/pages/all-tools.html) - Complete list of 28 tools
45
46
  - [Best Practices](https://mcp-probe-kit.bytezonex.com/pages/examples.html) - Full development workflow guide
46
47
  - [v3.0 Migration Guide](https://mcp-probe-kit.bytezonex.com/pages/migration.html) - Upgrade from v2.x to v3.0
@@ -105,10 +106,10 @@ A powerful MCP (Model Context Protocol) server providing **28 tools** covering t
105
106
 
106
107
  **Memory backend and embedding configuration:**
107
108
  - Vector database: **Qdrant**
108
- - 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))
109
110
  - Supported embedding providers:
110
111
  - `ollama`
111
- - `openai-compatible`
112
+ - `openai-compatible` (Infinity, OpenAI, etc.)
112
113
  - Required environment variables for memory write/search:
113
114
  - `MEMORY_QDRANT_URL`
114
115
  - `MEMORY_EMBEDDING_URL`
@@ -125,11 +126,9 @@ A powerful MCP (Model Context Protocol) server providing **28 tools** covering t
125
126
  - Memory write is enabled only when `MEMORY_QDRANT_URL`, `MEMORY_EMBEDDING_URL`, and `MEMORY_EMBEDDING_MODEL` are all configured
126
127
  - The Qdrant collection is auto-created on first write, and vector dimension is inferred from the first embedding response
127
128
 
128
- **Recommended local memory setup (Qdrant + Ollama):**
129
- ```bash
130
- docker run -d --name mcp-qdrant -p 6333:6333 qdrant/qdrant
131
- ollama pull nomic-embed-text
132
- ```
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)**
133
132
 
134
133
  ```json
135
134
  {
@@ -138,11 +137,13 @@ ollama pull nomic-embed-text
138
137
  "command": "npx",
139
138
  "args": ["-y", "mcp-probe-kit@latest"],
140
139
  "env": {
141
- "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",
142
142
  "MEMORY_QDRANT_COLLECTION": "mcp_probe_memory",
143
- "MEMORY_EMBEDDING_PROVIDER": "ollama",
144
- "MEMORY_EMBEDDING_URL": "http://127.0.0.1:11434/api/embeddings",
145
- "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",
146
147
  "MEMORY_SEARCH_LIMIT": "3",
147
148
  "MEMORY_SUMMARY_MAX_CHARS": "280"
148
149
  }
@@ -151,7 +152,21 @@ ollama pull nomic-embed-text
151
152
  }
152
153
  ```
153
154
 
154
- **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):**
155
170
  ```json
156
171
  {
157
172
  "mcpServers": {
@@ -416,6 +431,27 @@ No installation needed, use the latest version directly.
416
431
  }
417
432
  ```
418
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
+
419
455
  ### Method 2: Global Installation
420
456
 
421
457
  ```bash
@@ -440,21 +476,14 @@ If you want to use `memorize_asset`, `read_memory_asset`, and `scan_and_extract_
440
476
  1. A **Qdrant** vector database
441
477
  2. An **embedding service** in either `ollama` or `openai-compatible` mode
442
478
 
443
- #### Option A: Qdrant + Ollama
479
+ **Full guide (Docker Compose for Qdrant + Infinity, ports `50008` / `50012`, MCP env, smoke tests):**
444
480
 
445
- 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)
446
483
 
447
- ```bash
448
- docker run -d --name mcp-qdrant -p 6333:6333 qdrant/qdrant
449
- ```
450
-
451
- Start Ollama and pull the default embedding model:
452
-
453
- ```bash
454
- ollama pull nomic-embed-text
455
- ```
484
+ #### Option A: Qdrant + Nomic Embed / Infinity (recommended)
456
485
 
457
- Recommended MCP config env:
486
+ Lightweight local stack; no Ollama. Deploy Qdrant and `nomic-embed` via Docker Compose (see guide), then:
458
487
 
459
488
  ```json
460
489
  {
@@ -463,11 +492,13 @@ Recommended MCP config env:
463
492
  "command": "npx",
464
493
  "args": ["-y", "mcp-probe-kit@latest"],
465
494
  "env": {
466
- "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",
467
497
  "MEMORY_QDRANT_COLLECTION": "mcp_probe_memory",
468
- "MEMORY_EMBEDDING_PROVIDER": "ollama",
469
- "MEMORY_EMBEDDING_URL": "http://127.0.0.1:11434/api/embeddings",
470
- "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",
471
502
  "MEMORY_SEARCH_LIMIT": "3",
472
503
  "MEMORY_SUMMARY_MAX_CHARS": "280"
473
504
  }
@@ -476,36 +507,30 @@ Recommended MCP config env:
476
507
  }
477
508
  ```
478
509
 
479
- #### 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.
480
511
 
481
- Start Qdrant with Docker:
512
+ #### Option B: Qdrant + Ollama
482
513
 
483
514
  ```bash
484
515
  docker run -d --name mcp-qdrant -p 6333:6333 qdrant/qdrant
516
+ ollama pull nomic-embed-text
485
517
  ```
486
518
 
487
- Then point the embedding config to an OpenAI-compatible `/embeddings` endpoint:
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"
524
+ ```
525
+
526
+ #### Option C: Qdrant + hosted OpenAI-compatible API
488
527
 
489
528
  ```json
490
- {
491
- "mcpServers": {
492
- "mcp-probe-kit": {
493
- "command": "npx",
494
- "args": ["-y", "mcp-probe-kit@latest"],
495
- "env": {
496
- "MEMORY_QDRANT_URL": "http://127.0.0.1:6333",
497
- "MEMORY_QDRANT_COLLECTION": "mcp_probe_memory",
498
- "MEMORY_QDRANT_API_KEY": "",
499
- "MEMORY_EMBEDDING_PROVIDER": "openai-compatible",
500
- "MEMORY_EMBEDDING_URL": "https://your-embedding-endpoint/v1/embeddings",
501
- "MEMORY_EMBEDDING_API_KEY": "your-api-key",
502
- "MEMORY_EMBEDDING_MODEL": "text-embedding-3-small",
503
- "MEMORY_SEARCH_LIMIT": "3",
504
- "MEMORY_SUMMARY_MAX_CHARS": "280"
505
- }
506
- }
507
- }
508
- }
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"
509
534
  ```
510
535
 
511
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
+ }