A personal agent system built using Claude Code as the execution engine that is accessed via Telegram and relies on skills generally based on cli applications.
There is no agent framework — this approach uses the agentic capabilities that come along with Claude Code and works with any Claude Code subscription.
The agent's capabilities are dependent on the skills you choose to include. The skills shown here are just examples from my own setup. The system is designed so that you choose what capabilities to add by finding existing skills or writing your own and installing the required CLI tools. You could use it with completely different integrations.
These steps get you to the point where you can interact with Claude Code from your phone via Telegram — no skills required. Skills can be added later.
- Claude Code — install it with
npm install -g @anthropic-ai/claude-code. You can use any Claude Code subscription. - Python 3.11+
- A Telegram account
- Open Telegram and message @BotFather
- Send
/newbotand follow the prompts to name your bot - Save the bot token BotFather gives you
- Message @userinfobot on Telegram
- It will reply with your numeric user ID — save this
git clone <repo-url> claude-remote-agent
cd claude-remote-agent/telegram-bridge
python -m venv .venv
.venv/bin/pip install -e .From inside telegram-bridge/ (where you ended up in step 3):
cp .env.example .env
chmod 600 .env # restrict access — this file contains your bot tokenEdit .env with your values:
TELEGRAM_BOT_TOKEN=<your-bot-token>
TELEGRAM_ALLOWED_USER_IDS=<your-user-id>
CLAUDE_CLI_PATH=/path/to/claude # e.g., ~/.local/bin/claude
CLAUDE_WORKING_DIR=/path/to/claude_agent_remote # repo root (whatever you named it), where CLAUDE.md lives
CLAUDE_PERMISSION_MODE=acceptEdits # see note below
CLAUDE_TIMEOUT=600 # seconds per request
CLAUDE_MAX_BUDGET=2.0 # max $ per request (prevents runaway costs)Permission mode:
bypassPermissions— Claude runs autonomously without asking for approval. Necessary to run from the Telegram bot (you can't approve permissions from your phone), but means you're trusting Claude to act on its own. The security module (input blocklist, output redaction, rate limiting) provides some guardrails.
Run the bridge manually:
cd telegram-bridge
set -a && source .env && set +a
.venv/bin/python -m telegram_bridgeOpen Telegram, message your bot, and verify you get a response from Claude Code.
If you are running with systemd, the easiest approach is to create a telegram-bridge service; however you can run telegram-bridge directly or use another way to have it automatically run on startup and reload if necessary.
cp telegram-bridge/systemd/telegram-bridge.service ~/.config/systemd/user/
# Edit the service file to match your paths
systemctl --user daemon-reload
systemctl --user enable --now telegram-bridgeCheck logs with:
journalctl --user -u telegram-bridge -fOnce the bridge is working, you can start adding skills — see Adding a New Skill below.
Rather than importing an SDK or building an agent framework, this system uses claude -p (Claude Code's non-interactive mode) as a subprocess. Claude Code handles tool use, planning, error recovery, and session management. The code here handles integration plumbing.
Skills are generally being used here as instructions that rely on the use of various (mostly CLI) tools. Adding a new capability means writing a new skill, not implementing a plugin interface.
┌─────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ Telegram │────▶│ Telegram Bridge │────▶│ claude -p │
│ (phone) │◀────│ (Python bot) │◀────│ (Claude Code CLI) │
└─────────────┘ └──────────────────┘ └────────┬────────────┘
▲ │
┌──────────────────┐ │ reads
│ systemd timer │ ▼
│ writes .trigger │──────▶ ┌───────────┐
│ file to /tmp │ │ CLAUDE.md │
└──────────────────┘ │ SKILL.md×N │
│ settings │
└─────┬─────┘
│
┌────────────────────────┼────────────────────────┐
│ instructs │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ gog │ │ sonos_tool │ │ vimango-cli │
│ (Gmail/Cal/ │ │ (Sonos) │ │ (Notes) │
│ Drive) │
└──────────────┘ └──────────────┘ └──────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ playwright │ │ sncloud │ │ send_to_ │
│ -cli (NYT) │ │ (Supernote) │ │ telegram │
└──────────────┘ └──────────────┘ └──────────────┘
Every interaction — whether from Telegram or a scheduled trigger — ultimately calls:
claude -p "<prompt>" --output-format json --resume <session_id> --permission-mode <mode>Claude Code handles all the complexity: tool selection, multi-step planning, error recovery, file I/O. Our code just passes the prompt and parses the result.
A lightweight Python bot that bridges Telegram messages to Claude Code, enabling full agent capabilities (file editing, bash, tool use, all skills) from a phone.
| File | Purpose |
|---|---|
bot.py |
Telegram handlers, commands (/new, /status, /restart, /tools), message queuing, cancel button, file receiving, trigger file watcher |
claude.py |
Subprocess runner with streaming (start_claude_stream()) and batch (run_claude()) modes, JSONL parser, timeout handling |
streaming.py |
StreamingMessageEditor — accumulates text deltas and live-edits the Telegram message as Claude generates output |
formatting.py |
Markdown → Telegram HTML converter, HTML-aware message splitting (4096-char limit) |
session.py |
Session ID persistence (JSON file), model tracking per chat |
security.py |
Input blocklist, output redaction, rate limiting |
config.py |
Environment-based config (bot token, allowed users, CLI path, timeouts, trigger dir) |
The bridge maintains a JSON file mapping chat IDs to Claude session IDs. Each message resumes the previous session with --resume, giving the agent conversational context. If a session expires or isn't found, the bridge starts a new one automatically.
Location: telegram-bridge/telegram_bridge/
Some of telegram-bridge commands are system commands that do not invoke claude code but execute directly such as:
/new— start a fresh conversation/status— show CLI version, model, and session info/restart— restart the Telegram bridge service/tools— toggle tool progress display during streaming
Others are based on the skills you have, for example, for my setup some of the "commands" are:
/briefing— generate an on-demand morning briefing/nyt— generate a summary of the home page of the nyt/reminder— set a reminder for some event/list— list all available skills and slash commands
To give a skill an associated command, you just put /<skill> in the description field of its SKILL.md frontmatter.
Some skills don't have associated Telegram commands at all — they're just triggered by natural language messages that match the skill's description. For example, the sonos skill is activated whenever the user asks to play music; a command like /sonos would not do anything useful.
Skills are what give the agent its capabilities. Each skill is a markdown document (SKILL.md) that instructs Claude Code how to use a specific CLI tool or perform a specific workflow.
The typical pattern for adding a capability is: find or build a CLI tool, then write a SKILL.md that documents how to use it. Some skills (like Weather) don't need a separate CLI tool at all — they just use Claude Code's built-in web fetch to call an API directly. Claude is happy to build the skill and the CLI tool and to refine them iteratively based on your feedback until they work as you want.
The skills below are from my personal setup and are listed below as examples. Yours will be different — you add only the skills you need.
| Skill | CLI Tool | What It Does |
|---|---|---|
| briefing | multiple tools | Daily briefing: weather, calendar, emails, stock watchlist, NYT headlines |
| calendar | gog calendar |
View events, create meetings, check availability, respond to invitations |
| drive | gog drive |
Search, download, upload, and manage files on Google Drive |
| frontpage | get_front_page, send_to_telegram |
Send print newspaper front pages |
| gmail | gog gmail |
Search, read, send, label, filter, track emails across two accounts |
| list | (none — file-based) | Lists all available skills and slash commands |
| nyt | playwright-cli |
NYT top headlines fetched via DOM snapshots, organized by topic section |
| random | (none — file-based) | Quick-capture notes to a markdown file |
| reminder | remind.sh |
Timed reminders delivered via Telegram bot API |
| sonos | sonos_tool |
Music search, queue management, playback control, search via TUI using tmux |
| supernote | sncloud |
Download handwritten notes, transcribe with vision, save to vimango |
| stocks | playwright-cli, WebFetch |
Stock watchlist: price, change, market cap, 1Y performance |
| task | task (Taskwarrior) |
Task/todo management: add, list, complete, delete, modify tasks with projects and priorities |
| photo-search | photo-search |
Multimodel embeddings using the Gemini Embedding 2 model with text generated by Claude Haiku 4.5 |
| save | buku |
Save web bookmarks with auto-tagging from page content, search by keyword/tag, list and delete |
| find-photo | gog drive, vimango-cli |
Find a photo on Google Drive by filename (sent as Telegram document), optionally create a vimango note |
| vimango | vimango-cli |
Note-taking app with CRUD, full-text search, context/folder organization |
| weather | (none — uses WebFetch) | NWS forecast, adapts format for Telegram vs. terminal |
Note: In the list above, some of the cli tools were written by others with agents in mind (e.g., gog), some are based on existing cliapps like Taskwarrior, and some were written specifically for this project like sonos_tool, photo-search, and get_front_page.
Location: .claude/skills/<name>/SKILL.md
A skill is just a directory containing a SKILL.md file. Here's the process:
Create .claude/skills/<name>/SKILL.md with YAML frontmatter and markdown instructions:
---
name: my-tool
description: Use when the user asks to <do something specific>. Trigger phrases include "..."
---
# My Tool Skill
...The description field in the frontmatter is what Claude uses to decide when to activate the skill and to indicate if you want to be able to invoke the skill with a slash command. It helps to make the description specific about trigger phrases and use cases.
Add an entry to the skills table in CLAUDE.md so Claude knows the skill exists:
11. `my-tool` - description of what it does and when to use itClaude Code reads the skill docs at runtime and follows the instructions as documented. The next message you send to your bot can use the new skill immediately.
This is all just standard skill functionality that you are probably already using.
The bot includes multiple security layers to prevent credential leaks and misuse:
- User allowlist — only Telegram user IDs listed in
TELEGRAM_ALLOWED_USER_IDScan interact with the bot - Input blocklist — blocks messages that reference SSH keys, credential files, or API key variable names
- Output redaction — scans Claude's responses for API keys, bot tokens, private keys, and credential strings before sending to Telegram
- Rate limiting — 30 requests per hour per user
- Per-request budget cap —
CLAUDE_MAX_BUDGET(default $2.00) prevents any single request from running up costs - System prompt hardening — every Claude invocation is prefixed with rules forbidding credential access
- File permissions —
.envand other sensitive files arechmod 600(readable only by the owner) - systemd hardening — the service runs with
NoNewPrivileges=trueandPrivateTmp=true
See docs/security.md for the full threat model and audit.
Bot doesn't respond to messages
- Verify your Telegram user ID is in
TELEGRAM_ALLOWED_USER_IDS(you can check your ID by messaging @userinfobot) - Check the service logs:
journalctl --user -u telegram-bridge -f - Make sure the bot token is correct (you can test by messaging your bot on Telegram — if the message shows a single checkmark but never gets a double checkmark, the bot process isn't running)
"Session not found" or stale context
- Send
/newto your bot to clear the session and start fresh - The bridge handles this automatically in most cases, but
/newforces it
Service won't start
- Check logs:
journalctl --user -u telegram-bridge -f - Verify the paths in the systemd service file match your actual installation
- Make sure the
.envfile exists and is readable by your user
Claude CLI not found
- Set
CLAUDE_CLI_PATHin.envto the full path (find it withwhich claude) - Make sure Claude Code is installed and authenticated (run
claudein a terminal to check)
Responses are cut off or formatting looks wrong
- Telegram has a 4096-character message limit; the bridge splits long responses automatically
- Some markdown features (tables, nested lists) don't render well in Telegram — skills can detect the
[channel:telegram]marker and adapt their formatting
| File | Purpose |
|---|---|
CLAUDE.md |
Project instructions, skill registry, integration docs — this is the main file Claude reads to understand what it can do |
CLAUDE.local.md |
Personal context (gitignored) — your name, preferences, anything Claude should know about you |
.claude/settings.json |
Permission allowlist for CLI tools — controls which bash commands Claude is allowed to run. Only relevant when working directory in the repository and not when accessing through Telegram |
.claude/accounts.env |
Service account variables (gitignored) — e.g., Google account emails for the gog CLI |
telegram-bridge/.env |
Bot token, allowed user IDs, Claude CLI settings (gitignored) |
These are the CLI tools used by my skills. You won't need any of these unless you're using the same skills — they're listed here as examples of the kind of tools you might integrate. If you are actually interested in looking at the CLI apps, the original ones are under my github account.
| Tool | Purpose | Installed At |
|---|---|---|
gog |
Gmail, Google Calendar, Google Drive CLI, etc. written and maintained by Peter Steinberger | ~/.local/bin/gog |
vimango-cli |
Note-taking app CLI that talks to PostgreSQL-backend | $PATH |
sonos_tool |
Sonos speaker control (search, queue, playback) | $PATH |
playwright-cli |
Headless browser | $PATH |
sncloud |
Supernote Cloud file download | ~/.local/bin/sncloud |
get_front_page |
Newspaper front page image downloader | $PATH |
send_to_telegram |
Send images to Telegram via bot API | ~/.local/bin/send_to_telegram |
photo-search |
Content-based photo search (Claude vision + Gemini embeddings) | ~/photo-search/.venv/bin/photo-search |
buku |
Bookmark manager (SQLite-backed, auto-tagging) | $PATH |
task |
Taskwarrior task/todo management | /usr/bin/task |
retrieve_fp_urls |
Daily front page URL refresher (Playwright) | $PATH |