Discord and Telegram to tmux session bridge for agent-driven workflows.
Project description
tetherly
Discord / Telegram channel ↔ tmux session bridge.
📖 Full documentation: docs/. Start with Getting Started, then Command Reference, Architecture, and Security. This README is a quick start.
Features
Same slash commands work in both Discord and Telegram:
/bind <session>: bind the current chat to a tmux session/unbind: release this chat from its tmux session/config: toggle plain-text auto-send (Discordauto_send:true|false, Telegram/config on|off) andtrust_chatto let every member of a chat run commands without enumerating user IDs (owner-only)/send <text>: send text plus Enter into the bound tmux session/key <Enter|Escape|Ctrl-C|Ctrl-D|Tab|Up|Down|Left|Right>: send a special key/tail [lines]: fetch recent tmux output/status: inspect the current binding and tmux session status- Quick keys (no arguments):
/enter,/esc,/ctrlc,/ctrld,/tab - Inline buttons on Codex alerts and on
/status//tail: tap[Enter],[Yes],[Refresh],[Stop]directly in chat — no typing needed
CLI helpers (run from inside a tmux session):
tetherly send --message <text>: forward a reply to whichever chat (Discord or Telegram) is bound to the sessiontetherly codex-stop/tetherly codex-permission-request: Codex hook handlers that route messages to the bound chat
A tmux session is globally unique across platforms — it can be bound to one Discord channel or one Telegram chat, not both. Run /unbind first to move it.
Requirements
- Python 3.11+
tmuxinstalled- A Discord bot token and/or a Telegram bot token (at least one)
- Discord: enable Message Content Intent if you want plain-text auto-send
- Telegram: created via @BotFather — full walkthrough in docs/platforms/telegram.md
Setup
Install once on your machine:
pipx install tetherly
# or: uv tool install tetherly
tetherly init
tetherly init is interactive. It writes ~/.tetherly/.env and lets you enable Discord, Telegram, or both. It also asks where to install Codex hooks:
- Global — writes
~/.codex/hooks.jsononce. Hooks fire in every project automatically. - Project — skip global hooks and run
tetherly install-hooksinside each project. - Skip — don't touch Codex hooks.
Then start the bot(s):
tetherly
A single process runs whichever bots are configured. State lives at ~/.tetherly/state.json so one process serves every project.
Per-project usage
For each project you want to drive from chat:
tmux new -s <session-name>
# then in the bound chat:
# /bind <session-name> (Telegram)
# /bind session:<session-name> (Discord)
# /config on (Telegram) / /config auto_send:true (Discord)
If you chose Project mode during init, also run once per project:
cd <project>
tetherly install-hooks
install-hooks accepts --global to (re)install user-level hooks instead.
Sending from inside a session
tetherly send --message "작업 끝났습니다"
cat result.txt | tetherly send --stdin
tetherly send --session t1 --message "..." # explicit session
tetherly send automatically routes to whichever chat (Discord or Telegram) the session is bound to. The legacy tetherly discord-send is still accepted as an alias.
Configuration
tetherly init writes everything you need. Advanced overrides live in ~/.tetherly/.env or shell env.
Discord
| Variable | Default | Notes |
|---|---|---|
DISCORD_BOT_TOKEN |
— | Bot token (required to enable Discord) |
TETHERLY_ALLOWED_USER_IDS |
— | Comma-separated user IDs |
TETHERLY_ALLOWED_GUILD_IDS |
— | Restrict commands to these guilds |
TETHERLY_ALLOWED_ROLE_IDS |
— | Allow members holding any of these roles |
TETHERLY_TEST_GUILD_ID |
— | Dev guild for instant slash-command sync |
Telegram
| Variable | Default | Notes |
|---|---|---|
TELEGRAM_BOT_TOKEN |
— | Bot token from @BotFather (required to enable Telegram) |
TETHERLY_TELEGRAM_ALLOWED_USER_IDS |
— | Comma-separated user IDs (required) |
TETHERLY_TELEGRAM_ALLOWED_CHAT_IDS |
— | Restrict commands to these chats |
Shared
| Variable | Default | Notes |
|---|---|---|
TETHERLY_STATE_PATH |
~/.tetherly/state.json |
Where bindings are persisted |
TETHERLY_DEFAULT_TAIL_LINES |
40 |
Default /tail line count |
TETHERLY_MAX_TAIL_LINES |
200 |
Cap for /tail |
TETHERLY_LOG_LEVEL |
INFO |
Logger verbosity |
A .env in the current working directory still overrides ~/.tetherly/.env. At least one of DISCORD_BOT_TOKEN or TELEGRAM_BOT_TOKEN must be set.
Codex hooks
Both hooks only fire when the active tmux session has TETHERLY_NOTIFY_ON_FINISH=1 — /bind sets this flag automatically and /unbind clears it, so projects without a binding stay silent even when global hooks are installed.
Stop→tetherly codex-stopforwardslast_assistant_messageto the bound chat (Discord or Telegram).PermissionRequest→tetherly codex-permission-requestforwards the tool/command/reason. It does not return anallow/denydecision, so Codex's normal approval prompt still appears.
Project details
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 tetherly-0.2.0.tar.gz.
File metadata
- Download URL: tetherly-0.2.0.tar.gz
- Upload date:
- Size: 689.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
274f74bda0b040aa6290abf8668a579e1ae8b33f5265c8b765c7ab5af4f66bab
|
|
| MD5 |
71bc1a080294faa3d336d8b2745c36bc
|
|
| BLAKE2b-256 |
347db2a1de321f7891cdad65eb4826dbf03a520dd1bdf71e08c80e372dc32825
|
Provenance
The following attestation bundles were made for tetherly-0.2.0.tar.gz:
Publisher:
publish.yml on changhyeon363/tetherly
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tetherly-0.2.0.tar.gz -
Subject digest:
274f74bda0b040aa6290abf8668a579e1ae8b33f5265c8b765c7ab5af4f66bab - Sigstore transparency entry: 1467954074
- Sigstore integration time:
-
Permalink:
changhyeon363/tetherly@eb306f226a38fdc4b55aa6cce9960cc2f819d247 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/changhyeon363
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eb306f226a38fdc4b55aa6cce9960cc2f819d247 -
Trigger Event:
push
-
Statement type:
File details
Details for the file tetherly-0.2.0-py3-none-any.whl.
File metadata
- Download URL: tetherly-0.2.0-py3-none-any.whl
- Upload date:
- Size: 33.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c753b2b08c90b04b35912f98435a27cc1c06478073d5359830a4162d64fbeb4b
|
|
| MD5 |
9a6d44c969c313eebf3e4e37b419d7ba
|
|
| BLAKE2b-256 |
880f98ec6a896bf1ebd5652fdd7357e236a0650df4d28e83972ff24b2a7811fb
|
Provenance
The following attestation bundles were made for tetherly-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on changhyeon363/tetherly
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tetherly-0.2.0-py3-none-any.whl -
Subject digest:
c753b2b08c90b04b35912f98435a27cc1c06478073d5359830a4162d64fbeb4b - Sigstore transparency entry: 1467954190
- Sigstore integration time:
-
Permalink:
changhyeon363/tetherly@eb306f226a38fdc4b55aa6cce9960cc2f819d247 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/changhyeon363
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eb306f226a38fdc4b55aa6cce9960cc2f819d247 -
Trigger Event:
push
-
Statement type: