@vpxa/kb 0.1.1

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