Skip to content

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

FileResponsibility
src/index.tsBot commands, routing, inline keyboard handlers
src/bot.tsTelegraf instance, sendMessage, markdown→HTML conversion
src/config.tsZod env validation; exits on invalid config
src/sessionManager.tsLifecycle: create/send/end ClaudeSession per user
src/claudeSession.tsSpawns Claude CLI subprocess, parses stream-json events
src/workspaceManager.tsFS CRUD for sessions/<id>/<workspace>/ directories
src/repoManager.tsGit clone + FS CRUD for repos within workspaces
src/relayServer.tsLocal HTTP server; proxies MCP tool calls to main process
src/messageQueue.tsSerial queue for all Telegram API calls
src/commandsConfig.tsLoads/validates commands.json custom slash commands
mcp-server/src/index.tsMCP 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:

  1. MCP server receives a tool call (e.g., send_message)
  2. It POSTs to RelayServer at http://127.0.0.1:<port>/relay
  3. RelayServer forwards to MessageQueue.enqueue()
  4. MessageQueue sends to Telegram Bot API serially

Session lifecycle

  1. User sends /start or clicks a repo → SessionManager.startSession()
  2. ClaudeSession constructor spawns Claude CLI with --continue
  3. Each user message → ClaudeSession.send() spawns a one-shot Claude process
  4. Claude outputs stream-json events → handleJsonEvent() routes to onOutput
  5. Session ends on: /end, /stop, idle timeout, or process crash