npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hydra-acp/slack

v0.1.32

Published

Bridge hydra-acp sessions to Slack threads.

Downloads

1,282

Readme

hydra-acp-slack

Bridges every active hydra-acp session to a Slack thread, so any ACP agent (Claude Code, Codex, Gemini, etc.) running through hydra shows up in Slack:

  • One thread per agent session.
  • Tool calls render as cards with status icons (▶ → ✅ / ❌).
  • Tool output is collapsed by default; expand with 👀 / 📖 reactions.
  • Permission prompts surface as :lock: messages; ✅ / ❌ reactions approve or deny.
  • Slack-side messages flow back into the agent as user prompts.
  • New sessions can also be created directly from Slack with the !session bang command (see Slash-style commands).

The bridge runs as a hydra extension (or standalone), watches hydra for active sessions, and attaches over WSS to each one. Sessions that the bot itself starts (via !session) get adopted immediately — no waiting for the next discovery poll.

How it works

                 hydra REST  +-------------+        Slack
       /v1/sessions   <----  |  hydra-acp-slack  |  ---->  Web API
                             |   daemon    |  <----  Socket Mode WS
       hydra WSS      <----> |             |
       /acp                  +-------------+
                                    |
                            ~/.hydra-acp/slack/
                              hidden/     (hidden originals)
                              truncated/  (full output cache)
                              channels.json  (cwd → channel map)

The daemon polls GET /v1/sessions on hydra (default every 2s) and, for each new session id it sees, opens a WebSocket to hydra's /acp endpoint and sends session/attach. Hydra replays the session's history on attach, then live notifications flow through. Slack-side prompts are forwarded back via session/prompt.

Setup

1. Create the Slack app and write the config

npx @hydra-acp/slack setup
# or, after installing globally: hydra-acp slack setup

The wizard creates a Slack app from the manifest in this repo, runs the OAuth dance through a local callback server, prompts you for the App-Level token (the one piece Slack's API can't generate), optionally lets you pick a channel from the ones the bot has been invited to, and writes ~/.hydra-acp/slack.conf with mode 600. About 3–5 minutes; two browser visits.

Re-run hydra-acp slack setup any time to sync the deployed Slack app's manifest with whatever's in this repo — useful when scopes or events change in a release.

AUTHORIZED_USERS is the allowlist of Slack user IDs whose messages the bridge will forward to the agent (and whose reactions are honored for allow/deny/cancel). The wizard seeds it with your own ID. To add teammates, append comma-separated user IDs; find one by clicking a profile in Slack → MoreCopy member ID.

⚠️ Leaving AUTHORIZED_USERS empty means there is no allowlist — anyone the bot can see can prompt the agent and approve tool calls. Fine for a personal bot in a single-member workspace; set it before adding the bot to shared channels.

  1. Go to https://api.slack.com/apps → Create New AppFrom a manifest. Pick the workspace. Paste the contents of assets/slack-manifest.json (toggle the editor to JSON first). Click Next, then Create.

  2. OAuth & PermissionsInstall to Workspace. Copy the xoxb-... Bot User OAuth Token.

  3. Basic Information → scroll to App-Level TokensGenerate Token and Scopes → name it anything → check connections:writeGenerate. Copy the xapp-... token.

  4. In Slack, /invite @<your-bot> in whichever channel you want hydra to post in. Click the channel name → About → scroll to the bottom for the channel ID.

  5. Write ~/.hydra-acp/slack.conf:

    SLACK_BOT_TOKEN=xoxb-...
    SLACK_APP_TOKEN=xapp-...
    SLACK_CHANNEL_ID=C0123456789
    AUTHORIZED_USERS=U12345678

    Then chmod 600 ~/.hydra-acp/slack.conf.

2. State directory

The bridge owns one directory on disk: ~/.hydra-acp/slack/. It's created lazily as the bridge runs; you don't need to set it up by hand. Layout:

~/.hydra-acp/slack/
├── channels.json   # optional: { "<absolute cwd>": "<channelId>", ... }
├── hidden/         # full text of 🙈-hidden messages (so they can be restored)
└── truncated/      # full tool output cached for 📖 expand

channels.json is the only file you might want to seed by hand. It routes hydra sessions to Slack channels by their cwd — useful if you run hydra in multiple project directories and want each in its own channel. Example:

{
  "/home/me/work/big-project": "C01ABC",
  "/home/me/personal/side-quest": "C02DEF"
}

When a session attaches with cwd /home/me/work/big-project, the bridge posts to channel C01ABC. If the cwd isn't in the map (or the session has no cwd), the bridge falls back to SLACK_CHANNEL_ID.

The bridge watches channels.json and reloads it on change — edits take effect the next time a session resolves a channel, no restart required. Editor save styles that do atomic rename-over-original (vim, emacs auto-save, jq -i, etc.) are handled. Bad JSON or a deleted file just leaves the previous map in place and logs a warning so a typo doesn't blow away routing for active sessions.

3. Install or build

From npm (recommended):

npm install -g @hydra-acp/cli @hydra-acp/slack

This drops the hydra-acp (and hydra) CLI plus an hydra-acp-slack binary on your PATH. The CLI dispatches hydra-acp <name> to any hydra-acp-<name> binary on PATH, so the slack bridge is also reachable as hydra-acp slack.

Or from source:

git clone https://github.com/smagnuso/hydra-acp-slack.git ~/dev/hydra-acp-slack
cd ~/dev/hydra-acp-slack
npm install
npm run build

4. Run as a hydra extension (recommended)

Register the extension with hydra. If installed via npm:

hydra-acp extensions add hydra-acp-slack --command hydra-acp-slack

Or pointed at a local build:

hydra-acp extensions add hydra-acp-slack \
  --command node \
  --args ~/dev/hydra-acp-slack/dist/index.js

That writes the equivalent entry into ~/.hydra-acp/config.json:

{
  "extensions": {
    "hydra-acp-slack": {
      "command": ["node"],
      "args": ["/home/you/dev/hydra-acp-slack/dist/index.js"],
      "enabled": true
    }
  }
}

On hydra-acp daemon start, hydra spawns hydra-acp-slack with these env vars set: HYDRA_ACP_DAEMON_URL, HYDRA_ACP_TOKEN, HYDRA_ACP_WS_URL. hydra-acp-slack uses them to discover and attach to sessions. Stdout/stderr land in ~/.hydra-acp/extensions/hydra-acp-slack.log. Lifecycle is managed with hydra-acp extensions start|stop|restart hydra-acp-slack and hydra-acp extensions log hydra-acp-slack -f to tail.

5. Run standalone (alternative)

Set HYDRA_DAEMON_URL and HYDRA_TOKEN in ~/.hydra-acp/slack.conf (or export them as env vars), then:

npm start

The daemon prints which hydra it's polling and which authorized users it accepts.

Configuration keys

| Key | Default | Notes | |-----------------------------|------------------------------------|-------| | SLACK_BOT_TOKEN | (required) | Bot User OAuth Token from Slack, xoxb-.... | | SLACK_APP_TOKEN | (required) | App-Level Token from Slack, xapp-..., with connections:write. | | SLACK_CHANNEL_ID | none | Default channel ID (C…/G…). Used when the session's cwd has no entry in ~/.hydra-acp/slack/channels.json (or the session has no cwd). | | AUTHORIZED_USERS | empty | Comma-separated Slack user IDs (U…) allowed to prompt the agent. Empty = anyone in the bot's channels can prompt — see security note below. Bot reactions (allow/deny/cancel) are gated the same way. | | UPLOAD_BUNDLE_ON_END | true | When the hydra session closes, attach the daemon-built *.hydra bundle (meta + history JSON) to the Slack thread — re-importable into any hydra via hydra-acp sessions import or the browser UI. Set to false to disable. | | WEBSOCKET_STALE_THRESHOLD | 30 | Seconds of continuously-disconnected Slack Socket Mode WS before the bridge process.exit(1)s. Hydra's extension manager respawns it ~1s later with a fresh DNS cache + HTTP client; the existing process gets stuck in a reconnect loop after a network flap (VPN drop, etc.). | | BACKFILL_HISTORY | false | If true, replay hydra's cached history into Slack on attach. Off by default — replays trip Slack rate limits and create noise. | | LIVE_QUIET_MS | 2000 | Inbound silence (ms) needed before considering an attach "live" when BACKFILL_HISTORY=false. | | IMAGE_UPLOAD_RATE_LIMIT | 30 | Reserved. | | IMAGE_UPLOAD_RATE_WINDOW | 60 | Reserved. | | HYDRA_DAEMON_URL | http://127.0.0.1:8765 | Where to reach the hydra daemon. Set automatically when run as a hydra extension. | | HYDRA_WS_URL | derived from HYDRA_DAEMON_URL | WebSocket endpoint for ACP attach. Defaults to ws[s]://<host>:<port>/acp. | | HYDRA_TOKEN | (required) | Bearer token for hydra. Set automatically when run as a hydra extension. | | HYDRA_POLL_INTERVAL_MS | 2000 | How often to poll hydra for session changes. | | DELETE_ABANDONED_THREADS | false | Janitor: scan known channels for _session <id>_ thread parents whose session is no longer in hydra (live or cold) and delete the whole thread (every reply, then the parent). When false (default) the sweep still runs and logs would delete abandoned thread session=<id> … on first detection so you can validate matches before enabling. Delete mode requires the candidate to miss two consecutive sweeps, so a transient daemon read failure can't trigger deletions; dry-run logs immediately and dedupes per-process. Capped at 3 threads per sweep since each one issues N+1 chat.delete calls. | | THREAD_JANITOR_INTERVAL_MS| 60000 (delete) / 300000 (dry-run) | How often the janitor sweeps. Defaults depend on DELETE_ABANDONED_THREADS: 1 min when enabled (prompt cleanup), 5 min in dry-run (each sweep pages conversations.history across known channels, and nothing changes between sweeps once dedupe is populated). | | THREAD_JANITOR_SETTLE_MS | 5000 | Delay before the first sweep so initial attaches can register. The daemon-list check covers any straggler, so this can be small. | | DEBUG | false | Verbose logging. |

Reactions

| Reaction | Action | |----------------------------------------------------------------------------------|--------| | :white_check_mark: / :+1: / :star: | Approve once (picks the agent's allow_once option) | | :unlock: | Approve always (picks allow_always when offered, otherwise falls back to allow_once) | | :x: / :-1: | Deny | | :stop_sign: / :octagonal_sign: / :no_entry: / :no_entry_sign: / :stop: | Cancel — react on the active turn spinner to send session/cancel to the agent. Ignored on any other message. | | :see_no_evil: / :no_bell: | Hide message (toggle to restore) | | :eyes: | Expand truncated tool output | | :book: / :open_book: | Expand full tool output | | :heart: (and friends) | Forward as positive feedback to agent |

Slash-style commands

| Command | Where | Effect | |----------------------------------|------------------|--------| | !debug | inside a thread | Replies with the session's debug info (sessionId, channel, ws state, last-frame time). | | !agents | anywhere | Lists agents installed in hydra's registry (GET /v1/agents). | | !session [agent] [cwd] [prompt…] | anywhere | Asks hydra to create a fresh ACP session (POST /v1/sessions). Both positionals are optional — hydra falls back to defaultAgent and defaultCwd from ~/.hydra-acp/config.json (which itself defaults to claude-code and ~). | | !<rest> | inside a thread | Strict-mirror of slash commands: anything else starting with ! is forwarded as /<rest> — e.g. !hydra title foo/hydra title foo, !hydra agent claude-code/hydra agent claude-code, !create_plan write a function/create_plan write a function. The bot validates the verb against the daemon-advertised command set (available_commands_update, which the daemon merges its /hydra registry with the agent's own commands), so any new daemon or agent verb automatically becomes a !-command here. Unknown verbs get a :grey_question: reaction and a thread reply listing what's available. | | !cat <path> | inside a thread | Reads a file from the session's working directory and posts it in-thread as a syntax-highlighted code block. Path is resolved against the session cwd; absolute paths and ~ are accepted, relative paths escaping cwd are refused. Files larger than 512 KB are refused; text is chunked at ~11500 chars per Slack markdown block with "Show more" / "Show all" buttons for continuation ("Show all" uploads the raw file). Examples: !cat src/foo.ts, !cat /etc/hosts. |

!session parsing rules:

  • The first token, if path-like (/…, ~…, ./…), is the cwd; otherwise it's the agentId.
  • The second token, only if the first was an agentId, may be the cwd.
  • Anything remaining is the prompt sent as the session's first user message.
  • A -- separator forces everything after it to be the prompt — useful when the prompt itself starts with a word that would otherwise be parsed as the agent (e.g. !session -- what time is it?).

Examples:

!session                                  # default agent + default cwd, no first prompt
!session ~/dev/foo                        # default agent in ~/dev/foo
!session opencode                         # opencode in default cwd
!session opencode ~/dev/foo               # both
!session opencode ~/dev/foo fix the bug   # both + first prompt
!session ~/dev/foo fix the bug            # cwd + default agent + first prompt
!session -- what time is it?              # all defaults + first prompt

The bot reacts ✅ on the command message and replies with the resolved agent/cwd. The new thread appears in whichever channel the resolved cwd maps to in ~/.hydra-acp/slack/channels.json — falling back to SLACK_CHANNEL_ID when no mapping is found — which may differ from where !session was posted.

Tests

npm test

Runs the formatter, ndjson, reaction-map, and command-parser tests with the built-in Node test runner.

Out of scope

  • Outbound image upload via file watcher.
  • True ACP-to-ACP bridging (different project).

Status

Functional, in daily use, but rough around the edges. Open issues at the project repo.