@ai-devkit/memory 0.1.0

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 (59) hide show
  1. package/README.md +149 -0
  2. package/dist/api.d.ts +20 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +46 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/database/connection.d.ts +27 -0
  7. package/dist/database/connection.d.ts.map +1 -0
  8. package/dist/database/connection.js +107 -0
  9. package/dist/database/connection.js.map +1 -0
  10. package/dist/database/index.d.ts +4 -0
  11. package/dist/database/index.d.ts.map +1 -0
  12. package/dist/database/index.js +37 -0
  13. package/dist/database/index.js.map +1 -0
  14. package/dist/database/migrations/001_initial.sql +50 -0
  15. package/dist/database/schema.d.ts +6 -0
  16. package/dist/database/schema.d.ts.map +1 -0
  17. package/dist/database/schema.js +89 -0
  18. package/dist/database/schema.js.map +1 -0
  19. package/dist/handlers/search.d.ts +3 -0
  20. package/dist/handlers/search.d.ts.map +1 -0
  21. package/dist/handlers/search.js +78 -0
  22. package/dist/handlers/search.js.map +1 -0
  23. package/dist/handlers/store.d.ts +3 -0
  24. package/dist/handlers/store.d.ts.map +1 -0
  25. package/dist/handlers/store.js +71 -0
  26. package/dist/handlers/store.js.map +1 -0
  27. package/dist/index.d.ts +3 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +93 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/server.d.ts +4 -0
  32. package/dist/server.d.ts.map +1 -0
  33. package/dist/server.js +172 -0
  34. package/dist/server.js.map +1 -0
  35. package/dist/services/normalizer.d.ts +21 -0
  36. package/dist/services/normalizer.d.ts.map +1 -0
  37. package/dist/services/normalizer.js +52 -0
  38. package/dist/services/normalizer.js.map +1 -0
  39. package/dist/services/ranker.d.ts +21 -0
  40. package/dist/services/ranker.d.ts.map +1 -0
  41. package/dist/services/ranker.js +70 -0
  42. package/dist/services/ranker.js.map +1 -0
  43. package/dist/services/search.d.ts +29 -0
  44. package/dist/services/search.d.ts.map +1 -0
  45. package/dist/services/search.js +92 -0
  46. package/dist/services/search.js.map +1 -0
  47. package/dist/services/validator.d.ts +11 -0
  48. package/dist/services/validator.d.ts.map +1 -0
  49. package/dist/services/validator.js +144 -0
  50. package/dist/services/validator.js.map +1 -0
  51. package/dist/types/index.d.ts +58 -0
  52. package/dist/types/index.d.ts.map +1 -0
  53. package/dist/types/index.js +6 -0
  54. package/dist/types/index.js.map +1 -0
  55. package/dist/utils/errors.d.ts +21 -0
  56. package/dist/utils/errors.d.ts.map +1 -0
  57. package/dist/utils/errors.js +76 -0
  58. package/dist/utils/errors.js.map +1 -0
  59. package/package.json +67 -0
package/README.md ADDED
@@ -0,0 +1,149 @@
1
+ # @ai-devkit/memory
2
+
3
+ A lightweight MCP-based memory service for AI agents. Store and retrieve actionable knowledge using SQLite with FTS5 full-text search.
4
+
5
+ ## Features
6
+
7
+ - 🔍 **Full-Text Search** - FTS5 with BM25 ranking
8
+ - 🏷️ **Tag-Based Filtering** - Boost results by contextTags
9
+ - 📁 **Scoped Knowledge** - global, project, or repo-specific rules
10
+ - 🔄 **Deduplication** - Prevents duplicate content
11
+ - ⚡ **Fast** - SQLite with WAL mode, <50ms search latency
12
+ - 📦 **Portable** - Single database file, no external dependencies
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @ai-devkit/memory
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ### As MCP Server
23
+
24
+ Add to your MCP client configuration:
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "memory": {
30
+ "command": "npx",
31
+ "args": ["@ai-devkit/memory"]
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ ### Using the Tools
38
+
39
+ #### Store Knowledge
40
+
41
+ ```json
42
+ {
43
+ "tool": "memory.storeKnowledge",
44
+ "arguments": {
45
+ "title": "Always use Response DTOs for API endpoints",
46
+ "content": "When building REST APIs, always use Response DTOs instead of returning domain entities directly. This provides better API versioning, security, and decoupling.",
47
+ "tags": ["api", "backend", "dto"],
48
+ "scope": "global"
49
+ }
50
+ }
51
+ ```
52
+
53
+ #### Search Knowledge
54
+
55
+ ```json
56
+ {
57
+ "tool": "memory.searchKnowledge",
58
+ "arguments": {
59
+ "query": "building an API endpoint",
60
+ "contextTags": ["api"],
61
+ "limit": 5
62
+ }
63
+ }
64
+ ```
65
+
66
+ ### CLI Commands
67
+
68
+ You can also use the CLI directly:
69
+
70
+ ```bash
71
+ # Store knowledge
72
+ ai-devkit-memory store \
73
+ -t "Always use Response DTOs for API endpoints" \
74
+ -c "When building REST APIs, always use Response DTOs..." \
75
+ --tags "api,backend,dto" \
76
+ -s global
77
+
78
+ # Search knowledge
79
+ ai-devkit-memory search -q "API best practices" --tags "api" -l 5
80
+
81
+ # Start MCP server (default)
82
+ ai-devkit-memory serve
83
+ # or just:
84
+ ai-devkit-memory
85
+ ```
86
+
87
+ ## API Reference
88
+
89
+ ### `memory.storeKnowledge`
90
+
91
+ Store a new knowledge item.
92
+
93
+ | Parameter | Type | Required | Description |
94
+ |-----------|------|----------|-------------|
95
+ | `title` | string | ✅ | Short description (10-100 chars) |
96
+ | `content` | string | ✅ | Detailed explanation in markdown (50-5000 chars) |
97
+ | `tags` | string[] | ❌ | Domain keywords (max 10) |
98
+ | `scope` | string | ❌ | `global`, `project:<name>`, or `repo:<name>` |
99
+
100
+ ### `memory.searchKnowledge`
101
+
102
+ Search for relevant knowledge.
103
+
104
+ | Parameter | Type | Required | Description |
105
+ |-----------|------|----------|-------------|
106
+ | `query` | string | ✅ | Natural language task description (3-500 chars) |
107
+ | `contextTags` | string[] | ❌ | Tags to boost matching results |
108
+ | `scope` | string | ❌ | Scope filter (project results prioritized) |
109
+ | `limit` | number | ❌ | Max results (1-20, default: 5) |
110
+
111
+ ## Ranking Algorithm
112
+
113
+ Results are ranked using:
114
+
115
+ ```
116
+ final_score = bm25_score × tag_boost + scope_boost
117
+
118
+ Where:
119
+ bm25_score = FTS5 bm25() with column weights (title=10, content=5, tags=1)
120
+ tag_boost = 1 + (matching_tags × 0.1)
121
+ scope_boost = +0.5 if scope matches, +0.2 if global
122
+ ```
123
+
124
+ ## Database Location
125
+
126
+ Default: `~/.ai-devkit/memory.db`
127
+
128
+ ## Development
129
+
130
+ ```bash
131
+ # Install dependencies
132
+ npm install
133
+
134
+ # Build
135
+ npm run build
136
+
137
+ # Run tests
138
+ npm test
139
+
140
+ # Run with MCP Inspector
141
+ npm run inspect
142
+
143
+ # Start server
144
+ npm run start
145
+ ```
146
+
147
+ ## License
148
+
149
+ MIT
package/dist/api.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { storeKnowledge } from './handlers/store';
2
+ import { searchKnowledge } from './handlers/search';
3
+ import type { StoreKnowledgeInput, SearchKnowledgeInput, StoreKnowledgeResult, SearchKnowledgeResult } from './types';
4
+ export { storeKnowledge, searchKnowledge };
5
+ export type { StoreKnowledgeInput, SearchKnowledgeInput, StoreKnowledgeResult, SearchKnowledgeResult };
6
+ export interface MemoryStoreOptions {
7
+ title: string;
8
+ content: string;
9
+ tags?: string;
10
+ scope?: string;
11
+ }
12
+ export interface MemorySearchOptions {
13
+ query: string;
14
+ tags?: string;
15
+ scope?: string;
16
+ limit?: number;
17
+ }
18
+ export declare function memoryStoreCommand(options: MemoryStoreOptions): StoreKnowledgeResult;
19
+ export declare function memorySearchCommand(options: MemorySearchOptions): SearchKnowledgeResult;
20
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEtH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC;AAC3C,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,CAAC;AAGvG,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,oBAAoB,CASpF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,GAAG,qBAAqB,CASvF"}
package/dist/api.js ADDED
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get memorySearchCommand () {
13
+ return memorySearchCommand;
14
+ },
15
+ get memoryStoreCommand () {
16
+ return memoryStoreCommand;
17
+ },
18
+ get searchKnowledge () {
19
+ return _search.searchKnowledge;
20
+ },
21
+ get storeKnowledge () {
22
+ return _store.storeKnowledge;
23
+ }
24
+ });
25
+ const _store = require("./handlers/store");
26
+ const _search = require("./handlers/search");
27
+ function memoryStoreCommand(options) {
28
+ const input = {
29
+ title: options.title,
30
+ content: options.content,
31
+ tags: options.tags ? options.tags.split(',').map((t)=>t.trim()) : undefined,
32
+ scope: options.scope
33
+ };
34
+ return (0, _store.storeKnowledge)(input);
35
+ }
36
+ function memorySearchCommand(options) {
37
+ const input = {
38
+ query: options.query,
39
+ contextTags: options.tags ? options.tags.split(',').map((t)=>t.trim()) : undefined,
40
+ scope: options.scope,
41
+ limit: options.limit
42
+ };
43
+ return (0, _search.searchKnowledge)(input);
44
+ }
45
+
46
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api.ts"],"sourcesContent":["import { storeKnowledge } from './handlers/store';\nimport { searchKnowledge } from './handlers/search';\nimport type { StoreKnowledgeInput, SearchKnowledgeInput, StoreKnowledgeResult, SearchKnowledgeResult } from './types';\n\nexport { storeKnowledge, searchKnowledge };\nexport type { StoreKnowledgeInput, SearchKnowledgeInput, StoreKnowledgeResult, SearchKnowledgeResult };\n\n// CLI command handlers for integration with main ai-devkit CLI\nexport interface MemoryStoreOptions {\n title: string;\n content: string;\n tags?: string;\n scope?: string;\n}\n\nexport interface MemorySearchOptions {\n query: string;\n tags?: string;\n scope?: string;\n limit?: number;\n}\n\nexport function memoryStoreCommand(options: MemoryStoreOptions): StoreKnowledgeResult {\n const input: StoreKnowledgeInput = {\n title: options.title,\n content: options.content,\n tags: options.tags ? options.tags.split(',').map(t => t.trim()) : undefined,\n scope: options.scope,\n };\n\n return storeKnowledge(input);\n}\n\nexport function memorySearchCommand(options: MemorySearchOptions): SearchKnowledgeResult {\n const input: SearchKnowledgeInput = {\n query: options.query,\n contextTags: options.tags ? options.tags.split(',').map(t => t.trim()) : undefined,\n scope: options.scope,\n limit: options.limit,\n };\n\n return searchKnowledge(input);\n}\n"],"names":["memorySearchCommand","memoryStoreCommand","searchKnowledge","storeKnowledge","options","input","title","content","tags","split","map","t","trim","undefined","scope","query","contextTags","limit"],"mappings":";;;;;;;;;;;QAiCgBA;eAAAA;;QAXAC;eAAAA;;QAlBSC;eAAAA,uBAAe;;QAA/BC;eAAAA,qBAAc;;;uBAJQ;wBACC;AAqBzB,SAASF,mBAAmBG,OAA2B;IAC1D,MAAMC,QAA6B;QAC/BC,OAAOF,QAAQE,KAAK;QACpBC,SAASH,QAAQG,OAAO;QACxBC,MAAMJ,QAAQI,IAAI,GAAGJ,QAAQI,IAAI,CAACC,KAAK,CAAC,KAAKC,GAAG,CAACC,CAAAA,IAAKA,EAAEC,IAAI,MAAMC;QAClEC,OAAOV,QAAQU,KAAK;IACxB;IAEA,OAAOX,IAAAA,qBAAc,EAACE;AAC1B;AAEO,SAASL,oBAAoBI,OAA4B;IAC5D,MAAMC,QAA8B;QAChCU,OAAOX,QAAQW,KAAK;QACpBC,aAAaZ,QAAQI,IAAI,GAAGJ,QAAQI,IAAI,CAACC,KAAK,CAAC,KAAKC,GAAG,CAACC,CAAAA,IAAKA,EAAEC,IAAI,MAAMC;QACzEC,OAAOV,QAAQU,KAAK;QACpBG,OAAOb,QAAQa,KAAK;IACxB;IAEA,OAAOf,IAAAA,uBAAe,EAACG;AAC3B"}
@@ -0,0 +1,27 @@
1
+ import Database from 'better-sqlite3';
2
+ /**
3
+ * Default database path: ~/.ai-devkit/memory.db
4
+ */
5
+ export declare const DEFAULT_DB_PATH: string;
6
+ export interface DatabaseOptions {
7
+ dbPath?: string;
8
+ verbose?: boolean;
9
+ readonly?: boolean;
10
+ }
11
+ export declare class DatabaseConnection {
12
+ private db;
13
+ private readonly dbPath;
14
+ constructor(options?: DatabaseOptions);
15
+ private configure;
16
+ get instance(): Database.Database;
17
+ get path(): string;
18
+ get isOpen(): boolean;
19
+ query<T>(sql: string, params?: unknown[]): T[];
20
+ queryOne<T>(sql: string, params?: unknown[]): T | undefined;
21
+ execute(sql: string, params?: unknown[]): Database.RunResult;
22
+ transaction<T>(fn: () => T): T;
23
+ close(): void;
24
+ }
25
+ export declare function getDatabase(options?: DatabaseOptions): DatabaseConnection;
26
+ export declare function closeDatabase(): void;
27
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/database/connection.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAKtC;;GAEG;AACH,eAAO,MAAM,eAAe,QAA6C,CAAC;AAE1E,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,qBAAa,kBAAkB;IAC3B,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,GAAE,eAAoB;IAczC,OAAO,CAAC,SAAS;IAQjB,IAAI,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAEhC;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,CAAC,EAAE;IAIlD,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,CAAC,GAAG,SAAS;IAI/D,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,QAAQ,CAAC,SAAS;IAGhE,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IAI9B,KAAK,IAAI,IAAI;CAKhB;AAKD,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,kBAAkB,CAczE;AAED,wBAAgB,aAAa,IAAI,IAAI,CAMpC"}
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get DEFAULT_DB_PATH () {
13
+ return DEFAULT_DB_PATH;
14
+ },
15
+ get DatabaseConnection () {
16
+ return DatabaseConnection;
17
+ },
18
+ get closeDatabase () {
19
+ return closeDatabase;
20
+ },
21
+ get getDatabase () {
22
+ return getDatabase;
23
+ }
24
+ });
25
+ const _bettersqlite3 = /*#__PURE__*/ _interop_require_default(require("better-sqlite3"));
26
+ const _fs = require("fs");
27
+ const _path = require("path");
28
+ const _os = require("os");
29
+ function _interop_require_default(obj) {
30
+ return obj && obj.__esModule ? obj : {
31
+ default: obj
32
+ };
33
+ }
34
+ const DEFAULT_DB_PATH = (0, _path.join)((0, _os.homedir)(), '.ai-devkit', 'memory.db');
35
+ let DatabaseConnection = class DatabaseConnection {
36
+ db;
37
+ dbPath;
38
+ constructor(options = {}){
39
+ this.dbPath = options.dbPath ?? DEFAULT_DB_PATH;
40
+ const dir = (0, _path.dirname)(this.dbPath);
41
+ (0, _fs.mkdirSync)(dir, {
42
+ recursive: true
43
+ });
44
+ this.db = new _bettersqlite3.default(this.dbPath, {
45
+ readonly: options.readonly ?? false,
46
+ verbose: options.verbose ? console.log : undefined
47
+ });
48
+ this.configure();
49
+ }
50
+ configure() {
51
+ this.db.pragma('journal_mode = WAL');
52
+ this.db.pragma('foreign_keys = ON');
53
+ this.db.pragma('synchronous = NORMAL');
54
+ this.db.pragma('busy_timeout = 5000');
55
+ this.db.pragma('mmap_size = 268435456');
56
+ }
57
+ get instance() {
58
+ return this.db;
59
+ }
60
+ get path() {
61
+ return this.dbPath;
62
+ }
63
+ get isOpen() {
64
+ return this.db.open;
65
+ }
66
+ query(sql, params = []) {
67
+ return this.db.prepare(sql).all(...params);
68
+ }
69
+ queryOne(sql, params = []) {
70
+ return this.db.prepare(sql).get(...params);
71
+ }
72
+ execute(sql, params = []) {
73
+ return this.db.prepare(sql).run(...params);
74
+ }
75
+ transaction(fn) {
76
+ return this.db.transaction(fn)();
77
+ }
78
+ close() {
79
+ if (this.db.open) {
80
+ this.db.close();
81
+ }
82
+ }
83
+ };
84
+ let instance = null;
85
+ let schemaInitialized = false;
86
+ function getDatabase(options) {
87
+ if (!instance) {
88
+ instance = new DatabaseConnection(options);
89
+ }
90
+ // Auto-run migrations on first access
91
+ if (!schemaInitialized) {
92
+ // Lazy import to avoid circular dependency
93
+ const { initializeSchema } = require('./schema');
94
+ initializeSchema(instance);
95
+ schemaInitialized = true;
96
+ }
97
+ return instance;
98
+ }
99
+ function closeDatabase() {
100
+ if (instance) {
101
+ instance.close();
102
+ instance = null;
103
+ schemaInitialized = false;
104
+ }
105
+ }
106
+
107
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/database/connection.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { mkdirSync } from 'fs';\nimport { dirname, join } from 'path';\nimport { homedir } from 'os';\n\n/**\n * Default database path: ~/.ai-devkit/memory.db\n */\nexport const DEFAULT_DB_PATH = join(homedir(), '.ai-devkit', 'memory.db');\n\nexport interface DatabaseOptions {\n dbPath?: string;\n verbose?: boolean;\n readonly?: boolean;\n}\n\nexport class DatabaseConnection {\n private db: Database.Database;\n private readonly dbPath: string;\n\n constructor(options: DatabaseOptions = {}) {\n this.dbPath = options.dbPath ?? DEFAULT_DB_PATH;\n\n const dir = dirname(this.dbPath);\n mkdirSync(dir, { recursive: true });\n\n this.db = new Database(this.dbPath, {\n readonly: options.readonly ?? false,\n verbose: options.verbose ? console.log : undefined,\n });\n\n this.configure();\n }\n\n private configure(): void {\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('foreign_keys = ON');\n this.db.pragma('synchronous = NORMAL');\n this.db.pragma('busy_timeout = 5000');\n this.db.pragma('mmap_size = 268435456');\n }\n\n get instance(): Database.Database {\n return this.db;\n }\n\n get path(): string {\n return this.dbPath;\n }\n\n get isOpen(): boolean {\n return this.db.open;\n }\n\n query<T>(sql: string, params: unknown[] = []): T[] {\n return this.db.prepare(sql).all(...params) as T[];\n }\n\n queryOne<T>(sql: string, params: unknown[] = []): T | undefined {\n return this.db.prepare(sql).get(...params) as T | undefined;\n }\n\n execute(sql: string, params: unknown[] = []): Database.RunResult {\n return this.db.prepare(sql).run(...params);\n }\n transaction<T>(fn: () => T): T {\n return this.db.transaction(fn)();\n }\n\n close(): void {\n if (this.db.open) {\n this.db.close();\n }\n }\n}\n\nlet instance: DatabaseConnection | null = null;\nlet schemaInitialized = false;\n\nexport function getDatabase(options?: DatabaseOptions): DatabaseConnection {\n if (!instance) {\n instance = new DatabaseConnection(options);\n }\n\n // Auto-run migrations on first access\n if (!schemaInitialized) {\n // Lazy import to avoid circular dependency\n const { initializeSchema } = require('./schema');\n initializeSchema(instance);\n schemaInitialized = true;\n }\n\n return instance;\n}\n\nexport function closeDatabase(): void {\n if (instance) {\n instance.close();\n instance = null;\n schemaInitialized = false;\n }\n}"],"names":["DEFAULT_DB_PATH","DatabaseConnection","closeDatabase","getDatabase","join","homedir","db","dbPath","options","dir","dirname","mkdirSync","recursive","Database","readonly","verbose","console","log","undefined","configure","pragma","instance","path","isOpen","open","query","sql","params","prepare","all","queryOne","get","execute","run","transaction","fn","close","schemaInitialized","initializeSchema","require"],"mappings":";;;;;;;;;;;QAQaA;eAAAA;;QAQAC;eAAAA;;QA+EGC;eAAAA;;QAhBAC;eAAAA;;;sEA/EK;oBACK;sBACI;oBACN;;;;;;AAKjB,MAAMH,kBAAkBI,IAAAA,UAAI,EAACC,IAAAA,WAAO,KAAI,cAAc;AAQtD,IAAA,AAAMJ,qBAAN,MAAMA;IACDK,GAAsB;IACbC,OAAe;IAEhC,YAAYC,UAA2B,CAAC,CAAC,CAAE;QACvC,IAAI,CAACD,MAAM,GAAGC,QAAQD,MAAM,IAAIP;QAEhC,MAAMS,MAAMC,IAAAA,aAAO,EAAC,IAAI,CAACH,MAAM;QAC/BI,IAAAA,aAAS,EAACF,KAAK;YAAEG,WAAW;QAAK;QAEjC,IAAI,CAACN,EAAE,GAAG,IAAIO,sBAAQ,CAAC,IAAI,CAACN,MAAM,EAAE;YAChCO,UAAUN,QAAQM,QAAQ,IAAI;YAC9BC,SAASP,QAAQO,OAAO,GAAGC,QAAQC,GAAG,GAAGC;QAC7C;QAEA,IAAI,CAACC,SAAS;IAClB;IAEQA,YAAkB;QACtB,IAAI,CAACb,EAAE,CAACc,MAAM,CAAC;QACf,IAAI,CAACd,EAAE,CAACc,MAAM,CAAC;QACf,IAAI,CAACd,EAAE,CAACc,MAAM,CAAC;QACf,IAAI,CAACd,EAAE,CAACc,MAAM,CAAC;QACf,IAAI,CAACd,EAAE,CAACc,MAAM,CAAC;IACnB;IAEA,IAAIC,WAA8B;QAC9B,OAAO,IAAI,CAACf,EAAE;IAClB;IAEA,IAAIgB,OAAe;QACf,OAAO,IAAI,CAACf,MAAM;IACtB;IAEA,IAAIgB,SAAkB;QAClB,OAAO,IAAI,CAACjB,EAAE,CAACkB,IAAI;IACvB;IAEAC,MAASC,GAAW,EAAEC,SAAoB,EAAE,EAAO;QAC/C,OAAO,IAAI,CAACrB,EAAE,CAACsB,OAAO,CAACF,KAAKG,GAAG,IAAIF;IACvC;IAEAG,SAAYJ,GAAW,EAAEC,SAAoB,EAAE,EAAiB;QAC5D,OAAO,IAAI,CAACrB,EAAE,CAACsB,OAAO,CAACF,KAAKK,GAAG,IAAIJ;IACvC;IAEAK,QAAQN,GAAW,EAAEC,SAAoB,EAAE,EAAsB;QAC7D,OAAO,IAAI,CAACrB,EAAE,CAACsB,OAAO,CAACF,KAAKO,GAAG,IAAIN;IACvC;IACAO,YAAeC,EAAW,EAAK;QAC3B,OAAO,IAAI,CAAC7B,EAAE,CAAC4B,WAAW,CAACC;IAC/B;IAEAC,QAAc;QACV,IAAI,IAAI,CAAC9B,EAAE,CAACkB,IAAI,EAAE;YACd,IAAI,CAAClB,EAAE,CAAC8B,KAAK;QACjB;IACJ;AACJ;AAEA,IAAIf,WAAsC;AAC1C,IAAIgB,oBAAoB;AAEjB,SAASlC,YAAYK,OAAyB;IACjD,IAAI,CAACa,UAAU;QACXA,WAAW,IAAIpB,mBAAmBO;IACtC;IAEA,sCAAsC;IACtC,IAAI,CAAC6B,mBAAmB;QACpB,2CAA2C;QAC3C,MAAM,EAAEC,gBAAgB,EAAE,GAAGC,QAAQ;QACrCD,iBAAiBjB;QACjBgB,oBAAoB;IACxB;IAEA,OAAOhB;AACX;AAEO,SAASnB;IACZ,IAAImB,UAAU;QACVA,SAASe,KAAK;QACdf,WAAW;QACXgB,oBAAoB;IACxB;AACJ"}
@@ -0,0 +1,4 @@
1
+ export { DatabaseConnection, getDatabase, closeDatabase, DEFAULT_DB_PATH } from './connection';
2
+ export type { DatabaseOptions } from './connection';
3
+ export { initializeSchema, getSchemaVersion, resetSchema } from './schema';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/database/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/F,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get DEFAULT_DB_PATH () {
13
+ return _connection.DEFAULT_DB_PATH;
14
+ },
15
+ get DatabaseConnection () {
16
+ return _connection.DatabaseConnection;
17
+ },
18
+ get closeDatabase () {
19
+ return _connection.closeDatabase;
20
+ },
21
+ get getDatabase () {
22
+ return _connection.getDatabase;
23
+ },
24
+ get getSchemaVersion () {
25
+ return _schema.getSchemaVersion;
26
+ },
27
+ get initializeSchema () {
28
+ return _schema.initializeSchema;
29
+ },
30
+ get resetSchema () {
31
+ return _schema.resetSchema;
32
+ }
33
+ });
34
+ const _connection = require("./connection");
35
+ const _schema = require("./schema");
36
+
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/database/index.ts"],"sourcesContent":["export { DatabaseConnection, getDatabase, closeDatabase, DEFAULT_DB_PATH } from './connection';\nexport type { DatabaseOptions } from './connection';\nexport { initializeSchema, getSchemaVersion, resetSchema } from './schema';\n"],"names":["DEFAULT_DB_PATH","DatabaseConnection","closeDatabase","getDatabase","getSchemaVersion","initializeSchema","resetSchema"],"mappings":";;;;;;;;;;;QAAyDA;eAAAA,2BAAe;;QAA/DC;eAAAA,8BAAkB;;QAAeC;eAAAA,yBAAa;;QAA1BC;eAAAA,uBAAW;;QAEbC;eAAAA,wBAAgB;;QAAlCC;eAAAA,wBAAgB;;QAAoBC;eAAAA,mBAAW;;;4BAFwB;wBAEhB"}
@@ -0,0 +1,50 @@
1
+ -- Migration 001: Initial schema
2
+ -- Creates the core knowledge table and FTS5 index
3
+
4
+ -- Main knowledge table (simplified: 9 fields)
5
+ CREATE TABLE IF NOT EXISTS knowledge (
6
+ id TEXT PRIMARY KEY,
7
+ title TEXT NOT NULL CHECK (length(title) <= 100),
8
+ content TEXT NOT NULL CHECK (length(content) <= 5000),
9
+ tags TEXT NOT NULL DEFAULT '[]',
10
+ scope TEXT NOT NULL DEFAULT 'global',
11
+ normalized_title TEXT NOT NULL,
12
+ content_hash TEXT NOT NULL,
13
+ created_at TEXT NOT NULL,
14
+ updated_at TEXT NOT NULL,
15
+
16
+ UNIQUE (normalized_title, scope),
17
+ UNIQUE (content_hash, scope)
18
+ );
19
+
20
+ -- FTS5 virtual table for full-text search
21
+ -- Column weights for bm25(): title=10, content=5, tags=1
22
+ CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_fts USING fts5(
23
+ title,
24
+ content,
25
+ tags,
26
+ content='knowledge',
27
+ content_rowid='rowid',
28
+ tokenize='porter unicode61'
29
+ );
30
+
31
+ -- Triggers to keep FTS in sync
32
+ CREATE TRIGGER IF NOT EXISTS knowledge_ai AFTER INSERT ON knowledge BEGIN
33
+ INSERT INTO knowledge_fts(rowid, title, content, tags)
34
+ VALUES (NEW.rowid, NEW.title, NEW.content, NEW.tags);
35
+ END;
36
+
37
+ CREATE TRIGGER IF NOT EXISTS knowledge_ad AFTER DELETE ON knowledge BEGIN
38
+ INSERT INTO knowledge_fts(knowledge_fts, rowid, title, content, tags)
39
+ VALUES ('delete', OLD.rowid, OLD.title, OLD.content, OLD.tags);
40
+ END;
41
+
42
+ CREATE TRIGGER IF NOT EXISTS knowledge_au AFTER UPDATE ON knowledge BEGIN
43
+ INSERT INTO knowledge_fts(knowledge_fts, rowid, title, content, tags)
44
+ VALUES ('delete', OLD.rowid, OLD.title, OLD.content, OLD.tags);
45
+ INSERT INTO knowledge_fts(rowid, title, content, tags)
46
+ VALUES (NEW.rowid, NEW.title, NEW.content, NEW.tags);
47
+ END;
48
+
49
+ -- Index for fast scope filtering
50
+ CREATE INDEX IF NOT EXISTS idx_knowledge_scope ON knowledge(scope);
@@ -0,0 +1,6 @@
1
+ import type { DatabaseConnection } from './connection';
2
+ export declare function getSchemaVersion(db: DatabaseConnection): number;
3
+ export declare function initializeSchema(db: DatabaseConnection): void;
4
+ export declare function resetSchema(db: DatabaseConnection): void;
5
+ export declare function getPendingMigrations(db: DatabaseConnection): string[];
6
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/database/schema.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,kBAAkB,GAAG,MAAM,CAG/D;AAsCD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,kBAAkB,GAAG,IAAI,CAkB7D;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,kBAAkB,GAAG,IAAI,CAQxD;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAMrE"}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get getPendingMigrations () {
13
+ return getPendingMigrations;
14
+ },
15
+ get getSchemaVersion () {
16
+ return getSchemaVersion;
17
+ },
18
+ get initializeSchema () {
19
+ return initializeSchema;
20
+ },
21
+ get resetSchema () {
22
+ return resetSchema;
23
+ }
24
+ });
25
+ const _fs = require("fs");
26
+ const _path = require("path");
27
+ function getSchemaVersion(db) {
28
+ const result = db.instance.pragma('user_version');
29
+ return result[0]?.user_version ?? 0;
30
+ }
31
+ function setSchemaVersion(db, version) {
32
+ db.instance.pragma(`user_version = ${version}`);
33
+ }
34
+ function getMigrationsDir() {
35
+ // In production, migrations are in dist/database/migrations
36
+ // In development/testing, they are in src/database/migrations
37
+ const distPath = (0, _path.join)(__dirname, 'migrations');
38
+ return distPath;
39
+ }
40
+ function getMigrationFiles() {
41
+ const migrationsDir = getMigrationsDir();
42
+ let files;
43
+ try {
44
+ files = (0, _fs.readdirSync)(migrationsDir).filter((f)=>f.endsWith('.sql')).sort();
45
+ } catch {
46
+ return [];
47
+ }
48
+ return files.map((file)=>{
49
+ const match = file.match(/^(\d+)_(.+)\.sql$/);
50
+ if (!match || !match[1] || !match[2]) {
51
+ throw new Error(`Invalid migration filename: ${file}. Expected format: 001_name.sql`);
52
+ }
53
+ return {
54
+ version: parseInt(match[1], 10),
55
+ name: match[2],
56
+ path: (0, _path.join)(migrationsDir, file)
57
+ };
58
+ });
59
+ }
60
+ function initializeSchema(db) {
61
+ const currentVersion = getSchemaVersion(db);
62
+ const migrations = getMigrationFiles();
63
+ const pendingMigrations = migrations.filter((m)=>m.version > currentVersion);
64
+ if (pendingMigrations.length === 0) {
65
+ return;
66
+ }
67
+ for (const migration of pendingMigrations){
68
+ const sql = (0, _fs.readFileSync)(migration.path, 'utf-8');
69
+ db.transaction(()=>{
70
+ db.instance.exec(sql);
71
+ setSchemaVersion(db, migration.version);
72
+ });
73
+ }
74
+ }
75
+ function resetSchema(db) {
76
+ db.transaction(()=>{
77
+ db.execute('DROP TABLE IF EXISTS knowledge_fts');
78
+ db.execute('DROP TABLE IF EXISTS knowledge');
79
+ setSchemaVersion(db, 0);
80
+ });
81
+ initializeSchema(db);
82
+ }
83
+ function getPendingMigrations(db) {
84
+ const currentVersion = getSchemaVersion(db);
85
+ const migrations = getMigrationFiles();
86
+ return migrations.filter((m)=>m.version > currentVersion).map((m)=>`${m.version}_${m.name}`);
87
+ }
88
+
89
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/database/schema.ts"],"sourcesContent":["import { readFileSync, readdirSync } from 'fs';\nimport { join } from 'path';\nimport type { DatabaseConnection } from './connection';\n\nexport function getSchemaVersion(db: DatabaseConnection): number {\n const result = db.instance.pragma('user_version') as { user_version: number }[];\n return result[0]?.user_version ?? 0;\n}\n\nfunction setSchemaVersion(db: DatabaseConnection, version: number): void {\n db.instance.pragma(`user_version = ${version}`);\n}\n\nfunction getMigrationsDir(): string {\n // In production, migrations are in dist/database/migrations\n // In development/testing, they are in src/database/migrations\n const distPath = join(__dirname, 'migrations');\n return distPath;\n}\n\nfunction getMigrationFiles(): { version: number; path: string; name: string }[] {\n const migrationsDir = getMigrationsDir();\n\n let files: string[];\n try {\n files = readdirSync(migrationsDir)\n .filter(f => f.endsWith('.sql'))\n .sort();\n } catch {\n return [];\n }\n\n return files.map(file => {\n const match = file.match(/^(\\d+)_(.+)\\.sql$/);\n if (!match || !match[1] || !match[2]) {\n throw new Error(`Invalid migration filename: ${file}. Expected format: 001_name.sql`);\n }\n return {\n version: parseInt(match[1], 10),\n name: match[2],\n path: join(migrationsDir, file),\n };\n });\n}\n\nexport function initializeSchema(db: DatabaseConnection): void {\n const currentVersion = getSchemaVersion(db);\n const migrations = getMigrationFiles();\n\n const pendingMigrations = migrations.filter(m => m.version > currentVersion);\n\n if (pendingMigrations.length === 0) {\n return;\n }\n\n for (const migration of pendingMigrations) {\n const sql = readFileSync(migration.path, 'utf-8');\n\n db.transaction(() => {\n db.instance.exec(sql);\n setSchemaVersion(db, migration.version);\n });\n }\n}\n\nexport function resetSchema(db: DatabaseConnection): void {\n db.transaction(() => {\n db.execute('DROP TABLE IF EXISTS knowledge_fts');\n db.execute('DROP TABLE IF EXISTS knowledge');\n setSchemaVersion(db, 0);\n });\n\n initializeSchema(db);\n}\n\nexport function getPendingMigrations(db: DatabaseConnection): string[] {\n const currentVersion = getSchemaVersion(db);\n const migrations = getMigrationFiles();\n return migrations\n .filter(m => m.version > currentVersion)\n .map(m => `${m.version}_${m.name}`);\n}\n"],"names":["getPendingMigrations","getSchemaVersion","initializeSchema","resetSchema","db","result","instance","pragma","user_version","setSchemaVersion","version","getMigrationsDir","distPath","join","__dirname","getMigrationFiles","migrationsDir","files","readdirSync","filter","f","endsWith","sort","map","file","match","Error","parseInt","name","path","currentVersion","migrations","pendingMigrations","m","length","migration","sql","readFileSync","transaction","exec","execute"],"mappings":";;;;;;;;;;;QA2EgBA;eAAAA;;QAvEAC;eAAAA;;QAyCAC;eAAAA;;QAoBAC;eAAAA;;;oBAjE0B;sBACrB;AAGd,SAASF,iBAAiBG,EAAsB;IACnD,MAAMC,SAASD,GAAGE,QAAQ,CAACC,MAAM,CAAC;IAClC,OAAOF,MAAM,CAAC,EAAE,EAAEG,gBAAgB;AACtC;AAEA,SAASC,iBAAiBL,EAAsB,EAAEM,OAAe;IAC7DN,GAAGE,QAAQ,CAACC,MAAM,CAAC,CAAC,eAAe,EAAEG,SAAS;AAClD;AAEA,SAASC;IACL,4DAA4D;IAC5D,8DAA8D;IAC9D,MAAMC,WAAWC,IAAAA,UAAI,EAACC,WAAW;IACjC,OAAOF;AACX;AAEA,SAASG;IACL,MAAMC,gBAAgBL;IAEtB,IAAIM;IACJ,IAAI;QACAA,QAAQC,IAAAA,eAAW,EAACF,eACfG,MAAM,CAACC,CAAAA,IAAKA,EAAEC,QAAQ,CAAC,SACvBC,IAAI;IACb,EAAE,OAAM;QACJ,OAAO,EAAE;IACb;IAEA,OAAOL,MAAMM,GAAG,CAACC,CAAAA;QACb,MAAMC,QAAQD,KAAKC,KAAK,CAAC;QACzB,IAAI,CAACA,SAAS,CAACA,KAAK,CAAC,EAAE,IAAI,CAACA,KAAK,CAAC,EAAE,EAAE;YAClC,MAAM,IAAIC,MAAM,CAAC,4BAA4B,EAAEF,KAAK,+BAA+B,CAAC;QACxF;QACA,OAAO;YACHd,SAASiB,SAASF,KAAK,CAAC,EAAE,EAAE;YAC5BG,MAAMH,KAAK,CAAC,EAAE;YACdI,MAAMhB,IAAAA,UAAI,EAACG,eAAeQ;QAC9B;IACJ;AACJ;AAEO,SAAStB,iBAAiBE,EAAsB;IACnD,MAAM0B,iBAAiB7B,iBAAiBG;IACxC,MAAM2B,aAAahB;IAEnB,MAAMiB,oBAAoBD,WAAWZ,MAAM,CAACc,CAAAA,IAAKA,EAAEvB,OAAO,GAAGoB;IAE7D,IAAIE,kBAAkBE,MAAM,KAAK,GAAG;QAChC;IACJ;IAEA,KAAK,MAAMC,aAAaH,kBAAmB;QACvC,MAAMI,MAAMC,IAAAA,gBAAY,EAACF,UAAUN,IAAI,EAAE;QAEzCzB,GAAGkC,WAAW,CAAC;YACXlC,GAAGE,QAAQ,CAACiC,IAAI,CAACH;YACjB3B,iBAAiBL,IAAI+B,UAAUzB,OAAO;QAC1C;IACJ;AACJ;AAEO,SAASP,YAAYC,EAAsB;IAC9CA,GAAGkC,WAAW,CAAC;QACXlC,GAAGoC,OAAO,CAAC;QACXpC,GAAGoC,OAAO,CAAC;QACX/B,iBAAiBL,IAAI;IACzB;IAEAF,iBAAiBE;AACrB;AAEO,SAASJ,qBAAqBI,EAAsB;IACvD,MAAM0B,iBAAiB7B,iBAAiBG;IACxC,MAAM2B,aAAahB;IACnB,OAAOgB,WACFZ,MAAM,CAACc,CAAAA,IAAKA,EAAEvB,OAAO,GAAGoB,gBACxBP,GAAG,CAACU,CAAAA,IAAK,GAAGA,EAAEvB,OAAO,CAAC,CAAC,EAAEuB,EAAEL,IAAI,EAAE;AAC1C"}
@@ -0,0 +1,3 @@
1
+ import type { SearchKnowledgeInput, SearchKnowledgeResult } from '../types';
2
+ export declare function searchKnowledge(input: SearchKnowledgeInput): SearchKnowledgeResult;
3
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/handlers/search.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAgB5E,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,qBAAqB,CAwClF"}