9
9
import { TaskManager } from "../server/TaskManager.js" ;
10
10
import { createError , normalizeError } from "../utils/errors.js" ;
11
11
import { formatCliError } from "./errors.js" ;
12
+ import { formatProjectsList , formatTaskProgressTable } from "./taskFormattingUtils.js" ;
12
13
13
14
const program = new Command ( ) ;
14
15
@@ -28,7 +29,6 @@ program.hook('preAction', (thisCommand, actionCommand) => {
28
29
const envFilePath = process . env . TASK_MANAGER_FILE_PATH ;
29
30
const resolvedPath = cliFilePath || envFilePath || undefined ;
30
31
31
- console . log ( chalk . blue ( `Using task file path determined by CLI/Env: ${ resolvedPath || 'TaskManager Default' } ` ) ) ;
32
32
try {
33
33
taskManager = new TaskManager ( resolvedPath ) ;
34
34
} catch ( error ) {
@@ -306,8 +306,8 @@ program
306
306
307
307
program
308
308
. command ( "list" )
309
- . description ( "List all projects and their tasks" )
310
- . option ( '-p, --project <projectId>' , 'Show details for a specific project' )
309
+ . description ( "List project summaries, or list tasks for a specific project " )
310
+ . option ( '-p, --project <projectId>' , 'Show details and tasks for a specific project' )
311
311
. option ( '-s, --state <state>' , "Filter by task/project state (open, pending_approval, completed, all)" )
312
312
. action ( async ( options ) => {
313
313
try {
@@ -319,160 +319,79 @@ program
319
319
console . log ( chalk . yellow ( `Valid states are: ${ validStates . join ( ', ' ) } ` ) ) ;
320
320
process . exit ( 1 ) ;
321
321
}
322
- // Use 'undefined' if state is 'all' or not provided, as TaskManager methods expect TaskState or undefined
323
322
const filterState = ( stateOption === 'all' || ! stateOption ) ? undefined : stateOption as TaskState ;
324
323
325
324
if ( options . project ) {
326
325
// Show details for a specific project
327
326
const projectId = options . project ;
328
-
329
- // Fetch project details for display first
330
- let projectDetailsResponse ;
331
327
try {
332
- projectDetailsResponse = await taskManager . readProject ( projectId ) ;
333
- if ( 'error' in projectDetailsResponse ) {
334
- throw projectDetailsResponse . error ;
335
- }
336
- if ( projectDetailsResponse . status !== "success" ) {
337
- throw createError ( ErrorCode . InvalidResponseFormat , "Unexpected response format from TaskManager" ) ;
338
- }
339
- const project = projectDetailsResponse . data ;
340
-
341
- // Fetch tasks for this project, applying state filter
342
- const tasksResponse = await taskManager . listTasks ( projectId , filterState ) ;
343
- // Check for success before accessing data
344
- const tasks = tasksResponse . status === 'success' ? tasksResponse . data . tasks : [ ] ;
345
-
346
- console . log ( chalk . cyan ( `\n📋 Project ${ chalk . bold ( projectId ) } details:` ) ) ;
347
- console . log ( ` - ${ chalk . bold ( 'Initial Prompt:' ) } ${ project . initialPrompt } ` ) ;
348
- if ( project . projectPlan && project . projectPlan !== project . initialPrompt ) {
349
- console . log ( ` - ${ chalk . bold ( 'Project Plan:' ) } ${ project . projectPlan } ` ) ;
328
+ const projectResponse = await taskManager . readProject ( projectId ) ;
329
+ if ( 'error' in projectResponse ) throw projectResponse . error ;
330
+ if ( projectResponse . status !== "success" ) throw createError ( ErrorCode . InvalidResponseFormat , "Unexpected response" ) ;
331
+
332
+ const project = projectResponse . data ;
333
+
334
+ // Filter tasks based on state if provided
335
+ const tasksToList = filterState
336
+ ? project . tasks . filter ( ( task ) => {
337
+ if ( filterState === 'open' ) return task . status !== 'done' ;
338
+ if ( filterState === 'pending_approval' ) return task . status === 'done' && ! task . approved ;
339
+ if ( filterState === 'completed' ) return task . status === 'done' && task . approved ;
340
+ return true ; // Should not happen
341
+ } )
342
+ : project . tasks ;
343
+
344
+ // Use the formatter for the progress table - it now includes the header
345
+ const projectForTableDisplay = { ...project , tasks : tasksToList } ;
346
+ console . log ( formatTaskProgressTable ( projectForTableDisplay ) ) ;
347
+
348
+ if ( tasksToList . length === 0 ) {
349
+ console . log ( chalk . yellow ( `\nNo tasks found${ filterState ? ` matching state '${ filterState } '` : '' } in project ${ projectId } .` ) ) ;
350
+ } else if ( filterState ) {
351
+ console . log ( chalk . dim ( `(Filtered by state: ${ filterState } )` ) ) ;
350
352
}
351
- console . log ( ` - ${ chalk . bold ( 'Status:' ) } ${ project . completed ? chalk . green ( 'Completed ✓' ) : chalk . yellow ( 'In Progress' ) } ` ) ;
352
353
353
- // Show progress info (using data from readProject)
354
- const totalTasks = project . tasks . length ;
355
- const completedTasks = project . tasks . filter ( ( t : { status : string } ) => t . status === "done" ) . length ;
356
- const approvedTasks = project . tasks . filter ( ( t : { approved : boolean } ) => t . approved ) . length ;
357
-
358
- console . log ( chalk . cyan ( `\n📊 Progress: ${ chalk . bold ( `${ approvedTasks } /${ completedTasks } /${ totalTasks } ` ) } (approved/completed/total)` ) ) ;
359
-
360
- // Create a progress bar
361
- if ( totalTasks > 0 ) {
362
- const bar = '▓' . repeat ( approvedTasks ) + '▒' . repeat ( completedTasks - approvedTasks ) + '░' . repeat ( totalTasks - completedTasks ) ;
363
- console . log ( ` ${ bar } ` ) ;
364
- } else {
365
- console . log ( chalk . yellow ( ' No tasks in this project yet.' ) ) ;
366
- }
367
-
368
- if ( tasks . length > 0 ) {
369
- console . log ( chalk . cyan ( '\n📝 Tasks' + ( filterState ? ` (filtered by state: ${ filterState } )` : '' ) + ':' ) ) ;
370
- tasks . forEach ( ( t : {
371
- id : string ;
372
- title : string ;
373
- status : string ;
374
- approved : boolean ;
375
- description : string ;
376
- completedDetails ?: string ;
377
- toolRecommendations ?: string ;
378
- ruleRecommendations ?: string ;
379
- } ) => {
380
- const status = t . status === 'done' ? chalk . green ( 'Done ✓' ) : t . status === 'in progress' ? chalk . yellow ( 'In Progress ⟳' ) : chalk . blue ( 'Not Started ○' ) ;
381
- const approved = t . approved ? chalk . green ( 'Yes ✓' ) : chalk . red ( 'No ✗' ) ;
382
- console . log ( ` - ${ chalk . bold ( t . id ) } : ${ t . title } ` ) ;
383
- console . log ( ` Status: ${ status } , Approved: ${ approved } ` ) ;
384
- console . log ( ` Description: ${ t . description } ` ) ;
385
- if ( t . completedDetails ) {
386
- console . log ( ` Completed Details: ${ t . completedDetails } ` ) ;
387
- }
388
- if ( t . toolRecommendations ) {
389
- console . log ( ` Tool Recommendations: ${ t . toolRecommendations } ` ) ;
390
- }
391
- if ( t . ruleRecommendations ) {
392
- console . log ( ` Rule Recommendations: ${ t . ruleRecommendations } ` ) ;
393
- }
394
- } ) ;
395
- } else {
396
- console . log ( chalk . yellow ( `\nNo tasks found${ filterState ? ` matching state '${ filterState } '` : '' } in project ${ projectId } .` ) ) ;
397
- }
398
354
} catch ( error : unknown ) {
399
- if ( error instanceof Error ) {
400
- console . error ( chalk . red ( `Error fetching details for project ${ projectId } : ${ error . message } ` ) ) ;
401
- } else {
402
- console . error ( chalk . red ( `Error fetching details for project ${ projectId } : Unknown error` ) ) ;
403
- }
404
- // Handle ProjectNotFound specifically if desired, otherwise let generic handler catch
405
- const normalized = normalizeError ( error ) ;
355
+ const normalized = normalizeError ( error ) ;
406
356
if ( normalized . code === ErrorCode . ProjectNotFound ) {
407
357
console . error ( chalk . red ( `Project ${ chalk . bold ( projectId ) } not found.` ) ) ;
408
358
// Optionally list available projects
409
- const projectsResponse = await taskManager . listProjects ( ) ;
410
- if ( 'error' in projectsResponse ) {
411
- throw projectsResponse . error ;
412
- }
359
+ const projectsResponse = await taskManager . listProjects ( ) ; // Fetch summaries
413
360
if ( projectsResponse . status === "success" && projectsResponse . data . projects . length > 0 ) {
414
361
console . log ( chalk . yellow ( 'Available projects:' ) ) ;
415
362
projectsResponse . data . projects . forEach ( ( p : { projectId : string ; initialPrompt : string } ) => {
416
363
console . log ( ` - ${ p . projectId } : ${ p . initialPrompt . substring ( 0 , 50 ) } ${ p . initialPrompt . length > 50 ? '...' : '' } ` ) ;
417
364
} ) ;
418
- } else {
365
+ } else if ( projectsResponse . status === "success" ) {
419
366
console . log ( chalk . yellow ( 'No projects available.' ) ) ;
420
367
}
368
+ // else: error fetching list, handled by outer catch
421
369
process . exit ( 1 ) ;
370
+ } else {
371
+ console . error ( chalk . red ( formatCliError ( normalized ) ) ) ;
372
+ process . exit ( 1 ) ;
422
373
}
423
- throw error ; // Re-throw other errors
424
374
}
425
375
} else {
426
- // List all projects, applying state filter
427
- const projectsResponse = await taskManager . listProjects ( filterState ) ;
428
- // Check for success before accessing data
429
- const projectsToList = projectsResponse . status === 'success' ? projectsResponse . data . projects : [ ] ;
376
+ // List all projects, potentially filtered
377
+ const projectsSummaryResponse = await taskManager . listProjects ( filterState ) ;
378
+ if ( 'error' in projectsSummaryResponse ) throw projectsSummaryResponse . error ;
379
+ if ( projectsSummaryResponse . status !== "success" ) throw createError ( ErrorCode . InvalidResponseFormat , "Unexpected response" ) ;
380
+
381
+ const projectSummaries = projectsSummaryResponse . data . projects ;
430
382
431
- if ( projectsToList . length === 0 ) {
383
+ if ( projectSummaries . length === 0 ) {
432
384
console . log ( chalk . yellow ( `No projects found${ filterState ? ` matching state '${ filterState } '` : '' } .` ) ) ;
433
385
return ;
434
386
}
435
387
436
- console . log ( chalk . cyan ( '\n📋 Projects List' + ( filterState ? ` (filtered by state: ${ filterState } )` : '' ) ) ) ;
437
- // Fetch full details for progress bar calculation if needed, or use summary data
438
- for ( const pSummary of projectsToList ) {
439
- try {
440
- const projDetailsResp = await taskManager . readProject ( pSummary . projectId ) ;
441
- if ( 'error' in projDetailsResp ) {
442
- throw projDetailsResp . error ;
443
- }
444
- if ( projDetailsResp . status !== "success" ) {
445
- throw createError ( ErrorCode . InvalidResponseFormat , "Unexpected response format from TaskManager" ) ;
446
- }
447
- const p = projDetailsResp . data ;
448
-
449
- const totalTasks = p . tasks . length ;
450
- const completedTasks = p . tasks . filter ( ( t : { status : string } ) => t . status === "done" ) . length ;
451
- const approvedTasks = p . tasks . filter ( ( t : { approved : boolean } ) => t . approved ) . length ;
452
- const status = p . completed ? chalk . green ( 'Completed ✓' ) : chalk . yellow ( 'In Progress' ) ;
453
-
454
- console . log ( `\n${ chalk . bold ( p . projectId ) } : ${ status } ` ) ;
455
- console . log ( ` Initial Prompt: ${ p . initialPrompt . substring ( 0 , 100 ) } ${ p . initialPrompt . length > 100 ? '...' : '' } ` ) ;
456
- console . log ( ` Progress: ${ chalk . bold ( `${ approvedTasks } /${ completedTasks } /${ totalTasks } ` ) } (approved/completed/total)` ) ;
457
-
458
- // Create a progress bar
459
- if ( totalTasks > 0 ) {
460
- const bar = '▓' . repeat ( approvedTasks ) + '▒' . repeat ( completedTasks - approvedTasks ) + '░' . repeat ( totalTasks - completedTasks ) ;
461
- console . log ( ` ${ bar } ` ) ;
462
- } else {
463
- console . log ( chalk . yellow ( ' No tasks in this project.' ) ) ;
464
- }
465
- } catch ( error : unknown ) {
466
- if ( error instanceof Error ) {
467
- console . error ( chalk . red ( `Error fetching details for project ${ pSummary . projectId } : ${ error . message } ` ) ) ;
468
- } else {
469
- console . error ( chalk . red ( `Error fetching details for project ${ pSummary . projectId } : Unknown error` ) ) ;
470
- }
471
- }
388
+ // Use the formatter directly with the summary data
389
+ console . log ( chalk . cyan ( formatProjectsList ( projectSummaries ) ) ) ;
390
+ if ( filterState ) {
391
+ console . log ( chalk . dim ( `(Filtered by state: ${ filterState } )` ) ) ;
472
392
}
473
393
}
474
394
} catch ( error ) {
475
- // Handle errors generally - no need for TaskNotDone handling in list command
476
395
console . error ( chalk . red ( formatCliError ( normalizeError ( error ) ) ) ) ;
477
396
process . exit ( 1 ) ;
478
397
}
0 commit comments