Coverage for src / moai_adk / cli / commands / analyze.py: 0.00%

58 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-20 20:52 +0900

1#!/usr/bin/env python3 

2""" 

3Analyze command for MoAI-ADK 

4 

5Analyze Claude Code sessions and generate improvement suggestions. 

6""" 

7 

8 

9from pathlib import Path 

10from typing import Optional 

11 

12import click 

13from rich.console import Console 

14from rich.table import Table 

15 

16from moai_adk.core.analysis.session_analyzer import SessionAnalyzer 

17 

18console = Console() 

19 

20 

21@click.command() 

22@click.option("--days", "-d", default=7, help="Number of days to analyze (default: 7)") 

23@click.option("--output", "-o", type=click.Path(), help="Output file path") 

24@click.option("--verbose", "-v", is_flag=True, help="Verbose output") 

25@click.option( 

26 "--report-only", "-r", is_flag=True, help="Generate report only (no console output)" 

27) 

28@click.option( 

29 "--project-path", 

30 "-p", 

31 type=click.Path(), 

32 help="Project root path (default: current directory)", 

33) 

34def analyze( 

35 days: int, 

36 output: Optional[Path], 

37 verbose: bool, 

38 report_only: bool, 

39 project_path: Optional[Path], 

40): 

41 """ 

42 Analyze Claude Code sessions from the last N days 

43 

44 Analyzes Claude Code session logs to identify usage patterns, 

45 error frequencies, and generate improvement suggestions. 

46 

47 Examples: 

48 moai-adk analyze session 

49 moai-adk analyze session --days 14 --verbose 

50 moai-adk analyze session --output /path/to/report.md 

51 """ 

52 if project_path is None: 

53 project_path = Path.cwd() 

54 

55 # Validate project path 

56 if not (project_path / ".moai").exists(): 

57 console.print( 

58 "[red]Error:[/red] Not a MoAI-ADK project (missing .moai directory)" 

59 ) 

60 console.print(f"[blue]Current path:[/blue] {project_path}") 

61 return 

62 

63 # Initialize analyzer 

64 analyzer = SessionAnalyzer(days_back=days, verbose=verbose) 

65 

66 if not report_only: 

67 console.print(f"[blue]📊 Analyzing sessions from last {days} days...[/blue]") 

68 

69 # Parse sessions 

70 patterns = analyzer.parse_sessions() 

71 

72 if not report_only: 

73 console.print( 

74 f"[green]✅ Analyzed {patterns['total_sessions']} sessions[/green]" 

75 ) 

76 console.print(f"[blue] Total events: {patterns['total_events']}[/blue]") 

77 

78 # Display summary table 

79 table = Table(title="Session Summary") 

80 table.add_column("Metric", style="cyan") 

81 table.add_column("Value", style="green") 

82 

83 table.add_row("Total Sessions", str(patterns["total_sessions"])) 

84 table.add_row("Total Events", str(patterns["total_events"])) 

85 table.add_row("Failed Sessions", str(patterns["failed_sessions"])) 

86 table.add_row("Success Rate", f"{patterns.get('success_rate', 0):.1f}%") 

87 

88 console.print(table) 

89 

90 # Show top tools 

91 if patterns["tool_usage"]: 

92 console.print("\n[bold]🔧 Top Tools Used:[/bold]") 

93 top_tools = sorted( 

94 patterns["tool_usage"].items(), key=lambda x: x[1], reverse=True 

95 )[:10] 

96 

97 tools_table = Table() 

98 tools_table.add_column("Tool", style="cyan") 

99 tools_table.add_column("Usage", style="green") 

100 

101 for tool, count in top_tools: 

102 tools_table.add_row(f"`{tool}`", str(count)) 

103 

104 console.print(tools_table) 

105 

106 # Save report 

107 output_path_obj = Path(output) if output else None 

108 report_path = analyzer.save_report(output_path_obj, project_path) 

109 

110 if not report_only: 

111 console.print(f"\n[green]📄 Report saved: {report_path}[/green]") 

112 

113 # Show key suggestions 

114 report_content = analyzer.generate_report() 

115 if "💡 Improvement Suggestions" in report_content: 

116 console.print("\n[bold yellow]💡 Key Suggestions:[/bold yellow]") 

117 # Extract suggestions section 

118 suggestions_start = report_content.find("💡 Improvement Suggestions") 

119 if suggestions_start != -1: 

120 suggestions_section = report_content[suggestions_start:] 

121 # Extract first few suggestions 

122 lines = suggestions_section.split("\n")[ 

123 2: 

124 ] # Skip header and empty line 

125 for line in lines[:10]: # Show first 10 lines 

126 if line.strip() and not line.startswith("---"): 

127 console.print(line)