{"id":47672919,"url":"https://github.com/fitz123/claude-code-bot","last_synced_at":"2026-04-02T13:02:36.886Z","repository":{"id":344875856,"uuid":"1182917110","full_name":"fitz123/claude-code-bot","owner":"fitz123","description":"Multi-platform bot (Telegram + Discord) that routes messages to Claude Code CLI subprocesses. Runs on Max subscription — no API keys, no ToS violations.","archived":false,"fork":false,"pushed_at":"2026-03-24T08:18:36.000Z","size":1277,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-24T17:58:55.689Z","etag":null,"topics":["ai-agent","anthropic","claude","claude-code","claude-max","discord-bot","max-subscription","multi-agent","self-hosted","telegram-bot"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fitz123.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","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-03-16T04:50:25.000Z","updated_at":"2026-03-24T08:18:39.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/fitz123/claude-code-bot","commit_stats":null,"previous_names":["fitz123/claude-code-bot"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fitz123/claude-code-bot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitz123%2Fclaude-code-bot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitz123%2Fclaude-code-bot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitz123%2Fclaude-code-bot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitz123%2Fclaude-code-bot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fitz123","download_url":"https://codeload.github.com/fitz123/claude-code-bot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fitz123%2Fclaude-code-bot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31306710,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["ai-agent","anthropic","claude","claude-code","claude-max","discord-bot","max-subscription","multi-agent","self-hosted","telegram-bot"],"created_at":"2026-04-02T13:02:25.935Z","updated_at":"2026-04-02T13:02:36.728Z","avatar_url":"https://github.com/fitz123.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Minime\n\nMulti-platform bot (Telegram + Discord) that routes messages to Claude Code CLI subprocesses. Each chat/channel gets its own persistent Claude Code session. Runs on Max subscription (no API keys).\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/screenshot-voice-youtube.jpg\" width=\"300\" alt=\"Voice message with YouTube recommendations and sidebar showing multiple topic sessions\"\u003e\n  \u003cimg src=\"assets/screenshot-security-audit.jpg\" width=\"300\" alt=\"Security audit with 7 parallel review agents launched from voice command\"\u003e\n  \u003cimg src=\"assets/screenshot-rendering.jpg\" width=\"300\" alt=\"Markdown rendering: bold, italic, code blocks with syntax highlighting, blockquotes, nested lists, tables\"\u003e\n\u003c/p\u003e\n\n## Architecture\n\n```\nTelegram Cloud          Discord Gateway\n    │                        │\n    ▼ (long polling)         ▼ (websocket)\n┌────────────────┐    ┌────────────────┐\n│  grammY Bot    │    │  Discord.js    │\n│  telegram-bot  │    │  discord-bot   │\n└───────┬────────┘    └───────┬────────┘\n        │                     │\n        ▼                     ▼\n   ┌─────────────────────────────────┐\n   │  Platform Context (interface)   │\n   │  - sendMessage, editMessage     │\n   │  - sendTyping, sendFile         │\n   │  - sendDraft (DM streaming)     │\n   └───────────────┬─────────────────┘\n                   │\n                   ▼\n         ┌──────────────────┐\n         │  Message Queue   │\n         │  - 3s debounce   │\n         │  - mid-turn      │\n         │    collect (20)  │\n         └────────┬─────────┘\n                  │\n                  ▼\n         ┌──────────────────┐\n         │  Session Manager │\n         │  - 1 per chat    │\n         │  - LRU eviction  │\n         │  - idle timeout  │\n         │  - resume on     │\n         │    respawn       │\n         └────────┬─────────┘\n                  │ spawns claude -p (stream-json)\n                  ▼\n         ┌──────────────────┐\n         │  Claude Code CLI │\n         │  - per-agent     │\n         │    workspace     │\n         │  - model from    │\n         │    config        │\n         └────────┬─────────┘\n                  │\n                  ▼\n            Anthropic API\n```\n\nBoth platforms share one Session Manager and use the same stream-relay logic via the `PlatformContext` interface. Each platform provides an adapter that handles platform-specific message I/O (Telegram: grammY Context, Discord: discord.js Channel).\n\n**Message queue** sits between platform bots and Session Manager. Rapid messages are debounced (3s window) into a single prompt. Messages arriving while Claude is processing are collected (up to 20) and delivered as a combined followup after the current turn completes.\n\n**Cron jobs** run separately via launchd plists. Each plist calls `run-cron.sh \u003ctask-name\u003e`, which invokes `cron-runner.ts` to spawn a one-shot `claude -p` session with the cron's prompt.\n\n**Config:** `config.yaml` defines agents (workspace + model) and bindings (chatId/channelId -\u003e agentId). User-specific overrides live in `config.local.yaml` (gitignored, deep-merged over `config.yaml`). At least one platform (Telegram or Discord) must be configured. Tokens are read from macOS Keychain at runtime.\n\n## Installation\n\n### Prerequisites\n\n- macOS (launchd required for bot service management)\n- Node.js 20+ and npm\n- `jq` — required by hook scripts (`brew install jq`)\n- [Claude Code CLI](https://claude.ai/code) with Max subscription\n- A Telegram bot token from [@BotFather](https://t.me/BotFather) (or Discord bot token)\n\n### Steps\n\n**1. Clone and install**\n\n```bash\ngit clone https://github.com/fitz123/claude-code-bot.git ~/.minime\ncd ~/.minime/bot \u0026\u0026 npm install\n```\n\n**2. Configure for your environment**\n\n`config.yaml` ships with working defaults. Create `config.local.yaml` for your overrides:\n\n```bash\ncp config.local.yaml.example config.local.yaml\n```\n\nEdit `config.local.yaml` — set `workspaceCwd` to the absolute path of your repo and `chatId` to your Telegram user ID (send `/start` to [@userinfobot](https://t.me/userinfobot) to find it).\n\n`crons.yaml` ships with example crons (all disabled). Create `crons.local.yaml` for your own crons:\n\n```bash\ncp crons.local.yaml.example crons.local.yaml\n```\n\nCreate `.claude/settings.local.json` with required settings:\n\n```json\n{\n  \"outputStyle\": \"Your output style name\",\n  \"autoMemoryEnabled\": true,\n  \"autoMemoryDirectory\": \"/absolute/path/to/your/workspace/memory/auto\"\n}\n```\n\n**3. Store Telegram bot token in macOS Keychain**\n\n```bash\nsecurity add-generic-password -s 'telegram-bot-token' -a 'minime' -w 'YOUR_TOKEN_HERE'\n```\n\n**4. Store Claude Code OAuth token in Keychain**\n\n```bash\nclaude setup-token\n# Copy the token, then store it:\nsecurity add-generic-password -s 'claude-code-oauth-token' -a 'minime' -w 'YOUR_OAUTH_TOKEN'\n```\n\nThe bot reads this token at startup via `start-bot.sh` and `run-cron.sh` — it does not use `claude auth login`.\n\n**5. Create launchd service**\n\n```bash\nmkdir -p ~/.minime/logs\ncp bot/telegram-bot.plist.example ~/Library/LaunchAgents/ai.minime.telegram-bot.plist\n```\n\nEdit the plist — replace `WORKSPACE`, `LOG_DIR`, and `USER_HOME` with your paths.\n\n**6. Validate and start**\n\n```bash\ncd ~/.minime \u0026\u0026 npx tsx bot/src/config.ts --validate\nlaunchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.minime.telegram-bot.plist\n```\n\n**7. Verify**\n\n```bash\nlaunchctl list | grep ai.minime.telegram-bot\ntail -f ~/.minime/logs/telegram-bot.stdout.log\n```\n\nSend a message to your bot in Telegram to confirm it responds.\n\n### Optional setup\n\n**Discord:** Store token in Keychain (`security add-generic-password -s 'discord-bot-token' -a 'minime' -w 'TOKEN'`), add a `discord` section to `config.local.yaml`. See [config.yaml](config.yaml) for full reference.\n\n**Crons:** Add your crons to `crons.local.yaml` (copy from `crons.local.yaml.example`), then generate and load plists:\n```bash\ncd ~/.minime/bot \u0026\u0026 npx tsx scripts/generate-plists.ts\nlaunchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.minime.cron.\u003cname\u003e.plist\n```\n\n**Optional rules:** `cp .claude/optional-rules/memory-protocol.md .claude/rules/custom/`\n\n**ADR governance:** `mkdir -p reference/governance \u0026\u0026 cp reference/governance/decisions.md.example reference/governance/decisions.md`\n\n## Start / Stop\n\nThe bot runs as a launchd service: `ai.minime.telegram-bot`.\n\n```bash\n# Check status\nlaunchctl print gui/$(id -u)/ai.minime.telegram-bot 2\u003e\u00261 | head -5\n\n# Restart (graceful — waits for active sessions to finish)\nlaunchctl kill SIGTERM gui/$(id -u)/ai.minime.telegram-bot\n\n# Stop\nlaunchctl bootout gui/$(id -u)/ai.minime.telegram-bot\n\n# Start (if stopped)\nlaunchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.minime.telegram-bot.plist\n```\n\n**Warning:** Graceful restart sends SIGTERM — the bot injects a shutdown message into active sessions and waits up to 60s for turns to complete before exiting. Idle sessions close immediately. launchd auto-restarts via KeepAlive. Still, active work is interrupted — always confirm before restarting.\n\n## Add a Cron\n\n1. Edit `crons.local.yaml` — add a new entry:\n   ```yaml\n   - name: my-task\n     schedule: \"30 9 * * *\"\n     prompt: \u003e\n       Do the thing.\n     agentId: main\n     deliveryChatId: YOUR_CHAT_ID\n   ```\n\n   Cron field reference:\n\n   | Field | Type | Required | Description |\n   |-------|------|----------|-------------|\n   | `name` | string | yes | Unique identifier for the cron job |\n   | `schedule` | string | yes | 5-field cron expression, local timezone |\n   | `type` | `\"llm\"` or `\"script\"` | no | `\"llm\"` (default) runs `claude -p`; `\"script\"` runs a shell command |\n   | `prompt` | string | for llm | Prompt sent to Claude |\n   | `command` | string | for script | Shell command to execute |\n   | `agentId` | string | yes | Must match an agent in `config.yaml` or `config.local.yaml` |\n   | `deliveryChatId` | number | no | Telegram chat ID for delivery (falls back to config default) |\n   | `deliveryThreadId` | number | no | Telegram forum topic ID for delivery |\n   | `timeout` | number | no | Per-cron timeout in ms (default: 300000 = 5 min) |\n   | `enabled` | boolean | no | Set `false` to disable without deleting (default: `true`) |\n\n2. Generate launchd plists:\n   ```bash\n   cd ~/.minime/bot \u0026\u0026 npx tsx scripts/generate-plists.ts\n   ```\n\n3. Load and test:\n   ```bash\n   launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.minime.cron.my-task.plist\n   launchctl start ai.minime.cron.my-task\n   tail -f ~/.minime/logs/cron-my-task.log\n   ```\n\nTo remove: `launchctl bootout gui/$(id -u)/ai.minime.cron.\u003cname\u003e`, delete from `crons.local.yaml`, regenerate.\n\n## Add a Binding\n\n1. Add an agent and binding to `config.local.yaml`:\n   ```yaml\n   agents:\n     new-agent:\n       id: new-agent\n       workspaceCwd: /Users/YOU/.minime/workspace-new\n       model: claude-opus-4-6\n\n   bindings:\n     - chatId: 123456789\n       agentId: new-agent\n       kind: dm\n       label: New Agent DM\n   ```\n\n   See [config.yaml](config.yaml) for all binding options including `requireMention`, `voiceTranscriptEcho`, `typingIndicator`, and per-topic overrides for forum supergroups.\n\n2. Validate and restart:\n   ```bash\n   cd ~/.minime/bot \u0026\u0026 npx tsx src/config.ts --validate\n   launchctl kill SIGTERM gui/$(id -u)/ai.minime.telegram-bot\n   ```\n\n## Add a Discord Binding\n\n1. Store the Discord bot token in macOS Keychain:\n   ```bash\n   security add-generic-password -s 'discord-bot-token' -a 'minime' -w 'YOUR_TOKEN_HERE'\n   ```\n\n2. Add the `discord` section to `config.local.yaml`:\n   ```yaml\n   discord:\n     tokenService: discord-bot-token\n     bindings:\n       - guildId: \"9876543210\"\n         agentId: main\n         kind: channel\n         label: My Server\n         requireMention: true\n   ```\n\n   See [config.yaml](config.yaml) for per-channel overrides and guild-wide defaults.\n\n3. Required bot permissions/intents: Guilds, GuildMessages, MessageContent (privileged), DirectMessages. Slash commands (`/start`, `/reconnect`, `/clean`, `/status`) are registered per-guild on startup.\n\n`telegramTokenService` is optional — the bot can run Discord-only.\n\n## Configuration\n\n### Logging\n\nAll log output uses structured format: `TIMESTAMP LEVEL [tag] message`.\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `logLevel` (config.yaml) | string | `\"info\"` | Log verbosity: `debug`, `info`, `warn`, `error` |\n| `LOG_LEVEL` (env var) | string | — | Overrides `logLevel` from config when set |\n\n### Monitoring\n\nWhen `metricsPort` is set in `config.yaml`, the bot exposes a Prometheus-compatible `/metrics` endpoint on `127.0.0.1` at that port.\n\n```yaml\nmetricsPort: 9090\n```\n\nSee [bot/src/metrics.ts](bot/src/metrics.ts) for the full list of exported metrics.\n\n## Upgrading from config.yaml.example\n\nOlder versions shipped `config.yaml.example` which you copied to `config.yaml` (gitignored). The current version tracks `config.yaml` directly and uses `config.local.yaml` for user overrides.\n\nIf you have a local `config.yaml` from the old workflow, git will refuse to pull because the file is now tracked. Migrate before pulling:\n\n```bash\n# 1. Back up your current config\ncp config.yaml config.local.yaml\n\n# 2. Remove the untracked file so git can check out the new tracked version\nrm config.yaml\n\n# 3. Pull — git will restore config.yaml with upstream defaults\ngit pull\n\n# 4. Edit config.local.yaml — keep only your overrides (workspaceCwd, chatId, tokens, bindings)\n#    Remove anything that matches the upstream defaults in config.yaml\n```\n\nYour `config.local.yaml` is deep-merged over `config.yaml` at startup, so you only need to keep what differs from the defaults.\n\n## Similar Projects\n\n### Why this exists\n\nMost Telegram bots for Claude use the [Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview), which requires API keys and falls under Anthropic's Commercial Terms. Anthropic [explicitly prohibits](https://code.claude.com/docs/en/legal-and-compliance) using Max/Pro subscription OAuth tokens through the Agent SDK:\n\n\u003e OAuth authentication (used with Free, Pro, and Max plans) is intended exclusively for Claude Code and Claude.ai. Using OAuth tokens in any other product, tool, or service -- including the Agent SDK -- is not permitted.\n\nThis bot spawns the original `claude -p` binary directly. Same CLI you run in your terminal. Max subscription, no API keys, no per-token billing.\n\n### ToS compliance on Max subscription\n\n| Project | Engine | Max-compliant |\n|---------|--------|---------------|\n| **claude-code-bot** (this) | CLI binary (`claude -p`) | Yes |\n| [PleasePrompto/ductor](https://github.com/PleasePrompto/ductor) | CLI binary (subprocess) | Yes |\n| [Anthropic Official Plugin](https://github.com/anthropics/claude-plugins-official) | MCP extension of active CC session | Yes |\n| [RichardAtCT/claude-code-telegram](https://github.com/RichardAtCT/claude-code-telegram) | Agent SDK (`claude_agent_sdk`) | No |\n| [earlyaidopters/claudeclaw](https://github.com/earlyaidopters/claudeclaw) | Agent SDK (`@anthropic-ai/claude-agent-sdk`) | No |\n| [linuz90/claude-telegram-bot](https://github.com/linuz90/claude-telegram-bot) | Agent SDK (`@anthropic-ai/claude-agent-sdk`) | No |\n| [NachoSEO/claudegram](https://github.com/NachoSEO/claudegram) | Agent SDK (`@anthropic-ai/claude-agent-sdk`) | No |\n| [openclaw/openclaw](https://github.com/openclaw/openclaw) | Own agent runtime | No (API keys) |\n| [qwibitai/nanoclaw](https://github.com/qwibitai/nanoclaw) | Claude Code in Docker | No (API keys) |\n| [mtzanidakis/praktor](https://github.com/mtzanidakis/praktor) | Agent SDK in Docker | No (API keys) |\n| [six-ddc/ccbot](https://github.com/six-ddc/ccbot) | tmux bridge (CLI in tmux) | Yes |\n| [chenhg5/cc-connect](https://github.com/chenhg5/cc-connect) | Bridge/proxy | Depends on agent |\n\nFour projects run the actual CLI binary on a Max subscription without API keys: this bot, ductor, ccbot, and the official plugin.\n\n### vs Anthropic Official Plugin\n\nThe [official plugin](https://github.com/anthropics/claude-plugins-official) is an MCP server that adds Telegram tools to an already-running Claude Code session.\n\n- Not a standalone bot. Requires an active Claude Code session on your computer. Close the lid and it stops\n- No cron or scheduled tasks. No autonomous work while you're away\n- Single session. No parallel workspaces, no multi-agent\n- Supports group chats but not forum topic routing\n- No workspace health management or memory consolidation\n\nIt's a remote control for your terminal session, not an autonomous bot.\n\n### vs ccbot\n\n[ccbot](https://github.com/six-ddc/ccbot) runs Claude Code inside tmux and bridges it to Telegram via two channels: JSONL transcript polling for content, and terminal scraping for interactive UI.\n\nWhat ccbot does better: tool use visibility (which tool was called, what it returned), thinking content as expandable blockquotes, and interactive permission handling — approve or deny tool calls from Telegram via inline keyboard. These are real advantages that `claude -p` stream-json cannot provide today.\n\nThe trade-off is fragility. Hardcoded regex patterns match Claude Code's terminal UI text — prompt wordings, spinner characters, chrome separators. Any Claude Code TUI update can silently break detection. Input goes through `send_keys()` with empirical timing delays. Two polling loops (JSONL at 2s + terminal scrape at 1s per window) add overhead that scales linearly with sessions.\n\nNo cron system, no multi-agent, no workspace management, no Discord. Single-user remote control with excellent visibility into what Claude is doing.\n\n### vs Ductor\n\n[Ductor](https://github.com/PleasePrompto/ductor) is the closest alternative. Also spawns the CLI binary, also ToS-compliant, also supports forum topics.\n\n| | **claude-code-bot** | **ductor** |\n|---|---|---|\n| Language | TypeScript (grammY) | Python (aiogram) |\n| Codebase | ~3k LoC | ~150 modules |\n| Forum topic sessions | Yes | Yes |\n| Multi-agent with isolated workspaces | Yes | Yes |\n| Cron system | launchd plists (per-cron process isolation) | In-process scheduler |\n| Crash safety | Atomic JSON writes, launchd auto-restart | Atomic writes, in-flight turn tracking, process registry |\n| Workspace health | Filesystem guardian hooks + structural audits | Agent health with exponential backoff |\n| Memory consolidation | Nightly summarization cron | File sync |\n| Platforms | Telegram + Discord | Telegram + Matrix |\n| Multi-CLI support | Claude Code | Claude Code, Codex, Gemini |\n\nNeither project is strictly better than the other — feature sets are comparable. Ductor covers more CLIs and has deeper crash recovery (in-flight turn tracking, process registry, stream coalescing). We're significantly simpler: a thin TypeScript wrapper around `claude -p` that delegates complexity to the OS (launchd for process isolation, filesystem hooks for workspace protection) rather than reimplementing it in application code.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffitz123%2Fclaude-code-bot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffitz123%2Fclaude-code-bot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffitz123%2Fclaude-code-bot/lists"}