@@ -363,91 +363,107 @@ def cmd_update(args: argparse.Namespace) -> int:
363363 print (f"# Estimated time: ~{ est_time } s (timeout=3s per tool, { MAX_WORKERS } workers)" , file = sys .stderr )
364364 print ("" , file = sys .stderr )
365365
366- # Parallel audit with progress tracking
366+ # Group tools by category for organized output
367+ from cli_audit .render import CATEGORY_ORDER , CATEGORY_ICON , CATEGORY_DESC
368+ categorized : dict [str , list ] = {}
369+ for tool in tools_list :
370+ cat = tool .category or "general"
371+ if cat not in categorized :
372+ categorized [cat ] = []
373+ categorized [cat ].append (tool )
374+ sorted_cats = sorted (categorized .keys (), key = lambda c : CATEGORY_ORDER .get (c , 99 ))
375+
376+ # Parallel audit with progress tracking, grouped by category
367377 results = []
368378 completed = 0
369379
370380 try :
371- with ThreadPoolExecutor (max_workers = min (MAX_WORKERS , total )) as executor :
372- future_to_tool = {executor .submit (audit_tool , tool , None ): tool for tool in tools_list }
373-
374- for future in as_completed (future_to_tool ):
375- tool = future_to_tool [future ]
376- try :
377- result = future .result ()
378- results .append (result )
379- completed += 1
380-
381- # Progress
382- inst = result .get ("installed" , "" )
383- latest = result .get ("latest_upstream" , "" )
384- status = result .get ("status" , "" )
385-
386- # ANSI colors for all platforms
387- GREEN = "\033 [32m"
388- BOLD_GREEN = "\033 [1;32m"
389- YELLOW = "\033 [33m"
390- BLUE = "\033 [34m"
391- RESET = "\033 [0m"
392-
393- # Color the installed version based on status
394- if status == "UP-TO-DATE" :
395- inst_color = GREEN
396- latest_color = GREEN
397- op = "==="
398- elif status == "OUTDATED" :
399- inst_color = YELLOW
400- latest_color = BOLD_GREEN # Latest is newer, make it bold green
401- op = "!=="
402- elif status == "CONFLICT" :
403- inst_color = YELLOW
404- latest_color = BOLD_GREEN
405- op = "⚠️"
406- else : # NOT INSTALLED, UNKNOWN
407- inst_color = BLUE # Blue for not-installed
408- latest_color = BLUE
409- op = "?"
410-
411- inst_display = inst if inst else "n/a"
412- latest_display = latest if latest else "n/a"
413-
414- # Add pinned/skip markers
415- from cli_audit .catalog import ToolCatalog
416- catalog = ToolCatalog ()
417- markers = []
418- if catalog .is_pinned (tool .name ):
419- markers .append ("PINNED" )
420- if catalog .should_skip (tool .name , latest ):
421- markers .append ("SKIP" )
422-
423- marker_str = f" [{ ' ' .join (markers )} ]" if markers else ""
424- inst_fmt = f"{ inst_color } { inst_display } { RESET } "
425- latest_fmt = f"{ latest_color } { latest_display } { RESET } "
426- msg = f"# [{ completed } /{ total } ] { tool .name } (installed: { inst_fmt } { op } latest: { latest_fmt } ){ marker_str } "
427-
428- print (msg , file = sys .stderr , flush = True )
429-
430- except Exception as e :
431- completed += 1
432- print (f"# [{ completed } /{ total } ] { tool .name } (failed: { e } )" , file = sys .stderr , flush = True )
433-
434- # Add failure entry
435- results .append ({
436- "tool" : tool .name ,
437- "category" : tool .category ,
438- "installed" : "" ,
439- "installed_method" : "" ,
440- "installed_version" : "" ,
441- "installed_path_selected" : "" ,
442- "classification_reason_selected" : "Detection failed" ,
443- "latest_upstream" : "" ,
444- "latest_version" : "" ,
445- "upstream_method" : tool .source_kind ,
446- "status" : "UNKNOWN" ,
447- "tool_url" : tool_homepage_url (tool ),
448- "latest_url" : "" ,
449- "hint" : "" ,
450- })
381+ for category in sorted_cats :
382+ cat_tools = categorized [category ]
383+ icon = CATEGORY_ICON .get (category , "📦" )
384+ desc = CATEGORY_DESC .get (category , category )
385+ print (f"\n # { icon } { desc } ({ len (cat_tools )} tools)" , file = sys .stderr )
386+
387+ with ThreadPoolExecutor (max_workers = min (MAX_WORKERS , len (cat_tools ))) as executor :
388+ future_to_tool = {executor .submit (audit_tool , tool , None ): tool for tool in cat_tools }
389+
390+ for future in as_completed (future_to_tool ):
391+ tool = future_to_tool [future ]
392+ try :
393+ result = future .result ()
394+ results .append (result )
395+ completed += 1
396+
397+ # Progress
398+ inst = result .get ("installed" , "" )
399+ latest = result .get ("latest_upstream" , "" )
400+ status = result .get ("status" , "" )
401+
402+ # ANSI colors for all platforms
403+ GREEN = "\033 [32m"
404+ BOLD_GREEN = "\033 [1;32m"
405+ YELLOW = "\033 [33m"
406+ BLUE = "\033 [34m"
407+ RESET = "\033 [0m"
408+
409+ # Color the installed version based on status
410+ if status == "UP-TO-DATE" :
411+ inst_color = GREEN
412+ latest_color = GREEN
413+ op = "==="
414+ elif status == "OUTDATED" :
415+ inst_color = YELLOW
416+ latest_color = BOLD_GREEN # Latest is newer, make it bold green
417+ op = "!=="
418+ elif status == "CONFLICT" :
419+ inst_color = YELLOW
420+ latest_color = BOLD_GREEN
421+ op = "⚠️"
422+ else : # NOT INSTALLED, UNKNOWN
423+ inst_color = BLUE # Blue for not-installed
424+ latest_color = BLUE
425+ op = "?"
426+
427+ inst_display = inst if inst else "n/a"
428+ latest_display = latest if latest else "n/a"
429+
430+ # Add pinned/skip markers
431+ from cli_audit .catalog import ToolCatalog
432+ catalog = ToolCatalog ()
433+ markers = []
434+ if catalog .is_pinned (tool .name ):
435+ markers .append ("PINNED" )
436+ if catalog .should_skip (tool .name , latest ):
437+ markers .append ("SKIP" )
438+
439+ marker_str = f" [{ ' ' .join (markers )} ]" if markers else ""
440+ inst_fmt = f"{ inst_color } { inst_display } { RESET } "
441+ latest_fmt = f"{ latest_color } { latest_display } { RESET } "
442+ msg = f"# [{ completed } /{ total } ] { tool .name } (installed: { inst_fmt } { op } latest: { latest_fmt } ){ marker_str } "
443+
444+ print (msg , file = sys .stderr , flush = True )
445+
446+ except Exception as e :
447+ completed += 1
448+ print (f"# [{ completed } /{ total } ] { tool .name } (failed: { e } )" , file = sys .stderr , flush = True )
449+
450+ # Add failure entry
451+ results .append ({
452+ "tool" : tool .name ,
453+ "category" : tool .category ,
454+ "installed" : "" ,
455+ "installed_method" : "" ,
456+ "installed_version" : "" ,
457+ "installed_path_selected" : "" ,
458+ "classification_reason_selected" : "Detection failed" ,
459+ "latest_upstream" : "" ,
460+ "latest_version" : "" ,
461+ "upstream_method" : tool .source_kind ,
462+ "status" : "UNKNOWN" ,
463+ "tool_url" : tool_homepage_url (tool ),
464+ "latest_url" : "" ,
465+ "hint" : "" ,
466+ })
451467 except KeyboardInterrupt :
452468 # Shutdown executor immediately without waiting for threads
453469 executor .shutdown (wait = False , cancel_futures = True )
0 commit comments