
Research
TeamPCP Compromises Telnyx Python SDK to Deliver Credential-Stealing Malware
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.
@discountry/slack-cli
Advanced tools
Slack automation CLI for AI agents (TypeScript + Bun).
Guiding principle:
message draft
Install 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
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>
└── 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:
slack auth whoami
slack auth import-desktop
slack auth import-chrome
slack auth import-firefox
slack auth test
Alternatively, set env vars:
export SLACK_TOKEN="xoxc-..." # browser token
export SLACK_COOKIE_D="xoxd-..." # cookie d
slack auth test
Or use a standard Slack token (xoxb/xoxp):
export SLACK_TOKEN="xoxb-..."
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
slack message get "#general" --ts "1770165109.628379"
# List a full thread by channel + thread root ts
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:
slack message get "#general" --workspace "https://stablygroup.slack.com" --ts "1770165109.628379"
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)
slack message get "https://workspace.slack.com/archives/C123/p1700000000000000"
# Full thread for a message
slack message list "https://workspace.slack.com/archives/C123/p1700000000000000"
# Recent channel messages (browse channel history)
slack message list "#general" --limit 20
# Recent channel messages that are marked with :eyes:
slack message list "#general" --with-reaction eyes --oldest "1770165109.000000" --limit 20
# Recent channel messages that do not have :dart:
slack message list "#general" --without-reaction dart --oldest "1770165109.000000" --limit 20
Optional:
# Include reactions + which users reacted
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
slack message draft "#general"
# Open editor with initial text
slack message draft "#general" "Here's my update"
# Reply in a thread
slack message draft "https://workspace.slack.com/archives/C123/p1700000000000000"
After sending, the editor shows a "View in Slack" link to the posted message.
slack message send "https://workspace.slack.com/archives/C123/p1700000000000000" "I can take this."
slack message send "#alerts-staging" "here's the report" --attach ./report.md
slack message edit "https://workspace.slack.com/archives/C123/p1700000000000000" "I can take this today."
slack message delete "https://workspace.slack.com/archives/C123/p1700000000000000"
slack message react add "https://workspace.slack.com/archives/C123/p1700000000000000" "eyes"
slack message react remove "https://workspace.slack.com/archives/C123/p1700000000000000" "eyes"
Channel mode requires --ts:
slack message edit "#general" "Updated text" --workspace "myteam" --ts "1770165109.628379"
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)
slack channel list
# List conversations for a specific user
slack channel list --user "@alice" --limit 50
# List all workspace conversations (conversations.list)
slack channel list --all --limit 100
# Create a public channel
slack channel new --name "incident-war-room"
# Create a private channel
slack channel new --name "incident-leads" --private
# Invite users by id, handle, or email
slack channel invite --channel "incident-war-room" --users "U01AAAA,@alice,bob@example.com"
# Invite external Slack Connect users by email (restricted by default)
slack channel invite --channel "incident-war-room" --users "partner@vendor.com" --external
# External invite with permission for invitees to invite others
slack channel invite --channel "incident-war-room" --users "partner@vendor.com" --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 absolute paths in message.files[].path:
~/.agent-slack/tmp/downloads/Agents can read those paths directly (e.g. snippets as .txt, images as .png).
# Search both messages and files
slack search all "smoke tests failed" --channel "#alerts" --after 2026-01-01 --before 2026-02-01
# Search messages only
slack search messages "stably ai" --user "@stablyai" --channel general
# Search files only (downloads files and returns local paths)
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)
slack user list --workspace "https://workspace.slack.com" --limit 200 | jq .
# Get one user by id or handle
slack user get U12345678 --workspace "https://workspace.slack.com" | jq .
slack user get "@alice" --workspace "https://workspace.slack.com" | jq .
slack canvas get "https://workspace.slack.com/docs/T123/F456"
slack canvas get "F456" --workspace "https://workspace.slack.com"
See CONTRIBUTING.md.
Code. Ship. Test.
FAQs
Slack automation CLI for AI agents
We found that @discountry/slack-cli demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.

Security News
TeamPCP is partnering with ransomware group Vect to turn open source supply chain attacks on tools like Trivy and LiteLLM into large-scale ransomware operations.

Security News
/Research
Widespread GitHub phishing campaign uses fake Visual Studio Code security alerts in Discussions to trick developers into visiting malicious website.