Manage Claude Code sessions via Telegram
Project description
ccremote
Control Claude Code from your phone via Telegram.
ccremote bridges Claude Code CLI to Telegram DMs. Send prompts from your phone, get streaming responses via live draft previews, send photos/documents/voice messages — all without sitting at your computer.
How it works
Phone (Telegram) Local Machine
────────────────── ──────────────────
DM with bot ccremote
"fix the bug" ────► ├─ Telegram bot (aiogram)
◄ streaming draft... ├─ claude --print --resume <id>
◄ final response └─ Whisper transcription
- Run
uvx ccremote .in any project directory - Chat with Claude in your bot's DM
- Responses stream as live draft previews, then appear as final messages
- Send photos, documents, or voice messages
- Permission denials show inline buttons to approve and retry
Quick start
- Create a Telegram bot — open @BotFather, send
/newbot, follow the prompts, copy the token - Get your Telegram user ID — open @userinfobot, send
/start, copy the number - Create a
.ccremotefile in your project directory:CCREMOTE_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11 CCREMOTE_ALLOWED_USER=123456789
- Run:
uvx ccremote .
- Open your bot's DM in Telegram and start chatting with Claude
Tip: Add
.ccremoteto your global.gitignore— it contains secrets.
Prerequisites
- Claude Code CLI installed and authenticated
- Python 3.11+ and uv (recommended) or pip
- A Telegram bot token (see step 1 above)
- OpenAI API key (optional, only needed for voice message transcription)
Install locally (for development)
git clone https://github.com/nurikk/ccremote.git
cd ccremote
uv venv .venv
source .venv/bin/activate
uv pip install -e .
Configuration
Environment variables vs .ccremote file
You can configure ccremote in two ways — they can be combined:
Option A: Global environment variables (apply to all projects)
# Add to ~/.zshrc or ~/.bashrc
export CCREMOTE_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
export CCREMOTE_ALLOWED_USER=123456789
export CCREMOTE_OPENAI_API_KEY=sk-... # optional, for voice messages
Option B: Per-project .ccremote file (overrides env vars for that project)
CCREMOTE_BOT_TOKEN=999888:XYZ-different-bot-token
CCREMOTE_ALLOWED_USER=123456789
CCREMOTE_OPENAI_API_KEY=sk-...
Priority: .ccremote file > environment variables > defaults.
Configuration options
| Variable | Required | Default | Description |
|---|---|---|---|
CCREMOTE_BOT_TOKEN |
yes | — | Telegram bot token from @BotFather |
CCREMOTE_ALLOWED_USER |
yes | — | Your Telegram user ID |
CCREMOTE_OPENAI_API_KEY |
no | "" |
OpenAI key for voice transcription |
CCREMOTE_LOG_LEVEL |
no | info |
debug, info, warning, error |
CCREMOTE_INCLUDE_PARTIAL_MESSAGES |
no | true |
Include partial messages in stream |
CCREMOTE_DRAFT_THROTTLE_MS |
no | 300 |
Min ms between draft updates |
CCREMOTE_MAX_MESSAGE_LENGTH |
no | 4000 |
Max chars per Telegram message |
CCREMOTE_CLAUDE_ALLOWED_TOOLS |
no | — | JSON array of allowed tools (all if unset) |
Usage
# With uvx (no install needed)
uvx ccremote .
# Or if installed locally
ccremote .
ccremote ~/code/myproject
That's it. The bot connects to Telegram and you can start chatting.
Message types
- Text — sent directly as prompts to Claude
- Photos — downloaded to
.ccremote-attachments/in the project, path passed to Claude - Documents — same as photos, keeps original filename
- Voice messages — transcribed via OpenAI Whisper, sent as text (requires
CCREMOTE_OPENAI_API_KEY)
Permission handling
When Claude tries to use a tool that's blocked by permissions, you'll see an inline keyboard:
⚠️ Permission denied:
• Bash: ls ~/Downloads
[✅ Allow] [❌ Skip]
Tapping Allow re-runs the prompt with the denied tools added to --allowedTools.
Session continuity
ccremote uses claude --print --resume <session_id> to maintain conversation context. Each message continues the same Claude session, so context builds up naturally across your conversation.
Architecture
src/ccremote/
├── cli.py Entry point — starts bot, creates session
├── bot.py aiogram dispatcher, message sending helpers
├── relay.py Claude ↔ Telegram relay, streaming, permissions
├── markdown.py Markdown → Telegram HTML converter
├── config.py pydantic-settings configuration
└── models.py Pydantic data models (Session)
Key design decisions:
- Single session mode — one
ccremoteprocess per project, DM-only. This is intentional:sendMessageDraftonly works in private chats, supergroup forums don't allow bots to create new threads via the Bot API, making group-based workflows impractical - Each prompt spawns
claude --print --resume <id>(stateless process, persistent session) sendMessageDraft(Bot API 9.3+) for flicker-free live streaming- Markdown converted to Telegram HTML for formatted output
- Per-project
.ccremoteoverrides global env vars
Development
# Install dev dependencies
uv pip install -e ".[dev]"
# Run tests
python -m pytest tests/ -v
# Lint
ruff check src/ tests/
# Format
ruff format src/ tests/
# Type check
ty check src/
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ccremote-0.0.2.tar.gz.
File metadata
- Download URL: ccremote-0.0.2.tar.gz
- Upload date:
- Size: 104.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b85090736021b66abc3e09bbb46a7ce14e18c48c116f8c358407249f890347eb
|
|
| MD5 |
b9023a04012b4a75befd478cb0a89eaa
|
|
| BLAKE2b-256 |
42d60a7d85b981169d20945b6588ace55c542b9b4e2a0ac8346ecfb03cde7773
|
Provenance
The following attestation bundles were made for ccremote-0.0.2.tar.gz:
Publisher:
publish.yml on nurikk/ccremote
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ccremote-0.0.2.tar.gz -
Subject digest:
b85090736021b66abc3e09bbb46a7ce14e18c48c116f8c358407249f890347eb - Sigstore transparency entry: 1164234673
- Sigstore integration time:
-
Permalink:
nurikk/ccremote@46b0309bcfa130c5c5b68614add4e89f70eb72b9 -
Branch / Tag:
refs/tags/v0.0.2 - Owner: https://github.com/nurikk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@46b0309bcfa130c5c5b68614add4e89f70eb72b9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ccremote-0.0.2-py3-none-any.whl.
File metadata
- Download URL: ccremote-0.0.2-py3-none-any.whl
- Upload date:
- Size: 15.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58dcb155831c6a4336eedfa3b378b9f5dbdae4455f8170955b92ff9a98e2bcd3
|
|
| MD5 |
289642046813e007e57b867fcd8126ac
|
|
| BLAKE2b-256 |
ad45ee008be0586a4e819c35c31db314eba57eab4da7ba5e41c1bdccd77d23fe
|
Provenance
The following attestation bundles were made for ccremote-0.0.2-py3-none-any.whl:
Publisher:
publish.yml on nurikk/ccremote
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ccremote-0.0.2-py3-none-any.whl -
Subject digest:
58dcb155831c6a4336eedfa3b378b9f5dbdae4455f8170955b92ff9a98e2bcd3 - Sigstore transparency entry: 1164234736
- Sigstore integration time:
-
Permalink:
nurikk/ccremote@46b0309bcfa130c5c5b68614add4e89f70eb72b9 -
Branch / Tag:
refs/tags/v0.0.2 - Owner: https://github.com/nurikk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@46b0309bcfa130c5c5b68614add4e89f70eb72b9 -
Trigger Event:
push
-
Statement type: