mcp-probe-kit 3.0.18 → 3.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -55
- package/build/index.js +3 -1
- package/build/lib/__tests__/agents-md-template.unit.test.d.ts +1 -0
- package/build/lib/__tests__/agents-md-template.unit.test.js +27 -0
- package/build/lib/__tests__/memory-config.unit.test.js +9 -0
- package/build/lib/__tests__/memory-injection.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-injection.unit.test.js +51 -0
- package/build/lib/__tests__/memory-orchestration.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-orchestration.unit.test.js +84 -0
- package/build/lib/__tests__/memory-payload.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-payload.unit.test.js +35 -0
- package/build/lib/__tests__/project-context-layout.unit.test.d.ts +1 -0
- package/build/lib/__tests__/project-context-layout.unit.test.js +80 -0
- package/build/lib/agents-md-template.d.ts +25 -0
- package/build/lib/agents-md-template.js +57 -0
- package/build/lib/memory-client.d.ts +8 -1
- package/build/lib/memory-client.js +53 -44
- package/build/lib/memory-config.d.ts +8 -0
- package/build/lib/memory-config.js +19 -0
- package/build/lib/memory-orchestration.d.ts +10 -3
- package/build/lib/memory-orchestration.js +146 -7
- package/build/lib/memory-payload.d.ts +21 -0
- package/build/lib/memory-payload.js +65 -0
- package/build/lib/merge-agents-md.d.ts +6 -0
- package/build/lib/merge-agents-md.js +51 -0
- package/build/lib/project-context-layout.d.ts +78 -0
- package/build/lib/project-context-layout.js +350 -0
- package/build/lib/workspace-root.js +6 -1
- package/build/resources/ui-ux-data/metadata.json +1 -1
- package/build/schemas/index.d.ts +62 -11
- package/build/schemas/memory-tools.d.ts +38 -9
- package/build/schemas/memory-tools.js +24 -9
- package/build/schemas/project-tools.d.ts +24 -2
- package/build/schemas/project-tools.js +24 -2
- package/build/tools/__tests__/code_insight.unit.test.js +3 -3
- package/build/tools/__tests__/init_project_context.unit.test.js +32 -21
- package/build/tools/__tests__/start_feature.unit.test.js +2 -1
- package/build/tools/code_insight.js +11 -9
- package/build/tools/index.d.ts +1 -0
- package/build/tools/index.js +1 -0
- package/build/tools/init_project_context.js +563 -506
- package/build/tools/memorize_asset.js +12 -0
- package/build/tools/scan_and_extract_patterns.js +7 -7
- package/build/tools/search_memory.d.ts +7 -0
- package/build/tools/search_memory.js +57 -0
- package/build/tools/start_bugfix.js +257 -251
- package/build/tools/start_feature.js +140 -134
- package/build/tools/start_ui.js +405 -405
- package/docs/.mcp-probe/layout.json +11 -0
- package/docs/data/tools.js +18 -0
- package/docs/i18n/all-tools/en.json +6 -1
- package/docs/i18n/all-tools/ja.json +2 -1
- package/docs/i18n/all-tools/ko.json +2 -1
- package/docs/i18n/all-tools/zh-CN.json +7 -2
- package/docs/i18n/en.json +38 -7
- package/docs/i18n/ja.json +9 -2
- package/docs/i18n/ko.json +9 -2
- package/docs/i18n/zh-CN.json +40 -9
- package/docs/memory-local-setup.md +314 -0
- package/docs/memory-local-setup.zh-CN.md +283 -0
- package/docs/pages/getting-started.html +252 -33
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
> **Talk is cheap, show me the Context.**
|
|
17
17
|
>
|
|
18
|
-
> mcp-probe-kit is a protocol-level toolkit designed for developers who want AI to truly understand their project's intent. It's not just a collection of
|
|
18
|
+
> mcp-probe-kit is a protocol-level toolkit designed for developers who want AI to truly understand their project's intent. It's not just a collection of 29 tools—it's a context-aware system that helps AI agents grasp what you're building.
|
|
19
19
|
|
|
20
20
|
**Languages**: [English](README.md) | [简体中文](i18n/README.zh-CN.md) | [日本語](i18n/README.ja-JP.md) | [한국어](i18n/README.ko-KR.md) | [Español](i18n/README.es-ES.md) | [Français](i18n/README.fr-FR.md) | [Deutsch](i18n/README.de-DE.md) | [Português (BR)](i18n/README.pt-BR.md)
|
|
21
21
|
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
> 🚀 AI-Powered Complete Development Toolkit - Covering the Entire Development Lifecycle
|
|
28
28
|
|
|
29
|
-
A powerful MCP (Model Context Protocol) server providing **
|
|
29
|
+
A powerful MCP (Model Context Protocol) server providing **29 tools** covering the complete workflow from product analysis to final release (Requirements → Design → Development → Quality → Release), all tools support **structured output**.
|
|
30
30
|
|
|
31
31
|
**🎉 v3.0 Major Update**: Streamlined tool count, focus on core competencies, eliminate choice paralysis, let AI do more native work
|
|
32
32
|
|
|
@@ -41,7 +41,8 @@ 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
|
-
- [
|
|
44
|
+
- [Local Memory Stack (Qdrant + Nomic Embed)](docs/memory-local-setup.md) - Docker Compose, ports `50008` / `50012`, MCP env
|
|
45
|
+
- [All Tools](https://mcp-probe-kit.bytezonex.com/pages/all-tools.html) - Complete list of 29 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
|
|
47
48
|
|
|
@@ -63,8 +64,8 @@ A powerful MCP (Model Context Protocol) server providing **28 tools** covering t
|
|
|
63
64
|
- `init_project`, `init_project_context`, `add_feature`, `estimate`, `interview`, `ask_user`
|
|
64
65
|
- **🎨 UI/UX Utilities** (3 tools) - Design systems and UI data synchronization
|
|
65
66
|
- `ui_design_system`, `ui_search`, `sync_ui_data`
|
|
66
|
-
- **🧠 Memory & Cursor History** (
|
|
67
|
-
- `read_memory_asset`, `memorize_asset`, `scan_and_extract_patterns`, `cursor_list_conversations`, `cursor_search_conversations`, `cursor_read_conversation`
|
|
67
|
+
- **🧠 Memory & Cursor History** (7 tools) - Reusable asset memory and local Cursor conversation retrieval
|
|
68
|
+
- `search_memory`, `read_memory_asset`, `memorize_asset`, `scan_and_extract_patterns`, `cursor_list_conversations`, `cursor_search_conversations`, `cursor_read_conversation`
|
|
68
69
|
|
|
69
70
|
### 🧠 Code Graph Bridge (GitNexus)
|
|
70
71
|
|
|
@@ -99,16 +100,19 @@ A powerful MCP (Model Context Protocol) server providing **28 tools** covering t
|
|
|
99
100
|
- Linux: `~/.config/Cursor/User/globalStorage/state.vscdb`
|
|
100
101
|
|
|
101
102
|
**Memory tools:**
|
|
103
|
+
- `search_memory` - Semantic search across the shared memory pool (optionally prefer `type` / `tags`)
|
|
102
104
|
- `memorize_asset` - Persist reusable code/spec/pattern assets into vector memory
|
|
103
105
|
- `read_memory_asset` - Read full asset content by `asset_id`
|
|
104
106
|
- `scan_and_extract_patterns` - Extract reusable patterns from code/file/directory before deciding whether to persist
|
|
105
107
|
|
|
108
|
+
**Cross-repo memory pools:** do not rely on `source_project` / `source_path` for shared retrieval; put file paths in `content` instead. Search injection hides foreign `sourcePath` unless `MEMORY_REPO_ID` matches or `MEMORY_SEARCH_SHOW_SOURCE=true`.
|
|
109
|
+
|
|
106
110
|
**Memory backend and embedding configuration:**
|
|
107
111
|
- Vector database: **Qdrant**
|
|
108
|
-
- Recommended local setup
|
|
112
|
+
- **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
113
|
- Supported embedding providers:
|
|
110
114
|
- `ollama`
|
|
111
|
-
- `openai-compatible`
|
|
115
|
+
- `openai-compatible` (Infinity, OpenAI, etc.)
|
|
112
116
|
- Required environment variables for memory write/search:
|
|
113
117
|
- `MEMORY_QDRANT_URL`
|
|
114
118
|
- `MEMORY_EMBEDDING_URL`
|
|
@@ -120,16 +124,18 @@ A powerful MCP (Model Context Protocol) server providing **28 tools** covering t
|
|
|
120
124
|
- `MEMORY_EMBEDDING_PROVIDER` (`ollama` by default)
|
|
121
125
|
- `MEMORY_SEARCH_LIMIT` (default: `3`)
|
|
122
126
|
- `MEMORY_SUMMARY_MAX_CHARS` (default: `280`)
|
|
127
|
+
- `MEMORY_SEARCH_MIN_SCORE` (default: `0` = disabled; try `0.72` for noisy pools)
|
|
128
|
+
- `MEMORY_SEARCH_SHOW_SOURCE` (default: `false`)
|
|
129
|
+
- `MEMORY_REPO_ID` (optional; show `sourcePath` only when `sourceProject` matches)
|
|
130
|
+
- `MEMORY_INJECTION_CONTENT_MAX_CHARS` (default: `1500`; max content per hit injected into `start_*` guides)
|
|
123
131
|
- Behavior notes:
|
|
124
132
|
- Read-only memory access only requires `MEMORY_QDRANT_URL`
|
|
125
133
|
- Memory write is enabled only when `MEMORY_QDRANT_URL`, `MEMORY_EMBEDDING_URL`, and `MEMORY_EMBEDDING_MODEL` are all configured
|
|
126
134
|
- The Qdrant collection is auto-created on first write, and vector dimension is inferred from the first embedding response
|
|
127
135
|
|
|
128
|
-
**Recommended local memory setup (Qdrant +
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
ollama pull nomic-embed-text
|
|
132
|
-
```
|
|
136
|
+
**Recommended local memory setup (Qdrant + Nomic Embed / Infinity):**
|
|
137
|
+
|
|
138
|
+
Full Docker Compose, ports, and troubleshooting: **[docs/memory-local-setup.md](docs/memory-local-setup.md)**
|
|
133
139
|
|
|
134
140
|
```json
|
|
135
141
|
{
|
|
@@ -138,11 +144,13 @@ ollama pull nomic-embed-text
|
|
|
138
144
|
"command": "npx",
|
|
139
145
|
"args": ["-y", "mcp-probe-kit@latest"],
|
|
140
146
|
"env": {
|
|
141
|
-
"MEMORY_QDRANT_URL": "http://127.0.0.1:
|
|
147
|
+
"MEMORY_QDRANT_URL": "http://127.0.0.1:50008",
|
|
148
|
+
"MEMORY_QDRANT_API_KEY": "your-qdrant-api-key",
|
|
142
149
|
"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",
|
|
150
|
+
"MEMORY_EMBEDDING_PROVIDER": "openai-compatible",
|
|
151
|
+
"MEMORY_EMBEDDING_URL": "http://127.0.0.1:50012/embeddings",
|
|
152
|
+
"MEMORY_EMBEDDING_MODEL": "nomic-ai/nomic-embed-text-v1.5",
|
|
153
|
+
"MEMORY_EMBEDDING_API_KEY": "your-infinity-api-key",
|
|
146
154
|
"MEMORY_SEARCH_LIMIT": "3",
|
|
147
155
|
"MEMORY_SUMMARY_MAX_CHARS": "280"
|
|
148
156
|
}
|
|
@@ -151,7 +159,21 @@ ollama pull nomic-embed-text
|
|
|
151
159
|
}
|
|
152
160
|
```
|
|
153
161
|
|
|
154
|
-
**
|
|
162
|
+
**Alternative: Qdrant + Ollama** (if you already run Ollama):
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
docker run -d --name mcp-qdrant -p 6333:6333 qdrant/qdrant
|
|
166
|
+
ollama pull nomic-embed-text
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
"MEMORY_QDRANT_URL": "http://127.0.0.1:6333",
|
|
171
|
+
"MEMORY_EMBEDDING_PROVIDER": "ollama",
|
|
172
|
+
"MEMORY_EMBEDDING_URL": "http://127.0.0.1:11434/api/embeddings",
|
|
173
|
+
"MEMORY_EMBEDDING_MODEL": "nomic-embed-text"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**OpenAI-compatible embedding (hosted API):**
|
|
155
177
|
```json
|
|
156
178
|
{
|
|
157
179
|
"mcpServers": {
|
|
@@ -416,6 +438,27 @@ No installation needed, use the latest version directly.
|
|
|
416
438
|
}
|
|
417
439
|
```
|
|
418
440
|
|
|
441
|
+
#### OpenCode Configuration
|
|
442
|
+
|
|
443
|
+
**Config file location:**
|
|
444
|
+
- Project-level: `opencode.json` (in project root)
|
|
445
|
+
- Global: `~/.config/opencode/opencode.json`
|
|
446
|
+
|
|
447
|
+
**Config content:**
|
|
448
|
+
```json
|
|
449
|
+
{
|
|
450
|
+
"mcp": {
|
|
451
|
+
"mcp-probe-kit": {
|
|
452
|
+
"type": "local",
|
|
453
|
+
"command": ["npx", "-y", "mcp-probe-kit@latest"],
|
|
454
|
+
"enabled": true
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
> **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.
|
|
461
|
+
|
|
419
462
|
### Method 2: Global Installation
|
|
420
463
|
|
|
421
464
|
```bash
|
|
@@ -440,21 +483,14 @@ If you want to use `memorize_asset`, `read_memory_asset`, and `scan_and_extract_
|
|
|
440
483
|
1. A **Qdrant** vector database
|
|
441
484
|
2. An **embedding service** in either `ollama` or `openai-compatible` mode
|
|
442
485
|
|
|
443
|
-
|
|
486
|
+
**Full guide (Docker Compose for Qdrant + Infinity, ports `50008` / `50012`, MCP env, smoke tests):**
|
|
444
487
|
|
|
445
|
-
|
|
488
|
+
- English: [docs/memory-local-setup.md](docs/memory-local-setup.md)
|
|
489
|
+
- 中文: [docs/memory-local-setup.zh-CN.md](docs/memory-local-setup.zh-CN.md)
|
|
446
490
|
|
|
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
|
-
```
|
|
491
|
+
#### Option A: Qdrant + Nomic Embed / Infinity (recommended)
|
|
456
492
|
|
|
457
|
-
|
|
493
|
+
Lightweight local stack; no Ollama. Deploy Qdrant and `nomic-embed` via Docker Compose (see guide), then:
|
|
458
494
|
|
|
459
495
|
```json
|
|
460
496
|
{
|
|
@@ -463,11 +499,13 @@ Recommended MCP config env:
|
|
|
463
499
|
"command": "npx",
|
|
464
500
|
"args": ["-y", "mcp-probe-kit@latest"],
|
|
465
501
|
"env": {
|
|
466
|
-
"MEMORY_QDRANT_URL": "http://127.0.0.1:
|
|
502
|
+
"MEMORY_QDRANT_URL": "http://127.0.0.1:50008",
|
|
503
|
+
"MEMORY_QDRANT_API_KEY": "your-qdrant-api-key",
|
|
467
504
|
"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",
|
|
505
|
+
"MEMORY_EMBEDDING_PROVIDER": "openai-compatible",
|
|
506
|
+
"MEMORY_EMBEDDING_URL": "http://127.0.0.1:50012/embeddings",
|
|
507
|
+
"MEMORY_EMBEDDING_MODEL": "nomic-ai/nomic-embed-text-v1.5",
|
|
508
|
+
"MEMORY_EMBEDDING_API_KEY": "your-infinity-api-key",
|
|
471
509
|
"MEMORY_SEARCH_LIMIT": "3",
|
|
472
510
|
"MEMORY_SUMMARY_MAX_CHARS": "280"
|
|
473
511
|
}
|
|
@@ -476,36 +514,30 @@ Recommended MCP config env:
|
|
|
476
514
|
}
|
|
477
515
|
```
|
|
478
516
|
|
|
479
|
-
|
|
517
|
+
> Embedding URL must be `/embeddings` (not `/v1/embeddings`). Qdrant requires `api-key` when `QDRANT__SERVICE__API_KEY` is set.
|
|
480
518
|
|
|
481
|
-
|
|
519
|
+
#### Option B: Qdrant + Ollama
|
|
482
520
|
|
|
483
521
|
```bash
|
|
484
522
|
docker run -d --name mcp-qdrant -p 6333:6333 qdrant/qdrant
|
|
523
|
+
ollama pull nomic-embed-text
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
```json
|
|
527
|
+
"MEMORY_QDRANT_URL": "http://127.0.0.1:6333",
|
|
528
|
+
"MEMORY_EMBEDDING_PROVIDER": "ollama",
|
|
529
|
+
"MEMORY_EMBEDDING_URL": "http://127.0.0.1:11434/api/embeddings",
|
|
530
|
+
"MEMORY_EMBEDDING_MODEL": "nomic-embed-text"
|
|
485
531
|
```
|
|
486
532
|
|
|
487
|
-
|
|
533
|
+
#### Option C: Qdrant + hosted OpenAI-compatible API
|
|
488
534
|
|
|
489
535
|
```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
|
-
}
|
|
536
|
+
"MEMORY_QDRANT_URL": "http://127.0.0.1:50008",
|
|
537
|
+
"MEMORY_EMBEDDING_PROVIDER": "openai-compatible",
|
|
538
|
+
"MEMORY_EMBEDDING_URL": "https://your-embedding-endpoint/v1/embeddings",
|
|
539
|
+
"MEMORY_EMBEDDING_API_KEY": "your-api-key",
|
|
540
|
+
"MEMORY_EMBEDDING_MODEL": "text-embedding-3-small"
|
|
509
541
|
```
|
|
510
542
|
|
|
511
543
|
#### Memory Environment Variables
|
package/build/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { InMemoryTaskMessageQueue, InMemoryTaskStore, } from "@modelcontextproto
|
|
|
5
5
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ProgressNotificationSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
7
|
import * as path from "node:path";
|
|
8
|
-
import { initProject, gencommit, codeReview, codeInsight, gentest, refactor, initProjectContext, addFeature, fixBug, estimate, startFeature, startBugfix, startOnboard, startRalph, interview, askUser, uiDesignSystem, uiSearch, syncUiData, startUi, startProduct, gitWorkReport, readMemoryAsset, memorizeAsset, scanAndExtractPatterns, cursorListConversations, cursorSearchConversations, cursorReadConversation } from "./tools/index.js";
|
|
8
|
+
import { initProject, gencommit, codeReview, codeInsight, gentest, refactor, initProjectContext, addFeature, fixBug, estimate, startFeature, startBugfix, startOnboard, startRalph, interview, askUser, uiDesignSystem, uiSearch, syncUiData, startUi, startProduct, gitWorkReport, searchMemory, readMemoryAsset, memorizeAsset, scanAndExtractPatterns, cursorListConversations, cursorSearchConversations, cursorReadConversation } from "./tools/index.js";
|
|
9
9
|
import { VERSION, NAME } from "./version.js";
|
|
10
10
|
import { allToolSchemas } from "./schemas/index.js";
|
|
11
11
|
import { filterTools, getToolsetFromEnv } from "./lib/toolset-manager.js";
|
|
@@ -459,6 +459,8 @@ async function executeTool(name, args, context) {
|
|
|
459
459
|
return await startProduct((args ?? {}), context);
|
|
460
460
|
case "git_work_report":
|
|
461
461
|
return await gitWorkReport(args);
|
|
462
|
+
case "search_memory":
|
|
463
|
+
return await searchMemory(args);
|
|
462
464
|
case "read_memory_asset":
|
|
463
465
|
return await readMemoryAsset(args);
|
|
464
466
|
case "memorize_asset":
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
expect(md).toContain('search_memory');
|
|
25
|
+
expect(md).toContain('自动注入');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -30,4 +30,13 @@ describe('memory-config 单元测试', () => {
|
|
|
30
30
|
expect(isMemoryReadEnabled(config)).toBe(true);
|
|
31
31
|
expect(isMemoryEnabled(config)).toBe(true);
|
|
32
32
|
});
|
|
33
|
+
test('记忆检索展示与阈值环境变量', () => {
|
|
34
|
+
vi.stubEnv('MEMORY_SEARCH_SHOW_SOURCE', 'true');
|
|
35
|
+
vi.stubEnv('MEMORY_SEARCH_MIN_SCORE', '0.72');
|
|
36
|
+
vi.stubEnv('MEMORY_REPO_ID', 'my-org/my-repo');
|
|
37
|
+
const config = getMemoryConfig();
|
|
38
|
+
expect(config.searchShowSource).toBe(true);
|
|
39
|
+
expect(config.searchMinScore).toBe(0.72);
|
|
40
|
+
expect(config.repoId).toBe('my-org/my-repo');
|
|
41
|
+
});
|
|
33
42
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { renderMemoryGuideSection, truncateInjectionText, } from '../memory-orchestration.js';
|
|
3
|
+
afterEach(() => {
|
|
4
|
+
vi.unstubAllEnvs();
|
|
5
|
+
});
|
|
6
|
+
describe('memory injection auto-load', () => {
|
|
7
|
+
test('renders full asset content without asking for read_memory_asset', () => {
|
|
8
|
+
vi.stubEnv('MEMORY_SEARCH_SHOW_SOURCE', '');
|
|
9
|
+
vi.stubEnv('MEMORY_REPO_ID', '');
|
|
10
|
+
const section = renderMemoryGuideSection({
|
|
11
|
+
enabled: true,
|
|
12
|
+
available: true,
|
|
13
|
+
degraded: false,
|
|
14
|
+
query: 'proxy 400',
|
|
15
|
+
results: [
|
|
16
|
+
{
|
|
17
|
+
id: 'asset-1',
|
|
18
|
+
score: 0.88,
|
|
19
|
+
name: 'feishu-proxy-bug',
|
|
20
|
+
type: 'bugfix',
|
|
21
|
+
description: 'Feishu proxy mismatch',
|
|
22
|
+
summary: 'proxy caused 400 on HTTPS',
|
|
23
|
+
tags: ['bugfix', 'proxy'],
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
assetsById: {
|
|
27
|
+
'asset-1': {
|
|
28
|
+
id: 'asset-1',
|
|
29
|
+
name: 'feishu-proxy-bug',
|
|
30
|
+
type: 'bugfix',
|
|
31
|
+
description: 'Feishu proxy mismatch',
|
|
32
|
+
summary: 'proxy caused 400 on HTTPS',
|
|
33
|
+
content: '【现象】submit 成功但 sync_failed\n【根因】HTTP_PROXY 污染\n【修复】proxy:false',
|
|
34
|
+
tags: ['bugfix'],
|
|
35
|
+
confidence: 0.9,
|
|
36
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
37
|
+
updatedAt: '2026-01-01T00:00:00.000Z',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
expect(section).toContain('已自动加载的历史经验全文');
|
|
42
|
+
expect(section).toContain('【现象】submit 成功但 sync_failed');
|
|
43
|
+
expect(section).toContain('【根因】HTTP_PROXY 污染');
|
|
44
|
+
expect(section).toContain('无需再调 `read_memory_asset`');
|
|
45
|
+
expect(section).not.toMatch(/读取: read_memory_asset/);
|
|
46
|
+
});
|
|
47
|
+
test('truncateInjectionText limits long content', () => {
|
|
48
|
+
expect(truncateInjectionText('abcdef', 4)).toBe('a...');
|
|
49
|
+
expect(truncateInjectionText('abc', 10)).toBe('abc');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { renderMemoryGuideSection, shouldShowSourceInSearch, } from '../memory-orchestration.js';
|
|
3
|
+
afterEach(() => {
|
|
4
|
+
vi.unstubAllEnvs();
|
|
5
|
+
});
|
|
6
|
+
describe('memory-orchestration', () => {
|
|
7
|
+
test('does not show source path by default', () => {
|
|
8
|
+
vi.stubEnv('MEMORY_SEARCH_SHOW_SOURCE', '');
|
|
9
|
+
vi.stubEnv('MEMORY_REPO_ID', '');
|
|
10
|
+
const section = renderMemoryGuideSection({
|
|
11
|
+
enabled: true,
|
|
12
|
+
available: true,
|
|
13
|
+
degraded: false,
|
|
14
|
+
query: '404 submit',
|
|
15
|
+
results: [
|
|
16
|
+
{
|
|
17
|
+
id: '1',
|
|
18
|
+
score: 0.91,
|
|
19
|
+
name: 'purchase-create-submit-404',
|
|
20
|
+
type: 'bugfix',
|
|
21
|
+
description: 'desc',
|
|
22
|
+
summary: 'summary',
|
|
23
|
+
tags: ['bugfix'],
|
|
24
|
+
sourceProject: 'zhixing/gongyingshang',
|
|
25
|
+
sourcePath: 'admin-api/app.js',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
assetsById: {},
|
|
29
|
+
});
|
|
30
|
+
expect(section).toContain('purchase-create-submit-404');
|
|
31
|
+
expect(section).not.toContain('admin-api/app.js');
|
|
32
|
+
expect(section).not.toContain('来源:');
|
|
33
|
+
});
|
|
34
|
+
test('shows source path when MEMORY_REPO_ID matches', () => {
|
|
35
|
+
vi.stubEnv('MEMORY_SEARCH_SHOW_SOURCE', '');
|
|
36
|
+
vi.stubEnv('MEMORY_REPO_ID', 'zhixing/gongyingshang');
|
|
37
|
+
expect(shouldShowSourceInSearch({
|
|
38
|
+
id: '1',
|
|
39
|
+
score: 0.9,
|
|
40
|
+
name: 'x',
|
|
41
|
+
type: 'bugfix',
|
|
42
|
+
description: '',
|
|
43
|
+
summary: '',
|
|
44
|
+
tags: [],
|
|
45
|
+
sourceProject: 'zhixing/gongyingshang',
|
|
46
|
+
sourcePath: 'admin-api/app.js',
|
|
47
|
+
})).toBe(true);
|
|
48
|
+
expect(shouldShowSourceInSearch({
|
|
49
|
+
id: '2',
|
|
50
|
+
score: 0.9,
|
|
51
|
+
name: 'y',
|
|
52
|
+
type: 'bugfix',
|
|
53
|
+
description: '',
|
|
54
|
+
summary: '',
|
|
55
|
+
tags: [],
|
|
56
|
+
sourceProject: 'other/repo',
|
|
57
|
+
sourcePath: 'src/index.ts',
|
|
58
|
+
})).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
test('MEMORY_SEARCH_SHOW_SOURCE=true always shows source', () => {
|
|
61
|
+
vi.stubEnv('MEMORY_SEARCH_SHOW_SOURCE', 'true');
|
|
62
|
+
vi.stubEnv('MEMORY_REPO_ID', '');
|
|
63
|
+
const section = renderMemoryGuideSection({
|
|
64
|
+
enabled: true,
|
|
65
|
+
available: true,
|
|
66
|
+
degraded: false,
|
|
67
|
+
query: 'q',
|
|
68
|
+
results: [
|
|
69
|
+
{
|
|
70
|
+
id: '1',
|
|
71
|
+
score: 0.8,
|
|
72
|
+
name: 'n',
|
|
73
|
+
type: 'bugfix',
|
|
74
|
+
description: '',
|
|
75
|
+
summary: 's',
|
|
76
|
+
tags: [],
|
|
77
|
+
sourcePath: 'admin-api/app.js',
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
assetsById: {},
|
|
81
|
+
});
|
|
82
|
+
expect(section).toContain('来源: admin-api/app.js');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { normalizeMemoryPayload, payloadToMemoryFields } from '../memory-payload.js';
|
|
3
|
+
describe('memory-payload', () => {
|
|
4
|
+
test('maps legacy kind/title/source fields to current schema', () => {
|
|
5
|
+
const normalized = normalizeMemoryPayload({
|
|
6
|
+
kind: 'extracted_pattern',
|
|
7
|
+
title: 'Feishu submit success but sync_failed',
|
|
8
|
+
source: 'scan_and_extract_patterns-fallback',
|
|
9
|
+
content: 'Pattern: proxy mismatch',
|
|
10
|
+
tags: ['feishu', 'pattern'],
|
|
11
|
+
created_at: '2026-05-25T11:20:06.705Z',
|
|
12
|
+
});
|
|
13
|
+
expect(normalized.name).toBe('Feishu submit success but sync_failed');
|
|
14
|
+
expect(normalized.type).toBe('pattern');
|
|
15
|
+
expect(normalized.summary).toContain('Pattern');
|
|
16
|
+
expect(normalized.createdAt).toBe('2026-05-25T11:20:06.705Z');
|
|
17
|
+
});
|
|
18
|
+
test('payloadToMemoryFields preserves standard asset fields', () => {
|
|
19
|
+
const fields = payloadToMemoryFields({
|
|
20
|
+
id: 'asset-1',
|
|
21
|
+
name: 'purchase-create-submit-404',
|
|
22
|
+
type: 'bugfix',
|
|
23
|
+
description: '送审 404',
|
|
24
|
+
summary: 'res.data.purchase.id',
|
|
25
|
+
content: '【现象】404',
|
|
26
|
+
tags: ['bugfix'],
|
|
27
|
+
confidence: 0.95,
|
|
28
|
+
createdAt: '2026-05-27T04:28:04.684Z',
|
|
29
|
+
updatedAt: '2026-05-27T04:28:04.684Z',
|
|
30
|
+
});
|
|
31
|
+
expect(fields.name).toBe('purchase-create-submit-404');
|
|
32
|
+
expect(fields.type).toBe('bugfix');
|
|
33
|
+
expect(fields.tags).toEqual(['bugfix']);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -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;
|