neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
텔레그램 봇으로 서버에서 코딩 에이전트를 실행하고, 채팅으로 결과를 실시간 확인·버튼으로 계획 승인합니다
A Telegram bot that runs coding agents on your own server from your phone. Send a message, watch the work stream back into the chat, and approve the plan with a button: no terminal, no SSH.
개발 도구TypeScript · ShellGitHub @Mark-Life온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Telegram bot that runs Claude Code or OpenAI Codex on your VPS and streams responses back to chat. Supports project switching, sessions, voice, plan mode, and queues.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Telegram bot interface for coding agents (Claude Code + OpenAI Codex) on a VPS. Message the bot from any device, it runs the active agent in your project directories and answers in the chat. Every forum topic is a separate conversation, so several can run at once. Switch providers at runtime with /provider.README 전문 보기 ↗Telegram bot interface for coding agents (Claude Code + OpenAI Codex) on a VPS. Message the bot from any device, it runs the active agent in your project directories and answers in the chat. Every forum topic is a separate conversation, so several can run at once. Switch providers at runtime with /provider.
MAX_CONCURRENT_RUNS/verbose brings tool calls, thinking and the footer back.data/bot.db, with an optional nightly backup timer/provider; sessions and capabilities are tracked per provider/status and response footers.codex/plans/ convention; Claude's ExitPlanMode/.claude/plans/ flow is supported but off by default (the hardened Claude settings deny plan mode — re-enable via CLAUDE_SETTINGS_JSON). When active, the plan is presented for approval with options to execute (new/resume session), modify with feedback, or cancelSettings profile (plan mode + interactive/harness tools denied, bundled skills/remote-control/artifacts off, effortLevel: high); override any of it via CLAUDE_SETTINGS_JSON.data/events.jsonl, queryable with bun run logs (no external infra)/compose and /send~/.claude login, so authenticate once with claude logincodex login) — optional, only if you want the Codex providersqlite3 on PATH — only for the nightly database backup, which uses sqlite3 .backup because a plain file copy of a WAL database can miss committed writes (sudo apt-get install -y sqlite3)Agent auth is login-managed: authenticate on the host once and the bundled binaries reuse it. No API key is required by default — subscription/CLI login is used (an optional ANTHROPIC_API_KEY fallback exists for Docker/CI, see Optional configuration).
Note: This bot uses the Claude Agent SDK (
query()). Starting June 15, 2026, paid Claude plans include a dedicated monthly credit for programmatic usage (claude -p, Agent SDK, GitHub Actions). Usage draws from this credit first, then from optional usage credits at API rates. See Anthropic's announcement for details and credit amounts by plan.
/newbot → follow prompts → copy the bot tokenForward any message to @userinfobot — it replies with your user ID.
cp .env.example .env
Edit .env:
BOT_TOKEN=your_bot_token_here
ALLOWED_USER_ID=your_telegram_user_id
PROJECTS_DIR=/home/agent/projects
GROQ_API_KEY=your_groq_api_key
No OpenAI or Anthropic API key is required in .env — agent auth is handled by the login flow (see next step).
All optional, with sensible defaults:
| Env var | Default | Purpose |
|---|---|---|
ALLOWED_CHAT_ID | (unset) | Forum supergroup id. Only the file-send script reads it: a run started in a topic sends files back to the group, and without this the send is refused |
CLAUDE_SETTINGS_JSON | (hardened defaults) | JSON overlay of Claude Agent SDK Settings — override the default lockdown (see below) |
MAX_CONCURRENT_RUNS | 4 | Global cap on concurrent agent runs |
RUN_TIMEOUT_MS | (unbounded) | Per-run timeout in ms; unset means /stop is the only cancellation |
TG_LOG_FILE | .data/events.jsonl | Wide-event log path |
LOG_FORMAT | pretty on a TTY, else logfmt | pretty | logfmt | json |
LOG_LEVEL | Info | Minimum log level |
DRAFT_INTERVAL_MS | 300 | Telegram draft update interval (ms) |
SPLIT_AT | 4000 | Message split threshold (chars) |
ANTHROPIC_API_KEY | (unset) | Optional API-key fallback for Docker/CI; unset keeps subscription login |
EXECUTOR_MCP_URL | (unset) | Cloud Executor's org-scoped MCP endpoint (https://executor.sh/org_<id>/mcp) |
EXECUTOR_API_KEY | (unset) | Executor API key; sent as Authorization: Bearer <key> |
Executor (external integrations over MCP). Optionally wire Executor into every agent run as an MCP server, giving the agent a single tool surface for external systems — Notion, Google Workspace, Vercel, Atlassian, and whatever else you connect. Set both env vars (endpoint and key are minted in the Executor dashboard); when either is blank the bot runs without Executor and nothing else changes. Both providers are wired: Claude via the Agent SDK's mcpServers, Codex via an mcp_servers config override.
Rather than exposing one tool per integration, Executor exposes a small set of meta-tools that surface to the model under the mcp__executor__* prefix:
| Tool | Purpose |
|---|---|
execute | Run TypeScript in a sandboxed runtime; connected integrations are reachable as tools.<integration>.* |
resume | Continue a paused, approval-gated execution via its executionId |
skills | Fetch long-form how-to guidance (e.g. skills({name:"execute"})) kept out of the always-loaded tool descriptions |
Which integrations are available is configured in the Executor dashboard, not in .env; the execute tool description enumerates them at connect time.
EXECUTOR_MCP_URL=https://executor.sh/org_xxx/mcp
EXECUTOR_API_KEY=exec_...
Claude agent hardening. Every Claude run is passed a locked-down SDK Settings profile by default: plan mode (Enter/ExitPlanMode) and interactive/harness tools (AskUserQuestion, cron, remote/push, notebook, DesignSync, …) are denied, bundled skills / remote control / artifacts are disabled, and effortLevel is high; workflows stay on. Override any subset with CLAUDE_SETTINGS_JSON — top-level keys replace wholesale, permissions merges one level deep, and malformed JSON fails fast at boot. Examples:
CLAUDE_SETTINGS_JSON='{"permissions":{"deny":[]}}' # clear all denials (re-enables Claude plan mode)
CLAUDE_SETTINGS_JSON='{"effortLevel":"medium"}' # lower effort, keep every other default
Log into each coding-agent CLI once on the host (these persist for the bot):
claude login # Claude Code
codex login # Codex — optional, only if you want the Codex provider
codex login supports ChatGPT (Plus/Pro/Team) or an OpenAI API key, via a browser-based flow. On startup the bot runs a non-blocking codex login status check and only warns if Codex isn't logged in — Claude-only hosts still boot fine.
bun install
bun run src/index.ts
This runs the bot in the foreground — fine for a first check, and bun run dev auto-reloads on changes during development. For any real deployment, run it as a systemd service (next section): it auto-restarts on crashes and survives reboots.
To keep the bot running across reboots and auto-restart on crashes, install it as a systemd user service. One command detects your paths and sets everything up:
bun run service:install
This detects the bun binary (process.execPath), the repo directory, your .env, and the PATH dirs holding bun/claude/codex; generates ~/.config/systemd/user/telegram-claude.service; runs daemon-reload; enable --now; and enables linger so the bot survives logout and starts at boot. It is idempotent — re-run it any time (e.g. after bun upgrade moves the binary) and it reconciles the unit, restarting only if the definition changed.
Preview the generated unit without writing anything:
bun run service:install --dry-run
Add --with-backup to also install the nightly database snapshot timer (see State and backups):
bun run service:install --with-backup
Operations:
| Command | Description |
|---|---|
bun run service:status | Show active/enabled/linger and backup-timer state (--json for a machine-readable blob) |
bun run service:logs | Follow the journal (-n <N> scrollback, --no-follow to tail once) |
bun run service:backup | Take one database snapshot through the systemd unit, then show its journal |
bun run service:restart | Restart the service |
bun run service:start | Start the service |
bun run service:stop | Stop the service |
bun run service:uninstall | Disable, stop, and remove the units (leaves .env, linger and existing snapshots untouched) |
If loginctl enable-linger needs privilege on your host, install prints the exact sudo loginctl enable-linger $USER to run and continues. The raw systemctl --user … telegram-claude / journalctl --user -u telegram-claude -f commands still work for anyone who prefers them.
Alternative: tmux. If you'd rather not use systemd, run the bot in a detached tmux session — tmux new-session -d -s telegram-claude 'bun run src/index.ts' — and reattach with tmux attach -t telegram-claude. It survives SSH disconnects but won't auto-restart on crashes or come back after a reboot, so prefer the service for anything long-lived.
git pull
bun install
bun run service:restart
The first boot after the upgrade creates .data/bot.db and imports the old JSON stores into it — active project, provider, per-provider model and effort choices, and every session id in state.json and sessions.json. The import is guarded by a flag inside the database, so it runs once; the JSON files are left on disk untouched and are never read again. Topics need nothing: each one pins itself the first time you write in it.
Re-run bun run service:install --with-backup if you want the nightly snapshot timer. Install is idempotent, so re-running it is safe.
Each forum topic is a separate conversation. It pins its own project and provider, keeps its own session id, its own message queue and its own compose draft, and its run has its own slot — so topics work in parallel, bounded only by MAX_CONCURRENT_RUNS. A chat without topics, a plain group and the forum's General topic each stay one conversation, exactly as before.
In your private chat with the bot. Turn topic mode on for the bot in @BotFather (/mybots → pick the bot → bot settings → topics). Your chat with it then opens as a list of topics rather than one thread. To confirm it took, getMe reports the flag:
curl -s "https://api.telegram.org/bot$BOT_TOKEN/getMe" # "has_topics_enabled": true
In a forum supergroup. Create a supergroup, turn Topics on in its settings, then add the bot as an administrator with permission to manage topics. Behaviour is identical to private-chat topics. Two BotFather switches matter here and no API can change them: privacy mode must be disabled (/setprivacy) or the bot never sees plain messages in a topic, and group membership must be enabled (/setjoingroups). The bot logs a warning at startup for either.
Naming. A topic you name yourself is left alone. When Telegram names it for you, the bot renames it from the first text or voice message you send, using the active provider's cheapest model — one turn, no tools, no session file, and silently dropped if it fails. Renaming the topic yourself before that first message also wins.
Commands inside a topic. /stop and /new affect that topic alone. /projects re-pins the topic to the chosen project — starting a fresh session there — and also becomes the default for new conversations; /provider behaves the same way. Other topics keep the project and provider they are pinned to. /verbose is the one global switch: it applies to every conversation at once.
| Command | Description |
|---|---|
/projects | Select active project directory |
/provider | Switch coding agent provider (Claude Code / Codex) |
/model | Switch model within the active provider |
/effort | Switch reasoning-effort level within the active provider |
/history | Browse and resume past sessions |
/stop | Kill running agent process |
/status | Show active project, provider & process state |
/new | Clear session, start fresh conversation |
/compact | Summarize the active session in place, keeping it (Claude Code only) |
/verbose | Toggle tool calls, thinking and the metadata footer on or off |
/compose | Start collecting messages into a batch |
/send | Send all composed messages as one prompt |
/cancel | Cancel compose mode, discard messages |
/branch | Show current git branch |
/pr | List open pull requests |
/help | Show available commands |
Text messages are forwarded to the active coding agent as prompts. Voice messages are transcribed and forwarded the same way.
Output is quiet by default: a run shows a typing indicator and then one message with the answer. Tool calls, thinking, sub-agent messages and the metadata footer (project, cost, time, turns) never reach the chat, and no draft previews are streamed — a long answer is still split across messages, and an error always shows. /verbose toggles the full dev view back on and reports which mode you landed in. The choice is stored in .data/bot.db and applies to every conversation.
Use /provider to pick between Claude Code and OpenAI Codex via an inline keyboard. The choice is global, persisted across restarts, and switching auto-stops any running process. The active provider is shown in /status, /help, the pinned project message, and the startup message. Sessions are tracked separately per provider, so /history and follow-up continuity stay scoped to whichever provider is active.
Long conversations eventually fill the context window. /compact summarizes the active project's session for the active provider in place: the session id is unchanged, so the next message continues the same conversation instead of starting over as /new would. A run in flight is stopped first (as with /new), and the reply quotes the context size before and after when the provider reports it.
Only Claude Code supports it — the Agent SDK reaches the CLI's own compaction by sending /compact as the prompt for the resumed session, and reports the result on a compact_boundary message. The Codex SDK (0.155.1) exposes no compaction API, so /compact declines for Codex and points at /new rather than clearing anything.
Use /compose to batch multiple messages into a single prompt. Useful for forwarding context from other chats, combining voice notes with text, or building multi-part requests. All message types are supported: text, voice (auto-transcribed), forwarded messages, files, and photos. Send /send when done or /cancel to discard.
query() runs in-process (no CLI spawn); it bundles its own Claude binary and reuses ~/.claude logincodex exec --json (resume via codex exec resume <id>)resume option, Codex exec resume <id>); a forum topic keeps its session id on its own row, everything outside a topic keeps one session per project per providerwhisper-large-v3-turbo)EXECUTOR_MCP_URL + EXECUTOR_API_KEY are set, cloud Executor is attached to both providers as an MCP server, so the agent can reach external integrations through its mcp__executor__* meta-toolsMAX_CONCURRENT_RUNS.claude/plans/ and calls ExitPlanMode; Codex follows the .codex/plans/PLAN.md convention it's taught via an injected prompt prefix. Either triggers the same interception — the bot displays the plan as plain text and offers action buttons: execute in a new session, execute keeping context, or modify with feedback/stop to cancel the current process and clear the queueEverything the bot remembers lives in .data/, next to the code. It is git-ignored and the only thing worth backing up.
| File | Holds |
|---|---|
.data/bot.db | SQLite: forum topics (project pin, provider, session id), per-project sessions, compose drafts, and settings — active project and provider, model and effort per provider, verbosity |
.data/events.jsonl | one JSON line per run (see Observability) |
.data/backups/ | nightly bot.db snapshots, once you install the timer |
.data/state.json, .data/sessions.json | legacy JSON stores. Imported into bot.db once on first boot, then left in place and never read again |
The database runs in WAL mode, so bot.db-wal and bot.db-shm sit beside it.
Two more systemd user units: telegram-claude-backup.service takes one snapshot, telegram-claude-backup.timer fires it daily with Persistent=true, so a run missed while the machine was down is caught up. Install and check them:
bun run service:install --with-backup
systemctl --user list-timers telegram-claude-backup
Each run writes .data/backups/bot-YYYYMMDD.db.gz and deletes snapshots older than 14 days; set BACKUP_KEEP_DAYS in .env to change the window. The snapshot is taken with sqlite3 .backup, so sqlite3 must be on PATH. Take one by hand:
bun run service:backup # through the installed unit, then prints its journal
bun run backup # the script directly, no systemd
bun run service:status shows the timer's state next to the bot's.
bun run service:stop
gunzip -c .data/backups/bot-20260901.db.gz > .data/bot.db
rm -f .data/bot.db-wal .data/bot.db-shm
bun run service:start
Deleting the stale -wal and -shm files matters: they belong to the database you just replaced. events.jsonl and the legacy JSON files are not in the snapshot.
Each run appends exactly one structured JSON line to .data/events.jsonl — outcome, cost, tokens, duration, turns, provider, and project. Economics degrade to null (never a fabricated 0) when a run is interrupted or errors. Query it with no extra infra:
bun run logs # recent runs, aligned table
bun run logs:errors # only failed runs (error class + message)
bun run logs:stats # counts by outcome + total cost/time
bun run logs:follow # live tail
The path is overridable via TG_LOG_FILE, and LOG_FORMAT=json mirrors the same record to stdout.
TypeScript, Bun, Effect, grammy, Claude Agent SDK, Groq SDK
Contributions welcome! See CONTRIBUTING.md for guidelines.