@buzzie-ai/slack-claude
v0.5.0
Published
Slack bot that bridges a Slack workspace to Claude Code via Socket Mode and @buzzie-ai/claude-inject. Receives Slack messages, forwards them to a persistent Claude Code session per thread, and posts replies back.
Maintainers
Readme
@buzzie-ai/slack-claude
Slack ↔ Claude Code bridge. Connects to Slack over Socket Mode, forwards messages to a persistent Claude Code session per thread (via @buzzie-ai/claude-inject), and posts the replies back into the same thread.
┌──────────┐ WebSocket ┌──────────────────┐ stdin/stdout ┌──────────────┐
│ Slack │ ◄──────────► │ slack-claude │ ◄────────────► │ claude -p │
│ events │ Socket Mode │ (this package) │ stream-json │ (logged in) │
└──────────┘ └──────────────────┘ └──────────────┘One Claude session per Slack thread (or per DM channel), kept alive across follow-up messages so the conversation has memory.
Quick start
Either export the tokens:
export SLACK_APP_TOKEN=xapp-... # app-level token (Socket Mode)
export SLACK_BOT_TOKEN=xoxb-... # bot OAuth token
npx @buzzie-ai/slack-claude…or drop them in a .env file in your current directory (auto-loaded on
startup; real env vars win over file values):
cp .env.example .env # then edit .env
npx @buzzie-ai/slack-claudeUse DOTENV_CONFIG_PATH=/path/to/file to load from a non-default location.
That's it. The bot connects, prints ⚡️ slack-claude bot online …, and starts handling:
- @-mentions in any channel it's been invited to (every message — re-mention to continue)
- Direct messages in DMs (every message; no mention needed since there's only one bot per DM)
Pass --follow-threads to also reply to follow-up messages in any thread it's already in, without needing a re-mention. Default is off so multi-bot threads behave correctly (only the addressed bot answers).
Requirements
- Node ≥ 20
- Claude Code installed and logged in. The
claudeCLI must be on yourPATHand already authenticated (claudeshould run interactively without prompting for login). - A Slack app with Socket Mode enabled — see Slack app setup below.
Slack app setup
One-time, ~3 minutes. From api.slack.com/apps:
- Create app → "From scratch", pick a name and workspace.
- Socket Mode → toggle on. Generate an app-level token with the
connections:writescope. Save thexapp-…token. - OAuth & Permissions → add these bot scopes:
app_mentions:readchat:writechannels:historygroups:historyim:historympim:historyim:readreactions:readreactions:write
- Event Subscriptions → Enable Events. Subscribe to bot events:
app_mentionmessage.channelsmessage.groupsmessage.immessage.mpim
- Install to workspace → grab the bot token (
xoxb-…). - Invite the bot to a channel (
/invite @your-bot) or DM it.
Usage
CLI
npx @buzzie-ai/slack-claude [options]Options:
| Flag | Env | Default | Notes |
|---|---|---|---|
| --app-token <xapp-…> | SLACK_APP_TOKEN | (required) | App-level token for Socket Mode |
| --bot-token <xoxb-…> | SLACK_BOT_TOKEN | (required) | Bot OAuth token |
| --cwd <path> | | process.cwd() | Working directory passed to each Claude session |
| --system-prompt <text> | | sensible default | Sent on first message of every new session |
| --model <id\|alias> | | | opus, sonnet, haiku, or a full model ID |
| --permission-mode <mode> | | | acceptEdits | auto | bypassPermissions | default | dontAsk | plan |
| --dangerously-skip-permissions | | | Bypass all Claude permission prompts |
| --tools <spec> | | | "" (none), "default" (all), or "Bash,Edit,Read" |
| --log-level <level> | | info | debug | info | warn | error | silent |
| --follow-threads | | off | After a mention, keep answering follow-ups in that thread without re-mention. Off by default so multi-bot threads behave correctly. |
| -h, --help | | | Print help |
| -v, --version | | | Print version |
Programmatic
import { SlackClaudeBot } from '@buzzie-ai/slack-claude';
const bot = new SlackClaudeBot({
appToken: process.env.SLACK_APP_TOKEN!,
botToken: process.env.SLACK_BOT_TOKEN!,
cwd: '/path/to/repo',
systemPrompt: 'You are a code review bot. Be terse.',
claudeOptions: {
model: 'sonnet',
dangerouslySkipPermissions: true,
},
});
await bot.start();
// ... later
await bot.stop();How it works
- Bolt opens a Socket Mode WebSocket — no public HTTP endpoint needed.
- For each incoming Slack event, the bot computes a thread key:
- DM at top level →
dm:<channel> - Anything threaded →
<channel>:<thread_ts> - New @-mention →
<channel>:<message_ts>(the reply opens the thread)
- DM at top level →
- A
ClaudeSession(from@buzzie-ai/claude-inject) is created on first hit and cached under that key. Follow-up messages reuse the same persistentclaude -psubprocess, so context carries. - The bot adds a 👀 reaction on receipt, calls
session.send(text), then posts the full reply in the matching thread and removes the reaction.
Limits & gotchas
- One in-flight turn per thread.
ClaudeSession.send()queues; if a user fires three messages in quick succession to the same thread, Claude answers them in order, not in parallel. - Sessions live in memory. Restarting the process resets all conversations.
- Slack message size cap is 40 000 chars. Very long Claude replies will be truncated by Slack; chunking isn't done yet.
- No streaming yet. Replies post once Claude finishes the turn. (Open issue: stream-and-edit mode.)
- Logging. Default level is
info: incoming Slack messages, Claude tool calls, outbound posts, lifecycle events. Run with--log-level debugto also see filtered-out events, reaction state, and Bolt internals.--log-level silentfor cron jobs. - Bot-to-bot conversations are allowed. Other bots can @-mention us, DM us, or talk to us in threads, and we treat them like any other participant. The only filter is self — we never respond to our own posts (matched on both our
bot_idand ouruser_id). Slack'sbot_messagesubtype messages (legacy webhook bots) are also accepted.- Loop risk. Two LLM-backed bots that both @-mention each other in their replies will keep pinging until something stops them. There's no built-in turn limit yet — if you point two slack-claude bots at each other, give Claude a system prompt that discourages outbound mentions, or kill one of the processes. A
--max-turns-per-threadflag is on the roadmap.
- Loop risk. Two LLM-backed bots that both @-mention each other in their replies will keep pinging until something stops them. There's no built-in turn limit yet — if you point two slack-claude bots at each other, give Claude a system prompt that discourages outbound mentions, or kill one of the processes. A
- The
claudeCLI must already be logged in. This bridge does not handle auth. - Permissions in non-interactive mode. The bot launches
claude -p(no terminal). Anything not pre-approved inpermissions.allowis auto-denied because Claude can't show its prompt UI. To get things working, either:- Add explicit allows to
.claude/settings.json(project-level) or~/.claude/settings.json(user-level). Example:{ "permissions": { "allow": ["Bash(git:*)", "Read(*)", "Edit(*)"] } } - Or pass
--dangerously-skip-permissionsto bypass everything (only for trusted/sandboxed setups). - The bot logs which
settings.jsonfiles it found at startup, and warns if none exist.
- Add explicit allows to
- System prompt. By default the bot does not pass
--system-prompt, so Claude's built-in prompt + yourCLAUDE.mdare both honored. Pass--system-prompt "…"to override (this also disables CLAUDE.md, since--system-promptreplaces the default prompt entirely).
License
MIT
