* Print indexing results using clack log methods
(clack: typeof import('@clack/prompts'), result: IndexResult, projectPath?: string)
| 327 | * Print indexing results using clack log methods |
| 328 | */ |
| 329 | function printIndexResult(clack: typeof import('@clack/prompts'), result: IndexResult, projectPath?: string): void { |
| 330 | const hasErrors = result.filesErrored > 0; |
| 331 | |
| 332 | // Surface non-file-level failures (e.g. lock-acquisition failure |
| 333 | // when another indexer is running) before the file-count branches. |
| 334 | // Without this the CLI falls through to "No files found to index", |
| 335 | // which is actively misleading — the index DID run, it just couldn't |
| 336 | // get the lock. |
| 337 | // |
| 338 | // If success is false but no severity:'error' entry exists in |
| 339 | // `result.errors` (degenerate case — shouldn't happen in practice |
| 340 | // but worth guarding because the result shape is plumbed through |
| 341 | // multiple call sites), fall back to a generic message rather than |
| 342 | // continuing to the misleading "No files found" branch or throwing. |
| 343 | if (!result.success && !hasErrors && result.filesIndexed === 0) { |
| 344 | const generic = result.errors.find((e) => e.severity === 'error'); |
| 345 | clack.log.error(generic?.message ?? `Indexing failed ${getGlyphs().dash} no further details available`); |
| 346 | return; |
| 347 | } |
| 348 | |
| 349 | if (result.filesIndexed > 0) { |
| 350 | if (hasErrors) { |
| 351 | clack.log.success(`Indexed ${formatNumber(result.filesIndexed)} files (${formatNumber(result.filesErrored)} could not be parsed)`); |
| 352 | } else { |
| 353 | clack.log.success(`Indexed ${formatNumber(result.filesIndexed)} files`); |
| 354 | } |
| 355 | clack.log.info(`${formatNumber(result.nodesCreated)} nodes, ${formatNumber(result.edgesCreated)} edges in ${formatDuration(result.durationMs)}`); |
| 356 | } else if (hasErrors) { |
| 357 | clack.log.error(`Indexing failed ${getGlyphs().dash} all ${formatNumber(result.filesErrored)} files had errors`); |
| 358 | } else { |
| 359 | clack.log.warn('No files found to index'); |
| 360 | } |
| 361 | |
| 362 | if (hasErrors) { |
| 363 | const errorsByCode = new Map<string, number>(); |
| 364 | for (const err of result.errors) { |
| 365 | if (err.severity === 'error') { |
| 366 | const code = err.code || 'unknown'; |
| 367 | errorsByCode.set(code, (errorsByCode.get(code) || 0) + 1); |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | const codeLabels: Record<string, string> = { |
| 372 | parse_error: 'files failed to parse', |
| 373 | read_error: 'files could not be read', |
| 374 | size_exceeded: 'files exceeded size limit', |
| 375 | path_traversal: 'blocked paths', |
| 376 | unsupported_language: 'unsupported language', |
| 377 | parser_error: 'parser initialization failures', |
| 378 | }; |
| 379 | |
| 380 | const breakdown = Array.from(errorsByCode) |
| 381 | .map(([code, count]) => `${formatNumber(count)} ${codeLabels[code] || code}`) |
| 382 | .join('\n'); |
| 383 | clack.note(breakdown, 'Error breakdown'); |
| 384 | |
| 385 | if (projectPath) { |
| 386 | writeErrorLog(projectPath, result.errors); |
no test coverage detected