One SKILL.md, three channels: Slack, email, and website chat from the same agent
Stop re-implementing the same bot per channel. Declare Slack, email, and website chat in one markdown file and spawn one agent behind all three.
Here's how the multi-channel bot story usually goes. You write a Slack bot with the Bolt SDK. Support asks for email, so someone builds an inbox-polling script with its own prompt. Marketing wants website chat, so you buy a widget with a third copy of the logic in a vendor dashboard. Six months later the Slack bot knows about the new pricing, the email script doesn't, and the website widget is confidently quoting a product you sunsetted.
Three channels, three codebases, three personalities. The bug isn't in any one of them — it's in the architecture.
The whole bot, in one file
In rysh, an externally-reachable agent — a humanoid — is one markdown file. The body is the persona and the knowledge. The frontmatter declares the channels. Here's a support bot on all three live channels at once:
---
name: support-bot
description: Support humanoid for Northwind Devtools
contacts:
slack:
enabled: true
bot_token: "${SLACK_BOT_TOKEN}"
app_token: "${SLACK_APP_TOKEN}"
reply_mode: mentions
channels:
- "#support"
email:
enabled: true
address: "support@northwind.dev"
password: "${SUPPORT_EMAIL_PASSWORD}"
chatbot:
enabled: true
listen: "127.0.0.1:8710"
---
You are support-bot, the support engineer for Northwind Devtools.
Answer only from the product facts below — they are your complete
knowledge base. If the answer isn't here, say you don't know and offer
to open a ticket. Be brief: 1–3 sentences, at most one code block.
- Current release: nwcli v2.4.1. Install: brew install northwind/tap/nwcli
- Staging deploys freeze every Friday after 16:00 UTC.
- Roll back with: nwcli rollback <release-id> (IDs from: nwcli releases)
Then:
##humanoid spawn support-bot.md
##humanoid channel start support-bot slack
##humanoid channel start support-bot email
##humanoid channel start support-bot chatbot
##humanoid channels support-bot
# slack [connected]
# email [connected]
# chatbot [connected]
One spawn, three connects. A teammate mentions it in #support, a customer emails support@, a visitor types into the site widget — the same agent answers all three, from the same twelve lines of product facts.
Why "same agent" beats "three bots"
No persona drift. There is exactly one system prompt. When the release bumps to v2.4.2, you edit one line in one file, respawn, and every channel is current. The failure mode where email-bot and Slack-bot disagree about your own product simply has nowhere to live.
Channels are transport, not logic. Under the hood the humanoid is a single actor with one LLM execution loop; each contact block spawns a small channel adapter — Slack over Socket Mode, email over IMAP IDLE + SMTP, website chat over an HTTP/WebSocket endpoint. Adapters move messages; they contain zero prompt logic. An inbound message from any channel becomes a prompt to the one agent, and the reply routes back to the channel it came from.
Conversations don't cross-contaminate. Each conversation gets its own context, keyed by channel and thread — a 20-turn history with a 24-hour TTL. The angry-customer email thread and the internal Slack thread about that same customer stay separate, even though one brain handles both.
The bot ships like code. The file lives in git. Adding website chat is a three-line diff someone reviews in a PR, not a procurement decision. Rolling back a bad persona change is git revert. Try that with a vendor dashboard.
Secrets never touch the file. ${SLACK_BOT_TOKEN} and friends resolve from your environment at spawn time, so the file you commit — and the file you email a colleague — carries logic, not credentials.
Different channels, different trust
Same agent doesn't have to mean same leash. Governance in rysh is per-channel: run the internal Slack channel autonomous (ai) while customer-facing email runs draft-and-confirm (human) — every outbound email is drafted into your terminal and a person approves it before it sends. Draft-and-confirm is live today for email and website chat. One agent, per-channel trust levels — that's the combination the three-separate-bots architecture can't express at all.
And when you need to intervene, it's one grammar everywhere:
##humanoid channel stop support-bot chatbot # take the widget offline
@support-bot summarize today's tickets # talk to it from the terminal
@@support-bot deactivate # hard stop, all channels
Honest scope
- Live channels today: Slack, email (IMAP IDLE/SMTP), and website chat. WhatsApp and phone (Twilio) are on the roadmap — same skill-file shape, not live yet.
- rysh runs on Claude with your own Anthropic key; the provider layer is pluggable. No model-superiority claim here — the point is the architecture.
- Self-hostable, not open source. Your infra, your keys, your data.
- Private beta, onboarding design partners now.
The one-file-many-channels shape is, I think, the difference between "we have some bots" and "we have an agent" — and it's the foundation everything else in this series builds on.
If your team maintains two or more copies of the same bot right now, you're exactly who I want feedback from — rysh is onboarding design partners.
More in this series: Humanoids: agents with a Slack handle · Per-thread memory