@pella-labs/pinakes 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 (174) hide show
  1. package/README.md +208 -0
  2. package/dist/cli/audit.d.ts +30 -0
  3. package/dist/cli/audit.d.ts.map +1 -0
  4. package/dist/cli/audit.js +49 -0
  5. package/dist/cli/audit.js.map +1 -0
  6. package/dist/cli/export.d.ts +32 -0
  7. package/dist/cli/export.d.ts.map +1 -0
  8. package/dist/cli/export.js +73 -0
  9. package/dist/cli/export.js.map +1 -0
  10. package/dist/cli/import.d.ts +24 -0
  11. package/dist/cli/import.d.ts.map +1 -0
  12. package/dist/cli/import.js +96 -0
  13. package/dist/cli/import.js.map +1 -0
  14. package/dist/cli/index.d.ts +3 -0
  15. package/dist/cli/index.d.ts.map +1 -0
  16. package/dist/cli/index.js +172 -0
  17. package/dist/cli/index.js.map +1 -0
  18. package/dist/cli/purge.d.ts +23 -0
  19. package/dist/cli/purge.d.ts.map +1 -0
  20. package/dist/cli/purge.js +57 -0
  21. package/dist/cli/purge.js.map +1 -0
  22. package/dist/cli/rebuild.d.ts +54 -0
  23. package/dist/cli/rebuild.d.ts.map +1 -0
  24. package/dist/cli/rebuild.js +113 -0
  25. package/dist/cli/rebuild.js.map +1 -0
  26. package/dist/cli/serve.d.ts +49 -0
  27. package/dist/cli/serve.d.ts.map +1 -0
  28. package/dist/cli/serve.js +296 -0
  29. package/dist/cli/serve.js.map +1 -0
  30. package/dist/cli/status.d.ts +39 -0
  31. package/dist/cli/status.d.ts.map +1 -0
  32. package/dist/cli/status.js +108 -0
  33. package/dist/cli/status.js.map +1 -0
  34. package/dist/db/client.d.ts +109 -0
  35. package/dist/db/client.d.ts.map +1 -0
  36. package/dist/db/client.js +175 -0
  37. package/dist/db/client.js.map +1 -0
  38. package/dist/db/repository.d.ts +82 -0
  39. package/dist/db/repository.d.ts.map +1 -0
  40. package/dist/db/repository.js +173 -0
  41. package/dist/db/repository.js.map +1 -0
  42. package/dist/db/schema.d.ts +990 -0
  43. package/dist/db/schema.d.ts.map +1 -0
  44. package/dist/db/schema.js +259 -0
  45. package/dist/db/schema.js.map +1 -0
  46. package/dist/db/types.d.ts +28 -0
  47. package/dist/db/types.d.ts.map +1 -0
  48. package/dist/db/types.js +11 -0
  49. package/dist/db/types.js.map +1 -0
  50. package/dist/gaps/detector.d.ts +67 -0
  51. package/dist/gaps/detector.d.ts.map +1 -0
  52. package/dist/gaps/detector.js +160 -0
  53. package/dist/gaps/detector.js.map +1 -0
  54. package/dist/gate/budget.d.ts +90 -0
  55. package/dist/gate/budget.d.ts.map +1 -0
  56. package/dist/gate/budget.js +145 -0
  57. package/dist/gate/budget.js.map +1 -0
  58. package/dist/ingest/chokidar.d.ts +33 -0
  59. package/dist/ingest/chokidar.d.ts.map +1 -0
  60. package/dist/ingest/chokidar.js +152 -0
  61. package/dist/ingest/chokidar.js.map +1 -0
  62. package/dist/ingest/ingester.d.ts +117 -0
  63. package/dist/ingest/ingester.d.ts.map +1 -0
  64. package/dist/ingest/ingester.js +312 -0
  65. package/dist/ingest/ingester.js.map +1 -0
  66. package/dist/ingest/manifest.d.ts +87 -0
  67. package/dist/ingest/manifest.d.ts.map +1 -0
  68. package/dist/ingest/manifest.js +223 -0
  69. package/dist/ingest/manifest.js.map +1 -0
  70. package/dist/ingest/memory-store.d.ts +55 -0
  71. package/dist/ingest/memory-store.d.ts.map +1 -0
  72. package/dist/ingest/memory-store.js +94 -0
  73. package/dist/ingest/memory-store.js.map +1 -0
  74. package/dist/ingest/parse/chunk.d.ts +15 -0
  75. package/dist/ingest/parse/chunk.d.ts.map +1 -0
  76. package/dist/ingest/parse/chunk.js +88 -0
  77. package/dist/ingest/parse/chunk.js.map +1 -0
  78. package/dist/ingest/parse/markdown.d.ts +64 -0
  79. package/dist/ingest/parse/markdown.d.ts.map +1 -0
  80. package/dist/ingest/parse/markdown.js +152 -0
  81. package/dist/ingest/parse/markdown.js.map +1 -0
  82. package/dist/ingest/queue.d.ts +21 -0
  83. package/dist/ingest/queue.d.ts.map +1 -0
  84. package/dist/ingest/queue.js +24 -0
  85. package/dist/ingest/queue.js.map +1 -0
  86. package/dist/ingest/source.d.ts +42 -0
  87. package/dist/ingest/source.d.ts.map +1 -0
  88. package/dist/ingest/source.js +19 -0
  89. package/dist/ingest/source.js.map +1 -0
  90. package/dist/mcp/envelope.d.ts +73 -0
  91. package/dist/mcp/envelope.d.ts.map +1 -0
  92. package/dist/mcp/envelope.js +46 -0
  93. package/dist/mcp/envelope.js.map +1 -0
  94. package/dist/mcp/tools/execute.d.ts +55 -0
  95. package/dist/mcp/tools/execute.d.ts.map +1 -0
  96. package/dist/mcp/tools/execute.js +232 -0
  97. package/dist/mcp/tools/execute.js.map +1 -0
  98. package/dist/mcp/tools/search.d.ts +53 -0
  99. package/dist/mcp/tools/search.d.ts.map +1 -0
  100. package/dist/mcp/tools/search.js +114 -0
  101. package/dist/mcp/tools/search.js.map +1 -0
  102. package/dist/observability/audit.d.ts +25 -0
  103. package/dist/observability/audit.d.ts.map +1 -0
  104. package/dist/observability/audit.js +38 -0
  105. package/dist/observability/audit.js.map +1 -0
  106. package/dist/observability/logger.d.ts +4 -0
  107. package/dist/observability/logger.d.ts.map +1 -0
  108. package/dist/observability/logger.js +56 -0
  109. package/dist/observability/logger.js.map +1 -0
  110. package/dist/observability/metrics.d.ts +38 -0
  111. package/dist/observability/metrics.d.ts.map +1 -0
  112. package/dist/observability/metrics.js +64 -0
  113. package/dist/observability/metrics.js.map +1 -0
  114. package/dist/retrieval/embedder.d.ts +130 -0
  115. package/dist/retrieval/embedder.d.ts.map +1 -0
  116. package/dist/retrieval/embedder.js +278 -0
  117. package/dist/retrieval/embedder.js.map +1 -0
  118. package/dist/retrieval/fts.d.ts +42 -0
  119. package/dist/retrieval/fts.d.ts.map +1 -0
  120. package/dist/retrieval/fts.js +46 -0
  121. package/dist/retrieval/fts.js.map +1 -0
  122. package/dist/retrieval/hybrid.d.ts +43 -0
  123. package/dist/retrieval/hybrid.d.ts.map +1 -0
  124. package/dist/retrieval/hybrid.js +120 -0
  125. package/dist/retrieval/hybrid.js.map +1 -0
  126. package/dist/retrieval/vec.d.ts +39 -0
  127. package/dist/retrieval/vec.d.ts.map +1 -0
  128. package/dist/retrieval/vec.js +50 -0
  129. package/dist/retrieval/vec.js.map +1 -0
  130. package/dist/sandbox/bindings/budget.d.ts +10 -0
  131. package/dist/sandbox/bindings/budget.d.ts.map +1 -0
  132. package/dist/sandbox/bindings/budget.js +44 -0
  133. package/dist/sandbox/bindings/budget.js.map +1 -0
  134. package/dist/sandbox/bindings/install.d.ts +23 -0
  135. package/dist/sandbox/bindings/install.d.ts.map +1 -0
  136. package/dist/sandbox/bindings/install.js +15 -0
  137. package/dist/sandbox/bindings/install.js.map +1 -0
  138. package/dist/sandbox/bindings/kg.d.ts +29 -0
  139. package/dist/sandbox/bindings/kg.d.ts.map +1 -0
  140. package/dist/sandbox/bindings/kg.js +323 -0
  141. package/dist/sandbox/bindings/kg.js.map +1 -0
  142. package/dist/sandbox/bindings/logger.d.ts +11 -0
  143. package/dist/sandbox/bindings/logger.d.ts.map +1 -0
  144. package/dist/sandbox/bindings/logger.js +33 -0
  145. package/dist/sandbox/bindings/logger.js.map +1 -0
  146. package/dist/sandbox/bindings/write.d.ts +34 -0
  147. package/dist/sandbox/bindings/write.d.ts.map +1 -0
  148. package/dist/sandbox/bindings/write.js +195 -0
  149. package/dist/sandbox/bindings/write.js.map +1 -0
  150. package/dist/sandbox/executor.d.ts +68 -0
  151. package/dist/sandbox/executor.d.ts.map +1 -0
  152. package/dist/sandbox/executor.js +280 -0
  153. package/dist/sandbox/executor.js.map +1 -0
  154. package/dist/sandbox/helpers.d.ts +26 -0
  155. package/dist/sandbox/helpers.d.ts.map +1 -0
  156. package/dist/sandbox/helpers.js +131 -0
  157. package/dist/sandbox/helpers.js.map +1 -0
  158. package/dist/sandbox/pool.d.ts +63 -0
  159. package/dist/sandbox/pool.d.ts.map +1 -0
  160. package/dist/sandbox/pool.js +98 -0
  161. package/dist/sandbox/pool.js.map +1 -0
  162. package/dist/sandbox/vendored-codemode.d.ts +99 -0
  163. package/dist/sandbox/vendored-codemode.d.ts.map +1 -0
  164. package/dist/sandbox/vendored-codemode.js +471 -0
  165. package/dist/sandbox/vendored-codemode.js.map +1 -0
  166. package/dist/server.d.ts +3 -0
  167. package/dist/server.d.ts.map +1 -0
  168. package/dist/server.js +74 -0
  169. package/dist/server.js.map +1 -0
  170. package/dist/spike.d.ts +15 -0
  171. package/dist/spike.d.ts.map +1 -0
  172. package/dist/spike.js +90 -0
  173. package/dist/spike.js.map +1 -0
  174. package/package.json +60 -0
@@ -0,0 +1,175 @@
1
+ import { mkdirSync } from 'node:fs';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import Database from 'better-sqlite3';
5
+ import { drizzle } from 'drizzle-orm/better-sqlite3';
6
+ import { migrate } from 'drizzle-orm/better-sqlite3/migrator';
7
+ import * as sqliteVec from 'sqlite-vec';
8
+ import { logger } from '../observability/logger.js';
9
+ import * as schema from './schema.js';
10
+ // ----------------------------------------------------------------------------
11
+ // SQLite version check (presearch D18)
12
+ // ----------------------------------------------------------------------------
13
+ /** SQLite versions known to have an FTS5 regression — never use these. */
14
+ const FORBIDDEN_SQLITE_VERSIONS = ['3.51.0'];
15
+ /**
16
+ * The minimum acceptable SQLite version. better-sqlite3@12.8.0 bundles
17
+ * 3.51.3 which is fine. Anything older than 3.50.4 is rejected because
18
+ * older versions miss FTS5 features we rely on; anything in the 3.51.0-3.51.2
19
+ * range is rejected due to the FTS5 regression.
20
+ *
21
+ * The check is: version === '3.50.4' OR version >= '3.51.3'.
22
+ */
23
+ function isAllowedSqliteVersion(version) {
24
+ if (FORBIDDEN_SQLITE_VERSIONS.includes(version)) {
25
+ return false;
26
+ }
27
+ if (version === '3.50.4')
28
+ return true;
29
+ // Lexicographic compare works for 3-component semver where each component
30
+ // is the same width — but SQLite versions are not zero-padded, so we
31
+ // parse and compare numerically.
32
+ const parts = version.split('.').map((p) => parseInt(p, 10));
33
+ if (parts.length !== 3 || parts.some((n) => Number.isNaN(n)))
34
+ return false;
35
+ const [major, minor, patch] = parts;
36
+ if (major > 3)
37
+ return true;
38
+ if (major < 3)
39
+ return false;
40
+ if (minor > 51)
41
+ return true;
42
+ if (minor < 50)
43
+ return false;
44
+ if (minor === 50)
45
+ return patch >= 4;
46
+ // minor === 51
47
+ return patch >= 3;
48
+ }
49
+ // ----------------------------------------------------------------------------
50
+ // Pragmas — applied on every connection (writer + readers)
51
+ // ----------------------------------------------------------------------------
52
+ /**
53
+ * The 6 mandatory pragmas, applied on every fresh connection. Order matters
54
+ * for `journal_mode=WAL` (must run before any read/write). `cache_size=-20000`
55
+ * is in KB units (negative = KB instead of pages); presearch §Performance
56
+ * settled on 20MB per connection.
57
+ */
58
+ function applyPragmas(db) {
59
+ db.pragma('journal_mode = WAL');
60
+ db.pragma('busy_timeout = 5000');
61
+ db.pragma('synchronous = NORMAL');
62
+ db.pragma('foreign_keys = ON');
63
+ db.pragma('cache_size = -20000');
64
+ db.pragma('temp_store = MEMORY');
65
+ }
66
+ /**
67
+ * Load the sqlite-vec extension on a connection. Must be called BEFORE any
68
+ * query that touches `kg_chunks_vec`, including the migration that creates
69
+ * the virtual table.
70
+ *
71
+ * better-sqlite3 disables loadExtension() by default; we re-enable it on
72
+ * the specific connection only and load the bundled extension shipped by
73
+ * the sqlite-vec npm package (no system install required).
74
+ */
75
+ function loadSqliteVec(db) {
76
+ // Re-enable extension loading for this connection only.
77
+ db.loadExtension(sqliteVec.getLoadablePath());
78
+ }
79
+ // ----------------------------------------------------------------------------
80
+ // Migrations (drizzle-kit output, applied via drizzle's migrate())
81
+ // ----------------------------------------------------------------------------
82
+ /**
83
+ * The migrations directory, resolved relative to this file. Works under both
84
+ * tsx (ESM source) and post-build (compiled dist/) by using import.meta.url.
85
+ */
86
+ const MIGRATIONS_FOLDER = (() => {
87
+ const here = dirname(fileURLToPath(import.meta.url));
88
+ return resolve(here, './migrations');
89
+ })();
90
+ export function openDb(path, options = {}) {
91
+ const runMigrations = options.runMigrations ?? true;
92
+ const absPath = resolve(path);
93
+ // Step 1: ensure parent dir exists. better-sqlite3 will create the file
94
+ // itself but won't `mkdir -p` for the directory.
95
+ mkdirSync(dirname(absPath), { recursive: true });
96
+ // Step 2: open one writer + two readers.
97
+ const writer = new Database(absPath);
98
+ const readers = [new Database(absPath, { readonly: true }), new Database(absPath, { readonly: true })];
99
+ // Step 3 + 4: pragmas + sqlite-vec on every connection.
100
+ for (const conn of [writer, ...readers]) {
101
+ applyPragmas(conn);
102
+ loadSqliteVec(conn);
103
+ }
104
+ // Step 5: SQLite version check. Done after pragmas so any error message
105
+ // includes a sane connection state.
106
+ const versionRow = writer.prepare('SELECT sqlite_version() AS v').get();
107
+ if (!isAllowedSqliteVersion(versionRow.v)) {
108
+ // Clean up before throwing so we don't leak file handles.
109
+ closeDb({ path: absPath, writer, readers, drizzleWriter: drizzle(writer, { schema }) });
110
+ throw new Error(`SQLite version ${versionRow.v} is not allowed for KG-MCP — requires 3.50.4 or >=3.51.3 (presearch D18). ` +
111
+ `better-sqlite3@12.8.0 bundles 3.51.3; if you see this, your better-sqlite3 was compiled against the system sqlite.`);
112
+ }
113
+ // Build the drizzle wrapper before step 6 so migrate() can use it.
114
+ const drizzleWriter = drizzle(writer, { schema });
115
+ // Step 6: run migrations. drizzle's migrate() is idempotent — it tracks
116
+ // applied migrations in `__drizzle_migrations` and only runs new ones.
117
+ if (runMigrations) {
118
+ migrate(drizzleWriter, { migrationsFolder: MIGRATIONS_FOLDER });
119
+ }
120
+ // Step 7: stamp schema_version=1 if absent. Subsequent phases bump this.
121
+ if (runMigrations) {
122
+ writer
123
+ .prepare('INSERT OR IGNORE INTO kg_meta (key, value) VALUES (?, ?)')
124
+ .run('schema_version', '2');
125
+ }
126
+ logger.info({ path: absPath, sqliteVersion: versionRow.v }, 'opened SQLite db');
127
+ return { path: absPath, writer, readers, drizzleWriter };
128
+ }
129
+ /**
130
+ * Close every connection in a bundle. Idempotent — safe to call twice.
131
+ * Logs at debug level so the production stdio path stays quiet on shutdown.
132
+ */
133
+ export function closeDb(bundle) {
134
+ try {
135
+ if (bundle.writer.open)
136
+ bundle.writer.close();
137
+ }
138
+ catch (err) {
139
+ logger.warn({ err }, 'error closing writer');
140
+ }
141
+ for (const reader of bundle.readers) {
142
+ try {
143
+ if (reader.open)
144
+ reader.close();
145
+ }
146
+ catch (err) {
147
+ logger.warn({ err }, 'error closing reader');
148
+ }
149
+ }
150
+ logger.debug({ path: bundle.path }, 'closed SQLite db');
151
+ }
152
+ // ----------------------------------------------------------------------------
153
+ // Reader pool helper
154
+ // ----------------------------------------------------------------------------
155
+ /**
156
+ * Round-robin reader picker. Repository methods call this to spread queries
157
+ * across the read pool. Stateless function — caller passes a counter (or
158
+ * the bundle itself if it doesn't care about even distribution).
159
+ */
160
+ let readerCursor = 0;
161
+ export function nextReader(bundle) {
162
+ const idx = readerCursor++ % bundle.readers.length;
163
+ return bundle.readers[idx];
164
+ }
165
+ // ----------------------------------------------------------------------------
166
+ // Test helpers (exported for use in __tests__/)
167
+ // ----------------------------------------------------------------------------
168
+ /** Exposed for schema.test.ts to verify the version-check rule directly. */
169
+ export const __test = {
170
+ isAllowedSqliteVersion,
171
+ applyPragmas,
172
+ loadSqliteVec,
173
+ MIGRATIONS_FOLDER,
174
+ };
175
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import type { Chunk } from './types.js';
2
+ import { type DbBundle } from './client.js';
3
+ /**
4
+ * The repository is the seam between the tool handlers and the SQLite
5
+ * storage layer. Phase 1 used an in-memory `MemoryStore` directly; Phase 2
6
+ * swaps in this `Repository` without changing what the tool handlers see.
7
+ *
8
+ * **Method shapes mirror MemoryStore exactly** so the swap in Pass 4 is a
9
+ * one-line rename in each handler. `search()` returns the same `Chunk`
10
+ * shape and the same insertion-order semantics; `get()` returns the same
11
+ * `Chunk | null`.
12
+ *
13
+ * **Query implementation: LIKE, not FTS5.** Phase 2 stops short of the
14
+ * FTS5+RRF query path — that lands in Phase 4. Two reasons:
15
+ * 1. The Phase 1 spike tests use substring queries (e.g. "hashPassword")
16
+ * and assert ≥1 result; FTS5's tokenizer would split on case
17
+ * boundaries differently and may break those assertions.
18
+ * 2. The FTS5 virtual table is *populated* in Phase 2 (via the migration's
19
+ * triggers) so Phase 4 only has to swap the WHERE clause — the data is
20
+ * already indexed.
21
+ *
22
+ * **Scope routing**: the data plane supports both `project` and `personal`
23
+ * (separate DB bundles, separate manifests). The Phase 5 privacy invariant
24
+ * gate lives in the tool handlers, not here — repository methods accept
25
+ * a scope param and route to the matching bundle. If a personal bundle
26
+ * isn't loaded, personal-scope reads return empty / null. The 15-test
27
+ * privacy adversarial suite is a Phase 5 deliverable; Phase 2 keeps the
28
+ * existing tool-handler gate that rejects personal-scope tool calls.
29
+ *
30
+ * **Single writer / multi reader**: writes go through `bundle.writer`;
31
+ * reads go through round-robin `nextReader(bundle)`. Repository.search/get
32
+ * are read-only and use the read pool.
33
+ */
34
+ export declare class Repository {
35
+ private readonly project;
36
+ private readonly personal;
37
+ constructor(project: DbBundle, personal?: DbBundle | null);
38
+ /**
39
+ * Resolve the bundle for a given scope. Returns null for `'personal'`
40
+ * if no personal bundle was provided (so callers degrade gracefully).
41
+ * `'both'` is not handled here — callers must split into two calls and
42
+ * tag results with `source_scope` (CLAUDE.md §Security #6).
43
+ */
44
+ private bundleFor;
45
+ /**
46
+ * Case-insensitive substring search over chunks of the given scope.
47
+ *
48
+ * Phase 1 semantics preserved: returns matches in deterministic
49
+ * insertion order (by source_uri, then chunk_index). The `text LIKE`
50
+ * predicate is case-insensitive in SQLite when both sides are ASCII —
51
+ * to be safe across Unicode, we lowercase the query AND use a `LIKE`
52
+ * with `lower(text)` so the comparison is symmetrical.
53
+ *
54
+ * Empty query returns an empty array (matches MemoryStore behavior).
55
+ *
56
+ * Phase 4 swaps this to FTS5+RRF; the return shape stays identical.
57
+ */
58
+ search(query: string, scope?: 'project' | 'personal'): Chunk[];
59
+ /**
60
+ * Exact lookup by chunk id. Returns null on miss (matches MemoryStore).
61
+ */
62
+ get(id: string, scope?: 'project' | 'personal'): Chunk | null;
63
+ /**
64
+ * Total number of indexed chunks for a scope (or both if not specified).
65
+ * Used by `kg status` and the spike-equivalent `store.size()` checks.
66
+ */
67
+ size(scope?: 'project' | 'personal'): number;
68
+ /**
69
+ * The wiki root directory this scope's data was loaded from. Used by
70
+ * the CLI status command and Phase 1's `MemoryStore.root()` parity.
71
+ * Pass-through to whatever the caller stamped on the bundle when opening.
72
+ */
73
+ root(scope?: 'project' | 'personal'): string;
74
+ private countChunks;
75
+ /**
76
+ * Bump `last_accessed_at` on the parent nodes of the given chunk ids.
77
+ * Single statement, no transaction needed. Phase 5's LRU eviction reads
78
+ * this column; Phase 2 just keeps it warm.
79
+ */
80
+ private touchNodesByChunkIds;
81
+ }
82
+ //# sourceMappingURL=repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/db/repository.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,KAAK,QAAQ,EAAc,MAAM,aAAa,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,OAAO,EAAE,QAAQ,EACjB,QAAQ,GAAE,QAAQ,GAAG,IAAW;IAGnD;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAIjB;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,SAAS,GAAG,UAAsB,GAAG,KAAK,EAAE;IAuCzE;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,GAAE,SAAS,GAAG,UAAsB,GAAG,KAAK,GAAG,IAAI;IA8BxE;;;OAGG;IACH,IAAI,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM;IAY5C;;;;OAIG;IACH,IAAI,CAAC,KAAK,GAAE,SAAS,GAAG,UAAsB,GAAG,MAAM;IASvD,OAAO,CAAC,WAAW;IAYnB;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;CAU7B"}
@@ -0,0 +1,173 @@
1
+ import { nextReader } from './client.js';
2
+ /**
3
+ * The repository is the seam between the tool handlers and the SQLite
4
+ * storage layer. Phase 1 used an in-memory `MemoryStore` directly; Phase 2
5
+ * swaps in this `Repository` without changing what the tool handlers see.
6
+ *
7
+ * **Method shapes mirror MemoryStore exactly** so the swap in Pass 4 is a
8
+ * one-line rename in each handler. `search()` returns the same `Chunk`
9
+ * shape and the same insertion-order semantics; `get()` returns the same
10
+ * `Chunk | null`.
11
+ *
12
+ * **Query implementation: LIKE, not FTS5.** Phase 2 stops short of the
13
+ * FTS5+RRF query path — that lands in Phase 4. Two reasons:
14
+ * 1. The Phase 1 spike tests use substring queries (e.g. "hashPassword")
15
+ * and assert ≥1 result; FTS5's tokenizer would split on case
16
+ * boundaries differently and may break those assertions.
17
+ * 2. The FTS5 virtual table is *populated* in Phase 2 (via the migration's
18
+ * triggers) so Phase 4 only has to swap the WHERE clause — the data is
19
+ * already indexed.
20
+ *
21
+ * **Scope routing**: the data plane supports both `project` and `personal`
22
+ * (separate DB bundles, separate manifests). The Phase 5 privacy invariant
23
+ * gate lives in the tool handlers, not here — repository methods accept
24
+ * a scope param and route to the matching bundle. If a personal bundle
25
+ * isn't loaded, personal-scope reads return empty / null. The 15-test
26
+ * privacy adversarial suite is a Phase 5 deliverable; Phase 2 keeps the
27
+ * existing tool-handler gate that rejects personal-scope tool calls.
28
+ *
29
+ * **Single writer / multi reader**: writes go through `bundle.writer`;
30
+ * reads go through round-robin `nextReader(bundle)`. Repository.search/get
31
+ * are read-only and use the read pool.
32
+ */
33
+ export class Repository {
34
+ project;
35
+ personal;
36
+ constructor(project, personal = null) {
37
+ this.project = project;
38
+ this.personal = personal;
39
+ }
40
+ /**
41
+ * Resolve the bundle for a given scope. Returns null for `'personal'`
42
+ * if no personal bundle was provided (so callers degrade gracefully).
43
+ * `'both'` is not handled here — callers must split into two calls and
44
+ * tag results with `source_scope` (CLAUDE.md §Security #6).
45
+ */
46
+ bundleFor(scope) {
47
+ return scope === 'project' ? this.project : this.personal;
48
+ }
49
+ /**
50
+ * Case-insensitive substring search over chunks of the given scope.
51
+ *
52
+ * Phase 1 semantics preserved: returns matches in deterministic
53
+ * insertion order (by source_uri, then chunk_index). The `text LIKE`
54
+ * predicate is case-insensitive in SQLite when both sides are ASCII —
55
+ * to be safe across Unicode, we lowercase the query AND use a `LIKE`
56
+ * with `lower(text)` so the comparison is symmetrical.
57
+ *
58
+ * Empty query returns an empty array (matches MemoryStore behavior).
59
+ *
60
+ * Phase 4 swaps this to FTS5+RRF; the return shape stays identical.
61
+ */
62
+ search(query, scope = 'project') {
63
+ const bundle = this.bundleFor(scope);
64
+ if (!bundle)
65
+ return [];
66
+ const q = query.trim();
67
+ if (!q)
68
+ return [];
69
+ const reader = nextReader(bundle);
70
+ const rows = reader
71
+ .prepare(`SELECT c.id AS id, c.text AS text, n.source_uri AS source_uri, c.chunk_index AS chunk_index
72
+ FROM kg_chunks c JOIN kg_nodes n ON c.node_id = n.id
73
+ WHERE n.scope = ?
74
+ AND lower(c.text) LIKE '%' || lower(?) || '%'
75
+ ORDER BY n.source_uri, c.chunk_index`)
76
+ .all(scope, q);
77
+ // Bump last_accessed_at on hit nodes (Phase 5 LRU groundwork). Best-effort,
78
+ // failures are non-fatal — eviction is a future concern, not a hot path.
79
+ if (rows.length > 0) {
80
+ try {
81
+ this.touchNodesByChunkIds(bundle.writer, rows.map((r) => r.id));
82
+ }
83
+ catch {
84
+ /* swallow — tracking-only */
85
+ }
86
+ }
87
+ return rows.map((r) => ({
88
+ id: r.id,
89
+ text: r.text,
90
+ source_uri: r.source_uri,
91
+ chunk_index: r.chunk_index,
92
+ }));
93
+ }
94
+ /**
95
+ * Exact lookup by chunk id. Returns null on miss (matches MemoryStore).
96
+ */
97
+ get(id, scope = 'project') {
98
+ const bundle = this.bundleFor(scope);
99
+ if (!bundle)
100
+ return null;
101
+ const reader = nextReader(bundle);
102
+ const row = reader
103
+ .prepare(`SELECT c.id AS id, c.text AS text, n.source_uri AS source_uri, c.chunk_index AS chunk_index
104
+ FROM kg_chunks c JOIN kg_nodes n ON c.node_id = n.id
105
+ WHERE n.scope = ? AND c.id = ?
106
+ LIMIT 1`)
107
+ .get(scope, id);
108
+ if (!row)
109
+ return null;
110
+ try {
111
+ this.touchNodesByChunkIds(bundle.writer, [row.id]);
112
+ }
113
+ catch {
114
+ /* swallow */
115
+ }
116
+ return {
117
+ id: row.id,
118
+ text: row.text,
119
+ source_uri: row.source_uri,
120
+ chunk_index: row.chunk_index,
121
+ };
122
+ }
123
+ /**
124
+ * Total number of indexed chunks for a scope (or both if not specified).
125
+ * Used by `kg status` and the spike-equivalent `store.size()` checks.
126
+ */
127
+ size(scope) {
128
+ if (scope) {
129
+ const bundle = this.bundleFor(scope);
130
+ if (!bundle)
131
+ return 0;
132
+ return this.countChunks(bundle, scope);
133
+ }
134
+ return (this.countChunks(this.project, 'project') +
135
+ (this.personal ? this.countChunks(this.personal, 'personal') : 0));
136
+ }
137
+ /**
138
+ * The wiki root directory this scope's data was loaded from. Used by
139
+ * the CLI status command and Phase 1's `MemoryStore.root()` parity.
140
+ * Pass-through to whatever the caller stamped on the bundle when opening.
141
+ */
142
+ root(scope = 'project') {
143
+ const bundle = this.bundleFor(scope);
144
+ return bundle?.path ?? '';
145
+ }
146
+ // --------------------------------------------------------------------------
147
+ // Internal helpers
148
+ // --------------------------------------------------------------------------
149
+ countChunks(bundle, scope) {
150
+ const reader = nextReader(bundle);
151
+ const row = reader
152
+ .prepare(`SELECT count(*) AS c
153
+ FROM kg_chunks ch JOIN kg_nodes n ON ch.node_id = n.id
154
+ WHERE n.scope = ?`)
155
+ .get(scope);
156
+ return row?.c ?? 0;
157
+ }
158
+ /**
159
+ * Bump `last_accessed_at` on the parent nodes of the given chunk ids.
160
+ * Single statement, no transaction needed. Phase 5's LRU eviction reads
161
+ * this column; Phase 2 just keeps it warm.
162
+ */
163
+ touchNodesByChunkIds(writer, chunkIds) {
164
+ if (chunkIds.length === 0)
165
+ return;
166
+ const placeholders = chunkIds.map(() => '?').join(',');
167
+ writer
168
+ .prepare(`UPDATE kg_nodes SET last_accessed_at = ?
169
+ WHERE id IN (SELECT node_id FROM kg_chunks WHERE id IN (${placeholders}))`)
170
+ .run(Date.now(), ...chunkIds);
171
+ }
172
+ }
173
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/db/repository.ts"],"names":[],"mappings":"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"}