English | 中文文档
Forked from hanxiao/claudecode-telegram
Telegram bot bridge for Claude Code. Full bidirectional sync between desktop and mobile.
- Bidirectional sync — Desktop Claude responses + user input synced to Telegram via hooks; Telegram messages injected into desktop Claude via bridge
- Auto-binding — First Telegram message auto-binds the session, no manual
/bindneeded - Cross-project switching — Browse projects from Telegram, bridge handles
cd+ Claude restart automatically - Three-state sync — Active / Paused / Terminated, with local logs always recording regardless of sync state
- tmux integration — Mouse scrollback, 10000-line history, reliable session tracking
- Remote permission — When Claude requests tool permission, formatted info is forwarded to Telegram; CC dialog options (plan approval, questions) auto-forwarded as clickable buttons
- Local logs — Auto-summarized Claude code responses and user inputs daily for local session management, with auto-cleanup every 30 days
- Local alarm — Plays different sounds for task completion (
done.mp3) and user action needed (alert.mp3), so you never miss it while in another window (install independently)
- macOS, Linux, or Windows (WSL)
- Claude Code CLI
- Python 3.10+
- Open Telegram, find @BotFather
- Send
/newbot, follow the prompts - Copy the token (format:
123456789:ABC-DEF...) - Click your bot's @username to open the chat, press Start
git clone https://github.com/oyljyf/claudecode-remote
cd claudecode-remote
./scripts/install.sh YOUR_TELEGRAM_BOT_TOKENThis auto-installs dependencies (tmux, cloudflared, jq, uv), creates a Python venv, configures hooks, and saves the token.
source ~/.zshrc # or restart terminal
./scripts/start.sh --newThis creates a tmux session, starts Claude, launches the bridge + cloudflare tunnel, sets the Telegram webhook, and attaches you to the Claude terminal.
./scripts/install.sh --check./scripts/install.sh --force YOUR_TELEGRAM_BOT_TOKENIf you only need to install/update hooks (e.g. after a code update):
./scripts/start.sh --setup-hook
./scripts/start.sh # restart bridge to register new commands- Session — Each Claude conversation has a unique ID, stored in
~/.claude/projects/<project>/<id>.jsonl - Shared terminal — Desktop and Telegram share the same tmux terminal. Messages from either side are visible to both.
- Sync direction — Desktop-to-Telegram via hooks (automatic); Telegram-to-desktop via bridge
- Make sure the bridge is running on desktop (
./scripts/start.shor--new) - Open the Telegram bot chat, send any message
- Bridge auto-detects and binds the current session
/resume Show recent sessions, pick one
/continue Resume the most recent session
/projects Browse projects → pick session or create new
Cross-project switches auto-handle cd + Claude restart (1-2 second delay).
/stop Pause sync (logs still recorded, desktop Claude unaffected)
/escape Interrupt Claude (like pressing Escape), sync stays active
/terminate Fully disconnect, need /start to reconnect
Resume with /start, /resume, or /continue — all clear the paused/terminated state.
./scripts/start.sh # start bridge (tmux must exist)
./scripts/start.sh --new # create tmux + Claude + bridge
./scripts/start.sh --new <path> # start in a specific project directory
./scripts/start.sh --attach # attach to tmux (with session picker)
./scripts/start.sh --detach # detach from tmux (run from another terminal)
./scripts/start.sh --view # view recent Claude output without attaching
./scripts/start.sh --terminate # stop all processes and disable sync
./scripts/start.sh --stop-sync # pause sync locally (no bridge needed)
./scripts/start.sh --resume-sync # resume sync locallySince Claude Code captures most keybindings, use
--detachfrom another terminal instead of the tmux prefix key.
| State | Hook Behavior | Bridge Behavior |
|---|---|---|
| Active | Send to Telegram + log | Forward messages |
Paused (/stop) |
Log only | Reject with "paused" hint |
Terminated (/terminate) |
Log only | Reject with "terminated" hint |
Logs always record to ~/.claude/logs/ regardless of sync state, and rentain for 30 days.
| Command | Description |
|---|---|
/start |
Start new Claude session |
/stop |
Pause sync (recover with /start, /resume, /continue) |
/escape |
Interrupt Claude (send Escape key) |
/terminate |
Disconnect completely (need /start to reconnect) |
/resume |
Resume session (shows picker) |
/continue |
Continue most recent session |
/projects |
Browse projects and sessions |
/bind |
Bind current session to this chat |
/clear |
Clear conversation |
/status |
Check tmux, sync, and binding status |
/loop <prompt> |
Ralph Loop: auto-iteration mode |
/report |
Token usage report with cost estimation, bars, trend |
When Claude requests tool permission, the PermissionRequest hook formats the request by tool type and sends it to Telegram with an inline keyboard:
- Edit / Write: shows file path + Yes / Yes to all / No buttons
- Bash: shows command (truncated) + Yes / Yes to all / No buttons
- AskUserQuestion: shows question + option buttons
- Other tools: shows tool name + Yes / Yes to all / No buttons
Tap a button to select — the bridge navigates the CC terminal dialog via Down+Enter keystrokes.
Note: This only works when Claude is started without
--dangerously-skip-permissions. The defaultstart.sh --newuses skip-permissions, so permission hooks won't trigger in that mode.
How it works:
Claude needs permission → PermissionRequest hook → formats tool info + buttons to Telegram
→ User taps button → bridge sends Down+Enter to tmux → CC selects option
Setup: ./scripts/start.sh --setup-hook (included automatically with other hooks).
Keep logging even stop or terminate telegram sync.
| Log Type | Path | Description |
|---|---|---|
| Chat logs | ~/.claude/logs/cc_MMDDYYYY.log |
Daily conversations |
| Debug log | ~/.claude/logs/debug.log |
Hook debug info |
cat ~/.claude/logs/cc_$(date +%m%d%Y).log # today's chat log
tail -f ~/.claude/logs/debug.log # live debug log
bash ./scripts/clean-logs.sh # clean logs older than 30 days as default
bash ./scripts/clean-logs.sh 7 # clean logs older than 7 daysDifferent sounds play depending on the event, so you can tell what happened without switching windows.
| Hook Event | Sound File | When it fires |
|---|---|---|
Stop |
done.mp3 |
Claude finishes a response (task done or needs input) |
Notification |
alert.mp3 |
Claude asks a question or requests tool permission |
Place your sound files in the project's sounds/ directory (done.mp3 and alert.mp3), then run ./scripts/start.sh --setup-hook to install them to ~/.claude/sounds/.
touch ~/.claude/alarm_disabled # disable alarm
rm ~/.claude/alarm_disabled # enable alarm
export ALARM_VOLUME=0.3 # adjust volume (0.0-1.0, default 0.5)Sound configuration in config.env:
DEFAULT_SOUND_DIR=~/.claude/sounds
DEFAULT_SOUND_DONE=done.mp3
DEFAULT_SOUND_ALERT=alert.mp3
DEFAULT_ALARM_VOLUME=0.5Override at runtime via environment variables: SOUND_DIR, SOUND_DONE, SOUND_ALERT, ALARM_VOLUME.
./scripts/uninstall.sh # interactive uninstall (choose components)
./scripts/uninstall.sh --telegram # remove only Telegram hooks and bridge
./scripts/uninstall.sh --alarm # remove only alarm hook and sounds
./scripts/uninstall.sh --all # remove everything including logs
./scripts/uninstall.sh --keep-deps # keep system dependencies (tmux, cloudflared, jq)
./scripts/uninstall.sh --force # skip confirmation promptsWhat gets removed:
| Component | Files removed |
|---|---|
| Telegram | hooks (send-*-telegram.sh, handle-permission.sh), bridge state files, env vars, webhook, processes, .venv |
| Alarm | hook (play-alarm.sh), ~/.claude/sounds/ (done.mp3, alert.mp3), alarm_disabled |
| Shared | hooks/lib/ (when both removed), settings.json hook config |
What stays: Claude Code sessions (~/.claude/projects/), Claude Code itself, system deps (unless confirmed).
| Variable | Description | Default |
|---|---|---|
TELEGRAM_BOT_TOKEN |
Bot token (required) | - |
TMUX_SESSION |
tmux session name | claude |
PORT |
Bridge port | 8080 |
ALARM_VOLUME |
Alarm sound volume | 0.5 |
ALARM_ENABLED |
Enable/disable alarm | true |
Custom port:
export PORT=9090
./scripts/start.shAfter restart, bridge, cloudflared tunnel, and Telegram webhook will automatically use the new port.
Telegram messages not reaching desktop:
- Check bridge is running:
./scripts/start.sh - Check tmux session exists:
./scripts/start.sh --new - Send
/statusto check state
Desktop responses not reaching Telegram:
- Check hooks:
./scripts/start.sh --setup-hook - Check token:
grep TELEGRAM_BOT_TOKEN ~/.claude/hooks/lib/common.sh - Check debug log:
tail -20 ~/.claude/logs/debug.log
Connection unstable: Restart bridge: ./scripts/start.sh
tmux can't scroll: Old session — recreate with ./scripts/start.sh --new
| Component | Technology |
|---|---|
| Bridge Server | Python (stdlib only) |
| Tunnel | Cloudflare Quick Tunnels |
| Session Management | tmux |
| Hooks | Claude Code Stop / UserPromptSubmit / Notification / PermissionRequest hooks |
| Bot API | Telegram Bot API |
- Installation Guide — Install, hooks, and manual setup (Chinese)
- Startup Guide — Startup options, tmux control, and troubleshooting (Chinese)
- Usage Guide — Scenario-based usage for Telegram and desktop (English)
Same as upstream. See original repository for details.
