# Tool Call Spinner Feature Spec

## Problem
When tool calls are running, the thinking pane displays with shimmer but the tool tag section shows a static icon (tool_running = '▶'). User wants an animated spinner in the tag section during tool execution.

## Current Architecture

### Tool Rendering Flow
1. `llm_service.py` processes tool calls and passes to `message_display_service.py`
2. `message_display_service.py` calls `message_coordinator.display_message_sequence()` with tool messages
3. `message_coordinator.py` renders tools via `ModernMessageRenderer.tool_call()`
4. `ModernMessageRenderer.tool_call()` uses static icon from `C['tool_running']` (▶)

### Existing Spinner Infrastructure
- `C['spin']` = ['◐', '◓', '◑', '◒'] - classic spinner
- `C['spin_braille']` = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧'] - braille spinner (smoother)
- `ThinkingAnimationManager` in `layout.py` already animates spinners for thinking state
- Config: `terminal.spinner_frames` already exists in config.json

### Key Files
- `core/io/message_renderer.py:849` - `ModernMessageRenderer.tool_call()` method
- `core/io/message_coordinator.py:204-220` - Tool message rendering in coordinator
- `core/ui/design_system/components.py:173-219` - C dict with spinner frames
- `core/io/layout.py:89-162` - ThinkingAnimationManager with spinner logic
- `core/commands/config.json:4-13` - terminal.spinner_frames config
- `core/config/loader.py:372` - Default spinner frames

## Implementation Plan

### Phase 1: Config Extension (Agent 1)
Add new config options to `core/commands/config.json` and `core/config/loader.py`:
```json
"terminal": {
  "tool_spinner_enabled": true,
  "tool_spinner_style": "braille",  // "braille" | "classic" | "custom"
  "tool_spinner_frames": null,       // custom frames array if style="custom"
  "tool_spinner_speed": 100          // ms between frames
}
```

### Phase 2: Spinner State Manager (Agent 2)
Create `core/io/tool_spinner.py`:
- `ToolSpinnerManager` class
- Maintains frame index and timing
- `get_current_frame()` returns current spinner character
- `advance_frame()` moves to next frame
- `get_spinner_icon(style, config)` returns appropriate spinner frame
- Integrates with config system to read spinner_style/frames

### Phase 3: Update Tool Rendering (Agent 3)
Modify `core/io/message_renderer.py`:
- Import `ToolSpinnerManager` or add inline spinner logic
- Update `ModernMessageRenderer.tool_call()`:
  - When status="running", use animated spinner frame instead of static `C['tool_running']`
  - Use config to determine spinner style
  - Return current frame from spinner sequence

### Phase 4: Animation Loop Integration (Agent 4)
The tool spinner needs to animate. Two approaches:
- **Option A**: Use existing render loop in `terminal_renderer.render_active_area()`
  - Add tool spinner state tracking
  - Re-render tool calls during active thinking state
- **Option B**: Leverage existing ThinkingAnimationManager pattern
  - Tool calls display once (no re-render needed for finished tools)
  - Only running tools need animation - handled by render loop

Implement by modifying `core/io/terminal_renderer.py`:
- Track when tool calls are in progress
- During tool execution, include spinner update in render cycle

### Phase 5: Config UI Integration (Agent 5)
Add `/config` menu support for spinner settings:
- Modify config modal to include tool spinner options
- Dropdown for spinner style (braille/classic/custom)
- Toggle for enabled/disabled
- Preview of selected spinner in config UI

## File Changes Summary

| File | Changes |
|------|---------|
| `core/commands/config.json` | Add tool_spinner_* config keys |
| `core/config/loader.py` | Add defaults for new config keys |
| `core/io/tool_spinner.py` | NEW - ToolSpinnerManager class |
| `core/io/message_renderer.py` | Update tool_call() to use spinner |
| `core/io/terminal_renderer.py` | Integrate spinner animation in render loop |
| `core/io/message_coordinator.py` | Pass spinner state to tool rendering |

## Success Criteria
1. Tool calls show animated spinner in tag section when running
2. Spinner style configurable via /config (braille, classic, custom)
3. Spinner can be enabled/disabled via config
4. Existing thinking shimmer continues to work
5. Completed tool calls show static success/error icons (no regression)

## Notes
- The spinner should ONLY animate for running tools, not completed ones
- Keep frame rate reasonable (100ms default) to avoid CPU overhead
- Respect terminal.render_fps for animation timing
