@codefilabs/tq
v2.1.2
Published
Claude Code sessions via Telegram + tmux
Maintainers
Readme
tq — Claude Code via Telegram
Send a message. Get a Claude Code session. Reply to continue.
How It Works
You (Telegram) → tq daemon → tmux session → Claude Code
← ← ← /tq-replyEvery Telegram message spawns a Claude Code session in tmux. Reply to the bot's response to continue that conversation. Send a new message to start a new session.
Two routing rules. That's the whole system.
Requirements
- macOS (uses
securityCLI for keychain OAuth) - Python 3 (stdlib only — no pip install)
- tmux
claudeCLI (Claude Code)- A Telegram bot token (from @BotFather)
Setup
# 1. Configure Telegram
tq setup
# 2. Start the daemon
tq daemon start
# 3. Send a message to your bot on TelegramThat's it. You're running Claude Code from your phone.
Installation
CLI only
npm install -g @codefilabs/tqThis puts tq on your PATH. You can immediately use tq setup, tq daemon start, etc.
CLI + Claude Code plugin
npm install -g @codefilabs/tq
claude plugin install "$(npm root -g)/@codefilabs/tq"The Claude Code plugin gives spawned sessions the /tq-reply slash command so they can send responses back to Telegram.
CLI + OpenClaw plugin
npm install -g @codefilabs/tq
openclaw plugins install "$(npm root -g)/@codefilabs/tq/openclaw-plugin"Provides 3 tools (tq_run, tq_status, tq_stop), a health-check service, and auto-injects active session context into agent prompts.
Everything
npm install -g @codefilabs/tq
claude plugin install "$(npm root -g)/@codefilabs/tq"
openclaw plugins install "$(npm root -g)/@codefilabs/tq/openclaw-plugin"CLI
tq daemon start|stop|status Start/stop the Telegram daemon
tq status List all sessions
tq stop <id> Kill a session
tq run <prompt> [--cwd DIR] One-shot session (no Telegram)
tq run queue.yaml [--cwd DIR] Batch sessions from YAML
tq reply <id> <text> Send reply to Telegram (internal)
tq setup Configure Telegram botQueue Files
For batch automation without Telegram:
cwd: ~/project
tasks:
- Review yesterday's commits
- Run the test suite
- Add documentationtq run morning.yamlOptional features:
schedule: "0 9 * * *"— cron schedulingreset: daily— auto-clear state so tasks re-runsequential: true— run tasks one at a time in order
State
Everything lives in ~/.tq/:
~/.tq/
tq.db SQLite database (all sessions + messages)
config.json Telegram bot token + chat ID
hooks/<id>/ Generated per-session hooks (runtime)
daemon.pid Daemon process ID
daemon.log Daemon outputOne database. One config file. No scattered state directories.
Plugins
tq ships as both a Claude Code plugin and an OpenClaw plugin — see Installation for setup commands.
Architecture
tq/
__init__.py Version
__main__.py python -m tq entry point
cli.py 320 lines Entry point + queue parser
daemon.py 183 lines Telegram long-poll + health
session.py 149 lines tmux lifecycle + hooks
store.py 104 lines SQLite (2 tables)
telegram.py 63 lines Bot API
.claude-plugin/ Claude Code plugin
.claude/commands/ /tq-reply slash command
skills/tq/ Skill definition
openclaw-plugin/ OpenClaw plugin
src/index.ts 3 tools + 1 service + 1 hook
src/tq-bridge.ts CLI bridge~820 lines of Python. ~150 lines of TypeScript. Zero external dependencies.
Migrating from v1
If you're upgrading from tq v1 (the bash version):
bash migrate-v1-to-v2.shThe migration script:
- Stops v1 processes and removes v1 symlinks
- Cleans v1 crontab entries
- Removes v1 files (
scripts/,tools/,docs/, etc.) - Promotes
v2/contents to repo root - Renames all
tq2references totq - Installs a
tqwrapper to PATH - Preserves
~/.tq/runtime state
v1 state (~/.tq/queues/.tq/) is not migrated — v2 uses SQLite (~/.tq/tq.db).
If you had v1 queue files, they still work with tq run queue.yaml.
Session Management
Sessions are tracked in SQLite with a claude_session_id that enables suspend/resume:
tq suspend <id> # gracefully stop a session (resumable)
tq resume <id> # resume a suspended session
tq status # shows session IDs in brackets, e.g. [a3f5a3d0]Recovering stale tmux sessions
If you have orphaned tmux sessions from before session tracking was added (or from a crash), you can extract the Claude session IDs without losing them:
# Send /exit to claude in the tmux pane — it exits and prints the resume command
tmux send-keys -t "<session_name>:0.0" "/exit" Enter
# Wait a few seconds, then grab the session ID
tmux capture-pane -t "<session_name>:0.0" -p -S -30 | grep 'claude --resume'
# → claude --resume 063eaae1-68a1-4638-8c1a-ab5c86252a52
# Now you can safely kill the tmux session
tmux kill-session -t "<session_name>"
# And resume later from the correct project directory
cd ~/project && claude --resume 063eaae1-68a1-4638-8c1a-ab5c86252a52This works because /exit gracefully shuts down Claude Code's TUI but leaves the parent shell alive in tmux.
Security
- OAuth tokens read from macOS keychain at runtime — never stored in files
--dangerously-skip-permissionsis required for headless automation- Telegram bot token lives in
~/.tq/config.json— never commit this - SQLite database may contain message text — treat
~/.tq/as private
License
MIT
