kontext-engine 0.1.4 → 0.1.5
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 +8 -1
- package/dist/cli/index.js +372 -84
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +319 -130
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -802,29 +802,29 @@ async function createLocalEmbedder() {
|
|
|
802
802
|
}
|
|
803
803
|
var VOYAGE_API_URL = "https://api.voyageai.com/v1/embeddings";
|
|
804
804
|
var VOYAGE_MODEL = "voyage-code-3";
|
|
805
|
-
var
|
|
805
|
+
var VOYAGE_DEFAULT_DIMENSIONS = 1024;
|
|
806
806
|
var VOYAGE_BATCH_SIZE = 128;
|
|
807
|
-
function createVoyageEmbedder(apiKey) {
|
|
807
|
+
function createVoyageEmbedder(apiKey, dimensions = VOYAGE_DEFAULT_DIMENSIONS) {
|
|
808
808
|
return {
|
|
809
809
|
name: VOYAGE_MODEL,
|
|
810
|
-
dimensions
|
|
810
|
+
dimensions,
|
|
811
811
|
async embed(texts, onProgress) {
|
|
812
812
|
const results = [];
|
|
813
813
|
for (let i = 0; i < texts.length; i += VOYAGE_BATCH_SIZE) {
|
|
814
814
|
const batch = texts.slice(i, i + VOYAGE_BATCH_SIZE);
|
|
815
|
-
const vectors = await callVoyageAPI(apiKey, batch);
|
|
815
|
+
const vectors = await callVoyageAPI(apiKey, batch, "document", dimensions);
|
|
816
816
|
results.push(...vectors);
|
|
817
817
|
onProgress?.(Math.min(i + batch.length, texts.length), texts.length);
|
|
818
818
|
}
|
|
819
819
|
return results;
|
|
820
820
|
},
|
|
821
821
|
async embedSingle(text) {
|
|
822
|
-
const vectors = await callVoyageAPI(apiKey, [text]);
|
|
822
|
+
const vectors = await callVoyageAPI(apiKey, [text], "query", dimensions);
|
|
823
823
|
return vectors[0];
|
|
824
824
|
}
|
|
825
825
|
};
|
|
826
826
|
}
|
|
827
|
-
async function callVoyageAPI(apiKey, texts) {
|
|
827
|
+
async function callVoyageAPI(apiKey, texts, inputType, dimensions) {
|
|
828
828
|
const response = await fetchWithRetry(VOYAGE_API_URL, {
|
|
829
829
|
method: "POST",
|
|
830
830
|
headers: {
|
|
@@ -834,7 +834,8 @@ async function callVoyageAPI(apiKey, texts) {
|
|
|
834
834
|
body: JSON.stringify({
|
|
835
835
|
model: VOYAGE_MODEL,
|
|
836
836
|
input: texts,
|
|
837
|
-
input_type:
|
|
837
|
+
input_type: inputType,
|
|
838
|
+
output_dimension: dimensions
|
|
838
839
|
})
|
|
839
840
|
});
|
|
840
841
|
const json = await response.json();
|
|
@@ -842,29 +843,29 @@ async function callVoyageAPI(apiKey, texts) {
|
|
|
842
843
|
}
|
|
843
844
|
var OPENAI_API_URL = "https://api.openai.com/v1/embeddings";
|
|
844
845
|
var OPENAI_MODEL = "text-embedding-3-large";
|
|
845
|
-
var
|
|
846
|
+
var OPENAI_DEFAULT_DIMENSIONS = 1024;
|
|
846
847
|
var OPENAI_BATCH_SIZE = 128;
|
|
847
|
-
function createOpenAIEmbedder(apiKey) {
|
|
848
|
+
function createOpenAIEmbedder(apiKey, dimensions = OPENAI_DEFAULT_DIMENSIONS) {
|
|
848
849
|
return {
|
|
849
850
|
name: OPENAI_MODEL,
|
|
850
|
-
dimensions
|
|
851
|
+
dimensions,
|
|
851
852
|
async embed(texts, onProgress) {
|
|
852
853
|
const results = [];
|
|
853
854
|
for (let i = 0; i < texts.length; i += OPENAI_BATCH_SIZE) {
|
|
854
855
|
const batch = texts.slice(i, i + OPENAI_BATCH_SIZE);
|
|
855
|
-
const vectors = await callOpenAIAPI(apiKey, batch);
|
|
856
|
+
const vectors = await callOpenAIAPI(apiKey, batch, dimensions);
|
|
856
857
|
results.push(...vectors);
|
|
857
858
|
onProgress?.(Math.min(i + batch.length, texts.length), texts.length);
|
|
858
859
|
}
|
|
859
860
|
return results;
|
|
860
861
|
},
|
|
861
862
|
async embedSingle(text) {
|
|
862
|
-
const vectors = await callOpenAIAPI(apiKey, [text]);
|
|
863
|
+
const vectors = await callOpenAIAPI(apiKey, [text], dimensions);
|
|
863
864
|
return vectors[0];
|
|
864
865
|
}
|
|
865
866
|
};
|
|
866
867
|
}
|
|
867
|
-
async function callOpenAIAPI(apiKey, texts) {
|
|
868
|
+
async function callOpenAIAPI(apiKey, texts, dimensions) {
|
|
868
869
|
const response = await fetchWithRetry(OPENAI_API_URL, {
|
|
869
870
|
method: "POST",
|
|
870
871
|
headers: {
|
|
@@ -874,7 +875,7 @@ async function callOpenAIAPI(apiKey, texts) {
|
|
|
874
875
|
body: JSON.stringify({
|
|
875
876
|
model: OPENAI_MODEL,
|
|
876
877
|
input: texts,
|
|
877
|
-
dimensions
|
|
878
|
+
dimensions
|
|
878
879
|
})
|
|
879
880
|
});
|
|
880
881
|
const json = await response.json();
|
|
@@ -1038,9 +1039,57 @@ function searchVectors(db, query, limit) {
|
|
|
1038
1039
|
}));
|
|
1039
1040
|
}
|
|
1040
1041
|
|
|
1042
|
+
// src/utils/errors.ts
|
|
1043
|
+
var ErrorCode = {
|
|
1044
|
+
NOT_INITIALIZED: "NOT_INITIALIZED",
|
|
1045
|
+
INDEX_FAILED: "INDEX_FAILED",
|
|
1046
|
+
PARSE_FAILED: "PARSE_FAILED",
|
|
1047
|
+
CHUNK_FAILED: "CHUNK_FAILED",
|
|
1048
|
+
EMBEDDER_FAILED: "EMBEDDER_FAILED",
|
|
1049
|
+
SEARCH_FAILED: "SEARCH_FAILED",
|
|
1050
|
+
CONFIG_INVALID: "CONFIG_INVALID",
|
|
1051
|
+
DB_CORRUPTED: "DB_CORRUPTED",
|
|
1052
|
+
DB_WRITE_FAILED: "DB_WRITE_FAILED",
|
|
1053
|
+
WATCHER_FAILED: "WATCHER_FAILED",
|
|
1054
|
+
LLM_FAILED: "LLM_FAILED"
|
|
1055
|
+
};
|
|
1056
|
+
var KontextError = class extends Error {
|
|
1057
|
+
code;
|
|
1058
|
+
constructor(message, code, cause) {
|
|
1059
|
+
super(message, { cause });
|
|
1060
|
+
this.name = "KontextError";
|
|
1061
|
+
this.code = code;
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
var IndexError = class extends KontextError {
|
|
1065
|
+
constructor(message, code, cause) {
|
|
1066
|
+
super(message, code, cause);
|
|
1067
|
+
this.name = "IndexError";
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
var SearchError = class extends KontextError {
|
|
1071
|
+
constructor(message, code, cause) {
|
|
1072
|
+
super(message, code, cause);
|
|
1073
|
+
this.name = "SearchError";
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
var ConfigError = class extends KontextError {
|
|
1077
|
+
constructor(message, code, cause) {
|
|
1078
|
+
super(message, code, cause);
|
|
1079
|
+
this.name = "ConfigError";
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
var DatabaseError = class extends KontextError {
|
|
1083
|
+
constructor(message, code, cause) {
|
|
1084
|
+
super(message, code, cause);
|
|
1085
|
+
this.name = "DatabaseError";
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
|
|
1041
1089
|
// src/storage/db.ts
|
|
1042
1090
|
var DEFAULT_DIMENSIONS = 384;
|
|
1043
|
-
|
|
1091
|
+
var VECTOR_DIMENSIONS_META_KEY = "vector_dimensions";
|
|
1092
|
+
function createDatabase(dbPath, dimensions) {
|
|
1044
1093
|
const dir = path3.dirname(dbPath);
|
|
1045
1094
|
if (!fs4.existsSync(dir)) {
|
|
1046
1095
|
fs4.mkdirSync(dir, { recursive: true });
|
|
@@ -1049,7 +1098,8 @@ function createDatabase(dbPath, dimensions = DEFAULT_DIMENSIONS) {
|
|
|
1049
1098
|
db.pragma("journal_mode = WAL");
|
|
1050
1099
|
db.pragma("foreign_keys = ON");
|
|
1051
1100
|
sqliteVec.load(db);
|
|
1052
|
-
initializeSchema(db, dimensions);
|
|
1101
|
+
initializeSchema(db, dimensions ?? DEFAULT_DIMENSIONS);
|
|
1102
|
+
ensureVectorDimensions(db, dimensions);
|
|
1053
1103
|
const stmtUpsertFile = db.prepare(`
|
|
1054
1104
|
INSERT INTO files (path, language, hash, last_indexed, size)
|
|
1055
1105
|
VALUES (@path, @language, @hash, @lastIndexed, @size)
|
|
@@ -1058,6 +1108,7 @@ function createDatabase(dbPath, dimensions = DEFAULT_DIMENSIONS) {
|
|
|
1058
1108
|
hash = excluded.hash,
|
|
1059
1109
|
last_indexed = excluded.last_indexed,
|
|
1060
1110
|
size = excluded.size
|
|
1111
|
+
RETURNING id
|
|
1061
1112
|
`);
|
|
1062
1113
|
const stmtGetFile = db.prepare(
|
|
1063
1114
|
"SELECT id, path, language, hash, last_indexed as lastIndexed, size FROM files WHERE path = ?"
|
|
@@ -1101,18 +1152,20 @@ function createDatabase(dbPath, dimensions = DEFAULT_DIMENSIONS) {
|
|
|
1101
1152
|
);
|
|
1102
1153
|
return {
|
|
1103
1154
|
upsertFile(file) {
|
|
1104
|
-
const
|
|
1155
|
+
const row = stmtUpsertFile.get({
|
|
1105
1156
|
path: file.path,
|
|
1106
1157
|
language: file.language,
|
|
1107
1158
|
hash: file.hash,
|
|
1108
1159
|
lastIndexed: Date.now(),
|
|
1109
1160
|
size: file.size
|
|
1110
1161
|
});
|
|
1111
|
-
if (
|
|
1112
|
-
|
|
1162
|
+
if (!row?.id) {
|
|
1163
|
+
throw new DatabaseError(
|
|
1164
|
+
`Failed to upsert file: ${file.path}`,
|
|
1165
|
+
ErrorCode.DB_WRITE_FAILED
|
|
1166
|
+
);
|
|
1113
1167
|
}
|
|
1114
|
-
|
|
1115
|
-
return existing?.id ?? 0;
|
|
1168
|
+
return row.id;
|
|
1116
1169
|
},
|
|
1117
1170
|
getFile(filePath) {
|
|
1118
1171
|
const row = stmtGetFile.get(filePath);
|
|
@@ -1155,15 +1208,17 @@ function createDatabase(dbPath, dimensions = DEFAULT_DIMENSIONS) {
|
|
|
1155
1208
|
return row.lastIndexed;
|
|
1156
1209
|
},
|
|
1157
1210
|
deleteFile(filePath) {
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1211
|
+
db.transaction(() => {
|
|
1212
|
+
const file = stmtGetFile.get(filePath);
|
|
1213
|
+
if (file) {
|
|
1214
|
+
const chunkRows = stmtGetChunkIdsByFile.all(file.id);
|
|
1215
|
+
const chunkIds = chunkRows.map((r) => r.id);
|
|
1216
|
+
if (chunkIds.length > 0) {
|
|
1217
|
+
deleteVectorsByChunkIds(db, chunkIds);
|
|
1218
|
+
}
|
|
1164
1219
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1220
|
+
stmtDeleteFile.run(filePath);
|
|
1221
|
+
})();
|
|
1167
1222
|
},
|
|
1168
1223
|
insertChunks(fileId, chunks) {
|
|
1169
1224
|
const ids = [];
|
|
@@ -1258,12 +1313,14 @@ function createDatabase(dbPath, dimensions = DEFAULT_DIMENSIONS) {
|
|
|
1258
1313
|
}));
|
|
1259
1314
|
},
|
|
1260
1315
|
deleteChunksByFile(fileId) {
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1316
|
+
db.transaction(() => {
|
|
1317
|
+
const chunkRows = stmtGetChunkIdsByFile.all(fileId);
|
|
1318
|
+
const chunkIds = chunkRows.map((r) => r.id);
|
|
1319
|
+
if (chunkIds.length > 0) {
|
|
1320
|
+
deleteVectorsByChunkIds(db, chunkIds);
|
|
1321
|
+
}
|
|
1322
|
+
stmtDeleteChunksByFile.run(fileId);
|
|
1323
|
+
})();
|
|
1267
1324
|
},
|
|
1268
1325
|
insertDependency(sourceChunkId, targetChunkId, type) {
|
|
1269
1326
|
stmtInsertDep.run(sourceChunkId, targetChunkId, type);
|
|
@@ -1325,6 +1382,59 @@ function getMetaVersion(db) {
|
|
|
1325
1382
|
return 0;
|
|
1326
1383
|
}
|
|
1327
1384
|
}
|
|
1385
|
+
function ensureVectorDimensions(db, expectedDimensions) {
|
|
1386
|
+
const actual = getExistingVectorDimensions(db);
|
|
1387
|
+
const stored = db.prepare("SELECT value FROM meta WHERE key = ?").get(VECTOR_DIMENSIONS_META_KEY);
|
|
1388
|
+
const storedValue = stored?.value;
|
|
1389
|
+
const storedDimensions = storedValue ? Number.parseInt(storedValue, 10) : void 0;
|
|
1390
|
+
if (storedDimensions !== void 0 && (!Number.isInteger(storedDimensions) || storedDimensions <= 0)) {
|
|
1391
|
+
throw new DatabaseError(
|
|
1392
|
+
`Invalid stored vector dimensions metadata: ${storedValue ?? "unknown"}`,
|
|
1393
|
+
ErrorCode.DB_CORRUPTED
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
if (actual !== null && storedDimensions !== void 0 && storedDimensions !== actual) {
|
|
1397
|
+
throw new DatabaseError(
|
|
1398
|
+
`Vector dimensions metadata mismatch: meta=${storedDimensions}, table=${actual}.`,
|
|
1399
|
+
ErrorCode.DB_CORRUPTED
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
if (expectedDimensions === void 0) {
|
|
1403
|
+
if (storedDimensions !== void 0) return;
|
|
1404
|
+
const dimensions = actual ?? DEFAULT_DIMENSIONS;
|
|
1405
|
+
db.prepare(
|
|
1406
|
+
"INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)"
|
|
1407
|
+
).run(VECTOR_DIMENSIONS_META_KEY, String(dimensions));
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
if (!stored) {
|
|
1411
|
+
const dimensions = actual ?? expectedDimensions;
|
|
1412
|
+
db.prepare(
|
|
1413
|
+
"INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)"
|
|
1414
|
+
).run(VECTOR_DIMENSIONS_META_KEY, String(dimensions));
|
|
1415
|
+
if (actual !== null && actual !== expectedDimensions) {
|
|
1416
|
+
throw new DatabaseError(
|
|
1417
|
+
`Vector dimension mismatch: index uses ${actual} dims, but config requests ${expectedDimensions} dims. Rebuild the index.`,
|
|
1418
|
+
ErrorCode.CONFIG_INVALID
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
if (storedDimensions !== expectedDimensions) {
|
|
1424
|
+
throw new DatabaseError(
|
|
1425
|
+
`Vector dimension mismatch: index uses ${storedDimensions} dims, but config requests ${expectedDimensions} dims. Rebuild the index.`,
|
|
1426
|
+
ErrorCode.CONFIG_INVALID
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
function getExistingVectorDimensions(db) {
|
|
1431
|
+
const row = db.prepare("SELECT sql FROM sqlite_master WHERE name = 'chunk_vectors'").get();
|
|
1432
|
+
const sql = row?.sql;
|
|
1433
|
+
if (!sql) return null;
|
|
1434
|
+
const match = sql.match(/embedding\s+float\[(\d+)\]/i);
|
|
1435
|
+
if (!match) return null;
|
|
1436
|
+
return Number.parseInt(match[1], 10);
|
|
1437
|
+
}
|
|
1328
1438
|
|
|
1329
1439
|
// src/search/vector.ts
|
|
1330
1440
|
function distanceToScore(distance) {
|
|
@@ -2155,7 +2265,9 @@ var COMMON_STEMS = {
|
|
|
2155
2265
|
transformer: "transform",
|
|
2156
2266
|
transformation: "transform",
|
|
2157
2267
|
connection: "connect",
|
|
2268
|
+
connecting: "connect",
|
|
2158
2269
|
connector: "connect",
|
|
2270
|
+
migrating: "migrate",
|
|
2159
2271
|
migration: "migrate",
|
|
2160
2272
|
scheduling: "schedule",
|
|
2161
2273
|
scheduler: "schedule",
|
|
@@ -2164,7 +2276,8 @@ var COMMON_STEMS = {
|
|
|
2164
2276
|
routing: "route",
|
|
2165
2277
|
router: "route",
|
|
2166
2278
|
indexing: "index",
|
|
2167
|
-
indexer: "index"
|
|
2279
|
+
indexer: "index",
|
|
2280
|
+
subscribing: "subscribe"
|
|
2168
2281
|
};
|
|
2169
2282
|
var STEM_SUFFIXES = [
|
|
2170
2283
|
"tion",
|
|
@@ -2358,53 +2471,6 @@ async function steer(provider, query, limit, searchExecutor) {
|
|
|
2358
2471
|
import fs6 from "fs";
|
|
2359
2472
|
import path5 from "path";
|
|
2360
2473
|
|
|
2361
|
-
// src/utils/errors.ts
|
|
2362
|
-
var ErrorCode = {
|
|
2363
|
-
NOT_INITIALIZED: "NOT_INITIALIZED",
|
|
2364
|
-
INDEX_FAILED: "INDEX_FAILED",
|
|
2365
|
-
PARSE_FAILED: "PARSE_FAILED",
|
|
2366
|
-
CHUNK_FAILED: "CHUNK_FAILED",
|
|
2367
|
-
EMBEDDER_FAILED: "EMBEDDER_FAILED",
|
|
2368
|
-
SEARCH_FAILED: "SEARCH_FAILED",
|
|
2369
|
-
CONFIG_INVALID: "CONFIG_INVALID",
|
|
2370
|
-
DB_CORRUPTED: "DB_CORRUPTED",
|
|
2371
|
-
DB_WRITE_FAILED: "DB_WRITE_FAILED",
|
|
2372
|
-
WATCHER_FAILED: "WATCHER_FAILED",
|
|
2373
|
-
LLM_FAILED: "LLM_FAILED"
|
|
2374
|
-
};
|
|
2375
|
-
var KontextError = class extends Error {
|
|
2376
|
-
code;
|
|
2377
|
-
constructor(message, code, cause) {
|
|
2378
|
-
super(message, { cause });
|
|
2379
|
-
this.name = "KontextError";
|
|
2380
|
-
this.code = code;
|
|
2381
|
-
}
|
|
2382
|
-
};
|
|
2383
|
-
var IndexError = class extends KontextError {
|
|
2384
|
-
constructor(message, code, cause) {
|
|
2385
|
-
super(message, code, cause);
|
|
2386
|
-
this.name = "IndexError";
|
|
2387
|
-
}
|
|
2388
|
-
};
|
|
2389
|
-
var SearchError = class extends KontextError {
|
|
2390
|
-
constructor(message, code, cause) {
|
|
2391
|
-
super(message, code, cause);
|
|
2392
|
-
this.name = "SearchError";
|
|
2393
|
-
}
|
|
2394
|
-
};
|
|
2395
|
-
var ConfigError = class extends KontextError {
|
|
2396
|
-
constructor(message, code, cause) {
|
|
2397
|
-
super(message, code, cause);
|
|
2398
|
-
this.name = "ConfigError";
|
|
2399
|
-
}
|
|
2400
|
-
};
|
|
2401
|
-
var DatabaseError = class extends KontextError {
|
|
2402
|
-
constructor(message, code, cause) {
|
|
2403
|
-
super(message, code, cause);
|
|
2404
|
-
this.name = "DatabaseError";
|
|
2405
|
-
}
|
|
2406
|
-
};
|
|
2407
|
-
|
|
2408
2474
|
// src/utils/logger.ts
|
|
2409
2475
|
var LogLevel = {
|
|
2410
2476
|
DEBUG: 0,
|
|
@@ -2447,6 +2513,8 @@ function createLogger(options) {
|
|
|
2447
2513
|
// src/cli/commands/config.ts
|
|
2448
2514
|
import fs5 from "fs";
|
|
2449
2515
|
import path4 from "path";
|
|
2516
|
+
var CTX_DIR = ".ctx";
|
|
2517
|
+
var CONFIG_FILENAME = "config.json";
|
|
2450
2518
|
var DEFAULT_CONFIG = {
|
|
2451
2519
|
embedder: {
|
|
2452
2520
|
provider: "local",
|
|
@@ -2491,11 +2559,110 @@ var VALIDATION_RULES = {
|
|
|
2491
2559
|
message: `Must be null or one of: ${[...VALID_LLM_PROVIDERS].join(", ")}`
|
|
2492
2560
|
}
|
|
2493
2561
|
};
|
|
2562
|
+
function resolveCtxDir(projectPath) {
|
|
2563
|
+
const absoluteRoot = path4.resolve(projectPath);
|
|
2564
|
+
const ctxDir = path4.join(absoluteRoot, CTX_DIR);
|
|
2565
|
+
if (!fs5.existsSync(ctxDir)) {
|
|
2566
|
+
throw new ConfigError(
|
|
2567
|
+
`Project not initialized. Run "ctx init" first. (${CTX_DIR}/ not found)`,
|
|
2568
|
+
ErrorCode.NOT_INITIALIZED
|
|
2569
|
+
);
|
|
2570
|
+
}
|
|
2571
|
+
return ctxDir;
|
|
2572
|
+
}
|
|
2573
|
+
function configPath(ctxDir) {
|
|
2574
|
+
return path4.join(ctxDir, CONFIG_FILENAME);
|
|
2575
|
+
}
|
|
2576
|
+
function readConfig(ctxDir) {
|
|
2577
|
+
const filePath = configPath(ctxDir);
|
|
2578
|
+
if (!fs5.existsSync(filePath)) {
|
|
2579
|
+
writeConfig(ctxDir, DEFAULT_CONFIG);
|
|
2580
|
+
return structuredClone(DEFAULT_CONFIG);
|
|
2581
|
+
}
|
|
2582
|
+
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
2583
|
+
const parsed = JSON.parse(raw);
|
|
2584
|
+
return mergeWithDefaults(parsed);
|
|
2585
|
+
}
|
|
2586
|
+
function writeConfig(ctxDir, config) {
|
|
2587
|
+
fs5.writeFileSync(
|
|
2588
|
+
configPath(ctxDir),
|
|
2589
|
+
JSON.stringify(config, null, 2) + "\n"
|
|
2590
|
+
);
|
|
2591
|
+
}
|
|
2592
|
+
function mergeWithDefaults(partial) {
|
|
2593
|
+
return {
|
|
2594
|
+
embedder: { ...DEFAULT_CONFIG.embedder, ...partial.embedder },
|
|
2595
|
+
search: {
|
|
2596
|
+
...DEFAULT_CONFIG.search,
|
|
2597
|
+
...partial.search,
|
|
2598
|
+
weights: { ...DEFAULT_CONFIG.search.weights, ...partial.search?.weights }
|
|
2599
|
+
},
|
|
2600
|
+
watch: { ...DEFAULT_CONFIG.watch, ...partial.watch },
|
|
2601
|
+
llm: { ...DEFAULT_CONFIG.llm, ...partial.llm }
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
function runConfigShow(projectPath) {
|
|
2605
|
+
const ctxDir = resolveCtxDir(projectPath);
|
|
2606
|
+
const config = readConfig(ctxDir);
|
|
2607
|
+
return {
|
|
2608
|
+
config,
|
|
2609
|
+
text: JSON.stringify(config, null, 2)
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
// src/cli/embedder.ts
|
|
2614
|
+
function getProjectEmbedderConfig(projectPath) {
|
|
2615
|
+
const { config } = runConfigShow(projectPath);
|
|
2616
|
+
return config.embedder;
|
|
2617
|
+
}
|
|
2618
|
+
async function createProjectEmbedder(projectPath) {
|
|
2619
|
+
const config = getProjectEmbedderConfig(projectPath);
|
|
2620
|
+
validateProjectEmbedderConfig(config);
|
|
2621
|
+
switch (config.provider) {
|
|
2622
|
+
case "local":
|
|
2623
|
+
return await createLocalEmbedder();
|
|
2624
|
+
case "voyage": {
|
|
2625
|
+
const apiKey = requireApiKey("CTX_VOYAGE_KEY", "voyage");
|
|
2626
|
+
return createVoyageEmbedder(apiKey, config.dimensions);
|
|
2627
|
+
}
|
|
2628
|
+
case "openai": {
|
|
2629
|
+
const apiKey = requireApiKey("CTX_OPENAI_KEY", "openai");
|
|
2630
|
+
return createOpenAIEmbedder(apiKey, config.dimensions);
|
|
2631
|
+
}
|
|
2632
|
+
default:
|
|
2633
|
+
throw new ConfigError(
|
|
2634
|
+
`Unsupported embedder provider "${config.provider}". Use local, voyage, or openai.`,
|
|
2635
|
+
ErrorCode.CONFIG_INVALID
|
|
2636
|
+
);
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
function requireApiKey(envVar, provider) {
|
|
2640
|
+
const value = process.env[envVar];
|
|
2641
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
2642
|
+
throw new ConfigError(
|
|
2643
|
+
`Embedder provider "${provider}" requires ${envVar}. Export ${envVar} before running this command.`,
|
|
2644
|
+
ErrorCode.CONFIG_INVALID
|
|
2645
|
+
);
|
|
2646
|
+
}
|
|
2647
|
+
function validateProjectEmbedderConfig(config) {
|
|
2648
|
+
if (!Number.isInteger(config.dimensions) || config.dimensions <= 0) {
|
|
2649
|
+
throw new ConfigError(
|
|
2650
|
+
`Invalid embedder.dimensions (${String(config.dimensions)}). Must be a positive integer.`,
|
|
2651
|
+
ErrorCode.CONFIG_INVALID
|
|
2652
|
+
);
|
|
2653
|
+
}
|
|
2654
|
+
if (config.provider === "local" && config.dimensions !== 384) {
|
|
2655
|
+
throw new ConfigError(
|
|
2656
|
+
'Local embedder requires "embedder.dimensions" = 384. Update config or switch provider.',
|
|
2657
|
+
ErrorCode.CONFIG_INVALID
|
|
2658
|
+
);
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2494
2661
|
|
|
2495
2662
|
// src/cli/commands/init.ts
|
|
2496
|
-
var
|
|
2663
|
+
var CTX_DIR2 = ".ctx";
|
|
2497
2664
|
var DB_FILENAME = "index.db";
|
|
2498
|
-
var
|
|
2665
|
+
var CONFIG_FILENAME2 = "config.json";
|
|
2499
2666
|
var GITIGNORE_ENTRY = ".ctx/";
|
|
2500
2667
|
function ensureGitignore(projectRoot) {
|
|
2501
2668
|
const gitignorePath = path5.join(projectRoot, ".gitignore");
|
|
@@ -2511,10 +2678,10 @@ function ensureGitignore(projectRoot) {
|
|
|
2511
2678
|
}
|
|
2512
2679
|
}
|
|
2513
2680
|
function ensureConfig(ctxDir) {
|
|
2514
|
-
const
|
|
2515
|
-
if (fs6.existsSync(
|
|
2681
|
+
const configPath2 = path5.join(ctxDir, CONFIG_FILENAME2);
|
|
2682
|
+
if (fs6.existsSync(configPath2)) return;
|
|
2516
2683
|
fs6.writeFileSync(
|
|
2517
|
-
|
|
2684
|
+
configPath2,
|
|
2518
2685
|
JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n"
|
|
2519
2686
|
);
|
|
2520
2687
|
}
|
|
@@ -2536,12 +2703,13 @@ async function runInit(projectPath, options = {}) {
|
|
|
2536
2703
|
const absoluteRoot = path5.resolve(projectPath);
|
|
2537
2704
|
const start = performance.now();
|
|
2538
2705
|
log(`Indexing ${absoluteRoot}...`);
|
|
2539
|
-
const ctxDir = path5.join(absoluteRoot,
|
|
2706
|
+
const ctxDir = path5.join(absoluteRoot, CTX_DIR2);
|
|
2540
2707
|
if (!fs6.existsSync(ctxDir)) fs6.mkdirSync(ctxDir, { recursive: true });
|
|
2541
2708
|
ensureGitignore(absoluteRoot);
|
|
2542
2709
|
ensureConfig(ctxDir);
|
|
2710
|
+
const embedderConfig = getProjectEmbedderConfig(absoluteRoot);
|
|
2543
2711
|
const dbPath = path5.join(ctxDir, DB_FILENAME);
|
|
2544
|
-
const db = createDatabase(dbPath);
|
|
2712
|
+
const db = createDatabase(dbPath, embedderConfig.dimensions);
|
|
2545
2713
|
try {
|
|
2546
2714
|
const discovered = await discoverFiles({
|
|
2547
2715
|
root: absoluteRoot,
|
|
@@ -2629,7 +2797,7 @@ async function runInit(projectPath, options = {}) {
|
|
|
2629
2797
|
log(` ${allChunksWithMeta.length} chunks created`);
|
|
2630
2798
|
let vectorsCreated = 0;
|
|
2631
2799
|
if (!options.skipEmbedding && allChunksWithMeta.length > 0) {
|
|
2632
|
-
const embedder = await createEmbedder();
|
|
2800
|
+
const embedder = await createEmbedder(absoluteRoot);
|
|
2633
2801
|
const texts = allChunksWithMeta.map(
|
|
2634
2802
|
(cm) => prepareChunkText(cm.fileRelPath, cm.chunk.parent, cm.chunk.text)
|
|
2635
2803
|
);
|
|
@@ -2651,7 +2819,7 @@ async function runInit(projectPath, options = {}) {
|
|
|
2651
2819
|
log(
|
|
2652
2820
|
` ${discovered.length} files \u2192 ${allChunksWithMeta.length} chunks` + (vectorsCreated > 0 ? ` \u2192 ${vectorsCreated} vectors` : "")
|
|
2653
2821
|
);
|
|
2654
|
-
log(` Database: ${
|
|
2822
|
+
log(` Database: ${CTX_DIR2}/${DB_FILENAME} (${formatBytes(dbSize)})`);
|
|
2655
2823
|
return {
|
|
2656
2824
|
filesDiscovered: discovered.length,
|
|
2657
2825
|
filesAdded: changes.added.length,
|
|
@@ -2667,14 +2835,14 @@ async function runInit(projectPath, options = {}) {
|
|
|
2667
2835
|
db.close();
|
|
2668
2836
|
}
|
|
2669
2837
|
}
|
|
2670
|
-
async function createEmbedder() {
|
|
2671
|
-
return
|
|
2838
|
+
async function createEmbedder(projectPath) {
|
|
2839
|
+
return createProjectEmbedder(projectPath);
|
|
2672
2840
|
}
|
|
2673
2841
|
|
|
2674
2842
|
// src/cli/commands/query.ts
|
|
2675
2843
|
import fs7 from "fs";
|
|
2676
2844
|
import path6 from "path";
|
|
2677
|
-
var
|
|
2845
|
+
var CTX_DIR3 = ".ctx";
|
|
2678
2846
|
var DB_FILENAME2 = "index.db";
|
|
2679
2847
|
var SNIPPET_MAX_LENGTH = 200;
|
|
2680
2848
|
var STRATEGY_WEIGHTS = {
|
|
@@ -2740,17 +2908,21 @@ function isPathLike(query) {
|
|
|
2740
2908
|
async function runQuery(projectPath, query, options) {
|
|
2741
2909
|
const limit = normalizeLimit(options.limit);
|
|
2742
2910
|
const absoluteRoot = path6.resolve(projectPath);
|
|
2743
|
-
const dbPath = path6.join(absoluteRoot,
|
|
2911
|
+
const dbPath = path6.join(absoluteRoot, CTX_DIR3, DB_FILENAME2);
|
|
2744
2912
|
if (!fs7.existsSync(dbPath)) {
|
|
2745
2913
|
throw new KontextError(
|
|
2746
|
-
`Project not initialized. Run "ctx init" first. (${
|
|
2914
|
+
`Project not initialized. Run "ctx init" first. (${CTX_DIR3}/${DB_FILENAME2} not found)`,
|
|
2747
2915
|
ErrorCode.NOT_INITIALIZED
|
|
2748
2916
|
);
|
|
2749
2917
|
}
|
|
2750
2918
|
const start = performance.now();
|
|
2751
|
-
const
|
|
2919
|
+
const embedderConfig = getProjectEmbedderConfig(absoluteRoot);
|
|
2920
|
+
const db = createDatabase(dbPath, embedderConfig.dimensions);
|
|
2752
2921
|
try {
|
|
2753
|
-
const strategyResults = await runStrategies(db, query, {
|
|
2922
|
+
const strategyResults = await runStrategies(db, absoluteRoot, query, {
|
|
2923
|
+
...options,
|
|
2924
|
+
limit
|
|
2925
|
+
});
|
|
2754
2926
|
const pathBoostTerms = extractPathBoostTerms(query);
|
|
2755
2927
|
const fused = fusionMergeWithPathBoost(strategyResults, limit, pathBoostTerms);
|
|
2756
2928
|
const outputResults = fused.map(toOutputResult);
|
|
@@ -2770,7 +2942,7 @@ async function runQuery(projectPath, query, options) {
|
|
|
2770
2942
|
db.close();
|
|
2771
2943
|
}
|
|
2772
2944
|
}
|
|
2773
|
-
async function runStrategies(db, query, options) {
|
|
2945
|
+
async function runStrategies(db, projectPath, query, options) {
|
|
2774
2946
|
const results = [];
|
|
2775
2947
|
const filters = options.language ? { language: options.language } : void 0;
|
|
2776
2948
|
const limit = options.limit * 3;
|
|
@@ -2779,6 +2951,7 @@ async function runStrategies(db, query, options) {
|
|
|
2779
2951
|
const weight = effectiveWeights[strategy];
|
|
2780
2952
|
const searchResults = await executeStrategy(
|
|
2781
2953
|
db,
|
|
2954
|
+
projectPath,
|
|
2782
2955
|
strategy,
|
|
2783
2956
|
query,
|
|
2784
2957
|
limit,
|
|
@@ -2790,10 +2963,10 @@ async function runStrategies(db, query, options) {
|
|
|
2790
2963
|
}
|
|
2791
2964
|
return results;
|
|
2792
2965
|
}
|
|
2793
|
-
async function executeStrategy(db, strategy, query, limit, filters) {
|
|
2966
|
+
async function executeStrategy(db, projectPath, strategy, query, limit, filters) {
|
|
2794
2967
|
switch (strategy) {
|
|
2795
2968
|
case "vector": {
|
|
2796
|
-
const embedder = await loadEmbedder();
|
|
2969
|
+
const embedder = await loadEmbedder(projectPath);
|
|
2797
2970
|
return vectorSearch(db, embedder, query, limit, filters);
|
|
2798
2971
|
}
|
|
2799
2972
|
case "fts":
|
|
@@ -2826,16 +2999,23 @@ async function executeStrategy(db, strategy, query, limit, filters) {
|
|
|
2826
2999
|
}
|
|
2827
3000
|
}
|
|
2828
3001
|
var embedderInstance = null;
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
3002
|
+
var embedderKey = null;
|
|
3003
|
+
function getCacheKey(projectPath) {
|
|
3004
|
+
const config = getProjectEmbedderConfig(projectPath);
|
|
3005
|
+
return `${projectPath}:${config.provider}:${config.model}:${config.dimensions}`;
|
|
3006
|
+
}
|
|
3007
|
+
async function loadEmbedder(projectPath) {
|
|
3008
|
+
const cacheKey = getCacheKey(projectPath);
|
|
3009
|
+
if (embedderInstance && embedderKey === cacheKey) return embedderInstance;
|
|
3010
|
+
embedderInstance = await createProjectEmbedder(projectPath);
|
|
3011
|
+
embedderKey = cacheKey;
|
|
2832
3012
|
return embedderInstance;
|
|
2833
3013
|
}
|
|
2834
3014
|
|
|
2835
3015
|
// src/cli/commands/ask.ts
|
|
2836
3016
|
import fs8 from "fs";
|
|
2837
3017
|
import path7 from "path";
|
|
2838
|
-
var
|
|
3018
|
+
var CTX_DIR4 = ".ctx";
|
|
2839
3019
|
var DB_FILENAME3 = "index.db";
|
|
2840
3020
|
var SNIPPET_MAX_LENGTH2 = 200;
|
|
2841
3021
|
var FALLBACK_NOTICE = "No LLM provider configured. Set CTX_GEMINI_KEY, CTX_OPENAI_KEY, or CTX_ANTHROPIC_KEY. Running basic search instead.";
|
|
@@ -2895,13 +3075,13 @@ function formatTextOutput2(output) {
|
|
|
2895
3075
|
);
|
|
2896
3076
|
return lines.join("\n");
|
|
2897
3077
|
}
|
|
2898
|
-
function createSearchExecutor(db, query) {
|
|
3078
|
+
function createSearchExecutor(db, projectPath, query) {
|
|
2899
3079
|
const pathBoostTerms = extractPathBoostTerms(query);
|
|
2900
3080
|
return async (strategies, limit) => {
|
|
2901
3081
|
const strategyResults = [];
|
|
2902
3082
|
const fetchLimit = limit * 3;
|
|
2903
3083
|
for (const plan of strategies) {
|
|
2904
|
-
const results = await executeStrategy2(db, plan, fetchLimit);
|
|
3084
|
+
const results = await executeStrategy2(db, projectPath, plan, fetchLimit);
|
|
2905
3085
|
if (results.length > 0) {
|
|
2906
3086
|
strategyResults.push({
|
|
2907
3087
|
strategy: plan.strategy,
|
|
@@ -2920,10 +3100,10 @@ function extractSymbolNames2(query) {
|
|
|
2920
3100
|
function isPathLike2(query) {
|
|
2921
3101
|
return query.includes("/") || query.includes("*") || query.includes(".");
|
|
2922
3102
|
}
|
|
2923
|
-
async function executeStrategy2(db, plan, limit) {
|
|
3103
|
+
async function executeStrategy2(db, projectPath, plan, limit) {
|
|
2924
3104
|
switch (plan.strategy) {
|
|
2925
3105
|
case "vector": {
|
|
2926
|
-
const embedder = await loadEmbedder2();
|
|
3106
|
+
const embedder = await loadEmbedder2(projectPath);
|
|
2927
3107
|
return vectorSearch(db, embedder, plan.query, limit);
|
|
2928
3108
|
}
|
|
2929
3109
|
case "fts":
|
|
@@ -2952,13 +3132,20 @@ async function executeStrategy2(db, plan, limit) {
|
|
|
2952
3132
|
}
|
|
2953
3133
|
}
|
|
2954
3134
|
var embedderInstance2 = null;
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
3135
|
+
var embedderKey2 = null;
|
|
3136
|
+
function getCacheKey2(projectPath) {
|
|
3137
|
+
const config = getProjectEmbedderConfig(projectPath);
|
|
3138
|
+
return `${projectPath}:${config.provider}:${config.model}:${config.dimensions}`;
|
|
3139
|
+
}
|
|
3140
|
+
async function loadEmbedder2(projectPath) {
|
|
3141
|
+
const cacheKey = getCacheKey2(projectPath);
|
|
3142
|
+
if (embedderInstance2 && embedderKey2 === cacheKey) return embedderInstance2;
|
|
3143
|
+
embedderInstance2 = await createProjectEmbedder(projectPath);
|
|
3144
|
+
embedderKey2 = cacheKey;
|
|
2958
3145
|
return embedderInstance2;
|
|
2959
3146
|
}
|
|
2960
|
-
async function fallbackSearch(db, query, limit) {
|
|
2961
|
-
const executor = createSearchExecutor(db, query);
|
|
3147
|
+
async function fallbackSearch(db, projectPath, query, limit) {
|
|
3148
|
+
const executor = createSearchExecutor(db, projectPath, query);
|
|
2962
3149
|
const fallbackStrategies = buildFallbackStrategies(query);
|
|
2963
3150
|
const results = await executor(fallbackStrategies, limit);
|
|
2964
3151
|
return {
|
|
@@ -2978,25 +3165,26 @@ async function fallbackSearch(db, query, limit) {
|
|
|
2978
3165
|
async function runAsk(projectPath, query, options) {
|
|
2979
3166
|
const limit = normalizeLimit2(options.limit);
|
|
2980
3167
|
const absoluteRoot = path7.resolve(projectPath);
|
|
2981
|
-
const dbPath = path7.join(absoluteRoot,
|
|
3168
|
+
const dbPath = path7.join(absoluteRoot, CTX_DIR4, DB_FILENAME3);
|
|
2982
3169
|
if (!fs8.existsSync(dbPath)) {
|
|
2983
3170
|
throw new KontextError(
|
|
2984
|
-
`Project not initialized. Run "ctx init" first. (${
|
|
3171
|
+
`Project not initialized. Run "ctx init" first. (${CTX_DIR4}/${DB_FILENAME3} not found)`,
|
|
2985
3172
|
ErrorCode.NOT_INITIALIZED
|
|
2986
3173
|
);
|
|
2987
3174
|
}
|
|
2988
|
-
const
|
|
3175
|
+
const embedderConfig = getProjectEmbedderConfig(absoluteRoot);
|
|
3176
|
+
const db = createDatabase(dbPath, embedderConfig.dimensions);
|
|
2989
3177
|
try {
|
|
2990
3178
|
const provider = options.provider ?? null;
|
|
2991
3179
|
if (!provider) {
|
|
2992
|
-
const output = await fallbackSearch(db, query, limit);
|
|
3180
|
+
const output = await fallbackSearch(db, absoluteRoot, query, limit);
|
|
2993
3181
|
output.warning = FALLBACK_NOTICE;
|
|
2994
3182
|
if (options.format === "text") {
|
|
2995
3183
|
output.text = formatTextOutput2(output);
|
|
2996
3184
|
}
|
|
2997
3185
|
return output;
|
|
2998
3186
|
}
|
|
2999
|
-
const executor = createSearchExecutor(db, query);
|
|
3187
|
+
const executor = createSearchExecutor(db, absoluteRoot, query);
|
|
3000
3188
|
if (options.noExplain) {
|
|
3001
3189
|
return await runNoExplain(provider, query, limit, options, executor);
|
|
3002
3190
|
}
|
|
@@ -3048,9 +3236,9 @@ async function runWithSteering(provider, query, limit, options, executor) {
|
|
|
3048
3236
|
// src/cli/commands/status.ts
|
|
3049
3237
|
import fs9 from "fs";
|
|
3050
3238
|
import path8 from "path";
|
|
3051
|
-
var
|
|
3239
|
+
var CTX_DIR5 = ".ctx";
|
|
3052
3240
|
var DB_FILENAME4 = "index.db";
|
|
3053
|
-
var
|
|
3241
|
+
var CONFIG_FILENAME3 = "config.json";
|
|
3054
3242
|
function formatBytes2(bytes) {
|
|
3055
3243
|
if (bytes < 1024) return `${bytes} B`;
|
|
3056
3244
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -3065,14 +3253,15 @@ function formatTimestamp(raw) {
|
|
|
3065
3253
|
function capitalize(s) {
|
|
3066
3254
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
3067
3255
|
}
|
|
3068
|
-
function
|
|
3069
|
-
const
|
|
3070
|
-
if (!fs9.existsSync(
|
|
3256
|
+
function readConfig2(ctxDir) {
|
|
3257
|
+
const configPath2 = path8.join(ctxDir, CONFIG_FILENAME3);
|
|
3258
|
+
if (!fs9.existsSync(configPath2)) return null;
|
|
3071
3259
|
try {
|
|
3072
|
-
const raw = fs9.readFileSync(
|
|
3260
|
+
const raw = fs9.readFileSync(configPath2, "utf-8");
|
|
3073
3261
|
const parsed = JSON.parse(raw);
|
|
3074
3262
|
const embedder = parsed.embedder;
|
|
3075
3263
|
return {
|
|
3264
|
+
provider: embedder?.provider ?? parsed.provider ?? "unknown",
|
|
3076
3265
|
model: embedder?.model ?? parsed.model ?? "unknown",
|
|
3077
3266
|
dimensions: embedder?.dimensions ?? parsed.dimensions ?? 0
|
|
3078
3267
|
};
|
|
@@ -3093,7 +3282,7 @@ function formatStatus(projectPath, output) {
|
|
|
3093
3282
|
`Kontext Status \u2014 ${projectPath}`,
|
|
3094
3283
|
"",
|
|
3095
3284
|
` Initialized: Yes`,
|
|
3096
|
-
` Database: ${
|
|
3285
|
+
` Database: ${CTX_DIR5}/${DB_FILENAME4} (${formatBytes2(output.dbSizeBytes)})`
|
|
3097
3286
|
];
|
|
3098
3287
|
if (output.lastIndexed) {
|
|
3099
3288
|
lines.push(` Last indexed: ${formatTimestamp(output.lastIndexed)}`);
|
|
@@ -3116,7 +3305,7 @@ function formatStatus(projectPath, output) {
|
|
|
3116
3305
|
if (output.config) {
|
|
3117
3306
|
lines.push("");
|
|
3118
3307
|
lines.push(
|
|
3119
|
-
` Embedder:
|
|
3308
|
+
` Embedder: ${output.config.provider} (${output.config.model}, ${output.config.dimensions} dims)`
|
|
3120
3309
|
);
|
|
3121
3310
|
}
|
|
3122
3311
|
lines.push("");
|
|
@@ -3124,7 +3313,7 @@ function formatStatus(projectPath, output) {
|
|
|
3124
3313
|
}
|
|
3125
3314
|
async function runStatus(projectPath) {
|
|
3126
3315
|
const absoluteRoot = path8.resolve(projectPath);
|
|
3127
|
-
const ctxDir = path8.join(absoluteRoot,
|
|
3316
|
+
const ctxDir = path8.join(absoluteRoot, CTX_DIR5);
|
|
3128
3317
|
const dbPath = path8.join(ctxDir, DB_FILENAME4);
|
|
3129
3318
|
if (!fs9.existsSync(dbPath)) {
|
|
3130
3319
|
const output = {
|
|
@@ -3147,7 +3336,7 @@ async function runStatus(projectPath) {
|
|
|
3147
3336
|
const vectorCount = db.getVectorCount();
|
|
3148
3337
|
const languages = db.getLanguageBreakdown();
|
|
3149
3338
|
const lastIndexed = db.getLastIndexed();
|
|
3150
|
-
const config =
|
|
3339
|
+
const config = readConfig2(ctxDir);
|
|
3151
3340
|
const dbSizeBytes = fs9.statSync(dbPath).size;
|
|
3152
3341
|
const output = {
|
|
3153
3342
|
initialized: true,
|