Telegram bot interface for coding agents (Claude Code + OpenAI Codex) on a VPS. Message the bot from any device, it runs the active agent in your project directories and streams back results. Switch providers at runtime with /provider.
- Multi-provider — switch between Claude Code and OpenAI Codex at runtime via
/provider; sessions and capabilities are tracked per provider - Project switching — select any project directory via inline keyboard, auto-unpins old messages
- Streaming responses — real-time draft messages with edit-based fallback
- Session continuity — follow-up messages continue the same conversation (per provider)
- Message queuing — messages sent while the agent is busy are queued and processed in order
- Thinking stream — the agent's thinking/reasoning content streamed in a separate message
- Branch awareness — current git branch and open PRs shown in
/statusand response footers - Voice messages — voice notes transcribed via Groq Whisper, then sent to the agent as text
- Long response splitting — auto-splits messages exceeding Telegram's 4000 char limit
- MarkdownV2 rendering — formatted output with plain text fallback
- Plan mode interception — works with both providers (Claude's
ExitPlanMode/.claude/plans/, Codex's.codex/plans/convention); the plan is presented for approval with options to execute (new/resume session), modify with feedback, or cancel - Capability-aware UI — cost/turns footer, thinking panel, and subagent messages adapt to what the active provider supports (e.g. Codex shows duration only)
- Compose mode — collect multiple messages (text, voice, forwarded, files, photos) into a single prompt with
/composeand/send - Access control — single authorized user via Telegram user ID
- Bun runtime
- Claude Code CLI installed and authenticated (
claude login) - Codex CLI installed and authenticated (
codex login) — optional, only if you want the Codex provider - Groq API key — for voice message transcription
Agent auth is CLI-managed: log into each CLI once on the host. The bot handles no OpenAI/Anthropic API keys — there is no API-key env var for either provider.
Note: This bot uses
claude -p(programmatic usage). Starting June 15, 2026, paid Claude plans include a dedicated monthly credit for programmatic usage (claude -p, Agent SDK, GitHub Actions). Usage draws from this credit first, then from optional usage credits at API rates. See Anthropic's announcement for details and credit amounts by plan.
- Message @BotFather on Telegram
/newbot→ follow prompts → copy the bot token
Forward any message to @userinfobot — it replies with your user ID.
cp .env.example .envEdit .env:
BOT_TOKEN=your_bot_token_here
ALLOWED_USER_ID=your_telegram_user_id
PROJECTS_DIR=/home/agent/projects
GROQ_API_KEY=your_groq_api_key
No OpenAI or Anthropic API key goes in .env — agent auth is handled by the CLIs themselves (see next step).
Log into each coding-agent CLI once on the host (these persist for the bot):
claude login # Claude Code
codex login # Codex — optional, only if you want the Codex providercodex login supports ChatGPT (Plus/Pro/Team) or an OpenAI API key, via a browser-based flow. On startup the bot runs a non-blocking codex login status check and only warns if Codex isn't logged in — Claude-only hosts still boot fine.
bun install
bun run src/index.tsFor development, you can use bun run dev for auto-reload on changes, or run in a tmux session. However, tmux is not suitable for production — it won't auto-restart on crashes or survive reboots. Use a systemd service for persistent deployments.
To keep the bot running across reboots and auto-restart on crashes, set up a systemd user service.
A template service file is included in the repo. Edit telegram-claude.service to set the correct paths for your system:
WorkingDirectory— path to this repoEnvironmentFile— path to your.envfileExecStart— absolute path tobunEnvironment=PATH=...— must include directories containing thebun,claude, and (if used)codexbinaries
Then symlink and enable it:
# edit paths in the service file
vim telegram-claude.service
# symlink to systemd user directory
mkdir -p ~/.config/systemd/user
ln -sf "$(pwd)/telegram-claude.service" ~/.config/systemd/user/telegram-claude.service
# allow service to run without an active login session
loginctl enable-linger $USER
# enable and start
systemctl --user daemon-reload
systemctl --user enable telegram-claude
systemctl --user start telegram-claudeUseful commands:
systemctl --user status telegram-claude # check status
journalctl --user -u telegram-claude -f # follow logs
systemctl --user restart telegram-claude # restart
systemctl --user stop telegram-claude # stop| Command | Description |
|---|---|
/projects |
Select active project directory |
/provider |
Switch coding agent provider (Claude Code / Codex) |
/history |
Browse and resume past sessions |
/stop |
Kill running agent process |
/status |
Show active project, provider & process state |
/new |
Clear session, start fresh conversation |
/compose |
Start collecting messages into a batch |
/send |
Send all composed messages as one prompt |
/cancel |
Cancel compose mode, discard messages |
/branch |
Show current git branch |
/pr |
List open pull requests |
/help |
Show available commands |
Text messages are forwarded to the active coding agent as prompts. Voice messages are transcribed and forwarded the same way.
Use /provider to pick between Claude Code and OpenAI Codex via an inline keyboard. The choice is global, persisted across restarts, and switching auto-stops any running process. The active provider is shown in /status, /help, the pinned project message, and the startup message. Sessions are tracked separately per provider, so /history and follow-up continuity stay scoped to whichever provider is active.
Use /compose to batch multiple messages into a single prompt. Useful for forwarding context from other chats, combining voice notes with text, or building multi-part requests. All message types are supported: text, voice (auto-transcribed), forwarded messages, files, and photos. Send /send when done or /cancel to discard.
- Spawns the active provider's CLI in the selected project dir and parses its streaming JSON into a normalized internal event model
- Claude Code:
claude -p "<msg>" --output-format stream-json - Codex:
codex exec --json(resume viacodex exec resume <id>)
- Claude Code:
- Streams response back via
sendMessageDraft(~300ms interval), falling back to progressive message editing if drafts aren't supported - Long responses auto-split into multiple messages (4000 char limit)
- Follow-up messages continue the same session for the active provider (Claude
-r <id>, Codexexec resume <id>); sessions are tracked per provider - UI features adapt to provider capabilities — Codex omits cost/turns (duration only) and subagent messages; both stream thinking
- Voice notes are transcribed via Groq Whisper (
whisper-large-v3-turbo) - One active process per user (across providers); messages sent while busy are queued automatically
- Plan mode is organic for both providers: Claude writes to
.claude/plans/and callsExitPlanMode; Codex follows the.codex/plans/PLAN.mdconvention it's taught via an injected prompt prefix. Either triggers the same interception — the bot displays the plan as plain text and offers action buttons: execute in a new session, execute keeping context, or modify with feedback - Use
/stopto cancel the current process and clear the queue
TypeScript, Bun, grammy, Groq SDK
Contributions welcome! See CONTRIBUTING.md for guidelines.