diff --git a/headroom/learn/analyzer.py b/headroom/learn/analyzer.py index dfe133cfe..621e2377a 100644 --- a/headroom/learn/analyzer.py +++ b/headroom/learn/analyzer.py @@ -62,9 +62,7 @@ class SessionAnalyzer: def __init__(self, model: str | None = None): self.model = model - def analyze( - self, project: ProjectInfo, sessions: list[SessionData] - ) -> AnalysisResult: + def analyze(self, project: ProjectInfo, sessions: list[SessionData]) -> AnalysisResult: """Analyze sessions and produce recommendations via LLM.""" all_calls = [tc for s in sessions for tc in s.tool_calls] failed_calls = [tc for tc in all_calls if tc.is_error] @@ -134,7 +132,9 @@ def _build_digest(project: ProjectInfo, sessions: list[SessionData]) -> str: for session in sessions: if chars_used > char_budget: - lines.append(f"... (remaining {len(sessions) - sessions.index(session)} sessions truncated)") + lines.append( + f"... (remaining {len(sessions) - sessions.index(session)} sessions truncated)" + ) break session_header = ( @@ -179,7 +179,7 @@ def _format_event(event: SessionEvent) -> str | None: if event.type == "user_message" and event.text.strip(): text = event.text.strip()[:300] - return f" [{event.msg_index}] USER: \"{text}\"" + return f' [{event.msg_index}] USER: "{text}"' if event.type == "interruption": return f" [{event.msg_index}] INTERRUPTED: {event.text[:150]}" @@ -188,7 +188,7 @@ def _format_event(event: SessionEvent) -> str | None: return ( f" [{event.msg_index}] SUBAGENT: {event.agent_tool_count} tool calls, " f"{event.agent_tokens:,} tokens, {event.agent_duration_ms / 1000:.1f}s " - f"— prompt: \"{event.agent_prompt[:100]}\"" + f'— prompt: "{event.agent_prompt[:100]}"' ) return None @@ -384,7 +384,5 @@ class FailureAnalyzer: def __init__(self) -> None: self._analyzer = SessionAnalyzer() - def analyze( - self, project: ProjectInfo, sessions: list[SessionData] - ) -> AnalysisResult: + def analyze(self, project: ProjectInfo, sessions: list[SessionData]) -> AnalysisResult: return self._analyzer.analyze(project, sessions) diff --git a/headroom/learn/scanner.py b/headroom/learn/scanner.py index 709c03486..cbe7e6aa2 100644 --- a/headroom/learn/scanner.py +++ b/headroom/learn/scanner.py @@ -240,9 +240,7 @@ class ClaudeCodeScanner(ConversationScanner): total_input_tokens += usage.get("cache_creation_input_tokens", 0) total_output_tokens += usage.get("output_tokens", 0) elif line_type == "user": - self._extract_tool_results( - d, tool_uses, tool_calls, events, msg_index, ts - ) + self._extract_tool_results(d, tool_uses, tool_calls, events, msg_index, ts) self._extract_user_events(d, events, msg_index, ts) except (OSError, UnicodeDecodeError) as e: @@ -251,12 +249,8 @@ class ClaudeCodeScanner(ConversationScanner): # Also wrap tool_calls as events for unified access for tc in tool_calls: - if not any( - e.type == "tool_call" and e.tool_call is tc for e in events - ): - events.append( - SessionEvent(type="tool_call", msg_index=tc.msg_index, tool_call=tc) - ) + if not any(e.type == "tool_call" and e.tool_call is tc for e in events): + events.append(SessionEvent(type="tool_call", msg_index=tc.msg_index, tool_call=tc)) events.sort(key=lambda e: e.msg_index) return SessionData( @@ -332,7 +326,9 @@ class ClaudeCodeScanner(ConversationScanner): ) tool_calls.append(tc) events.append( - SessionEvent(type="tool_call", msg_index=msg_index, timestamp=timestamp, tool_call=tc) + SessionEvent( + type="tool_call", msg_index=msg_index, timestamp=timestamp, tool_call=tc + ) ) # Extract subagent summary from toolUseResult metadata diff --git a/tests/test_learn/test_analyzer.py b/tests/test_learn/test_analyzer.py index ae35fee41..30b344d76 100644 --- a/tests/test_learn/test_analyzer.py +++ b/tests/test_learn/test_analyzer.py @@ -261,7 +261,7 @@ class TestSessionAnalyzer: "memory_file_rules": [], } - analyzer = SessionAnalyzer() + analyzer = SessionAnalyzer(model="test-model") sessions = [ SessionData( session_id="s1", @@ -283,7 +283,7 @@ class TestSessionAnalyzer: def test_handles_llm_failure_gracefully(self, mock_call_llm: MagicMock): mock_call_llm.side_effect = RuntimeError("API key not set") - analyzer = SessionAnalyzer() + analyzer = SessionAnalyzer(model="test-model") sessions = [ SessionData( session_id="s1", @@ -309,7 +309,7 @@ class TestSessionAnalyzer: ] sessions = [SessionData(session_id="s1", tool_calls=[tc], events=events)] - analyzer = SessionAnalyzer() + analyzer = SessionAnalyzer(model="test-model") analyzer.analyze(_project(), sessions) # Check that the digest passed to the LLM includes user message