Control Claude Code and Codex CLI from Telegram. Live streaming, sessions, cron jobs, webhooks, Docker sandboxing.
Project description
Claude Code, Codex CLI, and Gemini CLI as your Telegram assistant.
Persistent memory. Scheduled tasks. Live streaming. Docker sandboxing.
Uses only official CLIs. Nothing spoofed, nothing proxied.
Quick start · Why ductor? · Features · Prerequisites · How it works · Commands · Docs · Contributing
ductor runs on your machine, uses your existing CLI authentication, and keeps state in plain JSON/Markdown under ~/.ductor/.
You can:
- chat from Telegram with Claude/Codex/Gemini,
- stream responses live into edited Telegram messages,
- offload one-off tasks via
/bgand receive a final result when they finish, - run cron jobs and webhooks,
- let heartbeat checks proactively notify you,
- isolate runtime with Docker.
Left: /start screen, right: quick action callbacks
Quick start
pipx install ductor
ductor
The onboarding wizard handles CLI checks, Telegram setup, timezone, optional Docker, and optional background service install.
Why ductor?
ductor executes the real provider CLIs as subprocesses. It does not proxy or spoof provider API calls.
- Official CLIs only (
claude,codex,gemini) - Rule files are plain Markdown (
CLAUDE.md,AGENTS.md,GEMINI.md) - Memory is one Markdown file (
workspace/memory_system/MAINMEMORY.md) - Automation state is JSON (
cron_jobs.json,webhooks.json)
Features
Core
- Real-time streaming with live Telegram edits
- Provider/model switching with
/model(provider sessions are preserved per chat) @modeldirective support (Claude + Gemini IDs/aliases)- Media-aware input flow (text/media handling in Telegram layer)
- Inline callback buttons (
[button:Label]) - Queue tracking with per-message cancel while lock is held
- Telegram forum topic support (
message_thread_idpropagation)
Automation
- Background tasks (
/bg): push one-off tasks from the main chat into async execution while the chat stays responsive; receive a final completion message when done - Cron jobs: in-process scheduler with timezone support, per-job overrides, quiet hours, dependency queue
- Webhooks:
wake(inject into active chat) andcron_task(isolated task run) modes - Heartbeat: proactive checks in active sessions with cooldown + quiet hours
- Cleanup: daily retention cleanup for
telegram_files/,output_to_user/, andapi_files/
Infrastructure
- Built-in service manager:
- Linux:
systemd --user - macOS:
launchdLaunch Agent - Windows: Task Scheduler task
- Linux:
- Docker sidecar sandbox support (
Dockerfile.sandbox) - Configurable Docker host-directory mounts (
docker.mounts,ductor docker mount ...) - Restart protocol (
EXIT_RESTART = 42) + sentinel-based user notifications - Version/update flow with Telegram
/upgradecallback path
Developer UX
- Auto-onboarding on first run
- Deep-merge config upgrades (new keys auto-added)
- Rich diagnostics (
/diagnose,/status) - Interactive
/cron,/model,/showfilesflows - Cross-tool skill sync (
~/.ductor/workspace/skills,~/.claude/skills,~/.codex/skills,~/.gemini/skills)
Prerequisites
| Requirement | Details |
|---|---|
| Python 3.11+ | python3 --version |
| pipx (recommended) or pip | pip install pipx |
| At least one CLI installed + authenticated | Claude, Codex, or Gemini |
| Telegram Bot Token | from @BotFather |
| Telegram User ID | from @userinfobot |
| Docker (optional) | recommended for sandboxing |
CLI references:
- Claude Code CLI: https://docs.anthropic.com/en/docs/claude-code
- Codex CLI: https://github.com/openai/codex
- Gemini CLI: https://github.com/google-gemini/gemini-cli
Detailed installation: docs/installation.md
Docker management
ductor docker enable
ductor docker disable
ductor docker rebuild
ductor docker mount /absolute/or/env/path
ductor docker unmount /absolute/or/env/path
ductor docker mounts
enable/disable: togglesdocker.enabledin~/.ductor/config/config.jsonrebuild: stops the bot, removes Docker container + image, rebuilds on next startmount: adds a host directory todocker.mounts(resolved absolute path; duplicates ignored)unmount: removes a configured mount (exact/resolved/basename match)mounts: shows configured host mounts and resolved container targets (/mnt/<name>)- mount/unmount changes require restart or container rebuild to apply
API management (beta)
ductor api enable
ductor api disable
enable: writes/updatesconfig.api, generates token if missingdisable: setsconfig.api.enabled=false- restart required after changes
Run as a background service
ductor service install
ductor service status
ductor service start
ductor service stop
ductor service logs
ductor service uninstall
Platform details:
- Linux:
~/.config/systemd/user/ductor.service, logs viajournalctl --user -u ductor -f - macOS:
~/Library/LaunchAgents/dev.ductor.plist, log files at~/.ductor/logs/service.log+service.err,ductor service logstails recent lines fromagent.log(fallback newest*.log) - Windows: Task Scheduler task
ductor(10s logon delay, restart-on-failure retries), preferspythonw.exe -m ductor_bot,ductor service logstails recent lines fromagent.log(fallback newest*.log)
How it works
You (Telegram)
-> aiogram (AuthMiddleware + SequentialMiddleware)
-> TelegramBot handlers
-> Orchestrator (commands/directives/flows)
-> CLIService
-> claude/codex/gemini subprocess
-> streamed or non-streamed response back to Telegram
Background systems run in the same process:
- periodic observers/watchers:
CronObserver,HeartbeatObserver,WebhookObserver,CleanupObserver,CodexCacheObserver,GeminiCacheObserver,UpdateObserver(only for upgradeable installs), rule sync watcher, skill sync watcher - on-demand subsystem:
BackgroundObserver(/bgtask runner)
Session behavior (important):
- sessions are chat-scoped,
- provider sessions are isolated per provider bucket,
/newresets only the active provider bucket,- switching back to another provider can resume that provider’s previous session.
Workspace layout
~/.ductor/
config/config.json
sessions.json
cron_jobs.json
webhooks.json
CLAUDE.md
AGENTS.md
GEMINI.md
logs/agent.log
workspace/
memory_system/MAINMEMORY.md
cron_tasks/
skills/
tools/
cron_tools/
webhook_tools/
telegram_tools/
user_tools/
telegram_files/
output_to_user/
api_files/
workspace/api_files/ is created lazily on first API upload.
Configuration
Config file: ~/.ductor/config/config.json
Core keys:
| Key | Purpose |
|---|---|
provider / model |
default runtime target |
telegram_token, allowed_user_ids |
Telegram auth + allowlist |
cli_timeout, permission_mode, file_access |
runtime execution behavior |
reasoning_effort |
default Codex reasoning level |
gemini_api_key |
config fallback key for Gemini API-key mode |
docker.* |
sandbox settings |
heartbeat.* |
proactive check settings |
cleanup.* |
daily cleanup settings |
webhooks.* |
webhook server settings |
cli_parameters.claude/codex/gemini |
provider-specific extra args |
Full schema: docs/config.md
Telegram bot commands
| Command | Description |
|---|---|
/start |
Welcome screen with quick actions |
/new |
Reset active provider session for this chat |
/stop |
Abort active run and drain queued messages |
/model |
Interactive model/provider selector |
/status |
Session/provider/auth status |
/memory |
Show persistent memory file |
/cron |
Interactive cron management |
/bg |
Run task in background (notification on completion) |
/showfiles |
Browse ~/.ductor/ |
/diagnose |
Runtime diagnostics + cache/log info |
/upgrade |
Check/apply update flow |
/restart |
Restart bot |
/info |
Version + links |
/help |
Command reference |
CLI commands
| Command | Description |
|---|---|
ductor |
Start bot (auto-onboarding if needed) |
ductor onboarding |
Run setup wizard (smart reset if configured) |
ductor reset |
Alias for onboarding |
ductor status |
Runtime status panel |
ductor stop |
Stop bot + optional Docker container |
ductor restart |
Stop and re-exec bot |
ductor upgrade |
Upgrade package and restart (non-dev mode) |
ductor uninstall |
Remove bot data + uninstall package |
| `ductor docker <enable | disable |
| `ductor api <enable | disable>` |
ductor service ... |
Service management (install/status/start/stop/logs/uninstall) |
ductor help |
Help + status |
Documentation
docs/README.mddocs/developer_quickstart.mddocs/architecture.mddocs/config.mddocs/automation.mddocs/modules/
Disclaimer
ductor runs official provider CLIs and does not impersonate provider clients. Terms and policies can change; validate your own compliance requirements before unattended automation.
Provider policy links:
- Anthropic: https://www.anthropic.com/policies/terms
- OpenAI: https://openai.com/policies/terms-of-use
- Google: https://policies.google.com/terms
Contributing
git clone https://github.com/PleasePrompto/ductor.git
cd ductor
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest
ruff format .
ruff check .
mypy ductor_bot
Target quality bar: zero warnings, zero errors.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ductor-0.6.0.tar.gz.
File metadata
- Download URL: ductor-0.6.0.tar.gz
- Upload date:
- Size: 840.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3246a9e6360cfa870448f20a5a46239f6801c5d4706f49f7c111c7a4be27606b
|
|
| MD5 |
ff6b25cfb09c5205b430ab1290539007
|
|
| BLAKE2b-256 |
8725a0315c876a8fbd34cb08421031399db418bc9d18577851b0ba661940f529
|
Provenance
The following attestation bundles were made for ductor-0.6.0.tar.gz:
Publisher:
publish.yml on PleasePrompto/ductor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ductor-0.6.0.tar.gz -
Subject digest:
3246a9e6360cfa870448f20a5a46239f6801c5d4706f49f7c111c7a4be27606b - Sigstore transparency entry: 995117643
- Sigstore integration time:
-
Permalink:
PleasePrompto/ductor@6dc14690be0e00898794d38588e04fa1729ff52e -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/PleasePrompto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6dc14690be0e00898794d38588e04fa1729ff52e -
Trigger Event:
push
-
Statement type:
File details
Details for the file ductor-0.6.0-py3-none-any.whl.
File metadata
- Download URL: ductor-0.6.0-py3-none-any.whl
- Upload date:
- Size: 942.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9188f3cca55a4e7f97ebb6f0163f3898b77414de73fdd6264988687b3740cf9
|
|
| MD5 |
90ca2d3ce8f4ada61eddeb8786bd8755
|
|
| BLAKE2b-256 |
49507a40c6f02e75a1cdc8ad9a349c6b383bb0914b91966980dfb22d3328a5e7
|
Provenance
The following attestation bundles were made for ductor-0.6.0-py3-none-any.whl:
Publisher:
publish.yml on PleasePrompto/ductor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ductor-0.6.0-py3-none-any.whl -
Subject digest:
f9188f3cca55a4e7f97ebb6f0163f3898b77414de73fdd6264988687b3740cf9 - Sigstore transparency entry: 995117645
- Sigstore integration time:
-
Permalink:
PleasePrompto/ductor@6dc14690be0e00898794d38588e04fa1729ff52e -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/PleasePrompto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6dc14690be0e00898794d38588e04fa1729ff52e -
Trigger Event:
push
-
Statement type: