33import argparse
44import contextlib
55import logging
6+ import signal
67import sys
8+ import time
79import traceback
810from pathlib import Path
911from typing import Any , Callable , Dict , List , NoReturn , Optional , Union
1012
13+ from rich .console import Console
14+
1115from claude_monitor import __version__
1216from claude_monitor .cli .bootstrap import (
1317 ensure_directories ,
1721)
1822from claude_monitor .core .plans import Plans , PlanType , get_token_limit
1923from claude_monitor .core .settings import Settings
24+ from claude_monitor .data .aggregator import UsageAggregator
2025from claude_monitor .data .analysis import analyze_usage
2126from claude_monitor .error_handling import report_error
2227from claude_monitor .monitoring .orchestrator import MonitoringOrchestrator
2934)
3035from claude_monitor .terminal .themes import get_themed_console , print_themed
3136from claude_monitor .ui .display_controller import DisplayController
37+ from claude_monitor .ui .table_views import TableViewsController
3238
3339# Type aliases for CLI callbacks
3440DataUpdateCallback = Callable [[Dict [str , Any ]], None ]
@@ -103,6 +109,8 @@ def main(argv: Optional[List[str]] = None) -> int:
103109
104110def _run_monitoring (args : argparse .Namespace ) -> None :
105111 """Main monitoring implementation without facade."""
112+ view_mode = getattr (args , "view" , "realtime" )
113+
106114 if hasattr (args , "theme" ) and args .theme :
107115 console = get_themed_console (force_theme = args .theme .lower ())
108116 else :
@@ -121,6 +129,11 @@ def _run_monitoring(args: argparse.Namespace) -> None:
121129 logger = logging .getLogger (__name__ )
122130 logger .info (f"Using data path: { data_path } " )
123131
132+ # Handle different view modes
133+ if view_mode in ["daily" , "monthly" ]:
134+ _run_table_view (args , data_path , view_mode , console )
135+ return
136+
124137 token_limit : int = _get_initial_token_limit (args , str (data_path ))
125138
126139 display_controller = DisplayController ()
@@ -151,9 +164,9 @@ def _run_monitoring(args: argparse.Namespace) -> None:
151164 live_display .update (loading_display )
152165
153166 orchestrator = MonitoringOrchestrator (
154- update_interval = args . refresh_rate
155- if hasattr (args , "refresh_rate" )
156- else 10 ,
167+ update_interval = (
168+ args . refresh_rate if hasattr (args , "refresh_rate" ) else 10
169+ ) ,
157170 data_path = str (data_path ),
158171 )
159172 orchestrator .set_args (args )
@@ -214,10 +227,13 @@ def on_session_change(
214227 logger .warning ("Timeout waiting for initial data" )
215228
216229 # Main loop - live display is already active
217- while True :
218- import time
219-
220- time .sleep (1 )
230+ # Use signal.pause() for more efficient waiting
231+ try :
232+ signal .pause ()
233+ except AttributeError :
234+ # Fallback for Windows which doesn't support signal.pause()
235+ while True :
236+ time .sleep (1 )
221237 finally :
222238 # Stop monitoring first
223239 if "orchestrator" in locals ():
@@ -362,5 +378,57 @@ def validate_cli_environment() -> Optional[str]:
362378 return f"Environment validation failed: { e } "
363379
364380
381+ def _run_table_view (
382+ args : argparse .Namespace , data_path : Path , view_mode : str , console : Console
383+ ) -> None :
384+ """Run table view mode (daily/monthly)."""
385+ logger = logging .getLogger (__name__ )
386+
387+ try :
388+ # Create aggregator with appropriate mode
389+ aggregator = UsageAggregator (
390+ data_path = str (data_path ),
391+ aggregation_mode = view_mode ,
392+ timezone = args .timezone ,
393+ )
394+
395+ # Create table controller
396+ controller = TableViewsController (console = console )
397+
398+ # Get aggregated data
399+ logger .info (f"Loading { view_mode } usage data..." )
400+ aggregated_data = aggregator .aggregate ()
401+
402+ if not aggregated_data :
403+ print_themed (f"No usage data found for { view_mode } view" , style = "warning" )
404+ return
405+
406+ # Display the table
407+ controller .display_aggregated_view (
408+ data = aggregated_data ,
409+ view_mode = view_mode ,
410+ timezone = args .timezone ,
411+ plan = args .plan ,
412+ token_limit = _get_initial_token_limit (args , data_path ),
413+ )
414+
415+ # Wait for user to press Ctrl+C
416+ print_themed ("\n Press Ctrl+C to exit" , style = "info" )
417+ try :
418+ # Use signal.pause() for more efficient waiting
419+ try :
420+ signal .pause ()
421+ except AttributeError :
422+ # Fallback for Windows which doesn't support signal.pause()
423+ while True :
424+ time .sleep (1 )
425+ except KeyboardInterrupt :
426+ print_themed ("\n Exiting..." , style = "info" )
427+
428+ except Exception as e :
429+ logger .error (f"Error in table view: { e } " , exc_info = True )
430+ print_themed (f"Error displaying { view_mode } view: { e } " , style = "error" )
431+
432+
365433if __name__ == "__main__" :
366434 sys .exit (main ())
0 commit comments