Telegram bridge for Claude Code via tmux. Get notifications on your phone when Claude needs input, and reply directly from Telegram.
Claude Code hook fires → daemon scrapes tmux → summarizes via Haiku → Telegram
User replies in Telegram → daemon injects text into tmux → Claude Code
A long-running daemon (src/index.ts) listens for Claude Code hook events on localhost:7777. When a hook fires, it captures the tmux pane content, extracts only the new output since your last message, summarizes it with Claude Haiku, and sends a Telegram notification. Messages you send to the bot get injected back into the Claude Code tmux pane.
A thin hook script (src/hook.ts) is called by Claude Code's hook system. It just POSTs {type} to the daemon and exits — no heavy dependencies in the hook path.
src/
├── index.ts # Daemon: HTTP server + bot startup + wiring
├── hook.ts # Hook script: POSTs to daemon and exits
├── tmux.ts # Capture pane content, send keys, find Claude pane
├── conduit.ts # Summarization via Haiku with dedup
├── bot.ts # Telegram bot (grammy, polling mode)
└── auth.ts # One-time PIN authentication with per-user banning
- Node.js >= 18
- tmux
ANTHROPIC_API_KEY(can be set in environment or.envfile)
git clone https://github.com/johnmorrow/puppeteer.git && cd puppeteer && npm install && cp .env.example .envMessage @BotFather on Telegram, send /newbot, and save the token.
Send any message to your new bot, then open:
https://api.telegram.org/bot<TOKEN>/getUpdates
Find "chat":{"id":XXXXXXXX} — that's your chat ID.
Edit .env with your TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, and ANTHROPIC_API_KEY. Leave TMUX_TARGET blank for auto-detection (finds panes by window name or command containing "claude").
Add to ~/.claude/settings.json:
{
"hooks": {
"SubagentStop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "npx tsx /path/to/puppeteer/src/hook.ts waiting",
"timeout": 5
}
]
}
]
}
}Start Claude Code inside tmux:
tmux new-session -s claude
claudeIn another terminal, start the daemon:
npm startA 6-digit PIN will appear in your terminal. Send it to the bot on Telegram to authenticate.
| Command | Description |
|---|---|
| any text | Injected into the Claude Code tmux pane |
/status |
Shows current pane content |
/target session:window.pane |
Switch which tmux pane to control |
/lock |
De-authenticate the session immediately |
All tmux output is summarized via Claude Haiku before sending to Telegram. The conduit deduplicates by finding your last sent message in the tmux capture and only summarizing new output after it. Questions and prompts needing input are put in bold at the top of the message.
Two-layer authentication prevents unauthorized access:
- Chat ID — only messages from your configured Telegram chat are processed
- One-time PIN — a 6-digit PIN is generated on each daemon start and must be sent to the bot before it responds to any commands. After 5 failed attempts from a given user, that user is silently ignored. Bans reset on daemon restart. The correct PIN always works regardless of failed attempts from other users.
- Session timeout — authenticated sessions expire after 4 hours of inactivity. Each command or message resets the timer. Use
/lockto de-authenticate immediately.
| Variable | Required | Description |
|---|---|---|
ANTHROPIC_API_KEY |
Yes | For Haiku summarization |
TELEGRAM_BOT_TOKEN |
Yes | From @BotFather |
TELEGRAM_CHAT_ID |
Yes | Your personal chat ID |
TMUX_TARGET |
No | Pane target (auto-detected if blank) |
HOOK_PORT |
No | Daemon port (default: 7777) |
See SETUP.md for a step-by-step walkthrough.