Skip to content
ILobeMhPublic
forked from six-ddc/ccbot

About

Telegram ↔ tmux bridge for Claude Code: 1 topic = 1 window = 1 session

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

CCBot

中文文档 Русская документация

Control Claude Code sessions remotely via Telegram — monitor, interact, and manage AI coding sessions running in tmux.

Jietu20260207-final.mp4

Why CCBot?

Claude Code runs in your terminal. When you step away from your computer — commuting, on the couch, or just away from your desk — the session keeps working, but you lose visibility and control.

CCBot solves this by letting you seamlessly continue the same session from Telegram. The key insight is that it operates on tmux, not the Claude Code SDK. Your Claude Code process stays exactly where it is, in a tmux window on your machine. CCBot simply reads its output and sends keystrokes to it. This means:

  • Switch from desktop to phone mid-conversation — Claude is working on a refactor? Walk away, keep monitoring and responding from Telegram.
  • Switch back to desktop anytime — Since the tmux session was never interrupted, just tmux attach and you're back in the terminal with full scrollback and context.
  • Run multiple sessions in parallel — Each Telegram topic maps to a separate tmux window, so you can juggle multiple projects from one chat group.

Other Telegram bots for Claude Code typically wrap the Claude Code SDK to create separate API sessions. Those sessions are isolated — you can't resume them in your terminal. CCBot takes a different approach: it's just a thin control layer over tmux, so the terminal remains the source of truth and you never lose the ability to switch back.

In fact, CCBot itself was built this way — iterating on itself through Claude Code sessions monitored and driven from Telegram via CCBot.

Features

  • Topic-based sessions — Each Telegram topic maps 1:1 to a tmux window and Claude session
  • Real-time notifications — Get Telegram messages for assistant responses, thinking content, tool use/result, and local command output
  • Interactive UI — Navigate AskUserQuestion, ExitPlanMode, and Permission Prompts via inline keyboard
  • Voice messages — Voice messages are transcribed via OpenAI and forwarded as text
  • Send messages — Forward text to Claude Code via tmux keystrokes
  • Slash command forwarding — Send any /command directly to Claude Code (e.g. /clear, /compact, /cost)
  • Create new sessions — Start Claude Code sessions from Telegram via directory browser
  • Resume sessions — Pick up where you left off by resuming an existing Claude session in a directory
  • Kill sessions — Close a topic to auto-kill the associated tmux window
  • Message history — Browse conversation history with pagination (newest first)
  • Hook-based session tracking — Auto-associates tmux windows with Claude sessions via SessionStart hook
  • Persistent state — Thread bindings and read offsets survive restarts

Prerequisites

  • tmux — must be installed and available in PATH
  • Claude Code — the CLI tool (claude) must be installed

Installation

Option 1: Install from GitHub (Recommended)

# Using uv (recommended)
uv tool install git+https://github.com/six-ddc/ccmux.git

# Or using pipx
pipx install git+https://github.com/six-ddc/ccmux.git

Option 2: Install from source

git clone https://github.com/six-ddc/ccmux.git
cd ccmux
uv sync

Configuration

1. Create a Telegram bot and enable Threaded Mode:

  1. Chat with @BotFather to create a new bot and get your bot token
  2. Open @BotFather's profile page, tap Open App to launch the mini app
  3. Select your bot, then go to Settings > Bot Settings
  4. Enable Threaded Mode

2. Configure environment variables:

Create ~/.ccbot/.env:

TELEGRAM_BOT_TOKEN=your_bot_token_here
ALLOWED_USERS=your_telegram_user_id

Required:

Variable Description
TELEGRAM_BOT_TOKEN Bot token from @BotFather
ALLOWED_USERS Comma-separated Telegram user IDs

Optional:

Variable Default Description
CCBOT_DIR ~/.ccbot Config/state directory (.env loaded from here)
TMUX_SESSION_NAME ccbot Tmux session name
CLAUDE_COMMAND claude Command to run in new windows
MONITOR_POLL_INTERVAL 2.0 Polling interval in seconds
CCBOT_SHOW_HIDDEN_DIRS false Show hidden (dot) directories in directory browser
CCBOT_SHOW_USER_MESSAGES true Mirror your own prompts (👤) into the topic
CCBOT_SHOW_TOOL_CALLS true Show tool_use / tool_result messages
CCBOT_SHOW_THINKING true Show thinking blocks as collapsed expandable quotes
CCBOT_MAX_THINKING_CHARS 500 Thinking chars per turn; 0 sends it all as [i/N] parts
CCBOT_STATUS_UPDATES true Live status-line edits ("Moseying… (7s)")
CCBOT_QUIET_HOURS (off) HH-HH window (server time) with no ccc / health alerts
CCBOT_CCC_ALERTS true ccc exhausted / available-again messages
CCBOT_NOTIFY_INPUT / _DONE / _LIFECYCLE / _ERRORS / _CCC true Notification kinds posted to the notifications topic
CCBOT_NOTIFY_DONE_MIN_SECONDS 0 Only announce finished turns that took at least this long
CLAUDE_PERMISSION_MODE default Initial launch mode offered first in the mode picker (default, acceptEdits, plan, bypassPermissions)
CCBOT_AUTO_TRUST_DIRS true Pre-trust the project in ~/.claude.json before launching claude (skips the "Quick safety check" dialog)
CCBOT_DEFAULT_DIR (bot cwd) Directory the directory browser opens at
CCBOT_SPECIAL_TOPICS shell,ccc,settings,notifications Bot-owned topics to create in the group (see below); empty disables
CCBOT_FORUM_CHAT_ID (learned) Supergroup id used to create special topics (needs the "Manage Topics" admin right)
CCBOT_SHELL_TIMEOUT 120 Per-command timeout (seconds) in the shell topic
CCBOT_CCC_COMMAND ccc Path to the ccc account switcher used by the ccc topic
CCBOT_CCC_POLL_INTERVAL 300 How often (seconds) the ccc topic re-checks quotas
CCBOT_SCREENSHOT_FONT (bundled MesloLGS NF) TTF/OTF used as the primary /screenshot font
CCBOT_SCREENSHOT_FONT_SIZE 28 /screenshot font size in px
OPENAI_API_KEY (none) OpenAI API key for voice message transcription
OPENAI_BASE_URL https://api.openai.com/v1 OpenAI API base URL (for proxies or compatible APIs)

Message formatting is always HTML via chatgpt-md-converter (chatgpt_md_converter package). There is no runtime formatter switch to MarkdownV2.

Permission mode is chosen per session in the mode picker (Normal / Accept edits / Plan / Skip permissions) and can be switched later with /mode or /restart <mode>. --dangerously-skip-permissions requires Claude Code's one-time acceptance dialog to have been answered once on that machine (the bot answers it, or set "skipDangerousModePermissionPrompt": true in ~/.claude/settings.json); it is refused as root.

Screenshots use MesloLGS NF (Latin, box drawing, Nerd Font icons) with Vazirmatn for Persian/Arabic, Noto Sans Mono CJK and Symbola as fallbacks — all bundled.

Hook Setup (Recommended)

Auto-install via CLI:

ccbot hook --install

Or manually add to ~/.claude/settings.json:

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [{ "type": "command", "command": "ccbot hook", "timeout": 5 }]
      }
    ]
  }
}

This writes window-session mappings to $CCBOT_DIR/session_map.json (~/.ccbot/ by default), so the bot automatically tracks which Claude session is running in each tmux window — even after /clear or session restarts.

Usage

# If installed via uv tool / pipx
ccbot

# If installed from source
uv run ccbot

Commands

Bot commands:

Command Description
/start Show welcome message
/history Message history for this topic
/screenshot Capture terminal screenshot (with control keys incl. ⇧⇥ Mode)
/esc Send Escape to interrupt Claude
/mode [m] Show the permission mode, or switch it via Shift+Tab (normal, accept, plan, bypass)
/restart [m] Restart Claude Code in the same window, resuming the session (optionally in mode m) — e.g. to apply a Claude Code update
/sessions Every bound session with state (working / idle / waiting / exited), memory of its process tree, mode, cwd and last activity; 🗑 Kill buttons (the topic gets a ▶ Resume message) and ↗ links
/info tmux attach command, window/session ids, launch command, transcript path, versions
/resume <id> In an empty topic: resume that session directly (/resume <path> <id> [mode] also works)
/kill Kill the tmux window and forget the session
/unbind Unbind the topic, keep the window running
/usage Claude Code usage remaining

Claude Code commands (forwarded via tmux):

Command Description
/clear Clear conversation history
/compact Compact conversation context
/cost Show token/cost usage
/help Show Claude Code help
/memory Edit CLAUDE.md

Any unrecognized /command is also forwarded to Claude Code as-is (e.g. /review, /doctor, /init).

Special topics

The bot creates these topics itself (it needs the Manage Topics admin right in the group). Delete one and it is recreated on the next bot restart; CCBOT_SPECIAL_TOPICS= (empty) disables them.

Topic What it does
shell A raw shell on the bot's host — no Claude Code. Every message runs as a command ($SHELL -lc); the reply shows the output, exit code, duration and cwd. cd persists, output over 3000 chars is attached as a file, and a running command has a ⏹ Kill button.
notifications One feed for events from every session, each with a button that jumps to its topic: ❓ needs input (question, permission / plan prompt, dialog — once per dialog), ✅ turn finished (with duration and the reply's first line; optional minimum duration), 🔄 lifecycle (created / resumed / restarted / exited / update installed), 🚨 API errors from the transcript (session limit, rate limit, 5xx), 🔀 ccc quota. Each kind toggles in settings; quiet hours apply.
settings Live bot settings with one button per option (thinking on/off and length incl. full split, tool calls, 👤 mirror, status line, default launch mode, auto-trust, shell timeout, ccc alerts/poll, screenshot font, quiet hours). Applied immediately and saved to ~/.ccbot/settings.json, which overrides the env defaults. /settings shows the same dashboard anywhere.
ccc Claude Code / Codex account switching via ccc. Any message shows the dashboard (accounts, plan, 5h/7d quota, reset times, reset credits) with ▶ Use / ⏭ Next / 🔄 Refresh buttons and a 🔁 button that restarts every Claude Code session so it picks up the new login. A watcher polls quota, wakes up right after the earliest reset, and posts when the account in use is exhausted (with the best alternative) and when an exhausted account is usable again.

Topic Workflow

1 Topic = 1 Window = 1 Session. The bot runs in Telegram Forum (topics) mode.

Creating a new session:

  1. Create a new topic in the Telegram group
  2. Send any message in the topic — or send a full path (e.g. /home/me/project) to skip the browser, or <session id> / <path> <session id> [mode] / /resume <session id> to resume a known session immediately (no pickers; uses your last launch mode unless a mode is given)
  3. A directory browser appears — select the project directory
  4. If the directory has existing Claude sessions, a session picker appears — choose one to resume or start fresh
  5. A mode picker appears — Normal / Accept edits / Plan / Skip permissions (your last choice is listed first)
  6. A tmux window is created, claude starts (with --resume if resuming), the trust dialog is handled automatically, and once Claude's input box is ready your pending message is forwarded

Sending messages:

Once a topic is bound to a session, just send text or voice messages in that topic — text gets forwarded to Claude Code via tmux keystrokes, and voice messages are automatically transcribed and forwarded as text.

Killing a session:

Close (or delete) the topic in Telegram. The associated tmux window is automatically killed and the binding is removed.

Message History

Navigate with inline buttons:

📋 [project-name] Messages (42 total)

───── 14:32 ─────

👤 fix the login bug

───── 14:33 ─────

I'll look into the login bug...

[◀ Older]    [2/9]    [Newer ▶]

Notifications

The monitor polls session JSONL files every 2 seconds and sends notifications for:

  • Assistant responses — Claude's text replies
  • Thinking content — Shown as expandable blockquotes
  • Tool use/result — Summarized with stats (e.g. "Read 42 lines", "Found 5 matches")
  • Local command output — stdout from commands like git status, prefixed with ❯ command_name

Notifications are delivered to the topic bound to the session's window.

Formatting note:

  • Telegram messages are rendered with parse mode HTML using chatgpt-md-converter
  • Long messages are split with HTML tag awareness to preserve code blocks and formatting

Run as a service (systemd)

deploy/ccbot.service is a systemd user unit that restarts the bot if it crashes and starts it at boot:

mkdir -p ~/.config/systemd/user
cp deploy/ccbot.service ~/.config/systemd/user/
loginctl enable-linger "$USER"   # start at boot without logging in
systemctl --user daemon-reload
systemctl --user enable --now ccbot
journalctl --user -u ccbot -f    # logs

It uses KillMode=process, so restarting the bot never kills the tmux server or the Claude Code sessions in it. After a reboot those sessions are gone; the bot posts a ▶ Resume button in every topic that lost its session (claude --resume in the same directory and permission mode).

Running Claude Code in tmux

Option 1: Create via Telegram (Recommended)

  1. Create a new topic in the Telegram group
  2. Send any message
  3. Select the project directory from the browser

Option 2: Create Manually

tmux attach -t ccbot
tmux new-window -n myproject -c ~/Code/myproject
# Then start Claude Code in the new window
claude

The window must be in the ccbot tmux session (configurable via TMUX_SESSION_NAME). The hook will automatically register it in session_map.json when Claude starts.

Data Storage

Path Description
$CCBOT_DIR/state.json Thread bindings, window states, display names, and per-user read offsets
$CCBOT_DIR/session_map.json Hook-generated {tmux_session:window_id: {session_id, cwd, window_name}} mappings
$CCBOT_DIR/monitor_state.json Monitor byte offsets per session (prevents duplicate notifications)
~/.claude/projects/ Claude Code session data (read-only)

File Structure

src/ccbot/
├── __init__.py            # Package entry point
├── main.py                # CLI dispatcher (hook subcommand + bot bootstrap)
├── hook.py                # Hook subcommand for session tracking (+ --install)
├── config.py              # Configuration from environment variables
├── bot.py                 # Telegram bot setup, command handlers, topic routing
├── session.py             # Session management, state persistence, message history
├── session_monitor.py     # JSONL file monitoring (polling + change detection)
├── monitor_state.py       # Monitor state persistence (byte offsets)
├── transcript_parser.py   # Claude Code JSONL transcript parsing
├── terminal_parser.py     # Terminal pane parsing (interactive UI + status line)
├── html_converter.py      # Markdown → Telegram HTML conversion + HTML-aware splitting
├── screenshot.py          # Terminal text → PNG image with ANSI color support
├── transcribe.py          # Voice-to-text transcription via OpenAI API
├── utils.py               # Shared utilities (atomic JSON writes, JSONL helpers)
├── tmux_manager.py        # Tmux window management (list, create, send keys, kill)
├── fonts/                 # Bundled fonts for screenshot rendering
└── handlers/
    ├── __init__.py        # Handler module exports
    ├── callback_data.py   # Callback data constants (CB_* prefixes)
    ├── directory_browser.py # Directory browser inline keyboard UI
    ├── history.py         # Message history pagination
    ├── interactive_ui.py  # Interactive UI handling (AskUser, ExitPlan, Permissions)
    ├── message_queue.py   # Per-user message queue + worker (merge, rate limit)
    ├── message_sender.py  # safe_reply / safe_edit / safe_send helpers
    ├── response_builder.py # Response message building (format tool_use, thinking, etc.)
    └── status_polling.py  # Terminal status line polling

Contributors

Thanks to all the people who contribute! We encourage using Claude Code to collaborate on contributions.

About

Telegram ↔ tmux bridge for Claude Code: 1 topic = 1 window = 1 session

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages