@@ -192,11 +192,16 @@ function printProgress(progress: IndexProgress): void {
192192 };
193193
194194 const phaseName = phaseNames[progress.phase] || progress.phase;
195- const bar = progressBar(progress.current, progress.total);
196195 const file = progress.currentFile ? chalk.dim(` $ { progress . currentFile } `) : '';
197196
198- // Clear line and print progress
199- process . stdout . write ( `\r${ chalk . cyan ( phaseName ) } : ${ bar } ${ file } ` . padEnd ( 100 ) ) ;
197+ if (progress.total > 0) {
198+ const bar = progressBar(progress.current, progress.total);
199+ process.stdout.write(` \r${chalk . cyan ( phaseName ) } : ${bar } ${file } `.padEnd(100));
200+ } else {
201+ // No known total (e.g. scanning) — show a running count
202+ const count = progress.current > 0 ? ` $ { chalk . green ( formatNumber ( progress . current ) ) } found ` : '';
203+ process.stdout.write(` \r${chalk . cyan ( phaseName ) } :${count } ${file } `.padEnd(100));
204+ }
200205}
201206
202207/**
@@ -227,6 +232,121 @@ function warn(message: string): void {
227232 console.log(chalk.yellow('⚠') + ' ' + message);
228233}
229234
235+ /**
236+ * Print a summary of indexing results with clear error breakdown
237+ */
238+ function printIndexResult(result: { success: boolean; filesIndexed: number; filesSkipped: number; filesErrored: number; nodesCreated: number; edgesCreated: number; errors: Array<{ message: string; filePath?: string; severity: string; code?: string }>; durationMs: number }, projectPath?: string): void {
239+ const hasErrors = result.filesErrored > 0;
240+
241+ // Always show what was indexed
242+ if (result.filesIndexed > 0) {
243+ if (hasErrors) {
244+ success(` Indexed ${formatNumber ( result . filesIndexed ) } files ( $ { formatNumber ( result . filesErrored ) } could not be parsed ) `);
245+ } else {
246+ success(` Indexed ${formatNumber ( result . filesIndexed ) } files `);
247+ }
248+ info(` Created ${formatNumber ( result . nodesCreated ) } nodes and ${formatNumber ( result . edgesCreated ) } edges `);
249+ info(` Completed in $ { formatDuration ( result . durationMs ) } `);
250+ } else if (hasErrors) {
251+ error(` Indexing failed — all ${formatNumber ( result . filesErrored ) } files had errors `);
252+ } else {
253+ warn('No files found to index');
254+ }
255+
256+ // Show error breakdown if there were errors
257+ if (hasErrors) {
258+ // Group errors by code for a concise summary
259+ const errorsByCode = new Map<string, number>();
260+ for (const err of result.errors) {
261+ if (err.severity === 'error') {
262+ const code = err.code || 'unknown';
263+ errorsByCode.set(code, (errorsByCode.get(code) || 0) + 1);
264+ }
265+ }
266+
267+ const codeLabels: Record<string, string> = {
268+ parse_error: 'files failed to parse',
269+ read_error: 'files could not be read',
270+ size_exceeded: 'files exceeded size limit',
271+ path_traversal: 'blocked paths',
272+ unsupported_language: 'unsupported language',
273+ parser_error: 'parser initialization failures',
274+ };
275+
276+ console.log('');
277+ console.log(chalk.dim(' Error breakdown:'));
278+ for (const [code, count] of errorsByCode) {
279+ const label = codeLabels[code] || code;
280+ console.log(chalk.dim(` $ { formatNumber ( count ) } ${label } `));
281+ }
282+
283+ // Write detailed error log to .codegraph/errors.log
284+ if (projectPath) {
285+ writeErrorLog(projectPath, result.errors);
286+ }
287+
288+ // Reassure the user the index is usable
289+ if (result.filesIndexed > 0) {
290+ console.log('');
291+ info('The index is fully usable — only the failed files are missing from the graph.');
292+ info('This is common in large repos with test fixtures or generated files that use non-standard syntax.');
293+ }
294+ } else if (projectPath) {
295+ // No errors — clean up any stale error log
296+ const logPath = path.join(projectPath, '.codegraph', 'errors.log');
297+ if (fs.existsSync(logPath)) {
298+ fs.unlinkSync(logPath);
299+ }
300+ }
301+ }
302+
303+ /**
304+ * Write detailed error log to .codegraph/errors.log
305+ */
306+ function writeErrorLog(projectPath: string, errors: Array<{ message: string; filePath?: string; severity: string; code?: string }>): void {
307+ const cgDir = path.join(projectPath, '.codegraph');
308+ if (!fs.existsSync(cgDir)) return;
309+
310+ const logPath = path.join(cgDir, 'errors.log');
311+
312+ // Group errors by file path
313+ const errorsByFile = new Map<string, Array<{ message: string; code?: string }>>();
314+ const noFileErrors: Array<{ message: string; code?: string }> = [];
315+
316+ for (const err of errors) {
317+ if (err.severity !== 'error') continue;
318+ if (err.filePath) {
319+ let list = errorsByFile.get(err.filePath);
320+ if (!list) {
321+ list = [];
322+ errorsByFile.set(err.filePath, list);
323+ }
324+ list.push({ message: err.message, code: err.code });
325+ } else {
326+ noFileErrors.push({ message: err.message, code: err.code });
327+ }
328+ }
329+
330+ const lines: string[] = [
331+ ` CodeGraph Error Log — ${new Date ( ) . toISOString ( ) } `,
332+ ` $ { errorsByFile . size } files with errors `,
333+ '',
334+ ];
335+
336+ for (const [filePath, fileErrors] of errorsByFile) {
337+ for (const err of fileErrors) {
338+ lines.push(` $ { filePath } : ${err . message } `);
339+ }
340+ }
341+
342+ for (const err of noFileErrors) {
343+ lines.push(err.message);
344+ }
345+
346+ fs.writeFileSync(logPath, lines.join('\n') + '\n');
347+ info(` See . codegraph / errors . log for the full list of failed files `);
348+ }
349+
230350// =============================================================================
231351// Commands
232352// =============================================================================
@@ -239,7 +359,8 @@ program
239359 .description('Initialize CodeGraph in a project directory')
240360 .option('-i, --index', 'Run initial indexing after initialization')
241361 .action(async (pathArg: string | undefined, options: { index?: boolean }) => {
242- const projectPath = resolveProjectPath ( pathArg ) ;
362+ // init should always target the exact path given (or cwd), never walk up parents
363+ const projectPath = path.resolve(pathArg || process.cwd());
243364
244365 console.log(chalk.bold('\nInitializing CodeGraph...\n'));
245366
@@ -271,13 +392,7 @@ program
271392 // Clear progress line
272393 process.stdout.write('\r' + ' '.repeat(100) + '\r');
273394
274- if ( result . success ) {
275- success ( `Indexed ${ formatNumber ( result . filesIndexed ) } files` ) ;
276- info ( `Created ${ formatNumber ( result . nodesCreated ) } nodes and ${ formatNumber ( result . edgesCreated ) } edges` ) ;
277- info ( `Completed in ${ formatDuration ( result . durationMs ) } ` ) ;
278- } else {
279- warn ( `Indexing completed with ${ result . errors . length } errors` ) ;
280- }
395+ printIndexResult(result, projectPath);
281396 } else {
282397 info('Run "codegraph index" to index the project');
283398 }
@@ -376,22 +491,11 @@ program
376491 process.stdout.write('\r' + ' '.repeat(100) + '\r');
377492 }
378493
379- if ( result . success ) {
380- if ( ! options . quiet ) {
381- success ( `Indexed ${ formatNumber ( result . filesIndexed ) } files` ) ;
382- info ( `Created ${ formatNumber ( result . nodesCreated ) } nodes and ${ formatNumber ( result . edgesCreated ) } edges` ) ;
383- info ( `Completed in ${ formatDuration ( result . durationMs ) } ` ) ;
384- }
385- } else {
386- if ( ! options . quiet ) {
387- warn ( `Indexing completed with ${ result . errors . length } errors` ) ;
388- for ( const err of result . errors . slice ( 0 , 5 ) ) {
389- console . log ( chalk . dim ( ` - ${ err . message } ` ) ) ;
390- }
391- if ( result . errors . length > 5 ) {
392- console . log ( chalk . dim ( ` ... and ${ result . errors . length - 5 } more` ) ) ;
393- }
394- }
494+ if (!options.quiet) {
495+ printIndexResult(result, projectPath);
496+ }
497+
498+ if (!result.success) {
395499 process.exit(1);
396500 }
397501
0 commit comments