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

@mushi-mushi/plugin-slack-app

v0.2.12

Published

Mushi Mushi plugin for Slack — /mushi slash command router, Slack request verification, OAuth install helpers and an app manifest, for self-hosting. Mushi Cloud has Add to Slack built in.

Readme

@mushi-mushi/plugin-slack-app

Your AI wrote it. Mushi tells you why it broke.

Building blocks for a Mushi Slack app: the /mushi slash command router, Slack request verification, the OAuth install helpers, and a Slack app manifest.

Do you need this package? Not on Mushi Cloud: open the Integrations page of the console and click Add to Slack. Report cards with buttons, threaded replies and /mushi then work with no code. This package is for people who run the Mushi backend themselves, or who want to host the Slack endpoints on their own server.

npm i @mushi-mushi/plugin-slack-app
import { verifySlackRequest, buildSlashRouter } from '@mushi-mushi/plugin-slack-app'

Docs: https://kensaur.us/mushi-mushi/docs/plugins/slack

Run standalone, createSlackPlugin(...) posts report and fix events to a Slack incoming webhook, and its handleSlackInteraction only verifies and acknowledges a button click; acting on it (dispatching a fix, say) is up to your server. On Mushi's own backend, clicks are handled by the slack-interactions function.

One-time Slack app setup (self-hosted Mushi backend)

  1. Go to api.slack.com/apps → Create from manifest.
  2. Paste the contents of manifest.json. It targets the hosted Mushi backend; self-hosters replace the https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1 prefix with their own functions host:
    • slash command /mushi → <functions>/api/v1/webhooks/slack/commands
    • Events API → <functions>/api/v1/webhooks/slack/events (Slack sends a one-time url_verification challenge; the route answers it, so save the manifest with the backend already deployed)
    • Interactivity → <functions>/slack-interactions
    • OAuth redirect → <functions>/api/v1/webhooks/slack/oauth-callback (Running this package standalone instead? Point everything at https://<your-host>/slack/command, /slack/interactions (mount handleSlackInteraction there; see the note above) and /slack/oauth/callback.)
  3. Copy the signing secret + client ID / secret into the backend secrets (SLACK_SIGNING_SECRET, SLACK_CLIENT_ID, SLACK_CLIENT_SECRET).
  4. Install to the workspace from the Mushi console (Integrations page → Slack → Add to Slack) so the bot token is vaulted per project, then invite the bot to the channels where clips will be dropped.

Scopes and events (why each one is in the manifest)

| Scope / event | Used for | | --- | --- | | commands | /mushi slash command | | chat:write, chat:write.public | Report cards, threaded replies, voice transcript cards | | channels:read | Channel picker in the console | | channels:history, groups:history, im:history | Receive message events (audio clips arrive as message events with files[]) in public / private channels and DMs | | files:read | files.info + downloading the clip (url_private_download / aac) for transcription | | im:read, im:write | Open a DM with the invoking user | | users:read, users:read.email | Map Slack users to console members | | message.channels, message.groups, message.im | Voice clips shared in channels / private channels / DMs | | file_shared | Second signal for a shared clip (deduped on the file id) | | app_mention, app_home_opened | Existing mention + App Home behaviour |

Voice clips are never transcribed by Slack's own transcription object; the backend downloads the audio and runs its own speech-to-text, then posts the verbatim transcript with Confirm / Cancel buttons before anything is dispatched.

Quick start — Hono server

import { Hono } from 'hono'
import {
  verifySlackRequest,
  buildSlashRouter,
  buildInstallUrl,
  exchangeCode,
} from '@mushi-mushi/plugin-slack-app'

const app = new Hono()
const signingSecret = process.env.SLACK_SIGNING_SECRET!

const router = buildSlashRouter({
  listReports: async (projectId, limit) => mushi.listReports(projectId, limit),
  openReport:  async (id) => mushi.getReport(id),
  transitionReport: async (id, status) => mushi.transition(id, status),
  projectIdForTeam: async (teamId) => db.projectForSlackTeam(teamId),
})

app.post('/slack/command', async (c) => {
  const raw = await c.req.text()
  const verdict = verifySlackRequest({
    signingSecret,
    timestampHeader: c.req.header('X-Slack-Request-Timestamp'),
    signatureHeader: c.req.header('X-Slack-Signature'),
    rawBody: raw,
  })
  if (!verdict.ok) return c.json({ error: verdict.reason }, 401)

  const payload = Object.fromEntries(new URLSearchParams(raw)) as never
  return c.json(await router(payload))
})

app.get('/slack/install', (c) => {
  const state = crypto.randomUUID()
  const url = buildInstallUrl(
    {
      clientId: process.env.SLACK_CLIENT_ID!,
      clientSecret: process.env.SLACK_CLIENT_SECRET!,
      redirectUri: process.env.SLACK_REDIRECT_URI!,
      scopes: ['commands', 'chat:write', 'users:read'],
    },
    state,
  )
  return c.redirect(url)
})

/mushi slash command

| Subcommand | Effect | | -------------------- | ------------------------------------------------------ | | /mushi voice <text>| Dictate a request (hosted backend only): transcript is read back with Confirm / Cancel buttons before dispatch. | | /mushi list | Most recent open reports (5 standalone, 10 on the hosted backend). | | /mushi open <id> | Show classification + summary for one report. | | /mushi resolve <id>| Transition a report to fixed. | | /mushi help | Print the command reference. |

All responses are ephemeral (visible only to the invoking user) unless you override response_type in a custom handler.

Security

  • verifySlackRequest runs HMAC-SHA256 over v0:<timestamp>:<rawBody> and compares in constant time. Reject requests older than 5 minutes to block replay.
  • OAuth token rotation is enabled in the manifest — persist both access_token and refresh_token.
  • Never log the raw payload from interactive actions; Slack embeds the invoking user's email there.

License

MIT


Monorepo scale (July 2026): 76 edge functions · 509 SQL migrations · 13 outbound plugins · 11 inbound adapters · 19 pipeline agents. Canonical counts: docs/stats.md · pnpm docs-stats