Skip to content

About

Voice-control your Claude Code REPL via Alexa — speak commands, get push notifications when done

Resources

Stars

22 stars

Watchers

0 watching

Forks

Latest commit

 

History

17 Commits

Folders and files

Repository files navigation

Alexa Claude Bridge

Voice-control your Claude Code REPL from across the room

Python 3.13+ Windows 11 AWS Alexa Version 0.3.0


"Alexa, ask my code assistant to run the tests"

Alexa sends the command to your Claude Code terminal. Claude runs it, then pushes the result to your phone via ntfy.sh.


How It Works

   You                    Cloud                        Your PC
  ------            ----------------              ----------------

  "Alexa,              Alexa Skill                  Background
   run the    ───>     (Lambda)       ───>          Daemon
   tests"              Sends to SQS                 Polls SQS
                                                       │
                                                       ▼
                       ntfy.sh        <───     Claude Code REPL
                       + DynamoDB              Executes command
                           │                   Sends result back
                           ▼
                      Phone push:
                      "All 42 tests
                       passed"

Architecture

flowchart LR
    subgraph User["fa:fa-user You"]
        Voice["fa:fa-microphone Voice Command"]
    end

    subgraph AWS["fa:fa-cloud AWS"]
        Alexa["fa:fa-assistive-listening-systems Alexa Skill"]
        Lambda["fa:fa-bolt Lambda"]
        SQS["fa:fa-layer-group SQS Queue"]
        DynamoDB["fa:fa-database DynamoDB"]
    end

    subgraph PC["fa:fa-desktop Your PC"]
        Daemon["fa:fa-sync Daemon"]
        Keyboard["fa:fa-keyboard Keyboard Injection"]
        Claude["fa:fa-terminal Claude Code"]
    end

    subgraph Notify["fa:fa-bell Notification"]
        Ntfy["fa:fa-mobile-alt ntfy.sh"]
    end

    Voice -->|"run the tests"| Alexa
    Alexa --> Lambda
    Lambda -->|SendMessage| SQS
    SQS -->|Long Poll| Daemon
    Daemon --> Keyboard
    Keyboard -->|Ctrl+V, Enter| Claude
    Claude -->|notify script| Ntfy
    Claude -->|store result| DynamoDB
    Ntfy -->|push notification| Voice
    DynamoDB -->|"what happened?"| Lambda

    style Voice fill:#8B5CF6,stroke:#6D28D9,color:#fff
    style Alexa fill:#00CAFF,stroke:#0099CC,color:#000
    style Lambda fill:#FF9900,stroke:#CC7A00,color:#000
    style SQS fill:#FF9900,stroke:#CC7A00,color:#000
    style DynamoDB fill:#FF9900,stroke:#CC7A00,color:#000
    style Daemon fill:#3776AB,stroke:#2B5F8A,color:#fff
    style Keyboard fill:#3776AB,stroke:#2B5F8A,color:#fff
    style Claude fill:#D97706,stroke:#B45309,color:#fff
    style Ntfy fill:#10B981,stroke:#059669,color:#fff
Loading

Step by Step

sequenceDiagram
    actor User
    participant Alexa
    participant Lambda
    participant SQS
    participant Daemon
    participant Claude as Claude Code

    User->>Alexa: "Ask my code assistant<br/>to run the tests"
    Alexa->>Lambda: RunCommandIntent<br/>command="run the tests"
    Lambda->>SQS: SendMessage
    Lambda-->>Alexa: "Running: run the tests"
    Alexa-->>User: "Running: run the tests.<br/>I'll notify you when done."

    loop Every 20s
        Daemon->>SQS: ReceiveMessage (long poll)
    end

    SQS-->>Daemon: Message received
    Daemon->>Claude: Paste + Enter<br/>(via Windows API)
    Daemon->>SQS: DeleteMessage

    Note over Claude: Executes command...

    Claude->>User: ntfy push: "All 42 tests passed"

    opt User asks later
        User->>Alexa: "What happened?"
        Alexa->>Lambda: GetResultIntent
        Lambda->>DynamoDB: Query latest
        DynamoDB-->>Lambda: result
        Lambda-->>Alexa: "All 42 tests passed"
        Alexa-->>User: speaks result
    end
Loading

Features

Feature Description
Voice commands Speak naturally — "run the tests", "check git status", "commit my changes"
Push notifications Get instant phone notifications via ntfy.sh when Claude finishes (Alexa-originated commands only)
Result recall Ask "Alexa, what happened?" anytime to hear the latest result
Window targeting Matches your terminal by window class — works even when Claude Code changes its title
Auto-notification Stop hook in Claude Code fires automatically — no manual CLAUDE.md rules needed
Crash resilient Daemon auto-restarts with exponential backoff; clipboard retries; poison messages can't crash-loop
Reliable input Uses Win32 SendInput API with correct 64-bit struct layout and AttachThreadInput for focus; PostMessage fallback
Zero dependencies on Windows Keyboard injection uses raw ctypes — no pywinauto, no COM
One-command setup make setup provisions all AWS resources and configures locally
Clean teardown make teardown removes everything from AWS and your machine

Prerequisites


Quick Start

1. Clone & Setup

git clone https://github.com/your-username/alexa-claude-bridge.git
cd alexa-claude-bridge
make setup

This runs three things:

  • make infra — Creates SQS queue, DynamoDB table, and IAM role in AWS
  • make install — Installs Python dependencies via uv sync
  • make bridge-install — Creates ~/.claude-bridge/ config and installs a Stop hook in ~/.claude/settings.json for auto-notifications

2. Create the Alexa Skill

  1. Go to Alexa Developer Console
  2. Create Skill → Custom → Provision your own
  3. Copy the contents of skill/interaction_model.json into the JSON Editor under Interaction Model
  4. Set the Endpoint to your Lambda function ARN
  5. Build the model
  6. Save your Skill ID:
    echo "amzn1.ask.skill.xxxxxxxx" > .skill-id

3. Deploy Lambda

make deploy

4. Configure Push Notifications

  1. Install the ntfy app on your phone (Android / iOS)
  2. Pick a random topic name (e.g., claude-bridge-a1b2c3d4) and add it to ~/.claude-bridge/config.json:
    "ntfy_topic": "claude-bridge-a1b2c3d4"
  3. Open the ntfy app → tap + → subscribe to the same topic name

Notifications are sent only for Alexa-originated commands, not when you type directly into Claude Code.

5. Start the Bridge

make start

Now talk to Alexa:

"Alexa, ask my code assistant to run the tests"


Voice Commands

Sending Commands

Say this... Claude receives...
"Alexa, ask my code assistant to run the tests" run the tests
"Alexa, tell my code assistant to check git status" check git status
"Alexa, ask my code assistant to commit my changes" commit my changes
"Alexa, tell my code assistant to explain the auth module" explain the auth module

You can use natural phrasing — "run", "execute", "do", "check", "try", "launch", "go ahead and", etc.

Checking Results

Say this...
"Alexa, ask my code assistant what happened"
"Alexa, ask my code assistant is it done"
"Alexa, ask my code assistant what's the result"

Configuration

~/.claude-bridge/config.json

{
  // Required
  "command_queue_url": "https://sqs.us-east-1.amazonaws.com/.../claude-bridge-commands",
  "aws_region": "us-east-1",

  // Result storage
  "results_table": "claude-bridge-results",

  // Push notifications (recommended)
  "ntfy_topic": "claude-bridge-your-random-id",  // Subscribe to this in the ntfy app
  "ntfy_server": "https://ntfy.sh",               // Default; change if self-hosting

  // Alexa voice notifications (optional fallback — get code from notifymyecho.com)
  "notify_me_access_code": "",

  // Window targeting — how the daemon finds your Claude Code terminal
  "window_class": "CASCADIA_HOSTING_WINDOW_CLASS",  // Windows Terminal
  "window_title": null,                               // Optional title filter
  "exclude_titles": ["Visual Studio Code"]            // Windows to skip
}

Window Matching

The daemon finds your terminal by window class (stable) rather than title (which changes with every Claude Code task).

Terminal Window Class
Windows Terminal CASCADIA_HOSTING_WINDOW_CLASS
cmd.exe ConsoleWindowClass
ConEmu / Cmder VirtualConsoleClass

If you use multiple Windows Terminal instances, add window_title to narrow down:

"window_class": "CASCADIA_HOSTING_WINDOW_CLASS",
"window_title": "some-unique-text"

Makefile Reference

Workflow

make setup      ─── First-time: AWS infra + Python deps + bridge config
make deploy     ─── Package Lambda + deploy (+ update Alexa skill if .skill-id exists)
make start      ─── Activate bridge (start daemon)
make stop       ─── Deactivate bridge (stop daemon)
make status     ─── Show bridge status
make logs       ─── Tail daemon log
make teardown   ─── Destroy all AWS resources (irreversible)

Testing & Debugging

# Send a test command without Alexa
make test-send CMD="hello world"

# Check the latest result in DynamoDB
make test-result

# Check SQS queue depth
make queue-status

# Tail Lambda CloudWatch logs
make lambda-logs

# Tail local daemon log
make logs

Project Structure

alexa-claude-bridge/
├── src/alexa_claude_bridge/
│   ├── bridge.py          # CLI — install, start, stop, status, notify, logs
│   ├── config.py          # Shared path constants (single source of truth)
│   ├── daemon.py          # Background SQS poller with auto-restart
│   ├── keyboard.py        # Window targeting + keyboard injection (SendInput / PostMessage)
│   └── notifier.py        # Alexa Notify Me API client
├── lambda/
│   └── handler.py         # Alexa skill Lambda — intent routing, SQS/DynamoDB
├── skill/
│   └── interaction_model.json   # Alexa interaction model (intents + slots)
├── Makefile               # Full workflow automation
└── pyproject.toml         # Package metadata + dependencies

Troubleshooting

Problem Cause Fix
Daemon stops immediately Clipboard access violation (another app holds clipboard) Fixed — clipboard retry with backoff; daemon auto-restarts up to 5 times
Text in clipboard but not pasted SendInput struct was wrong size on 64-bit Windows Fixed — INPUT struct now 40 bytes (includes MOUSEINPUT for correct union size)
Commands go to VS Code VS Code title contains the match string Set window_class in config (default now targets Windows Terminal)
"Window not found" Wrong window class or Claude Code not running Run make status, check make logs, verify terminal is open
Paste works but Enter doesn't fire Timing too tight between paste and Enter Fixed — increased delays; AttachThreadInput for reliable focus
Commands repeat / crash loop Poison SQS message not deleted on failure Fixed — finally block always deletes messages
Alexa says "I don't know that one" Skill not built or invocation name mismatch Rebuild skill in Alexa Developer Console
No push notification ntfy_topic not set or app not subscribed Add topic to config, subscribe in ntfy app
Notifications on every command pending-notify not being cleaned up Stop hook handles cleanup automatically

How Notifications Work

During alexa-bridge install, the bridge adds a Stop hook to ~/.claude/settings.json. This hook runs automatically every time Claude Code finishes a response:

# The hook (added automatically — you don't need to touch this)
bash -c '[ -f ~/.claude-bridge/pending-notify ] && ~/.claude-bridge/notify "Task completed" && rm ~/.claude-bridge/pending-notify; true'

The pending-notify file is created by the daemon only when it injects an Alexa command. This means:

  • Alexa command → daemon creates pending-notify → Claude completes → Stop hook fires notification → removes file
  • Direct typing → no pending-notify → hook no-ops instantly

This is more reliable than the previous approach (a CLAUDE.md instruction) because hooks execute automatically at the shell level — Claude doesn't need to remember to check.

Both ntfy and Notify Me can run in parallel — configure either or both in config.json.


AWS Resources Created

Resource Name Purpose
SQS Queue claude-bridge-commands Command pipeline (Alexa → your PC)
DynamoDB Table claude-bridge-results Result storage (your PC → Alexa)
Lambda Function claude-bridge-alexa Alexa skill backend
IAM Role claude-bridge-lambda-role Lambda execution permissions

All resources are created in your configured AWS region (default: us-east-1). Run make teardown to remove everything.


Future Work

Claude Code Channels (native MCP integration)

The biggest architectural improvement on the horizon is migrating from keyboard injection to Claude Code Channels, a new feature that lets MCP servers push events directly into a Claude Code session over stdio.

What changes: The most fragile part of this project, keyboard.py (window class lookup, focus stealing, clipboard manipulation, ctypes struct sizing), gets replaced by a single mcp.notification() call. Commands arrive as structured <channel> tags with metadata instead of pasted text.

What stays: The Alexa skill, Lambda, SQS queue, and all notification channels (ntfy.sh, Notify Me, DynamoDB) remain unchanged. The channel server would poll SQS the same way the current daemon does, just with a different delivery mechanism.

Current:    SQS → Daemon → keyboard injection → Claude terminal window
Future:     SQS → Channel MCP server → Claude Code session (native)

A channel server also enables a reply tool, so Claude can send results back directly through the channel instead of relying on the Stop hook + flag file chain. Permission relay would let you approve or deny Claude's tool calls from your phone.

Why not now: Channels are in research preview (requires Claude Code v2.1.80+, claude.ai login, and a --dangerously-load-development-channels flag for custom channels). Once the feature stabilizes and drops these requirements, the channel server can be built as an alternative transport alongside the current keyboard injection approach.


About

Voice-control your Claude Code REPL via Alexa — speak commands, get push notifications when done

Resources

Stars

22 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages