{"id":50706908,"url":"https://github.com/octopusgarage/tmux-claude-bot","last_synced_at":"2026-06-12T09:01:27.055Z","repository":{"id":363330429,"uuid":"1216613339","full_name":"OctopusGarage/tmux-claude-bot","owner":"OctopusGarage","description":"Drive Claude Code from Telegram — send prompts to tmux-hosted Claude sessions and get rich, formatted replies. macOS, one-line install.","archived":false,"fork":false,"pushed_at":"2026-06-09T11:51:00.000Z","size":171,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-09T13:09:42.793Z","etag":null,"topics":["claude-code","grammy","telegram-bot","tmux","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OctopusGarage.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-21T04:30:46.000Z","updated_at":"2026-06-09T11:50:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/OctopusGarage/tmux-claude-bot","commit_stats":null,"previous_names":["octopusgarage/tmux-claude-bot"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/OctopusGarage/tmux-claude-bot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OctopusGarage%2Ftmux-claude-bot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OctopusGarage%2Ftmux-claude-bot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OctopusGarage%2Ftmux-claude-bot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OctopusGarage%2Ftmux-claude-bot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OctopusGarage","download_url":"https://codeload.github.com/OctopusGarage/tmux-claude-bot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OctopusGarage%2Ftmux-claude-bot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34236552,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["claude-code","grammy","telegram-bot","tmux","typescript"],"created_at":"2026-06-09T13:00:18.548Z","updated_at":"2026-06-12T09:01:27.032Z","avatar_url":"https://github.com/OctopusGarage.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tmux-claude-telegram\n\nA Telegram bot that drives [Claude Code](https://docs.anthropic.com/en/docs/claude-code) inside tmux sessions — enabling voice/image input and remote control from Telegram. Supports **multiple projects**, each with its own tmux session.\n\n## Features\n\n- **Multi-project tmux sessions** — each project gets its own tmux session (`tmux_proj_\u003cpath\u003e`)\n- **Project switching** — create, switch, and remove projects via Telegram commands\n- **Real-time output streaming** — captures tmux pane and streams output to Telegram\n- **Queue-based execution** — prevents concurrent commands from interleaving\n- **Idle detection** — polls tmux pane to detect when Claude is idle vs. running\n- **Directory guard** — operations restricted to configured allowed directories\n\n## Architecture\n\n```\nTelegram Bot (grammy)\n    │\n    ├── bot/\n    │   └── handlers.ts  — command routing, middleware, session validation\n    │\n    ├── services/\n    │   ├── tmux.ts              — tmux pane capture, send-keys, session management\n    │   ├── claude.ts            — start/stop Claude, idle poll, queue orchestration\n    │   ├── queue.ts             — FIFO command queue with mutex\n    │   ├── output.ts            — tmux output → Telegram message chunking\n    │   └── currentProject.ts    — .current_project file lifecycle + session cache\n    │\n    └── config.ts    — env loading via dotenv + Zod validation\n```\n\n## Quick Start\n\n### Install (macOS)\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/OctopusGarage/tmux-claude-bot/main/install.sh | bash\n```\n\nThe installer checks prerequisites (`node`, `tmux`, Claude Code CLI), installs\ndependencies, then runs a guided wizard that:\n\n1. asks for your **bot token** (from [@BotFather](https://t.me/BotFather)) and validates it live,\n2. **auto-captures your Telegram id** — just send your bot any message when prompted,\n3. asks which project directories the bot may use,\n\nand finally installs and starts the bot as a launchd service (auto-restart on crash/boot).\n\n\u003e Prefer to clone first? `git clone … \u0026\u0026 cd tmux-claude-bot \u0026\u0026 ./install.sh` does the same thing in place.\n\u003e The install location defaults to `~/.tmux-claude-bot`; override it with `TMUX_CLAUDE_BOT_DIR=/path`.\n\n### Install with an AI assistant\n\nNot comfortable on the command line? Copy this prompt to any AI assistant (ChatGPT,\nClaude, Gemini, or an agent with shell access) — it downloads the release, reads the\nbundled guide, and walks you through it:\n\n```text\nInstall \"tmux-claude-bot\" on my Mac for me (open-source, macOS-only). Download the latest\nrelease tarball from https://github.com/OctopusGarage/tmux-claude-bot/releases/latest,\nextract it, read the INSTALL.md inside, and follow it. Guide me step by step and ask me\nfor anything it needs (like my Telegram bot token).\n```\n\nBy default the installer fetches the **latest stable release** — a lean tarball\nwithout `tests/`, `docs/`, or dev-config files. Pin a specific version, or track `main`:\n\n```bash\n# pin a released version\nTMUX_CLAUDE_BOT_VERSION=v0.1.0 curl -fsSL https://raw.githubusercontent.com/OctopusGarage/tmux-claude-bot/main/install.sh | bash\n\n# track the latest main (development)\nTMUX_CLAUDE_BOT_VERSION=main curl -fsSL https://raw.githubusercontent.com/OctopusGarage/tmux-claude-bot/main/install.sh | bash\n```\n\n### Manage\n\n```bash\nnpm run service:install     # (re)install + start the launchd service\nnpm run doctor              # health check (incl. single-instance 409 guard)\nnpm run setup:reconfigure   # change token / authorized users / directories\nnpm run service:uninstall   # stop and remove the launchd service\n```\n\n### First message\n\nIn Telegram: `/add_project ~/projects/myapp` then `/start`.\n\n## Configuration\n\nAll settings via `.env`:\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `BOT_TOKEN` | *(required)* | Telegram bot token from @BotFather |\n| `CLAUDE_START_COMMAND` | `claude-yolo` | Command to launch Claude |\n| `IDLE_POLL_TICKS` | `3` | Consecutive idle polls before considered idle |\n| `POLL_INTERVAL_MS` | `1000` | Milliseconds between idle polls |\n| `MAX_OUTPUT_LINES` | `200` | Max tmux pane lines to capture |\n| `MAX_MESSAGE_LENGTH` | `3500` | Max Telegram message size |\n| `ALLOWED_USER_IDS` | *(empty)* | Comma-separated Telegram user IDs that can use the bot |\n| `CD_ALLOWED_DIRS` | *(empty)* | Allowed directories for project creation |\n\n## Session Naming\n\ntmux session name format: `tmux_proj_\u003cabsolute-path\u003e` with `/` replaced by `-`\n\nExample: `/home/user/projects/myapp` → `tmux_proj_-home-user-projects-myapp`\n\nThe active session name is stored in `.current_project` (gitignored).\n\n## Telegram Commands\n\n### Projects (no Claude required)\n\n| Command | Description |\n|---------|-------------|\n| `/list_projects` | List all `tmux_proj_*` sessions |\n| `/current_project` | Show current project and session status |\n| `/add_project \u003cpath\u003e` | Create new project tmux session |\n| `/switch_\u003cN\u003e` | Switch to project by number |\n| `/remove_\u003cN\u003e` | Remove project session |\n\n### Claude control (session required)\n\n| Command | When | Description |\n|---------|------|-------------|\n| `start` | session exists | Start Claude session |\n| `status` | session exists | Check if Claude is running |\n| `peek` | session exists | Capture current tmux pane |\n| `esc` | Claude running | Send Escape key |\n| `interrupt` | Claude running | Send Ctrl-C |\n| `exit` | Claude running | Send /exit to Claude |\n| `restart` | Claude running | Restart with --continue flag |\n| `clear` | Claude running | Send /clear (clear context) |\n| `compact` | Claude running | Send /compact (compact context) |\n| `enter` | Claude running | Send Enter key |\n| `up` / `down` | Claude running | Send arrow keys |\n| `help` | always | Show all commands |\n\n### Natural language\n\nWhen Claude is running, any text message is sent to Claude and the result is returned.\n\n## Voice transcription (optional)\n\nVoice messages are transcribed locally with [mlx-whisper](https://pypi.org/project/mlx-whisper/)\n(Apple Silicon only) and then forwarded to Claude like any text. **The feature is\noff until you install it** — if you never use voice, you can ignore this entirely.\n\n**Enable it (two ways):**\n\n- **From Telegram:** send `/voice_install`. The bot runs the installer, enables the\n  feature, and persists the path to `.env` — no restart needed. (No-op politely if\n  the host isn't Apple Silicon.)\n- **On the host:** `npm run whisper:install`, then put the printed path into\n  `MLX_WHISPER_BIN` in `.env` (or re-run `npm run setup:reconfigure`).\n\n**What gets installed** (project-managed, reproducible — nothing global):\n\n- A project-local `.venv` created by [uv](https://docs.astral.sh/uv/), with\n  `mlx-whisper` pinned in [`requirements.txt`](requirements.txt). Model weights are\n  downloaded from HuggingFace on first transcription.\n- **ffmpeg** is required to decode audio (`brew install ffmpeg`). The installer and\n  `npm run doctor` both check for it.\n\nIf you send a voice message before enabling the feature, the bot replies with a\nshort note telling you how to turn it on — it never fails silently.\n\n**Recognition language.** whisper's auto-detect often misreads Chinese as\nJapanese, so transcription forces a language — **`zh` by default**. Switch any\ntime from Telegram with `/voice_lang \u003czh|en|auto\u003e` (`auto` re-enables detection);\nit persists to `.env`. Override the default with `WHISPER_LANGUAGE` in `.env`.\n\n## Claude Running Detection\n\nThe bot decides whether Claude is running by **process detection**, not screen\nscraping: it walks the tmux pane's process tree (`pane_pid` → `ps`) and looks for a\n`claude` process. Present → **running**; absent → **idle**. This is theme- and\noutput-independent. (Readiness — \"Claude finished loading\" — is still detected from\nthe pane, since the process exists before it is ready for input.)\n\n## Production Deployment (macOS launchd)\n\nThe bot runs as a `launchd` service with automatic crash recovery:\n\n```bash\nnpm run service:install     # install + start the launchd agent (auto-restart on crash/boot)\nnpm run service:uninstall   # stop and remove the launchd agent\nnpm run doctor              # verify it's healthy (single-instance check)\n```\n\n\u003e Note: the installer uses `sed` to substitute `__PROJECT_DIR__` in the plist template before\n\u003e copying it to `~/Library/LaunchAgents/`. Do **not** copy the plist manually — the placeholder\n\u003e will be left unresolved and the service will fail to spawn.\n\n**Features:**\n- `KeepAlive` — auto-restart on crash\n- `ThrottleInterval` — min 10s between restarts (prevents crash loops)\n- Logs to `logs/launchd.out.log` and `logs/launchd.err.log`\n\n## Resilience\n\n| Mechanism | Behavior |\n|-----------|----------|\n| Network retry | `getMe` retries 5× with exponential backoff (1s→30s) |\n| Message retry | Handler retries 3× with linear backoff (1s, 2s, 3s) |\n| Queue persistence | Unprocessed messages saved to `.queue/pending.json` |\n| Process auto-restart | launchd `KeepAlive` restarts bot on crash |\n| Handler isolation | Single message failure does not block queue |\n\n## Development \u0026 releases\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for local dev, the verification gates, and\nthe install/deploy/release flow. In short:\n\n- **Dev:** `npm install \u0026\u0026 npm run setup \u0026\u0026 npm run dev`\n- **Deploy this machine:** `/deploy` (Claude command) or re-run the installer\n- **Cut a release:** `/release [patch|minor|major]` — gates, bumps, tags, pushes\n  (CI publishes the GitHub Release), then redeploys + verifies this machine\n\n## Requirements\n\n- Node.js 20+\n- tmux\n- Claude Code CLI (`claude-yolo` or similar)\n- _Optional, for voice:_ Apple Silicon Mac + [uv](https://docs.astral.sh/uv/) + ffmpeg (see [Voice transcription](#voice-transcription-optional))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foctopusgarage%2Ftmux-claude-bot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foctopusgarage%2Ftmux-claude-bot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foctopusgarage%2Ftmux-claude-bot/lists"}