@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.
Files changed (138) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/packages/analyzers/dist/blast-radius-analyzer.js +13 -114
  4. package/packages/analyzers/dist/dependency-analyzer.js +11 -425
  5. package/packages/analyzers/dist/diagram-generator.js +4 -86
  6. package/packages/analyzers/dist/entry-point-analyzer.js +5 -239
  7. package/packages/analyzers/dist/index.js +1 -23
  8. package/packages/analyzers/dist/knowledge-producer.js +24 -113
  9. package/packages/analyzers/dist/pattern-analyzer.js +5 -359
  10. package/packages/analyzers/dist/regex-call-graph.js +1 -428
  11. package/packages/analyzers/dist/structure-analyzer.js +4 -258
  12. package/packages/analyzers/dist/symbol-analyzer.js +13 -442
  13. package/packages/analyzers/dist/ts-call-graph.js +1 -160
  14. package/packages/analyzers/dist/types.js +0 -1
  15. package/packages/chunker/dist/call-graph-extractor.js +1 -90
  16. package/packages/chunker/dist/chunker-factory.js +1 -36
  17. package/packages/chunker/dist/chunker.interface.js +0 -1
  18. package/packages/chunker/dist/code-chunker.js +14 -134
  19. package/packages/chunker/dist/generic-chunker.js +5 -72
  20. package/packages/chunker/dist/index.js +1 -21
  21. package/packages/chunker/dist/markdown-chunker.js +7 -119
  22. package/packages/chunker/dist/treesitter-chunker.js +8 -234
  23. package/packages/cli/dist/commands/analyze.js +3 -112
  24. package/packages/cli/dist/commands/context-cmds.js +1 -155
  25. package/packages/cli/dist/commands/environment.js +2 -204
  26. package/packages/cli/dist/commands/execution.js +1 -137
  27. package/packages/cli/dist/commands/graph.js +7 -81
  28. package/packages/cli/dist/commands/init.js +9 -87
  29. package/packages/cli/dist/commands/knowledge.js +1 -139
  30. package/packages/cli/dist/commands/search.js +8 -267
  31. package/packages/cli/dist/commands/system.js +4 -241
  32. package/packages/cli/dist/commands/workspace.js +2 -388
  33. package/packages/cli/dist/context.js +1 -14
  34. package/packages/cli/dist/helpers.js +3 -458
  35. package/packages/cli/dist/index.d.ts +1 -1
  36. package/packages/cli/dist/index.js +3 -69
  37. package/packages/cli/dist/kb-init.js +1 -82
  38. package/packages/cli/dist/types.js +0 -1
  39. package/packages/core/dist/constants.js +1 -43
  40. package/packages/core/dist/content-detector.js +1 -79
  41. package/packages/core/dist/errors.js +1 -40
  42. package/packages/core/dist/index.js +1 -9
  43. package/packages/core/dist/logger.js +1 -34
  44. package/packages/core/dist/types.js +0 -1
  45. package/packages/embeddings/dist/embedder.interface.js +0 -1
  46. package/packages/embeddings/dist/index.js +1 -5
  47. package/packages/embeddings/dist/onnx-embedder.js +1 -82
  48. package/packages/indexer/dist/file-hasher.js +1 -13
  49. package/packages/indexer/dist/filesystem-crawler.js +1 -125
  50. package/packages/indexer/dist/graph-extractor.js +1 -111
  51. package/packages/indexer/dist/incremental-indexer.js +1 -278
  52. package/packages/indexer/dist/index.js +1 -14
  53. package/packages/server/dist/api.js +1 -9
  54. package/packages/server/dist/config.js +1 -75
  55. package/packages/server/dist/curated-manager.js +9 -356
  56. package/packages/server/dist/index.js +1 -134
  57. package/packages/server/dist/replay-interceptor.js +1 -38
  58. package/packages/server/dist/resources/resources.js +2 -40
  59. package/packages/server/dist/server.js +1 -247
  60. package/packages/server/dist/tools/analyze.tools.js +1 -288
  61. package/packages/server/dist/tools/forge.tools.js +11 -499
  62. package/packages/server/dist/tools/forget.tool.js +3 -39
  63. package/packages/server/dist/tools/graph.tool.js +5 -110
  64. package/packages/server/dist/tools/list.tool.js +5 -53
  65. package/packages/server/dist/tools/lookup.tool.js +8 -51
  66. package/packages/server/dist/tools/onboard.tool.js +2 -112
  67. package/packages/server/dist/tools/produce.tool.js +4 -74
  68. package/packages/server/dist/tools/read.tool.js +4 -47
  69. package/packages/server/dist/tools/reindex.tool.js +2 -70
  70. package/packages/server/dist/tools/remember.tool.js +3 -42
  71. package/packages/server/dist/tools/replay.tool.js +6 -88
  72. package/packages/server/dist/tools/search.tool.js +17 -327
  73. package/packages/server/dist/tools/status.tool.js +3 -68
  74. package/packages/server/dist/tools/toolkit.tools.js +20 -1673
  75. package/packages/server/dist/tools/update.tool.js +3 -39
  76. package/packages/server/dist/tools/utility.tools.js +19 -456
  77. package/packages/store/dist/graph-store.interface.js +0 -1
  78. package/packages/store/dist/index.js +1 -9
  79. package/packages/store/dist/lance-store.js +1 -258
  80. package/packages/store/dist/sqlite-graph-store.js +8 -309
  81. package/packages/store/dist/store-factory.js +1 -14
  82. package/packages/store/dist/store.interface.js +0 -1
  83. package/packages/tools/dist/batch.js +1 -45
  84. package/packages/tools/dist/changelog.js +2 -112
  85. package/packages/tools/dist/check.js +2 -59
  86. package/packages/tools/dist/checkpoint.js +2 -43
  87. package/packages/tools/dist/codemod.js +2 -69
  88. package/packages/tools/dist/compact.js +3 -60
  89. package/packages/tools/dist/data-transform.js +1 -124
  90. package/packages/tools/dist/dead-symbols.js +2 -71
  91. package/packages/tools/dist/delegate.js +3 -128
  92. package/packages/tools/dist/diff-parse.js +3 -153
  93. package/packages/tools/dist/digest.js +7 -242
  94. package/packages/tools/dist/encode.js +1 -46
  95. package/packages/tools/dist/env-info.js +1 -58
  96. package/packages/tools/dist/eval.js +3 -79
  97. package/packages/tools/dist/evidence-map.js +3 -203
  98. package/packages/tools/dist/file-summary.js +2 -106
  99. package/packages/tools/dist/file-walk.js +1 -75
  100. package/packages/tools/dist/find-examples.js +3 -48
  101. package/packages/tools/dist/find.js +1 -120
  102. package/packages/tools/dist/forge-classify.js +2 -319
  103. package/packages/tools/dist/forge-ground.js +1 -184
  104. package/packages/tools/dist/git-context.js +3 -46
  105. package/packages/tools/dist/graph-query.js +1 -194
  106. package/packages/tools/dist/health.js +1 -118
  107. package/packages/tools/dist/http-request.js +1 -58
  108. package/packages/tools/dist/index.js +1 -273
  109. package/packages/tools/dist/lane.js +7 -227
  110. package/packages/tools/dist/measure.js +2 -119
  111. package/packages/tools/dist/onboard.js +42 -1136
  112. package/packages/tools/dist/parse-output.js +2 -158
  113. package/packages/tools/dist/process-manager.js +1 -69
  114. package/packages/tools/dist/queue.js +2 -126
  115. package/packages/tools/dist/regex-test.js +1 -39
  116. package/packages/tools/dist/rename.js +2 -70
  117. package/packages/tools/dist/replay.js +6 -108
  118. package/packages/tools/dist/schema-validate.js +1 -141
  119. package/packages/tools/dist/scope-map.js +1 -72
  120. package/packages/tools/dist/snippet.js +1 -80
  121. package/packages/tools/dist/stash.js +2 -60
  122. package/packages/tools/dist/stratum-card.js +5 -238
  123. package/packages/tools/dist/symbol.js +3 -87
  124. package/packages/tools/dist/test-run.js +2 -55
  125. package/packages/tools/dist/text-utils.js +2 -31
  126. package/packages/tools/dist/time-utils.js +1 -135
  127. package/packages/tools/dist/trace.js +2 -114
  128. package/packages/tools/dist/truncation.js +10 -41
  129. package/packages/tools/dist/watch.js +1 -61
  130. package/packages/tools/dist/web-fetch.js +9 -244
  131. package/packages/tools/dist/web-search.js +1 -46
  132. package/packages/tools/dist/workset.js +2 -77
  133. package/packages/tui/dist/App.js +260 -52468
  134. package/packages/tui/dist/index.js +286 -54551
  135. package/packages/tui/dist/panels/CuratedPanel.js +211 -34291
  136. package/packages/tui/dist/panels/LogPanel.js +259 -51703
  137. package/packages/tui/dist/panels/SearchPanel.js +212 -34824
  138. 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"../../../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
- ${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 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
- ${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=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: ${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_${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 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(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};