bluera-knowledge 0.9.32 → 0.9.36
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/.claude/hooks/post-edit-check.sh +5 -3
- package/.claude/skills/atomic-commits/SKILL.md +3 -1
- package/.husky/pre-commit +3 -2
- package/.prettierrc +9 -0
- package/.versionrc.json +1 -1
- package/CHANGELOG.md +70 -0
- package/CLAUDE.md +6 -0
- package/README.md +25 -13
- package/bun.lock +277 -33
- package/dist/{chunk-L2YVNC63.js → chunk-6FHWC36B.js} +9 -1
- package/dist/chunk-6FHWC36B.js.map +1 -0
- package/dist/{chunk-RST4XGRL.js → chunk-DC7CGSGT.js} +288 -241
- package/dist/chunk-DC7CGSGT.js.map +1 -0
- package/dist/{chunk-6PBP5DVD.js → chunk-WFNPNAAP.js} +3212 -3054
- package/dist/chunk-WFNPNAAP.js.map +1 -0
- package/dist/{chunk-WT2DAEO7.js → chunk-Z2KKVH45.js} +548 -482
- package/dist/chunk-Z2KKVH45.js.map +1 -0
- package/dist/index.js +871 -758
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +3 -3
- package/dist/watch.service-BJV3TI3F.js +7 -0
- package/dist/workers/background-worker-cli.js +97 -71
- package/dist/workers/background-worker-cli.js.map +1 -1
- package/eslint.config.js +43 -1
- package/package.json +18 -11
- package/plugin.json +8 -0
- package/python/requirements.txt +1 -1
- package/src/analysis/ast-parser.test.ts +12 -11
- package/src/analysis/ast-parser.ts +28 -22
- package/src/analysis/code-graph.test.ts +52 -62
- package/src/analysis/code-graph.ts +9 -13
- package/src/analysis/dependency-usage-analyzer.test.ts +91 -271
- package/src/analysis/dependency-usage-analyzer.ts +52 -24
- package/src/analysis/go-ast-parser.test.ts +22 -22
- package/src/analysis/go-ast-parser.ts +18 -25
- package/src/analysis/parser-factory.test.ts +9 -9
- package/src/analysis/parser-factory.ts +3 -3
- package/src/analysis/python-ast-parser.test.ts +27 -27
- package/src/analysis/python-ast-parser.ts +2 -2
- package/src/analysis/repo-url-resolver.test.ts +82 -82
- package/src/analysis/rust-ast-parser.test.ts +19 -19
- package/src/analysis/rust-ast-parser.ts +17 -27
- package/src/analysis/tree-sitter-parser.test.ts +3 -3
- package/src/analysis/tree-sitter-parser.ts +10 -16
- package/src/cli/commands/crawl.test.ts +40 -24
- package/src/cli/commands/crawl.ts +186 -166
- package/src/cli/commands/index-cmd.test.ts +90 -90
- package/src/cli/commands/index-cmd.ts +52 -36
- package/src/cli/commands/mcp.test.ts +6 -6
- package/src/cli/commands/mcp.ts +2 -2
- package/src/cli/commands/plugin-api.test.ts +16 -18
- package/src/cli/commands/plugin-api.ts +9 -6
- package/src/cli/commands/search.test.ts +16 -7
- package/src/cli/commands/search.ts +124 -87
- package/src/cli/commands/serve.test.ts +67 -25
- package/src/cli/commands/serve.ts +18 -3
- package/src/cli/commands/setup.test.ts +176 -101
- package/src/cli/commands/setup.ts +140 -117
- package/src/cli/commands/store.test.ts +82 -53
- package/src/cli/commands/store.ts +56 -37
- package/src/cli/program.ts +2 -2
- package/src/crawl/article-converter.test.ts +4 -1
- package/src/crawl/article-converter.ts +46 -31
- package/src/crawl/bridge.test.ts +240 -132
- package/src/crawl/bridge.ts +87 -30
- package/src/crawl/claude-client.test.ts +124 -56
- package/src/crawl/claude-client.ts +7 -15
- package/src/crawl/intelligent-crawler.test.ts +65 -22
- package/src/crawl/intelligent-crawler.ts +86 -53
- package/src/crawl/markdown-utils.ts +1 -4
- package/src/db/embeddings.ts +4 -6
- package/src/db/lance.test.ts +4 -4
- package/src/db/lance.ts +16 -12
- package/src/index.ts +26 -17
- package/src/logging/index.ts +1 -5
- package/src/logging/logger.ts +3 -5
- package/src/logging/payload.test.ts +1 -1
- package/src/logging/payload.ts +3 -5
- package/src/mcp/commands/index.ts +2 -2
- package/src/mcp/commands/job.commands.ts +12 -18
- package/src/mcp/commands/meta.commands.ts +13 -13
- package/src/mcp/commands/registry.ts +5 -8
- package/src/mcp/commands/store.commands.ts +19 -19
- package/src/mcp/handlers/execute.handler.test.ts +10 -10
- package/src/mcp/handlers/execute.handler.ts +4 -5
- package/src/mcp/handlers/index.ts +10 -14
- package/src/mcp/handlers/job.handler.test.ts +10 -10
- package/src/mcp/handlers/job.handler.ts +22 -25
- package/src/mcp/handlers/search.handler.test.ts +36 -65
- package/src/mcp/handlers/search.handler.ts +135 -104
- package/src/mcp/handlers/store.handler.test.ts +41 -52
- package/src/mcp/handlers/store.handler.ts +108 -88
- package/src/mcp/schemas/index.test.ts +73 -68
- package/src/mcp/schemas/index.ts +18 -12
- package/src/mcp/server.test.ts +1 -1
- package/src/mcp/server.ts +59 -46
- package/src/plugin/commands.test.ts +230 -95
- package/src/plugin/commands.ts +24 -25
- package/src/plugin/dependency-analyzer.test.ts +52 -52
- package/src/plugin/dependency-analyzer.ts +85 -22
- package/src/plugin/git-clone.test.ts +24 -13
- package/src/plugin/git-clone.ts +3 -7
- package/src/server/app.test.ts +109 -109
- package/src/server/app.ts +32 -23
- package/src/server/index.test.ts +64 -66
- package/src/services/chunking.service.test.ts +32 -32
- package/src/services/chunking.service.ts +16 -9
- package/src/services/code-graph.service.test.ts +30 -36
- package/src/services/code-graph.service.ts +24 -10
- package/src/services/code-unit.service.test.ts +55 -11
- package/src/services/code-unit.service.ts +85 -11
- package/src/services/config.service.test.ts +37 -18
- package/src/services/config.service.ts +30 -7
- package/src/services/index.service.test.ts +49 -18
- package/src/services/index.service.ts +98 -48
- package/src/services/index.ts +6 -9
- package/src/services/job.service.test.ts +22 -22
- package/src/services/job.service.ts +18 -18
- package/src/services/project-root.service.test.ts +1 -3
- package/src/services/search.service.test.ts +248 -120
- package/src/services/search.service.ts +286 -156
- package/src/services/services.test.ts +1 -1
- package/src/services/snippet.service.test.ts +14 -6
- package/src/services/snippet.service.ts +7 -5
- package/src/services/store.service.test.ts +68 -29
- package/src/services/store.service.ts +41 -12
- package/src/services/watch.service.test.ts +34 -14
- package/src/services/watch.service.ts +11 -1
- package/src/types/brands.test.ts +3 -1
- package/src/types/index.ts +2 -13
- package/src/types/search.ts +10 -8
- package/src/utils/type-guards.test.ts +20 -15
- package/src/utils/type-guards.ts +1 -1
- package/src/workers/background-worker-cli.ts +28 -30
- package/src/workers/background-worker.test.ts +54 -40
- package/src/workers/background-worker.ts +76 -60
- package/src/workers/pid-file.test.ts +167 -0
- package/src/workers/pid-file.ts +82 -0
- package/src/workers/spawn-worker.test.ts +22 -10
- package/src/workers/spawn-worker.ts +6 -6
- package/tests/analysis/ast-parser.test.ts +3 -3
- package/tests/analysis/code-graph.test.ts +5 -5
- package/tests/fixtures/code-snippets/api/error-handling.ts +4 -15
- package/tests/fixtures/code-snippets/api/rest-controller.ts +3 -9
- package/tests/fixtures/code-snippets/auth/jwt-auth.ts +5 -21
- package/tests/fixtures/code-snippets/auth/oauth-flow.ts +4 -4
- package/tests/fixtures/code-snippets/database/repository-pattern.ts +11 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/aws-lambda/handler.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-pages/handler.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/adapter/cloudflare-workers/serve-static.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/client/client.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/client/types.ts +22 -20
- package/tests/fixtures/corpus/oss-repos/hono/src/context.ts +13 -10
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/accepts/accepts.ts +10 -7
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/adapter/index.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/css/index.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/factory/index.ts +16 -16
- package/tests/fixtures/corpus/oss-repos/hono/src/helper/ssg/ssg.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/hono-base.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/hono.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/css.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/intrinsic-element/components.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/dom/render.ts +7 -7
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/hooks/index.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/intrinsic-element/components.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/jsx/utils.ts +6 -6
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/jsx-renderer/index.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/middleware/serve-static/index.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/quick.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/preset/tiny.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/router/pattern-router/router.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/node.ts +4 -4
- package/tests/fixtures/corpus/oss-repos/hono/src/router/reg-exp-router/router.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/router/trie-router/node.ts +1 -1
- package/tests/fixtures/corpus/oss-repos/hono/src/types.ts +166 -169
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/body.ts +8 -8
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/color.ts +3 -3
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/cookie.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/encode.ts +2 -2
- package/tests/fixtures/corpus/oss-repos/hono/src/utils/types.ts +30 -33
- package/tests/fixtures/corpus/oss-repos/hono/src/validator/validator.ts +2 -2
- package/tests/fixtures/test-server.ts +3 -2
- package/tests/helpers/performance-metrics.ts +8 -25
- package/tests/helpers/search-relevance.ts +14 -69
- package/tests/integration/cli-consistency.test.ts +6 -5
- package/tests/integration/python-bridge.test.ts +13 -3
- package/tests/mcp/server.test.ts +1 -1
- package/tests/services/code-unit.service.test.ts +48 -0
- package/tests/services/job.service.test.ts +124 -0
- package/tests/services/search.progressive-context.test.ts +2 -2
- package/.claude-plugin/plugin.json +0 -13
- package/dist/chunk-6PBP5DVD.js.map +0 -1
- package/dist/chunk-L2YVNC63.js.map +0 -1
- package/dist/chunk-RST4XGRL.js.map +0 -1
- package/dist/chunk-WT2DAEO7.js.map +0 -1
- package/dist/watch.service-YAIKKDCF.js +0 -7
- package/skills/atomic-commits/SKILL.md +0 -77
- /package/dist/{watch.service-YAIKKDCF.js.map → watch.service-BJV3TI3F.js.map} +0 -0
|
@@ -4,21 +4,27 @@ import {
|
|
|
4
4
|
createServices,
|
|
5
5
|
createStoreId,
|
|
6
6
|
summarizePayload
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-WFNPNAAP.js";
|
|
8
8
|
|
|
9
9
|
// src/mcp/server.ts
|
|
10
10
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11
11
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from "
|
|
12
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
+
|
|
14
|
+
// src/mcp/commands/job.commands.ts
|
|
15
|
+
import { z as z2 } from "zod";
|
|
16
16
|
|
|
17
17
|
// src/mcp/schemas/index.ts
|
|
18
18
|
import { z } from "zod";
|
|
19
19
|
var SearchArgsSchema = z.object({
|
|
20
20
|
query: z.string().min(1, "Query must be a non-empty string"),
|
|
21
|
-
intent: z.enum([
|
|
21
|
+
intent: z.enum([
|
|
22
|
+
"find-pattern",
|
|
23
|
+
"find-implementation",
|
|
24
|
+
"find-usage",
|
|
25
|
+
"find-definition",
|
|
26
|
+
"find-documentation"
|
|
27
|
+
]).optional(),
|
|
22
28
|
detail: z.enum(["minimal", "contextual", "full"]).default("minimal"),
|
|
23
29
|
limit: z.number().int().positive().default(10),
|
|
24
30
|
stores: z.array(z.string()).optional()
|
|
@@ -60,288 +66,106 @@ var ExecuteArgsSchema = z.object({
|
|
|
60
66
|
args: z.record(z.string(), z.unknown()).optional()
|
|
61
67
|
});
|
|
62
68
|
|
|
63
|
-
// src/mcp/
|
|
64
|
-
var
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
*/
|
|
72
|
-
constructor(maxSize = 1e3) {
|
|
73
|
-
this.maxSize = maxSize;
|
|
69
|
+
// src/mcp/handlers/job.handler.ts
|
|
70
|
+
var handleCheckJobStatus = (args, context) => {
|
|
71
|
+
const validated = CheckJobStatusArgsSchema.parse(args);
|
|
72
|
+
const { options } = context;
|
|
73
|
+
const jobService = new JobService(options.dataDir);
|
|
74
|
+
const job = jobService.getJob(validated.jobId);
|
|
75
|
+
if (!job) {
|
|
76
|
+
throw new Error(`Job not found: ${validated.jobId}`);
|
|
74
77
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
* @param key - The cache key
|
|
82
|
-
* @param value - The value to store
|
|
83
|
-
*/
|
|
84
|
-
set(key, value) {
|
|
85
|
-
if (this.cache.has(key)) {
|
|
86
|
-
this.cache.delete(key);
|
|
87
|
-
}
|
|
88
|
-
this.cache.set(key, value);
|
|
89
|
-
if (this.cache.size > this.maxSize) {
|
|
90
|
-
const firstKey = this.cache.keys().next().value;
|
|
91
|
-
if (firstKey !== void 0) {
|
|
92
|
-
this.cache.delete(firstKey);
|
|
78
|
+
return Promise.resolve({
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: "text",
|
|
82
|
+
text: JSON.stringify(job, null, 2)
|
|
93
83
|
}
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Retrieve a value from the cache
|
|
98
|
-
*
|
|
99
|
-
* If the key exists, it will be moved to the end (most recent).
|
|
100
|
-
*
|
|
101
|
-
* @param key - The cache key
|
|
102
|
-
* @returns The cached value, or undefined if not found
|
|
103
|
-
*/
|
|
104
|
-
get(key) {
|
|
105
|
-
const value = this.cache.get(key);
|
|
106
|
-
if (value !== void 0) {
|
|
107
|
-
this.cache.delete(key);
|
|
108
|
-
this.cache.set(key, value);
|
|
109
|
-
}
|
|
110
|
-
return value;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Check if a key exists in the cache
|
|
114
|
-
*
|
|
115
|
-
* @param key - The cache key
|
|
116
|
-
* @returns True if the key exists
|
|
117
|
-
*/
|
|
118
|
-
has(key) {
|
|
119
|
-
return this.cache.has(key);
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Remove a specific key from the cache
|
|
123
|
-
*
|
|
124
|
-
* @param key - The cache key
|
|
125
|
-
* @returns True if the key was removed, false if it didn't exist
|
|
126
|
-
*/
|
|
127
|
-
delete(key) {
|
|
128
|
-
return this.cache.delete(key);
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Clear all entries from the cache
|
|
132
|
-
*/
|
|
133
|
-
clear() {
|
|
134
|
-
this.cache.clear();
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Get the current number of items in the cache
|
|
138
|
-
*/
|
|
139
|
-
get size() {
|
|
140
|
-
return this.cache.size;
|
|
141
|
-
}
|
|
84
|
+
]
|
|
85
|
+
});
|
|
142
86
|
};
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return `~${String(tokens)}`;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// src/mcp/handlers/search.handler.ts
|
|
158
|
-
var logger = createLogger("mcp-search");
|
|
159
|
-
var resultCache = new LRUCache(1e3);
|
|
160
|
-
var handleSearch = async (args, context) => {
|
|
161
|
-
const validated = SearchArgsSchema.parse(args);
|
|
162
|
-
logger.info({
|
|
163
|
-
query: validated.query,
|
|
164
|
-
stores: validated.stores,
|
|
165
|
-
detail: validated.detail,
|
|
166
|
-
limit: validated.limit,
|
|
167
|
-
intent: validated.intent
|
|
168
|
-
}, "Search started");
|
|
169
|
-
const { services } = context;
|
|
170
|
-
const storeIds = validated.stores !== void 0 ? await Promise.all(validated.stores.map(async (s) => {
|
|
171
|
-
const store = await services.store.getByIdOrName(s);
|
|
172
|
-
if (!store) {
|
|
173
|
-
throw new Error(`Store not found: ${s}`);
|
|
174
|
-
}
|
|
175
|
-
return store.id;
|
|
176
|
-
})) : (await services.store.list()).map((s) => s.id);
|
|
177
|
-
try {
|
|
178
|
-
for (const storeId of storeIds) {
|
|
179
|
-
await services.lance.initialize(storeId);
|
|
180
|
-
}
|
|
181
|
-
} catch (error) {
|
|
182
|
-
throw new Error(
|
|
183
|
-
`Failed to initialize vector stores: ${error instanceof Error ? error.message : String(error)}`
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
const searchQuery = {
|
|
187
|
-
query: validated.query,
|
|
188
|
-
stores: storeIds,
|
|
189
|
-
mode: "hybrid",
|
|
190
|
-
limit: validated.limit,
|
|
191
|
-
detail: validated.detail
|
|
192
|
-
};
|
|
193
|
-
const results = await services.search.search(searchQuery);
|
|
194
|
-
for (const result of results.results) {
|
|
195
|
-
resultCache.set(result.id, result);
|
|
87
|
+
var handleListJobs = (args, context) => {
|
|
88
|
+
const validated = ListJobsArgsSchema.parse(args);
|
|
89
|
+
const { options } = context;
|
|
90
|
+
const jobService = new JobService(options.dataDir);
|
|
91
|
+
let jobs;
|
|
92
|
+
if (validated.activeOnly === true) {
|
|
93
|
+
jobs = jobService.listActiveJobs();
|
|
94
|
+
} else if (validated.status !== void 0) {
|
|
95
|
+
jobs = jobService.listJobs(validated.status);
|
|
96
|
+
} else {
|
|
97
|
+
jobs = jobService.listJobs();
|
|
196
98
|
}
|
|
197
|
-
|
|
198
|
-
const storeId = r.metadata.storeId;
|
|
199
|
-
const store = await services.store.getByIdOrName(storeId);
|
|
200
|
-
return {
|
|
201
|
-
id: r.id,
|
|
202
|
-
score: r.score,
|
|
203
|
-
summary: {
|
|
204
|
-
...r.summary,
|
|
205
|
-
storeName: store?.name,
|
|
206
|
-
repoRoot: store !== void 0 && store.type === "repo" ? store.path : void 0
|
|
207
|
-
},
|
|
208
|
-
context: r.context,
|
|
209
|
-
full: r.full
|
|
210
|
-
};
|
|
211
|
-
}));
|
|
212
|
-
const responseJson = JSON.stringify({
|
|
213
|
-
results: enhancedResults,
|
|
214
|
-
totalResults: results.totalResults,
|
|
215
|
-
mode: results.mode,
|
|
216
|
-
timeMs: results.timeMs
|
|
217
|
-
}, null, 2);
|
|
218
|
-
const responseTokens = estimateTokens(responseJson);
|
|
219
|
-
const header = `Search: "${validated.query}" | Results: ${String(results.totalResults)} | ${formatTokenCount(responseTokens)} tokens | ${String(results.timeMs)}ms
|
|
220
|
-
|
|
221
|
-
`;
|
|
222
|
-
logger.info({
|
|
223
|
-
query: validated.query,
|
|
224
|
-
totalResults: results.totalResults,
|
|
225
|
-
responseTokens,
|
|
226
|
-
timeMs: results.timeMs,
|
|
227
|
-
...summarizePayload(responseJson, "mcp-response", validated.query)
|
|
228
|
-
}, "Search complete - context sent to Claude Code");
|
|
229
|
-
return {
|
|
99
|
+
return Promise.resolve({
|
|
230
100
|
content: [
|
|
231
101
|
{
|
|
232
102
|
type: "text",
|
|
233
|
-
text:
|
|
103
|
+
text: JSON.stringify({ jobs }, null, 2)
|
|
234
104
|
}
|
|
235
105
|
]
|
|
236
|
-
};
|
|
106
|
+
});
|
|
237
107
|
};
|
|
238
|
-
var
|
|
239
|
-
const validated =
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
-
if (!
|
|
244
|
-
throw new Error(
|
|
245
|
-
`Result not found in cache: ${resultId}. Run a search first to cache results.`
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
if (cachedResult.full) {
|
|
249
|
-
const responseJson2 = JSON.stringify({
|
|
250
|
-
id: cachedResult.id,
|
|
251
|
-
score: cachedResult.score,
|
|
252
|
-
summary: cachedResult.summary,
|
|
253
|
-
context: cachedResult.context,
|
|
254
|
-
full: cachedResult.full
|
|
255
|
-
}, null, 2);
|
|
256
|
-
logger.info({
|
|
257
|
-
resultId,
|
|
258
|
-
cached: true,
|
|
259
|
-
hasFullContext: true,
|
|
260
|
-
...summarizePayload(responseJson2, "mcp-full-context", resultId)
|
|
261
|
-
}, "Full context retrieved from cache");
|
|
262
|
-
return {
|
|
263
|
-
content: [
|
|
264
|
-
{
|
|
265
|
-
type: "text",
|
|
266
|
-
text: responseJson2
|
|
267
|
-
}
|
|
268
|
-
]
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
const { services } = context;
|
|
272
|
-
const store = await services.store.getByIdOrName(cachedResult.metadata.storeId);
|
|
273
|
-
if (!store) {
|
|
274
|
-
throw new Error(`Store not found: ${cachedResult.metadata.storeId}`);
|
|
275
|
-
}
|
|
276
|
-
await services.lance.initialize(store.id);
|
|
277
|
-
const searchQuery = {
|
|
278
|
-
query: cachedResult.content.substring(0, 100),
|
|
279
|
-
// Use snippet of content as query
|
|
280
|
-
stores: [store.id],
|
|
281
|
-
mode: "hybrid",
|
|
282
|
-
limit: 1,
|
|
283
|
-
detail: "full"
|
|
284
|
-
};
|
|
285
|
-
const results = await services.search.search(searchQuery);
|
|
286
|
-
const fullResult = results.results.find((r) => r.id === resultId);
|
|
287
|
-
if (!fullResult) {
|
|
288
|
-
return {
|
|
289
|
-
content: [
|
|
290
|
-
{
|
|
291
|
-
type: "text",
|
|
292
|
-
text: JSON.stringify({
|
|
293
|
-
id: cachedResult.id,
|
|
294
|
-
score: cachedResult.score,
|
|
295
|
-
summary: cachedResult.summary,
|
|
296
|
-
context: cachedResult.context,
|
|
297
|
-
warning: "Could not retrieve full context, returning cached minimal result"
|
|
298
|
-
}, null, 2)
|
|
299
|
-
}
|
|
300
|
-
]
|
|
301
|
-
};
|
|
108
|
+
var handleCancelJob = (args, context) => {
|
|
109
|
+
const validated = CancelJobArgsSchema.parse(args);
|
|
110
|
+
const { options } = context;
|
|
111
|
+
const jobService = new JobService(options.dataDir);
|
|
112
|
+
const result = jobService.cancelJob(validated.jobId);
|
|
113
|
+
if (!result.success) {
|
|
114
|
+
throw new Error(result.error.message);
|
|
302
115
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
id: fullResult.id,
|
|
306
|
-
score: fullResult.score,
|
|
307
|
-
summary: fullResult.summary,
|
|
308
|
-
context: fullResult.context,
|
|
309
|
-
full: fullResult.full
|
|
310
|
-
}, null, 2);
|
|
311
|
-
logger.info({
|
|
312
|
-
resultId,
|
|
313
|
-
cached: false,
|
|
314
|
-
hasFullContext: true,
|
|
315
|
-
...summarizePayload(responseJson, "mcp-full-context", resultId)
|
|
316
|
-
}, "Full context retrieved via re-query");
|
|
317
|
-
return {
|
|
116
|
+
const job = jobService.getJob(validated.jobId);
|
|
117
|
+
return Promise.resolve({
|
|
318
118
|
content: [
|
|
319
119
|
{
|
|
320
120
|
type: "text",
|
|
321
|
-
text:
|
|
121
|
+
text: JSON.stringify(
|
|
122
|
+
{
|
|
123
|
+
success: true,
|
|
124
|
+
job,
|
|
125
|
+
message: "Job cancelled successfully"
|
|
126
|
+
},
|
|
127
|
+
null,
|
|
128
|
+
2
|
|
129
|
+
)
|
|
322
130
|
}
|
|
323
131
|
]
|
|
324
|
-
};
|
|
132
|
+
});
|
|
325
133
|
};
|
|
326
134
|
|
|
327
|
-
// src/mcp/
|
|
328
|
-
var
|
|
135
|
+
// src/mcp/commands/job.commands.ts
|
|
136
|
+
var jobCommands = [
|
|
329
137
|
{
|
|
330
|
-
name: "
|
|
331
|
-
description: "
|
|
332
|
-
|
|
333
|
-
|
|
138
|
+
name: "jobs",
|
|
139
|
+
description: "List all background jobs",
|
|
140
|
+
argsSchema: z2.object({
|
|
141
|
+
activeOnly: z2.boolean().optional().describe("Only show active jobs"),
|
|
142
|
+
status: z2.enum(["pending", "running", "completed", "failed", "cancelled"]).optional().describe("Filter by job status")
|
|
143
|
+
}),
|
|
144
|
+
handler: (args, context) => handleListJobs(args, context)
|
|
334
145
|
},
|
|
335
146
|
{
|
|
336
|
-
name: "
|
|
337
|
-
description: "
|
|
338
|
-
|
|
339
|
-
|
|
147
|
+
name: "job:status",
|
|
148
|
+
description: "Check the status of a specific background job",
|
|
149
|
+
argsSchema: z2.object({
|
|
150
|
+
jobId: z2.string().min(1).describe("Job ID to check")
|
|
151
|
+
}),
|
|
152
|
+
handler: (args, context) => handleCheckJobStatus(args, context)
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: "job:cancel",
|
|
156
|
+
description: "Cancel a running or pending background job",
|
|
157
|
+
argsSchema: z2.object({
|
|
158
|
+
jobId: z2.string().min(1).describe("Job ID to cancel")
|
|
159
|
+
}),
|
|
160
|
+
handler: (args, context) => handleCancelJob(args, context)
|
|
340
161
|
}
|
|
341
162
|
];
|
|
342
163
|
|
|
164
|
+
// src/mcp/commands/meta.commands.ts
|
|
165
|
+
import { z as z4 } from "zod";
|
|
166
|
+
|
|
343
167
|
// src/mcp/commands/registry.ts
|
|
344
|
-
import { z as
|
|
168
|
+
import { z as z3 } from "zod";
|
|
345
169
|
var CommandRegistry = class {
|
|
346
170
|
commands = /* @__PURE__ */ new Map();
|
|
347
171
|
/**
|
|
@@ -411,15 +235,11 @@ function generateHelp(commandName) {
|
|
|
411
235
|
if (command === void 0) {
|
|
412
236
|
throw new Error(`Unknown command: ${commandName}`);
|
|
413
237
|
}
|
|
414
|
-
const lines2 = [
|
|
415
|
-
`Command: ${command.name}`,
|
|
416
|
-
`Description: ${command.description}`,
|
|
417
|
-
""
|
|
418
|
-
];
|
|
238
|
+
const lines2 = [`Command: ${command.name}`, `Description: ${command.description}`, ""];
|
|
419
239
|
if (command.argsSchema !== void 0) {
|
|
420
240
|
lines2.push("Arguments:");
|
|
421
241
|
const schema = command.argsSchema;
|
|
422
|
-
if (schema instanceof
|
|
242
|
+
if (schema instanceof z3.ZodObject) {
|
|
423
243
|
const shape = schema.shape;
|
|
424
244
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
425
245
|
const isOptional = fieldSchema.safeParse(void 0).success;
|
|
@@ -445,8 +265,50 @@ function generateHelp(commandName) {
|
|
|
445
265
|
return lines.join("\n");
|
|
446
266
|
}
|
|
447
267
|
|
|
448
|
-
// src/mcp/commands/
|
|
449
|
-
|
|
268
|
+
// src/mcp/commands/meta.commands.ts
|
|
269
|
+
var metaCommands = [
|
|
270
|
+
{
|
|
271
|
+
name: "commands",
|
|
272
|
+
description: "List all available commands",
|
|
273
|
+
handler: () => {
|
|
274
|
+
const commands = commandRegistry.all();
|
|
275
|
+
const commandList = commands.map((cmd) => ({
|
|
276
|
+
name: cmd.name,
|
|
277
|
+
description: cmd.description
|
|
278
|
+
}));
|
|
279
|
+
return Promise.resolve({
|
|
280
|
+
content: [
|
|
281
|
+
{
|
|
282
|
+
type: "text",
|
|
283
|
+
text: JSON.stringify({ commands: commandList }, null, 2)
|
|
284
|
+
}
|
|
285
|
+
]
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: "help",
|
|
291
|
+
description: "Show help for a specific command or list all commands",
|
|
292
|
+
argsSchema: z4.object({
|
|
293
|
+
command: z4.string().optional().describe("Command name to get help for")
|
|
294
|
+
}),
|
|
295
|
+
handler: (args) => {
|
|
296
|
+
const commandName = args["command"];
|
|
297
|
+
const helpText = generateHelp(commandName);
|
|
298
|
+
return Promise.resolve({
|
|
299
|
+
content: [
|
|
300
|
+
{
|
|
301
|
+
type: "text",
|
|
302
|
+
text: helpText
|
|
303
|
+
}
|
|
304
|
+
]
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
];
|
|
309
|
+
|
|
310
|
+
// src/mcp/commands/store.commands.ts
|
|
311
|
+
import { z as z5 } from "zod";
|
|
450
312
|
|
|
451
313
|
// src/mcp/handlers/store.handler.ts
|
|
452
314
|
import { rm } from "fs/promises";
|
|
@@ -454,8 +316,8 @@ import { join } from "path";
|
|
|
454
316
|
|
|
455
317
|
// src/workers/spawn-worker.ts
|
|
456
318
|
import { spawn } from "child_process";
|
|
457
|
-
import { fileURLToPath } from "url";
|
|
458
319
|
import path from "path";
|
|
320
|
+
import { fileURLToPath } from "url";
|
|
459
321
|
function spawnBackgroundWorker(jobId, dataDir) {
|
|
460
322
|
const __dirname2 = path.dirname(fileURLToPath(import.meta.url));
|
|
461
323
|
const isProduction = __dirname2.includes("/dist/");
|
|
@@ -495,17 +357,21 @@ var handleListStores = async (args, context) => {
|
|
|
495
357
|
content: [
|
|
496
358
|
{
|
|
497
359
|
type: "text",
|
|
498
|
-
text: JSON.stringify(
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
360
|
+
text: JSON.stringify(
|
|
361
|
+
{
|
|
362
|
+
stores: filtered.map((s) => ({
|
|
363
|
+
id: s.id,
|
|
364
|
+
name: s.name,
|
|
365
|
+
type: s.type,
|
|
366
|
+
path: "path" in s ? s.path : void 0,
|
|
367
|
+
url: "url" in s && s.url !== void 0 ? s.url : void 0,
|
|
368
|
+
description: s.description,
|
|
369
|
+
createdAt: s.createdAt.toISOString()
|
|
370
|
+
}))
|
|
371
|
+
},
|
|
372
|
+
null,
|
|
373
|
+
2
|
|
374
|
+
)
|
|
509
375
|
}
|
|
510
376
|
]
|
|
511
377
|
};
|
|
@@ -521,18 +387,22 @@ var handleGetStoreInfo = async (args, context) => {
|
|
|
521
387
|
content: [
|
|
522
388
|
{
|
|
523
389
|
type: "text",
|
|
524
|
-
text: JSON.stringify(
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
390
|
+
text: JSON.stringify(
|
|
391
|
+
{
|
|
392
|
+
id: store.id,
|
|
393
|
+
name: store.name,
|
|
394
|
+
type: store.type,
|
|
395
|
+
path: "path" in store ? store.path : void 0,
|
|
396
|
+
url: "url" in store && store.url !== void 0 ? store.url : void 0,
|
|
397
|
+
branch: "branch" in store ? store.branch : void 0,
|
|
398
|
+
description: store.description,
|
|
399
|
+
status: store.status,
|
|
400
|
+
createdAt: store.createdAt.toISOString(),
|
|
401
|
+
updatedAt: store.updatedAt.toISOString()
|
|
402
|
+
},
|
|
403
|
+
null,
|
|
404
|
+
2
|
|
405
|
+
)
|
|
536
406
|
}
|
|
537
407
|
]
|
|
538
408
|
};
|
|
@@ -572,20 +442,24 @@ var handleCreateStore = async (args, context) => {
|
|
|
572
442
|
content: [
|
|
573
443
|
{
|
|
574
444
|
type: "text",
|
|
575
|
-
text: JSON.stringify(
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
445
|
+
text: JSON.stringify(
|
|
446
|
+
{
|
|
447
|
+
store: {
|
|
448
|
+
id: result.data.id,
|
|
449
|
+
name: result.data.name,
|
|
450
|
+
type: result.data.type,
|
|
451
|
+
path: "path" in result.data ? result.data.path : void 0
|
|
452
|
+
},
|
|
453
|
+
job: {
|
|
454
|
+
id: job.id,
|
|
455
|
+
status: job.status,
|
|
456
|
+
message: job.message
|
|
457
|
+
},
|
|
458
|
+
message: `Store created. Indexing started in background (Job ID: ${job.id})`
|
|
586
459
|
},
|
|
587
|
-
|
|
588
|
-
|
|
460
|
+
null,
|
|
461
|
+
2
|
|
462
|
+
)
|
|
589
463
|
}
|
|
590
464
|
]
|
|
591
465
|
};
|
|
@@ -615,18 +489,22 @@ var handleIndexStore = async (args, context) => {
|
|
|
615
489
|
content: [
|
|
616
490
|
{
|
|
617
491
|
type: "text",
|
|
618
|
-
text: JSON.stringify(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
492
|
+
text: JSON.stringify(
|
|
493
|
+
{
|
|
494
|
+
store: {
|
|
495
|
+
id: store.id,
|
|
496
|
+
name: store.name
|
|
497
|
+
},
|
|
498
|
+
job: {
|
|
499
|
+
id: job.id,
|
|
500
|
+
status: job.status,
|
|
501
|
+
message: job.message
|
|
502
|
+
},
|
|
503
|
+
message: `Indexing started in background (Job ID: ${job.id})`
|
|
627
504
|
},
|
|
628
|
-
|
|
629
|
-
|
|
505
|
+
null,
|
|
506
|
+
2
|
|
507
|
+
)
|
|
630
508
|
}
|
|
631
509
|
]
|
|
632
510
|
};
|
|
@@ -654,15 +532,19 @@ var handleDeleteStore = async (args, context) => {
|
|
|
654
532
|
content: [
|
|
655
533
|
{
|
|
656
534
|
type: "text",
|
|
657
|
-
text: JSON.stringify(
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
535
|
+
text: JSON.stringify(
|
|
536
|
+
{
|
|
537
|
+
deleted: true,
|
|
538
|
+
store: {
|
|
539
|
+
id: store.id,
|
|
540
|
+
name: store.name,
|
|
541
|
+
type: store.type
|
|
542
|
+
},
|
|
543
|
+
message: `Successfully deleted store: ${store.name}`
|
|
663
544
|
},
|
|
664
|
-
|
|
665
|
-
|
|
545
|
+
null,
|
|
546
|
+
2
|
|
547
|
+
)
|
|
666
548
|
}
|
|
667
549
|
]
|
|
668
550
|
};
|
|
@@ -673,198 +555,371 @@ var storeCommands = [
|
|
|
673
555
|
{
|
|
674
556
|
name: "stores",
|
|
675
557
|
description: "List all indexed knowledge stores",
|
|
676
|
-
argsSchema:
|
|
677
|
-
type:
|
|
558
|
+
argsSchema: z5.object({
|
|
559
|
+
type: z5.enum(["file", "repo", "web"]).optional().describe("Filter by store type")
|
|
678
560
|
}),
|
|
679
561
|
handler: (args, context) => handleListStores(args, context)
|
|
680
562
|
},
|
|
681
563
|
{
|
|
682
564
|
name: "store:info",
|
|
683
565
|
description: "Get detailed information about a specific store",
|
|
684
|
-
argsSchema:
|
|
685
|
-
store:
|
|
566
|
+
argsSchema: z5.object({
|
|
567
|
+
store: z5.string().min(1).describe("Store name or ID")
|
|
686
568
|
}),
|
|
687
569
|
handler: (args, context) => handleGetStoreInfo(args, context)
|
|
688
570
|
},
|
|
689
571
|
{
|
|
690
572
|
name: "store:create",
|
|
691
573
|
description: "Create a new knowledge store from git URL or local path",
|
|
692
|
-
argsSchema:
|
|
693
|
-
name:
|
|
694
|
-
type:
|
|
695
|
-
source:
|
|
696
|
-
branch:
|
|
697
|
-
description:
|
|
574
|
+
argsSchema: z5.object({
|
|
575
|
+
name: z5.string().min(1).describe("Store name"),
|
|
576
|
+
type: z5.enum(["file", "repo"]).describe("Store type"),
|
|
577
|
+
source: z5.string().min(1).describe("Git URL or local path"),
|
|
578
|
+
branch: z5.string().optional().describe("Git branch (for repo type)"),
|
|
579
|
+
description: z5.string().optional().describe("Store description")
|
|
698
580
|
}),
|
|
699
581
|
handler: (args, context) => handleCreateStore(args, context)
|
|
700
582
|
},
|
|
701
583
|
{
|
|
702
584
|
name: "store:index",
|
|
703
585
|
description: "Re-index a knowledge store to update search data",
|
|
704
|
-
argsSchema:
|
|
705
|
-
store:
|
|
586
|
+
argsSchema: z5.object({
|
|
587
|
+
store: z5.string().min(1).describe("Store name or ID")
|
|
706
588
|
}),
|
|
707
589
|
handler: (args, context) => handleIndexStore(args, context)
|
|
708
590
|
},
|
|
709
591
|
{
|
|
710
592
|
name: "store:delete",
|
|
711
593
|
description: "Delete a knowledge store and all associated data",
|
|
712
|
-
argsSchema:
|
|
713
|
-
store:
|
|
594
|
+
argsSchema: z5.object({
|
|
595
|
+
store: z5.string().min(1).describe("Store name or ID")
|
|
714
596
|
}),
|
|
715
597
|
handler: (args, context) => handleDeleteStore(args, context)
|
|
716
598
|
}
|
|
717
599
|
];
|
|
718
600
|
|
|
719
|
-
// src/mcp/commands/
|
|
720
|
-
|
|
601
|
+
// src/mcp/commands/index.ts
|
|
602
|
+
commandRegistry.registerAll(storeCommands);
|
|
603
|
+
commandRegistry.registerAll(jobCommands);
|
|
604
|
+
commandRegistry.registerAll(metaCommands);
|
|
721
605
|
|
|
722
|
-
// src/mcp/handlers/
|
|
723
|
-
var
|
|
724
|
-
const validated =
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
606
|
+
// src/mcp/handlers/execute.handler.ts
|
|
607
|
+
var handleExecute = async (args, context) => {
|
|
608
|
+
const validated = ExecuteArgsSchema.parse(args);
|
|
609
|
+
const commandArgs = validated.args ?? {};
|
|
610
|
+
return executeCommand(validated.command, commandArgs, context);
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
// src/services/token.service.ts
|
|
614
|
+
var CHARS_PER_TOKEN = 3.5;
|
|
615
|
+
function estimateTokens(text) {
|
|
616
|
+
if (!text) return 0;
|
|
617
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
618
|
+
}
|
|
619
|
+
function formatTokenCount(tokens) {
|
|
620
|
+
if (tokens >= 1e3) {
|
|
621
|
+
return `~${(tokens / 1e3).toFixed(1)}k`;
|
|
730
622
|
}
|
|
731
|
-
return
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
623
|
+
return `~${String(tokens)}`;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// src/mcp/cache.ts
|
|
627
|
+
var LRUCache = class {
|
|
628
|
+
cache = /* @__PURE__ */ new Map();
|
|
629
|
+
maxSize;
|
|
630
|
+
/**
|
|
631
|
+
* Create a new LRU cache
|
|
632
|
+
*
|
|
633
|
+
* @param maxSize - Maximum number of items to store (default: 1000)
|
|
634
|
+
*/
|
|
635
|
+
constructor(maxSize = 1e3) {
|
|
636
|
+
this.maxSize = maxSize;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Store a value in the cache
|
|
640
|
+
*
|
|
641
|
+
* If the key already exists, it will be moved to the end (most recent).
|
|
642
|
+
* If the cache is at capacity, the oldest item will be evicted.
|
|
643
|
+
*
|
|
644
|
+
* @param key - The cache key
|
|
645
|
+
* @param value - The value to store
|
|
646
|
+
*/
|
|
647
|
+
set(key, value) {
|
|
648
|
+
if (this.cache.has(key)) {
|
|
649
|
+
this.cache.delete(key);
|
|
650
|
+
}
|
|
651
|
+
this.cache.set(key, value);
|
|
652
|
+
if (this.cache.size > this.maxSize) {
|
|
653
|
+
const firstKey = this.cache.keys().next().value;
|
|
654
|
+
if (firstKey !== void 0) {
|
|
655
|
+
this.cache.delete(firstKey);
|
|
736
656
|
}
|
|
737
|
-
|
|
738
|
-
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Retrieve a value from the cache
|
|
661
|
+
*
|
|
662
|
+
* If the key exists, it will be moved to the end (most recent).
|
|
663
|
+
*
|
|
664
|
+
* @param key - The cache key
|
|
665
|
+
* @returns The cached value, or undefined if not found
|
|
666
|
+
*/
|
|
667
|
+
get(key) {
|
|
668
|
+
const value = this.cache.get(key);
|
|
669
|
+
if (value !== void 0) {
|
|
670
|
+
this.cache.delete(key);
|
|
671
|
+
this.cache.set(key, value);
|
|
672
|
+
}
|
|
673
|
+
return value;
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Check if a key exists in the cache
|
|
677
|
+
*
|
|
678
|
+
* @param key - The cache key
|
|
679
|
+
* @returns True if the key exists
|
|
680
|
+
*/
|
|
681
|
+
has(key) {
|
|
682
|
+
return this.cache.has(key);
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Remove a specific key from the cache
|
|
686
|
+
*
|
|
687
|
+
* @param key - The cache key
|
|
688
|
+
* @returns True if the key was removed, false if it didn't exist
|
|
689
|
+
*/
|
|
690
|
+
delete(key) {
|
|
691
|
+
return this.cache.delete(key);
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Clear all entries from the cache
|
|
695
|
+
*/
|
|
696
|
+
clear() {
|
|
697
|
+
this.cache.clear();
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Get the current number of items in the cache
|
|
701
|
+
*/
|
|
702
|
+
get size() {
|
|
703
|
+
return this.cache.size;
|
|
704
|
+
}
|
|
739
705
|
};
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
706
|
+
|
|
707
|
+
// src/mcp/handlers/search.handler.ts
|
|
708
|
+
var logger = createLogger("mcp-search");
|
|
709
|
+
var resultCache = new LRUCache(1e3);
|
|
710
|
+
var handleSearch = async (args, context) => {
|
|
711
|
+
const validated = SearchArgsSchema.parse(args);
|
|
712
|
+
logger.info(
|
|
713
|
+
{
|
|
714
|
+
query: validated.query,
|
|
715
|
+
stores: validated.stores,
|
|
716
|
+
detail: validated.detail,
|
|
717
|
+
limit: validated.limit,
|
|
718
|
+
intent: validated.intent
|
|
719
|
+
},
|
|
720
|
+
"Search started"
|
|
721
|
+
);
|
|
722
|
+
const { services } = context;
|
|
723
|
+
const storeIds = validated.stores !== void 0 ? await Promise.all(
|
|
724
|
+
validated.stores.map(async (s) => {
|
|
725
|
+
const store = await services.store.getByIdOrName(s);
|
|
726
|
+
if (!store) {
|
|
727
|
+
throw new Error(`Store not found: ${s}`);
|
|
728
|
+
}
|
|
729
|
+
return store.id;
|
|
730
|
+
})
|
|
731
|
+
) : (await services.store.list()).map((s) => s.id);
|
|
732
|
+
try {
|
|
733
|
+
for (const storeId of storeIds) {
|
|
734
|
+
await services.lance.initialize(storeId);
|
|
735
|
+
}
|
|
736
|
+
} catch (error) {
|
|
737
|
+
throw new Error(
|
|
738
|
+
`Failed to initialize vector stores: ${error instanceof Error ? error.message : String(error)}`
|
|
739
|
+
);
|
|
751
740
|
}
|
|
752
|
-
|
|
741
|
+
const searchQuery = {
|
|
742
|
+
query: validated.query,
|
|
743
|
+
stores: storeIds,
|
|
744
|
+
mode: "hybrid",
|
|
745
|
+
limit: validated.limit,
|
|
746
|
+
detail: validated.detail
|
|
747
|
+
};
|
|
748
|
+
const results = await services.search.search(searchQuery);
|
|
749
|
+
for (const result of results.results) {
|
|
750
|
+
resultCache.set(result.id, result);
|
|
751
|
+
}
|
|
752
|
+
const enhancedResults = await Promise.all(
|
|
753
|
+
results.results.map(async (r) => {
|
|
754
|
+
const storeId = r.metadata.storeId;
|
|
755
|
+
const store = await services.store.getByIdOrName(storeId);
|
|
756
|
+
return {
|
|
757
|
+
id: r.id,
|
|
758
|
+
score: r.score,
|
|
759
|
+
summary: {
|
|
760
|
+
...r.summary,
|
|
761
|
+
storeName: store?.name,
|
|
762
|
+
repoRoot: store?.type === "repo" ? store.path : void 0
|
|
763
|
+
},
|
|
764
|
+
context: r.context,
|
|
765
|
+
full: r.full
|
|
766
|
+
};
|
|
767
|
+
})
|
|
768
|
+
);
|
|
769
|
+
const responseJson = JSON.stringify(
|
|
770
|
+
{
|
|
771
|
+
results: enhancedResults,
|
|
772
|
+
totalResults: results.totalResults,
|
|
773
|
+
mode: results.mode,
|
|
774
|
+
timeMs: results.timeMs
|
|
775
|
+
},
|
|
776
|
+
null,
|
|
777
|
+
2
|
|
778
|
+
);
|
|
779
|
+
const responseTokens = estimateTokens(responseJson);
|
|
780
|
+
const header = `Search: "${validated.query}" | Results: ${String(results.totalResults)} | ${formatTokenCount(responseTokens)} tokens | ${String(results.timeMs)}ms
|
|
781
|
+
|
|
782
|
+
`;
|
|
783
|
+
logger.info(
|
|
784
|
+
{
|
|
785
|
+
query: validated.query,
|
|
786
|
+
totalResults: results.totalResults,
|
|
787
|
+
responseTokens,
|
|
788
|
+
timeMs: results.timeMs,
|
|
789
|
+
...summarizePayload(responseJson, "mcp-response", validated.query)
|
|
790
|
+
},
|
|
791
|
+
"Search complete - context sent to Claude Code"
|
|
792
|
+
);
|
|
793
|
+
return {
|
|
753
794
|
content: [
|
|
754
795
|
{
|
|
755
796
|
type: "text",
|
|
756
|
-
text:
|
|
797
|
+
text: header + responseJson
|
|
757
798
|
}
|
|
758
799
|
]
|
|
759
|
-
}
|
|
800
|
+
};
|
|
760
801
|
};
|
|
761
|
-
var
|
|
762
|
-
const validated =
|
|
763
|
-
|
|
764
|
-
const
|
|
765
|
-
const
|
|
766
|
-
if (!
|
|
767
|
-
throw new Error(
|
|
802
|
+
var handleGetFullContext = async (args, context) => {
|
|
803
|
+
const validated = GetFullContextArgsSchema.parse(args);
|
|
804
|
+
logger.info({ resultId: validated.resultId }, "Get full context requested");
|
|
805
|
+
const resultId = validated.resultId;
|
|
806
|
+
const cachedResult = resultCache.get(resultId);
|
|
807
|
+
if (!cachedResult) {
|
|
808
|
+
throw new Error(`Result not found in cache: ${resultId}. Run a search first to cache results.`);
|
|
768
809
|
}
|
|
769
|
-
|
|
770
|
-
|
|
810
|
+
if (cachedResult.full) {
|
|
811
|
+
const responseJson2 = JSON.stringify(
|
|
812
|
+
{
|
|
813
|
+
id: cachedResult.id,
|
|
814
|
+
score: cachedResult.score,
|
|
815
|
+
summary: cachedResult.summary,
|
|
816
|
+
context: cachedResult.context,
|
|
817
|
+
full: cachedResult.full
|
|
818
|
+
},
|
|
819
|
+
null,
|
|
820
|
+
2
|
|
821
|
+
);
|
|
822
|
+
logger.info(
|
|
823
|
+
{
|
|
824
|
+
resultId,
|
|
825
|
+
cached: true,
|
|
826
|
+
hasFullContext: true,
|
|
827
|
+
...summarizePayload(responseJson2, "mcp-full-context", resultId)
|
|
828
|
+
},
|
|
829
|
+
"Full context retrieved from cache"
|
|
830
|
+
);
|
|
831
|
+
return {
|
|
832
|
+
content: [
|
|
833
|
+
{
|
|
834
|
+
type: "text",
|
|
835
|
+
text: responseJson2
|
|
836
|
+
}
|
|
837
|
+
]
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
const { services } = context;
|
|
841
|
+
const store = await services.store.getByIdOrName(cachedResult.metadata.storeId);
|
|
842
|
+
if (!store) {
|
|
843
|
+
throw new Error(`Store not found: ${cachedResult.metadata.storeId}`);
|
|
844
|
+
}
|
|
845
|
+
await services.lance.initialize(store.id);
|
|
846
|
+
const searchQuery = {
|
|
847
|
+
query: cachedResult.content.substring(0, 100),
|
|
848
|
+
// Use snippet of content as query
|
|
849
|
+
stores: [store.id],
|
|
850
|
+
mode: "hybrid",
|
|
851
|
+
limit: 1,
|
|
852
|
+
detail: "full"
|
|
853
|
+
};
|
|
854
|
+
const results = await services.search.search(searchQuery);
|
|
855
|
+
const fullResult = results.results.find((r) => r.id === resultId);
|
|
856
|
+
if (!fullResult) {
|
|
857
|
+
return {
|
|
858
|
+
content: [
|
|
859
|
+
{
|
|
860
|
+
type: "text",
|
|
861
|
+
text: JSON.stringify(
|
|
862
|
+
{
|
|
863
|
+
id: cachedResult.id,
|
|
864
|
+
score: cachedResult.score,
|
|
865
|
+
summary: cachedResult.summary,
|
|
866
|
+
context: cachedResult.context,
|
|
867
|
+
warning: "Could not retrieve full context, returning cached minimal result"
|
|
868
|
+
},
|
|
869
|
+
null,
|
|
870
|
+
2
|
|
871
|
+
)
|
|
872
|
+
}
|
|
873
|
+
]
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
resultCache.set(resultId, fullResult);
|
|
877
|
+
const responseJson = JSON.stringify(
|
|
878
|
+
{
|
|
879
|
+
id: fullResult.id,
|
|
880
|
+
score: fullResult.score,
|
|
881
|
+
summary: fullResult.summary,
|
|
882
|
+
context: fullResult.context,
|
|
883
|
+
full: fullResult.full
|
|
884
|
+
},
|
|
885
|
+
null,
|
|
886
|
+
2
|
|
887
|
+
);
|
|
888
|
+
logger.info(
|
|
889
|
+
{
|
|
890
|
+
resultId,
|
|
891
|
+
cached: false,
|
|
892
|
+
hasFullContext: true,
|
|
893
|
+
...summarizePayload(responseJson, "mcp-full-context", resultId)
|
|
894
|
+
},
|
|
895
|
+
"Full context retrieved via re-query"
|
|
896
|
+
);
|
|
897
|
+
return {
|
|
771
898
|
content: [
|
|
772
899
|
{
|
|
773
900
|
type: "text",
|
|
774
|
-
text:
|
|
775
|
-
success: true,
|
|
776
|
-
job,
|
|
777
|
-
message: "Job cancelled successfully"
|
|
778
|
-
}, null, 2)
|
|
901
|
+
text: responseJson
|
|
779
902
|
}
|
|
780
903
|
]
|
|
781
|
-
}
|
|
904
|
+
};
|
|
782
905
|
};
|
|
783
906
|
|
|
784
|
-
// src/mcp/
|
|
785
|
-
var
|
|
786
|
-
{
|
|
787
|
-
name: "jobs",
|
|
788
|
-
description: "List all background jobs",
|
|
789
|
-
argsSchema: z4.object({
|
|
790
|
-
activeOnly: z4.boolean().optional().describe("Only show active jobs"),
|
|
791
|
-
status: z4.enum(["pending", "running", "completed", "failed", "cancelled"]).optional().describe("Filter by job status")
|
|
792
|
-
}),
|
|
793
|
-
handler: (args, context) => handleListJobs(args, context)
|
|
794
|
-
},
|
|
795
|
-
{
|
|
796
|
-
name: "job:status",
|
|
797
|
-
description: "Check the status of a specific background job",
|
|
798
|
-
argsSchema: z4.object({
|
|
799
|
-
jobId: z4.string().min(1).describe("Job ID to check")
|
|
800
|
-
}),
|
|
801
|
-
handler: (args, context) => handleCheckJobStatus(args, context)
|
|
802
|
-
},
|
|
803
|
-
{
|
|
804
|
-
name: "job:cancel",
|
|
805
|
-
description: "Cancel a running or pending background job",
|
|
806
|
-
argsSchema: z4.object({
|
|
807
|
-
jobId: z4.string().min(1).describe("Job ID to cancel")
|
|
808
|
-
}),
|
|
809
|
-
handler: (args, context) => handleCancelJob(args, context)
|
|
810
|
-
}
|
|
811
|
-
];
|
|
812
|
-
|
|
813
|
-
// src/mcp/commands/meta.commands.ts
|
|
814
|
-
import { z as z5 } from "zod";
|
|
815
|
-
var metaCommands = [
|
|
907
|
+
// src/mcp/handlers/index.ts
|
|
908
|
+
var tools = [
|
|
816
909
|
{
|
|
817
|
-
name: "
|
|
818
|
-
description: "
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
const commandList = commands.map((cmd) => ({
|
|
822
|
-
name: cmd.name,
|
|
823
|
-
description: cmd.description
|
|
824
|
-
}));
|
|
825
|
-
return Promise.resolve({
|
|
826
|
-
content: [
|
|
827
|
-
{
|
|
828
|
-
type: "text",
|
|
829
|
-
text: JSON.stringify({ commands: commandList }, null, 2)
|
|
830
|
-
}
|
|
831
|
-
]
|
|
832
|
-
});
|
|
833
|
-
}
|
|
910
|
+
name: "search",
|
|
911
|
+
description: "Search all indexed knowledge stores with pattern detection and AI-optimized results. Returns structured code units with progressive context layers.",
|
|
912
|
+
schema: SearchArgsSchema,
|
|
913
|
+
handler: handleSearch
|
|
834
914
|
},
|
|
835
915
|
{
|
|
836
|
-
name: "
|
|
837
|
-
description: "
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
}),
|
|
841
|
-
handler: (args) => {
|
|
842
|
-
const commandName = args["command"];
|
|
843
|
-
const helpText = generateHelp(commandName);
|
|
844
|
-
return Promise.resolve({
|
|
845
|
-
content: [
|
|
846
|
-
{
|
|
847
|
-
type: "text",
|
|
848
|
-
text: helpText
|
|
849
|
-
}
|
|
850
|
-
]
|
|
851
|
-
});
|
|
852
|
-
}
|
|
916
|
+
name: "get_full_context",
|
|
917
|
+
description: "Get complete code and context for a specific search result by ID. Use this after search to get full implementation details.",
|
|
918
|
+
schema: GetFullContextArgsSchema,
|
|
919
|
+
handler: handleGetFullContext
|
|
853
920
|
}
|
|
854
921
|
];
|
|
855
922
|
|
|
856
|
-
// src/mcp/commands/index.ts
|
|
857
|
-
commandRegistry.registerAll(storeCommands);
|
|
858
|
-
commandRegistry.registerAll(jobCommands);
|
|
859
|
-
commandRegistry.registerAll(metaCommands);
|
|
860
|
-
|
|
861
|
-
// src/mcp/handlers/execute.handler.ts
|
|
862
|
-
var handleExecute = async (args, context) => {
|
|
863
|
-
const validated = ExecuteArgsSchema.parse(args);
|
|
864
|
-
const commandArgs = validated.args ?? {};
|
|
865
|
-
return executeCommand(validated.command, commandArgs, context);
|
|
866
|
-
};
|
|
867
|
-
|
|
868
923
|
// src/mcp/server.ts
|
|
869
924
|
var logger2 = createLogger("mcp-server");
|
|
870
925
|
function createMCPServer(options) {
|
|
@@ -895,7 +950,13 @@ function createMCPServer(options) {
|
|
|
895
950
|
},
|
|
896
951
|
intent: {
|
|
897
952
|
type: "string",
|
|
898
|
-
enum: [
|
|
953
|
+
enum: [
|
|
954
|
+
"find-pattern",
|
|
955
|
+
"find-implementation",
|
|
956
|
+
"find-usage",
|
|
957
|
+
"find-definition",
|
|
958
|
+
"find-documentation"
|
|
959
|
+
],
|
|
899
960
|
description: "Search intent for better ranking"
|
|
900
961
|
},
|
|
901
962
|
detail: {
|
|
@@ -959,11 +1020,7 @@ function createMCPServer(options) {
|
|
|
959
1020
|
const { name, arguments: args } = request.params;
|
|
960
1021
|
const startTime = Date.now();
|
|
961
1022
|
logger2.info({ tool: name, args: JSON.stringify(args) }, "Tool invoked");
|
|
962
|
-
const services = await createServices(
|
|
963
|
-
options.config,
|
|
964
|
-
options.dataDir,
|
|
965
|
-
options.projectRoot
|
|
966
|
-
);
|
|
1023
|
+
const services = await createServices(options.config, options.dataDir, options.projectRoot);
|
|
967
1024
|
const context = { services, options };
|
|
968
1025
|
try {
|
|
969
1026
|
let result;
|
|
@@ -983,21 +1040,27 @@ function createMCPServer(options) {
|
|
|
983
1040
|
return result;
|
|
984
1041
|
} catch (error) {
|
|
985
1042
|
const durationMs = Date.now() - startTime;
|
|
986
|
-
logger2.error(
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1043
|
+
logger2.error(
|
|
1044
|
+
{
|
|
1045
|
+
tool: name,
|
|
1046
|
+
durationMs,
|
|
1047
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1048
|
+
},
|
|
1049
|
+
"Tool execution failed"
|
|
1050
|
+
);
|
|
991
1051
|
throw error;
|
|
992
1052
|
}
|
|
993
1053
|
});
|
|
994
1054
|
return server;
|
|
995
1055
|
}
|
|
996
1056
|
async function runMCPServer(options) {
|
|
997
|
-
logger2.info(
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1057
|
+
logger2.info(
|
|
1058
|
+
{
|
|
1059
|
+
dataDir: options.dataDir,
|
|
1060
|
+
projectRoot: options.projectRoot
|
|
1061
|
+
},
|
|
1062
|
+
"MCP server starting"
|
|
1063
|
+
);
|
|
1001
1064
|
const server = createMCPServer(options);
|
|
1002
1065
|
const transport = new StdioServerTransport();
|
|
1003
1066
|
await server.connect(transport);
|
|
@@ -1011,7 +1074,10 @@ if (isMCPServerEntry) {
|
|
|
1011
1074
|
config: process.env["CONFIG_PATH"],
|
|
1012
1075
|
projectRoot: process.env["PROJECT_ROOT"] ?? process.env["PWD"]
|
|
1013
1076
|
}).catch((error) => {
|
|
1014
|
-
logger2.error(
|
|
1077
|
+
logger2.error(
|
|
1078
|
+
{ error: error instanceof Error ? error.message : String(error) },
|
|
1079
|
+
"Failed to start MCP server"
|
|
1080
|
+
);
|
|
1015
1081
|
process.exit(1);
|
|
1016
1082
|
});
|
|
1017
1083
|
}
|
|
@@ -1020,4 +1086,4 @@ export {
|
|
|
1020
1086
|
createMCPServer,
|
|
1021
1087
|
runMCPServer
|
|
1022
1088
|
};
|
|
1023
|
-
//# sourceMappingURL=chunk-
|
|
1089
|
+
//# sourceMappingURL=chunk-Z2KKVH45.js.map
|