feat: Voice Pilot smart summary via Claude API

Voice Pilot Smart Summary via Claude API

Problem

The “more” command reads raw terminal output via TTS, which is unintelligible — ANSI artifacts, code snippets, and log noise read aloud are useless.

Solution

When user says “more”, send the last ~50 lines of tmux pane output to Claude API (Haiku for speed/cost), get back a one-sentence summary + a question about what to do next, read that via TTS.

Change

One function change in scripts/voice-pilot/voice_pilot.py:

Current dispatch_command “more” behavior (lines 179-187):

if word == "more":
    output = get_pane_output(event["pane_id"], lines=15)
    cleaned = strip_ansi(output)[:MAX_TTS_CHARS]
    if cleaned:
        say(cleaned)
    ...

New behavior:

if word == "more":
    output = get_pane_output(event["pane_id"], lines=50)
    cleaned = strip_ansi(output)
    summary = summarize_with_claude(cleaned, event["project"])
    say(summary)
    ...

New function summarize_with_claude:

def summarize_with_claude(terminal_output, project_name):
    """Send terminal output to Claude Haiku for a spoken summary."""
    prompt = (
        "You are summarizing a terminal session for someone listening via text-to-speech. "
        "The project is: {project_name}\n\n"
        "Terminal output:\n{terminal_output}\n\n"
        "Respond with exactly two sentences:\n"
        "1. A brief summary of what just happened\n"
        "2. A question asking what they'd like to do next\n\n"
        "Keep it conversational and concise — this will be read aloud."
    )

    # Call Anthropic API via urllib (no pip deps)
    body = json.dumps({
        "model": "claude-haiku-4-5-20251001",
        "max_tokens": 150,
        "messages": [{"role": "user", "content": prompt}]
    }).encode()

    req = urllib.request.Request(
        "https://api.anthropic.com/v1/messages",
        data=body,
        headers={
            "x-api-key": os.environ["ANTHROPIC_API_KEY"],
            "anthropic-version": "2023-06-01",
            "content-type": "application/json",
        },
    )

    with urllib.request.urlopen(req, timeout=15) as resp:
        result = json.loads(resp.read())
        return result["content"][0]["text"]

Acceptance Criteria

  • “more” sends last 50 lines (ANSI-stripped) to Claude Haiku
  • Response is a one-sentence summary + one question, read via TTS
  • Startup validates ANTHROPIC_API_KEY env var
  • API errors fall back to raw output (current behavior)
  • Zero new dependencies (urllib only)

Dependencies

  • ANTHROPIC_API_KEY env var (from console.anthropic.com)
  • Claude Haiku 4.5 for speed and low cost (~$0.001 per summary)