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.
- package/README.md +75 -50
- package/build/lib/__tests__/agents-md-template.unit.test.d.ts +1 -0
- package/build/lib/__tests__/agents-md-template.unit.test.js +25 -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 +55 -0
- package/build/lib/memory-orchestration.d.ts +3 -1
- package/build/lib/memory-orchestration.js +71 -5
- 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 +25 -3
- package/build/schemas/memory-tools.d.ts +1 -1
- package/build/schemas/memory-tools.js +1 -1
- 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/init_project_context.js +563 -506
- package/build/tools/start_bugfix.js +254 -248
- package/build/tools/start_feature.js +137 -131
- package/build/tools/start_ui.js +402 -402
- package/docs/.mcp-probe/layout.json +11 -0
- package/docs/i18n/en.json +36 -5
- package/docs/i18n/ja.json +9 -2
- package/docs/i18n/ko.json +9 -2
- package/docs/i18n/zh-CN.json +36 -5
- package/docs/memory-local-setup.md +314 -0
- package/docs/memory-local-setup.zh-CN.md +280 -0
- package/docs/pages/getting-started.html +249 -31
- 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
|
|
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 +
|
|
129
|
-
|
|
130
|
-
|
|
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:
|
|
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": "
|
|
144
|
-
"MEMORY_EMBEDDING_URL": "http://127.0.0.1:
|
|
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
|
-
**
|
|
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
|
-
|
|
479
|
+
**Full guide (Docker Compose for Qdrant + Infinity, ports `50008` / `50012`, MCP env, smoke tests):**
|
|
444
480
|
|
|
445
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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": "
|
|
469
|
-
"MEMORY_EMBEDDING_URL": "http://127.0.0.1:
|
|
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
|
-
|
|
510
|
+
> Embedding URL must be `/embeddings` (not `/v1/embeddings`). Qdrant requires `api-key` when `QDRANT__SERVICE__API_KEY` is set.
|
|
480
511
|
|
|
481
|
-
|
|
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
|
-
|
|
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
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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- 检索结果:
|
|
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) =>
|
|
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- 指令:
|
|
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: '
|
|
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
|
+
}
|