@@ -194,7 +194,7 @@ export class CodebaseIndexer {
194194
195195 console . error (
196196 `Incremental diff: ${ diff . added . length } added, ${ diff . changed . length } changed, ` +
197- `${ diff . deleted . length } deleted, ${ diff . unchanged . length } unchanged`
197+ `${ diff . deleted . length } deleted, ${ diff . unchanged . length } unchanged`
198198 ) ;
199199
200200 stats . incremental = {
@@ -210,6 +210,20 @@ export class CodebaseIndexer {
210210 this . updateProgress ( 'complete' , 100 ) ;
211211 stats . duration = Date . now ( ) - startTime ;
212212 stats . completedAt = new Date ( ) ;
213+
214+ // Preserve accurate counts from the existing index (nothing changed, index is intact)
215+ try {
216+ const existingIndexPath = path . join ( contextDir , KEYWORD_INDEX_FILENAME ) ;
217+ const existingChunks = JSON . parse ( await fs . readFile ( existingIndexPath , 'utf-8' ) ) ;
218+ if ( Array . isArray ( existingChunks ) ) {
219+ stats . totalChunks = existingChunks . length ;
220+ const uniqueFiles = new Set ( existingChunks . map ( ( c : { filePath ?: string } ) => c . filePath ) ) ;
221+ stats . indexedFiles = uniqueFiles . size ;
222+ }
223+ } catch {
224+ // Keyword index doesn't exist yet — keep counts as 0
225+ }
226+
213227 return stats ;
214228 }
215229 }
@@ -418,8 +432,8 @@ export class CodebaseIndexer {
418432 this . updateProgress ( 'embedding' , 50 ) ;
419433 console . error (
420434 `Creating embeddings for ${ chunksToEmbed . length } chunks` +
421- ( diff ? ` (${ allChunks . length } total, ${ chunksToEmbed . length } changed)` : '' ) +
422- '...'
435+ ( diff ? ` (${ allChunks . length } total, ${ chunksToEmbed . length } changed)` : '' ) +
436+ '...'
423437 ) ;
424438
425439 // Initialize embedding provider
@@ -480,11 +494,13 @@ export class CodebaseIndexer {
480494
481495 if ( diff ) {
482496 // Incremental: delete old chunks for changed + deleted files, then add new
483- const filesToDelete = [ ...diff . changed , ...diff . deleted ]
484- . map ( ( rel ) => path . join ( this . rootPath , rel ) . replace ( / \\ / g, '/' ) ) ;
497+ const filesToDelete = [ ...diff . changed , ...diff . deleted ] . map ( ( rel ) =>
498+ path . join ( this . rootPath , rel ) . replace ( / \\ / g, '/' )
499+ ) ;
485500 // Also try with OS-native separators for matching
486- const filePathsForDelete = [ ...diff . changed , ...diff . deleted ]
487- . map ( ( rel ) => path . resolve ( this . rootPath , rel ) ) ;
501+ const filePathsForDelete = [ ...diff . changed , ...diff . deleted ] . map ( ( rel ) =>
502+ path . resolve ( this . rootPath , rel )
503+ ) ;
488504 const allDeletePaths = [ ...new Set ( [ ...filesToDelete , ...filePathsForDelete ] ) ] ;
489505
490506 if ( allDeletePaths . length > 0 ) {
@@ -495,7 +511,7 @@ export class CodebaseIndexer {
495511 }
496512 console . error (
497513 `Incremental store: deleted chunks for ${ diff . changed . length + diff . deleted . length } files, ` +
498- `added ${ chunksWithEmbeddings . length } new chunks`
514+ `added ${ chunksWithEmbeddings . length } new chunks`
499515 ) ;
500516 } else {
501517 // Full: clear and re-store everything
@@ -508,7 +524,8 @@ export class CodebaseIndexer {
508524 // Keyword index always uses ALL chunks (full regen)
509525 const indexPath = path . join ( contextDir , KEYWORD_INDEX_FILENAME ) ;
510526 // Memory safety: cap keyword index too
511- const keywordChunks = allChunks . length > MAX_CHUNKS ? allChunks . slice ( 0 , MAX_CHUNKS ) : allChunks ;
527+ const keywordChunks =
528+ allChunks . length > MAX_CHUNKS ? allChunks . slice ( 0 , MAX_CHUNKS ) : allChunks ;
512529 await fs . writeFile ( indexPath , JSON . stringify ( keywordChunks ) ) ;
513530
514531 // Save library usage and pattern stats (always full regen)
@@ -552,7 +569,7 @@ export class CodebaseIndexer {
552569 const manifest : FileManifest = {
553570 version : 1 ,
554571 generatedAt : new Date ( ) . toISOString ( ) ,
555- files : currentHashes ?? await computeFileHashes ( files , this . rootPath )
572+ files : currentHashes ?? ( await computeFileHashes ( files , this . rootPath ) )
556573 } ;
557574 await writeManifest ( manifestPath , manifest ) ;
558575
@@ -565,8 +582,8 @@ export class CodebaseIndexer {
565582 if ( diff ) {
566583 console . error (
567584 `Incremental indexing complete in ${ stats . duration } ms ` +
568- `(${ diff . added . length } added, ${ diff . changed . length } changed, ` +
569- `${ diff . deleted . length } deleted, ${ diff . unchanged . length } unchanged)`
585+ `(${ diff . added . length } added, ${ diff . changed . length } changed, ` +
586+ `${ diff . deleted . length } deleted, ${ diff . unchanged . length } unchanged)`
570587 ) ;
571588 } else {
572589 console . error ( `Indexing complete in ${ stats . duration } ms` ) ;
@@ -588,6 +605,7 @@ export class CodebaseIndexer {
588605
589606 private async scanFiles ( ) : Promise < string [ ] > {
590607 const files : string [ ] = [ ] ;
608+ const seen = new Set < string > ( ) ;
591609
592610 // Read .gitignore if respecting it
593611 let ig : ReturnType < typeof ignore . default > | null = null ;
@@ -614,6 +632,12 @@ export class CodebaseIndexer {
614632 } ) ;
615633
616634 for ( const file of matches ) {
635+ const normalizedFile = file . replace ( / \\ / g, '/' ) ;
636+ if ( seen . has ( normalizedFile ) ) {
637+ continue ;
638+ }
639+ seen . add ( normalizedFile ) ;
640+
617641 const relativePath = path . relative ( this . rootPath , file ) ;
618642
619643 // Check gitignore
0 commit comments