Coverage for src / moai_adk / statusline / main.py: 19.63%

107 statements  

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

1# type: ignore 

2#!/usr/bin/env python3 

3""" 

4Claude Code Statusline Integration 

5 

6 

7Main entry point for MoAI-ADK statusline rendering in Claude Code. 

8Collects all necessary information from the project and renders it 

9in the specified format for display in the status bar. 

10""" 

11 

12import json 

13import os 

14import sys 

15from pathlib import Path 

16from typing import Optional 

17 

18from .alfred_detector import AlfredDetector 

19from .config import StatuslineConfig 

20from .git_collector import GitCollector 

21from .metrics_tracker import MetricsTracker 

22from .renderer import StatuslineData, StatuslineRenderer 

23from .update_checker import UpdateChecker 

24from .version_reader import VersionReader 

25 

26 

27def read_session_context() -> dict: 

28 """ 

29 Read JSON context from stdin (sent by Claude Code). 

30 

31 Returns: 

32 Dictionary containing session information 

33 """ 

34 try: 

35 input_data = sys.stdin.read() 

36 if input_data: 

37 try: 

38 return json.loads(input_data) 

39 except json.JSONDecodeError as e: 

40 import logging 

41 logging.error(f"Failed to parse JSON from stdin: {e}") 

42 logging.debug(f"Input data: {input_data[:200]}") 

43 return {} 

44 return {} 

45 except (EOFError, ValueError) as e: 

46 import logging 

47 logging.error(f"Error reading stdin: {e}") 

48 return {} 

49 

50 

51def safe_collect_git_info() -> tuple[str, str]: 

52 """ 

53 Safely collect git information with fallback. 

54 

55 Returns: 

56 Tuple of (branch_name, git_status_str) 

57 """ 

58 try: 

59 collector = GitCollector() 

60 git_info = collector.collect_git_info() 

61 

62 branch = git_info.branch or "unknown" 

63 git_status = f"+{git_info.staged} M{git_info.modified} ?{git_info.untracked}" 

64 

65 return branch, git_status 

66 except Exception: 

67 return "N/A", "" 

68 

69 

70def safe_collect_duration() -> str: 

71 """ 

72 Safely collect session duration with fallback. 

73 

74 Returns: 

75 Formatted duration string 

76 """ 

77 try: 

78 tracker = MetricsTracker() 

79 return tracker.get_duration() 

80 except Exception: 

81 return "0m" 

82 

83 

84def safe_collect_alfred_task() -> str: 

85 """ 

86 Safely collect active Alfred task with fallback. 

87 

88 Returns: 

89 Formatted task string 

90 """ 

91 try: 

92 detector = AlfredDetector() 

93 task = detector.detect_active_task() 

94 

95 if task.command: 

96 stage_suffix = f"-{task.stage}" if task.stage else "" 

97 return f"[{task.command.upper()}{stage_suffix}]" 

98 return "" 

99 except Exception: 

100 return "" 

101 

102 

103def safe_collect_version() -> str: 

104 """ 

105 Safely collect MoAI-ADK version with fallback. 

106 

107 Returns: 

108 Version string 

109 """ 

110 try: 

111 reader = VersionReader() 

112 version = reader.get_version() 

113 return version or "unknown" 

114 except Exception: 

115 return "unknown" 

116 

117 

118# safe_collect_output_style function removed - no longer needed 

119 

120 

121def safe_check_update(current_version: str) -> tuple[bool, Optional[str]]: 

122 """ 

123 Safely check for updates with fallback. 

124 

125 Args: 

126 current_version: Current version string 

127 

128 Returns: 

129 Tuple of (update_available, latest_version) 

130 """ 

131 try: 

132 checker = UpdateChecker() 

133 update_info = checker.check_for_update(current_version) 

134 

135 return update_info.available, update_info.latest_version 

136 except Exception: 

137 return False, None 

138 

139 

140 

141 

142def build_statusline_data(session_context: dict, mode: str = "compact") -> str: 

143 """ 

144 Build complete statusline string from all data sources. 

145 

146 Collects information from: 

147 - Claude Code session context (via stdin) 

148 - Git repository 

149 - Session metrics 

150 - Alfred workflow state 

151 - MoAI-ADK version 

152 - Update checker 

153 - Output style 

154 

155 Args: 

156 session_context: Context passed from Claude Code via stdin 

157 mode: Display mode (compact, extended, minimal) 

158 

159 Returns: 

160 Rendered statusline string 

161 """ 

162 try: 

163 # Extract model from session context with Claude Code version 

164 model_info = session_context.get("model", {}) 

165 model_name = model_info.get("display_name") or model_info.get("name") or "Unknown" 

166 

167 # Extract Claude Code version separately for new layout 

168 claude_version = session_context.get("version", "") 

169 model = model_name 

170 

171 # Extract directory 

172 cwd = session_context.get("cwd", "") 

173 if cwd: 

174 directory = Path(cwd).name or Path(cwd).parent.name or "project" 

175 else: 

176 directory = "project" 

177 

178 # Extract output style from session context 

179 output_style = session_context.get("output_style", {}).get("name", "") 

180 

181 # Collect all information from local sources 

182 branch, git_status = safe_collect_git_info() 

183 duration = safe_collect_duration() 

184 active_task = safe_collect_alfred_task() 

185 version = safe_collect_version() 

186 update_available, latest_version = safe_check_update(version) 

187 

188 # Build StatuslineData with dynamic fields 

189 data = StatuslineData( 

190 model=model, 

191 claude_version=claude_version, 

192 version=version, 

193 memory_usage="256MB", # TODO: Get actual memory usage 

194 branch=branch, 

195 git_status=git_status, 

196 duration=duration, 

197 directory=directory, 

198 active_task=active_task, 

199 output_style=output_style, 

200 update_available=update_available, 

201 latest_version=latest_version, 

202 ) 

203 

204 # Render statusline with labeled sections 

205 renderer = StatuslineRenderer() 

206 statusline = renderer.render(data, mode=mode) 

207 

208 return statusline 

209 

210 except Exception as e: 

211 # Graceful degradation on any error 

212 import logging 

213 

214 logging.warning(f"Statusline rendering error: {e}") 

215 return "" 

216 

217 

218def main(): 

219 """ 

220 Main entry point for Claude Code statusline. 

221 

222 Reads JSON from stdin, processes all information, 

223 and outputs the formatted statusline string. 

224 """ 

225 # Debug mode check 

226 debug_mode = os.environ.get("MOAI_STATUSLINE_DEBUG") == "1" 

227 

228 # Read session context from Claude Code 

229 session_context = read_session_context() 

230 

231 if debug_mode: 

232 # Write debug info to stderr for troubleshooting 

233 sys.stderr.write(f"[DEBUG] Received session_context: {json.dumps(session_context, indent=2)}\n") 

234 sys.stderr.flush() 

235 

236 # Load configuration 

237 config = StatuslineConfig() 

238 

239 # Determine display mode (priority: session context > environment > config > default) 

240 mode = ( 

241 session_context.get("statusline", {}).get("mode") 

242 or os.environ.get("MOAI_STATUSLINE_MODE") 

243 or config.get("statusline.mode") 

244 or "extended" 

245 ) 

246 

247 # Build and output statusline 

248 statusline = build_statusline_data(session_context, mode=mode) 

249 if debug_mode: 

250 sys.stderr.write(f"[DEBUG] Generated statusline: {statusline}\n") 

251 sys.stderr.flush() 

252 

253 if statusline: 

254 print(statusline, end="") 

255 

256 

257if __name__ == "__main__": 

258 main()