( project: ProjectState, incrementalOnly?: boolean )
| 1235 | } |
| 1236 | |
| 1237 | async function performIndexingOnce( |
| 1238 | project: ProjectState, |
| 1239 | incrementalOnly?: boolean |
| 1240 | ): Promise<void> { |
| 1241 | project.indexState.status = 'indexing'; |
| 1242 | const mode = incrementalOnly ? 'incremental' : 'full'; |
| 1243 | console.error(`Indexing (${mode}): ${project.rootPath}`); |
| 1244 | |
| 1245 | try { |
| 1246 | let lastLoggedProgress = { phase: '', percentage: -1 }; |
| 1247 | const indexer = new CodebaseIndexer({ |
| 1248 | rootPath: project.rootPath, |
| 1249 | ...(project.runtimeOverrides.extraExcludePatterns?.length |
| 1250 | ? { |
| 1251 | config: { |
| 1252 | exclude: [...EXCLUDED_GLOB_PATTERNS, ...project.runtimeOverrides.extraExcludePatterns] |
| 1253 | } |
| 1254 | } |
| 1255 | : {}), |
| 1256 | ...(project.runtimeOverrides.preferredAnalyzer || |
| 1257 | project.runtimeOverrides.extraSourceExtensions?.length |
| 1258 | ? { |
| 1259 | projectOptions: { |
| 1260 | preferredAnalyzer: project.runtimeOverrides.preferredAnalyzer, |
| 1261 | extraFileExtensions: project.runtimeOverrides.extraSourceExtensions |
| 1262 | } |
| 1263 | } |
| 1264 | : {}), |
| 1265 | incrementalOnly, |
| 1266 | onProgress: (progress) => { |
| 1267 | // Only log when phase or percentage actually changes (prevents duplicate logs) |
| 1268 | const shouldLog = |
| 1269 | progress.phase !== lastLoggedProgress.phase || |
| 1270 | (progress.percentage % 10 === 0 && progress.percentage !== lastLoggedProgress.percentage); |
| 1271 | |
| 1272 | if (shouldLog) { |
| 1273 | console.error(`[${progress.phase}] ${progress.percentage}%`); |
| 1274 | lastLoggedProgress = { phase: progress.phase, percentage: progress.percentage }; |
| 1275 | } |
| 1276 | } |
| 1277 | }); |
| 1278 | |
| 1279 | project.indexState.indexer = indexer; |
| 1280 | const stats = await indexer.index(); |
| 1281 | |
| 1282 | project.indexState.status = 'ready'; |
| 1283 | project.indexState.lastIndexed = new Date(); |
| 1284 | project.indexState.stats = stats; |
| 1285 | |
| 1286 | console.error( |
| 1287 | `Complete: ${stats.indexedFiles} files, ${stats.totalChunks} chunks in ${( |
| 1288 | stats.duration / 1000 |
| 1289 | ).toFixed(2)}s` |
| 1290 | ); |
| 1291 | |
| 1292 | // Auto-extract memories from git history (non-blocking, best-effort) |
| 1293 | try { |
| 1294 | const gitMemories = await extractGitMemories(project.rootPath, project.paths.memory); |
no test coverage detected