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
« 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
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"""
12import json
13import os
14import sys
15from pathlib import Path
16from typing import Optional
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
27def read_session_context() -> dict:
28 """
29 Read JSON context from stdin (sent by Claude Code).
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 {}
51def safe_collect_git_info() -> tuple[str, str]:
52 """
53 Safely collect git information with fallback.
55 Returns:
56 Tuple of (branch_name, git_status_str)
57 """
58 try:
59 collector = GitCollector()
60 git_info = collector.collect_git_info()
62 branch = git_info.branch or "unknown"
63 git_status = f"+{git_info.staged} M{git_info.modified} ?{git_info.untracked}"
65 return branch, git_status
66 except Exception:
67 return "N/A", ""
70def safe_collect_duration() -> str:
71 """
72 Safely collect session duration with fallback.
74 Returns:
75 Formatted duration string
76 """
77 try:
78 tracker = MetricsTracker()
79 return tracker.get_duration()
80 except Exception:
81 return "0m"
84def safe_collect_alfred_task() -> str:
85 """
86 Safely collect active Alfred task with fallback.
88 Returns:
89 Formatted task string
90 """
91 try:
92 detector = AlfredDetector()
93 task = detector.detect_active_task()
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 ""
103def safe_collect_version() -> str:
104 """
105 Safely collect MoAI-ADK version with fallback.
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"
118# safe_collect_output_style function removed - no longer needed
121def safe_check_update(current_version: str) -> tuple[bool, Optional[str]]:
122 """
123 Safely check for updates with fallback.
125 Args:
126 current_version: Current version string
128 Returns:
129 Tuple of (update_available, latest_version)
130 """
131 try:
132 checker = UpdateChecker()
133 update_info = checker.check_for_update(current_version)
135 return update_info.available, update_info.latest_version
136 except Exception:
137 return False, None
142def build_statusline_data(session_context: dict, mode: str = "compact") -> str:
143 """
144 Build complete statusline string from all data sources.
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
155 Args:
156 session_context: Context passed from Claude Code via stdin
157 mode: Display mode (compact, extended, minimal)
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"
167 # Extract Claude Code version separately for new layout
168 claude_version = session_context.get("version", "")
169 model = model_name
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"
178 # Extract output style from session context
179 output_style = session_context.get("output_style", {}).get("name", "")
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)
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 )
204 # Render statusline with labeled sections
205 renderer = StatuslineRenderer()
206 statusline = renderer.render(data, mode=mode)
208 return statusline
210 except Exception as e:
211 # Graceful degradation on any error
212 import logging
214 logging.warning(f"Statusline rendering error: {e}")
215 return ""
218def main():
219 """
220 Main entry point for Claude Code statusline.
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"
228 # Read session context from Claude Code
229 session_context = read_session_context()
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()
236 # Load configuration
237 config = StatuslineConfig()
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 )
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()
253 if statusline:
254 print(statusline, end="")
257if __name__ == "__main__":
258 main()