untether 0.22.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. untether-0.22.1/LICENSE +23 -0
  2. untether-0.22.1/PKG-INFO +267 -0
  3. untether-0.22.1/README.md +200 -0
  4. untether-0.22.1/pyproject.toml +112 -0
  5. untether-0.22.1/src/untether/__init__.py +3 -0
  6. untether-0.22.1/src/untether/api.py +116 -0
  7. untether-0.22.1/src/untether/backends.py +25 -0
  8. untether-0.22.1/src/untether/backends_helpers.py +14 -0
  9. untether-0.22.1/src/untether/cli/__init__.py +189 -0
  10. untether-0.22.1/src/untether/cli/config.py +320 -0
  11. untether-0.22.1/src/untether/cli/doctor.py +173 -0
  12. untether-0.22.1/src/untether/cli/init.py +113 -0
  13. untether-0.22.1/src/untether/cli/onboarding_cmd.py +126 -0
  14. untether-0.22.1/src/untether/cli/plugins.py +196 -0
  15. untether-0.22.1/src/untether/cli/run.py +419 -0
  16. untether-0.22.1/src/untether/commands.py +135 -0
  17. untether-0.22.1/src/untether/config.py +144 -0
  18. untether-0.22.1/src/untether/config_migrations.py +124 -0
  19. untether-0.22.1/src/untether/config_watch.py +146 -0
  20. untether-0.22.1/src/untether/context.py +9 -0
  21. untether-0.22.1/src/untether/cost_tracker.py +110 -0
  22. untether-0.22.1/src/untether/directives.py +146 -0
  23. untether-0.22.1/src/untether/engines.py +53 -0
  24. untether-0.22.1/src/untether/events.py +170 -0
  25. untether-0.22.1/src/untether/ids.py +17 -0
  26. untether-0.22.1/src/untether/lockfile.py +158 -0
  27. untether-0.22.1/src/untether/logging.py +283 -0
  28. untether-0.22.1/src/untether/markdown.py +298 -0
  29. untether-0.22.1/src/untether/model.py +77 -0
  30. untether-0.22.1/src/untether/plugins.py +312 -0
  31. untether-0.22.1/src/untether/presenter.py +25 -0
  32. untether-0.22.1/src/untether/progress.py +99 -0
  33. untether-0.22.1/src/untether/router.py +113 -0
  34. untether-0.22.1/src/untether/runner.py +712 -0
  35. untether-0.22.1/src/untether/runner_bridge.py +919 -0
  36. untether-0.22.1/src/untether/runners/__init__.py +1 -0
  37. untether-0.22.1/src/untether/runners/claude.py +1539 -0
  38. untether-0.22.1/src/untether/runners/codex.py +705 -0
  39. untether-0.22.1/src/untether/runners/mock.py +221 -0
  40. untether-0.22.1/src/untether/runners/opencode.py +517 -0
  41. untether-0.22.1/src/untether/runners/pi.py +540 -0
  42. untether-0.22.1/src/untether/runners/run_options.py +39 -0
  43. untether-0.22.1/src/untether/runners/tool_actions.py +90 -0
  44. untether-0.22.1/src/untether/runtime_loader.py +207 -0
  45. untether-0.22.1/src/untether/scheduler.py +159 -0
  46. untether-0.22.1/src/untether/schemas/__init__.py +1 -0
  47. untether-0.22.1/src/untether/schemas/claude.py +238 -0
  48. untether-0.22.1/src/untether/schemas/codex.py +251 -0
  49. untether-0.22.1/src/untether/schemas/opencode.py +51 -0
  50. untether-0.22.1/src/untether/schemas/pi.py +117 -0
  51. untether-0.22.1/src/untether/settings.py +402 -0
  52. untether-0.22.1/src/untether/shutdown.py +38 -0
  53. untether-0.22.1/src/untether/telegram/__init__.py +20 -0
  54. untether-0.22.1/src/untether/telegram/api_models.py +37 -0
  55. untether-0.22.1/src/untether/telegram/api_schemas.py +152 -0
  56. untether-0.22.1/src/untether/telegram/backend.py +184 -0
  57. untether-0.22.1/src/untether/telegram/bridge.py +445 -0
  58. untether-0.22.1/src/untether/telegram/chat_prefs.py +257 -0
  59. untether-0.22.1/src/untether/telegram/chat_sessions.py +133 -0
  60. untether-0.22.1/src/untether/telegram/client.py +409 -0
  61. untether-0.22.1/src/untether/telegram/client_api.py +539 -0
  62. untether-0.22.1/src/untether/telegram/commands/__init__.py +12 -0
  63. untether-0.22.1/src/untether/telegram/commands/agent.py +205 -0
  64. untether-0.22.1/src/untether/telegram/commands/browse.py +311 -0
  65. untether-0.22.1/src/untether/telegram/commands/cancel.py +116 -0
  66. untether-0.22.1/src/untether/telegram/commands/claude_control.py +171 -0
  67. untether-0.22.1/src/untether/telegram/commands/dispatch.py +235 -0
  68. untether-0.22.1/src/untether/telegram/commands/executor.py +457 -0
  69. untether-0.22.1/src/untether/telegram/commands/export.py +177 -0
  70. untether-0.22.1/src/untether/telegram/commands/file_transfer.py +586 -0
  71. untether-0.22.1/src/untether/telegram/commands/handlers.py +49 -0
  72. untether-0.22.1/src/untether/telegram/commands/media.py +143 -0
  73. untether-0.22.1/src/untether/telegram/commands/menu.py +139 -0
  74. untether-0.22.1/src/untether/telegram/commands/model.py +222 -0
  75. untether-0.22.1/src/untether/telegram/commands/overrides.py +159 -0
  76. untether-0.22.1/src/untether/telegram/commands/parse.py +30 -0
  77. untether-0.22.1/src/untether/telegram/commands/ping.py +39 -0
  78. untether-0.22.1/src/untether/telegram/commands/plan.py +16 -0
  79. untether-0.22.1/src/untether/telegram/commands/planmode.py +103 -0
  80. untether-0.22.1/src/untether/telegram/commands/reasoning.py +249 -0
  81. untether-0.22.1/src/untether/telegram/commands/reply.py +23 -0
  82. untether-0.22.1/src/untether/telegram/commands/restart.py +29 -0
  83. untether-0.22.1/src/untether/telegram/commands/topics.py +332 -0
  84. untether-0.22.1/src/untether/telegram/commands/trigger.py +148 -0
  85. untether-0.22.1/src/untether/telegram/commands/usage.py +194 -0
  86. untether-0.22.1/src/untether/telegram/context.py +140 -0
  87. untether-0.22.1/src/untether/telegram/engine_defaults.py +86 -0
  88. untether-0.22.1/src/untether/telegram/engine_overrides.py +118 -0
  89. untether-0.22.1/src/untether/telegram/files.py +178 -0
  90. untether-0.22.1/src/untether/telegram/loop.py +2035 -0
  91. untether-0.22.1/src/untether/telegram/onboarding.py +1090 -0
  92. untether-0.22.1/src/untether/telegram/outbox.py +225 -0
  93. untether-0.22.1/src/untether/telegram/parsing.py +245 -0
  94. untether-0.22.1/src/untether/telegram/render.py +247 -0
  95. untether-0.22.1/src/untether/telegram/state_store.py +88 -0
  96. untether-0.22.1/src/untether/telegram/topic_state.py +334 -0
  97. untether-0.22.1/src/untether/telegram/topics.py +256 -0
  98. untether-0.22.1/src/untether/telegram/trigger_mode.py +68 -0
  99. untether-0.22.1/src/untether/telegram/types.py +65 -0
  100. untether-0.22.1/src/untether/telegram/voice.py +110 -0
  101. untether-0.22.1/src/untether/transport.py +53 -0
  102. untether-0.22.1/src/untether/transport_runtime.py +319 -0
  103. untether-0.22.1/src/untether/transports.py +76 -0
  104. untether-0.22.1/src/untether/triggers/__init__.py +7 -0
  105. untether-0.22.1/src/untether/triggers/auth.py +68 -0
  106. untether-0.22.1/src/untether/triggers/cron.py +93 -0
  107. untether-0.22.1/src/untether/triggers/dispatcher.py +85 -0
  108. untether-0.22.1/src/untether/triggers/rate_limit.py +29 -0
  109. untether-0.22.1/src/untether/triggers/server.py +143 -0
  110. untether-0.22.1/src/untether/triggers/settings.py +110 -0
  111. untether-0.22.1/src/untether/triggers/templating.py +44 -0
  112. untether-0.22.1/src/untether/utils/__init__.py +1 -0
  113. untether-0.22.1/src/untether/utils/git.py +87 -0
  114. untether-0.22.1/src/untether/utils/json_state.py +21 -0
  115. untether-0.22.1/src/untether/utils/paths.py +49 -0
  116. untether-0.22.1/src/untether/utils/streams.py +44 -0
  117. untether-0.22.1/src/untether/utils/subprocess.py +86 -0
  118. untether-0.22.1/src/untether/worktrees.py +135 -0
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 banteg
4
+ Copyright (c) 2025-2026 Little Bear Apps and contributors
5
+ Copyright (c) 2025-2026 Nathan Schram and contributors
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
@@ -0,0 +1,267 @@
1
+ Metadata-Version: 2.3
2
+ Name: untether
3
+ Version: 0.22.1
4
+ Summary: Telegram bridge for Claude Code, Codex, and other agent CLIs. Fork of takopi with interactive permission control.
5
+ Keywords: telegram,claude-code,codex,opencode,ai-agents,coding-assistant,remote-control,cli-bridge
6
+ Author: Nathan Schram, banteg (upstream takopi)
7
+ License: MIT License
8
+
9
+ Copyright (c) 2025 banteg
10
+ Copyright (c) 2025-2026 Little Bear Apps and contributors
11
+ Copyright (c) 2025-2026 Nathan Schram and contributors
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ Classifier: Development Status :: 4 - Beta
31
+ Classifier: Environment :: Console
32
+ Classifier: Framework :: AsyncIO
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Operating System :: OS Independent
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3 :: Only
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Classifier: Programming Language :: Python :: 3.13
40
+ Classifier: Programming Language :: Python :: 3.14
41
+ Classifier: Topic :: Communications :: Chat
42
+ Classifier: Topic :: Software Development :: Build Tools
43
+ Classifier: Typing :: Typed
44
+ Requires-Dist: aiohttp>=3.9.0
45
+ Requires-Dist: anyio>=4.12.0
46
+ Requires-Dist: httpx>=0.28.1
47
+ Requires-Dist: markdown-it-py
48
+ Requires-Dist: msgspec>=0.20.0
49
+ Requires-Dist: openai>=2.15.0
50
+ Requires-Dist: pydantic>=2.12.5
51
+ Requires-Dist: pydantic-settings>=2.12.0
52
+ Requires-Dist: questionary>=2.1.1
53
+ Requires-Dist: rich>=14.2.0
54
+ Requires-Dist: structlog>=25.5.0
55
+ Requires-Dist: sulguk>=0.11.1
56
+ Requires-Dist: tomli-w>=1.2.0
57
+ Requires-Dist: typer>=0.21.0
58
+ Requires-Dist: watchfiles>=0.21.0
59
+ Requires-Python: >=3.12
60
+ Project-URL: Changelog, https://untether.cc/reference/changelog/
61
+ Project-URL: Documentation, https://untether.cc/
62
+ Project-URL: Homepage, https://github.com/littlebearapps/untether
63
+ Project-URL: Issues, https://github.com/littlebearapps/untether/issues
64
+ Project-URL: Repository, https://github.com/littlebearapps/untether
65
+ Project-URL: Upstream, https://github.com/banteg/takopi
66
+ Description-Content-Type: text/markdown
67
+
68
+ <p align="center">
69
+ <img src="docs/assets/logo.svg" alt="Untether" width="120" />
70
+ </p>
71
+
72
+ <h1 align="center">Untether</h1>
73
+
74
+ <p align="center">
75
+ <strong>Control your AI coding agents from Telegram.</strong><br>
76
+ Stream progress, approve actions, manage projects — from anywhere.
77
+ </p>
78
+
79
+ <p align="center">
80
+ <a href="https://github.com/littlebearapps/untether/actions/workflows/ci.yml"><img src="https://github.com/littlebearapps/untether/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
81
+ <a href="https://pypi.org/project/untether/"><img src="https://img.shields.io/pypi/v/untether" alt="PyPI" /></a>
82
+ <a href="https://pypi.org/project/untether/"><img src="https://img.shields.io/pypi/pyversions/untether" alt="Python" /></a>
83
+ <a href="https://github.com/littlebearapps/untether/blob/master/LICENSE"><img src="https://img.shields.io/github/license/littlebearapps/untether" alt="License" /></a>
84
+ </p>
85
+
86
+ ---
87
+
88
+ Untether is a Telegram bridge for AI coding agents. It connects [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Codex](https://github.com/openai/codex), [OpenCode](https://github.com/opencode-ai/opencode), and [Pi](https://github.com/nicholasgasior/pi) to Telegram so you can send coding tasks, watch progress live, and approve actions — all from your phone.
89
+
90
+ Walk the dog, watch the footy, sit at a friend's place. Your agents keep working. You stay in control.
91
+
92
+ ## Quick start
93
+
94
+ ```sh
95
+ uv tool install -U untether # install
96
+ untether # run setup wizard
97
+ ```
98
+
99
+ The wizard creates a Telegram bot, picks your workflow, and connects your chat. Then send a message to your bot:
100
+
101
+ > fix the failing tests in src/auth
102
+
103
+ That's it. Your agent runs on your machine, streams progress to Telegram, and you can reply to continue the conversation.
104
+
105
+ ## Why Untether?
106
+
107
+ | Problem | Untether's solution |
108
+ |---------|-------------------|
109
+ | You have to sit at your desk while agents work | Stream progress to Telegram — watch from anywhere |
110
+ | Agents need permission to run tools | Approve or deny actions with inline buttons on your phone |
111
+ | You switch between Claude, Codex, and other agents | One bot, multiple engines — switch with `/claude`, `/codex`, `/opencode`, or `/pi` |
112
+ | Managing multiple repos from chat is messy | Register projects, target them with `/myproject`, branch with `@feat/thing` |
113
+ | No cost visibility | Per-run and daily cost tracking with configurable budgets |
114
+ | Can't continue terminal sessions remotely | Stateless resume — pick up any session in chat or terminal |
115
+
116
+ ## Supported engines
117
+
118
+ | Engine | Install | What it's good at |
119
+ |--------|---------|-------------------|
120
+ | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `npm i -g @anthropic-ai/claude-code` | Complex refactors, architecture, long context |
121
+ | [Codex](https://github.com/openai/codex) | `npm i -g @openai/codex` | Fast edits, shell commands, quick fixes |
122
+ | [OpenCode](https://github.com/opencode-ai/opencode) | `npm i -g opencode-ai@latest` | 75+ providers via Models.dev, local models |
123
+ | [Pi](https://github.com/mariozechner/pi-coding-agent) | `npm i -g @mariozechner/pi-coding-agent` | Multi-provider auth, conversational |
124
+
125
+ Use your existing Claude or ChatGPT subscription — no extra API keys needed (unless you want API billing).
126
+
127
+ ## Features
128
+
129
+ ### Progress streaming
130
+
131
+ Watch your agent work in real time. See tool calls, file changes, and elapsed time as they happen.
132
+
133
+ ### Interactive permissions (Claude Code)
134
+
135
+ When Claude Code needs to run a tool, Untether shows **Approve / Deny / Pause & Outline Plan** buttons in Telegram. Routine tools (Read, Grep, Glob) are auto-approved. Dangerous operations require your explicit approval with a diff preview.
136
+
137
+ ### Plan mode
138
+
139
+ Toggle plan mode per chat with `/planmode`. Claude outlines its approach before making changes. Choose between:
140
+
141
+ - **on** — full plan mode with manual approval
142
+ - **auto** — plan mode with auto-approved transitions
143
+ - **off** — no plan phase
144
+
145
+ ### Projects and worktrees
146
+
147
+ Register repos with `untether init myproject`, then target them from chat:
148
+
149
+ > /myproject @feat/new-api add the endpoint
150
+
151
+ Each branch runs in an isolated git worktree. Multiple projects and branches can run in parallel.
152
+
153
+ ### Cost and usage tracking
154
+
155
+ ```toml
156
+ [footer]
157
+ show_api_cost = false # hide API cost line (default: true)
158
+ show_subscription_usage = true # show 5h/weekly window usage (default: false)
159
+
160
+ [cost_budget]
161
+ enabled = true
162
+ max_cost_per_run = 2.00
163
+ max_cost_per_day = 10.00
164
+ ```
165
+
166
+ See subscription usage or API costs in the progress footer. Use `/usage` for a detailed breakdown. Budget alerts fire at configurable thresholds, and can optionally auto-cancel runs.
167
+
168
+ ### Conversation modes
169
+
170
+ | Mode | Best for | How it works |
171
+ |------|----------|-------------|
172
+ | **Assistant** | Day-to-day use | Ongoing chat with auto-resume. `/new` to start fresh. |
173
+ | **Workspace** | Teams and multi-project | Forum topics bound to repos and branches. |
174
+ | **Handoff** | Terminal-first workflow | Reply-to-continue with resume lines you can paste into terminal. |
175
+
176
+ ### More features
177
+
178
+ - **Voice notes** — dictate tasks, Untether transcribes and sends to the agent
179
+ - **File transfer** — upload files to your repo or download results back
180
+ - **Scheduled tasks** — cron expressions and webhook triggers
181
+ - **Forum topics** — map Telegram topics to projects and branches
182
+ - **Session export** — `/export` for markdown or JSON transcripts
183
+ - **File browser** — `/browse` to navigate project files with inline buttons
184
+ - **Plugin system** — extend with custom engines, transports, and commands
185
+
186
+ ## Commands
187
+
188
+ | Command | What it does |
189
+ |---------|-------------|
190
+ | `/cancel` | Stop the running agent |
191
+ | `/agent` | Show or set the engine for this chat |
192
+ | `/model` | Override the model for an engine |
193
+ | `/planmode` | Toggle plan mode (on/auto/off) |
194
+ | `/usage` | Show API costs for the current session |
195
+ | `/export` | Export session transcript |
196
+ | `/browse` | Browse project files |
197
+ | `/new` | Clear stored sessions |
198
+ | `/file put/get` | Transfer files |
199
+ | `/topic` | Create or bind forum topics |
200
+ | `/restart` | Gracefully restart Untether (drains active runs first) |
201
+
202
+ Prefix any message with `/<engine>` to pick an engine for that task, or `/<project>` to target a repo:
203
+
204
+ > /claude /myproject @feat/auth implement OAuth2
205
+
206
+ ## Configuration
207
+
208
+ Untether reads `~/.untether/untether.toml`. The setup wizard creates this for you, or configure manually:
209
+
210
+ ```toml
211
+ default_engine = "codex"
212
+
213
+ [transports.telegram]
214
+ bot_token = "123456789:ABC..."
215
+ chat_id = 123456789
216
+ session_mode = "chat"
217
+
218
+ [projects.myapp]
219
+ path = "~/dev/myapp"
220
+ default_engine = "claude"
221
+
222
+ [cost_budget]
223
+ enabled = true
224
+ max_cost_per_run = 2.00
225
+ max_cost_per_day = 10.00
226
+ ```
227
+
228
+ See the [full configuration reference](https://untether.cc/reference/config/) for all options.
229
+
230
+ ## Requirements
231
+
232
+ - **Python 3.12+** — `uv python install 3.14`
233
+ - **uv** — `curl -LsSf https://astral.sh/uv/install.sh | sh`
234
+ - At least one agent CLI on PATH: `codex`, `claude`, `opencode`, or `pi`
235
+
236
+ ## Engine guides
237
+
238
+ Detailed setup and usage for each engine:
239
+
240
+ - [Claude Code guide](https://untether.cc/reference/runners/claude/runner/) — permission modes, plan mode, cost tracking, interactive approvals
241
+ - [Codex guide](https://untether.cc/reference/runners/codex/exec-json-cheatsheet/) — profiles, extra args, exec mode
242
+ - [OpenCode guide](https://untether.cc/reference/runners/opencode/runner/) — model selection, 75+ providers, local models
243
+ - [Pi guide](https://untether.cc/reference/runners/pi/runner/) — multi-provider auth, model and provider selection
244
+ - [Configuration reference](https://untether.cc/reference/config/) — full walkthrough of `untether.toml`
245
+ - [Troubleshooting guide](https://untether.cc/how-to/troubleshooting/) — common issues and solutions
246
+
247
+ ## Documentation
248
+
249
+ Full documentation is available at **[untether.cc](https://untether.cc/)**.
250
+
251
+ - [Install and onboard](https://untether.cc/tutorials/install/) — setup wizard walkthrough
252
+ - [First run](https://untether.cc/tutorials/first-run/) — send your first task
253
+ - [Projects and branches](https://untether.cc/tutorials/projects-and-branches/) — multi-repo workflows
254
+ - [Multi-engine workflows](https://untether.cc/tutorials/multi-engine/) — switching between agents
255
+ - [Architecture](https://untether.cc/explanation/architecture/) — how the pieces fit together
256
+
257
+ ## Contributing
258
+
259
+ Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and guidelines.
260
+
261
+ ## Acknowledgements
262
+
263
+ Untether is a fork of [takopi](https://github.com/banteg/takopi) by [@banteg](https://github.com/banteg), which provided the original Telegram-to-Codex bridge. Untether extends it with interactive permission control, multi-engine support, plan mode, cost tracking, and many other features.
264
+
265
+ ## Licence
266
+
267
+ [MIT](LICENSE)
@@ -0,0 +1,200 @@
1
+ <p align="center">
2
+ <img src="docs/assets/logo.svg" alt="Untether" width="120" />
3
+ </p>
4
+
5
+ <h1 align="center">Untether</h1>
6
+
7
+ <p align="center">
8
+ <strong>Control your AI coding agents from Telegram.</strong><br>
9
+ Stream progress, approve actions, manage projects — from anywhere.
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://github.com/littlebearapps/untether/actions/workflows/ci.yml"><img src="https://github.com/littlebearapps/untether/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
14
+ <a href="https://pypi.org/project/untether/"><img src="https://img.shields.io/pypi/v/untether" alt="PyPI" /></a>
15
+ <a href="https://pypi.org/project/untether/"><img src="https://img.shields.io/pypi/pyversions/untether" alt="Python" /></a>
16
+ <a href="https://github.com/littlebearapps/untether/blob/master/LICENSE"><img src="https://img.shields.io/github/license/littlebearapps/untether" alt="License" /></a>
17
+ </p>
18
+
19
+ ---
20
+
21
+ Untether is a Telegram bridge for AI coding agents. It connects [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Codex](https://github.com/openai/codex), [OpenCode](https://github.com/opencode-ai/opencode), and [Pi](https://github.com/nicholasgasior/pi) to Telegram so you can send coding tasks, watch progress live, and approve actions — all from your phone.
22
+
23
+ Walk the dog, watch the footy, sit at a friend's place. Your agents keep working. You stay in control.
24
+
25
+ ## Quick start
26
+
27
+ ```sh
28
+ uv tool install -U untether # install
29
+ untether # run setup wizard
30
+ ```
31
+
32
+ The wizard creates a Telegram bot, picks your workflow, and connects your chat. Then send a message to your bot:
33
+
34
+ > fix the failing tests in src/auth
35
+
36
+ That's it. Your agent runs on your machine, streams progress to Telegram, and you can reply to continue the conversation.
37
+
38
+ ## Why Untether?
39
+
40
+ | Problem | Untether's solution |
41
+ |---------|-------------------|
42
+ | You have to sit at your desk while agents work | Stream progress to Telegram — watch from anywhere |
43
+ | Agents need permission to run tools | Approve or deny actions with inline buttons on your phone |
44
+ | You switch between Claude, Codex, and other agents | One bot, multiple engines — switch with `/claude`, `/codex`, `/opencode`, or `/pi` |
45
+ | Managing multiple repos from chat is messy | Register projects, target them with `/myproject`, branch with `@feat/thing` |
46
+ | No cost visibility | Per-run and daily cost tracking with configurable budgets |
47
+ | Can't continue terminal sessions remotely | Stateless resume — pick up any session in chat or terminal |
48
+
49
+ ## Supported engines
50
+
51
+ | Engine | Install | What it's good at |
52
+ |--------|---------|-------------------|
53
+ | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `npm i -g @anthropic-ai/claude-code` | Complex refactors, architecture, long context |
54
+ | [Codex](https://github.com/openai/codex) | `npm i -g @openai/codex` | Fast edits, shell commands, quick fixes |
55
+ | [OpenCode](https://github.com/opencode-ai/opencode) | `npm i -g opencode-ai@latest` | 75+ providers via Models.dev, local models |
56
+ | [Pi](https://github.com/mariozechner/pi-coding-agent) | `npm i -g @mariozechner/pi-coding-agent` | Multi-provider auth, conversational |
57
+
58
+ Use your existing Claude or ChatGPT subscription — no extra API keys needed (unless you want API billing).
59
+
60
+ ## Features
61
+
62
+ ### Progress streaming
63
+
64
+ Watch your agent work in real time. See tool calls, file changes, and elapsed time as they happen.
65
+
66
+ ### Interactive permissions (Claude Code)
67
+
68
+ When Claude Code needs to run a tool, Untether shows **Approve / Deny / Pause & Outline Plan** buttons in Telegram. Routine tools (Read, Grep, Glob) are auto-approved. Dangerous operations require your explicit approval with a diff preview.
69
+
70
+ ### Plan mode
71
+
72
+ Toggle plan mode per chat with `/planmode`. Claude outlines its approach before making changes. Choose between:
73
+
74
+ - **on** — full plan mode with manual approval
75
+ - **auto** — plan mode with auto-approved transitions
76
+ - **off** — no plan phase
77
+
78
+ ### Projects and worktrees
79
+
80
+ Register repos with `untether init myproject`, then target them from chat:
81
+
82
+ > /myproject @feat/new-api add the endpoint
83
+
84
+ Each branch runs in an isolated git worktree. Multiple projects and branches can run in parallel.
85
+
86
+ ### Cost and usage tracking
87
+
88
+ ```toml
89
+ [footer]
90
+ show_api_cost = false # hide API cost line (default: true)
91
+ show_subscription_usage = true # show 5h/weekly window usage (default: false)
92
+
93
+ [cost_budget]
94
+ enabled = true
95
+ max_cost_per_run = 2.00
96
+ max_cost_per_day = 10.00
97
+ ```
98
+
99
+ See subscription usage or API costs in the progress footer. Use `/usage` for a detailed breakdown. Budget alerts fire at configurable thresholds, and can optionally auto-cancel runs.
100
+
101
+ ### Conversation modes
102
+
103
+ | Mode | Best for | How it works |
104
+ |------|----------|-------------|
105
+ | **Assistant** | Day-to-day use | Ongoing chat with auto-resume. `/new` to start fresh. |
106
+ | **Workspace** | Teams and multi-project | Forum topics bound to repos and branches. |
107
+ | **Handoff** | Terminal-first workflow | Reply-to-continue with resume lines you can paste into terminal. |
108
+
109
+ ### More features
110
+
111
+ - **Voice notes** — dictate tasks, Untether transcribes and sends to the agent
112
+ - **File transfer** — upload files to your repo or download results back
113
+ - **Scheduled tasks** — cron expressions and webhook triggers
114
+ - **Forum topics** — map Telegram topics to projects and branches
115
+ - **Session export** — `/export` for markdown or JSON transcripts
116
+ - **File browser** — `/browse` to navigate project files with inline buttons
117
+ - **Plugin system** — extend with custom engines, transports, and commands
118
+
119
+ ## Commands
120
+
121
+ | Command | What it does |
122
+ |---------|-------------|
123
+ | `/cancel` | Stop the running agent |
124
+ | `/agent` | Show or set the engine for this chat |
125
+ | `/model` | Override the model for an engine |
126
+ | `/planmode` | Toggle plan mode (on/auto/off) |
127
+ | `/usage` | Show API costs for the current session |
128
+ | `/export` | Export session transcript |
129
+ | `/browse` | Browse project files |
130
+ | `/new` | Clear stored sessions |
131
+ | `/file put/get` | Transfer files |
132
+ | `/topic` | Create or bind forum topics |
133
+ | `/restart` | Gracefully restart Untether (drains active runs first) |
134
+
135
+ Prefix any message with `/<engine>` to pick an engine for that task, or `/<project>` to target a repo:
136
+
137
+ > /claude /myproject @feat/auth implement OAuth2
138
+
139
+ ## Configuration
140
+
141
+ Untether reads `~/.untether/untether.toml`. The setup wizard creates this for you, or configure manually:
142
+
143
+ ```toml
144
+ default_engine = "codex"
145
+
146
+ [transports.telegram]
147
+ bot_token = "123456789:ABC..."
148
+ chat_id = 123456789
149
+ session_mode = "chat"
150
+
151
+ [projects.myapp]
152
+ path = "~/dev/myapp"
153
+ default_engine = "claude"
154
+
155
+ [cost_budget]
156
+ enabled = true
157
+ max_cost_per_run = 2.00
158
+ max_cost_per_day = 10.00
159
+ ```
160
+
161
+ See the [full configuration reference](https://untether.cc/reference/config/) for all options.
162
+
163
+ ## Requirements
164
+
165
+ - **Python 3.12+** — `uv python install 3.14`
166
+ - **uv** — `curl -LsSf https://astral.sh/uv/install.sh | sh`
167
+ - At least one agent CLI on PATH: `codex`, `claude`, `opencode`, or `pi`
168
+
169
+ ## Engine guides
170
+
171
+ Detailed setup and usage for each engine:
172
+
173
+ - [Claude Code guide](https://untether.cc/reference/runners/claude/runner/) — permission modes, plan mode, cost tracking, interactive approvals
174
+ - [Codex guide](https://untether.cc/reference/runners/codex/exec-json-cheatsheet/) — profiles, extra args, exec mode
175
+ - [OpenCode guide](https://untether.cc/reference/runners/opencode/runner/) — model selection, 75+ providers, local models
176
+ - [Pi guide](https://untether.cc/reference/runners/pi/runner/) — multi-provider auth, model and provider selection
177
+ - [Configuration reference](https://untether.cc/reference/config/) — full walkthrough of `untether.toml`
178
+ - [Troubleshooting guide](https://untether.cc/how-to/troubleshooting/) — common issues and solutions
179
+
180
+ ## Documentation
181
+
182
+ Full documentation is available at **[untether.cc](https://untether.cc/)**.
183
+
184
+ - [Install and onboard](https://untether.cc/tutorials/install/) — setup wizard walkthrough
185
+ - [First run](https://untether.cc/tutorials/first-run/) — send your first task
186
+ - [Projects and branches](https://untether.cc/tutorials/projects-and-branches/) — multi-repo workflows
187
+ - [Multi-engine workflows](https://untether.cc/tutorials/multi-engine/) — switching between agents
188
+ - [Architecture](https://untether.cc/explanation/architecture/) — how the pieces fit together
189
+
190
+ ## Contributing
191
+
192
+ Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and guidelines.
193
+
194
+ ## Acknowledgements
195
+
196
+ Untether is a fork of [takopi](https://github.com/banteg/takopi) by [@banteg](https://github.com/banteg), which provided the original Telegram-to-Codex bridge. Untether extends it with interactive permission control, multi-engine support, plan mode, cost tracking, and many other features.
197
+
198
+ ## Licence
199
+
200
+ [MIT](LICENSE)
@@ -0,0 +1,112 @@
1
+ [project]
2
+ name = "untether"
3
+ authors = [{name = "Nathan Schram"}, {name = "banteg (upstream takopi)"}]
4
+ version = "0.22.1"
5
+ keywords = ["telegram", "claude-code", "codex", "opencode", "ai-agents", "coding-assistant", "remote-control", "cli-bridge"]
6
+ description = "Telegram bridge for Claude Code, Codex, and other agent CLIs. Fork of takopi with interactive permission control."
7
+ readme = "README.md"
8
+ license = { file = "LICENSE" }
9
+ requires-python = ">=3.12"
10
+ dependencies = [
11
+ "aiohttp>=3.9.0",
12
+ "anyio>=4.12.0",
13
+ "httpx>=0.28.1",
14
+ "markdown-it-py",
15
+ "msgspec>=0.20.0",
16
+ "openai>=2.15.0",
17
+ "pydantic>=2.12.5",
18
+ "pydantic-settings>=2.12.0",
19
+ "questionary>=2.1.1",
20
+ "rich>=14.2.0",
21
+ "structlog>=25.5.0",
22
+ "sulguk>=0.11.1",
23
+ "tomli-w>=1.2.0",
24
+ "typer>=0.21.0",
25
+ "watchfiles>=0.21.0",
26
+ ]
27
+ classifiers = [
28
+ "Development Status :: 4 - Beta",
29
+ "Environment :: Console",
30
+ "Framework :: AsyncIO",
31
+ "Intended Audience :: Developers",
32
+ "License :: OSI Approved :: MIT License",
33
+ "Operating System :: OS Independent",
34
+ "Programming Language :: Python :: 3",
35
+ "Programming Language :: Python :: 3 :: Only",
36
+ "Programming Language :: Python :: 3.12",
37
+ "Programming Language :: Python :: 3.13",
38
+ "Programming Language :: Python :: 3.14",
39
+ "Topic :: Communications :: Chat",
40
+ "Topic :: Software Development :: Build Tools",
41
+ "Typing :: Typed",
42
+ ]
43
+
44
+ [project.urls]
45
+ Homepage = "https://github.com/littlebearapps/untether"
46
+ Repository = "https://github.com/littlebearapps/untether"
47
+ Issues = "https://github.com/littlebearapps/untether/issues"
48
+ Documentation = "https://untether.cc/"
49
+ Changelog = "https://untether.cc/reference/changelog/"
50
+ Upstream = "https://github.com/banteg/takopi"
51
+
52
+ [project.scripts]
53
+ untether = "untether.cli:main"
54
+
55
+ [project.entry-points."untether.engine_backends"]
56
+ codex = "untether.runners.codex:BACKEND"
57
+ claude = "untether.runners.claude:BACKEND"
58
+ opencode = "untether.runners.opencode:BACKEND"
59
+ pi = "untether.runners.pi:BACKEND"
60
+
61
+ [project.entry-points."untether.transport_backends"]
62
+ telegram = "untether.telegram.backend:BACKEND"
63
+
64
+ [project.entry-points."untether.command_backends"]
65
+ claude_control = "untether.telegram.commands.claude_control:BACKEND"
66
+ usage = "untether.telegram.commands.usage:BACKEND"
67
+ planmode = "untether.telegram.commands.planmode:BACKEND"
68
+ ping = "untether.telegram.commands.ping:BACKEND"
69
+ export = "untether.telegram.commands.export:BACKEND"
70
+ browse = "untether.telegram.commands.browse:BACKEND"
71
+ restart = "untether.telegram.commands.restart:BACKEND"
72
+
73
+ [build-system]
74
+ requires = ["uv_build>=0.9.18,<0.10.0"]
75
+ build-backend = "uv_build"
76
+
77
+ [dependency-groups]
78
+ dev = [
79
+ "bandit>=1.8.0",
80
+ "mutmut>=3.4.0",
81
+ "pip-audit>=2.7.0",
82
+ "pytest>=9.0.2",
83
+ "pytest-anyio>=0.0.0",
84
+ "pytest-cov>=7.0.0",
85
+ "ruff>=0.14.10",
86
+ "ty>=0.0.8",
87
+ ]
88
+ docs = [
89
+ "mkdocstrings-python>=2.0.1",
90
+ "zensical>=0.0.15",
91
+ ]
92
+
93
+ [tool.pytest.ini_options]
94
+ addopts = ["--cov=untether", "--cov-branch", "--cov-report=term-missing", "--cov-fail-under=80"]
95
+ testpaths = ["tests"]
96
+
97
+ [tool.mutmut]
98
+ paths_to_mutate = ["src/untether"]
99
+ tests_dir = ["tests"]
100
+ pytest_add_cli_args = ["-q", "--no-cov"]
101
+ do_not_mutate = ["src/untether/cli/*"]
102
+
103
+ [tool.ruff.lint]
104
+ extend-select = ["B", "BLE001", "C4", "PERF", "RUF043", "S110", "SIM", "UP"]
105
+
106
+ [tool.bandit]
107
+ # Untether is a subprocess manager — these are expected patterns
108
+ skips = ["B101", "B105", "B404", "B603", "B607"]
109
+
110
+ [tool.ty.src]
111
+ include = ["src", "tests"]
112
+ exclude = ["scripts"]
@@ -0,0 +1,3 @@
1
+ from importlib.metadata import version
2
+
3
+ __version__ = version("untether")