slack-term
Use Slack from a shell, or let your coding agents do it. Every post shows a preview first.
npm install -g slack-term
Also with bun or pnpm. A smaller native Rust build covers the core commands.
Usage
# What needs me? $ slack news $ slack read "#eng" --unreplied $ slack search "deploy" # Reply in a thread: the first run previews, the second sends $ slack send "#eng:1700000000.000100" "Deployed, looks good" Rerun with --code=3f9c $ slack send "#eng:1700000000.000100" "Deployed, looks good" --code=3f9c ✓ Sent # Ack without adding a message $ slack react "#eng:1700000000.000100" white_check_mark # Ask with one-tap choices, then collect the answer $ slack ask "@bob" "Ship v2 today?" "Ship it" "Wait"
Why we built it
This started in March 2026 as a quick tool to read mentions and search Slack from a terminal. Then our coding agents started calling it. Agents post to real channels under real accounts, so most of the codebase now handles safe writes and routing answers back to whichever agent asked the question.
It's the Slack interface for agent-yes, though neither tool depends on the other. It works standalone in any shell.
A confirm code that goes stale
Write commands take two runs. The first prints your active account, the target channel, recent messages and a 4-character confirm code. To actually send, you pass that code to the second run. Nothing is stored between runs; both hash the same state:
code = sha256(channel, thread, last message, your text, you)[:4]
If someone posts in the channel while you're looking at the preview, the last message changes. The second run computes a different hash, rejects your code and prints an updated preview with the new message included.
$ slack send "#eng" "Deploying v2 now" From: @alice (U00000001) — Acme To: #eng ...recent messages... Rerun with --code=3f9c bob, in #eng: "wait, hold the deploy" $ slack send "#eng" "Deploying v2 now" --code=3f9c Code mismatch ...now you see bob's message...
Switching accounts invalidates the code the same way. edit and delete hash the target message's current text, so you don't overwrite a change made after you fetched the preview.
This is not a security boundary. Any script can parse the output and rerun with --code, and our test suite does. It's there to catch accidental sends and races.
Background on the design: A confirm code that goes stale.
What we learned
Most checks only warn
Only a few conditions stop a write: a mismatched code, a URL Slack would mangle, or an agent about to post under a person's account. Everything else prints a warning: a near-duplicate in the same thread, your message already being the last one, an @mention that won't notify anyone, late-night hours for the recipient. send has no --force or --yes. If it did, scripts and agents would hardcode it everywhere.
Agents can't post as you by accident
An agent once ran slack ask without --as-bot and the question went out from a person's account. Now, inside an agent session, the CLI refuses to write with a personal token unless you name an identity: slack @deploy-bot send …, --as-bot or --as-user.
Answers arrive after the asker has moved on
slack ask puts numbered reactions on the question so people can answer with one click. In one count, 13 of 71 questions got answers nobody read, because the agent that asked had moved on. So --waitFor collects an answer later, --pending lists answers nobody has picked up, and --push sends them back to the agent. --edit and --void change or cancel a question.
Previews get sanitized
Display names are user-controlled, and they render in your terminal during a preview. We strip ANSI escape sequences and Unicode right-to-left overrides from them, and mask token-like strings in message previews. Files the CLI writes locally also go through secret redaction: gitleaks' rules, URL parameters, sk- keys and 6-digit codes.
The relay only passes IDs
Polling every channel hits Slack's rate limits fast. For realtime you can deploy our Cloudflare Worker relay on your own account. Slack sends events to the Worker, which forwards IDs such as channel, timestamp and thread over WebSocket or SSE. The client then fetches the message with your own token. Without a relay, stream falls back to paced polling and prints a warning.
Every post is logged
Each send, ask, poll and edit adds a record to a local log with the PID, working directory, git branch and session ID. slack sent queries it to see which agent posted a message, and --push uses it to route answers back.
CJK text breaks links
If Chinese or Japanese text follows a URL with no space, Slack absorbs it into the link. send catches that before the confirm step and shows the fix, or wraps the URL with --fix-urls. Japanese display names also resolve as @mentions, ask follows its readers' language, and late-night warnings use the recipient's timezone when Slack has it.
Fewer messages
Not everything needs a new message. slack react adds eyes or white_check_mark with no confirm step. pinlog keeps one pinned status message and adds updates as thread replies. tick checks off an item in a posted list.
Built for agents, usable by people
Output is plain text in a terminal and JSON with --json. Commands return distinct exit codes for scripting, and local state lives in files.
# Wake on anything that mentions the bot, across channels and threads $ slack stream --grep "<@U00000001>" --json # Ask a person, as the bot, and move on $ slack @deploy-bot ask "@bob" "Ship v2 today?" "Ship it" "Wait" # Hand each answer to the agent that asked, as it arrives $ slack @deploy-bot ask --pending --push --watch # Mark a message as seen $ slack react "#eng:1700000000.000100" eyes
Create a .slack-term/ directory in a repo and commands run there get project-scoped aliases, message prefixes and file-based inbox and outbox queues. It's found by walking up the tree, like .git.
What it does
Read
- slack news
- Your mentions, grouped by day.
- slack read "#eng"
- Channel and DM history, with threads marked.
--unrepliedshows what still needs you. - slack thread
- One whole thread.
- slack search "deploy"
- Full-text search across the workspace.
- slack dump
- Export channel history as markdown.
- slack download
- Save a file attachment.
Write, behind the confirm code
- slack send
- Post to a channel, DM or thread. A pasted permalink replies in that thread.
- slack edit / delete
- Change or remove a message you sent.
- slack upload
- Share one or more files.
- slack schedule
- Send later, list and cancel.
Coordinate
- slack react
- Acknowledge without growing the thread.
- slack ask
- A question with one-tap choices and an answer you can collect in a script.
- slack poll
- A reaction vote, counted for you.
- slack todo
- Task tracking on reactions, across a team.
- slack pinlog
- A pinned status board with a log in its thread.
- slack lists
- Read and update Slack Lists.
Listen and automate
- slack tail "#eng"
- New messages as they arrive, like
tail -f. - slack stream
- Every message matching a regex, across all channels and threads, with a resumable cursor.
- slack relay init
- Scaffold the realtime doorbell Worker.
- slack inbox / outbox
- Route messages to a directory as files, and queue replies the same way.
- slack sent
- Search what this machine posted, and which agent posted it.
Set up
- slack auth
- Sign in with a token or import a desktop or browser session. Several workspaces are fine.
- slack @<who>
- Act as a named bot or person.
- slack doctor
- Check a bot token can carry a two-way DM.
There's more in the README, including drafts, channels and users.
How it got here
From the git log.
- Mar 2026A Rust binary named
slfor mentions and search. The confirm hash onsendby month's end. - AprA TypeScript implementation alongside Rust, published to npm. Multi-workspace profiles and desktop token import.
- May
edit,upload,schedule,tail, browser cookie extraction and 429 Retry-After handling. - Jun
tail --exit-on-messageand--watch-threadto block until a reply arrives, plus--as-botandslack doctorso bot DMs actually notify people. - JulThread context in previews, duplicate and unreplied warnings,
react, Japanese @mentions. - AugSender identity bound into confirm codes. Recipient-timezone quiet hours,
todo,askandpoll. - SepThe local
slack sentlog, cross-platform Chrome cookie decryption, bot agent status and heartbeat. - Oct
pinlog,lists,todos, theasklifecycle, token masking in previews,stream --grepand the Worker relay. - Nov
.slack-term/per-directory scopes, in-process secret redaction,@<who>identities with the guard against agents posting as people, language packs,ask --pending --push.
Alternatives
Use slack-term if you call Slack from scripts or coding agents and want a preview before writes go out. Every send takes two runs and you have to set up a token, so other tools fit better for interactive chat or simple pipes:
| Tool | Choose it for |
|---|---|
| Official Slack CLI | Building and deploying Slack apps. |
| Go slack-term | A full-screen terminal chat client. Same name, different project. |
| wee-slack | Slack inside WeeChat, with ongoing chat. |
| slackcat | Piping files and command output to Slack. |
| slackdump | Archiving and exporting workspace data. |
| slkcli | macOS onboarding from the Slack desktop app's credentials. |