@@ -562,6 +562,68 @@ program
562562 } ) ;
563563 } ) ;
564564
565+ program
566+ . command ( 'co-change [file]' )
567+ . description (
568+ 'Analyze git history for files that change together. Use --analyze to scan, or query existing data.' ,
569+ )
570+ . option ( '--analyze' , 'Scan git history and populate co-change data' )
571+ . option ( '--since <date>' , 'Git date for history window (default: "1 year ago")' )
572+ . option ( '--min-support <n>' , 'Minimum co-occurrence count (default: 3)' )
573+ . option ( '--min-jaccard <n>' , 'Minimum Jaccard similarity 0-1 (default: 0.3)' )
574+ . option ( '--full' , 'Force full re-scan (ignore incremental state)' )
575+ . option ( '-n, --limit <n>' , 'Max results' , '20' )
576+ . option ( '-d, --db <path>' , 'Path to graph.db' )
577+ . option ( '-T, --no-tests' , 'Exclude test/spec files' )
578+ . option ( '--include-tests' , 'Include test/spec files (overrides excludeTests config)' )
579+ . option ( '-j, --json' , 'Output as JSON' )
580+ . action ( async ( file , opts ) => {
581+ const { analyzeCoChanges, coChangeData, coChangeTopData, formatCoChange, formatCoChangeTop } =
582+ await import ( './cochange.js' ) ;
583+
584+ if ( opts . analyze ) {
585+ const result = analyzeCoChanges ( opts . db , {
586+ since : opts . since || config . coChange ?. since ,
587+ minSupport : opts . minSupport ? parseInt ( opts . minSupport , 10 ) : config . coChange ?. minSupport ,
588+ maxFilesPerCommit : config . coChange ?. maxFilesPerCommit ,
589+ full : opts . full ,
590+ } ) ;
591+ if ( opts . json ) {
592+ console . log ( JSON . stringify ( result , null , 2 ) ) ;
593+ } else if ( result . error ) {
594+ console . error ( result . error ) ;
595+ process . exit ( 1 ) ;
596+ } else {
597+ console . log (
598+ `\nCo-change analysis complete: ${ result . pairsFound } pairs from ${ result . commitsScanned } commits (since: ${ result . since } )\n` ,
599+ ) ;
600+ }
601+ return ;
602+ }
603+
604+ const queryOpts = {
605+ limit : parseInt ( opts . limit , 10 ) ,
606+ minJaccard : opts . minJaccard ? parseFloat ( opts . minJaccard ) : config . coChange ?. minJaccard ,
607+ noTests : resolveNoTests ( opts ) ,
608+ } ;
609+
610+ if ( file ) {
611+ const data = coChangeData ( file , opts . db , queryOpts ) ;
612+ if ( opts . json ) {
613+ console . log ( JSON . stringify ( data , null , 2 ) ) ;
614+ } else {
615+ console . log ( formatCoChange ( data ) ) ;
616+ }
617+ } else {
618+ const data = coChangeTopData ( opts . db , queryOpts ) ;
619+ if ( opts . json ) {
620+ console . log ( JSON . stringify ( data , null , 2 ) ) ;
621+ } else {
622+ console . log ( formatCoChangeTop ( data ) ) ;
623+ }
624+ }
625+ } ) ;
626+
565627program
566628 . command ( 'watch [dir]' )
567629 . description ( 'Watch project for file changes and incrementally update the graph' )
0 commit comments