Coverage for src / moai_adk / statusline / alfred_detector.py: 38.30%
47 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"""
3Alfred task detector for statusline
5"""
7import json
8import logging
9from dataclasses import dataclass
10from datetime import datetime, timedelta
11from pathlib import Path
12from typing import Optional
14logger = logging.getLogger(__name__)
17@dataclass
18class AlfredTask:
19 """Alfred task information"""
21 command: Optional[str]
22 spec_id: Optional[str]
23 stage: Optional[str]
26class AlfredDetector:
27 """Detects active Alfred tasks with 1-second caching"""
29 # Configuration
30 _CACHE_TTL_SECONDS = 1
32 def __init__(self):
33 """Initialize Alfred detector"""
34 self._cache: Optional[AlfredTask] = None
35 self._cache_time: Optional[datetime] = None
36 self._cache_ttl = timedelta(seconds=self._CACHE_TTL_SECONDS)
37 self._session_state_path = (
38 Path.home() / ".moai" / "memory" / "last-session-state.json"
39 )
41 def detect_active_task(self) -> AlfredTask:
42 """
43 Detect currently active Alfred task
45 Returns:
46 AlfredTask with command and spec_id
47 """
48 # Check cache
49 if self._is_cache_valid():
50 return self._cache
52 # Read and parse session state
53 task = self._read_session_state()
54 self._update_cache(task)
55 return task
57 def _read_session_state(self) -> AlfredTask:
58 """
59 Read Alfred task from session state file
61 Returns:
62 AlfredTask from file or defaults
63 """
64 try:
65 if not self._session_state_path.exists():
66 return self._create_default_task()
68 with open(self._session_state_path, "r") as f:
69 data = json.load(f)
71 active_task = data.get("active_task")
72 if active_task is None:
73 return self._create_default_task()
75 # Parse command and spec_id
76 command = active_task.get("command")
77 spec_id = active_task.get("spec_id")
79 return AlfredTask(
80 command=command,
81 spec_id=spec_id,
82 stage=active_task.get("stage"),
83 )
85 except Exception as e:
86 logger.debug(f"Error reading Alfred task: {e}")
87 return self._create_default_task()
89 def _is_cache_valid(self) -> bool:
90 """Check if task cache is still valid"""
91 if self._cache is None or self._cache_time is None:
92 return False
93 return datetime.now() - self._cache_time < self._cache_ttl
95 def _update_cache(self, task: AlfredTask) -> None:
96 """Update task cache"""
97 self._cache = task
98 self._cache_time = datetime.now()
100 @staticmethod
101 def _create_default_task() -> AlfredTask:
102 """Create default task (no active task)"""
103 return AlfredTask(
104 command=None,
105 spec_id=None,
106 stage=None,
107 )