Voice calling for Claude Code — phone rings when agents need you, talk through decisions, instructions route back to the right tmux session.
You're running multiple Claude Code instances across tmux panes. One stops because it needs permission or has a question. You think it's still working. Hours later, you find it blocked. Time wasted.
pingme calls your phone. Not a text — an actual phone call. A voice agent tells you exactly what's happening: "Your frontend session wants to run npm run build. Should I approve?" You say yes, it routes back. All from your phone.
npx @hrushiborhade/pingme init # Setup (Bolna API key, phone number)
npx @hrushiborhade/pingme start # Start daemon + tunnelThat's it. When Claude Code needs you, your phone rings.
Claude Code hooks → daemon (port 7331) → decision engine → Bolna API → phone call
Your voice → Bolna STT → Custom LLM (our bridge) → Claude Sonnet → response
Voice agent → Custom Functions → daemon → tmux send-keys → back to Claude Code
- Hooks capture Claude Code lifecycle events (permission requests, questions, stops)
- Decision engine evaluates: is the session genuinely blocked? Only calls for permission/question events — normal stops are silent
- Bolna AI places the call with a voice agent that has live session context
- Custom LLM bridge injects real-time session data into every voice response
- Custom Functions let the voice agent check sessions, route instructions, and approve/deny — all through your daemon
The agent doesn't just say "your session stopped." It extracts the full context:
| Event | What the agent says |
|---|---|
| Bash permission | "Frontend wants to run npm run build. Approve karun?" |
| File write | "Backend wants to create /src/utils/auth.ts. Should I approve?" |
| Question with options | "Claude is asking: which section should we build? Option 1: Call History, Option 2: Knowledge Base" |
| Unexpected stop | "Your API session stopped unexpectedly" |
Normal end_turn stops? Ignored. No annoying calls when Claude finishes its job.
pingme start # Start daemon + Cloudflare tunnel
pingme stop # Stop daemon
pingme status # Show sessions, call state, uptime
pingme call # Trigger manual outbound call
pingme name <pane> <n> # Rename a session for voice-friendly IDs
pingme logs [-f] # Tail daemon logs
pingme config # Show/edit YAML configuration
pingme init # First-time setup| Component | Purpose |
|---|---|
| Daemon Server | Express on port 7331, 9 routes, auth middleware |
| Session Registry | Tracks all Claude Code sessions with rich pending action context |
| Decision Engine | Event → action routing (call/batch/sms/ignore) |
| Call Manager | Batch timer, Bolna outbound call triggering |
| Context Bridge | /v1/chat/completions — Anthropic → OpenAI format for Bolna Custom LLM |
| Custom Functions | 3 Bolna tools: get_sessions, route_instruction, trigger_action |
| Hook System | Bash scripts using jq for safe JSON, installed in ~/.claude/settings.json |
| Cloudflare Tunnel | Free, no rate limits, exposes daemon to Bolna |
Config lives at ~/.pingme/config.yaml:
mode: voice
phone: "+91XXXXXXXXXX"
bolna:
api_key: bn-xxxxx
agent_id: xxxxx
bridge:
provider: anthropic
model: claude-haiku-4-5-20251001
max_tokens: 150
policy:
cooldown_seconds: 60
batch_window_seconds: 10
call_on:
permission: true # Session blocked — needs approval
question: true # Session blocked — needs answer
stopped: false # Normal stops are silent
task_completed: false
quiet_hours:
enabled: true
start: "23:00"
end: "07:00"
mode: smspingme speaks Hinglish by default — natural Hindi-English mix, the way Indian developers talk.
- STT: Deepgram
nova-3withlanguage: "multi"for Hindi-English code-switching - TTS: ElevenLabs
eleven_turbo_v2_5with Daksh (conversational Hindi male voice) - LLM: Your configured bridge model with Hinglish system prompt
Keyword boosting for developer terms: tmux, Claude, pane, session, approve, deny, haan, nahi.
- Bearer token auth on all sensitive routes (constant-time comparison)
- Instruction blocklist: 30+ destructive patterns (
rm -rf,sudo,git push -f, etc.) - Webhook validation: execution ID matching with age-check fallback
- tmux target validation: regex check on all pane/session identifiers
- Atomic state writes: unique temp filenames per write, no partial reads
- Queue depth limit: max 200 queued instructions
- No IP-based auth bypass: cloudflared makes everything localhost, so all routes require Bearer token
Call-triggering (enabled by default):
| Event | Description |
|---|---|
| Permission request | Agent needs your permission to proceed |
| Question (PreToolUse) | Agent is about to ask you a question |
Silent by default (configurable):
| Event | Description |
|---|---|
| Task completed | Agent finished a task |
| Agent stopped | Agent stopped running |
| Tool failed | A tool call failed |
| Notification | Agent sent a notification |
| Subagent start/stop | Subagent lifecycle |
| Session start/end | Session lifecycle |
- Node.js 18+
- Bolna AI account with API key and agent ID
- Anthropic API key for the bridge LLM
- Claude Code CLI
jq(for hook script JSON construction)tmux(for multi-session management)
git clone https://github.com/HrushiBorhade/pingme.git
cd pingme
npm install
npm run build # TypeScript compile
npm test # Vitest (42 tests)
npx tsc --noEmit # Type check onlyTypeScript, Express 5, Node 18+, Anthropic Claude Sonnet, Bolna AI, Cloudflare Tunnel, Winston, YAML config.