@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.
- package/README.md +950 -938
- package/dist/cli.js +267 -267
- package/dist/generated/sqlite/internal/class.d.ts.map +1 -1
- package/dist/generated/sqlite/internal/class.js +4 -3
- package/dist/generated/sqlite/internal/class.js.map +1 -1
- package/dist/launcher.js +4 -1
- package/dist/launcher.js.map +1 -1
- package/dist/repositories/embeddings-sqlite.js +21 -21
- package/dist/repositories/embeddings.js +28 -28
- package/dist/repositories/mod.js +2 -2
- package/dist/search-adapter.js +51 -51
- package/dist/tools/compat-check.js +9 -9
- package/dist/tools/embed-registry.js +9 -9
- package/dist/tools/graphify.js +14 -14
- package/dist/tools/mc-fts.js +2 -2
- package/dist/tools/mixin-scan.d.ts +1 -1
- package/dist/tools/mixin-scan.js +16 -16
- package/dist/tools/version-diff.js +3 -3
- package/package.json +70 -62
- package/prisma/backends/schema.sqlite.prisma +215 -215
- package/prisma/schema.prisma +303 -303
- package/scripts/backup.mjs +151 -151
- package/scripts/check-atm10.mjs +27 -27
- package/scripts/check-sync.mjs +13 -13
- package/scripts/create-fts-index.mjs +50 -50
- package/scripts/enable-pgvector.mjs +90 -90
- package/scripts/enable-sqlite-vec.mjs +158 -158
- package/scripts/migrate-backend.mjs +188 -188
- package/scripts/migrate-embed-provenance.mjs +80 -80
- package/scripts/migrate-metadata-source.mjs +62 -62
- package/scripts/migrate-mod-source-files.mjs +67 -67
- package/scripts/post-update.mjs +77 -77
- package/scripts/verify-atm10.mjs +63 -63
- package/dist/access-flags.test.d.ts +0 -2
- package/dist/access-flags.test.d.ts.map +0 -1
- package/dist/access-flags.test.js +0 -189
- package/dist/access-flags.test.js.map +0 -1
- package/dist/fetch-utils.test.d.ts +0 -2
- package/dist/fetch-utils.test.d.ts.map +0 -1
- package/dist/fetch-utils.test.js +0 -133
- package/dist/fetch-utils.test.js.map +0 -1
- package/dist/mappings.test.d.ts +0 -2
- package/dist/mappings.test.d.ts.map +0 -1
- package/dist/mappings.test.js +0 -161
- package/dist/mappings.test.js.map +0 -1
- package/dist/processor.test.d.ts +0 -2
- package/dist/processor.test.d.ts.map +0 -1
- package/dist/processor.test.js +0 -629
- package/dist/processor.test.js.map +0 -1
- package/dist/search.test.d.ts +0 -2
- package/dist/search.test.d.ts.map +0 -1
- package/dist/search.test.js +0 -71
- package/dist/search.test.js.map +0 -1
- package/dist/security.test.d.ts +0 -2
- package/dist/security.test.d.ts.map +0 -1
- package/dist/security.test.js +0 -687
- package/dist/security.test.js.map +0 -1
- package/dist/tools/compat-check.test.d.ts +0 -2
- package/dist/tools/compat-check.test.d.ts.map +0 -1
- package/dist/tools/compat-check.test.js +0 -179
- package/dist/tools/compat-check.test.js.map +0 -1
- package/dist/tools/diagnostics.test.d.ts +0 -2
- package/dist/tools/diagnostics.test.d.ts.map +0 -1
- package/dist/tools/diagnostics.test.js +0 -168
- package/dist/tools/diagnostics.test.js.map +0 -1
- package/dist/tools/graphify.test.d.ts +0 -2
- package/dist/tools/graphify.test.d.ts.map +0 -1
- package/dist/tools/graphify.test.js +0 -39
- package/dist/tools/graphify.test.js.map +0 -1
- package/dist/tools/ingest.test.d.ts +0 -2
- package/dist/tools/ingest.test.d.ts.map +0 -1
- package/dist/tools/ingest.test.js +0 -340
- package/dist/tools/ingest.test.js.map +0 -1
- package/dist/tools/mod-tags.test.d.ts +0 -2
- package/dist/tools/mod-tags.test.d.ts.map +0 -1
- package/dist/tools/mod-tags.test.js +0 -75
- package/dist/tools/mod-tags.test.js.map +0 -1
- package/dist/tools/packtools.test.d.ts +0 -2
- package/dist/tools/packtools.test.d.ts.map +0 -1
- package/dist/tools/packtools.test.js +0 -99
- package/dist/tools/packtools.test.js.map +0 -1
- package/dist/tools/version-diff.test.d.ts +0 -2
- package/dist/tools/version-diff.test.d.ts.map +0 -1
- package/dist/tools/version-diff.test.js +0 -102
- package/dist/tools/version-diff.test.js.map +0 -1
- package/dist/validate.test.d.ts +0 -2
- package/dist/validate.test.d.ts.map +0 -1
- package/dist/validate.test.js +0 -73
- 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.");
|