dialnow
v0.2.0
Published
Give your Claude Code a phone number. Talk to it, and it calls you back when the work's done.
Readme
dial — give your Claude Code a phone number
Talk to your Claude Code, and have it call you back when the work's done. Type as
usual, or talk; Claude answers out loud in Dial's voice. After dial login, a phone
call to Dial routes into your session — so you can hand off a task, hang up, and get
rung back with the result. It does not modify or fork Claude Code — it runs the
real claude inside a pseudo-terminal and layers voice + a bridge on top.
┌──────────────── dial (wrapper) ───────────────┐
│ your keyboard ──────────────► the real │
│ Ctrl-T / hands-free mic ────► `claude` (pty) │
│ │
│ claude reply ── Stop hook ──► daemon ── TTS ──► 🔊
│ └── <callme> ─► Dial rings your phone 📞
│ a Dial phone call ◄──► bridge ◄──► this session
└────────────────────────────────────────────────┘- Voice in — Ctrl-T or hands-free; your speech streams to Deepgram live STT and is typed into the same session as if you'd typed it.
- Voice out — a
Stophook sends each reply to a tiny local daemon that speaks it. - Bridge — after
dial login, a phone call to Dial reaches this session, and Claude can ring you back when a task finishes.
Requirements
- macOS (playback uses
afplay) - Node ≥ 22 (the bridge uses the built-in
WebSocket) ffmpeg—brew install ffmpeg(microphone capture)DEEPGRAM_API_KEYin your environment (or a.envin the cwd)claudeon your PATH
Setup
cd voice-cli
npm install # builds node-pty
npm link # makes `dial` global
dial login # connect your phone (opens the browser, sign in)Use
Instead of claude, run:
dial # shows a quick menu: new / continue / resume / hands-free
dial --resume # or pass claude flags straight through
dial "fix the build" # a prompt goes straight to a new session- Talk: press Ctrl-T, speak, Ctrl-T to send. (Talk when the prompt is empty.)
- Hands-free: press Ctrl-G (or launch with
--handsfree, or say "go hands-free" into a Ctrl-T recording). The mic stays open — just talk; a ~1.2 s pause sends your words. The mic is muted while Dial speaks, so it never hears itself. Leave with Ctrl-G or by saying "hands-free off" / "stop listening". - Type: just type — Claude is completely normal underneath.
- Barge-in: pressing Ctrl-T while it's talking cuts the speech off (and in push-to-talk mode starts recording). Esc just silences it.
- Replies are spoken short: the screen keeps Claude's full answer, but only a 1–2 sentence conversational summary is read aloud (Claude appends it in a
<say>…</say>block; if it doesn't, we speak the first sentence or two).
Check your setup any time:
dial-voice doctor
dial-voice mic # live mic test — prints what it hears as you speakConfig (env)
| Var | Default | What |
|-----|---------|------|
| DEEPGRAM_API_KEY | — | required |
| DIAL_VOICE_MODEL | aura-2-… (from DEEPGRAM_TTS_MODEL) | the spoken voice |
| DEEPGRAM_MODEL | nova-3 | STT model |
| DIAL_VOICE_PORT | 8788 | local speak daemon |
| DIAL_MIC | auto-detected | avfoundation audio-device index |
| DIAL_MAX_SECONDS | 30 | max single push-to-talk utterance |
| DIAL_PTT | 20 (Ctrl-T) | push-to-talk key byte |
| DIAL_HF_KEY | 7 (Ctrl-G) | hands-free toggle key byte |
Find your mic index if 0 is wrong:
ffmpeg -f avfoundation -list_devices true -i "" 2>&1 | grep -A20 audioPhone bridge (call your Claude from anywhere)
With the bridge configured, this session registers with the Dial server as your "local Claude." On a Dial call (iOS app / phone), say "talk to my Claude" — the server routes your words here as text, Claude answers, and the reply is spoken into the call. Audio never leaves the call; your Mac exchanges text only.
DIAL_USER_ID=<your dial user id> DIAL_BRIDGE_SECRET=<server DIAL_INTERNAL_SECRET> dial-voice(or put both in your .env). dial-voice doctor shows the bridge status. While a
phone turn is pending, the reply goes to the call instead of the local speakers.
Notes
- First run prompts for microphone permission (macOS gives it to your terminal app).
dial-voice initinstalls the hooks globally instead (opt-in); the default never touches~/.claude/settings.json.- Set
DIAL_VOICE_LOG=/tmp/hook.logto debug the speak hook.
