Architecture
Overview
The bot is a Node.js application built with Telegraf. It bridges Telegram DMs to isolated Claude Code CLI subprocesses using an MCP (Model Context Protocol) relay.
┌──────────────────────────────────────────────────────────────────────┐│ Telegram ││ Long-polling via Telegraf bot │└──────────────────────────────────────────────────────────────────────┘ │ ▼┌──────────────────────────────────────────────────────────────────────┐│ index.ts ││ Command routing, inline keyboard handlers, session orchestration │└──────────────────────────────────────────────────────────────────────┘ │ ▼┌──────────────────────────────────────────────────────────────────────┐│ SessionManager ││ Per-user ClaudeSession lifecycle │└──────────────────────────────────────────────────────────────────────┘ │ ┌─────────────────┴──────────────────┐ ▼ ▼┌───────────────────────┐ ┌───────────────────────────────────┐│ ClaudeSession │ │ RelayServer ││ Spawns Claude CLI │ │ Local HTTP server (localhost) ││ subprocess │ │ Proxies MCP tool calls back ││ stream-json output │ │ to the main process │└───────────────────────┘ └───────────────────────────────────┘ │ │ ▼ ▼┌───────────────────────┐ ┌───────────────────────────────────┐│ Claude Code CLI │ │ MCP Server (stdio) ││ User's repo as CWD │ │ Provides send_message, ││ │ │ send_document, confirmation │└───────────────────────┘ └───────────────────────────────────┘ │ ▼ ┌───────────────────────┐ │ MessageQueue │ │ Serial queue for │ │ all Telegram sends │ └───────────────────────┘ │ ▼ ┌───────────────────────┐ │ Telegram Bot API │ └───────────────────────┘Modules
| File | Responsibility |
|---|---|
src/index.ts | Bot commands, routing, inline keyboard handlers |
src/bot.ts | Telegraf instance, sendMessage, markdown→HTML conversion |
src/config.ts | Zod env validation; exits on invalid config |
src/sessionManager.ts | Lifecycle: create/send/end ClaudeSession per user |
src/claudeSession.ts | Spawns Claude CLI subprocess, parses stream-json events |
src/workspaceManager.ts | FS CRUD for sessions/<id>/<workspace>/ directories |
src/repoManager.ts | Git clone + FS CRUD for repos within workspaces |
src/relayServer.ts | Local HTTP server; proxies MCP tool calls to main process |
src/messageQueue.ts | Serial queue for all Telegram API calls |
src/commandsConfig.ts | Loads/validates commands.json custom slash commands |
mcp-server/src/index.ts | MCP tools: send_message, send_document, send_confirmation_request |
Data storage
All state is stored on the filesystem under sessions/. No database is used.
sessions/<chat_id>/ default/ <repo-name>/ .active_repo ← marks active repo .active_branch ← records current branch ... ← cloned repo files <workspace-name>/ <repo-name>/Dotfiles (.active_repo, .active_branch) track the active state. Deleting these files does not affect the cloned repositories.
MCP relay pattern
Claude’s MCP server runs as a stdio subprocess. It cannot make outbound HTTP requests to the Telegram Bot API directly, so it proxies through RelayServer:
- MCP server receives a tool call (e.g.,
send_message) - It POSTs to
RelayServerathttp://127.0.0.1:<port>/relay RelayServerforwards toMessageQueue.enqueue()MessageQueuesends to Telegram Bot API serially
Session lifecycle
- User sends
/startor clicks a repo →SessionManager.startSession() ClaudeSessionconstructor spawns Claude CLI with--continue- Each user message →
ClaudeSession.send()spawns a one-shot Claude process - Claude outputs
stream-jsonevents →handleJsonEvent()routes toonOutput - Session ends on:
/end,/stop, idle timeout, or process crash