@hsingjui/contextweaver 0.0.1
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/LICENSE +21 -0
- package/README.md +380 -0
- package/dist/SearchService-YLOUJF4S.js +1496 -0
- package/dist/chunk-34YZ2U3O.js +1177 -0
- package/dist/chunk-5SRSUMKW.js +612 -0
- package/dist/chunk-5TV4JNTE.js +258 -0
- package/dist/chunk-6C2D5Y4R.js +798 -0
- package/dist/chunk-PN7DP6XL.js +158 -0
- package/dist/codebaseRetrieval-RDCNIUDM.js +10 -0
- package/dist/config-IEL3M4V5.js +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +130 -0
- package/dist/scanner-66CLKCSZ.js +9 -0
- package/dist/server-2SAFEAEY.js +131 -0
- package/package.json +59 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
var isDev = process.env.NODE_ENV !== "dev";
|
|
7
|
+
function loadEnv() {
|
|
8
|
+
const candidates = isDev ? [
|
|
9
|
+
path.join(process.cwd(), ".env"),
|
|
10
|
+
// 1. 当前目录(开发用)
|
|
11
|
+
path.join(os.homedir(), ".contextweaver", ".env")
|
|
12
|
+
// 2. 用户配置目录(回退)
|
|
13
|
+
] : [
|
|
14
|
+
path.join(os.homedir(), ".contextweaver", ".env")
|
|
15
|
+
// 生产环境只用用户配置
|
|
16
|
+
];
|
|
17
|
+
const envPath = candidates.find((p) => fs.existsSync(p));
|
|
18
|
+
if (envPath) {
|
|
19
|
+
const result = dotenv.config({ path: envPath, quiet: true });
|
|
20
|
+
if (result.error) {
|
|
21
|
+
console.error(`[config] \u52A0\u8F7D\u73AF\u5883\u53D8\u91CF\u5931\u8D25: ${result.error.message}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
loadEnv();
|
|
27
|
+
function checkEmbeddingEnv() {
|
|
28
|
+
const missingVars = [];
|
|
29
|
+
if (!process.env.EMBEDDINGS_API_KEY) {
|
|
30
|
+
missingVars.push("EMBEDDINGS_API_KEY");
|
|
31
|
+
}
|
|
32
|
+
if (!process.env.EMBEDDINGS_BASE_URL) {
|
|
33
|
+
missingVars.push("EMBEDDINGS_BASE_URL");
|
|
34
|
+
}
|
|
35
|
+
if (!process.env.EMBEDDINGS_MODEL) {
|
|
36
|
+
missingVars.push("EMBEDDINGS_MODEL");
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
isValid: missingVars.length === 0,
|
|
40
|
+
missingVars
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function checkRerankerEnv() {
|
|
44
|
+
const missingVars = [];
|
|
45
|
+
if (!process.env.RERANK_API_KEY) {
|
|
46
|
+
missingVars.push("RERANK_API_KEY");
|
|
47
|
+
}
|
|
48
|
+
if (!process.env.RERANK_BASE_URL) {
|
|
49
|
+
missingVars.push("RERANK_BASE_URL");
|
|
50
|
+
}
|
|
51
|
+
if (!process.env.RERANK_MODEL) {
|
|
52
|
+
missingVars.push("RERANK_MODEL");
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
isValid: missingVars.length === 0,
|
|
56
|
+
missingVars
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function getEmbeddingConfig() {
|
|
60
|
+
const apiKey = process.env.EMBEDDINGS_API_KEY;
|
|
61
|
+
const baseUrl = process.env.EMBEDDINGS_BASE_URL;
|
|
62
|
+
const model = process.env.EMBEDDINGS_MODEL;
|
|
63
|
+
const maxConcurrency = parseInt(process.env.EMBEDDINGS_MAX_CONCURRENCY || "10", 10);
|
|
64
|
+
if (!apiKey) {
|
|
65
|
+
throw new Error("EMBEDDINGS_API_KEY \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
66
|
+
}
|
|
67
|
+
if (!baseUrl) {
|
|
68
|
+
throw new Error("EMBEDDINGS_BASE_URL \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
69
|
+
}
|
|
70
|
+
if (!model) {
|
|
71
|
+
throw new Error("EMBEDDINGS_MODEL \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
72
|
+
}
|
|
73
|
+
const dimensions = parseInt(process.env.EMBEDDINGS_DIMENSIONS || "1024", 10);
|
|
74
|
+
return {
|
|
75
|
+
apiKey,
|
|
76
|
+
baseUrl,
|
|
77
|
+
model,
|
|
78
|
+
maxConcurrency: isNaN(maxConcurrency) ? 4 : maxConcurrency,
|
|
79
|
+
dimensions: isNaN(dimensions) ? 1024 : dimensions
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function getRerankerConfig() {
|
|
83
|
+
const apiKey = process.env.RERANK_API_KEY;
|
|
84
|
+
const baseUrl = process.env.RERANK_BASE_URL;
|
|
85
|
+
const model = process.env.RERANK_MODEL;
|
|
86
|
+
const topN = parseInt(process.env.RERANK_TOP_N || "10", 10);
|
|
87
|
+
if (!apiKey) {
|
|
88
|
+
throw new Error("RERANK_API_KEY \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
89
|
+
}
|
|
90
|
+
if (!baseUrl) {
|
|
91
|
+
throw new Error("RERANK_BASE_URL \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
92
|
+
}
|
|
93
|
+
if (!model) {
|
|
94
|
+
throw new Error("RERANK_MODEL \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
apiKey,
|
|
98
|
+
baseUrl,
|
|
99
|
+
model,
|
|
100
|
+
topN: isNaN(topN) ? 10 : topN
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
var DEFAULT_EXCLUDE_PATTERNS = [
|
|
104
|
+
// 虚拟环境
|
|
105
|
+
".venv",
|
|
106
|
+
"venv",
|
|
107
|
+
".env",
|
|
108
|
+
"env",
|
|
109
|
+
"node_modules",
|
|
110
|
+
// 版本控制
|
|
111
|
+
".git",
|
|
112
|
+
".svn",
|
|
113
|
+
".hg",
|
|
114
|
+
// Python 缓存
|
|
115
|
+
"__pycache__",
|
|
116
|
+
".pytest_cache",
|
|
117
|
+
".mypy_cache",
|
|
118
|
+
".tox",
|
|
119
|
+
".eggs",
|
|
120
|
+
"*.egg-info",
|
|
121
|
+
// 构建产物
|
|
122
|
+
"dist",
|
|
123
|
+
"build",
|
|
124
|
+
"target",
|
|
125
|
+
"out",
|
|
126
|
+
// IDE 配置
|
|
127
|
+
".idea",
|
|
128
|
+
".vscode",
|
|
129
|
+
".vs",
|
|
130
|
+
// 系统文件
|
|
131
|
+
".DS_Store",
|
|
132
|
+
"Thumbs.db",
|
|
133
|
+
// 编译文件
|
|
134
|
+
"*.pyc",
|
|
135
|
+
"*.pyo",
|
|
136
|
+
"*.pyd",
|
|
137
|
+
"*.so",
|
|
138
|
+
"*.dll"
|
|
139
|
+
];
|
|
140
|
+
function getExcludePatterns() {
|
|
141
|
+
const envPatterns = process.env.IGNORE_PATTERNS;
|
|
142
|
+
const patterns = [...DEFAULT_EXCLUDE_PATTERNS];
|
|
143
|
+
if (envPatterns) {
|
|
144
|
+
const additional = envPatterns.split(",").map((p) => p.trim()).filter(Boolean);
|
|
145
|
+
patterns.push(...additional);
|
|
146
|
+
}
|
|
147
|
+
return patterns;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export {
|
|
151
|
+
isDev,
|
|
152
|
+
checkEmbeddingEnv,
|
|
153
|
+
checkRerankerEnv,
|
|
154
|
+
getEmbeddingConfig,
|
|
155
|
+
getRerankerConfig,
|
|
156
|
+
DEFAULT_EXCLUDE_PATTERNS,
|
|
157
|
+
getExcludePatterns
|
|
158
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_EXCLUDE_PATTERNS,
|
|
3
|
+
checkEmbeddingEnv,
|
|
4
|
+
checkRerankerEnv,
|
|
5
|
+
getEmbeddingConfig,
|
|
6
|
+
getExcludePatterns,
|
|
7
|
+
getRerankerConfig,
|
|
8
|
+
isDev
|
|
9
|
+
} from "./chunk-PN7DP6XL.js";
|
|
10
|
+
export {
|
|
11
|
+
DEFAULT_EXCLUDE_PATTERNS,
|
|
12
|
+
checkEmbeddingEnv,
|
|
13
|
+
checkRerankerEnv,
|
|
14
|
+
getEmbeddingConfig,
|
|
15
|
+
getExcludePatterns,
|
|
16
|
+
getRerankerConfig,
|
|
17
|
+
isDev
|
|
18
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
scan
|
|
4
|
+
} from "./chunk-34YZ2U3O.js";
|
|
5
|
+
import "./chunk-6C2D5Y4R.js";
|
|
6
|
+
import {
|
|
7
|
+
generateProjectId,
|
|
8
|
+
logger
|
|
9
|
+
} from "./chunk-5SRSUMKW.js";
|
|
10
|
+
import "./chunk-PN7DP6XL.js";
|
|
11
|
+
|
|
12
|
+
// src/index.ts
|
|
13
|
+
import cac from "cac";
|
|
14
|
+
import { promises as fs } from "fs";
|
|
15
|
+
import path from "path";
|
|
16
|
+
import os from "os";
|
|
17
|
+
var cli = cac("contextweaver");
|
|
18
|
+
cli.command("init", "\u521D\u59CB\u5316 ContextWeaver \u914D\u7F6E").action(async () => {
|
|
19
|
+
const configDir = path.join(os.homedir(), ".contextweaver");
|
|
20
|
+
const envFile = path.join(configDir, ".env");
|
|
21
|
+
logger.info("\u5F00\u59CB\u521D\u59CB\u5316 ContextWeaver...");
|
|
22
|
+
try {
|
|
23
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
24
|
+
logger.info(`\u521B\u5EFA\u914D\u7F6E\u76EE\u5F55: ${configDir}`);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
if (err.code !== "EEXIST") {
|
|
27
|
+
logger.error({ err, stack: err.stack }, `\u521B\u5EFA\u914D\u7F6E\u76EE\u5F55\u5931\u8D25: ${err.message}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
logger.info(`\u914D\u7F6E\u76EE\u5F55\u5DF2\u5B58\u5728: ${configDir}`);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
await fs.access(envFile);
|
|
34
|
+
logger.warn(`.env \u6587\u4EF6\u5DF2\u5B58\u5728: ${envFile}`);
|
|
35
|
+
logger.info("\u521D\u59CB\u5316\u5B8C\u6210\uFF01");
|
|
36
|
+
return;
|
|
37
|
+
} catch {
|
|
38
|
+
}
|
|
39
|
+
const defaultEnvContent = `# ContextWeaver \u793A\u4F8B\u73AF\u5883\u53D8\u91CF\u914D\u7F6E\u6587\u4EF6
|
|
40
|
+
|
|
41
|
+
# Embedding API \u914D\u7F6E\uFF08\u5FC5\u9700\uFF09
|
|
42
|
+
EMBEDDINGS_API_KEY=your-api-key-here
|
|
43
|
+
EMBEDDINGS_BASE_URL=https://api.siliconflow.cn/v1/embeddings
|
|
44
|
+
EMBEDDINGS_MODEL=BAAI/bge-m3
|
|
45
|
+
EMBEDDINGS_MAX_CONCURRENCY=10
|
|
46
|
+
EMBEDDINGS_DIMENSIONS=1024
|
|
47
|
+
|
|
48
|
+
# Reranker \u914D\u7F6E\uFF08\u5FC5\u9700\uFF09
|
|
49
|
+
RERANK_API_KEY=your-api-key-here
|
|
50
|
+
RERANK_BASE_URL=https://api.siliconflow.cn/v1/rerank
|
|
51
|
+
RERANK_MODEL=BAAI/bge-reranker-v2-m3
|
|
52
|
+
RERANK_TOP_N=20
|
|
53
|
+
|
|
54
|
+
# \u7D22\u5F15\u5FFD\u7565\u6A21\u5F0F\uFF08\u53EF\u9009\uFF0C\u9017\u53F7\u5206\u9694\uFF0C\u9ED8\u8BA4\u5DF2\u5305\u542B\u5E38\u89C1\u5FFD\u7565\u9879\uFF09
|
|
55
|
+
# IGNORE_PATTERNS=.venv,node_modules
|
|
56
|
+
`;
|
|
57
|
+
try {
|
|
58
|
+
await fs.writeFile(envFile, defaultEnvContent);
|
|
59
|
+
logger.info(`\u521B\u5EFA .env \u6587\u4EF6: ${envFile}`);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
logger.error({ err, stack: err.stack }, `\u521B\u5EFA .env \u6587\u4EF6\u5931\u8D25: ${err.message}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
logger.info("\u4E0B\u4E00\u6B65\u64CD\u4F5C:");
|
|
65
|
+
logger.info(` 1. \u7F16\u8F91\u914D\u7F6E\u6587\u4EF6: ${envFile}`);
|
|
66
|
+
logger.info(" 2. \u586B\u5199\u4F60\u7684 API Key \u548C\u5176\u4ED6\u914D\u7F6E");
|
|
67
|
+
logger.info("\u521D\u59CB\u5316\u5B8C\u6210\uFF01");
|
|
68
|
+
});
|
|
69
|
+
cli.command("index [path]", "\u626B\u63CF\u4EE3\u7801\u5E93\u5E76\u5EFA\u7ACB\u7D22\u5F15").option("-f, --force", "\u5F3A\u5236\u91CD\u65B0\u7D22\u5F15").action(async (targetPath, options) => {
|
|
70
|
+
const rootPath = targetPath ? path.resolve(targetPath) : process.cwd();
|
|
71
|
+
const projectId = generateProjectId(rootPath);
|
|
72
|
+
logger.info(`\u5F00\u59CB\u626B\u63CF: ${rootPath}`);
|
|
73
|
+
logger.info(`\u9879\u76EE ID: ${projectId}`);
|
|
74
|
+
if (options.force) {
|
|
75
|
+
logger.info("\u5F3A\u5236\u91CD\u65B0\u7D22\u5F15: \u662F");
|
|
76
|
+
}
|
|
77
|
+
const startTime = Date.now();
|
|
78
|
+
try {
|
|
79
|
+
const stats = await scan(rootPath, {
|
|
80
|
+
force: options.force,
|
|
81
|
+
onProgress: (current, total) => {
|
|
82
|
+
const percent = (current / total * 100).toFixed(1);
|
|
83
|
+
process.stdout.write(`\r\u8FDB\u5EA6: ${current}/${total} (${percent}%)`);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
process.stdout.write("\n");
|
|
87
|
+
const duration = ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
88
|
+
logger.info("\u626B\u63CF\u5B8C\u6210\uFF01");
|
|
89
|
+
logger.info(`\u8017\u65F6: ${duration}s`);
|
|
90
|
+
logger.info(`\u6587\u4EF6\u603B\u6570: ${stats.totalFiles}`);
|
|
91
|
+
logger.info(`\u65B0\u589E: ${stats.added}`);
|
|
92
|
+
logger.info(`\u4FEE\u6539: ${stats.modified}`);
|
|
93
|
+
logger.info(`\u672A\u53D8: ${stats.unchanged}`);
|
|
94
|
+
logger.info(`\u5220\u9664: ${stats.deleted}`);
|
|
95
|
+
logger.info(`\u8DF3\u8FC7: ${stats.skipped}`);
|
|
96
|
+
logger.info(`\u9519\u8BEF: ${stats.errors}`);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
logger.error({ err, stack: err.stack }, `\u626B\u63CF\u5931\u8D25: ${err.message}`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
cli.command("mcp", "\u542F\u52A8 MCP \u670D\u52A1\u5668").action(async () => {
|
|
103
|
+
const { startMcpServer } = await import("./server-2SAFEAEY.js");
|
|
104
|
+
try {
|
|
105
|
+
await startMcpServer();
|
|
106
|
+
} catch (error) {
|
|
107
|
+
logger.error({ error: error.message, stack: error.stack }, `MCP \u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25: ${error.message}`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
cli.command("search", "\u672C\u5730\u68C0\u7D22\uFF08\u53C2\u6570\u5BF9\u9F50 MCP\uFF09").option("--repo-path <path>", "\u4EE3\u7801\u5E93\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09").option("--information-request <text>", "\u81EA\u7136\u8BED\u8A00\u95EE\u9898\u63CF\u8FF0\uFF08\u5FC5\u586B\uFF09").option("--technical-terms <terms>", "\u7CBE\u786E\u672F\u8BED\uFF08\u9017\u53F7\u5206\u9694\uFF09").option("--zen", "\u4F7F\u7528 MCP Zen \u914D\u7F6E\uFF08\u9ED8\u8BA4\u5F00\u542F\uFF09").action(async (options) => {
|
|
112
|
+
const repoPath = options.repoPath ? path.resolve(options.repoPath) : process.cwd();
|
|
113
|
+
const informationRequest = options.informationRequest;
|
|
114
|
+
if (!informationRequest) {
|
|
115
|
+
logger.error("\u7F3A\u5C11 --information-request");
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
const technicalTerms = (options.technicalTerms || "").split(",").map((t) => t.trim()).filter(Boolean);
|
|
119
|
+
const useZen = options.zen !== false;
|
|
120
|
+
const { handleCodebaseRetrieval } = await import("./codebaseRetrieval-RDCNIUDM.js");
|
|
121
|
+
const response = await handleCodebaseRetrieval({
|
|
122
|
+
repo_path: repoPath,
|
|
123
|
+
information_request: informationRequest,
|
|
124
|
+
technical_terms: technicalTerms.length > 0 ? technicalTerms : void 0
|
|
125
|
+
}, useZen ? void 0 : {});
|
|
126
|
+
const text = response.content.map((item) => item.text).join("\n");
|
|
127
|
+
process.stdout.write(text + "\n");
|
|
128
|
+
});
|
|
129
|
+
cli.help();
|
|
130
|
+
cli.parse();
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {
|
|
2
|
+
codebaseRetrievalSchema,
|
|
3
|
+
handleCodebaseRetrieval
|
|
4
|
+
} from "./chunk-5TV4JNTE.js";
|
|
5
|
+
import {
|
|
6
|
+
logger
|
|
7
|
+
} from "./chunk-5SRSUMKW.js";
|
|
8
|
+
import "./chunk-PN7DP6XL.js";
|
|
9
|
+
|
|
10
|
+
// src/mcp/server.ts
|
|
11
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
12
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
+
import {
|
|
14
|
+
CallToolRequestSchema,
|
|
15
|
+
ListToolsRequestSchema
|
|
16
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
17
|
+
var SERVER_NAME = "contextweaver";
|
|
18
|
+
var SERVER_VERSION = "0.1.0";
|
|
19
|
+
var TOOLS = [
|
|
20
|
+
{
|
|
21
|
+
name: "codebase-retrieval",
|
|
22
|
+
description: `
|
|
23
|
+
IMPORTANT: This is the PRIMARY tool for searching the codebase.
|
|
24
|
+
It uses a hybrid engine (Semantic + Exact Match) to find relevant code.
|
|
25
|
+
Think of it as the "Google Search" for this repository.
|
|
26
|
+
|
|
27
|
+
Capabilities:
|
|
28
|
+
1. Semantic Search: Understands "what code does" (e.g., "auth logic") via high-dimensional embeddings.
|
|
29
|
+
2. Exact Match: Filters by precise symbols (e.g., class names) via FTS (Full Text Search).
|
|
30
|
+
3. Zen Context: Returns code with localized context (breadcrumbs) to avoid token overflow.
|
|
31
|
+
|
|
32
|
+
<RULES>
|
|
33
|
+
# 1. Tool Selection (When to use)
|
|
34
|
+
- ALWAYS use this tool FIRST for any code exploration or understanding task.
|
|
35
|
+
- DO NOT try to guess file paths. If you don't have the exact path, use this tool.
|
|
36
|
+
- DO NOT use 'grep' or 'find' for semantic understanding. Only use them for exhaustive text matching (e.g. "Find ALL occurrences of string 'foo'").
|
|
37
|
+
|
|
38
|
+
# 2. Before Editing (Critical)
|
|
39
|
+
- Before creating a plan or editing any file, YOU MUST call this tool to gather context.
|
|
40
|
+
- Ask for ALL symbols involved in the edit (classes, functions, types, constants).
|
|
41
|
+
- Do not assume you remember the code structure. Verify it with this tool.
|
|
42
|
+
|
|
43
|
+
# 3. Query Strategy (How to use)
|
|
44
|
+
- Split your intent:
|
|
45
|
+
- Put the "Goal/Context" in 'information_request'.
|
|
46
|
+
- Put "Known Class/Func Names" in 'technical_terms'.
|
|
47
|
+
- If the first search is too broad, add more specific 'technical_terms'.
|
|
48
|
+
</RULES>
|
|
49
|
+
|
|
50
|
+
Examples of GOOD queries:
|
|
51
|
+
* [Goal: Understand Auth]
|
|
52
|
+
information_request: "How is user authentication flow handled?"
|
|
53
|
+
* [Goal: Fix DB Pool bug]
|
|
54
|
+
information_request: "Logic for database connection pooling and error handling"
|
|
55
|
+
technical_terms: ["PoolConfig", "Connection", "release"]
|
|
56
|
+
|
|
57
|
+
Examples of BAD queries:
|
|
58
|
+
* "Show me src/main.ts" (Use 'read_file' instead)
|
|
59
|
+
* "Find definition of constructor of class Foo" (Use this tool, but put "Foo" in technical_terms)
|
|
60
|
+
* "Find all references to function bar across the whole project" (Use 'grep' tool for exhaustive reference counting)
|
|
61
|
+
`,
|
|
62
|
+
inputSchema: {
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: {
|
|
65
|
+
repo_path: {
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "The absolute file system path to the repository root."
|
|
68
|
+
},
|
|
69
|
+
information_request: {
|
|
70
|
+
type: "string",
|
|
71
|
+
description: "The SEMANTIC GOAL. Describe the functionality, logic, or behavior you are looking for in full natural language sentences. Focus on 'how it works' rather than exact names. (e.g., 'Trace the execution flow of the login process')"
|
|
72
|
+
},
|
|
73
|
+
technical_terms: {
|
|
74
|
+
type: "array",
|
|
75
|
+
items: { type: "string" },
|
|
76
|
+
description: "HARD FILTERS. An optional list of EXACT, KNOWN identifiers (class/function names, constants) that MUST appear in the code. Only use terms you are 100% sure exist. Leave empty if exploring."
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
required: ["repo_path", "information_request"]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
];
|
|
83
|
+
async function startMcpServer() {
|
|
84
|
+
logger.info({ name: SERVER_NAME, version: SERVER_VERSION }, "\u542F\u52A8 MCP \u670D\u52A1\u5668");
|
|
85
|
+
const server = new Server(
|
|
86
|
+
{
|
|
87
|
+
name: SERVER_NAME,
|
|
88
|
+
version: SERVER_VERSION
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
capabilities: {
|
|
92
|
+
tools: {}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
97
|
+
logger.debug("\u6536\u5230 list_tools \u8BF7\u6C42");
|
|
98
|
+
return { tools: TOOLS };
|
|
99
|
+
});
|
|
100
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
101
|
+
const { name, arguments: args } = request.params;
|
|
102
|
+
logger.info({ tool: name }, "\u6536\u5230 call_tool \u8BF7\u6C42");
|
|
103
|
+
try {
|
|
104
|
+
switch (name) {
|
|
105
|
+
case "codebase-retrieval": {
|
|
106
|
+
const parsed = codebaseRetrievalSchema.parse(args);
|
|
107
|
+
return await handleCodebaseRetrieval(parsed);
|
|
108
|
+
}
|
|
109
|
+
default:
|
|
110
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
logger.error({ error: error.message, stack: error.stack, tool: name }, "\u5DE5\u5177\u8C03\u7528\u5931\u8D25");
|
|
114
|
+
return {
|
|
115
|
+
content: [
|
|
116
|
+
{
|
|
117
|
+
type: "text",
|
|
118
|
+
text: `Error: ${error.message}`
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
isError: true
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
const transport = new StdioServerTransport();
|
|
126
|
+
await server.connect(transport);
|
|
127
|
+
logger.info("MCP \u670D\u52A1\u5668\u5DF2\u542F\u52A8\uFF0C\u7B49\u5F85\u8FDE\u63A5...");
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
startMcpServer
|
|
131
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hsingjui/contextweaver",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A context weaving tool for LLMs",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "hsingjui",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/hsingjui/contextweaver.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/hsingjui/contextweaver#readme",
|
|
12
|
+
"private": false,
|
|
13
|
+
"type": "module",
|
|
14
|
+
"bin": {
|
|
15
|
+
"contextweaver": "dist/index.js",
|
|
16
|
+
"cw": "dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist/**/*.js",
|
|
20
|
+
"dist/**/*.d.ts"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@lancedb/lancedb": "^0.19.1",
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
28
|
+
"better-sqlite3": "^11.10.0",
|
|
29
|
+
"cac": "^6.7.14",
|
|
30
|
+
"chardet": "^2.1.1",
|
|
31
|
+
"dotenv": "^17.2.3",
|
|
32
|
+
"fdir": "^6.5.0",
|
|
33
|
+
"iconv-lite": "^0.7.1",
|
|
34
|
+
"ignore": "^7.0.5",
|
|
35
|
+
"p-limit": "^7.2.0",
|
|
36
|
+
"pino": "^10.1.0",
|
|
37
|
+
"tree-sitter": "0.20.6",
|
|
38
|
+
"tree-sitter-go": "0.20.0",
|
|
39
|
+
"tree-sitter-java": "0.20.2",
|
|
40
|
+
"tree-sitter-javascript": "0.20.4",
|
|
41
|
+
"tree-sitter-python": "0.20.4",
|
|
42
|
+
"tree-sitter-rust": "0.20.4",
|
|
43
|
+
"tree-sitter-typescript": "0.20.5",
|
|
44
|
+
"zod": "^4.2.1"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
48
|
+
"@types/node": "^25.0.3",
|
|
49
|
+
"pino-pretty": "^13.1.3",
|
|
50
|
+
"tsup": "^8.5.1",
|
|
51
|
+
"typescript": "^5.9.3"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup src/index.ts src/mcp/main.ts --format esm --dts --out-dir dist --sourcemap --clean",
|
|
55
|
+
"build:release": "tsup src/index.ts src/mcp/main.ts --format esm --dts --out-dir dist --clean",
|
|
56
|
+
"dev": "tsup src/index.ts src/mcp/main.ts --format esm --dts --out-dir dist --sourcemap --watch",
|
|
57
|
+
"start": "node dist/index.js"
|
|
58
|
+
}
|
|
59
|
+
}
|