English | 中文
Drive your local Claude Code from Telegram.
A self-hosted TypeScript bot that bridges a Telegram chat to locally-active Claude Code sessions — streaming replies, scheduled tasks, multimodal input, and a built-in admin console.
Landing page · Docs · License
Claude Code is a powerful agentic coding tool — but it lives in your terminal. CoBot puts it in your pocket: from any phone, send a message to Telegram and Claude Code starts (or resumes) a session in your project directory, streaming its work back to the chat. It is fully self-hosted, your code never leaves your machine, and it ships with the guardrails an autonomous code-executing agent needs.
Highlights
- 📱 Talk to Claude Code from Telegram — any device, anywhere
- 🌊 Streaming Markdown replies (tables, code blocks, LaTeX) via progressive Rich Message edits
- 🗂️ Multi-project sessions — switch projects and resume sessions right from chat
- ⏰ Cron-scheduled prompts — "every morning, run the test suite and report back"
- 🖼️ Multimodal input — send photos, PDFs, log files alongside your prompt
- 🛡️ Guardrails — per-task turn cap, daily cost/token quotas, audit log, interactive tool approval
- 🖥️ Built-in Web Admin console — live tasks, costs, tool usage, logs, config
- 🔒 Self-hosted — your code stays on your machine; a user allowlist gates access
- Quick start
- How it works
- Features
- Commands
- Configuration
- Safety & guardrails
- Web admin console
- Development
- FAQ
- Uninstall
- Roadmap
- Contributing
- License
| Need | How to get it |
|---|---|
| Node.js ≥ 20 | https://nodejs.org |
| Claude Code installed & authenticated locally | claude CLI must run on this machine — CoBot drives it via the Agent SDK |
| Telegram bot token | create a bot with @BotFather |
| Your Telegram user ID | @userinfobot prints it |
ℹ️ CoBot talks to the machine's local Claude Code. It does not call the Anthropic API directly — Claude Code must be installed and logged in on the host.
git clone https://github.com/zaneven/CoBot.git
cd CoBot
./scripts/cobot.sh install
# copies config templates · installs deps · interactive setup · links the global `cobot` CLI · starts the botInstall drops into an interactive setup — fill in just three fields to run, everything else stays at its default:
| Field | What it is | Where to get it |
|---|---|---|
| Telegram bot token | the bot's token (required) | create a bot with @BotFather |
| Your Telegram user ID | who may control the bot (required; comma-separated for several) | query @userinfobot |
| Dev root directory | whose subdirectories /bind can attach (defaults to this dir's parent — your projects folder) |
press Enter to accept the default |
When done, .env and config.yaml are written, the admin panel URL + key are printed, and the bot is started automatically. To start it again later:
cobot start💡 Re-run
npx tsx scripts/setup.tsany time to change just those three; every other option lives in Configuration.
Manual alternative (skip the prompts)
cp .env.example .env
cp config.example.yaml config.yaml
npm install
npm rebuild better-sqlite3 # only if your Node version changedThen fill in config.yaml (the bot token & allowed users live here, not .env — setup writes them here):
telegram:
botToken: "123456789:ABCdefGhi..." # from @BotFather
allowedUsers: [100123456] # your Telegram user ID (comma-separated)
devRoots:
- /Users/YOU/Develop # immediate subdirectories become switchable projects
admin:
enabled: true # built-in Web Admin console
port: 8085
apiKey: "choose-a-secret" # bearer token for the admin APIThe interactive install already fills in the three fields needed to start. To tune the model, quotas, approval mode, proxy and more, edit .env and config.yaml directly (env vars win) or use the built-in Web admin console — see Configuration for the full reference.
cobot start # background service, auto-detects proxy
cobot status # process, proxy, recent logsForeground/dev mode: npm run dev (auto-reload on save).
- Open Telegram, message your bot.
/projects→ tap a project.- Just send text — Claude Code answers, streaming into the chat.
🖼️ Screenshot wanted: a screenshot/GIF of a streamed reply here sells the project. Drop it at
docs/assets/demo.pngand replace this note.
Telegram (long-poll)
│
▼
┌────────────────────────┐
│ grammY bot │
│ auth gate (allowlist) │
└────────────┬───────────┘
/commands │ plain text / photo / doc
▼
┌────────────────────────┐ per-chat Registry
│ SQLite store │◀────── (FIFO queue,
│ bindings · tasks · │ active-run tracking)
│ cron · audit_logs │
└────────────┬───────────┘
│ runTurn
▼
┌────────────────────────┐
│ Claude Agent SDK │── streaming events ──▶ chat
│ (driver.ts) │
└────────────┬───────────┘
▼
local Claude Code session
(started or resumed in the bound dir)
node-cron ──▶ scheduled prompts dispatched to the same pipeline
Each Telegram chat binds to one whitelisted project directory. Every prompt starts (or resumes) a Claude Code session there, and the assistant's output streams back to the chat. A per-chat FIFO queue keeps tasks serial — /stop interrupts the running task, /drop clears the queue.
| Feature | What it means | |
|---|---|---|
| 🌊 | Streaming replies | Claude's output streams in via progressive Rich Message edits with full Markdown (tables, code, LaTeX), with HTML→plain fallback. |
| 🗂️ | Session management | List, resume, and switch between Claude Code sessions across all your projects. |
| 📋 | Task queue | FIFO per-chat queue. /stop interrupts the running task, /drop clears waiting ones. |
| ⏰ | Scheduled tasks | Cron-style prompts (/cron) dispatched to Claude Code — list/enable/disable/manage. |
| 🖼️ | Multimodal input | Send photos, PDFs, or text/code/log files alongside your prompt. |
| 🧩 | Skill browser | /skills lists available Skills; tap to fill the input bar. |
| ➕ | Project creation | Tap ➕ 新建项目 in /projects to scaffold a new dir with git init. |
| 📊 | Context usage | /context shows the last turn's context-window % with a visual bar. |
| ⌨️ | Typing indicator | Telegram "typing…" while the bot works. |
| 🛡️ | Guardrails | Turn cap, daily cost/token caps, audit log, interactive tool approval. |
| 🖥️ | Web admin console | Live tasks, costs, tool usage, logs, and config editing. |
| Command | Description |
|---|---|
/projects |
List projects (tap to switch); ➕ 新建项目 at bottom |
/project <name> |
Switch active project by name |
/bind <path> |
Bind to an explicit whitelisted path |
/unbind |
Unbind this chat from its project |
/new |
Start a fresh Claude Code session |
/auto [off] |
Persistent auto-session mode |
/sessions [all] |
List sessions (tap to switch) |
/switch <id> |
Switch active session by ID |
/stop |
Interrupt the running task |
/tasks |
Active + recent tasks |
/queue |
Show queued prompts |
/drop |
Clear queued prompts |
/cron <schedule> | <prompt> |
Schedule a task |
/cron list | rm <id> | enable <id> | disable <id> |
Manage cron jobs |
/context |
Last turn's context-window usage (%) |
/skills |
Browse available Skills (tap to use) |
/models [<id>|off] |
Pick the model — button picker, per chat & engine (live-discovered from the CLI + config list) |
/approve auto|interactive|list|clear <tool|all> |
Tool-call approval mode |
/help |
Show this list |
Keyboard buttons: Projects Sessions New — Stop Queue Tasks
Plain text, photo, or document → sent straight to Claude Code (streaming).
CoBot reads two layers: environment variables (.env) for secrets/runtime, and config.yaml for structure/quotas. Env vars override the YAML where they overlap.
.env — environment variables
# ---- Claude Code ----
CLAUDE_MODEL= # default model for spawned sessions (optional)
CLAUDE_PERMISSION_MODE=acceptEdits # acceptEdits | bypassPermissions | default | plan
CLAUDE_TASK_TIMEOUT_MS=1800000 # per-task wall-clock cap (30m). 0 = off
CLAUDE_ALLOWED_TOOLS= # e.g. "Bash(git *),Edit,Read"
CLAUDE_ALLOW_DANGEROUS_SKIP_PERMISSIONS=false # DANGEROUS
CLAUDE_MAX_TURNS= # cap agentic turns per task (default 50)
# ---- CoBot ----
# Telegram bot token & allowed users live in config.yaml (telegram.botToken /
# telegram.allowedUsers) — that's where `setup` writes them. These env vars
# still override the YAML if set, but setup no longer touches .env for them.
COBOT_DB_PATH=./data/cobot.db
COBOT_PROXY=http://127.0.0.1:10808 # outbound proxy to reach Telegram (optional)
LOG_LEVEL=info # trace | debug | info | warn | error
# ---- Hermes (optional backend, v1.5) ----
# HERMES_API_URL=http://127.0.0.1:8642
# HERMES_API_KEY=config.yaml — structure & quotas
devRoots: # dirs whose immediate subdirectories are projects
- /Users/YOU/Develop
projects: # extra whitelisted paths outside devRoots
# - /Users/YOU/.hermes
telegram:
botToken: "" # from @BotFather (env overrides)
allowedUsers: [12345678]
maxEditChars: 3500 # chars before appending a new chunk message
pollTimeout: 30 # long-poll timeout (s)
flushMs: 900 # ms between streamed edits
approval: # interactive tool-call approval
mode: auto # auto | interactive
timeoutMs: 300000
timeoutAction: allow # allow | deny (when user doesn't respond)
skipTools: [Read, LS, Glob, Grep, TodoWrite] # never prompt (read-only)
defaults:
maxTurns: 50 # 0 = unlimited
dailyCostCapUsd: 5.0 # per-chat daily ceiling (omit = none)
dailyTokenCap: 200000 # per-chat daily token ceiling
admin:
enabled: true
host: "127.0.0.1"
port: 8085
apiKey: "" # bearer token for Admin APICoBot is a headless agent with local code-execution power, so it ships guardrails to keep autonomous use controllable and observable.
- User allowlist —
telegram.allowedUsersinconfig.yamlgates access (envTELEGRAM_ALLOWED_USERSoverrides); unauthorized users are ignored. - Path whitelist —
/bindonly accepts paths underdevRoots/projectsinconfig.yaml. - Per-task turn cap —
DEFAULT_MAX_TURNS = 50(override viaCLAUDE_MAX_TURNSordefaults.maxTurns;0= unlimited). - Per-chat daily quota —
dailyCostCapUsd/dailyTokenCapreject new tasks once exceeded; resets at local midnight. - Audit log — every task is recorded in the
audit_logstable (chatId, prompt, tools, costUsd, duration, tokens, contextUsagePct, status) for cost accounting and forensics. - Interactive tool approval —
/approve interactiveprompts before mutating tool calls; read-only tools inskipToolsskip the prompt; tap ✅/❌/⭐ (always-allow). Mode + "always allow" rules persist in SQLite. Cron tasks always auto-approve. Default mode isauto(headless); setCOBOT_APPROVAL_TIMEOUT_ACTION=denyto fail closed. - Wall-clock watchdog —
CLAUDE_TASK_TIMEOUT_MSaborts a hung task (e.g. one waiting on a prompt) so it can't block the chat. - Permission mode — defaults to
acceptEdits(headless auto-accept of file edits).CLAUDE_ALLOW_DANGEROUS_SKIP_PERMISSIONSis off by default.
A built-in dashboard ships with CoBot (admin.enabled: true) at http://127.0.0.1:8085. Authenticate with the admin.apiKey bearer token. It gives you:
- Overview — uptime, active tasks, total tasks & success rate, cumulative cost & tokens, 7-day trend, tool-usage breakdown
- Tasks & audit — full history with cost, duration, status, prompt replay
- Cron — list / enable / disable / delete scheduled jobs
- Bindings — which chat is bound to which project
- Config — view/edit runtime config (raw YAML)
- Live logs — streaming teleprinter of bot output
Design language: a dark "dispatch console" — amber (working), mint (healthy), violet (flagged) — with Space Grotesk + IBM Plex Mono. See
src/admin/web/index.html.
npm test # 226 tests, Node built-in runner
npm run typecheck # tsc --noEmit
npm run rebuild:native # rebuild better-sqlite3 after a Node version change
npm run dev # foreground, watch reloadCI note:
better-sqlite3is a native module. Afternpm ci, runnpm run rebuild:nativeso the binary matches the CI Node version, or Store-backed tests fail withERR_DLOPEN_FAILED.
src/
├── bot/ — grammY setup, commands, runs, streaming, media, approval
├── claude/ — Agent SDK driver + types + sessions (the ONLY SDK boundary)
├── registry/ — per-chat active-run tracking + FIFO queue
├── store/ — SQLite (better-sqlite3): bindings, tasks, cron, audit
├── scheduler/ — node-cron manager
├── admin/ — Web Admin HTTP server + static console
├── util/ — proxy, http, Telegram HTML formatting, logger
└── index.ts — entry point
- SDK isolation —
src/claude/driver.tsis the only place that imports@anthropic-ai/claude-agent-sdk. Bot/registry layers talk to it solely via theDriverEventunion insrc/claude/types.ts. - Non-blocking — never
awaita long-running task inside a grammY update handler (it would block/stop). Usevoid runOne(...)/submitInteractive. - Streaming —
TelegramStreamerhas two length controls:maxEditChars(new-message threshold, default 3500) andTG_HARD_LIMIT = 32000(Telegram's per-message cap). All sends are serialized to avoid edit rate-limits.
See AGENTS.md for the full developer/agent guide and docs/development.md.
The bot starts but never responds / "Telegram init timed out"
Your network can't reach api.telegram.org. Set COBOT_PROXY (or HTTPS_PROXY) in .env. On macOS, CoBot auto-detects the system proxy via scutil --proxy.
Tests fail with ERR_DLOPEN_FAILED
The better-sqlite3 native binary doesn't match your Node version. Run npm run rebuild:native.
409 Conflict on start
A previous bot instance is still polling. CoBot retries automatically; if it persists, cobot stop then cobot start.
How do I limit what Claude Code can do?
CLAUDE_ALLOWED_TOOLS restricts tools; /approve interactive prompts before mutating calls; CLAUDE_MAX_TURNS and the daily caps bound runaway spend. For the strictest posture, set COBOT_APPROVAL_TIMEOUT_ACTION=deny.
Does my code leave my machine?
No. CoBot drives your local Claude Code via the Agent SDK. Source files, prompts, and session state stay on the host. Only the chat traffic to Telegram traverses the network.
cobot uninstall # interactive confirm
cobot uninstall --yes # skip the prompt (CI / scripts)Removes the global cobot CLI, node_modules, dist, data/, logs, config.yaml, and .env. The source repo is kept — delete it with rm -rf if you want it gone too.
- Docker image & docker-compose for one-command deploy
- Multi-user / team mode with per-user quotas
- Webhook transport alongside long-poll
- Plugin API for custom commands
- More providers (Cursor / local LLMs) behind the same driver boundary
Have an idea? Open a discussion or an issue.
Contributions are welcome! Please read AGENTS.md first — it documents the architecture constraints (SDK isolation, non-blocking handlers, streaming invariants) that every change must respect.
- Fork & branch (
git checkout -b feat/my-feature) npm test && npm run typecheckbefore committing- Open a PR describing the change and linking any issue
By contributing, you agree that your contributions are licensed under the project's MIT license.
Claude Code and the Claude Agent SDK are products of Anthropic. CoBot is an independent, community project and is not affiliated with or endorsed by Anthropic.