ralph-hero-knowledge-index 0.1.19 → 0.1.20
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.
- package/.claude-plugin/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/dist/db.js +23 -1
- package/dist/db.js.map +1 -1
- package/dist/reindex.js +1 -1
- package/package.json +1 -1
- package/src/__tests__/db.test.ts +165 -0
- package/src/__tests__/reindex.test.ts +2 -2
- package/src/db.ts +27 -1
- package/src/reindex.ts +1 -1
- package/test_script.js +50 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralph-knowledge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "Knowledge graph for ralph-hero: semantic search, relationship traversal, and document indexing across thoughts/ documents. Optional companion to ralph-hero.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Chad Dubiel",
|
package/.mcp.json
CHANGED
package/dist/db.js
CHANGED
|
@@ -71,6 +71,18 @@ export class KnowledgeDB {
|
|
|
71
71
|
key TEXT PRIMARY KEY,
|
|
72
72
|
value TEXT
|
|
73
73
|
);
|
|
74
|
+
|
|
75
|
+
CREATE TABLE IF NOT EXISTS chunks (
|
|
76
|
+
id TEXT PRIMARY KEY,
|
|
77
|
+
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
|
78
|
+
chunk_index INTEGER NOT NULL,
|
|
79
|
+
content TEXT NOT NULL,
|
|
80
|
+
char_start INTEGER NOT NULL,
|
|
81
|
+
char_end INTEGER NOT NULL,
|
|
82
|
+
context_prefix TEXT NOT NULL DEFAULT '',
|
|
83
|
+
UNIQUE(document_id, chunk_index)
|
|
84
|
+
);
|
|
85
|
+
CREATE INDEX IF NOT EXISTS idx_chunks_document_id ON chunks(document_id);
|
|
74
86
|
`);
|
|
75
87
|
// Migration: add is_stub column for databases created before it existed.
|
|
76
88
|
// SQLite has no IF NOT EXISTS for ALTER TABLE ADD COLUMN, so we catch the
|
|
@@ -81,6 +93,16 @@ export class KnowledgeDB {
|
|
|
81
93
|
catch {
|
|
82
94
|
// Column already exists — expected for new databases
|
|
83
95
|
}
|
|
96
|
+
// Migration: add memory_tier column (schema v3) for databases created before it existed.
|
|
97
|
+
// Uses the same try/catch pattern as is_stub. CHECK constraint restricts values to
|
|
98
|
+
// 'doc' (existing documents), 'raw' (dream-loop raw memories), 'reflection' (synthesized).
|
|
99
|
+
try {
|
|
100
|
+
this.db.exec("ALTER TABLE documents ADD COLUMN memory_tier TEXT NOT NULL DEFAULT 'doc' CHECK(memory_tier IN ('doc','raw','reflection'))");
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Column already exists — expected for new databases
|
|
104
|
+
}
|
|
105
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS idx_documents_memory_tier ON documents(memory_tier)");
|
|
84
106
|
// Migration: rebuild relationships table for databases created before the
|
|
85
107
|
// context column, post_mortem/untyped CHECK types, and target_id FK were added.
|
|
86
108
|
// SQLite cannot ALTER CHECK constraints, so a full table rebuild is required.
|
|
@@ -306,7 +328,7 @@ export class KnowledgeDB {
|
|
|
306
328
|
}
|
|
307
329
|
clearAll() {
|
|
308
330
|
// outcome_events is intentionally NOT cleared — outcome data is preserved across rebuilds
|
|
309
|
-
this.db.exec("DELETE FROM relationships; DELETE FROM tags; DELETE FROM documents; DELETE FROM sync;");
|
|
331
|
+
this.db.exec("DELETE FROM chunks; DELETE FROM relationships; DELETE FROM tags; DELETE FROM documents; DELETE FROM sync;");
|
|
310
332
|
}
|
|
311
333
|
close() {
|
|
312
334
|
this.db.close();
|
package/dist/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuFpC,MAAM,OAAO,WAAW;IACb,EAAE,CAAe;IAE1B,YAAY,MAAc;QACxB,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACrC,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
package/dist/reindex.js
CHANGED
|
@@ -16,7 +16,7 @@ export async function reindex(dirs, dbPath, generate = false) {
|
|
|
16
16
|
const vec = new VectorSearch(db);
|
|
17
17
|
vec.createIndex();
|
|
18
18
|
// Schema version check — force full re-embed when embedding algorithm changes
|
|
19
|
-
const SCHEMA_VERSION = "
|
|
19
|
+
const SCHEMA_VERSION = "3";
|
|
20
20
|
const currentVersion = db.getMeta("schema_version");
|
|
21
21
|
let needsFullFtsRebuild = false;
|
|
22
22
|
if (currentVersion !== SCHEMA_VERSION) {
|
package/package.json
CHANGED
package/src/__tests__/db.test.ts
CHANGED
|
@@ -543,3 +543,168 @@ describe("documentExists", () => {
|
|
|
543
543
|
expect(db.documentExists("stub-1")).toBe(true);
|
|
544
544
|
});
|
|
545
545
|
});
|
|
546
|
+
|
|
547
|
+
describe("schema v3: chunks table", () => {
|
|
548
|
+
it("creates the chunks table with expected columns", () => {
|
|
549
|
+
// PRAGMA table_info(chunks) returns one row per column with name, type, notnull, dflt_value, pk
|
|
550
|
+
const cols = db.db
|
|
551
|
+
.prepare("PRAGMA table_info(chunks)")
|
|
552
|
+
.all() as Array<{ name: string; type: string; notnull: number; dflt_value: unknown; pk: number }>;
|
|
553
|
+
const byName = Object.fromEntries(cols.map(c => [c.name, c]));
|
|
554
|
+
|
|
555
|
+
expect(byName.id).toMatchObject({ type: "TEXT", pk: 1 });
|
|
556
|
+
expect(byName.document_id).toMatchObject({ type: "TEXT", notnull: 1 });
|
|
557
|
+
expect(byName.chunk_index).toMatchObject({ type: "INTEGER", notnull: 1 });
|
|
558
|
+
expect(byName.content).toMatchObject({ type: "TEXT", notnull: 1 });
|
|
559
|
+
expect(byName.char_start).toMatchObject({ type: "INTEGER", notnull: 1 });
|
|
560
|
+
expect(byName.char_end).toMatchObject({ type: "INTEGER", notnull: 1 });
|
|
561
|
+
expect(byName.context_prefix).toMatchObject({ type: "TEXT", notnull: 1 });
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it("creates idx_chunks_document_id index", () => {
|
|
565
|
+
const indexes = db.db
|
|
566
|
+
.prepare("PRAGMA index_list(chunks)")
|
|
567
|
+
.all() as Array<{ name: string }>;
|
|
568
|
+
const names = indexes.map(i => i.name);
|
|
569
|
+
expect(names).toContain("idx_chunks_document_id");
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
it("enforces UNIQUE(document_id, chunk_index)", () => {
|
|
573
|
+
db.upsertDocument({ id: "doc-1", path: "p", title: "t", date: null, type: null, status: null, githubIssue: null, content: "" });
|
|
574
|
+
db.db.prepare(
|
|
575
|
+
"INSERT INTO chunks (id, document_id, chunk_index, content, char_start, char_end) VALUES (?, ?, ?, ?, ?, ?)"
|
|
576
|
+
).run("doc-1#c0", "doc-1", 0, "hello", 0, 5);
|
|
577
|
+
|
|
578
|
+
expect(() => {
|
|
579
|
+
db.db.prepare(
|
|
580
|
+
"INSERT INTO chunks (id, document_id, chunk_index, content, char_start, char_end) VALUES (?, ?, ?, ?, ?, ?)"
|
|
581
|
+
).run("doc-1#c0-dup", "doc-1", 0, "hello again", 0, 11);
|
|
582
|
+
}).toThrow();
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it("cascades ON DELETE from documents", () => {
|
|
586
|
+
// better-sqlite3 needs foreign_keys pragma enabled to enforce FK constraints
|
|
587
|
+
db.db.pragma("foreign_keys = ON");
|
|
588
|
+
db.upsertDocument({ id: "doc-1", path: "p", title: "t", date: null, type: null, status: null, githubIssue: null, content: "" });
|
|
589
|
+
db.db.prepare(
|
|
590
|
+
"INSERT INTO chunks (id, document_id, chunk_index, content, char_start, char_end) VALUES (?, ?, ?, ?, ?, ?)"
|
|
591
|
+
).run("doc-1#c0", "doc-1", 0, "hello", 0, 5);
|
|
592
|
+
|
|
593
|
+
db.deleteDocument("doc-1");
|
|
594
|
+
|
|
595
|
+
const remaining = db.db.prepare("SELECT COUNT(*) AS c FROM chunks WHERE document_id = ?").get("doc-1") as { c: number };
|
|
596
|
+
expect(remaining.c).toBe(0);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it("defaults context_prefix to empty string", () => {
|
|
600
|
+
db.upsertDocument({ id: "doc-1", path: "p", title: "t", date: null, type: null, status: null, githubIssue: null, content: "" });
|
|
601
|
+
db.db.prepare(
|
|
602
|
+
"INSERT INTO chunks (id, document_id, chunk_index, content, char_start, char_end) VALUES (?, ?, ?, ?, ?, ?)"
|
|
603
|
+
).run("doc-1#c0", "doc-1", 0, "hello", 0, 5);
|
|
604
|
+
const row = db.db.prepare("SELECT context_prefix FROM chunks WHERE id = ?").get("doc-1#c0") as { context_prefix: string };
|
|
605
|
+
expect(row.context_prefix).toBe("");
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
describe("schema v3: memory_tier column", () => {
|
|
610
|
+
it("adds memory_tier column with default 'doc'", () => {
|
|
611
|
+
db.upsertDocument({ id: "doc-1", path: "p", title: "t", date: null, type: null, status: null, githubIssue: null, content: "" });
|
|
612
|
+
const row = db.db.prepare("SELECT memory_tier FROM documents WHERE id = ?").get("doc-1") as { memory_tier: string };
|
|
613
|
+
expect(row.memory_tier).toBe("doc");
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it("accepts 'raw' memory_tier values", () => {
|
|
617
|
+
expect(() => {
|
|
618
|
+
db.db.prepare(
|
|
619
|
+
"INSERT INTO documents (id, path, title, date, type, status, github_issue, content, is_stub, memory_tier) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, 'raw')"
|
|
620
|
+
).run("raw-doc", "raw/path.md", "Raw Memory", null, null, null, null, "");
|
|
621
|
+
}).not.toThrow();
|
|
622
|
+
const row = db.db.prepare("SELECT memory_tier FROM documents WHERE id = ?").get("raw-doc") as { memory_tier: string };
|
|
623
|
+
expect(row.memory_tier).toBe("raw");
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
it("accepts 'reflection' memory_tier values", () => {
|
|
627
|
+
expect(() => {
|
|
628
|
+
db.db.prepare(
|
|
629
|
+
"INSERT INTO documents (id, path, title, date, type, status, github_issue, content, is_stub, memory_tier) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, 'reflection')"
|
|
630
|
+
).run("refl-doc", "refl/path.md", "Reflection", null, null, null, null, "");
|
|
631
|
+
}).not.toThrow();
|
|
632
|
+
const row = db.db.prepare("SELECT memory_tier FROM documents WHERE id = ?").get("refl-doc") as { memory_tier: string };
|
|
633
|
+
expect(row.memory_tier).toBe("reflection");
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
it("rejects invalid memory_tier values via CHECK constraint", () => {
|
|
637
|
+
expect(() => {
|
|
638
|
+
db.db.prepare(
|
|
639
|
+
"INSERT INTO documents (id, path, title, date, type, status, github_issue, content, is_stub, memory_tier) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, 'garbage')"
|
|
640
|
+
).run("bad-doc", "bad/path.md", "Bad", null, null, null, null, "");
|
|
641
|
+
}).toThrow();
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
it("creates idx_documents_memory_tier index", () => {
|
|
645
|
+
const indexes = db.db
|
|
646
|
+
.prepare("PRAGMA index_list(documents)")
|
|
647
|
+
.all() as Array<{ name: string }>;
|
|
648
|
+
const names = indexes.map(i => i.name);
|
|
649
|
+
expect(names).toContain("idx_documents_memory_tier");
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it("preserves existing documents with default 'doc' when migrating from v2", () => {
|
|
653
|
+
// Simulate a v2 database without memory_tier column
|
|
654
|
+
const dir = mkdtempSync(join(tmpdir(), "knowledge-v3-migration-"));
|
|
655
|
+
const dbPath = join(dir, "legacy-v2.db");
|
|
656
|
+
|
|
657
|
+
const rawDb = new Database(dbPath);
|
|
658
|
+
rawDb.exec(`
|
|
659
|
+
CREATE TABLE documents (
|
|
660
|
+
id TEXT PRIMARY KEY, path TEXT, title TEXT, date TEXT, type TEXT,
|
|
661
|
+
status TEXT, github_issue INTEGER, content TEXT, is_stub INTEGER DEFAULT 0
|
|
662
|
+
);
|
|
663
|
+
CREATE TABLE tags (doc_id TEXT REFERENCES documents(id) ON DELETE CASCADE, tag TEXT, PRIMARY KEY (doc_id, tag));
|
|
664
|
+
CREATE TABLE relationships (
|
|
665
|
+
source_id TEXT REFERENCES documents(id) ON DELETE CASCADE,
|
|
666
|
+
target_id TEXT REFERENCES documents(id) ON DELETE CASCADE,
|
|
667
|
+
type TEXT CHECK(type IN ('builds_on', 'tensions', 'superseded_by', 'post_mortem', 'untyped')),
|
|
668
|
+
context TEXT,
|
|
669
|
+
PRIMARY KEY (source_id, target_id, type)
|
|
670
|
+
);
|
|
671
|
+
CREATE TABLE outcome_events (
|
|
672
|
+
id TEXT PRIMARY KEY, event_type TEXT NOT NULL, issue_number INTEGER NOT NULL,
|
|
673
|
+
session_id TEXT, timestamp TEXT NOT NULL, duration_ms INTEGER, verdict TEXT,
|
|
674
|
+
component_area TEXT, estimate TEXT, drift_count INTEGER, model TEXT,
|
|
675
|
+
agent_type TEXT, iteration_count INTEGER, payload TEXT DEFAULT '{}'
|
|
676
|
+
);
|
|
677
|
+
CREATE TABLE sync (path TEXT PRIMARY KEY, mtime INTEGER NOT NULL, indexed_at INTEGER NOT NULL);
|
|
678
|
+
CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT);
|
|
679
|
+
INSERT INTO documents (id, path, title, content) VALUES ('existing', 'existing.md', 'Existing Doc', 'content');
|
|
680
|
+
`);
|
|
681
|
+
rawDb.close();
|
|
682
|
+
|
|
683
|
+
const migrated = new KnowledgeDB(dbPath);
|
|
684
|
+
const row = migrated.db
|
|
685
|
+
.prepare("SELECT memory_tier FROM documents WHERE id = ?")
|
|
686
|
+
.get("existing") as { memory_tier: string };
|
|
687
|
+
expect(row.memory_tier).toBe("doc");
|
|
688
|
+
migrated.close();
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
describe("schema v3: clearAll includes chunks", () => {
|
|
693
|
+
it("deletes chunks along with documents", () => {
|
|
694
|
+
db.upsertDocument({ id: "doc-1", path: "p", title: "t", date: null, type: null, status: null, githubIssue: null, content: "" });
|
|
695
|
+
db.db.prepare(
|
|
696
|
+
"INSERT INTO chunks (id, document_id, chunk_index, content, char_start, char_end) VALUES (?, ?, ?, ?, ?, ?)"
|
|
697
|
+
).run("doc-1#c0", "doc-1", 0, "hello", 0, 5);
|
|
698
|
+
db.db.prepare(
|
|
699
|
+
"INSERT INTO chunks (id, document_id, chunk_index, content, char_start, char_end) VALUES (?, ?, ?, ?, ?, ?)"
|
|
700
|
+
).run("doc-1#c1", "doc-1", 1, "world", 5, 10);
|
|
701
|
+
|
|
702
|
+
const before = db.db.prepare("SELECT COUNT(*) AS c FROM chunks").get() as { c: number };
|
|
703
|
+
expect(before.c).toBe(2);
|
|
704
|
+
|
|
705
|
+
db.clearAll();
|
|
706
|
+
|
|
707
|
+
const after = db.db.prepare("SELECT COUNT(*) AS c FROM chunks").get() as { c: number };
|
|
708
|
+
expect(after.c).toBe(0);
|
|
709
|
+
});
|
|
710
|
+
});
|
|
@@ -188,7 +188,7 @@ describe("incremental reindex", () => {
|
|
|
188
188
|
|
|
189
189
|
// Verify schema version is set
|
|
190
190
|
const db1 = new KnowledgeDB(dbPath);
|
|
191
|
-
expect(db1.getMeta("schema_version")).toBe("
|
|
191
|
+
expect(db1.getMeta("schema_version")).toBe("3");
|
|
192
192
|
db1.close();
|
|
193
193
|
|
|
194
194
|
mockedEmbed.mockClear();
|
|
@@ -210,7 +210,7 @@ describe("incremental reindex", () => {
|
|
|
210
210
|
|
|
211
211
|
// Verify version was updated
|
|
212
212
|
const db3 = new KnowledgeDB(dbPath);
|
|
213
|
-
expect(db3.getMeta("schema_version")).toBe("
|
|
213
|
+
expect(db3.getMeta("schema_version")).toBe("3");
|
|
214
214
|
db3.close();
|
|
215
215
|
});
|
|
216
216
|
|
package/src/db.ts
CHANGED
|
@@ -160,6 +160,18 @@ export class KnowledgeDB {
|
|
|
160
160
|
key TEXT PRIMARY KEY,
|
|
161
161
|
value TEXT
|
|
162
162
|
);
|
|
163
|
+
|
|
164
|
+
CREATE TABLE IF NOT EXISTS chunks (
|
|
165
|
+
id TEXT PRIMARY KEY,
|
|
166
|
+
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
|
167
|
+
chunk_index INTEGER NOT NULL,
|
|
168
|
+
content TEXT NOT NULL,
|
|
169
|
+
char_start INTEGER NOT NULL,
|
|
170
|
+
char_end INTEGER NOT NULL,
|
|
171
|
+
context_prefix TEXT NOT NULL DEFAULT '',
|
|
172
|
+
UNIQUE(document_id, chunk_index)
|
|
173
|
+
);
|
|
174
|
+
CREATE INDEX IF NOT EXISTS idx_chunks_document_id ON chunks(document_id);
|
|
163
175
|
`);
|
|
164
176
|
|
|
165
177
|
// Migration: add is_stub column for databases created before it existed.
|
|
@@ -171,6 +183,20 @@ export class KnowledgeDB {
|
|
|
171
183
|
// Column already exists — expected for new databases
|
|
172
184
|
}
|
|
173
185
|
|
|
186
|
+
// Migration: add memory_tier column (schema v3) for databases created before it existed.
|
|
187
|
+
// Uses the same try/catch pattern as is_stub. CHECK constraint restricts values to
|
|
188
|
+
// 'doc' (existing documents), 'raw' (dream-loop raw memories), 'reflection' (synthesized).
|
|
189
|
+
try {
|
|
190
|
+
this.db.exec(
|
|
191
|
+
"ALTER TABLE documents ADD COLUMN memory_tier TEXT NOT NULL DEFAULT 'doc' CHECK(memory_tier IN ('doc','raw','reflection'))"
|
|
192
|
+
);
|
|
193
|
+
} catch {
|
|
194
|
+
// Column already exists — expected for new databases
|
|
195
|
+
}
|
|
196
|
+
this.db.exec(
|
|
197
|
+
"CREATE INDEX IF NOT EXISTS idx_documents_memory_tier ON documents(memory_tier)"
|
|
198
|
+
);
|
|
199
|
+
|
|
174
200
|
// Migration: rebuild relationships table for databases created before the
|
|
175
201
|
// context column, post_mortem/untyped CHECK types, and target_id FK were added.
|
|
176
202
|
// SQLite cannot ALTER CHECK constraints, so a full table rebuild is required.
|
|
@@ -448,7 +474,7 @@ export class KnowledgeDB {
|
|
|
448
474
|
|
|
449
475
|
clearAll(): void {
|
|
450
476
|
// outcome_events is intentionally NOT cleared — outcome data is preserved across rebuilds
|
|
451
|
-
this.db.exec("DELETE FROM relationships; DELETE FROM tags; DELETE FROM documents; DELETE FROM sync;");
|
|
477
|
+
this.db.exec("DELETE FROM chunks; DELETE FROM relationships; DELETE FROM tags; DELETE FROM documents; DELETE FROM sync;");
|
|
452
478
|
}
|
|
453
479
|
|
|
454
480
|
close(): void {
|
package/src/reindex.ts
CHANGED
|
@@ -19,7 +19,7 @@ export async function reindex(dirs: string[], dbPath: string, generate: boolean
|
|
|
19
19
|
vec.createIndex();
|
|
20
20
|
|
|
21
21
|
// Schema version check — force full re-embed when embedding algorithm changes
|
|
22
|
-
const SCHEMA_VERSION = "
|
|
22
|
+
const SCHEMA_VERSION = "3";
|
|
23
23
|
const currentVersion = db.getMeta("schema_version");
|
|
24
24
|
let needsFullFtsRebuild = false;
|
|
25
25
|
if (currentVersion !== SCHEMA_VERSION) {
|
package/test_script.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const Database = require("better-sqlite3");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
|
|
6
|
+
// Create a fresh test DB
|
|
7
|
+
const testDir = fs.mkdtempSync(path.join(os.tmpdir(), "fts-bug-"));
|
|
8
|
+
const dbPath = path.join(testDir, "test.db");
|
|
9
|
+
|
|
10
|
+
// Create a fresh database
|
|
11
|
+
const db = new Database(dbPath);
|
|
12
|
+
db.pragma("journal_mode = WAL");
|
|
13
|
+
|
|
14
|
+
// Create basic schema like KnowledgeDB does
|
|
15
|
+
db.exec(`
|
|
16
|
+
CREATE TABLE IF NOT EXISTS documents (
|
|
17
|
+
id TEXT PRIMARY KEY,
|
|
18
|
+
path TEXT,
|
|
19
|
+
title TEXT,
|
|
20
|
+
date TEXT,
|
|
21
|
+
type TEXT,
|
|
22
|
+
status TEXT,
|
|
23
|
+
github_issue INTEGER,
|
|
24
|
+
content TEXT,
|
|
25
|
+
is_stub INTEGER DEFAULT 0
|
|
26
|
+
);
|
|
27
|
+
`);
|
|
28
|
+
|
|
29
|
+
// Insert a test document
|
|
30
|
+
db.prepare(`
|
|
31
|
+
INSERT INTO documents (id, path, title, date, type, status, content)
|
|
32
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
33
|
+
`).run("test-doc", "test.md", "Test Doc", "2026-04-03", "idea", "active", "searchable content");
|
|
34
|
+
|
|
35
|
+
// Now try to query the FTS table that doesn't exist
|
|
36
|
+
try {
|
|
37
|
+
const results = db.prepare(`
|
|
38
|
+
SELECT d.id FROM documents_fts
|
|
39
|
+
JOIN documents d ON d.rowid = documents_fts.rowid
|
|
40
|
+
WHERE documents_fts MATCH ?
|
|
41
|
+
`).all("test");
|
|
42
|
+
console.log("SUCCESS: Got results:", results);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.log("ERROR (bug confirmed):");
|
|
45
|
+
console.log(" Message:", err.message);
|
|
46
|
+
console.log(" Code:", err.code);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
db.close();
|
|
50
|
+
fs.rmSync(testDir, { recursive: true });
|