99import { TaskManager } from "../server/TaskManager.js" ;
1010import { createError , normalizeError } from "../utils/errors.js" ;
1111import { formatCliError } from "./errors.js" ;
12+ import { formatProjectsList , formatTaskProgressTable } from "./taskFormattingUtils.js" ;
1213
1314const program = new Command ( ) ;
1415
@@ -28,7 +29,6 @@ program.hook('preAction', (thisCommand, actionCommand) => {
2829 const envFilePath = process . env . TASK_MANAGER_FILE_PATH ;
2930 const resolvedPath = cliFilePath || envFilePath || undefined ;
3031
31- console . log ( chalk . blue ( `Using task file path determined by CLI/Env: ${ resolvedPath || 'TaskManager Default' } ` ) ) ;
3232 try {
3333 taskManager = new TaskManager ( resolvedPath ) ;
3434 } catch ( error ) {
@@ -306,8 +306,8 @@ program
306306
307307program
308308 . 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' )
311311 . option ( '-s, --state <state>' , "Filter by task/project state (open, pending_approval, completed, all)" )
312312 . action ( async ( options ) => {
313313 try {
@@ -319,160 +319,79 @@ program
319319 console . log ( chalk . yellow ( `Valid states are: ${ validStates . join ( ', ' ) } ` ) ) ;
320320 process . exit ( 1 ) ;
321321 }
322- // Use 'undefined' if state is 'all' or not provided, as TaskManager methods expect TaskState or undefined
323322 const filterState = ( stateOption === 'all' || ! stateOption ) ? undefined : stateOption as TaskState ;
324323
325324 if ( options . project ) {
326325 // Show details for a specific project
327326 const projectId = options . project ;
328-
329- // Fetch project details for display first
330- let projectDetailsResponse ;
331327 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 } )` ) ) ;
350352 }
351- console . log ( ` - ${ chalk . bold ( 'Status:' ) } ${ project . completed ? chalk . green ( 'Completed ✓' ) : chalk . yellow ( 'In Progress' ) } ` ) ;
352353
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- }
398354 } 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 ) ;
406356 if ( normalized . code === ErrorCode . ProjectNotFound ) {
407357 console . error ( chalk . red ( `Project ${ chalk . bold ( projectId ) } not found.` ) ) ;
408358 // 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
413360 if ( projectsResponse . status === "success" && projectsResponse . data . projects . length > 0 ) {
414361 console . log ( chalk . yellow ( 'Available projects:' ) ) ;
415362 projectsResponse . data . projects . forEach ( ( p : { projectId : string ; initialPrompt : string } ) => {
416363 console . log ( ` - ${ p . projectId } : ${ p . initialPrompt . substring ( 0 , 50 ) } ${ p . initialPrompt . length > 50 ? '...' : '' } ` ) ;
417364 } ) ;
418- } else {
365+ } else if ( projectsResponse . status === "success" ) {
419366 console . log ( chalk . yellow ( 'No projects available.' ) ) ;
420367 }
368+ // else: error fetching list, handled by outer catch
421369 process . exit ( 1 ) ;
370+ } else {
371+ console . error ( chalk . red ( formatCliError ( normalized ) ) ) ;
372+ process . exit ( 1 ) ;
422373 }
423- throw error ; // Re-throw other errors
424374 }
425375 } 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 ;
430382
431- if ( projectsToList . length === 0 ) {
383+ if ( projectSummaries . length === 0 ) {
432384 console . log ( chalk . yellow ( `No projects found${ filterState ? ` matching state '${ filterState } '` : '' } .` ) ) ;
433385 return ;
434386 }
435387
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 } )` ) ) ;
472392 }
473393 }
474394 } catch ( error ) {
475- // Handle errors generally - no need for TaskNotDone handling in list command
476395 console . error ( chalk . red ( formatCliError ( normalizeError ( error ) ) ) ) ;
477396 process . exit ( 1 ) ;
478397 }
0 commit comments