Slack automation CLI for AI agents
npx skills add https://github.com/stablyai/agent-slack --skill agent-slackInstala esta habilidad con la CLI y comienza a usar el flujo de trabajo SKILL.md en tu espacio de trabajo.
Slack automation CLI for AI agents (TypeScript + Bun).
Guiding principle:
message draftInstall via Bun (recommended):
curl -fsSL https://raw.githubusercontent.com/stablyai/agent-slack/main/install.sh | sh
OR npm global install (requires Node >= 22.5):
npm i -g agent-slack
OR run via Nix flake:
nix run github:stablyai/agent-slack
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
bash ./scripts/install-skill.sh
agent-slack
├── update # self-update (detects npm/bun/binary)
├── auth
│ ├── whoami
│ ├── test
│ ├── import-desktop
│ ├── import-chrome
│ ├── import-firefox
│ └── parse-curl
├── message
│ ├── get <target> # fetch 1 message (+ thread meta )
│ ├── list <target> # fetch thread or recent channel messages
│ ├── send <target> <text> # send / reply (supports --attach)
│ ├── draft <target> [text] # open Slack-like editor in browser
│ ├── edit <target> <text> # edit a message
│ ├── delete <target> # delete a message
│ └── react
│ ├── add <target> <emoji>
│ └── remove <target> <emoji>
├── channel
│ ├── list # list conversations (user-scoped or all)
│ ├── new # create channel
│ └── invite # invite users to channel
├── user
│ ├── list
│ └── get <user>
├── search
│ ├── all <query> # messages + files
│ ├── messages <query>
│ └── files <query>
├── workflow
│ ├── list <channel> # workflows bookmarked in a channel
│ ├── preview <trigger-id> # trigger metadata (no side effects)
│ ├── get <id> # workflow definition + form fields
│ └── run <trigger-id> # trip a workflow trigger
└── canvas
└── get <canvas-url-or-id> # canvas → markdown
Notes:
null/empty fields removed).On macOS and Windows, authentication happens automatically:
You can also run manual imports:
agent-slack auth whoami
agent-slack auth import-desktop
agent-slack auth import-chrome
agent-slack auth import-firefox
agent-slack auth test
Alternatively, 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
message get / message list accept either a Slack message URL or a channel reference:
https://workspace.slack.com/archives/<channel>/p<digits>[?thread_ts=...]#general (or bare general) or a channel ID like C0123...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).
--workspace accepts a full URL or a unique substring selector:
agent-slack message get "#general" --workspace "https://stablygroup.slack.com" --ts "1770165109.628379"
agent-slack message get "#general" --workspace "stablygroup" --ts "1770165109.628379"
[!TIP]
You should probably just use the skill for your agent instead of reading below.
# 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"
# Recent channel messages (browse channel history)
agent-slack message list "#general" --limit 20
# Recent channel messages that are marked with :eyes:
agent-slack message list "#general" --with-reaction eyes --oldest "1770165109.000000" --limit 20
# Recent channel messages that do not have :dart:
agent-slack message list "#general" --without-reaction dart --oldest "1770165109.000000" --limit 20
Optional:
# Include reactions + which users reacted
agent-slack message get "https://workspace.slack.com/archives/C123/p1700000000000000" --include-reactions
Opens a Slack-like WYSIWYG editor in your browser for composing messages with full formatting support (bold, italic, strikethrough, links, lists, quotes, code, code blocks).
# Open editor for a channel
agent-slack message draft "#general"
# Open editor with initial text
agent-slack message draft "#general" "Here's my update"
# Reply in a thread
agent-slack message draft "https://workspace.slack.com/archives/C123/p1700000000000000"
After sending, the editor shows a "View in Slack" link to the posted message.
agent-slack message send "https://workspace.slack.com/archives/C123/p1700000000000000" "I can take this."
agent-slack message send "#alerts-staging" "here's the report" --attach ./report.md
agent-slack message edit "https://workspace.slack.com/archives/C123/p1700000000000000" "I can take this today."
agent-slack message delete "https://workspace.slack.com/archives/C123/p1700000000000000"
agent-slack message react add "https://workspace.slack.com/archives/C123/p1700000000000000" "eyes"
agent-slack message react remove "https://workspace.slack.com/archives/C123/p1700000000000000" "eyes"
Channel mode requires --ts:
agent-slack message edit "#general" "Updated text" --workspace "myteam" --ts "1770165109.628379"
agent-slack message delete "#general" --workspace "myteam" --ts "1770165109.628379"
Attach options for message send:
--attach <path> upload a local file (repeatable)# List conversations for current user (users.conversations)
agent-slack channel list
# List conversations for a specific user
agent-slack channel list --user "@alice" --limit 50
# List all workspace conversations (conversations.list)
agent-slack channel list --all --limit 100
# Create a public channel
agent-slack channel new --name "incident-war-room"
# Create a private channel
agent-slack channel new --name "incident-leads" --private
# Invite users by id, handle, or email
agent-slack channel invite --channel "incident-war-room" --users "U01AAAA,@alice,[email protected]"
# Invite external Slack Connect users by email (restricted by default)
agent-slack channel invite --channel "incident-war-room" --users "[email protected]" --external
# External invite with permission for invitees to invite others
agent-slack channel invite --channel "incident-war-room" --users "[email protected]" --external --allow-external-user-invites
Notes:
channel list returns a single page plus next_cursor; use --cursor to fetch the next page.channel list --all and channel list --user are mutually exclusive.--external maps to conversations.inviteShared and expects email targets.external_limited=true); add --allow-external-user-invites to disable that restriction.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": { "ts": "...", "length": 6 }
}
message list fetches all replies in a thread, or recent channel messages when no thread is specified. Use this when you need the full conversation:
{
"messages": [
{ "ts": "...", "text": "...", "user": "U123", ... },
{ "ts": "...", "text": "...", "user": "U456", ... }
]
}
When to use which:
get to check a single message or see if there's a thread worth expandinglist to read an entire thread conversationlist on a channel (without --thread-ts) to browse recent channel messageslist with --with-reaction / --without-reaction plus --oldest to filter channel history by reaction markersmessage get/list auto-download attached files to an agent-friendly temp directory and return file metadata in message.files[], including name when Slack provides the original filename and path for the local download. Failed downloads keep the attachment entry, preserve message.files[].path with a local .download-error.txt file, and include message.files[].error. search messages and search all use the same attachment shape for message results, while search files skips entries whose download fails.
~/.agent-slack/tmp/downloads/Agents can read those paths directly (e.g. snippets as .txt, images as .png).
# 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:
--channel ... (channel-scoped search scans history/files and filters locally).--workspace <url-or-unique-substring> when using #channel names across multiple workspaces.# 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 .
agent-slack canvas get "https://workspace.slack.com/docs/T123/F456"
agent-slack canvas get "F456" --workspace "https://workspace.slack.com"
See CONTRIBUTING.md.
Code. Ship. Test.