* Print indexing results using clack log methods
(clack: typeof import('@clack/prompts'), result: IndexResult, projectPath?: string)
| 371 | * Print indexing results using clack log methods |
| 372 | */ |
| 373 | function printIndexResult(clack: typeof import('@clack/prompts'), result: IndexResult, projectPath?: string): void { |
| 374 | const hasErrors = result.filesErrored > 0; |
| 375 | |
| 376 | // Surface non-file-level failures (e.g. lock-acquisition failure |
| 377 | // when another indexer is running) before the file-count branches. |
| 378 | // Without this the CLI falls through to "No files found to index", |
| 379 | // which is actively misleading — the index DID run, it just couldn't |
| 380 | // get the lock. |
| 381 | // |
| 382 | // If success is false but no severity:'error' entry exists in |
| 383 | // `result.errors` (degenerate case — shouldn't happen in practice |
| 384 | // but worth guarding because the result shape is plumbed through |
| 385 | // multiple call sites), fall back to a generic message rather than |
| 386 | // continuing to the misleading "No files found" branch or throwing. |
| 387 | if (!result.success && !hasErrors && result.filesIndexed === 0) { |
| 388 | const generic = result.errors.find((e) => e.severity === 'error'); |
| 389 | clack.log.error(generic?.message ?? `Indexing failed ${getGlyphs().dash} no further details available`); |
| 390 | return; |
| 391 | } |
| 392 | |
| 393 | if (result.filesIndexed > 0) { |
| 394 | if (hasErrors) { |
| 395 | clack.log.success(`Indexed ${formatNumber(result.filesIndexed)} files (${formatNumber(result.filesErrored)} could not be parsed)`); |
| 396 | } else { |
| 397 | clack.log.success(`Indexed ${formatNumber(result.filesIndexed)} files`); |
| 398 | } |
| 399 | clack.log.info(`${formatNumber(result.nodesCreated)} nodes, ${formatNumber(result.edgesCreated)} edges in ${formatDuration(result.durationMs)}`); |
| 400 | // A PARTIAL index (files silently dropped mid-pipeline) must not pass |
| 401 | // as a clean run — it's the difference between "indexed the repo" and |
| 402 | // "indexed most of the repo, quietly". Only the completeness |
| 403 | // reconciliation warning; per-file extractor warnings stay in the |
| 404 | // error-code summary below. |
| 405 | for (const w of result.errors.filter((e) => e.code === 'index_partial')) { |
| 406 | clack.log.warn(w.message); |
| 407 | } |
| 408 | } else if (hasErrors) { |
| 409 | clack.log.error(`Indexing failed ${getGlyphs().dash} all ${formatNumber(result.filesErrored)} files had errors`); |
| 410 | } else { |
| 411 | clack.log.warn('No files found to index'); |
| 412 | } |
| 413 | |
| 414 | if (hasErrors) { |
| 415 | const errorsByCode = new Map<string, number>(); |
| 416 | for (const err of result.errors) { |
| 417 | if (err.severity === 'error') { |
| 418 | const code = err.code || 'unknown'; |
| 419 | errorsByCode.set(code, (errorsByCode.get(code) || 0) + 1); |
| 420 | } |
| 421 | } |
| 422 | |
| 423 | const codeLabels: Record<string, string> = { |
| 424 | parse_error: 'files failed to parse', |
| 425 | read_error: 'files could not be read', |
| 426 | size_exceeded: 'files exceeded size limit', |
| 427 | path_traversal: 'blocked paths', |
| 428 | unsupported_language: 'unsupported language', |
| 429 | parser_error: 'parser initialization failures', |
| 430 | }; |
no test coverage detected