@creeperhost/modlens-mcp 1.6.0 → 1.6.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 (89) hide show
  1. package/README.md +950 -938
  2. package/dist/cli.js +267 -267
  3. package/dist/generated/sqlite/internal/class.d.ts.map +1 -1
  4. package/dist/generated/sqlite/internal/class.js +4 -3
  5. package/dist/generated/sqlite/internal/class.js.map +1 -1
  6. package/dist/launcher.js +4 -1
  7. package/dist/launcher.js.map +1 -1
  8. package/dist/repositories/embeddings-sqlite.js +21 -21
  9. package/dist/repositories/embeddings.js +28 -28
  10. package/dist/repositories/mod.js +2 -2
  11. package/dist/search-adapter.js +51 -51
  12. package/dist/tools/compat-check.js +9 -9
  13. package/dist/tools/embed-registry.js +9 -9
  14. package/dist/tools/graphify.js +14 -14
  15. package/dist/tools/mc-fts.js +2 -2
  16. package/dist/tools/mixin-scan.d.ts +1 -1
  17. package/dist/tools/mixin-scan.js +16 -16
  18. package/dist/tools/version-diff.js +3 -3
  19. package/package.json +70 -62
  20. package/prisma/backends/schema.sqlite.prisma +215 -215
  21. package/prisma/schema.prisma +303 -303
  22. package/scripts/backup.mjs +151 -151
  23. package/scripts/check-atm10.mjs +27 -27
  24. package/scripts/check-sync.mjs +13 -13
  25. package/scripts/create-fts-index.mjs +50 -50
  26. package/scripts/enable-pgvector.mjs +90 -90
  27. package/scripts/enable-sqlite-vec.mjs +158 -158
  28. package/scripts/migrate-backend.mjs +188 -188
  29. package/scripts/migrate-embed-provenance.mjs +80 -80
  30. package/scripts/migrate-metadata-source.mjs +62 -62
  31. package/scripts/migrate-mod-source-files.mjs +67 -67
  32. package/scripts/post-update.mjs +77 -77
  33. package/scripts/verify-atm10.mjs +63 -63
  34. package/dist/access-flags.test.d.ts +0 -2
  35. package/dist/access-flags.test.d.ts.map +0 -1
  36. package/dist/access-flags.test.js +0 -189
  37. package/dist/access-flags.test.js.map +0 -1
  38. package/dist/fetch-utils.test.d.ts +0 -2
  39. package/dist/fetch-utils.test.d.ts.map +0 -1
  40. package/dist/fetch-utils.test.js +0 -133
  41. package/dist/fetch-utils.test.js.map +0 -1
  42. package/dist/mappings.test.d.ts +0 -2
  43. package/dist/mappings.test.d.ts.map +0 -1
  44. package/dist/mappings.test.js +0 -161
  45. package/dist/mappings.test.js.map +0 -1
  46. package/dist/processor.test.d.ts +0 -2
  47. package/dist/processor.test.d.ts.map +0 -1
  48. package/dist/processor.test.js +0 -629
  49. package/dist/processor.test.js.map +0 -1
  50. package/dist/search.test.d.ts +0 -2
  51. package/dist/search.test.d.ts.map +0 -1
  52. package/dist/search.test.js +0 -71
  53. package/dist/search.test.js.map +0 -1
  54. package/dist/security.test.d.ts +0 -2
  55. package/dist/security.test.d.ts.map +0 -1
  56. package/dist/security.test.js +0 -687
  57. package/dist/security.test.js.map +0 -1
  58. package/dist/tools/compat-check.test.d.ts +0 -2
  59. package/dist/tools/compat-check.test.d.ts.map +0 -1
  60. package/dist/tools/compat-check.test.js +0 -179
  61. package/dist/tools/compat-check.test.js.map +0 -1
  62. package/dist/tools/diagnostics.test.d.ts +0 -2
  63. package/dist/tools/diagnostics.test.d.ts.map +0 -1
  64. package/dist/tools/diagnostics.test.js +0 -168
  65. package/dist/tools/diagnostics.test.js.map +0 -1
  66. package/dist/tools/graphify.test.d.ts +0 -2
  67. package/dist/tools/graphify.test.d.ts.map +0 -1
  68. package/dist/tools/graphify.test.js +0 -39
  69. package/dist/tools/graphify.test.js.map +0 -1
  70. package/dist/tools/ingest.test.d.ts +0 -2
  71. package/dist/tools/ingest.test.d.ts.map +0 -1
  72. package/dist/tools/ingest.test.js +0 -340
  73. package/dist/tools/ingest.test.js.map +0 -1
  74. package/dist/tools/mod-tags.test.d.ts +0 -2
  75. package/dist/tools/mod-tags.test.d.ts.map +0 -1
  76. package/dist/tools/mod-tags.test.js +0 -75
  77. package/dist/tools/mod-tags.test.js.map +0 -1
  78. package/dist/tools/packtools.test.d.ts +0 -2
  79. package/dist/tools/packtools.test.d.ts.map +0 -1
  80. package/dist/tools/packtools.test.js +0 -99
  81. package/dist/tools/packtools.test.js.map +0 -1
  82. package/dist/tools/version-diff.test.d.ts +0 -2
  83. package/dist/tools/version-diff.test.d.ts.map +0 -1
  84. package/dist/tools/version-diff.test.js +0 -102
  85. package/dist/tools/version-diff.test.js.map +0 -1
  86. package/dist/validate.test.d.ts +0 -2
  87. package/dist/validate.test.d.ts.map +0 -1
  88. package/dist/validate.test.js +0 -73
  89. package/dist/validate.test.js.map +0 -1
@@ -1,90 +1,90 @@
1
- /**
2
- * Enables the pgvector extension and adds embedding columns to the three tables
3
- * that support semantic search: doc_entries, primers, mc_source_files.
4
- *
5
- * Run once after `docker compose up`:
6
- * npm run db:vector
7
- *
8
- * Safe to re-run — all statements use IF NOT EXISTS / IF NOT EXISTS.
9
- */
10
- import { PrismaClient } from "@prisma/client";
11
- import { readFileSync, existsSync } from "fs";
12
- import { join, dirname } from "path";
13
- import { fileURLToPath } from "url";
14
-
15
- const __dirname = dirname(fileURLToPath(import.meta.url));
16
- const envPath = join(__dirname, "..", ".env");
17
- if (existsSync(envPath)) {
18
- for (const line of readFileSync(envPath, "utf8").split("\n")) {
19
- const m = line.match(/^([^#=]+)=(.*)$/);
20
- if (m) process.env[m[1].trim()] ??= m[2].trim().replace(/^["']|["']$/g, "");
21
- }
22
- }
23
-
24
- const prisma = new PrismaClient();
25
-
26
- async function main() {
27
- // 1. Enable extension
28
- await prisma.$executeRawUnsafe(`CREATE EXTENSION IF NOT EXISTS vector`);
29
- console.log("✓ pgvector extension enabled");
30
-
31
- // 2. Add or resize embedding columns
32
- const dim = parseInt(process.env.OLLAMA_EMBED_DIM ?? "768", 10);
33
- for (const table of ["doc_entries", "primers", "mc_source_files", "mod_source_files"]) {
34
- // Check existing column dimension (atttypmod = dimension for pgvector)
35
- const rows = await prisma.$queryRawUnsafe(
36
- `SELECT atttypmod AS dim FROM pg_attribute
37
- JOIN pg_class ON pg_attribute.attrelid = pg_class.oid
38
- WHERE pg_class.relname = $1 AND pg_attribute.attname = 'embedding' AND pg_attribute.attnum > 0`,
39
- table
40
- );
41
- const currentDim = Array.isArray(rows) && rows.length > 0 ? Number(rows[0].dim) : null;
42
-
43
- if (currentDim === null) {
44
- await prisma.$executeRawUnsafe(
45
- `ALTER TABLE ${table} ADD COLUMN embedding vector(${dim})`
46
- );
47
- console.log(`✓ ${table}.embedding added (dim=${dim})`);
48
- } else if (currentDim !== dim) {
49
- // Drop the HNSW index first — ALTER TYPE fails if index exists
50
- const idxName = `${table}_embedding_idx`;
51
- await prisma.$executeRawUnsafe(`DROP INDEX IF EXISTS ${idxName}`);
52
- await prisma.$executeRawUnsafe(
53
- `ALTER TABLE ${table} ALTER COLUMN embedding TYPE vector(${dim})`
54
- );
55
- console.log(`✓ ${table}.embedding resized ${currentDim} → ${dim}`);
56
- } else {
57
- console.log(` ${table}.embedding already vector(${dim}) — skipping`);
58
- }
59
- }
60
-
61
- // 3. HNSW indexes for fast approximate nearest-neighbour
62
- const indexes = [
63
- ["doc_entries_embedding_idx", "doc_entries"],
64
- ["primers_embedding_idx", "primers"],
65
- ["mc_source_files_embedding_idx", "mc_source_files"],
66
- ["mod_source_files_embedding_idx","mod_source_files"],
67
- ];
68
- for (const [idxName, table] of indexes) {
69
- const existing = await prisma.$queryRawUnsafe(
70
- `SELECT 1 FROM pg_indexes WHERE schemaname='public' AND indexname=$1 LIMIT 1`,
71
- idxName,
72
- );
73
- if (Array.isArray(existing) && existing.length > 0) {
74
- console.log(` (index ${idxName} already exists — skipping)`);
75
- } else {
76
- console.log(` Creating HNSW index ${idxName}…`);
77
- await prisma.$executeRawUnsafe(
78
- `CREATE INDEX ${idxName} ON ${table} USING hnsw (embedding vector_cosine_ops)`
79
- );
80
- console.log(`✓ ${idxName} created`);
81
- }
82
- }
83
-
84
- console.log("\nDone — semantic search is ready.");
85
- console.log("Next: set OLLAMA_URL in .env and run node dist/cli.js backfill-embeddings --type docs");
86
- }
87
-
88
- main()
89
- .catch((err) => { console.error("Failed:", err); process.exit(1); })
90
- .finally(() => prisma.$disconnect());
1
+ /**
2
+ * Enables the pgvector extension and adds embedding columns to the three tables
3
+ * that support semantic search: doc_entries, primers, mc_source_files.
4
+ *
5
+ * Run once after `docker compose up`:
6
+ * npm run db:vector
7
+ *
8
+ * Safe to re-run — all statements use IF NOT EXISTS / IF NOT EXISTS.
9
+ */
10
+ import { PrismaClient } from "@prisma/client";
11
+ import { readFileSync, existsSync } from "fs";
12
+ import { join, dirname } from "path";
13
+ import { fileURLToPath } from "url";
14
+
15
+ const __dirname = dirname(fileURLToPath(import.meta.url));
16
+ const envPath = join(__dirname, "..", ".env");
17
+ if (existsSync(envPath)) {
18
+ for (const line of readFileSync(envPath, "utf8").split("\n")) {
19
+ const m = line.match(/^([^#=]+)=(.*)$/);
20
+ if (m) process.env[m[1].trim()] ??= m[2].trim().replace(/^["']|["']$/g, "");
21
+ }
22
+ }
23
+
24
+ const prisma = new PrismaClient();
25
+
26
+ async function main() {
27
+ // 1. Enable extension
28
+ await prisma.$executeRawUnsafe(`CREATE EXTENSION IF NOT EXISTS vector`);
29
+ console.log("✓ pgvector extension enabled");
30
+
31
+ // 2. Add or resize embedding columns
32
+ const dim = parseInt(process.env.OLLAMA_EMBED_DIM ?? "768", 10);
33
+ for (const table of ["doc_entries", "primers", "mc_source_files", "mod_source_files"]) {
34
+ // Check existing column dimension (atttypmod = dimension for pgvector)
35
+ const rows = await prisma.$queryRawUnsafe(
36
+ `SELECT atttypmod AS dim FROM pg_attribute
37
+ JOIN pg_class ON pg_attribute.attrelid = pg_class.oid
38
+ WHERE pg_class.relname = $1 AND pg_attribute.attname = 'embedding' AND pg_attribute.attnum > 0`,
39
+ table
40
+ );
41
+ const currentDim = Array.isArray(rows) && rows.length > 0 ? Number(rows[0].dim) : null;
42
+
43
+ if (currentDim === null) {
44
+ await prisma.$executeRawUnsafe(
45
+ `ALTER TABLE ${table} ADD COLUMN embedding vector(${dim})`
46
+ );
47
+ console.log(`✓ ${table}.embedding added (dim=${dim})`);
48
+ } else if (currentDim !== dim) {
49
+ // Drop the HNSW index first — ALTER TYPE fails if index exists
50
+ const idxName = `${table}_embedding_idx`;
51
+ await prisma.$executeRawUnsafe(`DROP INDEX IF EXISTS ${idxName}`);
52
+ await prisma.$executeRawUnsafe(
53
+ `ALTER TABLE ${table} ALTER COLUMN embedding TYPE vector(${dim})`
54
+ );
55
+ console.log(`✓ ${table}.embedding resized ${currentDim} → ${dim}`);
56
+ } else {
57
+ console.log(` ${table}.embedding already vector(${dim}) — skipping`);
58
+ }
59
+ }
60
+
61
+ // 3. HNSW indexes for fast approximate nearest-neighbour
62
+ const indexes = [
63
+ ["doc_entries_embedding_idx", "doc_entries"],
64
+ ["primers_embedding_idx", "primers"],
65
+ ["mc_source_files_embedding_idx", "mc_source_files"],
66
+ ["mod_source_files_embedding_idx","mod_source_files"],
67
+ ];
68
+ for (const [idxName, table] of indexes) {
69
+ const existing = await prisma.$queryRawUnsafe(
70
+ `SELECT 1 FROM pg_indexes WHERE schemaname='public' AND indexname=$1 LIMIT 1`,
71
+ idxName,
72
+ );
73
+ if (Array.isArray(existing) && existing.length > 0) {
74
+ console.log(` (index ${idxName} already exists — skipping)`);
75
+ } else {
76
+ console.log(` Creating HNSW index ${idxName}…`);
77
+ await prisma.$executeRawUnsafe(
78
+ `CREATE INDEX ${idxName} ON ${table} USING hnsw (embedding vector_cosine_ops)`
79
+ );
80
+ console.log(`✓ ${idxName} created`);
81
+ }
82
+ }
83
+
84
+ console.log("\nDone — semantic search is ready.");
85
+ console.log("Next: set OLLAMA_URL in .env and run node dist/cli.js backfill-embeddings --type docs");
86
+ }
87
+
88
+ main()
89
+ .catch((err) => { console.error("Failed:", err); process.exit(1); })
90
+ .finally(() => prisma.$disconnect());
@@ -1,158 +1,158 @@
1
- #!/usr/bin/env node
2
- // scripts/enable-sqlite-vec.mjs
3
- // Creates FTS5 virtual tables, sync triggers, and sqlite-vec vec0 tables.
4
- // Run after: npm run db:push:sqlite
5
-
6
- import Database from "better-sqlite3";
7
- import { createRequire } from "module";
8
-
9
- const url = process.env.DATABASE_URL ?? "";
10
- const dbPath = url.replace(/^file:\/\//, "").replace(/^file:/, "");
11
- if (!dbPath) {
12
- console.error("DATABASE_URL must be set to a file: path");
13
- process.exit(1);
14
- }
15
-
16
- const db = new Database(dbPath);
17
-
18
- // Load sqlite-vec extension
19
- const require = createRequire(import.meta.url);
20
- try {
21
- const sqliteVec = require("sqlite-vec");
22
- sqliteVec.load(db);
23
- console.log("sqlite-vec loaded");
24
- } catch (e) {
25
- console.warn("sqlite-vec not available — vector search disabled:", e.message);
26
- }
27
-
28
- db.exec(`
29
- -- FTS5 virtual table for Minecraft source files
30
- CREATE VIRTUAL TABLE IF NOT EXISTS fts_mc_source (
31
- content,
32
- class_name,
33
- mc_version_id UNINDEXED
34
- ) USING fts5(content, class_name, mc_version_id UNINDEXED);
35
-
36
- -- Sync triggers for mc_source_files
37
- CREATE TRIGGER IF NOT EXISTS mc_source_fts_insert
38
- AFTER INSERT ON mc_source_files BEGIN
39
- INSERT INTO fts_mc_source(rowid, content, class_name, mc_version_id)
40
- VALUES (new.id, new.content, new.class_name, new.mc_version_id);
41
- END;
42
-
43
- CREATE TRIGGER IF NOT EXISTS mc_source_fts_delete
44
- AFTER DELETE ON mc_source_files BEGIN
45
- DELETE FROM fts_mc_source WHERE rowid = old.id;
46
- END;
47
-
48
- CREATE TRIGGER IF NOT EXISTS mc_source_fts_update
49
- AFTER UPDATE ON mc_source_files BEGIN
50
- DELETE FROM fts_mc_source WHERE rowid = old.id;
51
- INSERT INTO fts_mc_source(rowid, content, class_name, mc_version_id)
52
- VALUES (new.id, new.content, new.class_name, new.mc_version_id);
53
- END;
54
-
55
- -- FTS5 virtual table for doc entries
56
- CREATE VIRTUAL TABLE IF NOT EXISTS fts_doc_entries (
57
- title,
58
- summary,
59
- url UNINDEXED,
60
- category UNINDEXED
61
- ) USING fts5(title, summary, url UNINDEXED, category UNINDEXED);
62
-
63
- -- Sync triggers for doc_entries
64
- CREATE TRIGGER IF NOT EXISTS doc_entries_fts_insert
65
- AFTER INSERT ON doc_entries BEGIN
66
- INSERT INTO fts_doc_entries(rowid, title, summary, url, category)
67
- VALUES (new.id, new.title, new.summary, new.url, new.category);
68
- END;
69
-
70
- CREATE TRIGGER IF NOT EXISTS doc_entries_fts_delete
71
- AFTER DELETE ON doc_entries BEGIN
72
- DELETE FROM fts_doc_entries WHERE rowid = old.id;
73
- END;
74
-
75
- CREATE TRIGGER IF NOT EXISTS doc_entries_fts_update
76
- AFTER UPDATE ON doc_entries BEGIN
77
- DELETE FROM fts_doc_entries WHERE rowid = old.id;
78
- INSERT INTO fts_doc_entries(rowid, title, summary, url, category)
79
- VALUES (new.id, new.title, new.summary, new.url, new.category);
80
- END;
81
-
82
- -- FTS5 virtual table for primers
83
- CREATE VIRTUAL TABLE IF NOT EXISTS fts_primers (
84
- title,
85
- summary,
86
- content
87
- ) USING fts5(title, summary, content);
88
-
89
- -- Sync triggers for primers
90
- CREATE TRIGGER IF NOT EXISTS primers_fts_insert
91
- AFTER INSERT ON primers BEGIN
92
- INSERT INTO fts_primers(rowid, title, summary, content)
93
- VALUES (new.id, new.title, new.summary, new.content);
94
- END;
95
-
96
- CREATE TRIGGER IF NOT EXISTS primers_fts_delete
97
- AFTER DELETE ON primers BEGIN
98
- DELETE FROM fts_primers WHERE rowid = old.id;
99
- END;
100
-
101
- CREATE TRIGGER IF NOT EXISTS primers_fts_update
102
- AFTER UPDATE ON primers BEGIN
103
- DELETE FROM fts_primers WHERE rowid = old.id;
104
- INSERT INTO fts_primers(rowid, title, summary, content)
105
- VALUES (new.id, new.title, new.summary, new.content);
106
- END;
107
-
108
- -- FTS5 virtual table for mod source files (fabric / neoforge / forge / quilt)
109
- CREATE VIRTUAL TABLE IF NOT EXISTS fts_mod_source (
110
- content,
111
- class_name,
112
- mod_id UNINDEXED
113
- ) USING fts5(content, class_name, mod_id UNINDEXED);
114
-
115
- -- Sync triggers for mod_source_files
116
- CREATE TRIGGER IF NOT EXISTS mod_source_fts_insert
117
- AFTER INSERT ON mod_source_files BEGIN
118
- INSERT INTO fts_mod_source(rowid, content, class_name, mod_id)
119
- VALUES (new.id, new.content, new.class_name, new.mod_id);
120
- END;
121
-
122
- CREATE TRIGGER IF NOT EXISTS mod_source_fts_delete
123
- AFTER DELETE ON mod_source_files BEGIN
124
- DELETE FROM fts_mod_source WHERE rowid = old.id;
125
- END;
126
-
127
- CREATE TRIGGER IF NOT EXISTS mod_source_fts_update
128
- AFTER UPDATE ON mod_source_files BEGIN
129
- DELETE FROM fts_mod_source WHERE rowid = old.id;
130
- INSERT INTO fts_mod_source(rowid, content, class_name, mod_id)
131
- VALUES (new.id, new.content, new.class_name, new.mod_id);
132
- END;
133
- `);
134
-
135
- console.log("FTS5 virtual tables and triggers created");
136
-
137
- // sqlite-vec tables (created only if the extension loaded)
138
- try {
139
- db.exec(`
140
- CREATE VIRTUAL TABLE IF NOT EXISTS vec_mc_source
141
- USING vec0(embedding float[768]);
142
-
143
- CREATE VIRTUAL TABLE IF NOT EXISTS vec_mod_source
144
- USING vec0(embedding float[768]);
145
-
146
- CREATE VIRTUAL TABLE IF NOT EXISTS vec_doc_entries
147
- USING vec0(embedding float[768]);
148
-
149
- CREATE VIRTUAL TABLE IF NOT EXISTS vec_primers
150
- USING vec0(embedding float[768]);
151
- `);
152
- console.log("sqlite-vec vec0 tables created (dim=768)");
153
- } catch (e) {
154
- console.warn("Skipping vec0 tables (sqlite-vec unavailable):", e.message);
155
- }
156
-
157
- db.close();
158
- console.log("Done.");
1
+ #!/usr/bin/env node
2
+ // scripts/enable-sqlite-vec.mjs
3
+ // Creates FTS5 virtual tables, sync triggers, and sqlite-vec vec0 tables.
4
+ // Run after: npm run db:push:sqlite
5
+
6
+ import Database from "better-sqlite3";
7
+ import { createRequire } from "module";
8
+
9
+ const url = process.env.DATABASE_URL ?? "";
10
+ const dbPath = url.replace(/^file:\/\//, "").replace(/^file:/, "");
11
+ if (!dbPath) {
12
+ console.error("DATABASE_URL must be set to a file: path");
13
+ process.exit(1);
14
+ }
15
+
16
+ const db = new Database(dbPath);
17
+
18
+ // Load sqlite-vec extension
19
+ const require = createRequire(import.meta.url);
20
+ try {
21
+ const sqliteVec = require("sqlite-vec");
22
+ sqliteVec.load(db);
23
+ console.log("sqlite-vec loaded");
24
+ } catch (e) {
25
+ console.warn("sqlite-vec not available — vector search disabled:", e.message);
26
+ }
27
+
28
+ db.exec(`
29
+ -- FTS5 virtual table for Minecraft source files
30
+ CREATE VIRTUAL TABLE IF NOT EXISTS fts_mc_source (
31
+ content,
32
+ class_name,
33
+ mc_version_id UNINDEXED
34
+ ) USING fts5(content, class_name, mc_version_id UNINDEXED);
35
+
36
+ -- Sync triggers for mc_source_files
37
+ CREATE TRIGGER IF NOT EXISTS mc_source_fts_insert
38
+ AFTER INSERT ON mc_source_files BEGIN
39
+ INSERT INTO fts_mc_source(rowid, content, class_name, mc_version_id)
40
+ VALUES (new.id, new.content, new.class_name, new.mc_version_id);
41
+ END;
42
+
43
+ CREATE TRIGGER IF NOT EXISTS mc_source_fts_delete
44
+ AFTER DELETE ON mc_source_files BEGIN
45
+ DELETE FROM fts_mc_source WHERE rowid = old.id;
46
+ END;
47
+
48
+ CREATE TRIGGER IF NOT EXISTS mc_source_fts_update
49
+ AFTER UPDATE ON mc_source_files BEGIN
50
+ DELETE FROM fts_mc_source WHERE rowid = old.id;
51
+ INSERT INTO fts_mc_source(rowid, content, class_name, mc_version_id)
52
+ VALUES (new.id, new.content, new.class_name, new.mc_version_id);
53
+ END;
54
+
55
+ -- FTS5 virtual table for doc entries
56
+ CREATE VIRTUAL TABLE IF NOT EXISTS fts_doc_entries (
57
+ title,
58
+ summary,
59
+ url UNINDEXED,
60
+ category UNINDEXED
61
+ ) USING fts5(title, summary, url UNINDEXED, category UNINDEXED);
62
+
63
+ -- Sync triggers for doc_entries
64
+ CREATE TRIGGER IF NOT EXISTS doc_entries_fts_insert
65
+ AFTER INSERT ON doc_entries BEGIN
66
+ INSERT INTO fts_doc_entries(rowid, title, summary, url, category)
67
+ VALUES (new.id, new.title, new.summary, new.url, new.category);
68
+ END;
69
+
70
+ CREATE TRIGGER IF NOT EXISTS doc_entries_fts_delete
71
+ AFTER DELETE ON doc_entries BEGIN
72
+ DELETE FROM fts_doc_entries WHERE rowid = old.id;
73
+ END;
74
+
75
+ CREATE TRIGGER IF NOT EXISTS doc_entries_fts_update
76
+ AFTER UPDATE ON doc_entries BEGIN
77
+ DELETE FROM fts_doc_entries WHERE rowid = old.id;
78
+ INSERT INTO fts_doc_entries(rowid, title, summary, url, category)
79
+ VALUES (new.id, new.title, new.summary, new.url, new.category);
80
+ END;
81
+
82
+ -- FTS5 virtual table for primers
83
+ CREATE VIRTUAL TABLE IF NOT EXISTS fts_primers (
84
+ title,
85
+ summary,
86
+ content
87
+ ) USING fts5(title, summary, content);
88
+
89
+ -- Sync triggers for primers
90
+ CREATE TRIGGER IF NOT EXISTS primers_fts_insert
91
+ AFTER INSERT ON primers BEGIN
92
+ INSERT INTO fts_primers(rowid, title, summary, content)
93
+ VALUES (new.id, new.title, new.summary, new.content);
94
+ END;
95
+
96
+ CREATE TRIGGER IF NOT EXISTS primers_fts_delete
97
+ AFTER DELETE ON primers BEGIN
98
+ DELETE FROM fts_primers WHERE rowid = old.id;
99
+ END;
100
+
101
+ CREATE TRIGGER IF NOT EXISTS primers_fts_update
102
+ AFTER UPDATE ON primers BEGIN
103
+ DELETE FROM fts_primers WHERE rowid = old.id;
104
+ INSERT INTO fts_primers(rowid, title, summary, content)
105
+ VALUES (new.id, new.title, new.summary, new.content);
106
+ END;
107
+
108
+ -- FTS5 virtual table for mod source files (fabric / neoforge / forge / quilt)
109
+ CREATE VIRTUAL TABLE IF NOT EXISTS fts_mod_source (
110
+ content,
111
+ class_name,
112
+ mod_id UNINDEXED
113
+ ) USING fts5(content, class_name, mod_id UNINDEXED);
114
+
115
+ -- Sync triggers for mod_source_files
116
+ CREATE TRIGGER IF NOT EXISTS mod_source_fts_insert
117
+ AFTER INSERT ON mod_source_files BEGIN
118
+ INSERT INTO fts_mod_source(rowid, content, class_name, mod_id)
119
+ VALUES (new.id, new.content, new.class_name, new.mod_id);
120
+ END;
121
+
122
+ CREATE TRIGGER IF NOT EXISTS mod_source_fts_delete
123
+ AFTER DELETE ON mod_source_files BEGIN
124
+ DELETE FROM fts_mod_source WHERE rowid = old.id;
125
+ END;
126
+
127
+ CREATE TRIGGER IF NOT EXISTS mod_source_fts_update
128
+ AFTER UPDATE ON mod_source_files BEGIN
129
+ DELETE FROM fts_mod_source WHERE rowid = old.id;
130
+ INSERT INTO fts_mod_source(rowid, content, class_name, mod_id)
131
+ VALUES (new.id, new.content, new.class_name, new.mod_id);
132
+ END;
133
+ `);
134
+
135
+ console.log("FTS5 virtual tables and triggers created");
136
+
137
+ // sqlite-vec tables (created only if the extension loaded)
138
+ try {
139
+ db.exec(`
140
+ CREATE VIRTUAL TABLE IF NOT EXISTS vec_mc_source
141
+ USING vec0(embedding float[768]);
142
+
143
+ CREATE VIRTUAL TABLE IF NOT EXISTS vec_mod_source
144
+ USING vec0(embedding float[768]);
145
+
146
+ CREATE VIRTUAL TABLE IF NOT EXISTS vec_doc_entries
147
+ USING vec0(embedding float[768]);
148
+
149
+ CREATE VIRTUAL TABLE IF NOT EXISTS vec_primers
150
+ USING vec0(embedding float[768]);
151
+ `);
152
+ console.log("sqlite-vec vec0 tables created (dim=768)");
153
+ } catch (e) {
154
+ console.warn("Skipping vec0 tables (sqlite-vec unavailable):", e.message);
155
+ }
156
+
157
+ db.close();
158
+ console.log("Done.");