diff --git a/.changeset/storage-rebuild-fts.md b/.changeset/storage-rebuild-fts.md new file mode 100644 index 0000000..9219d00 --- /dev/null +++ b/.changeset/storage-rebuild-fts.md @@ -0,0 +1,9 @@ +--- +'@colony/storage': minor +'@imdeadpool/colony': patch +--- + +Add `Storage.rebuildFts()` so the CLI `reindex` command no longer +reaches through the type system to poke `better-sqlite3`. Behavior is +unchanged — `reindex` still runs the FTS5 `'rebuild'` statement — but +the public API is now typed and callers do not cast through `unknown`. diff --git a/apps/cli/src/commands/reindex.ts b/apps/cli/src/commands/reindex.ts index 43919b9..5bc0896 100644 --- a/apps/cli/src/commands/reindex.ts +++ b/apps/cli/src/commands/reindex.ts @@ -10,15 +10,11 @@ export function registerReindexCommand(program: Command): void { .action(async () => { const settings = loadSettings(); const s = new Storage(join(resolveDataDir(settings.dataDir), 'data.db')); - // FTS5 rebuild pattern. - const db = (s as unknown as { db: { exec: (q: string) => void } }).db; - // Fallback to raw exec via better-sqlite3 if exposed; otherwise use public API to no-op. try { - if (db?.exec) db.exec("INSERT INTO observations_fts(observations_fts) VALUES('rebuild');"); - } catch { - // swallow — rebuild is best-effort + s.rebuildFts(); + } finally { + s.close(); } - s.close(); process.stdout.write('reindex ok\n'); }); } diff --git a/packages/storage/src/storage.ts b/packages/storage/src/storage.ts index 5a2309e..549e054 100644 --- a/packages/storage/src/storage.ts +++ b/packages/storage/src/storage.ts @@ -218,6 +218,10 @@ export class Storage { })); } + rebuildFts(): void { + this.db.exec("INSERT INTO observations_fts(observations_fts) VALUES('rebuild');"); + } + // --- embeddings --- putEmbedding(observationId: number, model: string, vec: Float32Array): void { diff --git a/packages/storage/test/storage.test.ts b/packages/storage/test/storage.test.ts index 06fda68..1a078b9 100644 --- a/packages/storage/test/storage.test.ts +++ b/packages/storage/test/storage.test.ts @@ -108,6 +108,26 @@ describe('Storage', () => { expect(hits[0]?.snippet).toContain('[auth]'); }); + it('rebuildFts leaves FTS queryable', () => { + storage.createSession({ + id: 'sfts', + ide: 'claude-code', + cwd: null, + started_at: Date.now(), + metadata: null, + }); + storage.insertObservation({ + session_id: 'sfts', + kind: 'note', + content: 'token bucket rate limiter', + compressed: true, + intensity: 'full', + }); + expect(() => storage.rebuildFts()).not.toThrow(); + const hits = storage.searchFts('bucket'); + expect(hits.length).toBeGreaterThan(0); + }); + it('stores and retrieves embeddings', () => { storage.createSession({ id: 's2',