@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.
Files changed (136) hide show
  1. package/package.json +1 -1
  2. package/packages/analyzers/dist/blast-radius-analyzer.js +13 -114
  3. package/packages/analyzers/dist/dependency-analyzer.js +11 -425
  4. package/packages/analyzers/dist/diagram-generator.js +4 -86
  5. package/packages/analyzers/dist/entry-point-analyzer.js +5 -239
  6. package/packages/analyzers/dist/index.js +1 -23
  7. package/packages/analyzers/dist/knowledge-producer.js +24 -113
  8. package/packages/analyzers/dist/pattern-analyzer.js +5 -359
  9. package/packages/analyzers/dist/regex-call-graph.js +1 -428
  10. package/packages/analyzers/dist/structure-analyzer.js +4 -258
  11. package/packages/analyzers/dist/symbol-analyzer.js +13 -442
  12. package/packages/analyzers/dist/ts-call-graph.js +1 -160
  13. package/packages/analyzers/dist/types.js +0 -1
  14. package/packages/chunker/dist/call-graph-extractor.js +1 -90
  15. package/packages/chunker/dist/chunker-factory.js +1 -36
  16. package/packages/chunker/dist/chunker.interface.js +0 -1
  17. package/packages/chunker/dist/code-chunker.js +14 -134
  18. package/packages/chunker/dist/generic-chunker.js +5 -72
  19. package/packages/chunker/dist/index.js +1 -21
  20. package/packages/chunker/dist/markdown-chunker.js +7 -119
  21. package/packages/chunker/dist/treesitter-chunker.js +8 -234
  22. package/packages/cli/dist/commands/analyze.js +3 -112
  23. package/packages/cli/dist/commands/context-cmds.js +1 -155
  24. package/packages/cli/dist/commands/environment.js +2 -204
  25. package/packages/cli/dist/commands/execution.js +1 -137
  26. package/packages/cli/dist/commands/graph.js +7 -81
  27. package/packages/cli/dist/commands/init.js +9 -87
  28. package/packages/cli/dist/commands/knowledge.js +1 -139
  29. package/packages/cli/dist/commands/search.js +8 -267
  30. package/packages/cli/dist/commands/system.js +4 -241
  31. package/packages/cli/dist/commands/workspace.js +2 -388
  32. package/packages/cli/dist/context.js +1 -14
  33. package/packages/cli/dist/helpers.js +3 -458
  34. package/packages/cli/dist/index.js +3 -69
  35. package/packages/cli/dist/kb-init.js +1 -82
  36. package/packages/cli/dist/types.js +0 -1
  37. package/packages/core/dist/constants.js +1 -43
  38. package/packages/core/dist/content-detector.js +1 -79
  39. package/packages/core/dist/errors.js +1 -40
  40. package/packages/core/dist/index.js +1 -9
  41. package/packages/core/dist/logger.js +1 -34
  42. package/packages/core/dist/types.js +0 -1
  43. package/packages/embeddings/dist/embedder.interface.js +0 -1
  44. package/packages/embeddings/dist/index.js +1 -5
  45. package/packages/embeddings/dist/onnx-embedder.js +1 -82
  46. package/packages/indexer/dist/file-hasher.js +1 -13
  47. package/packages/indexer/dist/filesystem-crawler.js +1 -125
  48. package/packages/indexer/dist/graph-extractor.js +1 -111
  49. package/packages/indexer/dist/incremental-indexer.js +1 -278
  50. package/packages/indexer/dist/index.js +1 -14
  51. package/packages/server/dist/api.js +1 -9
  52. package/packages/server/dist/config.js +1 -75
  53. package/packages/server/dist/curated-manager.js +9 -356
  54. package/packages/server/dist/index.js +1 -134
  55. package/packages/server/dist/replay-interceptor.js +1 -38
  56. package/packages/server/dist/resources/resources.js +2 -40
  57. package/packages/server/dist/server.js +1 -247
  58. package/packages/server/dist/tools/analyze.tools.js +1 -288
  59. package/packages/server/dist/tools/forge.tools.js +11 -499
  60. package/packages/server/dist/tools/forget.tool.js +3 -39
  61. package/packages/server/dist/tools/graph.tool.js +5 -110
  62. package/packages/server/dist/tools/list.tool.js +5 -53
  63. package/packages/server/dist/tools/lookup.tool.js +8 -51
  64. package/packages/server/dist/tools/onboard.tool.js +2 -112
  65. package/packages/server/dist/tools/produce.tool.js +4 -74
  66. package/packages/server/dist/tools/read.tool.js +4 -47
  67. package/packages/server/dist/tools/reindex.tool.js +2 -70
  68. package/packages/server/dist/tools/remember.tool.js +3 -42
  69. package/packages/server/dist/tools/replay.tool.js +6 -88
  70. package/packages/server/dist/tools/search.tool.js +17 -327
  71. package/packages/server/dist/tools/status.tool.js +3 -68
  72. package/packages/server/dist/tools/toolkit.tools.js +20 -1673
  73. package/packages/server/dist/tools/update.tool.js +3 -39
  74. package/packages/server/dist/tools/utility.tools.js +19 -456
  75. package/packages/store/dist/graph-store.interface.js +0 -1
  76. package/packages/store/dist/index.js +1 -9
  77. package/packages/store/dist/lance-store.js +1 -258
  78. package/packages/store/dist/sqlite-graph-store.js +8 -309
  79. package/packages/store/dist/store-factory.js +1 -14
  80. package/packages/store/dist/store.interface.js +0 -1
  81. package/packages/tools/dist/batch.js +1 -45
  82. package/packages/tools/dist/changelog.js +2 -112
  83. package/packages/tools/dist/check.js +2 -59
  84. package/packages/tools/dist/checkpoint.js +2 -43
  85. package/packages/tools/dist/codemod.js +2 -69
  86. package/packages/tools/dist/compact.js +3 -60
  87. package/packages/tools/dist/data-transform.js +1 -124
  88. package/packages/tools/dist/dead-symbols.js +2 -71
  89. package/packages/tools/dist/delegate.js +3 -128
  90. package/packages/tools/dist/diff-parse.js +3 -153
  91. package/packages/tools/dist/digest.js +7 -242
  92. package/packages/tools/dist/encode.js +1 -46
  93. package/packages/tools/dist/env-info.js +1 -58
  94. package/packages/tools/dist/eval.js +3 -79
  95. package/packages/tools/dist/evidence-map.js +3 -203
  96. package/packages/tools/dist/file-summary.js +2 -106
  97. package/packages/tools/dist/file-walk.js +1 -75
  98. package/packages/tools/dist/find-examples.js +3 -48
  99. package/packages/tools/dist/find.js +1 -120
  100. package/packages/tools/dist/forge-classify.js +2 -319
  101. package/packages/tools/dist/forge-ground.js +1 -184
  102. package/packages/tools/dist/git-context.js +3 -46
  103. package/packages/tools/dist/graph-query.js +1 -194
  104. package/packages/tools/dist/health.js +1 -118
  105. package/packages/tools/dist/http-request.js +1 -58
  106. package/packages/tools/dist/index.js +1 -273
  107. package/packages/tools/dist/lane.js +7 -227
  108. package/packages/tools/dist/measure.js +2 -119
  109. package/packages/tools/dist/onboard.js +42 -1136
  110. package/packages/tools/dist/parse-output.js +2 -158
  111. package/packages/tools/dist/process-manager.js +1 -69
  112. package/packages/tools/dist/queue.js +2 -126
  113. package/packages/tools/dist/regex-test.js +1 -39
  114. package/packages/tools/dist/rename.js +2 -70
  115. package/packages/tools/dist/replay.js +6 -108
  116. package/packages/tools/dist/schema-validate.js +1 -141
  117. package/packages/tools/dist/scope-map.js +1 -72
  118. package/packages/tools/dist/snippet.js +1 -80
  119. package/packages/tools/dist/stash.js +2 -60
  120. package/packages/tools/dist/stratum-card.js +5 -238
  121. package/packages/tools/dist/symbol.js +3 -87
  122. package/packages/tools/dist/test-run.js +2 -55
  123. package/packages/tools/dist/text-utils.js +2 -31
  124. package/packages/tools/dist/time-utils.js +1 -135
  125. package/packages/tools/dist/trace.js +2 -114
  126. package/packages/tools/dist/truncation.js +10 -41
  127. package/packages/tools/dist/watch.js +1 -61
  128. package/packages/tools/dist/web-fetch.js +9 -244
  129. package/packages/tools/dist/web-search.js +1 -46
  130. package/packages/tools/dist/workset.js +2 -77
  131. package/packages/tui/dist/App.js +260 -52468
  132. package/packages/tui/dist/index.js +286 -54551
  133. package/packages/tui/dist/panels/CuratedPanel.js +211 -34291
  134. package/packages/tui/dist/panels/LogPanel.js +259 -51703
  135. package/packages/tui/dist/panels/SearchPanel.js +212 -34824
  136. package/packages/tui/dist/panels/StatusPanel.js +211 -34304
@@ -1,89 +1,7 @@
1
- import { replayClear, replayList, replayTrim } from "@kb/tools";
2
- import { z } from "zod";
3
- function registerReplayTool(server) {
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"@kb/tools";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
- ${lines.join("\n\n")}`
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 { graphAugmentSearch } from "@kb/tools";
2
- import { z } from "zod";
3
- function reciprocalRankFusion(vectorResults, ftsResults, k = 60) {
4
- const merged = /* @__PURE__ */ new Map();
5
- for (let i = 0; i < vectorResults.length; i++) {
6
- const r = vectorResults[i];
7
- merged.set(r.record.id, { record: r.record, score: 1 / (k + i + 1) });
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 F}from"@kb/tools";import{z as f}from"zod";function j(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],l=o.get(t.record.id);l?l.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 l=e.length;for(const i of t[0]){let h=i,r=i+a[0].length;for(let d=1;d<t.length;d++){let g=t[d][0],p=Math.abs(g-i);for(let c=1;c<t[d].length;c++){const w=Math.abs(t[d][c]-i);w<p&&(p=w,g=t[d][c])}h=Math.min(h,g),r=Math.max(r,g+a[d].length)}l=Math.min(l,r-h)}const s=1+.25/(1+l/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 d=r.toLowerCase();/[_A-Z]/.test(r)&&e.set(`__id__${d}`,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 l=[];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,d=s.length>8?.5:0;l.push({term:s,score:h+r+d})}return l.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:l,content_type:s,origin:i,category:h,tags:r,min_score:d,graph_hops:g})=>{try{const p={limit:t,minScore:d,contentType:s,origin:i,category:h,tags:r};let c;if(l==="keyword")c=await a.ftsSearch(e,p),c=c.slice(0,t);else if(l==="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=j(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,$;if(g>0&&!o&&($="> **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 F(o,u,{hops:g,maxPerHit:5});w=new Map;for(const n of S)if(n.graphContext.nodes.length>0){const x=n.graphContext.nodes.slice(0,5).map(b=>` - **${b.name}** (${b.type})`).join(`
2
+ `),k=n.graphContext.edges.slice(0,5).map(b=>` - ${b.fromId} \u2014[${b.type}]\u2192 ${b.toId}`).join(`
3
+ `),v=[`- **Graph Context** (${g} hop${g>1?"s":""}):`];x&&v.push(` Entities:
4
+ ${x}`),k&&v.push(` Relationships:
5
+ ${k}`),w.set(n.recordId,v.join(`
6
+ `))}}catch(u){console.error("[KB] Graph augmentation failed (non-fatal):",u),$="> **Note:** Graph augmentation failed. Results shown without graph context."}const M=c.map((u,S)=>{const n=u.record,x=`### Result ${S+1} (score: ${u.score.toFixed(3)})`,k=[`- **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`${x}
8
+ ${k}
297
9
 
298
- ${rec.content}`;
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
- ` : ""}${formatted}
12
+ ---
13
+
14
+ `),T=l==="hybrid"?"hybrid (vector + keyword RRF)":l==="keyword"?"keyword (FTS)":"semantic (vector)",R=I(c,e),C=R.length>0?`
15
+ _Distinctive terms: ${R.map(u=>`\`${u}\``).join(", ")}_`:"";return{content:[{type:"text",text:`${$?`${$}
16
+
17
+ `:""}${M}
311
18
 
312
19
  ---
313
- _Search mode: ${modeLabel} | ${results.length} results_${termsHint}
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_${C}
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 registerStatusTool(server, store, graphStore) {
2
- server.registerTool(
3
- "status",
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(c,n,a){c.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,d])=>` - ${r}: ${d}`))}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};