agent-slack
Slack automation CLI for AI agents (TypeScript + Bun).
Guiding principle:
- token-efficient output by default (compact JSON, minimal duplication, and empty/null fields pruned) so LLMs can consume results cheaply.
- zero-config auth -- Auth just works if you have Slack Desktop (with fallbacks available). No Python dependency.
At a glance
- Read: fetch a message, detect threads, list full threads
- Search: messages + files (with filters)
- Artifacts: auto-download snippets/images/files to local paths for agents
- Write: reply in thread, add reactions
- Canvas: fetch Slack canvases as Markdown
Installation
Recommended (Bun install script):
curl -fsSL https://raw.githubusercontent.com/stablyai/agent-slack/master/install.sh | shOR npm global install (requires Node >= 22.5):
Agent skill
This repo ships an agent skill at skills/agent-slack/ compatible with Claude Code, Codex, Cursor, etc
Install via skills.sh (recommended):
npx skills add stablyai/agent-slack
Manual installation
```bash bash ./scripts/install-skill.sh ```Command map (high level)
agent-slack
├── auth
│ ├── whoami
│ ├── test
│ ├── import-desktop
│ ├── import-chrome
│ └── parse-curl
├── message
│ ├── get <target> # fetch 1 message (+ thread meta )
│ ├── list <target> # fetch full thread
│ ├── send <target> <text> # send / reply (does the right thing)
│ └── react
│ ├── add <target> <emoji>
│ └── remove <target> <emoji>
├── user
│ ├── list
│ └── get <user>
├── search
│ ├── all <query> # messages + files
│ ├── messages <query>
│ └── files <query>
└── canvas
└── get <canvas-url-or-id> # canvas → markdown
Notes:
- Output is always JSON and aggressively pruned (
null/empty fields removed). - Attached files are auto-downloaded and returned as absolute local paths.
Authentication (no fancy setup)
On macOS, authentication happens automatically:
- Default: reads Slack Desktop local data (no need to quit Slack)
- Fallback: if that fails, tries Chrome extraction (if Slack is open in Chrome)
You can also run manual imports:
agent-slack auth whoami
agent-slack auth import-desktop
agent-slack auth import-chrome
agent-slack auth testAlternatively, set env vars:
export SLACK_TOKEN="xoxc-..." # browser token export SLACK_COOKIE_D="xoxd-..." # cookie d agent-slack auth test
Or use a standard Slack token (xoxb/xoxp):
export SLACK_TOKEN="xoxb-..." agent-slack auth test
Targets: URL or channel
message get / message list accept either a Slack message URL or a channel reference:
- URL:
https://workspace.slack.com/archives/<channel>/p<digits>[?thread_ts=...] - Channel:
#general(or baregeneral) or a channel ID likeC0123...
In practice:
# Get a single message by channel + ts agent-slack message get "#general" --ts "1770165109.628379" # List a full thread by channel + thread root ts agent-slack message list "#general" --thread-ts "1770165109.000001"
If you have multiple workspaces configured and you use a channel name (#channel / channel), you must pass --workspace (or set SLACK_WORKSPACE_URL):
agent-slack message get "#general" --workspace "https://stablygroup.slack.com" --ts "1770165109.628379"
Examples
Tip
You should probably just use the skill for your agent instead of reading below.
Read messages / threads
# Single message (+ thread summary if threaded) agent-slack message get "https://workspace.slack.com/archives/C123/p1700000000000000" # Full thread for a message agent-slack message list "https://workspace.slack.com/archives/C123/p1700000000000000"
Optional:
# Include reactions + which users reacted agent-slack message get "https://workspace.slack.com/archives/C123/p1700000000000000" --include-reactions
Message get vs list
message get fetches a single message. If the message is in a thread, it also returns thread metadata (reply count, participants) but not the full thread contents:
{
"message": { "ts": "...", "text": "...", "user": "U123", ... },
"thread": { "replyCount": 5, "participants": ["U123", "U456"] }
}message list fetches all messages in a thread (or channel history if no thread). Use this when you need the full conversation:
{
"messages": [
{ "ts": "...", "text": "...", "user": "U123", ... },
{ "ts": "...", "text": "...", "user": "U456", ... }
]
}When to use which:
- Use
getto check a single message or see if there's a thread worth expanding - Use
listto read an entire thread conversation
Files (snippets/images/attachments)
message get/list auto-download attached files to an agent-friendly temp directory and return absolute paths in message.files[].path:
- macOS default:
~/.agent-slack/tmp/downloads/
Agents can read those paths directly (e.g. snippets as .txt, images as .png).
Search (messages + files)
# Search both messages and files agent-slack search all "smoke tests failed" --channel "#alerts" --after 2026-01-01 --before 2026-02-01 # Search messages only agent-slack search messages "stably ai" --user "@stablyai" --channel general # Search files only (downloads files and returns local paths) agent-slack search files "testing" --content-type snippet --limit 10
Tips:
- For reliable results, include
--channel ...(channel-scoped search scans history/files and filters locally). - Use
--workspace https://...slack.comwhen using#channelnames across multiple workspaces.
Users
# List users (email requires appropriate Slack scopes; fields are pruned if missing) agent-slack user list --workspace "https://workspace.slack.com" --limit 200 | jq . # Get one user by id or handle agent-slack user get U12345678 --workspace "https://workspace.slack.com" | jq . agent-slack user get "@alice" --workspace "https://workspace.slack.com" | jq .
Fetch a Canvas as Markdown
agent-slack canvas get "https://workspace.slack.com/docs/T123/F456" agent-slack canvas get "F456" --workspace "https://workspace.slack.com"
Developing / Contributing
See CONTRIBUTING.md.
Stably
Code. Ship. Test.