Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 89 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[中文文档](README_CN.md)
[Русская документация](README_RU.md)

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

https://github.com/user-attachments/assets/15ffb38e-5eb9-4720-93b9-412e4961dc93

Expand All @@ -23,23 +23,26 @@ In fact, CCBot itself was built this way — iterating on itself through Claude

## Features

- **Topic-based sessions** — Each Telegram topic maps 1:1 to a tmux window and Claude session
- **Topic-based sessions** — Each Telegram topic maps 1:1 to a tmux window and agent 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
- **Send messages** — Forward text to the active CLI via tmux keystrokes
- **Slash command forwarding** — Optional forwarding for unknown `/command` to the active CLI
- **Create new sessions** — Start agent 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
- **Provider support** — Claude Code (`claude`) and Codex CLI (`codex`)
- **Session tracking** — Claude via `SessionStart` hook, Codex via rollout mapper
- **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
- **Agent CLI** — install one or both:
- Claude Code (`claude`)
- Codex CLI (`codex`)

## Installation

Expand Down Expand Up @@ -92,7 +95,12 @@ ALLOWED_USERS=your_telegram_user_id
| ----------------------- | ---------- | ------------------------------------------------ |
| `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 |
| `CCBOT_PROVIDER` | `claude` | Session provider: `claude` or `codex` |
| `CCBOT_AGENT_COMMAND` | provider default (`claude`/`codex`) | Command to run in new windows |
| `CLAUDE_COMMAND` | `claude` | Backward-compatible alias for `CCBOT_AGENT_COMMAND` |
| `CCBOT_CODEX_SESSIONS_PATH` | `~/.codex/sessions` | Codex rollout root path |
| `CCBOT_FORWARD_PORTS` | _(none)_ | Comma-separated local ports to expose publicly on startup (e.g. `3000,5173`) |
| `CCBOT_FORWARD_SLASH` | `true` | Forward unknown `/command` to CLI |
| `MONITOR_POLL_INTERVAL` | `2.0` | Polling interval in seconds |
| `CCBOT_SHOW_HIDDEN_DIRS` | `false` | Show hidden (dot) directories in directory browser |
| `OPENAI_API_KEY` | _(none)_ | OpenAI API key for voice message transcription |
Expand All @@ -104,10 +112,12 @@ There is no runtime formatter switch to MarkdownV2.
> If running on a VPS where there's no interactive terminal to approve permissions, consider:
>
> ```
> CLAUDE_COMMAND=IS_SANDBOX=1 claude --dangerously-skip-permissions
> CCBOT_AGENT_COMMAND=IS_SANDBOX=1 claude --dangerously-skip-permissions
> ```

## Hook Setup (Recommended)
## Session Tracking Setup

### Claude Provider (Recommended)

Auto-install via CLI:

Expand All @@ -131,6 +141,10 @@ Or manually add to `~/.claude/settings.json`:

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.

### Codex Provider

No hook installation is required. CCBot maps tmux windows to Codex rollout files from `~/.codex/sessions` (or `CCBOT_CODEX_SESSIONS_PATH`).

## Usage

```bash
Expand All @@ -141,6 +155,40 @@ ccbot
uv run ccbot
```

### Provider Switching

Switch provider at startup:

```bash
# Claude Code
CCBOT_PROVIDER=claude CCBOT_AGENT_COMMAND=claude uv run ccbot

# Codex CLI
CCBOT_PROVIDER=codex CCBOT_AGENT_COMMAND=codex uv run ccbot
```

You can also place these values in `~/.ccbot/.env`.

### Port Forwarding

Expose local dev ports with CLI flags:

```bash
uv run ccbot --forward 3000
```

Multiple ports:

```bash
uv run ccbot --forward 3000 --forward 5173
```

Behavior:

- On startup, CCBot creates tunnel(s), sends real public URLs to all allowed users, and pins that message.
- On shutdown, CCBot stops tunnel(s) and unpins the message.
- Tunnel provider priority: `ngrok` -> `cloudflared` -> `localhost.run (ssh)`.

### Commands

**Bot commands:**
Expand All @@ -150,9 +198,9 @@ uv run ccbot
| `/start` | Show welcome message |
| `/history` | Message history for this topic |
| `/screenshot` | Capture terminal screenshot |
| `/esc` | Send Escape to interrupt Claude |
| `/esc` | Send Escape to interrupt active session |

**Claude Code commands (forwarded via tmux):**
**Claude provider commands (forwarded via tmux):**

| Command | Description |
| ---------- | ---------------------------- |
Expand All @@ -162,7 +210,7 @@ uv run ccbot
| `/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`).
For `CCBOT_PROVIDER=claude`, unknown `/command` is forwarded as-is (e.g. `/review`, `/doctor`, `/init`).

### Topic Workflow

Expand All @@ -174,11 +222,11 @@ Any unrecognized `/command` is also forwarded to Claude Code as-is (e.g. `/revie
2. Send any message in the topic
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 tmux window is created, `claude` starts (with `--resume` if resuming), and your pending message is forwarded
5. A tmux window is created, agent command starts (with `--resume` when selecting a Claude session), and 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.
Once a topic is bound to a session, just send text or voice messages in that topic — text gets forwarded to the active CLI via tmux keystrokes, and voice messages are transcribed then forwarded as text.

**Killing a session:**

Expand Down Expand Up @@ -206,18 +254,14 @@ I'll look into the login bug...

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

- **Assistant responses** — Claude's text replies
- **Assistant responses** — model 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

## Running Claude Code in tmux
## Running in tmux

### Option 1: Create via Telegram (Recommended)

Expand All @@ -230,20 +274,41 @@ Formatting note:
```bash
tmux attach -t ccbot
tmux new-window -n myproject -c ~/Code/myproject
# Then start Claude Code in the new window
# Then start your CLI in the new window
claude
# or
codex
```

The window must be in the `ccbot` tmux session (configurable via `TMUX_SESSION_NAME`). Claude provider uses hooks for session mapping; Codex provider uses rollout mapping.

## Resume Existing Session

To attach CCBot to an existing Codex session, run:

```bash
CCBOT_PROVIDER=codex \
CCBOT_AGENT_COMMAND='codex resume <session-id>' \
uv run ccbot
```

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.
Example:

```bash
CCBOT_PROVIDER=codex \
CCBOT_AGENT_COMMAND='codex resume 019c9eef-c5f7-7dc2-9e92-de59a1c3cd28' \
uv run ccbot
```

## 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/session_map.json` | Provider mappings `{tmux_session:window_id: {session_id, cwd, ...}}` |
| `$CCBOT_DIR/monitor_state.json` | Monitor byte offsets per session (prevents duplicate notifications) |
| `~/.claude/projects/` | Claude Code session data (read-only) |
| `~/.codex/sessions/` | Codex rollout session logs (read-only) |

## File Structure

Expand Down
Loading
Loading