Path: @/
- A TypeScript CLI that exposes the entire Slack Web API as a single command:
nori-slack <method> [--param value ...] - Designed for coding agents: all output is JSON on stdout, human-readable errors go to stderr
- Uses
@slack/web-apiWebClient for dynamic dispatch -- the CLI is not limited to a fixed set of methods - Supports automatic cursor pagination via
--paginate, which fetches all pages and returns a single merged JSON response - Supports
--dry-runto preview resolved API requests without sending them -- designed as a safety net for coding agents to validate parameter resolution - Supports
describe <method>to look up parameter documentation for any Slack API method without requiring a token -- the metadata map covers all methods inKNOWN_METHODS, so agents always get full parameter documentation rather than a fallback
- Standalone repository (was originally imported from the
nori-integrationsmonorepo and now lives on its own). Distributed via the public npm registry asnori-slack-cli - The canonical install path is
npm install -g nori-slack-cli, which places thenori-slackbinary onPATH;npm linkfrom a local clone is retained for contributors - Authentication is bot-token-only via
SLACK_BOT_TOKENenvironment variable (no user OAuth flows) - The CLI is a thin wrapper -- it does not contain business logic, scheduling, or state management; it translates CLI flags into Slack API calls and returns the raw JSON response
- The pagination merge logic in src/paginate.ts is a pure function decoupled from the Slack SDK -- it operates on any
AsyncIterableof page objects - User-facing installation and usage documentation lives in README.md
- Entry point is src/index.ts, which uses Commander.js with
allowUnknownOption()so arbitrary--flag valuepairs pass through without Commander rejecting them - The dynamic handler has three code paths:
--dry-runshort-circuits after param resolution (no token required, no API call),--paginatetriggersWebClient.paginate()+mergePages(), and the default path usesWebClient.apiCall() - Two input modes: CLI flags (
--channel C123 --text "hi") and piped JSON via--json-input; when both are provided, CLI flags override stdin values - Two discovery subcommands that do not require
SLACK_BOT_TOKEN:list-methodsoutputs known method names as JSON (supports--namespacefiltering and--descriptionsto include method descriptions), anddescribe <method>returns structured parameter documentation describeuses src/method-metadata.ts, a hand-curated static map covering every method inKNOWN_METHODS-- this is static because@slack/web-apierases parameter type information at compile time, so runtime introspection is not possible- For unknown methods (not in
KNOWN_METHODS),getMethodMetadata()returns a fallback entry with empty params and a generated docs URL, sodescribenever errors; theknownfield in the output distinguishes curated entries from fallbacks - When an unknown method is used, src/suggest.ts provides fuzzy matching via Levenshtein distance against
KNOWN_METHODS, surfacing "Did you mean?" suggestions; suggestions are non-blocking -- unknown methods still proceed to the API - Successful API responses and error responses both go to stdout as JSON; errors additionally write a human-readable line to stderr
- Exit codes:
0for success,1for API/token errors,2for missing args or invalid stdin JSON
The published npm artifact is assembled at pack time, not committed to git. The relevant package.json fields form a single chain that must stay in sync:
dist/ (gitignored)
└─ produced by `prepare: "npm run build"` (runs on npm pack / npm publish / npm install from tarball)
└─ made executable by `postbuild: "chmod +x dist/index.js"`
└─ included in the tarball by `files: ["dist"]`
└─ exposed as `nori-slack` via `bin: { "nori-slack": "./dist/index.js" }`
└─ verified end-to-end by test/packaging.test.ts on every `npm test`
test/packaging.test.tsrunsnpm pack, installs the resulting tarball into a tmpdir, and executes the installednori-slackbinary to confirmdist/actually ships. See test/docs.md for test-level detail- CI is defined in .github/workflows/pr-ci.yaml (on pull requests to
main) and .github/workflows/main-ci.yaml (on push tomain). Both mirrornori-registrarconventions: checkout,actions/setup-nodereading Node version from .nvmrc,npm install,npm run build,npm test - .nvmrc pins Node 22 to match the
nori-registrarbaseline
- Flag parsing in src/parse-args.ts converts
--kebab-casetosnake_casebecause the Slack API uses snake_case parameter names - Type coercion in
coerceValuehandles booleans ("true"/"false"), numbers (but preserves leading-zero strings like"007"), and inline JSON arrays/objects - A standalone
--flagwith no following value (or followed by another--flag) is treated as booleantrue - Error formatting in src/errors.ts maps Slack error codes to actionable suggestions (e.g.,
channel_not_foundsuggests runningconversations.list); unknown errors get a generic suggestion pointing to the source directory - Every error response includes a
sourcefield with the filesystem path to the CLI, so agents can locate the source code for debugging - The method metadata in src/method-metadata.ts marks
files.uploadas deprecated with a pointer to the two-stepfiles.getUploadURLExternal+files.completeUploadExternalflow - The CLI version string is currently duplicated: once in package.json
versionand once as a hardcoded argument to Commander's.version()call in src/index.ts. Both must be bumped together on release - Packaging invariant: anything that changes how the distributed artifact is produced must keep the
preparescript, thefilesallowlist,bin, and test/packaging.test.ts consistent. Concretely, any future change that removesprepare, removesfiles, emits generated code outsidedist/, or adds a second bin entry needs matching updates in the allowlist and the packaging test -- otherwisenpm install -g nori-slack-clisilently ships a broken binary (this was the exact0.1.0regression)
Created and maintained by Nori.