kotadb 2.0.1-next.20260203174555 → 2.0.1-next.20260203184744
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/package.json +2 -1
- package/src/api/queries.ts +220 -0
- package/src/indexer/incremental.ts +554 -0
- package/src/mcp/auto-index.ts +237 -0
- package/src/mcp/tools.ts +133 -2
- package/src/sync/index.ts +11 -0
- package/src/sync/source-watcher.ts +403 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kotadb",
|
|
3
|
-
"version": "2.0.1-next.
|
|
3
|
+
"version": "2.0.1-next.20260203184744",
|
|
4
4
|
"description": "Local-only code intelligence tool for CLI agents. SQLite-backed repository indexing and code search via MCP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "src/index.ts",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"@typescript-eslint/parser": "^8.0.0",
|
|
58
58
|
"@typescript-eslint/types": "^8.54.0",
|
|
59
59
|
"bcryptjs": "^2.4.3",
|
|
60
|
+
"chokidar": "^5.0.0",
|
|
60
61
|
"cors": "^2.8.6",
|
|
61
62
|
"express": "^4.18.0",
|
|
62
63
|
"zod": "^4.3.6"
|
package/src/api/queries.ts
CHANGED
|
@@ -1066,6 +1066,180 @@ export async function runIndexingWorkflow(
|
|
|
1066
1066
|
};
|
|
1067
1067
|
}
|
|
1068
1068
|
|
|
1069
|
+
// ============================================================================
|
|
1070
|
+
// Repository Indexing Status & File Deletion Operations
|
|
1071
|
+
// ============================================================================
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* Check if a repository has been indexed (has files in indexed_files table).
|
|
1075
|
+
*
|
|
1076
|
+
* @param repositoryId - Repository UUID or full_name
|
|
1077
|
+
* @returns true if the repository has indexed files, false otherwise
|
|
1078
|
+
*/
|
|
1079
|
+
function isRepositoryIndexedInternal(
|
|
1080
|
+
db: KotaDatabase,
|
|
1081
|
+
repositoryId: string,
|
|
1082
|
+
): boolean {
|
|
1083
|
+
// First try to match by ID, then by full_name
|
|
1084
|
+
const result = db.queryOne<{ count: number }>(
|
|
1085
|
+
`SELECT COUNT(*) as count FROM indexed_files
|
|
1086
|
+
WHERE repository_id = ?
|
|
1087
|
+
OR repository_id IN (SELECT id FROM repositories WHERE full_name = ?)`,
|
|
1088
|
+
[repositoryId, repositoryId]
|
|
1089
|
+
);
|
|
1090
|
+
return (result?.count ?? 0) > 0;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* Check if a repository has been indexed.
|
|
1095
|
+
*
|
|
1096
|
+
* @param repositoryId - Repository UUID or full_name
|
|
1097
|
+
* @returns true if the repository has indexed files, false otherwise
|
|
1098
|
+
*/
|
|
1099
|
+
export function isRepositoryIndexed(repositoryId: string): boolean {
|
|
1100
|
+
return isRepositoryIndexedInternal(getGlobalDatabase(), repositoryId);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* Delete a single file from the index by path.
|
|
1105
|
+
* Cascading deletes will remove associated symbols and references.
|
|
1106
|
+
*
|
|
1107
|
+
* @param repositoryId - Repository UUID
|
|
1108
|
+
* @param filePath - Relative file path to delete
|
|
1109
|
+
* @returns true if file was deleted, false if not found
|
|
1110
|
+
*/
|
|
1111
|
+
function deleteFileByPathInternal(
|
|
1112
|
+
db: KotaDatabase,
|
|
1113
|
+
repositoryId: string,
|
|
1114
|
+
filePath: string,
|
|
1115
|
+
): boolean {
|
|
1116
|
+
const normalizedPath = normalizePath(filePath);
|
|
1117
|
+
|
|
1118
|
+
// The indexed_files table has ON DELETE CASCADE for:
|
|
1119
|
+
// - indexed_symbols (via file_id FK)
|
|
1120
|
+
// - indexed_references (via file_id FK)
|
|
1121
|
+
// FTS5 triggers handle indexed_files_fts cleanup automatically
|
|
1122
|
+
|
|
1123
|
+
const result = db.queryOne<{ id: string }>(
|
|
1124
|
+
`SELECT id FROM indexed_files WHERE repository_id = ? AND path = ?`,
|
|
1125
|
+
[repositoryId, normalizedPath]
|
|
1126
|
+
);
|
|
1127
|
+
|
|
1128
|
+
if (!result) {
|
|
1129
|
+
logger.debug("File not found for deletion", { repositoryId, filePath: normalizedPath });
|
|
1130
|
+
return false;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
db.run(
|
|
1134
|
+
`DELETE FROM indexed_files WHERE id = ?`,
|
|
1135
|
+
[result.id]
|
|
1136
|
+
);
|
|
1137
|
+
|
|
1138
|
+
logger.info("Deleted file from index", { repositoryId, filePath: normalizedPath, fileId: result.id });
|
|
1139
|
+
return true;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* Delete a single file from the index by path.
|
|
1144
|
+
*
|
|
1145
|
+
* @param repositoryId - Repository UUID
|
|
1146
|
+
* @param filePath - Relative file path to delete
|
|
1147
|
+
* @returns true if file was deleted, false if not found
|
|
1148
|
+
*/
|
|
1149
|
+
export function deleteFileByPath(
|
|
1150
|
+
repositoryId: string,
|
|
1151
|
+
filePath: string,
|
|
1152
|
+
): boolean {
|
|
1153
|
+
return deleteFileByPathInternal(getGlobalDatabase(), repositoryId, filePath);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* Delete multiple files from the index by paths.
|
|
1158
|
+
* Uses a transaction for atomic operation.
|
|
1159
|
+
* Cascading deletes will remove associated symbols and references.
|
|
1160
|
+
*
|
|
1161
|
+
* @param repositoryId - Repository UUID
|
|
1162
|
+
* @param filePaths - Array of relative file paths to delete
|
|
1163
|
+
* @returns Object with deleted count and list of deleted paths
|
|
1164
|
+
*/
|
|
1165
|
+
function deleteFilesByPathsInternal(
|
|
1166
|
+
db: KotaDatabase,
|
|
1167
|
+
repositoryId: string,
|
|
1168
|
+
filePaths: string[],
|
|
1169
|
+
): { deletedCount: number; deletedPaths: string[] } {
|
|
1170
|
+
if (filePaths.length === 0) {
|
|
1171
|
+
return { deletedCount: 0, deletedPaths: [] };
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
const normalizedPaths = filePaths.map(normalizePath);
|
|
1175
|
+
const deletedPaths: string[] = [];
|
|
1176
|
+
|
|
1177
|
+
db.transaction(() => {
|
|
1178
|
+
for (const normalizedPath of normalizedPaths) {
|
|
1179
|
+
const result = db.queryOne<{ id: string }>(
|
|
1180
|
+
`SELECT id FROM indexed_files WHERE repository_id = ? AND path = ?`,
|
|
1181
|
+
[repositoryId, normalizedPath]
|
|
1182
|
+
);
|
|
1183
|
+
|
|
1184
|
+
if (result) {
|
|
1185
|
+
db.run(`DELETE FROM indexed_files WHERE id = ?`, [result.id]);
|
|
1186
|
+
deletedPaths.push(normalizedPath);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
logger.info("Deleted files from index", {
|
|
1192
|
+
repositoryId,
|
|
1193
|
+
requestedCount: filePaths.length,
|
|
1194
|
+
deletedCount: deletedPaths.length
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1197
|
+
return { deletedCount: deletedPaths.length, deletedPaths };
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* Delete multiple files from the index by paths.
|
|
1202
|
+
*
|
|
1203
|
+
* @param repositoryId - Repository UUID
|
|
1204
|
+
* @param filePaths - Array of relative file paths to delete
|
|
1205
|
+
* @returns Object with deleted count and list of deleted paths
|
|
1206
|
+
*/
|
|
1207
|
+
export function deleteFilesByPaths(
|
|
1208
|
+
repositoryId: string,
|
|
1209
|
+
filePaths: string[],
|
|
1210
|
+
): { deletedCount: number; deletedPaths: string[] } {
|
|
1211
|
+
return deleteFilesByPathsInternal(getGlobalDatabase(), repositoryId, filePaths);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* Get repository ID from full_name.
|
|
1216
|
+
* Useful for auto-indexing when you have the path but need the UUID.
|
|
1217
|
+
*
|
|
1218
|
+
* @param fullName - Repository full name (e.g., "owner/repo" or "local/path")
|
|
1219
|
+
* @returns Repository UUID or null if not found
|
|
1220
|
+
*/
|
|
1221
|
+
function getRepositoryIdByNameInternal(
|
|
1222
|
+
db: KotaDatabase,
|
|
1223
|
+
fullName: string,
|
|
1224
|
+
): string | null {
|
|
1225
|
+
const result = db.queryOne<{ id: string }>(
|
|
1226
|
+
`SELECT id FROM repositories WHERE full_name = ?`,
|
|
1227
|
+
[fullName]
|
|
1228
|
+
);
|
|
1229
|
+
return result?.id ?? null;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* Get repository ID from full_name.
|
|
1234
|
+
*
|
|
1235
|
+
* @param fullName - Repository full name
|
|
1236
|
+
* @returns Repository UUID or null if not found
|
|
1237
|
+
*/
|
|
1238
|
+
export function getRepositoryIdByName(fullName: string): string | null {
|
|
1239
|
+
return getRepositoryIdByNameInternal(getGlobalDatabase(), fullName);
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
|
|
1069
1243
|
// ============================================================================
|
|
1070
1244
|
// Backward-compatible aliases that accept db parameter
|
|
1071
1245
|
// These use the passed database (for tests) rather than the global one
|
|
@@ -1172,6 +1346,52 @@ export function updateRepositoryLastIndexedLocal(
|
|
|
1172
1346
|
return updateRepositoryLastIndexedInternal(db, repositoryId);
|
|
1173
1347
|
}
|
|
1174
1348
|
|
|
1349
|
+
/**
|
|
1350
|
+
* Check if a repository has been indexed.
|
|
1351
|
+
* Version that accepts db parameter for testing.
|
|
1352
|
+
*/
|
|
1353
|
+
export function isRepositoryIndexedLocal(
|
|
1354
|
+
db: KotaDatabase,
|
|
1355
|
+
repositoryId: string
|
|
1356
|
+
): boolean {
|
|
1357
|
+
return isRepositoryIndexedInternal(db, repositoryId);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
/**
|
|
1361
|
+
* Delete a single file from the index by path.
|
|
1362
|
+
* Version that accepts db parameter for testing.
|
|
1363
|
+
*/
|
|
1364
|
+
export function deleteFileByPathLocal(
|
|
1365
|
+
db: KotaDatabase,
|
|
1366
|
+
repositoryId: string,
|
|
1367
|
+
filePath: string
|
|
1368
|
+
): boolean {
|
|
1369
|
+
return deleteFileByPathInternal(db, repositoryId, filePath);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* Delete multiple files from the index by paths.
|
|
1374
|
+
* Version that accepts db parameter for testing.
|
|
1375
|
+
*/
|
|
1376
|
+
export function deleteFilesByPathsLocal(
|
|
1377
|
+
db: KotaDatabase,
|
|
1378
|
+
repositoryId: string,
|
|
1379
|
+
filePaths: string[]
|
|
1380
|
+
): { deletedCount: number; deletedPaths: string[] } {
|
|
1381
|
+
return deleteFilesByPathsInternal(db, repositoryId, filePaths);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
/**
|
|
1385
|
+
* Get repository ID from full_name.
|
|
1386
|
+
* Version that accepts db parameter for testing.
|
|
1387
|
+
*/
|
|
1388
|
+
export function getRepositoryIdByNameLocal(
|
|
1389
|
+
db: KotaDatabase,
|
|
1390
|
+
fullName: string
|
|
1391
|
+
): string | null {
|
|
1392
|
+
return getRepositoryIdByNameInternal(db, fullName);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1175
1395
|
// Add alias for runIndexingWorkflowLocal
|
|
1176
1396
|
export const runIndexingWorkflowLocal = runIndexingWorkflow;
|
|
1177
1397
|
|