Drive Claude Code or OpenAI Codex on your own machines β straight from Slack.
| π± From your pocket | π₯οΈ On your hardware | π Zero babysitting |
|---|---|---|
| Launch experiments from the bus, the couch, the conference hallway | Agents run on your GPUs and workstations β no cloud runner, no data leaving your lab | Long jobs run in tmux and ping the thread back the moment they finish |
Mention the bot in a channel, and it runs Claude Code or the Codex CLI headlessly on your workstation, streams the result back into the thread, and remembers the conversation. Switch agents per message with --model β each keeps its own resumable per-thread session. Kick off a training run from your phone, get pinged when it finishes, and ask follow-up questions in the same thread β the session context carries over.
You β @research-bot plot the training curves from last night's run
Bot β π β³ `server` running...
β β
`server` done!
β Loss curves attached β validation plateaus at epoch 40. [loss.png]
You β @research-bot rerun with lr=3e-4 and ping me when it's done
Bot β β
Launched in tmux session `training`. I'll notify this thread.
β ... hours later ...
β π₯οΈ server: β
done β final val acc 91.2%
Use it if you can! But many workspaces β especially enterprise ones β don't allow installing third-party Slack apps, so the official Claude for Slack integration is often simply unavailable. research-bot sidesteps that:
- It's your own custom app, created from a one-page manifest under your workspace β custom internal apps are typically permitted where marketplace installs aren't (and worst case, a free Slack workspace for your lab takes two minutes).
- Your tokens, your machines β the bot talks to Slack over Socket Mode from a machine you own; nothing is installed from a vendor, and no code or data leaves your hardware.
- It does things the official app can't β SSH-route commands across all your GPU machines, launch training runs in tmux, ping you back hours later, and run Codex as easily as Claude.
flowchart LR
U(("π©βπ¬ you")) -- "@research-bot /server ..." --> S[Slack]
S -- Socket Mode --> B["bot.py<br/>(gateway machine)"]
B -- "local command" --> T1["tmux βΈ claude / codex CLI"]
B -- "ssh /server" --> W["worker machine"] --> T2["tmux βΈ claude / codex CLI"]
T1 & T2 -. "stream-json output" .-> B
B -- "reply + files + notifications" --> S
B --- D["dashboard.py :8080<br/>live thread cards"]
One machine runs bot.py as the gateway (receives Slack events over Socket Mode β no public URL or inbound ports needed). Commands either run locally or are routed over SSH to worker machines. Every run happens inside a dedicated tmux session, so you can always attach and watch.
- π§΅ Thread = session β each Slack thread maps to a persistent Claude Code session (
--resume), per machine. Follow-ups keep full context. - π₯οΈ Multi-machine routing β
/server <command>targets a specific machine over SSH;/allbroadcasts to every machine. - π€ Agent-agnostic β first-class support for both Claude Code and the Codex CLI:
--model claude/sonnet,--model codex/gpt-4.1,--effort highper command. Both runners get per-thread session resume, and theMODELSregistry inbot.pymakes new CLI agents easy to add. - π Live dashboard: cards you can chat with β
dashboard.pyserves per-thread status cards (running / idle / ended, todos, live output tail, End button). Click a card and it opens as a chat: full conversation history plus a message box β what you type is posted into the Slack thread and run by the bot, so both surfaces stay in sync. Includes a β settings page for.env(secrets masked),STYLE.md, and recipes. Chat + settings are localhost-only. - πͺ Crash-safe β in-flight runs are journaled; if the bot restarts mid-run, it re-attaches to the tmux output and still delivers the result.
- π§ Two-layer memory β per-channel context files auto-updated after every command (git-synced across machines), plus a SQLite FTS5 full-text history you can query with
/history <query>. - π File attachments, with a token budget β drop files into Slack; they're downloaded and
scp'd to the target machine so the agent can use them.ATTACHMENT_MODE=text(or--attachments textper message) keeps token-hungry media β images, video, documents, zips β out of the agent's context entirely. - π Long-job notifications β the agent is instructed to launch long tasks (training, evals) in background tmux and ping the exact channel + thread via
notify.pywhen they finish. - π Watch live β
watch_claude.pytails every active session in auto-split tmux panes. - βοΈ Style guardrails β
STYLE.mdis injected into every prompt: ban the words you never want to see ("leverage", "delve", your least-favorite jargon), set tone rules, add your own. Re-read live on every command β edit it and the next message already complies. - π Recipe playbooks β drop markdown playbooks in
recipes/(plotting conventions, GPU monitoring, Slurm patterns ship as starters) and every run gets a one-line index of them; the agent reads the full recipe only when the task matches. Your team's tribal knowledge, injected for ~50 tokens. - β‘ Terminal mode β at the keyboard? Run
claudein the repo and/channel my-projectloads that channel's accumulated context for a fast, temporary brainstorm β nothing hits Slack until you close with/slack my-project, which posts a clean summary back to the channel. - π‘
/ideacapture (optional) β file a raw research idea into an Obsidian-style wiki vault, deduped and cross-linked by the agent.
| Reaction | On | Effect |
|---|---|---|
| π | your message | added by the bot: command acknowledged, running |
| β | any message in a thread | kill every running session in that thread |
| π | a user message | re-run that command |
| π | thread root | archive the thread (dashboard hides it; remove π to revive) |
Prereqs: Python 3.9+, tmux, and the Claude Code CLI installed and logged in on every machine that will run commands. The optional Codex runner needs the codex CLI.
1. Create the Slack app (~3 minutes, all copy-paste β no server, no public URL, no webhooks; the bot connects outward to Slack when it starts)
- Open api.slack.com/apps β Create New App β From a manifest β pick your workspace.
- In the YAML tab, paste the manifest below β Next β Create.
- Left sidebar β Basic Information β App-Level Tokens β Generate Token and Scopes β any name β Add Scope β
connections:writeβ Generate. Copy thexapp-...token β this is yourSLACK_APP_TOKEN. - Left sidebar β Install App β Install to Workspace β Allow.
- Left sidebar β OAuth & Permissions β copy the Bot User OAuth Token (
xoxb-...) β this is yourSLACK_BOT_TOKEN.
π slack-app-manifest.yaml β click to expand, then copy-paste
display_information:
name: research-bot
description: Drive Claude Code on your own machines from Slack
background_color: "#1a1d21"
features:
bot_user:
display_name: research-bot
always_online: true
oauth_config:
scopes:
bot:
- app_mentions:read
- channels:history
- groups:history
- im:history
- im:read
- im:write
- mpim:history
- chat:write
- files:read
- files:write
- reactions:read
- reactions:write
settings:
event_subscriptions:
bot_events:
- app_mention
- message.im
- reaction_added
- reaction_removed
interactivity:
is_enabled: false
org_deploy_enabled: false
socket_mode_enabled: true
token_rotation_enabled: false2. Install the bot:
git clone https://github.com/Minyoung1005/research-bot.git
cd research-bot
pip install -r requirements.txt
cp .env.example .env # then fill in the two tokens + MACHINE_NAME3. Run it (tmux recommended so it survives your terminal):
tmux new-session -d -s bot "python bot.py"
tmux new-session -d -s dashboard "python dashboard.py" # optional, http://localhost:80804. In Slack: /invite @research-bot to a channel, then:
@research-bot what's the GPU utilization right now?
DMs to the bot work too β no mention needed.
| Message | What happens |
|---|---|
@research-bot <command> |
runs on this channel's default machine |
@research-bot /server <command> |
runs on machine server over SSH |
@research-bot /all <command> |
broadcast to every machine |
... --model codex |
run with the Codex CLI instead of Claude |
... --model claude/sonnet |
pick a specific Claude model for this command |
... --effort high |
pass a reasoning-effort level to Claude |
... --attachments text |
this message: pass only text-like attachments (full/text/none) |
@research-bot /history <query> |
full-text search over everything the bot has done |
@research-bot summary / recap |
answer using the channel's recent history |
@research-bot /idea <text> |
file an idea into your vault (needs IDEAS_VAULT) |
Deeper guides live in docs/:
Setup Β· Usage Β· Multi-machine Β· Architecture Β· Troubleshooting
Run bot.py on one gateway machine (IS_GATEWAY=true) that has SSH key access to your workers. Add one line per worker to the gateway's .env:
SSH_server=you@server.example.com # port 22
SSH_cluster=you@10.0.0.7:43284 # custom portThat's it β /server and /cluster become routable, the machine list is derived automatically, and attached files are scp'd over before the run. Workers don't need the bot running at all (the gateway drives them over SSH); each just needs tmux + the claude CLI. Optionally pin a channel to a machine with CHANNEL_DEFAULTS=C0AAAAAAAAA:server.
| Variable | Required | Default | Purpose |
|---|---|---|---|
SLACK_BOT_TOKEN |
β | β | bot OAuth token (xoxb-...) |
SLACK_APP_TOKEN |
β | β | app-level token for Socket Mode (xapp-...) |
MACHINE_NAME |
β | β | this machine's name (targeted as /<name>) |
WORK_DIR |
~/Projects |
directory Claude runs in (use an absolute path) | |
IS_GATEWAY |
true |
false on machines that shouldn't route |
|
ALLOWED_USERS |
everyone | comma-separated Slack user IDs allowed to command the bot | |
SSH_<name> |
β | user@host[:port] per remote worker |
|
KNOWN_MACHINES |
auto | override the derived machine list | |
CHANNEL_DEFAULTS |
β | C0AAA:machine1,C0BBB:machine2 per-channel targets |
|
CHANNEL_NAMES |
β | my-project:C0AAA,... friendly names for terminal mode |
|
CONTEXT_GIT_SYNC |
true |
auto-commit/push channel contexts to origin (multi-machine sync) | |
ATTACHMENT_MODE |
full |
full / text / none β which attachments reach the agent |
|
CLAUDE_MODEL |
CLI default | default Claude model | |
CLAUDE_EFFORT |
β | default reasoning effort (lowβmax); per-message --effort overrides |
|
CLAUDE_PERMISSION_ARGS |
--permission-mode auto |
permission flags for headless runs (--dangerously-skip-permissions where bypass is allowed) |
|
MAX_CONCURRENT |
5 |
max simultaneous agent runs per machine | |
CODEX_MODEL |
gpt-4.1 |
default Codex model | |
IDEAS_VAULT |
(disabled) | path to an Obsidian-style vault for /idea |
|
SLACK_WEBHOOK_URL |
β | fallback webhook for notify.py |
|
DASHBOARD_HOST / DASHBOARD_PORT |
0.0.0.0 / 8080 |
dashboard bind address | |
DASHBOARD_AUTO_END_HOURS |
24 |
auto-end threads idle this long (0 = never) |
bot.py # the Slack bot: routing, tmux runners, sessions, recovery
dashboard.py # local web dashboard of live thread cards (+ End button)
history.py # SQLite FTS5 persistent conversation store
watch_claude.py # tail active sessions in auto-split tmux panes
notify.py # "job finished" pings back to the right channel + thread
chat_to_slack.py # terminal-session bridge: load channel context / post summaries
.claude/commands/ # /channel and /slack commands for terminal Claude Code
STYLE.md # editable writing rules injected into every prompt
recipes/ # task playbooks (plotting, monitoring, slurm, ...) indexed into prompts
contexts/ # per-channel context memory (auto-committed by the bot)
This bot gives Slack users arbitrary code execution on your machines β that's the point, but treat it accordingly:
- Claude runs with
--dangerously-skip-permissions(full autonomy, no confirmation prompts). - Set
ALLOWED_USERS. With it unset, anyone in your workspace who can message the bot can run commands. - Install it only in a workspace you control; prefer a private workspace for a lab.
- With
CONTEXT_GIT_SYNC=true(the default) the bot auto-commitscontexts/*.md(summaries of what you worked on) and pushes toorigin. Pointoriginat a private fork if your research context is sensitive β or setCONTEXT_GIT_SYNC=falseto keep contexts purely local (recommended for single-machine setups).
Questions, ideas, or a cool setup to show off? Open an issue or email myhwang.robo@gmail.com.
MIT β do whatever you want, attribution appreciated. If this bot saved you a trip back to the lab, consider βing the repo.