Your personal AI assistant, right in your pocket.
Orchestrate Claude Code from Telegram — parallel research, iterative loops, multi-project workflows.
Most Claude Telegram bots are simple API wrappers. Pocket Claude is different — it runs Claude Code CLI as a subprocess, giving you the full power of Claude Code from your phone:
- MCP Tools — Slack, Notion, Gmail, and any configured MCP server
- File Operations — Read, write, edit files on your machine
- Shell Commands — Execute terminal commands with your approval
- Multimodal — Send photos and documents for analysis
- Session Memory — Conversations persist across messages
Think of it as SSH-ing into Claude Code, but through Telegram.
- Instant Processing — Messages processed immediately via
claude -p(no polling delay) - Session Management — Conversations persist via explicit
--resume <session_id>tracking - Session Resume — Switch between previous conversations via inline keyboard (
/resume) - Session Naming — Label sessions for easy identification (
/name) - Context Notes — Add context without triggering full processing (
/btw) - Interactive Permissions — Approve/deny tool access via inline buttons with detailed tool info
- Model Switching — Change models on the fly (
/model sonnet,/model opus) - Photo & File Support — Send photos, screenshots, or documents for Claude to analyze
- Extended Directory Access — Access files outside the project via
CLAUDE_ADD_DIRS - Message TTL — Auto-expire stale messages (default 10 min), preventing retry loops
- Smart Error Handling — Restart kills retry silently; real errors notify and retry up to 3x
- Single Instance Guard — PID file prevents duplicate instances; auto-kills previous on start
- Typing Indicator — "typing..." shown in Telegram while Claude processes
- Multi-Project Support — Switch between repos at runtime via
/project, search for git repos with/project search, each with isolated sessions and cost tracking - Background Tasks — Run long tasks in parallel via
/bgwhile continuing to chat. Up to 3 concurrent slots with independent sessions, permissions, and cost tracking - Ralph Loop — Iterative autonomous execution via
/ralph. Claude repeats a task across iterations, seeing its previous work each time. Auto-completes or stops on safety limits - Plan Mode —
/planasks Claude to analyze and plan without executing. Review, modify via conversation, then say "execute" when ready - Usage Tracking — Per-project messages and API-equivalent cost via
/usageand/project info - Queue Notifications — "Queued (#N)" when worker is busy with another request
- Structured Logging — Logs to both stdout and file with timestamps and levels
Telegram (phone) — text, photos, files
| HTTPS Long Polling
v
Go Bot (local machine, single instance via PID file)
|-- Save to inbox.json (pending)
|-- Download attachments to /tmp (if photo/file)
|-- Worker --> ProjectManager --> claude -p --resume <session_id> (subprocess)
| | |-- projects["default"] → Executor (workDir: "./")
| | |-- projects["my-app"] → Executor (workDir: "/path/to/my-app")
| | '-- projects["api"] → Executor (workDir: "/path/to/api")
| |-- Permission denied? --> Inline keyboard [Allow] [Deny]
| '-- Approved? --> Re-execute with --dangerously-skip-permissions
|-- Background Pool (up to 3 concurrent)
| |-- /bg <task> --> ephemeral Executor --> independent session
| '-- Separate approval flow (bg_ prefix routing)
'-- Send result to Telegram + record in outbox.json (audit)
- Go 1.23+
- Claude Code CLI installed and authenticated (
claudecommand available in your terminal) - A Telegram bot token and your chat ID (setup guide below)
- Open Telegram and search for @BotFather
- Send
/newbotand follow the prompts to name your bot - BotFather will give you a bot token like
123456789:ABCdefGHIjklMNOpqrsTUVwxyz— save this
- Search for @userinfobot on Telegram
- Send any message to it
- It replies with your chat ID (a number like
123456789) — save this
Only messages from this chat ID will be processed. All others are silently ignored.
git clone https://github.com/GrapeInTheTree/pocket-claude.git
cd pocket-claude
go mod download
cp .env.example .envEdit .env with your values:
TELEGRAM_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz # from BotFather
TELEGRAM_CHAT_ID=123456789 # from userinfobotThat's it for the required config. Optional settings:
All Environment Variables
| Variable | Default | Description |
|---|---|---|
TELEGRAM_TOKEN |
(required) | Bot token from BotFather |
TELEGRAM_CHAT_ID |
(required) | Your chat ID (all others ignored) |
CLAUDE_WORK_DIR |
. |
Working directory for CLI |
CLAUDE_MODEL |
(none) | Model override (e.g., sonnet, opus) |
CLAUDE_SYSTEM_PROMPT |
(none) | Custom system prompt |
CLAUDE_ADD_DIRS |
~ |
Extra directories Claude can access |
CLAUDE_TIMEOUT_SECONDS |
1200 |
CLI execution timeout (20 min) |
CLAUDE_CLI_PATH |
claude |
Claude CLI binary path |
MESSAGE_TTL_MINUTES |
10 |
Auto-expire messages older than this |
MAX_RETRY_COUNT |
3 |
Max retries for failed messages |
WORKER_QUEUE_SIZE |
100 |
Processing queue capacity |
LOG_FILE |
./bot.log |
Log file path |
PROJECTS_FILE |
./projects.json |
Project persistence file |
go build -o pocket-claude ./cmd/pocket-claude/
./pocket-claudemake test # run all tests (65 cases)
make test-race # with race detector
make ci # full CI pipeline locally (fmt + vet + build + test)Or directly:
go test ./... # run tests
go test -race ./... # with race detector
go vet ./... # static analysis
gofmt -l . # check formattingThe PID file (
bot.pid) ensures only one instance runs at a time. Restarting automatically kills the previous instance.
Send /setcommands to @BotFather:
help - Show available commands
new - Start a new conversation
name - Rename current session
resume - Resume a previous session
btw - Add context note
model - Switch AI model
project - Switch, search, or manage projects
bg - Run background tasks
ralph - Iterative auto-loop
plan - Plan then execute
cancel - Cancel current processing
usage - Token cost tracking
status - Message queue status
clear - Clean up completed messages
retry - Force retry error messages
| Command | Description |
|---|---|
/help |
Show available commands |
/new |
Start a new conversation (reset session) |
/name <text> |
Rename current session (shown in /resume) |
/resume |
Select a previous session via inline buttons |
/btw <note> |
Add context note without full processing |
/model <name> |
Switch model (sonnet, opus, haiku) |
/project |
Switch project via inline buttons |
/project info |
Current project details + usage |
/project add <name> <path> |
Add a new project (validates path) |
/project search <keyword> |
Search git repos and add via buttons |
/project rename <old> <new> |
Rename a project |
/project remove <name> |
Remove a project |
/bg <message> |
Run task in background (current project) |
/bg <project> <message> |
Run background task in specific project |
/bg status |
Show running background tasks |
/bg inject <id> |
Inject completed result into current session |
/bg cancel <id> |
Cancel a background task |
/ralph <message> |
Iterative auto-loop until task is done |
/ralph <msg> --max <N> |
Set max iterations (default 5) |
/ralph status |
Show running loops with iteration progress |
/ralph cancel <id> |
Cancel a loop |
/plan <message> |
Plan first, execute on approval |
/cancel |
Cancel the currently processing foreground message |
/usage |
Show API-equivalent cost and message count (per project) |
/status |
Show message queue status (+ background task count) |
/clear |
Remove completed/failed/expired messages |
/retry |
Force retry error and failed messages |
1. You send a message (text, photo, or file) on Telegram
2. If photo/file: bot downloads attachment to a temp file
3. Bot saves message to inbox.json with status "pending"
4. Worker checks TTL — skips if older than MESSAGE_TTL_MINUTES
5. Worker calls: claude -p "message" --resume <session_id> --output-format json
6. If permission denied:
- Bot shows inline keyboard with tool details
- [Allow] → re-execute with --dangerously-skip-permissions
- [Deny] → return partial result
7. Result sent to Telegram + recorded in outbox.json
pending --> processing --> sent
| |
expired error --> (auto-retry up to 2x) --> pending
|
failed --> (permanent, /retry to reset)
| Status | Description |
|---|---|
pending |
Waiting to be processed |
processing |
Claude CLI is running |
sent |
Result delivered to Telegram |
error |
Failed, will auto-retry |
failed |
Max retries exceeded (use /retry to reset) |
expired |
TTL exceeded, skipped |
Sessions are tracked using explicit --resume <session_id>, ensuring the bot never conflicts with other Claude Code instances in the same directory.
You: "Search for Daniel on Slack"
Bot: "Found Daniel (Product - Defi)..."
You: "Send him a DM saying hello" <-- Claude remembers "him" = Daniel
Bot: "DM sent!"
/name slack-daniel <-- Label this session
/new <-- Start fresh
/resume <-- See all sessions, tap to switch
Work across multiple repositories without restarting the bot. Each project gets its own sessions, working directory, and cost tracking.
Search and add repos:
/project search my-app <-- Scans ~/... for git repos
🔍 Found 2 repo(s) matching "my-app"
Tap to add as project:
[+ my-app (~/projects/my-app)]
[+ my-app-v2 (~/work/my-app-v2)] <-- Tap to add instantly
Or add manually:
/project add api /Users/me/api-server <-- Path is validated
Switch between projects:
/project <-- Inline keyboard
📂 Projects (2)
Active: default
[▶ default (.)]
[ my-app (~/projects/my-app)]
/project my-app <-- Or direct switch by name
You: "Run the tests" <-- Executes in my-app's directory
/project default <-- Switch back, session preserved
/usage <-- Shows cost for active project
Other project commands:
/project info <-- Current project details + usage
/project rename my-app frontend <-- Rename without re-adding
/project remove api <-- Remove (can't remove active/default)
Projects persist to projects.json and survive bot restarts.
Run long-running tasks in the background while continuing to chat. Each background task gets its own ephemeral Executor with an independent session, so there's no interference with your foreground conversation.
/bg analyze the entire codebase and find security issues
🔄 Background task started
🆔 bg_1710756000123
📂 Project: my-app
💬 analyze the entire codebase and find security issues
Use /bg status to check progress.
You can keep chatting normally while it runs:
You: "What's in the README?" <-- processed immediately
Bot: "The README contains..."
/bg status
🔄 Background Tasks
🔄 bg_1710756000123 [running] analyze the entire codebas...
📂 my-app | ⏱ 1m23s
Slots: 1/3
Run tasks in other projects without switching:
/bg api-server run all integration tests <-- runs in api-server project
When a background task finishes:
✅ Background Task Done
📂 Project: my-app
🆔 bg_1710756000123
📋 📖Read ×12 🔎Grep ×8 ⚡Bash ×3 | 95s | $0.0847
⏱ 1m35s
Found 3 potential security issues...
Inject results into your main conversation:
/bg inject bg_1710756000123
💉 Injected bg_1710756000123 into current session.
You: "Fix issue #3 from that analysis" <-- Claude knows the context
Bot: "Fixed the SQL injection in auth.go..."
This "independent analysis → selective context merge" workflow is unique to Pocket Claude. Background tasks have their own permission flow — if a background task needs approval, you'll see a separate inline keyboard tagged with the task ID, so it won't interfere with foreground approvals.
Named after the Ralph Wiggum plugin for Claude Code. Claude repeats a task across multiple iterations, seeing its previous work each time. Solves the 10-minute timeout problem for large tasks.
/ralph refactor all handlers to use dependency injection --max 5
🔁 Ralph loop started
🆔 bg_123 | 📂 my-app | 🔄 Max: 5
🔁 Ralph [bg_123] Iteration 1/5
📂 my-app | 💰 $0.0234
Refactored UserHandler and AuthHandler...
🔁 Ralph [bg_123] Iteration 2/5
📂 my-app | 💰 $0.0512
Refactored remaining handlers, updated tests...
✅ Ralph Done (2/5 iterations)
📂 my-app | 💰 $0.0512 | ⏱ 4m32s
🏁 completed
Safety limits: max 20 iterations, $1.00 cost limit, stall detection (3 iterations without progress).
Ask Claude to plan before executing. Runs in your main session, so you can review and modify naturally.
/plan add authentication to the API
Claude: Here's my plan:
1. Create middleware/auth.go with JWT verification
2. Add token validation in router.go
3. Write tests in auth_test.go
You: "3번을 OAuth로 바꿔줘" <-- natural conversation
Claude: Updated plan: ...
You: "좋아 실행해" <-- Claude remembers the plan
Claude: Done! Created 3 files...
When Claude needs tools that require approval:
+------------------------------------------+
| Permission Required |
| |
| * Terminal Command |
| gcloud auth login --cred-file=... |
| * File Write |
| write -> /Users/.../config.json |
| |
| Claude: I need to set up auth... |
| |
| Expires in 2 min |
| |
| [ Allow ] [ Deny ] |
+------------------------------------------+
| Error Type | Behavior |
|---|---|
signal: killed (restart) |
Silent retry, no notification |
/cancel |
Mark as failed, no retry |
| Timeout / CLI error | Notify user, auto-retry up to 2x |
| Max retries exceeded | Mark as failed, notify user |
| Message too old (TTL) | Mark as expired, skip silently |
pocket-claude/
+-- cmd/
| +-- pocket-claude/
| +-- main.go # Entry point, wiring, graceful shutdown
+-- internal/
| +-- config/
| | +-- config.go # Config loading, logger, PID file
| +-- store/
| | +-- models.go # Data types, 7 status constants
| | +-- store.go # JSON file I/O, mutex, lock file
| +-- bot/
| | +-- bot.go # Telegram listener, callbacks, outbox poller
| | +-- commands.go # 12 commands with inline keyboards
| | +-- media.go # Photo/document download
| +-- claude/
| | +-- executor.go # CLI execution, --resume session tracking
| +-- project/
| | +-- types.go # ProjectConfig, ProjectsFile, ProjectUsage types
| | +-- manager.go # Multi-project executor routing, persistence
| +-- worker/
| +-- worker.go # Message queue, TTL, error classification
| +-- approval.go # Permission flow, tool name formatting
| +-- background.go # Background task pool (3 concurrent slots)
| +-- ralph.go # Ralph iterative loop
+-- .github/
| +-- workflows/
| +-- ci.yml # GitHub Actions: build, vet, fmt, test -race
+-- Makefile # make build/test/ci/fmt/run
+-- .env.example
+-- LICENSE # MIT
+-- CLAUDE.md # Project context for Claude Code
+-- CHANGELOG.md # Version history
Create ~/Library/LaunchAgents/com.pocket-claude.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.pocket-claude</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/pocket-claude</string>
</array>
<key>WorkingDirectory</key>
<string>/path/to/pocket-claude</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/pocket-claude.log</string>
<key>StandardErrorPath</key>
<string>/tmp/pocket-claude.err</string>
</dict>
</plist>launchctl load ~/Library/LaunchAgents/com.pocket-claude.plist- Only messages from your
TELEGRAM_CHAT_IDare processed; all others are silently ignored - Permission-sensitive tools require explicit approval via inline buttons
- Sessions use explicit
--resume <id>— never conflicts with other Claude instances - Goroutine spawning is bounded by semaphore (max 10 concurrent handlers)
- All Telegram messages are UTF-8 sanitized before sending
.envcontains secrets — never commit it- Bot token can be revoked instantly via BotFather
- Requires the host machine to be running (macOS sleep will pause the bot)
- Response time depends on Claude's processing (typically 5-30 seconds)
- Session history is in-memory — session list resets on bot restart (sessions themselves persist in Claude)
- Background tasks are in-memory — lost on bot restart (max 3 concurrent)
- Each message consumes Claude Plan usage (Pro/Max recommended)
- Single user only (
TELEGRAM_CHAT_IDsupports one ID)
Contributions are welcome! Please open an issue or submit a pull request.