@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
package/dist/launcher.js
CHANGED
|
@@ -41,7 +41,10 @@ for (const ep of [ENV_FILE, join(PKG_ROOT, ".env")]) {
|
|
|
41
41
|
}
|
|
42
42
|
const args = process.argv.slice(2);
|
|
43
43
|
const wantSetup = args.includes("--setup");
|
|
44
|
-
|
|
44
|
+
// Bootstrap embedded SQLite only when nothing else is configured. A
|
|
45
|
+
// pre-supplied DATABASE_URL (e.g. Postgres via a container env var) opts out, so
|
|
46
|
+
// `docker run -e DATABASE_URL=postgresql://…` works without a stray SQLite db.
|
|
47
|
+
const isFirstRun = IS_INSTALLED && !existsSync(ENV_FILE) && !process.env.DATABASE_URL;
|
|
45
48
|
// ── Explicit setup wizard (--setup) ─────────────────────────────────────────
|
|
46
49
|
// Opt-in interactive configuration (Postgres, semantic search, MCP client, …).
|
|
47
50
|
if (wantSetup) {
|
package/dist/launcher.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launcher.js","sourceRoot":"","sources":["../src/launcher.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AACtF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,2EAA2E;AAC3E,+EAA+E;AAC/E,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AACvC,MAAM,YAAY,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AAEzD,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;AACzG,MAAM,QAAQ,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACpF,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;AAEnD,gFAAgF;AAChF,KAAK,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;IAClD,IAAI,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC;QACjB,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACtD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACxC,IAAI,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAChF,CAAC;QACD,MAAM;IACV,CAAC;AACL,CAAC;AAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAC3C,MAAM,UAAU,GAAG,YAAY,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"launcher.js","sourceRoot":"","sources":["../src/launcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -39,8 +39,8 @@ export async function upsertDocEmbedding(id, vec) {
|
|
|
39
39
|
export async function searchDocsByVector(vec, limit = 5) {
|
|
40
40
|
const db = await getVecDb();
|
|
41
41
|
try {
|
|
42
|
-
const rows = db.prepare(`SELECT rowid AS id, distance FROM vec_doc_entries
|
|
43
|
-
WHERE embedding MATCH ? AND k = ?
|
|
42
|
+
const rows = db.prepare(`SELECT rowid AS id, distance FROM vec_doc_entries
|
|
43
|
+
WHERE embedding MATCH ? AND k = ?
|
|
44
44
|
ORDER BY distance`).all(float32Blob(vec), limit);
|
|
45
45
|
return rows.map((r) => ({ id: r.id, similarity: 1 - r.distance }));
|
|
46
46
|
}
|
|
@@ -61,8 +61,8 @@ export async function upsertPrimerEmbedding(id, vec) {
|
|
|
61
61
|
export async function searchPrimersByVector(vec, limit = 5) {
|
|
62
62
|
const db = await getVecDb();
|
|
63
63
|
try {
|
|
64
|
-
const rows = db.prepare(`SELECT rowid AS id, distance FROM vec_primers
|
|
65
|
-
WHERE embedding MATCH ? AND k = ?
|
|
64
|
+
const rows = db.prepare(`SELECT rowid AS id, distance FROM vec_primers
|
|
65
|
+
WHERE embedding MATCH ? AND k = ?
|
|
66
66
|
ORDER BY distance`).all(float32Blob(vec), limit);
|
|
67
67
|
return rows.map((r) => ({ id: r.id, similarity: 1 - r.distance }));
|
|
68
68
|
}
|
|
@@ -84,13 +84,13 @@ export async function searchSourceByVector(vec, mcVersionId, limit = 10, provena
|
|
|
84
84
|
const db = await getVecDb();
|
|
85
85
|
try {
|
|
86
86
|
const sql = provenance
|
|
87
|
-
? `SELECT v.rowid AS id, s.class_name, s.embed_source, v.distance FROM vec_mc_source v
|
|
88
|
-
JOIN mc_source_files s ON s.id = v.rowid
|
|
89
|
-
WHERE v.embedding MATCH ? AND v.k = ? AND s.mc_version_id = ? AND s.embed_source = ?
|
|
87
|
+
? `SELECT v.rowid AS id, s.class_name, s.embed_source, v.distance FROM vec_mc_source v
|
|
88
|
+
JOIN mc_source_files s ON s.id = v.rowid
|
|
89
|
+
WHERE v.embedding MATCH ? AND v.k = ? AND s.mc_version_id = ? AND s.embed_source = ?
|
|
90
90
|
ORDER BY v.distance`
|
|
91
|
-
: `SELECT v.rowid AS id, s.class_name, s.embed_source, v.distance FROM vec_mc_source v
|
|
92
|
-
JOIN mc_source_files s ON s.id = v.rowid
|
|
93
|
-
WHERE v.embedding MATCH ? AND v.k = ? AND s.mc_version_id = ?
|
|
91
|
+
: `SELECT v.rowid AS id, s.class_name, s.embed_source, v.distance FROM vec_mc_source v
|
|
92
|
+
JOIN mc_source_files s ON s.id = v.rowid
|
|
93
|
+
WHERE v.embedding MATCH ? AND v.k = ? AND s.mc_version_id = ?
|
|
94
94
|
ORDER BY v.distance`;
|
|
95
95
|
const args = provenance
|
|
96
96
|
? [float32Blob(vec), limit, mcVersionId, provenance]
|
|
@@ -116,13 +116,13 @@ export async function searchModSourceByVector(vec, modId, limit = 10, provenance
|
|
|
116
116
|
const db = await getVecDb();
|
|
117
117
|
try {
|
|
118
118
|
const sql = provenance
|
|
119
|
-
? `SELECT v.rowid AS id, s.class_name, s.embed_source, v.distance FROM vec_mod_source v
|
|
120
|
-
JOIN mod_source_files s ON s.id = v.rowid
|
|
121
|
-
WHERE v.embedding MATCH ? AND v.k = ? AND s.mod_id = ? AND s.embed_source = ?
|
|
119
|
+
? `SELECT v.rowid AS id, s.class_name, s.embed_source, v.distance FROM vec_mod_source v
|
|
120
|
+
JOIN mod_source_files s ON s.id = v.rowid
|
|
121
|
+
WHERE v.embedding MATCH ? AND v.k = ? AND s.mod_id = ? AND s.embed_source = ?
|
|
122
122
|
ORDER BY v.distance`
|
|
123
|
-
: `SELECT v.rowid AS id, s.class_name, s.embed_source, v.distance FROM vec_mod_source v
|
|
124
|
-
JOIN mod_source_files s ON s.id = v.rowid
|
|
125
|
-
WHERE v.embedding MATCH ? AND v.k = ? AND s.mod_id = ?
|
|
123
|
+
: `SELECT v.rowid AS id, s.class_name, s.embed_source, v.distance FROM vec_mod_source v
|
|
124
|
+
JOIN mod_source_files s ON s.id = v.rowid
|
|
125
|
+
WHERE v.embedding MATCH ? AND v.k = ? AND s.mod_id = ?
|
|
126
126
|
ORDER BY v.distance`;
|
|
127
127
|
const args = provenance
|
|
128
128
|
? [float32Blob(vec), limit, modId, provenance]
|
|
@@ -142,7 +142,7 @@ export async function findSourceIdsByClassNames(classNames, mcVersionId, require
|
|
|
142
142
|
const placeholders = classNames.map(() => "?").join(",");
|
|
143
143
|
const embFilter = requireEmbedding ? " AND embedding IS NOT NULL" : "";
|
|
144
144
|
try {
|
|
145
|
-
const rows = db.prepare(`SELECT id, class_name FROM mc_source_files
|
|
145
|
+
const rows = db.prepare(`SELECT id, class_name FROM mc_source_files
|
|
146
146
|
WHERE mc_version_id = ? AND class_name IN (${placeholders})${embFilter}`).all(mcVersionId, ...classNames);
|
|
147
147
|
return new Map(rows.map((r) => [r.class_name, r.id]));
|
|
148
148
|
}
|
|
@@ -157,7 +157,7 @@ export async function findModSourceIdsByClassNames(classNames, modId, requireEmb
|
|
|
157
157
|
const placeholders = classNames.map(() => "?").join(",");
|
|
158
158
|
const embFilter = requireEmbedding ? " AND embedding IS NOT NULL" : "";
|
|
159
159
|
try {
|
|
160
|
-
const rows = db.prepare(`SELECT id, class_name FROM mod_source_files
|
|
160
|
+
const rows = db.prepare(`SELECT id, class_name FROM mod_source_files
|
|
161
161
|
WHERE mod_id = ? AND class_name IN (${placeholders})${embFilter}`).all(modId, ...classNames);
|
|
162
162
|
return new Map(rows.map((r) => [r.class_name, r.id]));
|
|
163
163
|
}
|
|
@@ -172,7 +172,7 @@ export async function getEmbedSources(table, ids) {
|
|
|
172
172
|
const db = await getVecDb();
|
|
173
173
|
const placeholders = ids.map(() => "?").join(",");
|
|
174
174
|
try {
|
|
175
|
-
const rows = db.prepare(`SELECT id, embed_source, embed_updated_at FROM ${table}
|
|
175
|
+
const rows = db.prepare(`SELECT id, embed_source, embed_updated_at FROM ${table}
|
|
176
176
|
WHERE id IN (${placeholders}) AND embedding IS NOT NULL`).all(...ids);
|
|
177
177
|
return new Map(rows.map((r) => [r.id, {
|
|
178
178
|
source: r.embed_source,
|
|
@@ -188,8 +188,8 @@ export async function countEmbedsBySource(table, scopeColumn, scopeId) {
|
|
|
188
188
|
const db = await getVecDb();
|
|
189
189
|
const result = { local: 0, registry: 0, community: 0, unknown: 0 };
|
|
190
190
|
try {
|
|
191
|
-
const rows = db.prepare(`SELECT embed_source, COUNT(*) AS count FROM ${table}
|
|
192
|
-
WHERE ${scopeColumn} = ? AND embedding IS NOT NULL
|
|
191
|
+
const rows = db.prepare(`SELECT embed_source, COUNT(*) AS count FROM ${table}
|
|
192
|
+
WHERE ${scopeColumn} = ? AND embedding IS NOT NULL
|
|
193
193
|
GROUP BY embed_source`).all(scopeId);
|
|
194
194
|
for (const r of rows) {
|
|
195
195
|
if (r.embed_source === "local")
|
|
@@ -17,10 +17,10 @@ export async function upsertDocEmbedding(id, vec) {
|
|
|
17
17
|
}
|
|
18
18
|
export async function searchDocsByVector(vec, limit = 5) {
|
|
19
19
|
const db = await getDb();
|
|
20
|
-
return db.$queryRawUnsafe(`SELECT id, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
21
|
-
FROM doc_entries
|
|
22
|
-
WHERE embedding IS NOT NULL
|
|
23
|
-
ORDER BY embedding <=> $1::vector
|
|
20
|
+
return db.$queryRawUnsafe(`SELECT id, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
21
|
+
FROM doc_entries
|
|
22
|
+
WHERE embedding IS NOT NULL
|
|
23
|
+
ORDER BY embedding <=> $1::vector
|
|
24
24
|
LIMIT $2`, vecLiteral(vec), limit);
|
|
25
25
|
}
|
|
26
26
|
// ── primers ───────────────────────────────────────────────────────────────────
|
|
@@ -30,10 +30,10 @@ export async function upsertPrimerEmbedding(id, vec) {
|
|
|
30
30
|
}
|
|
31
31
|
export async function searchPrimersByVector(vec, limit = 5) {
|
|
32
32
|
const db = await getDb();
|
|
33
|
-
return db.$queryRawUnsafe(`SELECT id, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
34
|
-
FROM primers
|
|
35
|
-
WHERE embedding IS NOT NULL
|
|
36
|
-
ORDER BY embedding <=> $1::vector
|
|
33
|
+
return db.$queryRawUnsafe(`SELECT id, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
34
|
+
FROM primers
|
|
35
|
+
WHERE embedding IS NOT NULL
|
|
36
|
+
ORDER BY embedding <=> $1::vector
|
|
37
37
|
LIMIT $2`, vecLiteral(vec), limit);
|
|
38
38
|
}
|
|
39
39
|
// ── mc_source_files ───────────────────────────────────────────────────────────
|
|
@@ -44,16 +44,16 @@ export async function upsertSourceEmbedding(id, vec, source = "local") {
|
|
|
44
44
|
export async function searchSourceByVector(vec, mcVersionId, limit = 10, provenance) {
|
|
45
45
|
const db = await getDb();
|
|
46
46
|
if (provenance) {
|
|
47
|
-
return db.$queryRawUnsafe(`SELECT id, class_name, embed_source, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
48
|
-
FROM mc_source_files
|
|
49
|
-
WHERE mc_version_id = $3 AND embedding IS NOT NULL AND embed_source = $4
|
|
50
|
-
ORDER BY embedding <=> $1::vector
|
|
47
|
+
return db.$queryRawUnsafe(`SELECT id, class_name, embed_source, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
48
|
+
FROM mc_source_files
|
|
49
|
+
WHERE mc_version_id = $3 AND embedding IS NOT NULL AND embed_source = $4
|
|
50
|
+
ORDER BY embedding <=> $1::vector
|
|
51
51
|
LIMIT $2`, vecLiteral(vec), limit, mcVersionId, provenance);
|
|
52
52
|
}
|
|
53
|
-
return db.$queryRawUnsafe(`SELECT id, class_name, embed_source, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
54
|
-
FROM mc_source_files
|
|
55
|
-
WHERE mc_version_id = $3 AND embedding IS NOT NULL
|
|
56
|
-
ORDER BY embedding <=> $1::vector
|
|
53
|
+
return db.$queryRawUnsafe(`SELECT id, class_name, embed_source, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
54
|
+
FROM mc_source_files
|
|
55
|
+
WHERE mc_version_id = $3 AND embedding IS NOT NULL
|
|
56
|
+
ORDER BY embedding <=> $1::vector
|
|
57
57
|
LIMIT $2`, vecLiteral(vec), limit, mcVersionId);
|
|
58
58
|
}
|
|
59
59
|
/** Count rows with no embedding yet (for backfill progress). */
|
|
@@ -74,16 +74,16 @@ export async function upsertModSourceEmbedding(id, vec, source = "local") {
|
|
|
74
74
|
export async function searchModSourceByVector(vec, modId, limit = 10, provenance) {
|
|
75
75
|
const db = await getDb();
|
|
76
76
|
if (provenance) {
|
|
77
|
-
return db.$queryRawUnsafe(`SELECT id, class_name, embed_source, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
78
|
-
FROM mod_source_files
|
|
79
|
-
WHERE mod_id = $3 AND embedding IS NOT NULL AND embed_source = $4
|
|
80
|
-
ORDER BY embedding <=> $1::vector
|
|
77
|
+
return db.$queryRawUnsafe(`SELECT id, class_name, embed_source, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
78
|
+
FROM mod_source_files
|
|
79
|
+
WHERE mod_id = $3 AND embedding IS NOT NULL AND embed_source = $4
|
|
80
|
+
ORDER BY embedding <=> $1::vector
|
|
81
81
|
LIMIT $2`, vecLiteral(vec), limit, modId, provenance);
|
|
82
82
|
}
|
|
83
|
-
return db.$queryRawUnsafe(`SELECT id, class_name, embed_source, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
84
|
-
FROM mod_source_files
|
|
85
|
-
WHERE mod_id = $3 AND embedding IS NOT NULL
|
|
86
|
-
ORDER BY embedding <=> $1::vector
|
|
83
|
+
return db.$queryRawUnsafe(`SELECT id, class_name, embed_source, (1 - (embedding <=> $1::vector))::float AS similarity
|
|
84
|
+
FROM mod_source_files
|
|
85
|
+
WHERE mod_id = $3 AND embedding IS NOT NULL
|
|
86
|
+
ORDER BY embedding <=> $1::vector
|
|
87
87
|
LIMIT $2`, vecLiteral(vec), limit, modId);
|
|
88
88
|
}
|
|
89
89
|
// ── Class-name → ID lookups (for diff semantic enrichment) ───────────────────
|
|
@@ -92,7 +92,7 @@ export async function findSourceIdsByClassNames(classNames, mcVersionId, require
|
|
|
92
92
|
return new Map();
|
|
93
93
|
const db = await getDb();
|
|
94
94
|
const embFilter = requireEmbedding ? " AND embedding IS NOT NULL" : "";
|
|
95
|
-
const rows = await db.$queryRawUnsafe(`SELECT id, class_name FROM mc_source_files
|
|
95
|
+
const rows = await db.$queryRawUnsafe(`SELECT id, class_name FROM mc_source_files
|
|
96
96
|
WHERE mc_version_id = $1 AND class_name = ANY($2::text[])${embFilter}`, mcVersionId, classNames);
|
|
97
97
|
return new Map(rows.map((r) => [r.class_name, r.id]));
|
|
98
98
|
}
|
|
@@ -101,7 +101,7 @@ export async function findModSourceIdsByClassNames(classNames, modId, requireEmb
|
|
|
101
101
|
return new Map();
|
|
102
102
|
const db = await getDb();
|
|
103
103
|
const embFilter = requireEmbedding ? " AND embedding IS NOT NULL" : "";
|
|
104
|
-
const rows = await db.$queryRawUnsafe(`SELECT id, class_name FROM mod_source_files
|
|
104
|
+
const rows = await db.$queryRawUnsafe(`SELECT id, class_name FROM mod_source_files
|
|
105
105
|
WHERE mod_id = $1 AND class_name = ANY($2::text[])${embFilter}`, modId, classNames);
|
|
106
106
|
return new Map(rows.map((r) => [r.class_name, r.id]));
|
|
107
107
|
}
|
|
@@ -116,8 +116,8 @@ export async function getEmbedSources(table, ids) {
|
|
|
116
116
|
/** Count embeddings by source provenance. */
|
|
117
117
|
export async function countEmbedsBySource(table, scopeColumn, scopeId) {
|
|
118
118
|
const db = await getDb();
|
|
119
|
-
const rows = await db.$queryRawUnsafe(`SELECT embed_source, COUNT(*)::text AS count FROM ${table}
|
|
120
|
-
WHERE ${scopeColumn} = $1 AND embedding IS NOT NULL
|
|
119
|
+
const rows = await db.$queryRawUnsafe(`SELECT embed_source, COUNT(*)::text AS count FROM ${table}
|
|
120
|
+
WHERE ${scopeColumn} = $1 AND embedding IS NOT NULL
|
|
121
121
|
GROUP BY embed_source`, scopeId);
|
|
122
122
|
const result = { local: 0, registry: 0, community: 0, unknown: 0 };
|
|
123
123
|
for (const r of rows) {
|
package/dist/repositories/mod.js
CHANGED
|
@@ -327,8 +327,8 @@ export async function countModSourceFiles(modId) {
|
|
|
327
327
|
}
|
|
328
328
|
export async function findModSourceFilesUnembedded(modId, limit, offset) {
|
|
329
329
|
const db = await getDb();
|
|
330
|
-
return db.$queryRawUnsafe(`SELECT id, class_name AS "className", content FROM mod_source_files
|
|
331
|
-
WHERE mod_id = $1 AND embedding IS NULL
|
|
330
|
+
return db.$queryRawUnsafe(`SELECT id, class_name AS "className", content FROM mod_source_files
|
|
331
|
+
WHERE mod_id = $1 AND embedding IS NULL
|
|
332
332
|
ORDER BY id LIMIT $2 OFFSET $3`, modId, limit, offset);
|
|
333
333
|
}
|
|
334
334
|
export async function countUnembeddedModSourceFiles(modId) {
|
package/dist/search-adapter.js
CHANGED
|
@@ -22,10 +22,10 @@ export async function ftsSearchSource(mcVersionId, query, limit) {
|
|
|
22
22
|
const Database = (await import("better-sqlite3")).default;
|
|
23
23
|
const db = new Database(path, { readonly: true });
|
|
24
24
|
try {
|
|
25
|
-
const rows = db.prepare(`SELECT s.id, s.class_name, s.content FROM fts_mc_source f
|
|
26
|
-
JOIN mc_source_files s ON s.id = f.rowid
|
|
27
|
-
WHERE f.fts_mc_source MATCH ? AND s.mc_version_id = ?
|
|
28
|
-
ORDER BY rank
|
|
25
|
+
const rows = db.prepare(`SELECT s.id, s.class_name, s.content FROM fts_mc_source f
|
|
26
|
+
JOIN mc_source_files s ON s.id = f.rowid
|
|
27
|
+
WHERE f.fts_mc_source MATCH ? AND s.mc_version_id = ?
|
|
28
|
+
ORDER BY rank
|
|
29
29
|
LIMIT ?`).all(query, mcVersionId, limit);
|
|
30
30
|
return rows.map(r => ({
|
|
31
31
|
className: r.class_name,
|
|
@@ -38,18 +38,18 @@ export async function ftsSearchSource(mcVersionId, query, limit) {
|
|
|
38
38
|
}
|
|
39
39
|
// Postgres / PGlite — existing tsvector query
|
|
40
40
|
const db = await getDb();
|
|
41
|
-
const rows = await db.$queryRaw `
|
|
42
|
-
SELECT
|
|
43
|
-
class_name,
|
|
44
|
-
ts_headline('simple', content,
|
|
45
|
-
plainto_tsquery('simple', ${query}),
|
|
46
|
-
'MaxWords=25, MinWords=15, StartSel="", StopSel=""'
|
|
47
|
-
) AS snippet
|
|
48
|
-
FROM mc_source_files
|
|
49
|
-
WHERE mc_version_id = ${mcVersionId}
|
|
50
|
-
AND to_tsvector('simple', content) @@ plainto_tsquery('simple', ${query})
|
|
51
|
-
ORDER BY ts_rank(to_tsvector('simple', content), plainto_tsquery('simple', ${query})) DESC
|
|
52
|
-
LIMIT ${limit}
|
|
41
|
+
const rows = await db.$queryRaw `
|
|
42
|
+
SELECT
|
|
43
|
+
class_name,
|
|
44
|
+
ts_headline('simple', content,
|
|
45
|
+
plainto_tsquery('simple', ${query}),
|
|
46
|
+
'MaxWords=25, MinWords=15, StartSel="", StopSel=""'
|
|
47
|
+
) AS snippet
|
|
48
|
+
FROM mc_source_files
|
|
49
|
+
WHERE mc_version_id = ${mcVersionId}
|
|
50
|
+
AND to_tsvector('simple', content) @@ plainto_tsquery('simple', ${query})
|
|
51
|
+
ORDER BY ts_rank(to_tsvector('simple', content), plainto_tsquery('simple', ${query})) DESC
|
|
52
|
+
LIMIT ${limit}
|
|
53
53
|
`;
|
|
54
54
|
return rows.map(r => ({ className: r.class_name, snippet: r.snippet }));
|
|
55
55
|
}
|
|
@@ -68,10 +68,10 @@ export async function ftsSearchModSource(modId, query, limit) {
|
|
|
68
68
|
const Database = (await import("better-sqlite3")).default;
|
|
69
69
|
const db = new Database(path, { readonly: true });
|
|
70
70
|
try {
|
|
71
|
-
const rows = db.prepare(`SELECT s.id, s.class_name, s.content FROM fts_mod_source f
|
|
72
|
-
JOIN mod_source_files s ON s.id = f.rowid
|
|
73
|
-
WHERE f.fts_mod_source MATCH ? AND s.mod_id = ?
|
|
74
|
-
ORDER BY rank
|
|
71
|
+
const rows = db.prepare(`SELECT s.id, s.class_name, s.content FROM fts_mod_source f
|
|
72
|
+
JOIN mod_source_files s ON s.id = f.rowid
|
|
73
|
+
WHERE f.fts_mod_source MATCH ? AND s.mod_id = ?
|
|
74
|
+
ORDER BY rank
|
|
75
75
|
LIMIT ?`).all(query, modId, limit);
|
|
76
76
|
return rows.map(r => ({
|
|
77
77
|
className: r.class_name,
|
|
@@ -84,18 +84,18 @@ export async function ftsSearchModSource(modId, query, limit) {
|
|
|
84
84
|
}
|
|
85
85
|
// PostgreSQL — GIN index on to_tsvector('simple', content)
|
|
86
86
|
const db = await getDb();
|
|
87
|
-
const rows = await db.$queryRaw `
|
|
88
|
-
SELECT
|
|
89
|
-
class_name,
|
|
90
|
-
ts_headline('simple', content,
|
|
91
|
-
plainto_tsquery('simple', ${query}),
|
|
92
|
-
'MaxWords=25, MinWords=15, StartSel="", StopSel=""'
|
|
93
|
-
) AS snippet
|
|
94
|
-
FROM mod_source_files
|
|
95
|
-
WHERE mod_id = ${modId}
|
|
96
|
-
AND to_tsvector('simple', content) @@ plainto_tsquery('simple', ${query})
|
|
97
|
-
ORDER BY ts_rank(to_tsvector('simple', content), plainto_tsquery('simple', ${query})) DESC
|
|
98
|
-
LIMIT ${limit}
|
|
87
|
+
const rows = await db.$queryRaw `
|
|
88
|
+
SELECT
|
|
89
|
+
class_name,
|
|
90
|
+
ts_headline('simple', content,
|
|
91
|
+
plainto_tsquery('simple', ${query}),
|
|
92
|
+
'MaxWords=25, MinWords=15, StartSel="", StopSel=""'
|
|
93
|
+
) AS snippet
|
|
94
|
+
FROM mod_source_files
|
|
95
|
+
WHERE mod_id = ${modId}
|
|
96
|
+
AND to_tsvector('simple', content) @@ plainto_tsquery('simple', ${query})
|
|
97
|
+
ORDER BY ts_rank(to_tsvector('simple', content), plainto_tsquery('simple', ${query})) DESC
|
|
98
|
+
LIMIT ${limit}
|
|
99
99
|
`;
|
|
100
100
|
return rows.map(r => ({ className: r.class_name, snippet: r.snippet }));
|
|
101
101
|
}
|
|
@@ -108,11 +108,11 @@ export async function ftsSearchDocs(query, limit = 20) {
|
|
|
108
108
|
const Database = (await import("better-sqlite3")).default;
|
|
109
109
|
const db = new Database(path, { readonly: true });
|
|
110
110
|
try {
|
|
111
|
-
const rows = db.prepare(`SELECT d.id, d.class_name, d.title, d.summary, d.url, d.category, d.namespace, d.tags
|
|
112
|
-
FROM fts_doc_entries f
|
|
113
|
-
JOIN doc_entries d ON d.id = f.rowid
|
|
114
|
-
WHERE f.fts_doc_entries MATCH ?
|
|
115
|
-
ORDER BY rank
|
|
111
|
+
const rows = db.prepare(`SELECT d.id, d.class_name, d.title, d.summary, d.url, d.category, d.namespace, d.tags
|
|
112
|
+
FROM fts_doc_entries f
|
|
113
|
+
JOIN doc_entries d ON d.id = f.rowid
|
|
114
|
+
WHERE f.fts_doc_entries MATCH ?
|
|
115
|
+
ORDER BY rank
|
|
116
116
|
LIMIT ?`).all(query, limit);
|
|
117
117
|
return rows.map(r => ({ ...r, tags: JSON.parse(r.tags ?? "[]") }));
|
|
118
118
|
}
|
|
@@ -123,14 +123,14 @@ export async function ftsSearchDocs(query, limit = 20) {
|
|
|
123
123
|
// Postgres / PGlite — raw LIKE (case-insensitive via lower())
|
|
124
124
|
const db = await getDb();
|
|
125
125
|
const kw = escapeLike(query.toLowerCase());
|
|
126
|
-
const rows = await db.$queryRaw `
|
|
127
|
-
SELECT id, class_name, title, summary, url, category, namespace, tags
|
|
128
|
-
FROM doc_entries
|
|
129
|
-
WHERE lower(title) LIKE ${"%" + kw + "%"}
|
|
130
|
-
OR lower(summary) LIKE ${"%" + kw + "%"}
|
|
131
|
-
OR lower(class_name) LIKE ${"%" + kw + "%"}
|
|
132
|
-
ORDER BY id
|
|
133
|
-
LIMIT ${limit}
|
|
126
|
+
const rows = await db.$queryRaw `
|
|
127
|
+
SELECT id, class_name, title, summary, url, category, namespace, tags
|
|
128
|
+
FROM doc_entries
|
|
129
|
+
WHERE lower(title) LIKE ${"%" + kw + "%"}
|
|
130
|
+
OR lower(summary) LIKE ${"%" + kw + "%"}
|
|
131
|
+
OR lower(class_name) LIKE ${"%" + kw + "%"}
|
|
132
|
+
ORDER BY id
|
|
133
|
+
LIMIT ${limit}
|
|
134
134
|
`;
|
|
135
135
|
return rows;
|
|
136
136
|
}
|
|
@@ -143,12 +143,12 @@ export async function ftsSearchPrimers(query, modloader, limit = 20) {
|
|
|
143
143
|
const Database = (await import("better-sqlite3")).default;
|
|
144
144
|
const db = new Database(path, { readonly: true });
|
|
145
145
|
try {
|
|
146
|
-
const rows = db.prepare(`SELECT p.id, p.title, p.summary, p.from_version, p.to_version, p.modloader, p.url
|
|
147
|
-
FROM fts_primers f
|
|
148
|
-
JOIN primers p ON p.id = f.rowid
|
|
149
|
-
WHERE f.fts_primers MATCH ?
|
|
150
|
-
${modloader ? "AND p.modloader = ?" : ""}
|
|
151
|
-
ORDER BY rank
|
|
146
|
+
const rows = db.prepare(`SELECT p.id, p.title, p.summary, p.from_version, p.to_version, p.modloader, p.url
|
|
147
|
+
FROM fts_primers f
|
|
148
|
+
JOIN primers p ON p.id = f.rowid
|
|
149
|
+
WHERE f.fts_primers MATCH ?
|
|
150
|
+
${modloader ? "AND p.modloader = ?" : ""}
|
|
151
|
+
ORDER BY rank
|
|
152
152
|
LIMIT ?`).all(...[query, ...(modloader ? [modloader] : []), limit]);
|
|
153
153
|
return rows.map(r => ({
|
|
154
154
|
id: r.id, title: r.title, summary: r.summary,
|
|
@@ -36,15 +36,15 @@ async function findModsWithMixinTargetsMatching(targets, loader, mcVersion) {
|
|
|
36
36
|
}
|
|
37
37
|
const whereExtra = extra.length ? " AND " + extra.join(" AND ") : "";
|
|
38
38
|
const db = await getDb();
|
|
39
|
-
const rows = await db.$queryRawUnsafe(`
|
|
40
|
-
SELECT
|
|
41
|
-
m.mod_id,
|
|
42
|
-
m.display_name,
|
|
43
|
-
ARRAY_AGG(t.cls) FILTER (WHERE t.cls = ANY($1)) AS matched
|
|
44
|
-
FROM "mods" m
|
|
45
|
-
CROSS JOIN LATERAL jsonb_array_elements_text(m.mixin_targets::jsonb) AS t(cls)
|
|
46
|
-
WHERE t.cls = ANY($1) ${whereExtra}
|
|
47
|
-
GROUP BY m.mod_id, m.display_name
|
|
39
|
+
const rows = await db.$queryRawUnsafe(`
|
|
40
|
+
SELECT
|
|
41
|
+
m.mod_id,
|
|
42
|
+
m.display_name,
|
|
43
|
+
ARRAY_AGG(t.cls) FILTER (WHERE t.cls = ANY($1)) AS matched
|
|
44
|
+
FROM "mods" m
|
|
45
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(m.mixin_targets::jsonb) AS t(cls)
|
|
46
|
+
WHERE t.cls = ANY($1) ${whereExtra}
|
|
47
|
+
GROUP BY m.mod_id, m.display_name
|
|
48
48
|
`, ...params);
|
|
49
49
|
return rows.map((r) => ({
|
|
50
50
|
modId: r.mod_id,
|
|
@@ -49,7 +49,7 @@ export async function exportModEmbeddings(dbId, outputDir) {
|
|
|
49
49
|
const chunkSize = parseInt(process.env.OLLAMA_EMBED_CHUNK ?? "1500", 10);
|
|
50
50
|
// Query all embedded source files
|
|
51
51
|
const db = await getDb();
|
|
52
|
-
const rows = await db.$queryRawUnsafe(`SELECT class_name, embedding::text AS embedding FROM mod_source_files
|
|
52
|
+
const rows = await db.$queryRawUnsafe(`SELECT class_name, embedding::text AS embedding FROM mod_source_files
|
|
53
53
|
WHERE mod_id = $1 AND embedding IS NOT NULL`, dbId);
|
|
54
54
|
if (rows.length === 0) {
|
|
55
55
|
throw new Error(`No embeddings found for mod #${dbId} (${mod.modId}). Run index_semantic first.`);
|
|
@@ -96,7 +96,7 @@ export async function exportVanillaEmbeddings(mcVersion, outputDir) {
|
|
|
96
96
|
const dims = parseInt(process.env.OLLAMA_EMBED_DIM ?? "768", 10);
|
|
97
97
|
const chunkSize = parseInt(process.env.OLLAMA_EMBED_CHUNK ?? "1500", 10);
|
|
98
98
|
const db = await getDb();
|
|
99
|
-
const rows = await db.$queryRawUnsafe(`SELECT class_name, embedding::text AS embedding FROM mc_source_files
|
|
99
|
+
const rows = await db.$queryRawUnsafe(`SELECT class_name, embedding::text AS embedding FROM mc_source_files
|
|
100
100
|
WHERE mc_version_id = $1 AND embedding IS NOT NULL`, versionRow.id);
|
|
101
101
|
if (rows.length === 0) {
|
|
102
102
|
throw new Error(`No vanilla embeddings found for MC ${mcVersion}. Run mc_source index_semantic first.`);
|
|
@@ -156,7 +156,7 @@ export async function exportAllEmbeddings(outputDir) {
|
|
|
156
156
|
const db = await getDb();
|
|
157
157
|
const model = process.env.OLLAMA_EMBED_MODEL ?? "nomic-embed-text";
|
|
158
158
|
// Find all mods with at least one embedded source file
|
|
159
|
-
const mods = await db.$queryRawUnsafe(`SELECT mod_id, COUNT(*)::text AS count FROM mod_source_files
|
|
159
|
+
const mods = await db.$queryRawUnsafe(`SELECT mod_id, COUNT(*)::text AS count FROM mod_source_files
|
|
160
160
|
WHERE embedding IS NOT NULL GROUP BY mod_id`);
|
|
161
161
|
let exported = 0;
|
|
162
162
|
let skipped = 0;
|
|
@@ -497,8 +497,8 @@ export async function getEmbedStatus(target) {
|
|
|
497
497
|
if (!mod)
|
|
498
498
|
throw new Error(`Mod #${target.dbId} not found`);
|
|
499
499
|
scopeId = target.dbId;
|
|
500
|
-
stats = await db.$queryRawUnsafe(`SELECT COUNT(*)::text AS total,
|
|
501
|
-
COUNT(CASE WHEN embedding IS NOT NULL THEN 1 END)::text AS embedded
|
|
500
|
+
stats = await db.$queryRawUnsafe(`SELECT COUNT(*)::text AS total,
|
|
501
|
+
COUNT(CASE WHEN embedding IS NOT NULL THEN 1 END)::text AS embedded
|
|
502
502
|
FROM mod_source_files WHERE mod_id = $1`, target.dbId);
|
|
503
503
|
}
|
|
504
504
|
else if (target.targetType === "vanilla") {
|
|
@@ -510,8 +510,8 @@ export async function getEmbedStatus(target) {
|
|
|
510
510
|
sourceTable = "mc_source_files";
|
|
511
511
|
scopeColumn = "mc_version_id";
|
|
512
512
|
scopeId = versionRow.id;
|
|
513
|
-
stats = await db.$queryRawUnsafe(`SELECT COUNT(*)::text AS total,
|
|
514
|
-
COUNT(CASE WHEN embedding IS NOT NULL THEN 1 END)::text AS embedded
|
|
513
|
+
stats = await db.$queryRawUnsafe(`SELECT COUNT(*)::text AS total,
|
|
514
|
+
COUNT(CASE WHEN embedding IS NOT NULL THEN 1 END)::text AS embedded
|
|
515
515
|
FROM mc_source_files WHERE mc_version_id = $1`, versionRow.id);
|
|
516
516
|
}
|
|
517
517
|
else {
|
|
@@ -529,8 +529,8 @@ export async function getEmbedStatus(target) {
|
|
|
529
529
|
if (!mod)
|
|
530
530
|
throw new Error(`Mod #${modDbId} not found`);
|
|
531
531
|
scopeId = modDbId;
|
|
532
|
-
stats = await db.$queryRawUnsafe(`SELECT COUNT(*)::text AS total,
|
|
533
|
-
COUNT(CASE WHEN embedding IS NOT NULL THEN 1 END)::text AS embedded
|
|
532
|
+
stats = await db.$queryRawUnsafe(`SELECT COUNT(*)::text AS total,
|
|
533
|
+
COUNT(CASE WHEN embedding IS NOT NULL THEN 1 END)::text AS embedded
|
|
534
534
|
FROM mod_source_files WHERE mod_id = $1`, modDbId);
|
|
535
535
|
}
|
|
536
536
|
const total = parseInt(stats[0].total, 10);
|
package/dist/tools/graphify.js
CHANGED
|
@@ -496,14 +496,14 @@ export async function getGraphReport(dbId) {
|
|
|
496
496
|
return content.slice(0, 30_000); // cap at 30KB (reports are typically 2-5KB)
|
|
497
497
|
}
|
|
498
498
|
// ── Graph enrichment (Plan 1: Chat-Agent-Driven) ──────────────────────────────
|
|
499
|
-
const ENRICHMENT_SCHEMA = `Return JSON with this exact structure:
|
|
500
|
-
{
|
|
501
|
-
"nodes": [{"id": "ClassName", "type": "class", "src": "path/to/File.java", "loc": "L42", "community": "subsystem_name"}],
|
|
502
|
-
"edges": [{"source": "ClassA", "target": "ClassB", "relation": "implements_pattern", "context": "Singleton pattern via lazy init"}]
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
Valid relation types for semantic edges: implements_pattern, belongs_to_subsystem, delegates_to, adapts, wraps, event_handler_for, config_for, registry_entry, extends_behavior.
|
|
506
|
-
Node types: class, interface, enum, annotation, package, subsystem.
|
|
499
|
+
const ENRICHMENT_SCHEMA = `Return JSON with this exact structure:
|
|
500
|
+
{
|
|
501
|
+
"nodes": [{"id": "ClassName", "type": "class", "src": "path/to/File.java", "loc": "L42", "community": "subsystem_name"}],
|
|
502
|
+
"edges": [{"source": "ClassA", "target": "ClassB", "relation": "implements_pattern", "context": "Singleton pattern via lazy init"}]
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
Valid relation types for semantic edges: implements_pattern, belongs_to_subsystem, delegates_to, adapts, wraps, event_handler_for, config_for, registry_entry, extends_behavior.
|
|
506
|
+
Node types: class, interface, enum, annotation, package, subsystem.
|
|
507
507
|
Focus on design patterns, subsystem groupings, and architectural relationships — NOT call graphs (those are already in the AST layer).`;
|
|
508
508
|
const CHUNK_SIZE = 20; // files per chunk
|
|
509
509
|
/**
|
|
@@ -542,12 +542,12 @@ export async function enrichNextChunk(dbId) {
|
|
|
542
542
|
existingNodes: nodesInChunk.map(n => ({ id: n.id, type: n.type, src: n.src })),
|
|
543
543
|
existingEdges: edgesInChunk.map(e => ({ source: e.source, target: e.target, relation: e.relation })),
|
|
544
544
|
};
|
|
545
|
-
const prompt = `Analyze these ${chunkFiles.length} source files from mod "${mod.modId}" and identify semantic relationships.
|
|
546
|
-
|
|
547
|
-
AST context (structural relationships already captured):
|
|
548
|
-
${JSON.stringify(astContext, null, 2)}
|
|
549
|
-
|
|
550
|
-
Add SEMANTIC relationships on top — design patterns, subsystem groupings, architectural intent.
|
|
545
|
+
const prompt = `Analyze these ${chunkFiles.length} source files from mod "${mod.modId}" and identify semantic relationships.
|
|
546
|
+
|
|
547
|
+
AST context (structural relationships already captured):
|
|
548
|
+
${JSON.stringify(astContext, null, 2)}
|
|
549
|
+
|
|
550
|
+
Add SEMANTIC relationships on top — design patterns, subsystem groupings, architectural intent.
|
|
551
551
|
Do NOT duplicate existing structural edges (calls, imports, extends, implements).`;
|
|
552
552
|
return {
|
|
553
553
|
chunkIndex: cursor,
|
package/dist/tools/mc-fts.js
CHANGED
|
@@ -128,8 +128,8 @@ export async function indexMcSourceSemantic(version, batchSize = 50) {
|
|
|
128
128
|
while (true) {
|
|
129
129
|
const { getDb } = await import("../db.js");
|
|
130
130
|
const _db = await getDb();
|
|
131
|
-
const batch = await _db.$queryRawUnsafe(`SELECT id, class_name, content FROM mc_source_files
|
|
132
|
-
WHERE mc_version_id = $1 AND embedding IS NULL
|
|
131
|
+
const batch = await _db.$queryRawUnsafe(`SELECT id, class_name, content FROM mc_source_files
|
|
132
|
+
WHERE mc_version_id = $1 AND embedding IS NULL
|
|
133
133
|
ORDER BY id LIMIT $2`, mcVersionId, batchSize);
|
|
134
134
|
if (!batch.length)
|
|
135
135
|
break;
|
|
@@ -40,7 +40,6 @@ export declare function getMixinTargets(modId: string | number): Promise<{
|
|
|
40
40
|
export declare function getMixinConflicts(targetClass: string): Promise<{
|
|
41
41
|
targetClass: string;
|
|
42
42
|
conflictingMods: {
|
|
43
|
-
tags: string[];
|
|
44
43
|
sha512: string | null;
|
|
45
44
|
id: number;
|
|
46
45
|
version: string;
|
|
@@ -67,6 +66,7 @@ export declare function getMixinConflicts(targetClass: string): Promise<{
|
|
|
67
66
|
atEntries: import("@prisma/client/runtime/library").JsonValue;
|
|
68
67
|
awEntries: import("@prisma/client/runtime/library").JsonValue;
|
|
69
68
|
metadataSource: string | null;
|
|
69
|
+
tags: string[];
|
|
70
70
|
ingestedAt: Date;
|
|
71
71
|
updatedAt: Date;
|
|
72
72
|
}[];
|
package/dist/tools/mixin-scan.js
CHANGED
|
@@ -25,22 +25,22 @@ async function mixinConflictRaw(loader, mcVersion, minConflicts = 2) {
|
|
|
25
25
|
}
|
|
26
26
|
const whereSQL = whereClauses.join(" AND ");
|
|
27
27
|
const db = await getDb();
|
|
28
|
-
const rows = await db.$queryRawUnsafe(`
|
|
29
|
-
WITH deduped AS (
|
|
30
|
-
SELECT DISTINCT ON (mod_id) id, mod_id, mixin_targets
|
|
31
|
-
FROM "mods"
|
|
32
|
-
WHERE ${whereSQL}
|
|
33
|
-
ORDER BY mod_id, id DESC
|
|
34
|
-
)
|
|
35
|
-
SELECT
|
|
36
|
-
t.class_name,
|
|
37
|
-
COUNT(DISTINCT d.mod_id)::int AS mod_count,
|
|
38
|
-
ARRAY_AGG(DISTINCT d.id) AS mod_ids
|
|
39
|
-
FROM deduped d
|
|
40
|
-
CROSS JOIN LATERAL jsonb_array_elements_text(d.mixin_targets::jsonb) AS t(class_name)
|
|
41
|
-
GROUP BY t.class_name
|
|
42
|
-
HAVING COUNT(DISTINCT d.mod_id) >= $1
|
|
43
|
-
ORDER BY mod_count DESC
|
|
28
|
+
const rows = await db.$queryRawUnsafe(`
|
|
29
|
+
WITH deduped AS (
|
|
30
|
+
SELECT DISTINCT ON (mod_id) id, mod_id, mixin_targets
|
|
31
|
+
FROM "mods"
|
|
32
|
+
WHERE ${whereSQL}
|
|
33
|
+
ORDER BY mod_id, id DESC
|
|
34
|
+
)
|
|
35
|
+
SELECT
|
|
36
|
+
t.class_name,
|
|
37
|
+
COUNT(DISTINCT d.mod_id)::int AS mod_count,
|
|
38
|
+
ARRAY_AGG(DISTINCT d.id) AS mod_ids
|
|
39
|
+
FROM deduped d
|
|
40
|
+
CROSS JOIN LATERAL jsonb_array_elements_text(d.mixin_targets::jsonb) AS t(class_name)
|
|
41
|
+
GROUP BY t.class_name
|
|
42
|
+
HAVING COUNT(DISTINCT d.mod_id) >= $1
|
|
43
|
+
ORDER BY mod_count DESC
|
|
44
44
|
`, ...params);
|
|
45
45
|
return rows.map((r) => ({
|
|
46
46
|
className: r.class_name,
|
|
@@ -330,9 +330,9 @@ async function batchSimilarities(pairs, backend, table) {
|
|
|
330
330
|
const db = await getDb();
|
|
331
331
|
const aIds = pairs.map((p) => p.aId);
|
|
332
332
|
const bIds = pairs.map((p) => p.bId);
|
|
333
|
-
const rows = await db.$queryRawUnsafe(`SELECT p.a_id, p.b_id, (1 - (a.embedding <=> b.embedding))::float AS sim
|
|
334
|
-
FROM unnest($1::int[], $2::int[]) AS p(a_id, b_id)
|
|
335
|
-
JOIN ${table} a ON a.id = p.a_id AND a.embedding IS NOT NULL
|
|
333
|
+
const rows = await db.$queryRawUnsafe(`SELECT p.a_id, p.b_id, (1 - (a.embedding <=> b.embedding))::float AS sim
|
|
334
|
+
FROM unnest($1::int[], $2::int[]) AS p(a_id, b_id)
|
|
335
|
+
JOIN ${table} a ON a.id = p.a_id AND a.embedding IS NOT NULL
|
|
336
336
|
JOIN ${table} b ON b.id = p.b_id AND b.embedding IS NOT NULL`, aIds, bIds);
|
|
337
337
|
for (const { a_id, b_id, sim } of rows)
|
|
338
338
|
out.set(`${a_id}:${b_id}`, sim);
|