English | Deutsch | Français | 日本語 | 한국어 | Nederlands | ไทย | Tiếng Việt | 简体中文 | 繁體中文(香港) | 繁體中文(台灣)
Lightweight, Multi Bots, Multi sessions, Multi-tasking, 24/7 AI Coding Agent
Control your local AI coding agent from anywhere with Telegram.
→ Setup with one-liner:
curl -fsSL https://raw.githubusercontent.com/daocha/coding-agent-telegram/main/install.sh | bash
|
Before starting the server, make sure you have:
|
Openclaw offers you full capabilities and has integrated agent loop called Pi-Agent. It's quite comprehensive and designed for more diversified use cases. I am also Openclaw lover and I used to code with Openclaw. However, that is not the best choice for coding due to the built in large system prompt and context. Using Claude Code / Codex / Copilot for coding is still more effecient, accurate, less distracted, and straightforward. This project is quite simple, purely integrating with Codex / Copilot / Claude Code CLI. So you are directly delegating Codex / Copilot / Claude Code to work for you.
| Capability | Claude Code + Official Telegram Plugin | coding-agent-telegram (with Claude support) |
|---|---|---|
| Telegram chat with AI | ✅ | ✅ |
| Edit local code and run commands | ✅ | ✅ |
| Requires an already-running CLI session | Yes | No (automatically starts or resumes sessions) |
| Multiple AI providers | ❌ Claude only | ✅ Claude Code, Codex CLI, GitHub Copilot CLI |
| Project management from Telegram | ❌ | ✅ /project |
| Branch management from Telegram | Manual Git commands | ✅ /branch workflow |
| Create and switch sessions | Limited to active Claude session | ✅ /new, /switch, /current, /compact |
| Resume existing local CLI sessions | ❌ | ✅ |
| Cross-device session continuity | Limited | ✅ |
| Workspace concurrency protection | ❌ | ✅ Prevents multiple agents from editing the same project simultaneously |
| Task queue while an agent is busy | ❌ | ✅ |
| Independent filesystem snapshot & diff | ❌ | ✅ |
| View structured file diffs in Telegram | ❌ | ✅ |
| Secret / sensitive diff filtering | ❌ | ✅ |
| Built-in Git workflow (pull / push / commit) | Manual | ✅ |
| Multi-bot support | Multiple instances required | ✅ Managed by a single server |
| Project-level state management | ❌ | ✅ |
| Provider-agnostic architecture | ❌ | ✅ |
Key difference
The official Claude Code Telegram plugin connects Telegram to one already-running Claude Code session.
coding-agent-telegram acts as a Telegram control plane that manages projects, branches, sessions, Git workflows, and multiple coding agents (Claude Code, Codex CLI, GitHub Copilot CLI) from a single interface.
Telegram
│
▼
Claude Code Channel
│
▼
One running Claude Code session
Telegram
│
▼
coding-agent-telegram
│
├── Claude Code
├── Codex CLI
└── GitHub Copilot CLI
│
▼
Project • Branch • Session • Queue • Git • Diff • Secret Filter
curl -fsSL https://raw.githubusercontent.com/daocha/coding-agent-telegram/main/install.sh | bashpip install coding-agent-telegram
coding-agent-telegramgit clone https://github.com/daocha/coding-agent-telegram
cd coding-agent-telegram
./startup.sh# if you follow Option A or Option B, then run
coding-agent-telegram
# if you follow Option C, then run this again
./startup.shThis enables optional local Whisper-based voice-message speech-to-text for Telegram voice notes. Voice files are capped to 20MB max.
# if you installed from pip or one-liner install.sh
coding-agent-telegram-stt-install
# if you run from a cloned repository
./install-stt.shThe installer writes the STT env flags automatically after prerequisites are ready.
Estimated local footprint:
openai-whisper: about50 MBffmpegpackage: about50 MB- Whisper model downloads vary by model:
tinyabout72 MB,baseabout139 MB,large-v3-turboabout1.5 GB
Recommended env settings for the local Whisper backend:
ENABLE_OPENAI_WHISPER_SPEECH_TO_TEXT=true
OPENAI_WHISPER_MODEL=base
OPENAI_WHISPER_TIMEOUT_SECONDS=120
Notes:
- Whisper downloads the selected model automatically on first use into
~/.cache/whisper. - If you choose
OPENAI_WHISPER_MODEL=turbo, the first voice transcription is more likely to hit the timeout whilelarge-v3-turbo.ptis still downloading. - After a voice note is transcribed, the bot immediately sends the recognized transcript back to Telegram before the agent reply. If the run can start immediately it says “working on it”; if the project is busy it shows that the transcript was queued instead.
- Open Telegram and start a chat with
@BotFather. - Send
/newbot. - Follow the prompts to choose:
- a display name
- a bot username ending in
bot
- BotFather will return an HTTP API token.
- Put that token into
TELEGRAM_BOT_TOKENSin your~/.coding-agent-telegram/.env_coding_agent_telegram.
The most reliable way is to use Telegram's getUpdates API with your own bot token.
- Start a chat with your bot and send it a message such as
/start. - Open this URL in your browser, replacing
<BOT_TOKEN>:
https://api.telegram.org/bot<BOT_TOKEN>/getUpdates
- Find the
chatobject in the JSON response. - Copy the numeric
idfield from that object. - Put that value into
ALLOWED_CHAT_IDSin your~/.coding-agent-telegram/.env_coding_agent_telegram
Notes:
- For private chats, the chat ID is usually a positive integer.
- If
getUpdatesreturns an empty result, send another message to the bot and try again.
The bot currently accepts:
- Text messages
- photos
- voice messages when
ENABLE_OPENAI_WHISPER_SPEECH_TO_TEXT=trueand local Whisper prerequisites are installed - Codex and Claude Code sessions support text and image; Copilot sessions currently support text only. Video is not supported by any provider.
/provider |
Choose the provider for new sessions. The selection is stored per bot and chat until you change it. |
/project <project_folder> |
Set the current project folder. If the folder does not exist, the app creates it and marks it trusted. If it already exists and is still untrusted, the app asks you to trust it explicitly. |
/branch <new_branch> |
Prepare or switch a branch for the current project. If the branch already exists, the bot treats that branch as the source candidate. Otherwise it uses the repository default branch as the source candidate. |
/branch <origin_branch> <new_branch> |
Prepare or switch a branch using <origin_branch> as the source candidate. For both forms, the bot then offers the source choices that actually exist: local/<branch> origin/<branch> If only one of those exists, only that option is shown. If neither exists, the bot tells you the branch source is missing. |
/current |
Show the active session for the current bot and chat. |
/status |
Show each provider's quota usage: 5-hour and weekly usage percentages, with reset times. Never makes a paid API call: Codex is always a free local query, and Claude's numbers are reused only from your most recent real Claude activity through the bot, shown as "last observed X ago" (Pro/Max accounts logged in via OAuth only). The two windows are tracked separately, so if one has passed its reset time (or nothing's been observed yet) it shows N/A until your next Claude turn refreshes it, even while the other window still has fresh data. Copilot has no supported API for this and is reported as unavailable. |
/new [session_name] |
Create a new session for the current project. If you omit the name, the bot uses the real session ID. If provider, project, or branch is missing, the bot guides you through the missing step. |
/switch |
Show the latest sessions, newest first. The list includes both bot-managed sessions and local Codex/Copilot/Claude Code CLI sessions for the current project. |
/switch page <number> |
Show another page of stored sessions. |
/switch <session_id> |
Switch to a specific session by ID. If you choose a local CLI session, the bot imports it and continues from there. |
/compact |
Create a fresh compacted session from the active session and switch to it. |
/commit <git commands> |
Run validated git commit-related commands inside the active session project. Available only when ENABLE_COMMIT_COMMAND=true. Mutating git commands require a trusted project. |
/diff |
Show changed filenames for the active session project, separated into tracked and untracked files. Tracked files include inline buttons to open per-file diffs. |
/pull |
Pull from origin for the active session branch after confirmation. The bot also refreshes the default branch when applicable. |
/push |
Push origin <branch> for the current active session. The bot asks for confirmation before pushing. |
/abort |
Abort the current agent run for the current project. If queued questions are waiting, the bot asks whether to continue them. |
CODING_AGENT_TELEGRAM_ENV_FILE |
Use this if you want to point the app to a specific env file. |
~/.coding-agent-telegram/.env_coding_agent_telegram |
Default env file location. |
./.env_coding_agent_telegram |
Used only if this local file already exists. |
WORKSPACE_ROOT |
Parent folder that contains your project directories. |
TELEGRAM_BOT_TOKENS |
Comma-separated Telegram bot tokens. |
ALLOWED_CHAT_IDS |
Comma-separated Telegram private chat IDs allowed to use the bot. |
APP_LOCALE |
UI locale for shared bot messages and command descriptions. Supported values: en, de, fr, ja, ko, nl, th, vi, zh-CN, zh-HK, zh-TW. |
DEFAULT_AGENT_PROVIDER |
Default provider for new sessions: codex, copilot, or claude. Default: codex. |
CODEX_BIN |
Command used to launch Codex CLI. The app would try to detect the locally installed Codex path when initializing the .env_coding_agent_telegram. Alternatively use which codex to view the path. |
COPILOT_BIN |
Command used to launch Copilot CLI. The app would try to detect the locally installed Copilot path when initializing the .env_coding_agent_telegram. Alternatively use which copilot to view the path. |
CLAUDE_BIN |
Command used to launch Claude Code CLI. The app would try to detect the locally installed Claude path when initializing the .env_coding_agent_telegram. Alternatively use which claude to view the path. |
CODEX_MODEL |
Optional Codex model override.
Leave empty to use the Codex CLI default model.
Example: gpt-5.4
OpenAI Codex/OpenAI models
|
COPILOT_MODEL |
Optional Copilot model override.
Leave empty to use the Copilot CLI default model.
Examples: gpt-5.4, claude-sonnet-4.6
GitHub Copilot supported models
|
CLAUDE_MODEL |
Optional Claude Code model override.
Leave empty to use the Claude Code CLI default model.
Examples: sonnet, opus, haiku
Claude Code model configuration
|
CODEX_APPROVAL_POLICY |
Approval mode passed to Codex. Default: never. |
CODEX_SANDBOX_MODE |
Sandbox mode passed to Codex. Default: workspace-write. |
CODEX_SKIP_GIT_REPO_CHECK |
If enabled, always bypass Codex trusted-repo checks. |
CLAUDE_PERMISSION_MODE |
Permission mode passed to Claude Code. One of default, acceptEdits, plan, auto, dontAsk, bypassPermissions, manual. Default: bypassPermissions (fully autonomous, since there is no interactive terminal to approve prompts). |
CLAUDE_ALLOWED_TOOLS |
Comma-separated Claude Code tool allowlist, using Claude Code's permission rule syntax. Example: Read,Edit,Bash(git *) |
CLAUDE_DISALLOWED_TOOLS |
Comma-separated Claude Code tool denylist. Example: Bash(rm *) |
ENABLE_COMMIT_COMMAND |
Enable the /commit Telegram command. Default: false. |
AGENT_HARD_TIMEOUT_SECONDS |
Hard timeout for a single agent run. Default: 0 (disabled). |
LONG_GAP_WARNING_ENABLED |
Before resuming a session that has been idle a while and has accumulated enough context for a reprocess to be costly, warn that the provider's prompt cache has likely expired — with buttons to compact first or proceed anyway. Default: true. See the FAQ below. |
CLAUDE_LONG_GAP_SECONDS |
Idle threshold in seconds before the warning fires for Claude Code sessions. Default: 3600 (1 hour, matching Claude Code's extended prompt-cache window). |
CODEX_LONG_GAP_SECONDS |
Idle threshold in seconds before the warning fires for Codex sessions. Default: 3600 (1 hour, matching Claude's threshold; Codex/OpenAI don't document an idle-based cache-expiry number, and Codex's own cache is generally shorter-lived than Claude's anyway, so there's no accuracy cost to matching it — paired with a size gate so small sessions don't nag). |
COPILOT_LONG_GAP_SECONDS |
Idle threshold in seconds before the warning fires for Copilot sessions. Default: 0 (disabled). GitHub's own docs state Copilot CLI has no inactivity timeout and already auto-compacts its own context natively (~80-95% usage) — there's no idle-based risk to warn about here, so this defers to Copilot's own mechanism instead of inventing one. Set a positive value to opt into an idle-based nudge anyway. |
SNAPSHOT_TEXT_FILE_MAX_BYTES |
Maximum file size the bot will read as text when building the before/after snapshot for per-run diffs. Default: 200000. |
MAX_TELEGRAM_MESSAGE_LENGTH |
Max message size used before the app splits responses. Default: 3000 |
ENABLE_SENSITIVE_DIFF_FILTER |
Hide diffs for sensitive files. Default: true> |
ENABLE_SECRET_SCRUB_FILTER |
Redact tokens, keys, .env values, certificates, and similar secret-like output before sending it to Telegram. Default true (Strongly recommended) |
SNAPSHOT_INCLUDE_PATH_GLOBS |
Force-include matching paths in diffs. Example: .github/*,.profile.test,.profile.prod |
SNAPSHOT_EXCLUDE_PATH_GLOBS |
Add extra diff exclusions on top of the packaged defaults.
Example: .*,personal/*,sensitive*.txt
Note: .* matches hidden paths, including files inside hidden directories. |
ENABLE_OPENAI_WHISPER_SPEECH_TO_TEXT |
Default: false. If true, it enables the audio messages capability. System will check the prerequisites regarding required binaries or libraries on startup. |
OPENAI_WHISPER_MODEL |
Model for the Whisper SST. Default: baseAvailable models: tiny about 72 MB, base about 139 MB, large-v3-turbo about 1.5 GBModels will be automatically downloaded on your first voice message. Recommended: base for general usage. If you want better accuracy and quality, you can try with turbo
|
OPENAI_WHISPER_TIMEOUT_SECONDS |
Default: 120Timeout for the STT process. Usually the STT processing is fast enough. |
~/.coding-agent-telegram/state.json |
Main session state file. |
~/.coding-agent-telegram/state.json.bak |
Backup state file. |
~/.coding-agent-telegram/logs |
Log directory. |
Example:
APP_LOCALE=en
WORKSPACE_ROOT=~/git
TELEGRAM_BOT_TOKENS=bot_token_one
ALLOWED_CHAT_IDS=123456789
DEFAULT_AGENT_PROVIDER=codex
CODEX_BIN=codex
COPILOT_BIN=copilot
CLAUDE_BIN=claude
CODEX_APPROVAL_POLICY=never
CODEX_SANDBOX_MODE=workspace-write
CLAUDE_PERMISSION_MODE=bypassPermissions
ENABLE_SENSITIVE_DIFF_FILTER=true
ENABLE_SECRET_SCRUB_FILTER=trueSessions are scoped by:
- Telegram bot
- Telegram chat
That means the same Telegram account can use multiple bots without mixing sessions.
Example:
- Bot A + your chat -> backend work
- Bot B + your chat -> frontend work
- Bot C + your chat -> infra work
The active session is also tied to:
- project folder
- provider
- branch name when available
Each session stores:
- session name
- project folder
- branch name
- provider
- timestamps
- active session selection for that bot/chat scope
Only one agent run can be active per project folder at a time — regardless of which chat ID or Telegram bot triggers it.
This is different from “an agent is still processing the current question”:
- project is busy means the workspace already has one live agent run
- agent is busy means that one live run is still working on the current request
The bot enforces one active run per project on purpose so two agents do not write to the same workspace at the same time. That avoids conflicting edits and reduces the chance of data corruption.
If a message arrives while an agent is already running on the same project, the bot immediately replies:
⏳ An agent is already running on project '…'. Please wait for it to finish.
The lock is held in memory (not on disk), so it is automatically released when the agent finishes, errors out, or if the server restarts. There are no stale lock files to clean up after a crash.
If the current project already has one live agent run, later text messages are not rejected. They are queued instead:
- the new question is appended to a queued-questions file on disk
- the current agent keeps working on the earlier request
- when that run finishes normally, the bot automatically starts processing the queued questions next
If the current run is aborted and there are queued questions waiting, the bot does not auto-continue. It asks whether you want to continue processing the remaining queued questions. You can choose to batch process or one-by-one.
During each agent run, the bot also takes a lightweight before/after project snapshot so it can summarize changed files and send diffs back to Telegram. This snapshot is taken by the bot app itself, not by Codex, Copilot, or Claude Code.
Snapshot notes:
- the app walks the project directory before and after the run
- for normal text files, the app prefers the per-run snapshot diff rather than a git-head diff
- common dependency, cache, and runtime directories are also skipped
- binary files and files larger than
SNAPSHOT_TEXT_FILE_MAX_BYTESare not loaded as text - for huge projects, this extra scan can add noticeable I/O and memory overhead
- if the snapshot cannot represent a file as text, the app falls back to git diff when possible
- for large or non-text files, the diff may still be omitted and replaced with a short unavailable message
Snapshot exclusion rules live in package resource files:
src/coding_agent_telegram/resources/snapshot_excluded_dir_names.txtsrc/coding_agent_telegram/resources/snapshot_excluded_dir_globs.txtsrc/coding_agent_telegram/resources/snapshot_excluded_file_globs.txt
You can override those defaults in the env file without editing the installed package:
-
SNAPSHOT_INCLUDE_PATH_GLOBSForce-include matching paths in diffs. Example:.github/*,.profile.test,.profile.prod -
SNAPSHOT_EXCLUDE_PATH_GLOBSAdd extra diff exclusions on top of the packaged defaults. Example:.*,personal/*,sensitive*.txtNote:.*matches hidden paths, including files inside hidden directories.
If both include and exclude rules match, the include rule wins.
The bot treats project and branch as a bundle.
- choosing a project does not silently choose an unrelated branch
- if branch input is needed, the bot asks you to pick it
- when branch information is printed in session-related messages, project and branch are shown together
When you create or change a branch, the bot guides you through the source explicitly:
local/<branch>means use the local branch as the sourceorigin/<branch>means update from the remote branch first and then switch
If the bot sees that the stored session branch and the repository's current branch do not match, it does not blindly continue. It asks which branch you want to use:
- keep the stored session branch
- keep the current repository branch
If your preferred source branch is missing, the bot offers fallback source choices based on the default branch and current branch instead of leaving you at a raw git error.
- Existing folders follow
CODEX_SKIP_GIT_REPO_CHECK - Folders created through
/project <name>are marked as trusted by this app - Existing folders selected through
/project <name>remain untrusted until you confirm trust in the Telegram prompt - That means newly created project folders can be used immediately
/commitcan be disabled entirely withENABLE_COMMIT_COMMAND- Mutating
/commitoperations are allowed only for trusted projects
Logs are written to both stdout and a rotating log file under:
~/.coding-agent-telegram/logs(rotated at 10 MB, 3 backups kept)
Note: Because messages go to both stdout and the log file, watching the terminal and tailing the log file at the same time (e.g.
tail -f ~/.coding-agent-telegram/logs/coding-agent-telegram.log) will make each message appear twice — once from each sink. This is expected behavior. View one or the other, not both simultaneously.
Typical logged events
- bot startup and polling start
- project selection
- session creation
- session switching
- active session reporting
- normal run execution (includes an audit log line with the truncated prompt)
- session replacement after resume failure
- warnings and runtime errors
-
src/coding_agent_telegram/Main application code -
tests/Test suite -
startup.shLocal bootstrap and startup entrypoint -
src/coding_agent_telegram/resources/.env.exampleCanonical environment template used by both repo startup and packaged installs -
pyproject.tomlPackaging and dependency configuration
Package versions are derived from Git tags.
- TestPyPI/testing:
v2026.3.26.dev1 - PyPI prerelease:
v2026.3.26rc1 - PyPI stable:
v2026.3.26
Why doesn't claude --resume in a plain terminal show sessions created from Telegram?
This is expected Claude Code CLI behavior, not a bug in this app.
Sessions created by this bot run through Claude Code's headless -p/print mode. Claude Code tags any session started that way with entrypoint: "sdk-cli" in its transcript, versus entrypoint: "cli" for a session you start by typing claude directly in a terminal. The interactive claude --resume picker (with no session ID) only lists cli-entrypoint sessions — it deliberately hides headless/SDK-driven runs, treating them as automation output rather than conversations meant to be picked back up by hand.
The session data itself is not lost or different — it is a normal, fully resumable Claude Code session stored under ~/.claude/projects/<encoded-project-path>/<session-id>.jsonl. You can resume it directly once you have the ID:
claude --resume <session-id>This is exactly why this app ships its own session discovery (used by /switch) instead of relying on the native picker — it scans the JSONL files directly and matches them by project path, so Telegram-created sessions show up there even though they never appear in a plain claude --resume.
Codex and Copilot don't make this interactive-vs-headless distinction in their own resume/list commands, which is why sessions from those providers still show up fine in a plain terminal.
Does this app burn more tokens than using the Claude Code terminal directly?
Not because of some inherent per-call overhead difference — headless (-p) and interactive Claude Code use the same underlying protocol and pricing. But in practice, 24/7 Telegram usage can burn noticeably more tokens than typical terminal usage, for two compounding reasons:
- Sessions can grow unbounded. Since the bot conveniently resumes the same session across hours or days, a session can accumulate hundreds of turns and megabytes of transcript if you never rotate it. In an interactive terminal you'd more naturally finish a task and start fresh next time, keeping context smaller.
- Idle gaps between Telegram messages expire the prompt cache. Claude's prompt cache has a short TTL. If you reply within that window, follow-up turns are cheap cache reads. If there's a long gap (e.g. you go to sleep and reply the next morning), the entire accumulated context has to be reprocessed from scratch as a much more expensive cache-write on your next message — and this cost grows with how large the session has already become. This is why usage can spike right when you send your first message of the day, even before "peak" hours.
Mitigation: periodically run /compact on long-lived sessions (this app supports it as a Telegram command) instead of letting one session run indefinitely, especially if you notice it's been idle for a long stretch. Starting a fresh /new session for unrelated work also helps keep context — and cost — bounded.
The app also does this automatically, combining two signals per provider so it only interrupts you when it's actually likely to matter: an idle-time threshold (CLAUDE_LONG_GAP_SECONDS / CODEX_LONG_GAP_SECONDS / COPILOT_LONG_GAP_SECONDS) and how much context the session has already accumulated (skipping the warning for small/cheap sessions even if they've been idle a while, since reprocessing those from scratch is negligible anyway). Defaults: 1 hour for both Claude Code and Codex — Claude's number has real evidence behind it (see above), and while OpenAI doesn't document one for Codex, Codex's own prompt cache is generally shorter-lived than Claude's anyway, so matching Claude's threshold costs nothing in accuracy and just means fewer interruptions, especially now paired with the size gate; and disabled by default for Copilot, because GitHub's own docs state Copilot CLI has no inactivity timeout at all and already auto-compacts its own context natively (around 80–95% usage) — there's nothing idle-related to warn about there, so this defers to Copilot's own mechanism rather than inventing one. Set COPILOT_LONG_GAP_SECONDS to a positive value if you want an idle-based nudge for Copilot anyway.
When the threshold and size gate are both met, it holds your message and asks:
⏳ This session has been idle for {gap}. Resuming it now will likely reprocess the whole conversation from scratch (the provider's response cache has probably expired), which can burn significantly more tokens than usual. Compacting also reprocesses the current context once to write its summary, so it can burn a lot of tokens too if this session is already large. Switching to a new session skips that reprocessing entirely, but starts with no memory of this conversation. Switch to a new session, compact first, or proceed anyway?
[🆕 Switch to new session] [🔄 Compact first] [
⚠️ Proceed anyway]
Note that /compact itself is not free of this cost: it works by resuming the current (possibly cold) session and asking it to summarize itself, so it still pays the same one-time full-transcript reprocess as just replying would — it just means you only pay it once instead of on every subsequent turn, since the resulting session starts small. Switch to new session is the only option that avoids that reprocess altogether: it abandons the old session's context without ever resuming it and starts completely fresh, at the cost of losing that context entirely rather than compressing it into a summary.
Choosing Switch to new session starts a brand-new, empty session and continues with your message there — named after the old session with an incrementing -newN suffix (e.g. fix-bug → fix-bug-new1 → fix-bug-new2 if you switch again), so you can still tell it apart from the original in /switch. Choosing Compact first summarizes the session, starts a fresh one from that summary, and then continues with your message on the new session — named similarly but with a -resumeN suffix instead (e.g. fix-bug → fix-bug-resume1 → fix-bug-resume2 on the next compaction). Choosing Proceed anyway just continues on the existing session as normal. Disable the whole check with LONG_GAP_WARNING_ENABLED=false.
Claude sessions suddenly fail with "Failed to authenticate: OAuth session expired and could not be refreshed"
This can happen even when claude auth status reports you're logged in, and even right after you've logged in again. The interactive OAuth session Claude Code normally uses can stop working specifically for the headless, detached-subprocess way this bot creates sessions, without your actual login being at fault — this has been observed after a Claude Code CLI auto-update, and can also be intermittent.
If a user hits it live via /new, a resumed session, or any message, the bot recognizes this specific failure and replies with fix instructions immediately, instead of the raw CLI error.
To fix it, on the host running the bot:
-
Run
claude setup-tokenand approve access in the browser it opens. The actual token is then printed back in your terminal (it starts withsk-ant-oat01-), not shown anywhere in the browser — copying something from the browser page itself instead of the terminal is a common mistake and won't work. This creates a long-lived (about 1 year) authentication token, which is Anthropic's own supported mechanism for headless/automated use (the same one used for GitHub Actions) — unlike the interactive OAuth session, it doesn't depend on Keychain/session refresh working from a detached background process, so once set it isn't expected to need touching again until it's due to expire. -
Copy the token it prints, then save it with whichever command matches how you run this app — the bot's own reply picks the right one automatically, but for reference:
- Installed via
pipor the one-lineinstall.sh(Quick Start Options A/B):coding-agent-telegram claude-auth <token> - Running from a cloned repository with
./startup.sh(Quick Start Option C):./startup.sh claude-auth <token>
Either command saves the token as
CLAUDE_CODE_OAUTH_TOKENin your env file and immediately re-checks Claude auth, so you get a pass/fail right away instead of a blind restart-and-hope. - Installed via
-
Restart the bot so the running process picks up the change.
You can also skip the command and set CLAUDE_CODE_OAUTH_TOKEN=<token> directly in your env file (.env_coding_agent_telegram) yourself — the two commands above are just a convenience wrapper around doing exactly that, plus verification.
- This project is designed for users running the agents locally on their own machine.
- The Telegram bot is a control surface, not the execution environment itself.
- If you run multiple bots, all of them can be managed by one server process.
