@@ -4,6 +4,7 @@ import ProgressBar from 'cli-progress';
44import SummaryManager from './summary-manager' ;
55import { ProcessProgress , ProgressManagerOptions , Failure } from '../interfaces' ;
66import { configHandler } from '..' ;
7+ import { ProgressStrategyRegistry } from './progress-strategy' ;
78
89interface ProgressCallback {
910 onModuleStart ?: ( moduleName : string ) => void ;
@@ -56,7 +57,7 @@ export default class CLIProgressManager {
5657 this . multiBar = null ;
5758 this . currentProcess = null ;
5859 this . callbacks = { } ;
59- this . branchName = '' ; // Default branch name
60+ this . branchName = '' ;
6061
6162 this . initializeProgress ( ) ;
6263 this . setupGlobalSummaryIntegration ( ) ;
@@ -93,10 +94,56 @@ export default class CLIProgressManager {
9394 }
9495
9596 /**
96- * Print the final summary for all modules
97+ * Print the final summary for all modules using strategies
9798 */
9899 static printGlobalSummary ( ) : void {
99- CLIProgressManager . globalSummary ?. printFinalSummary ( ) ;
100+ if ( ! CLIProgressManager . globalSummary ) {
101+ return ;
102+ }
103+
104+ // Apply strategy-based corrections before printing
105+ CLIProgressManager . applyStrategyCorrections ( ) ;
106+
107+ // Print the final summary
108+ CLIProgressManager . globalSummary . printFinalSummary ( ) ;
109+ }
110+
111+ /**
112+ * Apply strategy-based corrections to module data
113+ */
114+ private static applyStrategyCorrections ( ) : void {
115+ if ( ! CLIProgressManager . globalSummary ) return ;
116+
117+ const modules = Array . from ( CLIProgressManager . globalSummary . getModules ( ) . values ( ) ) ;
118+
119+ modules . forEach ( ( module ) => {
120+ // Check if this module has a registered strategy
121+ if ( ProgressStrategyRegistry . has ( module . name ) ) {
122+ const strategy = ProgressStrategyRegistry . get ( module . name ) ;
123+
124+ // Create a processes map from module data if available
125+ const processesMap = new Map < string , ProcessProgress > ( ) ;
126+
127+ // If module has process data, populate the map
128+ if ( module . processes && Array . isArray ( module . processes ) ) {
129+ module . processes . forEach ( ( processData : any ) => {
130+ if ( processData . processName ) {
131+ processesMap . set ( processData . processName , processData ) ;
132+ }
133+ } ) ;
134+ }
135+
136+ // Calculate corrected progress using strategy
137+ const correctedResult = strategy . calculate ( processesMap ) ;
138+
139+ if ( correctedResult ) {
140+ // Update module with corrected counts
141+ module . totalItems = correctedResult . total ;
142+ module . successCount = correctedResult . success ;
143+ module . failureCount = correctedResult . failures ;
144+ }
145+ }
146+ } ) ;
100147 }
101148
102149 /**
@@ -154,6 +201,8 @@ export default class CLIProgressManager {
154201 CLIProgressManager . globalSummary ?. startModule ( name ) ;
155202 } ,
156203 onModuleComplete : ( name , success , error ) => {
204+ // Register process data with summary manager before completing
205+ this . registerProcessDataWithSummary ( name ) ;
157206 CLIProgressManager . globalSummary ?. completeModule ( name , success , error ) ;
158207 } ,
159208 onProgress : ( name , success , itemName , error ) => {
@@ -166,6 +215,26 @@ export default class CLIProgressManager {
166215 }
167216 }
168217
218+ /**
219+ * Register process data with summary manager for strategy calculations
220+ */
221+ private registerProcessDataWithSummary ( moduleName : string ) : void {
222+ if ( ! CLIProgressManager . globalSummary ) return ;
223+
224+ // Register each process with the summary manager
225+ this . processes . forEach ( ( processData , processName ) => {
226+ CLIProgressManager . globalSummary ?. registerProcessData ( moduleName , processName , {
227+ processName,
228+ total : processData . total ,
229+ current : processData . current ,
230+ successCount : processData . successCount ,
231+ failureCount : processData . failureCount ,
232+ status : processData . status ,
233+ failures : processData . failures ,
234+ } ) ;
235+ } ) ;
236+ }
237+
169238 /**
170239 * Set callbacks for external integration
171240 */
@@ -180,6 +249,32 @@ export default class CLIProgressManager {
180249 return name . charAt ( 0 ) . toUpperCase ( ) + name . slice ( 1 ) . toLowerCase ( ) ;
181250 }
182251
252+ /**
253+ * Format process name with smart truncation (modules should use short names)
254+ */
255+ private formatProcessName ( processName : string ) : string {
256+ const cleaned = processName . trim ( ) ;
257+
258+ if ( cleaned . length <= 20 ) {
259+ return cleaned ;
260+ }
261+
262+ return cleaned . length <= 20 ? cleaned : cleaned . substring ( 0 , 20 ) + '...' ;
263+ }
264+
265+ /**
266+ * Format percentage for consistent alignment (always 3 characters)
267+ */
268+ private formatPercentage ( percentage : number ) : string {
269+ if ( percentage === 100 ) {
270+ return '100' ;
271+ } else if ( percentage >= 10 ) {
272+ return ` ${ percentage } ` ;
273+ } else {
274+ return ` ${ percentage } ` ;
275+ }
276+ }
277+
183278 private initializeProgress ( ) : void {
184279 if ( this . showConsoleLogs ) {
185280 return ;
@@ -217,7 +312,7 @@ export default class CLIProgressManager {
217312 this . progressBar . start ( this . total , 0 , {
218313 label : chalk . gray ( ` └─ ${ displayName } ` . padEnd ( 25 ) ) ,
219314 status : chalk . gray ( 'Starting...' ) ,
220- percentage : '0' ,
315+ percentage : ' 0' ,
221316 } ) ;
222317 } else {
223318 this . spinner = ora ( `${ chalk . bold ( this . moduleName ) } : Processing...` ) . start ( ) ;
@@ -241,19 +336,36 @@ export default class CLIProgressManager {
241336 } ;
242337
243338 if ( ! this . showConsoleLogs ) {
244- const truncatedName = processName . length > 20 ? processName . substring ( 0 , 17 ) + '...' : processName ;
245- const indentedLabel = ` ├─ ${ truncatedName } ` . padEnd ( 25 ) ;
339+ const displayName = this . formatProcessName ( processName ) ;
340+ const indentedLabel = ` ├─ ${ displayName } ` . padEnd ( 25 ) ;
246341 process . progressBar = this . multiBar . create ( total , 0 , {
247342 label : chalk . gray ( indentedLabel ) ,
248343 status : chalk . gray ( 'Pending' ) ,
249- percentage : '0' ,
344+ percentage : ' 0' ,
250345 } ) ;
251346 }
252347
253348 this . processes . set ( processName , process ) ;
254349 return this ;
255350 }
256351
352+ /**
353+ * Update the total for a specific process (for dynamic totals after API calls)
354+ */
355+ updateProcessTotal ( processName : string , newTotal : number ) : this {
356+ if ( ! this . enableNestedProgress ) return this ;
357+
358+ const process = this . processes . get ( processName ) ;
359+ if ( process ) {
360+ process . total = newTotal ;
361+ if ( process . progressBar && ! this . showConsoleLogs ) {
362+ // Update the progress bar with the new total
363+ process . progressBar . setTotal ( newTotal ) ;
364+ }
365+ }
366+ return this ;
367+ }
368+
257369 /**
258370 * Start a specific process
259371 */
@@ -264,12 +376,12 @@ export default class CLIProgressManager {
264376 if ( process ) {
265377 process . status = 'active' ;
266378 if ( ! this . showConsoleLogs && process . progressBar ) {
267- const truncatedName = processName . length > 20 ? processName . substring ( 0 , 17 ) + '...' : processName ;
268- const indentedLabel = ` ├─ ${ truncatedName } ` . padEnd ( 25 ) ;
379+ const displayName = this . formatProcessName ( processName ) ;
380+ const indentedLabel = ` ├─ ${ displayName } ` . padEnd ( 25 ) ;
269381 process . progressBar . update ( 0 , {
270382 label : chalk . yellow ( indentedLabel ) ,
271383 status : chalk . yellow ( 'Processing' ) ,
272- percentage : '0' ,
384+ percentage : ' 0' ,
273385 } ) ;
274386 }
275387 this . currentProcess = processName ;
@@ -288,15 +400,16 @@ export default class CLIProgressManager {
288400 process . status = success ? 'completed' : 'failed' ;
289401 if ( ! this . showConsoleLogs && process . progressBar ) {
290402 const percentage = Math . round ( ( process . current / process . total ) * 100 ) ;
403+ const formattedPercentage = this . formatPercentage ( percentage ) ;
291404 const statusText = success
292405 ? chalk . green ( `✓ Complete (${ process . successCount } /${ process . current } )` )
293406 : chalk . red ( `✗ Failed (${ process . successCount } /${ process . current } )` ) ;
294- const truncatedName = processName . length > 20 ? processName . substring ( 0 , 17 ) + '...' : processName ;
295- const indentedLabel = ` ├─ ${ truncatedName } ` . padEnd ( 25 ) ;
407+ const displayName = this . formatProcessName ( processName ) ;
408+ const indentedLabel = ` ├─ ${ displayName } ` . padEnd ( 25 ) ;
296409 process . progressBar . update ( process . total , {
297410 label : success ? chalk . green ( indentedLabel ) : chalk . red ( indentedLabel ) ,
298411 status : statusText ,
299- percentage : percentage . toString ( ) ,
412+ percentage : formattedPercentage ,
300413 } ) ;
301414 }
302415 }
@@ -312,22 +425,24 @@ export default class CLIProgressManager {
312425 const process = this . processes . get ( processName ) ;
313426 if ( process && process . progressBar ) {
314427 const percentage = Math . round ( ( process . current / process . total ) * 100 ) ;
315- const truncatedName = processName . length > 20 ? processName . substring ( 0 , 17 ) + '...' : processName ;
316- const indentedLabel = ` ├─ ${ truncatedName } ` . padEnd ( 25 ) ;
428+ const formattedPercentage = this . formatPercentage ( percentage ) ;
429+ const displayName = this . formatProcessName ( processName ) ;
430+ const indentedLabel = ` ├─ ${ displayName } ` . padEnd ( 25 ) ;
317431 process . progressBar . update ( process . current , {
318432 label : chalk . yellow ( indentedLabel ) ,
319433 status : chalk . yellow ( message ) ,
320- percentage : percentage . toString ( ) ,
434+ percentage : formattedPercentage ,
321435 } ) ;
322436 }
323437 } else if ( this . progressBar ) {
324438 const percentage = Math . round ( this . progressBar . getProgress ( ) * 100 ) ;
439+ const formattedPercentage = this . formatPercentage ( percentage ) ;
325440 const formattedName = this . formatModuleName ( this . moduleName ) ;
326441 const displayName = formattedName . length > 20 ? formattedName . substring ( 0 , 17 ) + '...' : formattedName ;
327442 this . progressBar . update ( this . progressBar . getProgress ( ) * this . total , {
328443 label : chalk . yellow ( ` └─ ${ displayName } ` . padEnd ( 25 ) ) ,
329444 status : chalk . yellow ( message ) ,
330- percentage : percentage . toString ( ) ,
445+ percentage : formattedPercentage ,
331446 } ) ;
332447 } else if ( this . spinner ) {
333448 this . spinner . text = `${ chalk . bold ( this . moduleName ) } : ${ message } ` ;
@@ -371,14 +486,15 @@ export default class CLIProgressManager {
371486 // Only update progress bar if console logs are disabled
372487 if ( ! this . showConsoleLogs && process . progressBar ) {
373488 const percentage = Math . round ( ( process . current / process . total ) * 100 ) ;
489+ const formattedPercentage = this . formatPercentage ( percentage ) ;
374490 const statusText = `${ process . successCount } ✓ ${ process . failureCount } ✗` ;
375491
376- const truncatedName = targetProcess . length > 20 ? targetProcess . substring ( 0 , 17 ) + '...' : targetProcess ;
377- const indentedLabel = ` ├─ ${ truncatedName } ` . padEnd ( 25 ) ;
492+ const displayName = this . formatProcessName ( targetProcess ) ;
493+ const indentedLabel = ` ├─ ${ displayName } ` . padEnd ( 25 ) ;
378494 process . progressBar . increment ( 1 , {
379495 label : chalk . cyan ( indentedLabel ) ,
380496 status : chalk . cyan ( statusText ) ,
381- percentage : percentage . toString ( ) ,
497+ percentage : formattedPercentage ,
382498 } ) ;
383499 }
384500 }
@@ -387,6 +503,7 @@ export default class CLIProgressManager {
387503 if ( ! this . showConsoleLogs ) {
388504 if ( this . progressBar ) {
389505 const percentage = Math . round ( ( ( this . successCount + this . failureCount ) / this . total ) * 100 ) ;
506+ const formattedPercentage = this . formatPercentage ( percentage ) ;
390507 const totalProcessed = this . successCount + this . failureCount ;
391508
392509 // Show completion status when finished, otherwise show running count
@@ -405,7 +522,7 @@ export default class CLIProgressManager {
405522 this . progressBar . increment ( 1 , {
406523 label : labelColor ( ` └─ ${ displayName } ` . padEnd ( 25 ) ) ,
407524 status : statusText ,
408- percentage : percentage . toString ( ) ,
525+ percentage : formattedPercentage ,
409526 } ) ;
410527 } else if ( this . spinner ) {
411528 const total = this . successCount + this . failureCount ;
0 commit comments