@vpxa/kb 0.1.1 → 0.1.2
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/package.json +1 -1
- package/packages/analyzers/dist/blast-radius-analyzer.js +13 -114
- package/packages/analyzers/dist/dependency-analyzer.js +11 -425
- package/packages/analyzers/dist/diagram-generator.js +4 -86
- package/packages/analyzers/dist/entry-point-analyzer.js +5 -239
- package/packages/analyzers/dist/index.js +1 -23
- package/packages/analyzers/dist/knowledge-producer.js +24 -113
- package/packages/analyzers/dist/pattern-analyzer.js +5 -359
- package/packages/analyzers/dist/regex-call-graph.js +1 -428
- package/packages/analyzers/dist/structure-analyzer.js +4 -258
- package/packages/analyzers/dist/symbol-analyzer.js +13 -442
- package/packages/analyzers/dist/ts-call-graph.js +1 -160
- package/packages/analyzers/dist/types.js +0 -1
- package/packages/chunker/dist/call-graph-extractor.js +1 -90
- package/packages/chunker/dist/chunker-factory.js +1 -36
- package/packages/chunker/dist/chunker.interface.js +0 -1
- package/packages/chunker/dist/code-chunker.js +14 -134
- package/packages/chunker/dist/generic-chunker.js +5 -72
- package/packages/chunker/dist/index.js +1 -21
- package/packages/chunker/dist/markdown-chunker.js +7 -119
- package/packages/chunker/dist/treesitter-chunker.js +8 -234
- package/packages/cli/dist/commands/analyze.js +3 -112
- package/packages/cli/dist/commands/context-cmds.js +1 -155
- package/packages/cli/dist/commands/environment.js +2 -204
- package/packages/cli/dist/commands/execution.js +1 -137
- package/packages/cli/dist/commands/graph.js +7 -81
- package/packages/cli/dist/commands/init.js +9 -87
- package/packages/cli/dist/commands/knowledge.js +1 -139
- package/packages/cli/dist/commands/search.js +8 -267
- package/packages/cli/dist/commands/system.js +4 -241
- package/packages/cli/dist/commands/workspace.js +2 -388
- package/packages/cli/dist/context.js +1 -14
- package/packages/cli/dist/helpers.js +3 -458
- package/packages/cli/dist/index.js +3 -69
- package/packages/cli/dist/kb-init.js +1 -82
- package/packages/cli/dist/types.js +0 -1
- package/packages/core/dist/constants.js +1 -43
- package/packages/core/dist/content-detector.js +1 -79
- package/packages/core/dist/errors.js +1 -40
- package/packages/core/dist/index.js +1 -9
- package/packages/core/dist/logger.js +1 -34
- package/packages/core/dist/types.js +0 -1
- package/packages/embeddings/dist/embedder.interface.js +0 -1
- package/packages/embeddings/dist/index.js +1 -5
- package/packages/embeddings/dist/onnx-embedder.js +1 -82
- package/packages/indexer/dist/file-hasher.js +1 -13
- package/packages/indexer/dist/filesystem-crawler.js +1 -125
- package/packages/indexer/dist/graph-extractor.js +1 -111
- package/packages/indexer/dist/incremental-indexer.js +1 -278
- package/packages/indexer/dist/index.js +1 -14
- package/packages/server/dist/api.js +1 -9
- package/packages/server/dist/config.js +1 -75
- package/packages/server/dist/curated-manager.js +9 -356
- package/packages/server/dist/index.js +1 -134
- package/packages/server/dist/replay-interceptor.js +1 -38
- package/packages/server/dist/resources/resources.js +2 -40
- package/packages/server/dist/server.js +1 -247
- package/packages/server/dist/tools/analyze.tools.js +1 -288
- package/packages/server/dist/tools/forge.tools.js +11 -499
- package/packages/server/dist/tools/forget.tool.js +3 -39
- package/packages/server/dist/tools/graph.tool.js +5 -110
- package/packages/server/dist/tools/list.tool.js +5 -53
- package/packages/server/dist/tools/lookup.tool.js +8 -51
- package/packages/server/dist/tools/onboard.tool.js +2 -112
- package/packages/server/dist/tools/produce.tool.js +4 -74
- package/packages/server/dist/tools/read.tool.js +4 -47
- package/packages/server/dist/tools/reindex.tool.js +2 -70
- package/packages/server/dist/tools/remember.tool.js +3 -42
- package/packages/server/dist/tools/replay.tool.js +6 -88
- package/packages/server/dist/tools/search.tool.js +17 -327
- package/packages/server/dist/tools/status.tool.js +3 -68
- package/packages/server/dist/tools/toolkit.tools.js +20 -1673
- package/packages/server/dist/tools/update.tool.js +3 -39
- package/packages/server/dist/tools/utility.tools.js +19 -456
- package/packages/store/dist/graph-store.interface.js +0 -1
- package/packages/store/dist/index.js +1 -9
- package/packages/store/dist/lance-store.js +1 -258
- package/packages/store/dist/sqlite-graph-store.js +8 -309
- package/packages/store/dist/store-factory.js +1 -14
- package/packages/store/dist/store.interface.js +0 -1
- package/packages/tools/dist/batch.js +1 -45
- package/packages/tools/dist/changelog.js +2 -112
- package/packages/tools/dist/check.js +2 -59
- package/packages/tools/dist/checkpoint.js +2 -43
- package/packages/tools/dist/codemod.js +2 -69
- package/packages/tools/dist/compact.js +3 -60
- package/packages/tools/dist/data-transform.js +1 -124
- package/packages/tools/dist/dead-symbols.js +2 -71
- package/packages/tools/dist/delegate.js +3 -128
- package/packages/tools/dist/diff-parse.js +3 -153
- package/packages/tools/dist/digest.js +7 -242
- package/packages/tools/dist/encode.js +1 -46
- package/packages/tools/dist/env-info.js +1 -58
- package/packages/tools/dist/eval.js +3 -79
- package/packages/tools/dist/evidence-map.js +3 -203
- package/packages/tools/dist/file-summary.js +2 -106
- package/packages/tools/dist/file-walk.js +1 -75
- package/packages/tools/dist/find-examples.js +3 -48
- package/packages/tools/dist/find.js +1 -120
- package/packages/tools/dist/forge-classify.js +2 -319
- package/packages/tools/dist/forge-ground.js +1 -184
- package/packages/tools/dist/git-context.js +3 -46
- package/packages/tools/dist/graph-query.js +1 -194
- package/packages/tools/dist/health.js +1 -118
- package/packages/tools/dist/http-request.js +1 -58
- package/packages/tools/dist/index.js +1 -273
- package/packages/tools/dist/lane.js +7 -227
- package/packages/tools/dist/measure.js +2 -119
- package/packages/tools/dist/onboard.js +42 -1136
- package/packages/tools/dist/parse-output.js +2 -158
- package/packages/tools/dist/process-manager.js +1 -69
- package/packages/tools/dist/queue.js +2 -126
- package/packages/tools/dist/regex-test.js +1 -39
- package/packages/tools/dist/rename.js +2 -70
- package/packages/tools/dist/replay.js +6 -108
- package/packages/tools/dist/schema-validate.js +1 -141
- package/packages/tools/dist/scope-map.js +1 -72
- package/packages/tools/dist/snippet.js +1 -80
- package/packages/tools/dist/stash.js +2 -60
- package/packages/tools/dist/stratum-card.js +5 -238
- package/packages/tools/dist/symbol.js +3 -87
- package/packages/tools/dist/test-run.js +2 -55
- package/packages/tools/dist/text-utils.js +2 -31
- package/packages/tools/dist/time-utils.js +1 -135
- package/packages/tools/dist/trace.js +2 -114
- package/packages/tools/dist/truncation.js +10 -41
- package/packages/tools/dist/watch.js +1 -61
- package/packages/tools/dist/web-fetch.js +9 -244
- package/packages/tools/dist/web-search.js +1 -46
- package/packages/tools/dist/workset.js +2 -77
- package/packages/tui/dist/App.js +260 -52468
- package/packages/tui/dist/index.js +286 -54551
- package/packages/tui/dist/panels/CuratedPanel.js +211 -34291
- package/packages/tui/dist/panels/LogPanel.js +259 -51703
- package/packages/tui/dist/panels/SearchPanel.js +212 -34824
- package/packages/tui/dist/panels/StatusPanel.js +211 -34304
|
@@ -1,357 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
this.curatedDir = curatedDir;
|
|
8
|
-
this.store = store;
|
|
9
|
-
this.embedder = embedder;
|
|
10
|
-
}
|
|
11
|
-
async remember(title, content, category, tags = []) {
|
|
12
|
-
this.validateCategoryName(category);
|
|
13
|
-
this.validateContentSize(content);
|
|
14
|
-
const slug = this.slugify(title);
|
|
15
|
-
const relativePath = await this.uniqueRelativePath(category, slug);
|
|
16
|
-
const filePath = join(this.curatedDir, relativePath);
|
|
17
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18
|
-
const frontmatter = {
|
|
19
|
-
title,
|
|
20
|
-
category,
|
|
21
|
-
tags,
|
|
22
|
-
created: now,
|
|
23
|
-
updated: now,
|
|
24
|
-
version: 1,
|
|
25
|
-
origin: "curated",
|
|
26
|
-
changelog: [{ version: 1, date: now, reason: "Initial creation" }]
|
|
27
|
-
};
|
|
28
|
-
const fileContent = this.serializeFile(content, frontmatter);
|
|
29
|
-
await mkdir(dirname(filePath), { recursive: true });
|
|
30
|
-
await writeFile(filePath, fileContent, "utf-8");
|
|
31
|
-
await this.indexCuratedFile(relativePath, content, frontmatter);
|
|
32
|
-
return { path: relativePath };
|
|
33
|
-
}
|
|
34
|
-
async update(relativePath, newContent, reason) {
|
|
35
|
-
this.guardPath(relativePath);
|
|
36
|
-
this.validateContentSize(newContent);
|
|
37
|
-
const filePath = join(this.curatedDir, relativePath);
|
|
38
|
-
const raw = await readFile(filePath, "utf-8");
|
|
39
|
-
const { frontmatter } = this.parseFile(raw);
|
|
40
|
-
const newVersion = (frontmatter.version ?? 1) + 1;
|
|
41
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
42
|
-
frontmatter.version = newVersion;
|
|
43
|
-
frontmatter.updated = now;
|
|
44
|
-
frontmatter.changelog = [
|
|
45
|
-
...frontmatter.changelog ?? [],
|
|
46
|
-
{ version: newVersion, date: now, reason }
|
|
47
|
-
];
|
|
48
|
-
const fileContent = this.serializeFile(newContent, frontmatter);
|
|
49
|
-
await writeFile(filePath, fileContent, "utf-8");
|
|
50
|
-
await this.indexCuratedFile(relativePath, newContent, frontmatter);
|
|
51
|
-
return { path: relativePath, version: newVersion };
|
|
52
|
-
}
|
|
53
|
-
async forget(relativePath, _reason) {
|
|
54
|
-
this.guardPath(relativePath);
|
|
55
|
-
const filePath = join(this.curatedDir, relativePath);
|
|
56
|
-
const storePath = `curated/${relativePath}`;
|
|
57
|
-
await this.store.deleteBySourcePath(storePath);
|
|
58
|
-
await unlink(filePath);
|
|
59
|
-
return { path: relativePath };
|
|
60
|
-
}
|
|
61
|
-
async read(relativePath) {
|
|
62
|
-
this.guardPath(relativePath);
|
|
63
|
-
const filePath = join(this.curatedDir, relativePath);
|
|
64
|
-
const raw = await readFile(filePath, "utf-8");
|
|
65
|
-
const { frontmatter, content } = this.parseFile(raw);
|
|
66
|
-
const category = relativePath.split("/")[0];
|
|
67
|
-
return {
|
|
68
|
-
path: relativePath,
|
|
69
|
-
title: frontmatter.title ?? relativePath,
|
|
70
|
-
category,
|
|
71
|
-
tags: frontmatter.tags ?? [],
|
|
72
|
-
version: frontmatter.version ?? 1,
|
|
73
|
-
created: frontmatter.created ?? "",
|
|
74
|
-
updated: frontmatter.updated ?? "",
|
|
75
|
-
contentPreview: content.slice(0, 200),
|
|
76
|
-
content
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
async list(filters) {
|
|
80
|
-
const entries = [];
|
|
81
|
-
const categories = filters?.category ? [filters.category] : await this.discoverCategories();
|
|
82
|
-
for (const cat of categories) {
|
|
83
|
-
const catDir = join(this.curatedDir, cat);
|
|
84
|
-
try {
|
|
85
|
-
const files = await readdir(catDir);
|
|
86
|
-
for (const file of files) {
|
|
87
|
-
if (!file.endsWith(".md")) continue;
|
|
88
|
-
const filePath = join(catDir, file);
|
|
89
|
-
const raw = await readFile(filePath, "utf-8");
|
|
90
|
-
const { frontmatter, content } = this.parseFile(raw);
|
|
91
|
-
if (filters?.tag && !(frontmatter.tags ?? []).includes(filters.tag)) continue;
|
|
92
|
-
entries.push({
|
|
93
|
-
path: `${cat}/${file}`,
|
|
94
|
-
title: frontmatter.title ?? file,
|
|
95
|
-
category: cat,
|
|
96
|
-
tags: frontmatter.tags ?? [],
|
|
97
|
-
version: frontmatter.version ?? 1,
|
|
98
|
-
created: frontmatter.created ?? "",
|
|
99
|
-
updated: frontmatter.updated ?? "",
|
|
100
|
-
contentPreview: content.slice(0, 200)
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
} catch {
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return entries;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Re-index all curated files into the vector store.
|
|
110
|
-
* Call this after the indexer has run to restore curated vectors
|
|
111
|
-
* that may have been lost, or to bulk-sync disk state to vectors.
|
|
112
|
-
*/
|
|
113
|
-
async reindexAll() {
|
|
114
|
-
const categories = await this.discoverCategories();
|
|
115
|
-
const errors = [];
|
|
116
|
-
const pending = [];
|
|
117
|
-
for (const cat of categories) {
|
|
118
|
-
const catDir = join(this.curatedDir, cat);
|
|
119
|
-
let files;
|
|
120
|
-
try {
|
|
121
|
-
files = (await readdir(catDir)).filter((f) => f.endsWith(".md"));
|
|
122
|
-
} catch {
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
for (const file of files) {
|
|
126
|
-
const relativePath = `${cat}/${file}`;
|
|
127
|
-
const filePath = join(catDir, file);
|
|
128
|
-
try {
|
|
129
|
-
const raw = await readFile(filePath, "utf-8");
|
|
130
|
-
const { frontmatter, content } = this.parseFile(raw);
|
|
131
|
-
pending.push({ relativePath, content, frontmatter });
|
|
132
|
-
} catch (err) {
|
|
133
|
-
errors.push(`${relativePath}: ${err.message}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (pending.length === 0) return { indexed: 0, errors };
|
|
138
|
-
const vectors = await this.embedder.embedBatch(pending.map((p) => p.content));
|
|
139
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
140
|
-
const records = pending.map((p) => {
|
|
141
|
-
const storePath = `curated/${p.relativePath}`;
|
|
142
|
-
return {
|
|
143
|
-
id: this.hashId(storePath, 0),
|
|
144
|
-
content: p.content,
|
|
145
|
-
sourcePath: storePath,
|
|
146
|
-
contentType: "curated-knowledge",
|
|
147
|
-
headingPath: p.frontmatter.title,
|
|
148
|
-
chunkIndex: 0,
|
|
149
|
-
totalChunks: 1,
|
|
150
|
-
startLine: 1,
|
|
151
|
-
endLine: p.content.split("\n").length,
|
|
152
|
-
fileHash: this.hash(p.content),
|
|
153
|
-
indexedAt: now,
|
|
154
|
-
origin: "curated",
|
|
155
|
-
tags: p.frontmatter.tags,
|
|
156
|
-
category: p.frontmatter.category,
|
|
157
|
-
version: p.frontmatter.version
|
|
158
|
-
};
|
|
159
|
-
});
|
|
160
|
-
await this.store.upsert(records, vectors);
|
|
161
|
-
return { indexed: pending.length, errors };
|
|
162
|
-
}
|
|
163
|
-
// --- Private helpers ---
|
|
164
|
-
async indexCuratedFile(relativePath, content, frontmatter) {
|
|
165
|
-
const vector = await this.embedder.embed(content);
|
|
166
|
-
const storePath = `curated/${relativePath}`;
|
|
167
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
168
|
-
const record = {
|
|
169
|
-
id: this.hashId(storePath, 0),
|
|
170
|
-
content,
|
|
171
|
-
sourcePath: storePath,
|
|
172
|
-
contentType: "curated-knowledge",
|
|
173
|
-
headingPath: frontmatter.title,
|
|
174
|
-
chunkIndex: 0,
|
|
175
|
-
totalChunks: 1,
|
|
176
|
-
startLine: 1,
|
|
177
|
-
endLine: content.split("\n").length,
|
|
178
|
-
fileHash: this.hash(content),
|
|
179
|
-
indexedAt: now,
|
|
180
|
-
origin: "curated",
|
|
181
|
-
tags: frontmatter.tags,
|
|
182
|
-
category: frontmatter.category,
|
|
183
|
-
version: frontmatter.version
|
|
184
|
-
};
|
|
185
|
-
await this.store.upsert([record], [vector]);
|
|
186
|
-
}
|
|
187
|
-
async discoverCategories() {
|
|
188
|
-
try {
|
|
189
|
-
const entries = await readdir(this.curatedDir, { withFileTypes: true });
|
|
190
|
-
return entries.filter((e) => e.isDirectory() && /^[a-z][a-z0-9-]*$/.test(e.name)).map((e) => e.name);
|
|
191
|
-
} catch {
|
|
192
|
-
return [];
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
guardPath(relativePath) {
|
|
196
|
-
if (relativePath.includes("..") || isAbsolute(relativePath)) {
|
|
197
|
-
throw new Error(`Invalid path: ${relativePath}. Must be relative within curated/ directory.`);
|
|
198
|
-
}
|
|
199
|
-
const category = relativePath.split("/")[0];
|
|
200
|
-
this.validateCategoryName(category);
|
|
201
|
-
}
|
|
202
|
-
validateCategoryName(category) {
|
|
203
|
-
if (!/^[a-z][a-z0-9-]*$/.test(category)) {
|
|
204
|
-
throw new Error(
|
|
205
|
-
`Invalid category name: "${category}". Must be lowercase kebab-case (e.g., "decisions", "api-contracts").`
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
validateContentSize(content) {
|
|
210
|
-
if (Buffer.byteLength(content, "utf-8") > MAX_CONTENT_SIZE) {
|
|
211
|
-
throw new Error(`Content exceeds maximum size of ${MAX_CONTENT_SIZE / 1024}KB`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
slugify(title) {
|
|
215
|
-
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80);
|
|
216
|
-
}
|
|
217
|
-
/** Return a unique `category/slug.md` path, appending `-2`, `-3`, … on collision. */
|
|
218
|
-
async uniqueRelativePath(category, slug) {
|
|
219
|
-
const base = `${category}/${slug}.md`;
|
|
220
|
-
const basePath = join(this.curatedDir, base);
|
|
221
|
-
try {
|
|
222
|
-
await stat(basePath);
|
|
223
|
-
} catch {
|
|
224
|
-
return base;
|
|
225
|
-
}
|
|
226
|
-
for (let i = 2; i <= 100; i++) {
|
|
227
|
-
const candidate = `${category}/${slug}-${i}.md`;
|
|
228
|
-
try {
|
|
229
|
-
await stat(join(this.curatedDir, candidate));
|
|
230
|
-
} catch {
|
|
231
|
-
return candidate;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
throw new Error(`Too many entries with slug "${slug}" in category "${category}"`);
|
|
235
|
-
}
|
|
236
|
-
hash(content) {
|
|
237
|
-
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
238
|
-
}
|
|
239
|
-
hashId(sourcePath, chunkIndex) {
|
|
240
|
-
return this.hash(`${sourcePath}::${chunkIndex}`);
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Simple YAML frontmatter serializer (no gray-matter dependency).
|
|
244
|
-
*/
|
|
245
|
-
serializeFile(content, frontmatter) {
|
|
246
|
-
const yaml = [
|
|
247
|
-
"---",
|
|
248
|
-
`title: "${frontmatter.title.replace(/"/g, '\\"')}"`,
|
|
249
|
-
`category: ${frontmatter.category}`,
|
|
250
|
-
`tags: [${frontmatter.tags.map((t) => `"${t}"`).join(", ")}]`,
|
|
251
|
-
`created: ${frontmatter.created}`,
|
|
252
|
-
`updated: ${frontmatter.updated}`,
|
|
253
|
-
`version: ${frontmatter.version}`,
|
|
254
|
-
`origin: ${frontmatter.origin}`,
|
|
255
|
-
"changelog:",
|
|
256
|
-
...frontmatter.changelog.map(
|
|
257
|
-
(c) => ` - version: ${c.version}
|
|
258
|
-
date: ${c.date}
|
|
259
|
-
reason: "${c.reason.replace(/"/g, '\\"')}"`
|
|
260
|
-
),
|
|
261
|
-
"---"
|
|
262
|
-
].join("\n");
|
|
263
|
-
return `${yaml}
|
|
1
|
+
import{createHash as C}from"node:crypto";import{mkdir as b,readdir as f,readFile as m,stat as y,unlink as P,writeFile as v}from"node:fs/promises";import{dirname as F,isAbsolute as I,join as u}from"node:path";const w=50*1024;class E{constructor(t,e,n){this.curatedDir=t;this.store=e;this.embedder=n}async remember(t,e,n,r=[]){this.validateCategoryName(n),this.validateContentSize(e);const a=this.slugify(t),s=await this.uniqueRelativePath(n,a),i=u(this.curatedDir,s),g=new Date().toISOString(),c={title:t,category:n,tags:r,created:g,updated:g,version:1,origin:"curated",changelog:[{version:1,date:g,reason:"Initial creation"}]},d=this.serializeFile(e,c);return await b(F(i),{recursive:!0}),await v(i,d,"utf-8"),await this.indexCuratedFile(s,e,c),{path:s}}async update(t,e,n){this.guardPath(t),this.validateContentSize(e);const r=u(this.curatedDir,t),a=await m(r,"utf-8"),{frontmatter:s}=this.parseFile(a),i=(s.version??1)+1,g=new Date().toISOString();s.version=i,s.updated=g,s.changelog=[...s.changelog??[],{version:i,date:g,reason:n}];const c=this.serializeFile(e,s);return await v(r,c,"utf-8"),await this.indexCuratedFile(t,e,s),{path:t,version:i}}async forget(t,e){this.guardPath(t);const n=u(this.curatedDir,t),r=`curated/${t}`;return await this.store.deleteBySourcePath(r),await P(n),{path:t}}async read(t){this.guardPath(t);const e=u(this.curatedDir,t),n=await m(e,"utf-8"),{frontmatter:r,content:a}=this.parseFile(n),s=t.split("/")[0];return{path:t,title:r.title??t,category:s,tags:r.tags??[],version:r.version??1,created:r.created??"",updated:r.updated??"",contentPreview:a.slice(0,200),content:a}}async list(t){const e=[],n=t?.category?[t.category]:await this.discoverCategories();for(const r of n){const a=u(this.curatedDir,r);try{const s=await f(a);for(const i of s){if(!i.endsWith(".md"))continue;const g=u(a,i),c=await m(g,"utf-8"),{frontmatter:d,content:h}=this.parseFile(c);t?.tag&&!(d.tags??[]).includes(t.tag)||e.push({path:`${r}/${i}`,title:d.title??i,category:r,tags:d.tags??[],version:d.version??1,created:d.created??"",updated:d.updated??"",contentPreview:h.slice(0,200)})}}catch{}}return e}async reindexAll(){const t=await this.discoverCategories(),e=[],n=[];for(const i of t){const g=u(this.curatedDir,i);let c;try{c=(await f(g)).filter(d=>d.endsWith(".md"))}catch{continue}for(const d of c){const h=`${i}/${d}`,p=u(g,d);try{const o=await m(p,"utf-8"),{frontmatter:l,content:$}=this.parseFile(o);n.push({relativePath:h,content:$,frontmatter:l})}catch(o){e.push(`${h}: ${o.message}`)}}}if(n.length===0)return{indexed:0,errors:e};const r=await this.embedder.embedBatch(n.map(i=>i.content)),a=new Date().toISOString(),s=n.map(i=>{const g=`curated/${i.relativePath}`;return{id:this.hashId(g,0),content:i.content,sourcePath:g,contentType:"curated-knowledge",headingPath:i.frontmatter.title,chunkIndex:0,totalChunks:1,startLine:1,endLine:i.content.split(`
|
|
2
|
+
`).length,fileHash:this.hash(i.content),indexedAt:a,origin:"curated",tags:i.frontmatter.tags,category:i.frontmatter.category,version:i.frontmatter.version}});return await this.store.upsert(s,r),{indexed:n.length,errors:e}}async indexCuratedFile(t,e,n){const r=await this.embedder.embed(e),a=`curated/${t}`,s=new Date().toISOString(),i={id:this.hashId(a,0),content:e,sourcePath:a,contentType:"curated-knowledge",headingPath:n.title,chunkIndex:0,totalChunks:1,startLine:1,endLine:e.split(`
|
|
3
|
+
`).length,fileHash:this.hash(e),indexedAt:s,origin:"curated",tags:n.tags,category:n.category,version:n.version};await this.store.upsert([i],[r])}async discoverCategories(){try{return(await f(this.curatedDir,{withFileTypes:!0})).filter(e=>e.isDirectory()&&/^[a-z][a-z0-9-]*$/.test(e.name)).map(e=>e.name)}catch{return[]}}guardPath(t){if(t.includes("..")||I(t))throw new Error(`Invalid path: ${t}. Must be relative within curated/ directory.`);const e=t.split("/")[0];this.validateCategoryName(e)}validateCategoryName(t){if(!/^[a-z][a-z0-9-]*$/.test(t))throw new Error(`Invalid category name: "${t}". Must be lowercase kebab-case (e.g., "decisions", "api-contracts").`)}validateContentSize(t){if(Buffer.byteLength(t,"utf-8")>w)throw new Error(`Content exceeds maximum size of ${w/1024}KB`)}slugify(t){return t.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"").slice(0,80)}async uniqueRelativePath(t,e){const n=`${t}/${e}.md`,r=u(this.curatedDir,n);try{await y(r)}catch{return n}for(let a=2;a<=100;a++){const s=`${t}/${e}-${a}.md`;try{await y(u(this.curatedDir,s))}catch{return s}}throw new Error(`Too many entries with slug "${e}" in category "${t}"`)}hash(t){return C("sha256").update(t).digest("hex").slice(0,16)}hashId(t,e){return this.hash(`${t}::${e}`)}serializeFile(t,e){return`${["---",`title: "${e.title.replace(/"/g,'\\"')}"`,`category: ${e.category}`,`tags: [${e.tags.map(r=>`"${r}"`).join(", ")}]`,`created: ${e.created}`,`updated: ${e.updated}`,`version: ${e.version}`,`origin: ${e.origin}`,"changelog:",...e.changelog.map(r=>` - version: ${r.version}
|
|
4
|
+
date: ${r.date}
|
|
5
|
+
reason: "${r.reason.replace(/"/g,'\\"')}"`),"---"].join(`
|
|
6
|
+
`)}
|
|
264
7
|
|
|
265
|
-
${
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Simple YAML frontmatter parser (no gray-matter dependency).
|
|
270
|
-
*/
|
|
271
|
-
parseFile(raw) {
|
|
272
|
-
const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n\n?([\s\S]*)$/);
|
|
273
|
-
if (!fmMatch) {
|
|
274
|
-
return {
|
|
275
|
-
frontmatter: {
|
|
276
|
-
title: "Untitled",
|
|
277
|
-
category: "notes",
|
|
278
|
-
tags: [],
|
|
279
|
-
created: "",
|
|
280
|
-
updated: "",
|
|
281
|
-
version: 1,
|
|
282
|
-
origin: "curated",
|
|
283
|
-
changelog: []
|
|
284
|
-
},
|
|
285
|
-
content: raw
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
const yamlStr = fmMatch[1];
|
|
289
|
-
const content = fmMatch[2].trim();
|
|
290
|
-
const fm = {};
|
|
291
|
-
const changelog = [];
|
|
292
|
-
const lines = yamlStr.split("\n");
|
|
293
|
-
let inChangelog = false;
|
|
294
|
-
let currentEntry = {};
|
|
295
|
-
for (const line of lines) {
|
|
296
|
-
if (/^changelog:\s*$/.test(line)) {
|
|
297
|
-
inChangelog = true;
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
|
-
if (inChangelog) {
|
|
301
|
-
const itemMatch = line.match(/^\s+-\s+version:\s*(\d+)$/);
|
|
302
|
-
if (itemMatch) {
|
|
303
|
-
if (currentEntry.version != null) changelog.push(currentEntry);
|
|
304
|
-
currentEntry = { version: parseInt(itemMatch[1], 10) };
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
const dateMatch = line.match(/^\s+date:\s*(.+)$/);
|
|
308
|
-
if (dateMatch) {
|
|
309
|
-
currentEntry.date = dateMatch[1].trim();
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
const reasonMatch = line.match(/^\s+reason:\s*"?(.*?)"?\s*$/);
|
|
313
|
-
if (reasonMatch) {
|
|
314
|
-
currentEntry.reason = reasonMatch[1];
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
if (/^\w/.test(line)) {
|
|
318
|
-
inChangelog = false;
|
|
319
|
-
if (currentEntry.version != null) changelog.push(currentEntry);
|
|
320
|
-
currentEntry = {};
|
|
321
|
-
}
|
|
322
|
-
continue;
|
|
323
|
-
}
|
|
324
|
-
const kvMatch = line.match(/^(\w+):\s*(.*)$/);
|
|
325
|
-
if (kvMatch) {
|
|
326
|
-
const key = kvMatch[1];
|
|
327
|
-
let value = kvMatch[2];
|
|
328
|
-
if (typeof value === "string" && value.startsWith("[") && value.endsWith("]")) {
|
|
329
|
-
value = value.slice(1, -1).split(",").map((s) => s.trim().replace(/^"|"$/g, "")).filter((s) => s.length > 0);
|
|
330
|
-
} else if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
331
|
-
value = parseInt(value, 10);
|
|
332
|
-
} else if (typeof value === "string" && value.startsWith('"') && value.endsWith('"')) {
|
|
333
|
-
value = value.slice(1, -1);
|
|
334
|
-
}
|
|
335
|
-
fm[key] = value;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
if (currentEntry.version != null) changelog.push(currentEntry);
|
|
339
|
-
return {
|
|
340
|
-
frontmatter: {
|
|
341
|
-
title: fm.title ?? "Untitled",
|
|
342
|
-
category: fm.category ?? "notes",
|
|
343
|
-
tags: fm.tags ?? [],
|
|
344
|
-
created: fm.created ?? "",
|
|
345
|
-
updated: fm.updated ?? "",
|
|
346
|
-
version: fm.version ?? 1,
|
|
347
|
-
origin: "curated",
|
|
348
|
-
changelog
|
|
349
|
-
},
|
|
350
|
-
content
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
export {
|
|
355
|
-
CuratedKnowledgeManager
|
|
356
|
-
};
|
|
357
|
-
//# sourceMappingURL=curated-manager.js.map
|
|
8
|
+
${t}
|
|
9
|
+
`}parseFile(t){const e=t.match(/^---\n([\s\S]*?)\n---\n\n?([\s\S]*)$/);if(!e)return{frontmatter:{title:"Untitled",category:"notes",tags:[],created:"",updated:"",version:1,origin:"curated",changelog:[]},content:t};const n=e[1],r=e[2].trim(),a={},s=[],i=n.split(`
|
|
10
|
+
`);let g=!1,c={};for(const d of i){if(/^changelog:\s*$/.test(d)){g=!0;continue}if(g){const p=d.match(/^\s+-\s+version:\s*(\d+)$/);if(p){c.version!=null&&s.push(c),c={version:parseInt(p[1],10)};continue}const o=d.match(/^\s+date:\s*(.+)$/);if(o){c.date=o[1].trim();continue}const l=d.match(/^\s+reason:\s*"?(.*?)"?\s*$/);if(l){c.reason=l[1];continue}/^\w/.test(d)&&(g=!1,c.version!=null&&s.push(c),c={});continue}const h=d.match(/^(\w+):\s*(.*)$/);if(h){const p=h[1];let o=h[2];typeof o=="string"&&o.startsWith("[")&&o.endsWith("]")?o=o.slice(1,-1).split(",").map(l=>l.trim().replace(/^"|"$/g,"")).filter(l=>l.length>0):typeof o=="string"&&/^\d+$/.test(o)?o=parseInt(o,10):typeof o=="string"&&o.startsWith('"')&&o.endsWith('"')&&(o=o.slice(1,-1)),a[p]=o}}return c.version!=null&&s.push(c),{frontmatter:{title:a.title??"Untitled",category:a.category??"notes",tags:a.tags??[],created:a.created??"",updated:a.updated??"",version:a.version??1,origin:"curated",changelog:s},content:r}}}export{E as CuratedKnowledgeManager};
|
|
@@ -1,134 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { loadConfig } from "./config.js";
|
|
3
|
-
import { createMcpServer, createServer, initializeKnowledgeBase } from "./server.js";
|
|
4
|
-
const { values } = parseArgs({
|
|
5
|
-
options: {
|
|
6
|
-
transport: { type: "string", default: process.env.KB_TRANSPORT ?? "stdio" },
|
|
7
|
-
port: { type: "string", default: process.env.KB_PORT ?? "3210" }
|
|
8
|
-
}
|
|
9
|
-
});
|
|
10
|
-
async function main() {
|
|
11
|
-
console.error("[KB] Starting MCP Knowledge Base server...");
|
|
12
|
-
const config = loadConfig();
|
|
13
|
-
console.error(
|
|
14
|
-
`[KB] Config loaded: ${config.sources.length} source(s), store at ${config.store.path}`
|
|
15
|
-
);
|
|
16
|
-
if (values.transport === "http") {
|
|
17
|
-
const { StreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
18
|
-
const express = (await import("express")).default;
|
|
19
|
-
const kb = await initializeKnowledgeBase(config);
|
|
20
|
-
const mcpServer = createMcpServer(kb, config);
|
|
21
|
-
console.error("[KB] MCP server configured with 46 tools and 2 resources");
|
|
22
|
-
const app = express();
|
|
23
|
-
app.use(express.json());
|
|
24
|
-
app.use((_req, res, next) => {
|
|
25
|
-
res.setHeader("Access-Control-Allow-Origin", process.env.KB_CORS_ORIGIN ?? "*");
|
|
26
|
-
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
27
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
28
|
-
if (_req.method === "OPTIONS") {
|
|
29
|
-
res.status(204).end();
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
next();
|
|
33
|
-
});
|
|
34
|
-
app.get("/health", (_req, res) => {
|
|
35
|
-
res.json({ status: "ok" });
|
|
36
|
-
});
|
|
37
|
-
app.post("/mcp", async (req, res) => {
|
|
38
|
-
try {
|
|
39
|
-
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
40
|
-
await mcpServer.connect(transport);
|
|
41
|
-
await transport.handleRequest(req, res, req.body);
|
|
42
|
-
res.on("close", () => {
|
|
43
|
-
transport.close();
|
|
44
|
-
});
|
|
45
|
-
} catch (err) {
|
|
46
|
-
console.error("[KB] MCP handler error:", err);
|
|
47
|
-
if (!res.headersSent) {
|
|
48
|
-
res.status(500).json({
|
|
49
|
-
jsonrpc: "2.0",
|
|
50
|
-
error: { code: -32603, message: "Internal server error" },
|
|
51
|
-
id: null
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
app.get("/mcp", (_req, res) => {
|
|
57
|
-
res.writeHead(405).end(
|
|
58
|
-
JSON.stringify({
|
|
59
|
-
jsonrpc: "2.0",
|
|
60
|
-
error: { code: -32e3, message: "Method not allowed." },
|
|
61
|
-
id: null
|
|
62
|
-
})
|
|
63
|
-
);
|
|
64
|
-
});
|
|
65
|
-
app.delete("/mcp", (_req, res) => {
|
|
66
|
-
res.writeHead(405).end(
|
|
67
|
-
JSON.stringify({
|
|
68
|
-
jsonrpc: "2.0",
|
|
69
|
-
error: { code: -32e3, message: "Method not allowed." },
|
|
70
|
-
id: null
|
|
71
|
-
})
|
|
72
|
-
);
|
|
73
|
-
});
|
|
74
|
-
const port = Number(values.port);
|
|
75
|
-
const httpServer = app.listen(port, () => {
|
|
76
|
-
console.error(`[KB] MCP server listening on http://0.0.0.0:${port}/mcp`);
|
|
77
|
-
const runInitialIndex = async () => {
|
|
78
|
-
try {
|
|
79
|
-
const sourcePaths = config.sources.map((s) => s.path).join(", ");
|
|
80
|
-
console.error(`[KB] Running initial index for sources: ${sourcePaths}`);
|
|
81
|
-
const result = await kb.indexer.index(config, (p) => {
|
|
82
|
-
if (p.phase === "crawling" || p.phase === "done") return;
|
|
83
|
-
if (p.phase === "chunking" && p.currentFile) {
|
|
84
|
-
console.error(`[KB] [${p.filesProcessed + 1}/${p.filesTotal}] ${p.currentFile}`);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
console.error(
|
|
88
|
-
`[KB] Indexed ${result.filesProcessed} files (${result.filesSkipped} skipped, ${result.chunksCreated} chunks) in ${(result.durationMs / 1e3).toFixed(1)}s`
|
|
89
|
-
);
|
|
90
|
-
try {
|
|
91
|
-
const curatedResult = await kb.curated.reindexAll();
|
|
92
|
-
console.error(
|
|
93
|
-
`[KB] Curated re-index: ${curatedResult.indexed} entries restored to vector store`
|
|
94
|
-
);
|
|
95
|
-
} catch (curatedErr) {
|
|
96
|
-
console.error("[KB] Curated re-index failed:", curatedErr);
|
|
97
|
-
}
|
|
98
|
-
} catch (err) {
|
|
99
|
-
console.error("[KB] Initial index failed (will retry on kb_reindex):", err);
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
runInitialIndex();
|
|
103
|
-
});
|
|
104
|
-
const shutdown = async (signal) => {
|
|
105
|
-
console.error(`[KB] ${signal} received \u2014 shutting down...`);
|
|
106
|
-
httpServer.close();
|
|
107
|
-
await mcpServer.close();
|
|
108
|
-
await kb.store.close();
|
|
109
|
-
await kb.embedder.shutdown();
|
|
110
|
-
process.exit(0);
|
|
111
|
-
};
|
|
112
|
-
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
113
|
-
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
114
|
-
} else {
|
|
115
|
-
const { server, runInitialIndex } = await createServer(config);
|
|
116
|
-
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
117
|
-
const transport = new StdioServerTransport();
|
|
118
|
-
await server.connect(transport);
|
|
119
|
-
console.error("[KB] MCP server started (stdio)");
|
|
120
|
-
const autoIndex = process.env.KB_AUTO_INDEX !== "false";
|
|
121
|
-
if (autoIndex) {
|
|
122
|
-
runInitialIndex();
|
|
123
|
-
} else {
|
|
124
|
-
console.error(
|
|
125
|
-
"[KB] Auto-index disabled (KB_AUTO_INDEX=false). Use kb_reindex to index manually."
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
main().catch((err) => {
|
|
131
|
-
console.error("[KB] Fatal error:", err);
|
|
132
|
-
process.exit(1);
|
|
133
|
-
});
|
|
134
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import{parseArgs as m}from"node:util";import{loadConfig as f}from"./config.js";import{createMcpServer as w,createServer as x,initializeKnowledgeBase as g}from"./server.js";const{values:u}=m({options:{transport:{type:"string",default:process.env.KB_TRANSPORT??"stdio"},port:{type:"string",default:process.env.KB_PORT??"3210"}}});async function I(){console.error("[KB] Starting MCP Knowledge Base server...");const t=f();if(console.error(`[KB] Config loaded: ${t.sources.length} source(s), store at ${t.store.path}`),u.transport==="http"){const{StreamableHTTPServerTransport:d}=await import("@modelcontextprotocol/sdk/server/streamableHttp.js"),a=(await import("express")).default,i=await g(t),c=w(i,t);console.error("[KB] MCP server configured with 46 tools and 2 resources");const n=a();n.use(a.json()),n.use((s,e,o)=>{if(e.setHeader("Access-Control-Allow-Origin",process.env.KB_CORS_ORIGIN??"*"),e.setHeader("Access-Control-Allow-Methods","GET, POST, DELETE, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),s.method==="OPTIONS"){e.status(204).end();return}o()}),n.get("/health",(s,e)=>{e.json({status:"ok"})}),n.post("/mcp",async(s,e)=>{try{const o=new d({sessionIdGenerator:void 0});await c.connect(o),await o.handleRequest(s,e,s.body),e.on("close",()=>{o.close()})}catch(o){console.error("[KB] MCP handler error:",o),e.headersSent||e.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:"Internal server error"},id:null})}}),n.get("/mcp",(s,e)=>{e.writeHead(405).end(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed."},id:null}))}),n.delete("/mcp",(s,e)=>{e.writeHead(405).end(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed."},id:null}))});const l=Number(u.port),h=n.listen(l,()=>{console.error(`[KB] MCP server listening on http://0.0.0.0:${l}/mcp`),(async()=>{try{const e=t.sources.map(r=>r.path).join(", ");console.error(`[KB] Running initial index for sources: ${e}`);const o=await i.indexer.index(t,r=>{r.phase==="crawling"||r.phase==="done"||r.phase==="chunking"&&r.currentFile&&console.error(`[KB] [${r.filesProcessed+1}/${r.filesTotal}] ${r.currentFile}`)});console.error(`[KB] Indexed ${o.filesProcessed} files (${o.filesSkipped} skipped, ${o.chunksCreated} chunks) in ${(o.durationMs/1e3).toFixed(1)}s`);try{const r=await i.curated.reindexAll();console.error(`[KB] Curated re-index: ${r.indexed} entries restored to vector store`)}catch(r){console.error("[KB] Curated re-index failed:",r)}}catch(e){console.error("[KB] Initial index failed (will retry on kb_reindex):",e)}})()}),p=async s=>{console.error(`[KB] ${s} received \u2014 shutting down...`),h.close(),await c.close(),await i.store.close(),await i.embedder.shutdown(),process.exit(0)};process.on("SIGINT",()=>p("SIGINT")),process.on("SIGTERM",()=>p("SIGTERM"))}else{const{server:d,runInitialIndex:a}=await x(t),{StdioServerTransport:i}=await import("@modelcontextprotocol/sdk/server/stdio.js"),c=new i;await d.connect(c),console.error("[KB] MCP server started (stdio)"),process.env.KB_AUTO_INDEX!=="false"?a():console.error("[KB] Auto-index disabled (KB_AUTO_INDEX=false). Use kb_reindex to index manually.")}}I().catch(t=>{console.error("[KB] Fatal error:",t),process.exit(1)});
|
|
@@ -1,38 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
function installReplayInterceptor(server) {
|
|
3
|
-
const original = server.registerTool.bind(server);
|
|
4
|
-
server.registerTool = (name, config, handler) => {
|
|
5
|
-
const wrappedHandler = async (args, extra) => {
|
|
6
|
-
const start = Date.now();
|
|
7
|
-
try {
|
|
8
|
-
const result = await handler(args, extra);
|
|
9
|
-
replayAppend({
|
|
10
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11
|
-
source: "mcp",
|
|
12
|
-
tool: name,
|
|
13
|
-
input: JSON.stringify(args),
|
|
14
|
-
durationMs: Date.now() - start,
|
|
15
|
-
status: "ok",
|
|
16
|
-
output: JSON.stringify(result).slice(0, 200)
|
|
17
|
-
});
|
|
18
|
-
return result;
|
|
19
|
-
} catch (err) {
|
|
20
|
-
replayAppend({
|
|
21
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22
|
-
source: "mcp",
|
|
23
|
-
tool: name,
|
|
24
|
-
input: JSON.stringify(args),
|
|
25
|
-
durationMs: Date.now() - start,
|
|
26
|
-
status: "error",
|
|
27
|
-
output: err instanceof Error ? err.message : String(err)
|
|
28
|
-
});
|
|
29
|
-
throw err;
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
return original(name, config, wrappedHandler);
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
export {
|
|
36
|
-
installReplayInterceptor
|
|
37
|
-
};
|
|
38
|
-
//# sourceMappingURL=replay-interceptor.js.map
|
|
1
|
+
import{replayAppend as s}from"@kb/tools";function w(n){const i=n.registerTool.bind(n);n.registerTool=(o,a,p)=>i(o,a,async(r,c)=>{const e=Date.now();try{const t=await p(r,c);return s({ts:new Date().toISOString(),source:"mcp",tool:o,input:JSON.stringify(r),durationMs:Date.now()-e,status:"ok",output:JSON.stringify(t).slice(0,200)}),t}catch(t){throw s({ts:new Date().toISOString(),source:"mcp",tool:o,input:JSON.stringify(r),durationMs:Date.now()-e,status:"error",output:t instanceof Error?t.message:String(t)}),t}})}export{w as installReplayInterceptor};
|
|
@@ -1,40 +1,2 @@
|
|
|
1
|
-
function
|
|
2
|
-
|
|
3
|
-
"kb-status",
|
|
4
|
-
"kb://status",
|
|
5
|
-
{ description: "Current knowledge base status and statistics", mimeType: "text/plain" },
|
|
6
|
-
async () => {
|
|
7
|
-
const stats = await store.getStats();
|
|
8
|
-
return {
|
|
9
|
-
contents: [
|
|
10
|
-
{
|
|
11
|
-
uri: "kb://status",
|
|
12
|
-
text: `Knowledge Base: ${stats.totalRecords} records from ${stats.totalFiles} files. Last indexed: ${stats.lastIndexedAt ?? "Never"}`,
|
|
13
|
-
mimeType: "text/plain"
|
|
14
|
-
}
|
|
15
|
-
]
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
);
|
|
19
|
-
server.resource(
|
|
20
|
-
"kb-file-tree",
|
|
21
|
-
"kb://file-tree",
|
|
22
|
-
{ description: "List of all indexed source files", mimeType: "text/plain" },
|
|
23
|
-
async () => {
|
|
24
|
-
const paths = await store.listSourcePaths();
|
|
25
|
-
return {
|
|
26
|
-
contents: [
|
|
27
|
-
{
|
|
28
|
-
uri: "kb://file-tree",
|
|
29
|
-
text: paths.sort().join("\n"),
|
|
30
|
-
mimeType: "text/plain"
|
|
31
|
-
}
|
|
32
|
-
]
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
export {
|
|
38
|
-
registerResources
|
|
39
|
-
};
|
|
40
|
-
//# sourceMappingURL=resources.js.map
|
|
1
|
+
function r(t,s){t.resource("kb-status","kb://status",{description:"Current knowledge base status and statistics",mimeType:"text/plain"},async()=>{const e=await s.getStats();return{contents:[{uri:"kb://status",text:`Knowledge Base: ${e.totalRecords} records from ${e.totalFiles} files. Last indexed: ${e.lastIndexedAt??"Never"}`,mimeType:"text/plain"}]}}),t.resource("kb-file-tree","kb://file-tree",{description:"List of all indexed source files",mimeType:"text/plain"},async()=>({contents:[{uri:"kb://file-tree",text:(await s.listSourcePaths()).sort().join(`
|
|
2
|
+
`),mimeType:"text/plain"}]}))}export{r as registerResources};
|