@vpxa/kb 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- 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.d.ts +1 -1
- 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,89 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
server.registerTool(
|
|
5
|
-
"replay_list",
|
|
6
|
-
{
|
|
7
|
-
description: "View the audit trail of recent MCP tool and CLI invocations. Shows tool name, duration, status, and input/output summaries. Useful for debugging agent behavior and understanding what happened.",
|
|
8
|
-
inputSchema: {
|
|
9
|
-
last: z.number().optional().describe("Number of entries to return (default: 20)"),
|
|
10
|
-
tool: z.string().optional().describe('Filter by tool name (e.g., "search")'),
|
|
11
|
-
source: z.enum(["mcp", "cli"]).optional().describe('Filter by source: "mcp" or "cli"'),
|
|
12
|
-
since: z.string().optional().describe("ISO timestamp \u2014 only show entries after this time")
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
async ({ last, tool, source, since }) => {
|
|
16
|
-
try {
|
|
17
|
-
const entries = replayList({ last, tool, source, since });
|
|
18
|
-
if (entries.length === 0) {
|
|
19
|
-
return {
|
|
20
|
-
content: [
|
|
21
|
-
{
|
|
22
|
-
type: "text",
|
|
23
|
-
text: "No replay entries found. Activity is logged when tools are invoked via MCP or CLI."
|
|
24
|
-
}
|
|
25
|
-
]
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
const lines = entries.map((e) => {
|
|
29
|
-
const time = e.ts.split("T")[1]?.split(".")[0] ?? e.ts;
|
|
30
|
-
const status = e.status === "ok" ? "\u2713" : "\u2717";
|
|
31
|
-
return `${time} ${status} ${e.tool} (${e.durationMs}ms) [${e.source}]
|
|
32
|
-
in: ${e.input}
|
|
33
|
-
out: ${e.output}`;
|
|
34
|
-
});
|
|
35
|
-
replayTrim();
|
|
36
|
-
return {
|
|
37
|
-
content: [
|
|
38
|
-
{
|
|
39
|
-
type: "text",
|
|
40
|
-
text: `**Replay Log** (${entries.length} entries)
|
|
1
|
+
import{replayClear as u,replayList as d,replayTrim as m}from"../../../tools/dist/index.js";import{z as o}from"zod";function f(n){n.registerTool("replay_list",{description:"View the audit trail of recent MCP tool and CLI invocations. Shows tool name, duration, status, and input/output summaries. Useful for debugging agent behavior and understanding what happened.",inputSchema:{last:o.number().optional().describe("Number of entries to return (default: 20)"),tool:o.string().optional().describe('Filter by tool name (e.g., "search")'),source:o.enum(["mcp","cli"]).optional().describe('Filter by source: "mcp" or "cli"'),since:o.string().optional().describe("ISO timestamp \u2014 only show entries after this time")}},async({last:r,tool:s,source:i,since:a})=>{try{const e=d({last:r,tool:s,source:i,since:a});if(e.length===0)return{content:[{type:"text",text:"No replay entries found. Activity is logged when tools are invoked via MCP or CLI."}]};const l=e.map(t=>{const c=t.ts.split("T")[1]?.split(".")[0]??t.ts,p=t.status==="ok"?"\u2713":"\u2717";return`${c} ${p} ${t.tool} (${t.durationMs}ms) [${t.source}]
|
|
2
|
+
in: ${t.input}
|
|
3
|
+
out: ${t.output}`});return m(),{content:[{type:"text",text:`**Replay Log** (${e.length} entries)
|
|
41
4
|
|
|
42
|
-
${
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
46
|
-
} catch (err) {
|
|
47
|
-
console.error("[KB] Replay list failed:", err);
|
|
48
|
-
return {
|
|
49
|
-
content: [
|
|
50
|
-
{ type: "text", text: `Replay list failed: ${err.message}` }
|
|
51
|
-
],
|
|
52
|
-
isError: true
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
);
|
|
57
|
-
server.registerTool(
|
|
58
|
-
"replay_clear",
|
|
59
|
-
{
|
|
60
|
-
description: "Clear the entire replay audit trail.",
|
|
61
|
-
inputSchema: {}
|
|
62
|
-
},
|
|
63
|
-
async () => {
|
|
64
|
-
try {
|
|
65
|
-
replayClear();
|
|
66
|
-
return {
|
|
67
|
-
content: [
|
|
68
|
-
{
|
|
69
|
-
type: "text",
|
|
70
|
-
text: "Replay log cleared."
|
|
71
|
-
}
|
|
72
|
-
]
|
|
73
|
-
};
|
|
74
|
-
} catch (err) {
|
|
75
|
-
console.error("[KB] Replay clear failed:", err);
|
|
76
|
-
return {
|
|
77
|
-
content: [
|
|
78
|
-
{ type: "text", text: `Replay clear failed: ${err.message}` }
|
|
79
|
-
],
|
|
80
|
-
isError: true
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
export {
|
|
87
|
-
registerReplayTool
|
|
88
|
-
};
|
|
89
|
-
//# sourceMappingURL=replay.tool.js.map
|
|
5
|
+
${l.join(`
|
|
6
|
+
|
|
7
|
+
`)}`}]}}catch(e){return console.error("[KB] Replay list failed:",e),{content:[{type:"text",text:`Replay list failed: ${e.message}`}],isError:!0}}}),n.registerTool("replay_clear",{description:"Clear the entire replay audit trail.",inputSchema:{}},async()=>{try{return u(),{content:[{type:"text",text:"Replay log cleared."}]}}catch(r){return console.error("[KB] Replay clear failed:",r),{content:[{type:"text",text:`Replay clear failed: ${r.message}`}],isError:!0}}})}export{f as registerReplayTool};
|
|
@@ -1,331 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
for (let i = 0; i < ftsResults.length; i++) {
|
|
10
|
-
const r = ftsResults[i];
|
|
11
|
-
const existing = merged.get(r.record.id);
|
|
12
|
-
if (existing) {
|
|
13
|
-
existing.score += 1 / (k + i + 1);
|
|
14
|
-
} else {
|
|
15
|
-
merged.set(r.record.id, { record: r.record, score: 1 / (k + i + 1) });
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return [...merged.values()].sort((a, b) => b.score - a.score).map(({ record, score }) => ({ record, score }));
|
|
19
|
-
}
|
|
20
|
-
function applyProximityReranking(results, query) {
|
|
21
|
-
const terms = query.toLowerCase().split(/\s+/).filter((t) => t.length >= 2);
|
|
22
|
-
if (terms.length < 2) return results;
|
|
23
|
-
return results.map((r) => {
|
|
24
|
-
const content = r.record.content.toLowerCase();
|
|
25
|
-
const positions = terms.map((term) => {
|
|
26
|
-
const pos = [];
|
|
27
|
-
let idx = content.indexOf(term);
|
|
28
|
-
while (idx !== -1) {
|
|
29
|
-
pos.push(idx);
|
|
30
|
-
idx = content.indexOf(term, idx + 1);
|
|
31
|
-
}
|
|
32
|
-
return pos;
|
|
33
|
-
});
|
|
34
|
-
if (positions.some((p) => p.length === 0)) return r;
|
|
35
|
-
let minSpan = content.length;
|
|
36
|
-
for (const startPos of positions[0]) {
|
|
37
|
-
let spanStart = startPos;
|
|
38
|
-
let spanEnd = startPos + terms[0].length;
|
|
39
|
-
for (let t = 1; t < positions.length; t++) {
|
|
40
|
-
let nearest = positions[t][0];
|
|
41
|
-
let nearestDist = Math.abs(nearest - startPos);
|
|
42
|
-
for (let pi = 1; pi < positions[t].length; pi++) {
|
|
43
|
-
const dist = Math.abs(positions[t][pi] - startPos);
|
|
44
|
-
if (dist < nearestDist) {
|
|
45
|
-
nearestDist = dist;
|
|
46
|
-
nearest = positions[t][pi];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
spanStart = Math.min(spanStart, nearest);
|
|
50
|
-
spanEnd = Math.max(spanEnd, nearest + terms[t].length);
|
|
51
|
-
}
|
|
52
|
-
minSpan = Math.min(minSpan, spanEnd - spanStart);
|
|
53
|
-
}
|
|
54
|
-
const proximityBoost = 1 + 0.25 / (1 + minSpan / 200);
|
|
55
|
-
return { record: r.record, score: r.score * proximityBoost };
|
|
56
|
-
}).sort((a, b) => b.score - a.score);
|
|
57
|
-
}
|
|
58
|
-
function extractDistinctiveTerms(results, query, maxTerms = 8) {
|
|
59
|
-
const queryTerms = new Set(
|
|
60
|
-
query.toLowerCase().split(/\s+/).filter((t) => t.length >= 2)
|
|
61
|
-
);
|
|
62
|
-
const termCounts = /* @__PURE__ */ new Map();
|
|
63
|
-
const totalDocs = results.length;
|
|
64
|
-
for (const r of results) {
|
|
65
|
-
const origWords = new Set(
|
|
66
|
-
r.record.content.split(/[^a-zA-Z0-9_]+/).filter((w) => w.length >= 3 && !STOP_WORDS.has(w.toLowerCase()))
|
|
67
|
-
);
|
|
68
|
-
for (const w of origWords) {
|
|
69
|
-
const lower = w.toLowerCase();
|
|
70
|
-
if (/[_A-Z]/.test(w)) {
|
|
71
|
-
termCounts.set(`__id__${lower}`, 1);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const words = new Set(
|
|
75
|
-
r.record.content.toLowerCase().split(/[^a-zA-Z0-9_]+/).filter((w) => w.length >= 3 && !STOP_WORDS.has(w))
|
|
76
|
-
);
|
|
77
|
-
for (const w of words) {
|
|
78
|
-
termCounts.set(w, (termCounts.get(w) ?? 0) + 1);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
const scored = [];
|
|
82
|
-
for (const [term, count] of termCounts) {
|
|
83
|
-
if (term.startsWith("__id__")) continue;
|
|
84
|
-
if (queryTerms.has(term)) continue;
|
|
85
|
-
if (count > totalDocs * 0.8) continue;
|
|
86
|
-
const idf = Math.log(totalDocs / count);
|
|
87
|
-
const identifierBonus = termCounts.has(`__id__${term}`) ? 1 : 0;
|
|
88
|
-
const lenBonus = term.length > 8 ? 0.5 : 0;
|
|
89
|
-
scored.push({ term, score: idf + identifierBonus + lenBonus });
|
|
90
|
-
}
|
|
91
|
-
return scored.sort((a, b) => b.score - a.score).slice(0, maxTerms).map((s) => s.term);
|
|
92
|
-
}
|
|
93
|
-
const STOP_WORDS = /* @__PURE__ */ new Set([
|
|
94
|
-
"the",
|
|
95
|
-
"and",
|
|
96
|
-
"for",
|
|
97
|
-
"are",
|
|
98
|
-
"but",
|
|
99
|
-
"not",
|
|
100
|
-
"you",
|
|
101
|
-
"all",
|
|
102
|
-
"can",
|
|
103
|
-
"had",
|
|
104
|
-
"her",
|
|
105
|
-
"was",
|
|
106
|
-
"one",
|
|
107
|
-
"our",
|
|
108
|
-
"out",
|
|
109
|
-
"has",
|
|
110
|
-
"have",
|
|
111
|
-
"from",
|
|
112
|
-
"this",
|
|
113
|
-
"that",
|
|
114
|
-
"with",
|
|
115
|
-
"they",
|
|
116
|
-
"been",
|
|
117
|
-
"said",
|
|
118
|
-
"each",
|
|
119
|
-
"which",
|
|
120
|
-
"their",
|
|
121
|
-
"will",
|
|
122
|
-
"other",
|
|
123
|
-
"about",
|
|
124
|
-
"many",
|
|
125
|
-
"then",
|
|
126
|
-
"them",
|
|
127
|
-
"these",
|
|
128
|
-
"some",
|
|
129
|
-
"would",
|
|
130
|
-
"make",
|
|
131
|
-
"like",
|
|
132
|
-
"into",
|
|
133
|
-
"could",
|
|
134
|
-
"time",
|
|
135
|
-
"very",
|
|
136
|
-
"when",
|
|
137
|
-
"come",
|
|
138
|
-
"just",
|
|
139
|
-
"know",
|
|
140
|
-
"take",
|
|
141
|
-
"people",
|
|
142
|
-
"also",
|
|
143
|
-
"back",
|
|
144
|
-
"after",
|
|
145
|
-
"only",
|
|
146
|
-
"more",
|
|
147
|
-
"than",
|
|
148
|
-
"over",
|
|
149
|
-
"such",
|
|
150
|
-
"import",
|
|
151
|
-
"export",
|
|
152
|
-
"const",
|
|
153
|
-
"function",
|
|
154
|
-
"return",
|
|
155
|
-
"true",
|
|
156
|
-
"false",
|
|
157
|
-
"null",
|
|
158
|
-
"undefined",
|
|
159
|
-
"string",
|
|
160
|
-
"number",
|
|
161
|
-
"boolean",
|
|
162
|
-
"void",
|
|
163
|
-
"type",
|
|
164
|
-
"interface"
|
|
165
|
-
]);
|
|
166
|
-
function registerSearchTool(server, embedder, store, graphStore) {
|
|
167
|
-
server.registerTool(
|
|
168
|
-
"search",
|
|
169
|
-
{
|
|
170
|
-
description: "Search the knowledge base with hybrid vector + keyword matching (BM25 + RRF fusion). Best for finding code, docs, and prior decisions. Supports semantic, keyword, and hybrid modes.",
|
|
171
|
-
inputSchema: {
|
|
172
|
-
query: z.string().describe("Natural language search query"),
|
|
173
|
-
limit: z.number().min(1).max(20).default(5).describe("Maximum results to return"),
|
|
174
|
-
search_mode: z.enum(["hybrid", "semantic", "keyword"]).default("hybrid").describe(
|
|
175
|
-
"Search strategy: hybrid (vector + FTS + RRF fusion, default), semantic (vector only), keyword (FTS only)"
|
|
176
|
-
),
|
|
177
|
-
content_type: z.enum([
|
|
178
|
-
"markdown",
|
|
179
|
-
"code-typescript",
|
|
180
|
-
"code-javascript",
|
|
181
|
-
"code-python",
|
|
182
|
-
"config-json",
|
|
183
|
-
"config-yaml",
|
|
184
|
-
"config-toml",
|
|
185
|
-
"config-dotenv",
|
|
186
|
-
"infrastructure",
|
|
187
|
-
"documentation",
|
|
188
|
-
"test",
|
|
189
|
-
"script",
|
|
190
|
-
"curated-knowledge",
|
|
191
|
-
"produced-knowledge",
|
|
192
|
-
"other"
|
|
193
|
-
]).optional().describe("Filter by content type"),
|
|
194
|
-
origin: z.enum(["indexed", "curated", "produced"]).optional().describe("Filter by knowledge origin"),
|
|
195
|
-
category: z.string().optional().describe("Filter by category (e.g., decisions, patterns, conventions)"),
|
|
196
|
-
tags: z.array(z.string()).optional().describe("Filter by tags (returns results matching ANY of the specified tags)"),
|
|
197
|
-
min_score: z.number().min(0).max(1).default(0.25).describe("Minimum similarity score"),
|
|
198
|
-
graph_hops: z.number().min(0).max(3).default(0).describe(
|
|
199
|
-
"Number of graph hops to augment results with (0 = no graph context, 1-3 = enrich results with connected entities)"
|
|
200
|
-
)
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
async ({
|
|
204
|
-
query,
|
|
205
|
-
limit,
|
|
206
|
-
search_mode,
|
|
207
|
-
content_type,
|
|
208
|
-
origin,
|
|
209
|
-
category,
|
|
210
|
-
tags,
|
|
211
|
-
min_score,
|
|
212
|
-
graph_hops
|
|
213
|
-
}) => {
|
|
214
|
-
try {
|
|
215
|
-
const filterOpts = {
|
|
216
|
-
limit,
|
|
217
|
-
minScore: min_score,
|
|
218
|
-
contentType: content_type,
|
|
219
|
-
origin,
|
|
220
|
-
category,
|
|
221
|
-
tags
|
|
222
|
-
};
|
|
223
|
-
let results;
|
|
224
|
-
if (search_mode === "keyword") {
|
|
225
|
-
results = await store.ftsSearch(query, filterOpts);
|
|
226
|
-
results = results.slice(0, limit);
|
|
227
|
-
} else if (search_mode === "semantic") {
|
|
228
|
-
const vector = await embedder.embedQuery(query);
|
|
229
|
-
results = await store.search(vector, filterOpts);
|
|
230
|
-
} else {
|
|
231
|
-
const vector = await embedder.embedQuery(query);
|
|
232
|
-
const [vectorResults, ftsResults] = await Promise.all([
|
|
233
|
-
store.search(vector, { ...filterOpts, limit: limit * 2 }),
|
|
234
|
-
store.ftsSearch(query, { ...filterOpts, limit: limit * 2 }).catch(() => [])
|
|
235
|
-
]);
|
|
236
|
-
results = reciprocalRankFusion(vectorResults, ftsResults).slice(0, limit);
|
|
237
|
-
}
|
|
238
|
-
if (results.length > 1) {
|
|
239
|
-
results = applyProximityReranking(results, query);
|
|
240
|
-
}
|
|
241
|
-
if (results.length === 0) {
|
|
242
|
-
return {
|
|
243
|
-
content: [{ type: "text", text: "No results found for the given query." }]
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
let graphSections;
|
|
247
|
-
let graphWarning;
|
|
248
|
-
if (graph_hops > 0 && !graphStore) {
|
|
249
|
-
graphWarning = "> **Note:** `graph_hops` was set but no graph store is available. Graph augmentation skipped.";
|
|
250
|
-
}
|
|
251
|
-
if (graph_hops > 0 && graphStore) {
|
|
252
|
-
try {
|
|
253
|
-
const hits = results.map((r) => ({
|
|
254
|
-
recordId: r.record.id,
|
|
255
|
-
score: r.score,
|
|
256
|
-
sourcePath: r.record.sourcePath
|
|
257
|
-
}));
|
|
258
|
-
const augmented = await graphAugmentSearch(graphStore, hits, {
|
|
259
|
-
hops: graph_hops,
|
|
260
|
-
maxPerHit: 5
|
|
261
|
-
});
|
|
262
|
-
graphSections = /* @__PURE__ */ new Map();
|
|
263
|
-
for (const aug of augmented) {
|
|
264
|
-
if (aug.graphContext.nodes.length > 0) {
|
|
265
|
-
const entities = aug.graphContext.nodes.slice(0, 5).map((n) => ` - **${n.name}** (${n.type})`).join("\n");
|
|
266
|
-
const rels = aug.graphContext.edges.slice(0, 5).map((e) => ` - ${e.fromId} \u2014[${e.type}]\u2192 ${e.toId}`).join("\n");
|
|
267
|
-
const parts = [
|
|
268
|
-
`- **Graph Context** (${graph_hops} hop${graph_hops > 1 ? "s" : ""}):`
|
|
269
|
-
];
|
|
270
|
-
if (entities) parts.push(` Entities:
|
|
271
|
-
${entities}`);
|
|
272
|
-
if (rels) parts.push(` Relationships:
|
|
273
|
-
${rels}`);
|
|
274
|
-
graphSections.set(aug.recordId, parts.join("\n"));
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
} catch (err) {
|
|
278
|
-
console.error("[KB] Graph augmentation failed (non-fatal):", err);
|
|
279
|
-
graphWarning = "> **Note:** Graph augmentation failed. Results shown without graph context.";
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
const formatted = results.map((r, i) => {
|
|
283
|
-
const rec = r.record;
|
|
284
|
-
const header = `### Result ${i + 1} (score: ${r.score.toFixed(3)})`;
|
|
285
|
-
const meta = [
|
|
286
|
-
`- **Source**: ${rec.sourcePath}`,
|
|
287
|
-
rec.headingPath ? `- **Section**: ${rec.headingPath}` : null,
|
|
288
|
-
`- **Type**: ${rec.contentType}`,
|
|
289
|
-
rec.startLine ? `- **Lines**: ${rec.startLine}-${rec.endLine}` : null,
|
|
290
|
-
rec.origin !== "indexed" ? `- **Origin**: ${rec.origin}` : null,
|
|
291
|
-
rec.category ? `- **Category**: ${rec.category}` : null,
|
|
292
|
-
rec.tags?.length ? `- **Tags**: ${rec.tags.join(", ")}` : null,
|
|
293
|
-
graphSections?.get(rec.id) ?? null
|
|
294
|
-
].filter(Boolean).join("\n");
|
|
295
|
-
return `${header}
|
|
296
|
-
${meta}
|
|
1
|
+
import{graphAugmentSearch as C}from"../../../tools/dist/index.js";import{z as f}from"zod";function F(m,y,a=60){const o=new Map;for(let e=0;e<m.length;e++){const t=m[e];o.set(t.record.id,{record:t.record,score:1/(a+e+1)})}for(let e=0;e<y.length;e++){const t=y[e],d=o.get(t.record.id);d?d.score+=1/(a+e+1):o.set(t.record.id,{record:t.record,score:1/(a+e+1)})}return[...o.values()].sort((e,t)=>t.score-e.score).map(({record:e,score:t})=>({record:e,score:t}))}function L(m,y){const a=y.toLowerCase().split(/\s+/).filter(o=>o.length>=2);return a.length<2?m:m.map(o=>{const e=o.record.content.toLowerCase(),t=a.map(i=>{const h=[];let r=e.indexOf(i);for(;r!==-1;)h.push(r),r=e.indexOf(i,r+1);return h});if(t.some(i=>i.length===0))return o;let d=e.length;for(const i of t[0]){let h=i,r=i+a[0].length;for(let l=1;l<t.length;l++){let g=t[l][0],p=Math.abs(g-i);for(let c=1;c<t[l].length;c++){const w=Math.abs(t[l][c]-i);w<p&&(p=w,g=t[l][c])}h=Math.min(h,g),r=Math.max(r,g+a[l].length)}d=Math.min(d,r-h)}const s=1+.25/(1+d/200);return{record:o.record,score:o.score*s}}).sort((o,e)=>e.score-o.score)}function I(m,y,a=8){const o=new Set(y.toLowerCase().split(/\s+/).filter(s=>s.length>=2)),e=new Map,t=m.length;for(const s of m){const i=new Set(s.record.content.split(/[^a-zA-Z0-9_]+/).filter(r=>r.length>=3&&!_.has(r.toLowerCase())));for(const r of i){const l=r.toLowerCase();/[_A-Z]/.test(r)&&e.set(`__id__${l}`,1)}const h=new Set(s.record.content.toLowerCase().split(/[^a-zA-Z0-9_]+/).filter(r=>r.length>=3&&!_.has(r)));for(const r of h)e.set(r,(e.get(r)??0)+1)}const d=[];for(const[s,i]of e){if(s.startsWith("__id__")||o.has(s)||i>t*.8)continue;const h=Math.log(t/i),r=e.has(`__id__${s}`)?1:0,l=s.length>8?.5:0;d.push({term:s,score:h+r+l})}return d.sort((s,i)=>i.score-s.score).slice(0,a).map(s=>s.term)}const _=new Set(["the","and","for","are","but","not","you","all","can","had","her","was","one","our","out","has","have","from","this","that","with","they","been","said","each","which","their","will","other","about","many","then","them","these","some","would","make","like","into","could","time","very","when","come","just","know","take","people","also","back","after","only","more","than","over","such","import","export","const","function","return","true","false","null","undefined","string","number","boolean","void","type","interface"]);function O(m,y,a,o){m.registerTool("search",{description:"Search the knowledge base with hybrid vector + keyword matching (BM25 + RRF fusion). Best for finding code, docs, and prior decisions. Supports semantic, keyword, and hybrid modes.",inputSchema:{query:f.string().describe("Natural language search query"),limit:f.number().min(1).max(20).default(5).describe("Maximum results to return"),search_mode:f.enum(["hybrid","semantic","keyword"]).default("hybrid").describe("Search strategy: hybrid (vector + FTS + RRF fusion, default), semantic (vector only), keyword (FTS only)"),content_type:f.enum(["markdown","code-typescript","code-javascript","code-python","config-json","config-yaml","config-toml","config-dotenv","infrastructure","documentation","test","script","curated-knowledge","produced-knowledge","other"]).optional().describe("Filter by content type"),origin:f.enum(["indexed","curated","produced"]).optional().describe("Filter by knowledge origin"),category:f.string().optional().describe("Filter by category (e.g., decisions, patterns, conventions)"),tags:f.array(f.string()).optional().describe("Filter by tags (returns results matching ANY of the specified tags)"),min_score:f.number().min(0).max(1).default(.25).describe("Minimum similarity score"),graph_hops:f.number().min(0).max(3).default(0).describe("Number of graph hops to augment results with (0 = no graph context, 1-3 = enrich results with connected entities)")}},async({query:e,limit:t,search_mode:d,content_type:s,origin:i,category:h,tags:r,min_score:l,graph_hops:g})=>{try{const p={limit:t,minScore:l,contentType:s,origin:i,category:h,tags:r};let c;if(d==="keyword")c=await a.ftsSearch(e,p),c=c.slice(0,t);else if(d==="semantic"){const u=await y.embedQuery(e);c=await a.search(u,p)}else{const u=await y.embedQuery(e),[S,n]=await Promise.all([a.search(u,{...p,limit:t*2}),a.ftsSearch(e,{...p,limit:t*2}).catch(()=>[])]);c=F(S,n).slice(0,t)}if(c.length>1&&(c=L(c,e)),c.length===0)return{content:[{type:"text",text:"No results found for the given query."}]};let w,x;if(g>0&&!o&&(x="> **Note:** `graph_hops` was set but no graph store is available. Graph augmentation skipped."),g>0&&o)try{const u=c.map(n=>({recordId:n.record.id,score:n.score,sourcePath:n.record.sourcePath})),S=await C(o,u,{hops:g,maxPerHit:5});w=new Map;for(const n of S)if(n.graphContext.nodes.length>0){const $=n.graphContext.nodes.slice(0,5).map(b=>` - **${b.name}** (${b.type})`).join(`
|
|
2
|
+
`),v=n.graphContext.edges.slice(0,5).map(b=>` - ${b.fromId} \u2014[${b.type}]\u2192 ${b.toId}`).join(`
|
|
3
|
+
`),k=[`- **Graph Context** (${g} hop${g>1?"s":""}):`];$&&k.push(` Entities:
|
|
4
|
+
${$}`),v&&k.push(` Relationships:
|
|
5
|
+
${v}`),w.set(n.recordId,k.join(`
|
|
6
|
+
`))}}catch(u){console.error("[KB] Graph augmentation failed (non-fatal):",u),x="> **Note:** Graph augmentation failed. Results shown without graph context."}const M=c.map((u,S)=>{const n=u.record,$=`### Result ${S+1} (score: ${u.score.toFixed(3)})`,v=[`- **Source**: ${n.sourcePath}`,n.headingPath?`- **Section**: ${n.headingPath}`:null,`- **Type**: ${n.contentType}`,n.startLine?`- **Lines**: ${n.startLine}-${n.endLine}`:null,n.origin!=="indexed"?`- **Origin**: ${n.origin}`:null,n.category?`- **Category**: ${n.category}`:null,n.tags?.length?`- **Tags**: ${n.tags.join(", ")}`:null,w?.get(n.id)??null].filter(Boolean).join(`
|
|
7
|
+
`);return`${$}
|
|
8
|
+
${v}
|
|
297
9
|
|
|
298
|
-
${
|
|
299
|
-
}).join("\n\n---\n\n");
|
|
300
|
-
const modeLabel = search_mode === "hybrid" ? "hybrid (vector + keyword RRF)" : search_mode === "keyword" ? "keyword (FTS)" : "semantic (vector)";
|
|
301
|
-
const distinctiveTerms = extractDistinctiveTerms(results, query);
|
|
302
|
-
const termsHint = distinctiveTerms.length > 0 ? `
|
|
303
|
-
_Distinctive terms: ${distinctiveTerms.map((t) => `\`${t}\``).join(", ")}_` : "";
|
|
304
|
-
return {
|
|
305
|
-
content: [
|
|
306
|
-
{
|
|
307
|
-
type: "text",
|
|
308
|
-
text: `${graphWarning ? `${graphWarning}
|
|
10
|
+
${n.content}`}).join(`
|
|
309
11
|
|
|
310
|
-
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
`),T=d==="hybrid"?"hybrid (vector + keyword RRF)":d==="keyword"?"keyword (FTS)":"semantic (vector)",R=I(c,e),j=R.length>0?`
|
|
15
|
+
_Distinctive terms: ${R.map(u=>`\`${u}\``).join(", ")}_`:"";return{content:[{type:"text",text:`${x?`${x}
|
|
16
|
+
|
|
17
|
+
`:""}${M}
|
|
311
18
|
|
|
312
19
|
---
|
|
313
|
-
_Search mode: ${
|
|
314
|
-
_Next: Use \`lookup\` to see all chunks from a specific file, or \`analyze_structure\` to understand the codebase layout._`
|
|
315
|
-
}
|
|
316
|
-
]
|
|
317
|
-
};
|
|
318
|
-
} catch (err) {
|
|
319
|
-
console.error("[KB] Search failed:", err);
|
|
320
|
-
return {
|
|
321
|
-
content: [{ type: "text", text: `Search failed: ${err.message}` }],
|
|
322
|
-
isError: true
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
export {
|
|
329
|
-
registerSearchTool
|
|
330
|
-
};
|
|
331
|
-
//# sourceMappingURL=search.tool.js.map
|
|
20
|
+
_Search mode: ${T} | ${c.length} results_${j}
|
|
21
|
+
_Next: Use \`lookup\` to see all chunks from a specific file, or \`analyze_structure\` to understand the codebase layout._`}]}}catch(p){return console.error("[KB] Search failed:",p),{content:[{type:"text",text:`Search failed: ${p.message}`}],isError:!0}}})}export{O as registerSearchTool};
|
|
@@ -1,68 +1,3 @@
|
|
|
1
|
-
function
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
{
|
|
5
|
-
description: "Get the current status and statistics of the knowledge base index."
|
|
6
|
-
},
|
|
7
|
-
async () => {
|
|
8
|
-
try {
|
|
9
|
-
const stats = await store.getStats();
|
|
10
|
-
const paths = await store.listSourcePaths();
|
|
11
|
-
const text = [
|
|
12
|
-
"## Knowledge Base Status",
|
|
13
|
-
"",
|
|
14
|
-
`- **Total Records**: ${stats.totalRecords}`,
|
|
15
|
-
`- **Total Files**: ${stats.totalFiles}`,
|
|
16
|
-
`- **Last Indexed**: ${stats.lastIndexedAt ?? "Never"}`,
|
|
17
|
-
"",
|
|
18
|
-
"### Content Types",
|
|
19
|
-
...Object.entries(stats.contentTypeBreakdown).map(
|
|
20
|
-
([type, count]) => `- ${type}: ${count}`
|
|
21
|
-
),
|
|
22
|
-
"",
|
|
23
|
-
"### Indexed Files",
|
|
24
|
-
...paths.slice(0, 50).map((p) => `- ${p}`),
|
|
25
|
-
paths.length > 50 ? `
|
|
26
|
-
... and ${paths.length - 50} more files` : ""
|
|
27
|
-
];
|
|
28
|
-
if (graphStore) {
|
|
29
|
-
try {
|
|
30
|
-
const graphStats = await graphStore.getStats();
|
|
31
|
-
text.push(
|
|
32
|
-
"",
|
|
33
|
-
"### Knowledge Graph",
|
|
34
|
-
`- **Nodes**: ${graphStats.nodeCount}`,
|
|
35
|
-
`- **Edges**: ${graphStats.edgeCount}`,
|
|
36
|
-
...Object.entries(graphStats.nodeTypes).map(
|
|
37
|
-
([type, count]) => ` - ${type}: ${count}`
|
|
38
|
-
)
|
|
39
|
-
);
|
|
40
|
-
} catch {
|
|
41
|
-
text.push("", "### Knowledge Graph", "- Graph store unavailable");
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const output = text.join("\n");
|
|
45
|
-
return {
|
|
46
|
-
content: [
|
|
47
|
-
{
|
|
48
|
-
type: "text",
|
|
49
|
-
text: output + "\n\n---\n_Next: Use `search` to query indexed content, `graph(stats)` to explore the knowledge graph, or `reindex` to refresh the index._"
|
|
50
|
-
}
|
|
51
|
-
]
|
|
52
|
-
};
|
|
53
|
-
} catch (err) {
|
|
54
|
-
console.error("[KB] Status failed:", err);
|
|
55
|
-
return {
|
|
56
|
-
content: [
|
|
57
|
-
{ type: "text", text: `Status check failed: ${err.message}` }
|
|
58
|
-
],
|
|
59
|
-
isError: true
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
export {
|
|
66
|
-
registerStatusTool
|
|
67
|
-
};
|
|
68
|
-
//# sourceMappingURL=status.tool.js.map
|
|
1
|
+
function p(d,n,a){d.registerTool("status",{description:"Get the current status and statistics of the knowledge base index."},async()=>{try{const e=await n.getStats(),o=await n.listSourcePaths(),s=["## Knowledge Base Status","",`- **Total Records**: ${e.totalRecords}`,`- **Total Files**: ${e.totalFiles}`,`- **Last Indexed**: ${e.lastIndexedAt??"Never"}`,"","### Content Types",...Object.entries(e.contentTypeBreakdown).map(([t,r])=>`- ${t}: ${r}`),"","### Indexed Files",...o.slice(0,50).map(t=>`- ${t}`),o.length>50?`
|
|
2
|
+
... and ${o.length-50} more files`:""];if(a)try{const t=await a.getStats();s.push("","### Knowledge Graph",`- **Nodes**: ${t.nodeCount}`,`- **Edges**: ${t.edgeCount}`,...Object.entries(t.nodeTypes).map(([r,c])=>` - ${r}: ${c}`))}catch{s.push("","### Knowledge Graph","- Graph store unavailable")}return{content:[{type:"text",text:s.join(`
|
|
3
|
+
`)+"\n\n---\n_Next: Use `search` to query indexed content, `graph(stats)` to explore the knowledge graph, or `reindex` to refresh the index._"}]}}catch(e){return console.error("[KB] Status failed:",e),{content:[{type:"text",text:`Status check failed: ${e.message}`}],isError:!0}}})}export{p as registerStatusTool};
|