You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix broken npm install (missing dist) + add CI (#2)
## Summary
🤖 Generated with [Nori](https://www.npmjs.com/package/nori-ai)
- `nori-slack-cli@0.1.0` on npm shipped without `dist/`, so `npm install
-g nori-slack-cli` created a `nori-slack` bin pointing at a missing
file. Fix: add `"files": ["dist"]` and `"prepare": "npm run build"` to
`package.json`; bump to `0.1.1`.
- New `test/packaging.test.ts` black-box test that `npm pack`s (in an
isolated tmpdir to avoid racing with `build.test.ts`), installs the
tarball into another tmpdir, and runs `nori-slack list-methods
--namespace chat` — verifies the install works end-to-end. Runs on every
`npm test`.
- New `.github/workflows/pr-ci.yaml` + `main-ci.yaml` mirroring
`nori-registrar` conventions (kebab-case names, single-entry matrix,
`.nvmrc`-driven Node 22). Steps: install → build → test.
- Docs updated: `README.md` now documents `npm install -g
nori-slack-cli` as the primary install path;
`docs.md`/`src/docs.md`/`test/docs.md` corrected for pre-existing stale
claims (e.g., that `postbuild` runs `npm link`, which it never did) and
new packaging invariants.
## Test Plan
- [ ] CI passes on this PR.
- [ ] After merge + `npm publish` (manual, from a clean checkout), `npm
install -g nori-slack-cli@0.1.1` exposes a working `nori-slack` on PATH.
- [ ] `nori-slack list-methods --namespace chat` returns JSON with
`chat.postMessage` in the methods array.
- [ ] `nori-slack --version` prints `0.1.1`.
## Notes / open questions for review
- Action versions pinned at `@v3` to match `nori-registrar` — happy to
bump to `@v4` in a follow-up if preferred.
- `src/index.ts` has a hardcoded `.version('0.1.1')` duplicate with
`package.json`. Left as-is — `test/build.test.ts` already enforces drift
detection by asserting `--version` matches `pkg.version`.
- Did not unpublish `0.1.0` from npm. Within the 72h window it is
possible via `npm unpublish nori-slack-cli@0.1.0` — your call.
Share Nori with your team: https://www.npmjs.com/package/nori-skillsets
Co-authored-by: Nori <contact@tilework.tech>
Copy file name to clipboardExpand all lines: README.md
+9-1Lines changed: 9 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,10 +12,18 @@ Bolt is built for human developers writing TypeScript. This CLI is built for cod
12
12
-**Exhaustive surface.** The agent has access to the full Slack Web API — not a hand-picked subset. Capability boundaries are enforced through **bot token scopes**, not through code.
13
13
-**Bot tokens only.** Uses `SLACK_BOT_TOKEN` exclusively. There is no user-OAuth flow because there is no human in the loop.
14
14
-**Self-locating errors.** Every error response includes a `source` field with the on-disk path to the CLI, so an agent can read the source code to debug.
15
-
-**Distributed as source.**Build it locally; the postbuild step makes `nori-slack`available on your `PATH`.
15
+
-**Install from npm.**`npm install -g nori-slack-cli` puts `nori-slack` on your `PATH`. Cloning and building from source is also supported for contributors.
- A TypeScript CLI that exposes the entire Slack Web API as a single command: `nori-slack <method> [--param value ...]`
@@ -11,31 +11,49 @@ Path: @/nori-slack-cli
11
11
- Supports `describe <method>` to look up parameter documentation for any Slack API method without requiring a token -- the metadata map covers all methods in `KNOWN_METHODS`, so agents always get full parameter documentation rather than a fallback
12
12
13
13
### How it fits into the larger codebase
14
-
-Lives as a standalone tool under the `nori-integrations` monorepo, in the `slack` worktree
15
-
-Intended to be `npm link`ed or installed globally so agents can invoke `nori-slack` from any working directory
14
+
-Standalone repository (was originally imported from the `nori-integrations` monorepo and now lives on its own). Distributed via the public npm registry as `nori-slack-cli`
15
+
-The canonical install path is `npm install -g nori-slack-cli`, which places the `nori-slack`binary on `PATH`; `npm link`from a local clone is retained for contributors
16
16
- Authentication is bot-token-only via `SLACK_BOT_TOKEN` environment variable (no user OAuth flows)
17
17
- 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
18
-
- The project spec lives in [spec/APPLICATION-SPEC.md](spec/APPLICATION-SPEC.md)
19
18
- The pagination merge logic in [src/paginate.ts](src/paginate.ts) is a pure function decoupled from the Slack SDK -- it operates on any `AsyncIterable` of page objects
19
+
- User-facing installation and usage documentation lives in [README.md](README.md)
20
20
21
21
### Core Implementation
22
22
- Entry point is [src/index.ts](src/index.ts), which uses Commander.js with `allowUnknownOption()` so arbitrary `--flag value` pairs pass through without Commander rejecting them
23
23
- The dynamic handler has three code paths: `--dry-run` short-circuits after param resolution (no token required, no API call), `--paginate` triggers `WebClient.paginate()` + `mergePages()`, and the default path uses `WebClient.apiCall()`
24
24
- Two input modes: CLI flags (`--channel C123 --text "hi"`) and piped JSON via `--json-input`; when both are provided, CLI flags override stdin values
25
-
- Two discovery subcommands that do not require `SLACK_BOT_TOKEN`: `list-methods` outputs known method names as JSON (supports `--namespace` filtering and `--descriptions` to include method descriptions), and `describe <method>` returns structured parameter documentation (required params, optional params, pagination support, deprecation notices, and docs URL)
25
+
- Two discovery subcommands that do not require `SLACK_BOT_TOKEN`: `list-methods` outputs known method names as JSON (supports `--namespace` filtering and `--descriptions` to include method descriptions), and `describe <method>` returns structured parameter documentation
26
26
-`describe` uses [src/method-metadata.ts](src/method-metadata.ts), a hand-curated static map covering every method in `KNOWN_METHODS` -- this is static because `@slack/web-api` erases parameter type information at compile time, so runtime introspection is not possible
27
27
- For unknown methods (not in `KNOWN_METHODS`), `getMethodMetadata()` returns a fallback entry with empty params and a generated docs URL, so `describe` never errors; the `known` field in the output distinguishes curated entries from fallbacks
28
-
- When an unknown method is used, [src/suggest.ts](src/suggest.ts) provides fuzzy matching via Levenshtein distance against `KNOWN_METHODS`, surfacing up to 3 "Did you mean?" suggestions in both `--dry-run` JSON output and stderr warnings before API calls; suggestions are non-blocking -- unknown methods still proceed to the API
28
+
- When an unknown method is used, [src/suggest.ts](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
29
29
- Successful API responses and error responses both go to stdout as JSON; errors additionally write a human-readable line to stderr
30
30
- Exit codes: `0` for success, `1` for API/token errors, `2` for missing args or invalid stdin JSON
31
31
32
+
### Packaging and distribution
33
+
34
+
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:
35
+
36
+
```
37
+
dist/ (gitignored)
38
+
└─ produced by `prepare: "npm run build"` (runs on npm pack / npm publish / npm install from tarball)
39
+
└─ made executable by `postbuild: "chmod +x dist/index.js"`
40
+
└─ included in the tarball by `files: ["dist"]`
41
+
└─ exposed as `nori-slack` via `bin: { "nori-slack": "./dist/index.js" }`
42
+
└─ verified end-to-end by test/packaging.test.ts on every `npm test`
43
+
```
44
+
45
+
-`test/packaging.test.ts` runs `npm pack`, installs the resulting tarball into a tmpdir, and executes the installed `nori-slack` binary to confirm `dist/` actually ships. See [test/docs.md](test/docs.md) for test-level detail
46
+
- CI is defined in [.github/workflows/pr-ci.yaml](.github/workflows/pr-ci.yaml) (on pull requests to `main`) and [.github/workflows/main-ci.yaml](.github/workflows/main-ci.yaml) (on push to `main`). Both mirror `nori-registrar` conventions: checkout, `actions/setup-node` reading Node version from [.nvmrc](.nvmrc), `npm install`, `npm run build`, `npm test`
47
+
-[.nvmrc](.nvmrc) pins Node 22 to match the `nori-registrar` baseline
48
+
32
49
### Things to Know
33
50
- Flag parsing in [src/parse-args.ts](src/parse-args.ts) converts `--kebab-case` to `snake_case` because the Slack API uses snake_case parameter names
34
51
- Type coercion in `coerceValue` handles booleans (`"true"`/`"false"`), numbers (but preserves leading-zero strings like `"007"`), and inline JSON arrays/objects
35
52
- A standalone `--flag` with no following value (or followed by another `--flag`) is treated as boolean `true`
36
53
- Error formatting in [src/errors.ts](src/errors.ts) maps Slack error codes to actionable suggestions (e.g., `channel_not_found` suggests running `conversations.list`); unknown errors get a generic suggestion pointing to the source directory
37
54
- Every error response includes a `source` field with the filesystem path to the CLI, so agents can locate the source code for debugging
38
55
- The method metadata in [src/method-metadata.ts](src/method-metadata.ts) marks `files.upload` as deprecated with a pointer to the two-step `files.getUploadURLExternal` + `files.completeUploadExternal` flow
39
-
- The `postbuild` script runs `chmod +x` on the output and `npm link` to make the binary available immediately after build
56
+
- The CLI version string is currently duplicated: once in [package.json](package.json)`version` and once as a hardcoded argument to Commander's `.version()` call in [src/index.ts](src/index.ts). Both must be bumped together on release
57
+
-**Packaging invariant**: anything that changes how the distributed artifact is produced must keep the `prepare` script, the `files` allowlist, `bin`, and [test/packaging.test.ts](test/packaging.test.ts) consistent. Concretely, any future change that removes `prepare`, removes `files`, emits generated code outside `dist/`, or adds a second bin entry needs matching updates in the allowlist and the packaging test -- otherwise `npm install -g nori-slack-cli` silently ships a broken binary (this was the exact `0.1.0` regression)
Copy file name to clipboardExpand all lines: src/docs.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,14 +1,14 @@
1
-
# Noridoc: nori-slack-cli/src
1
+
# Noridoc: src
2
2
3
-
Path: @/nori-slack-cli/src
3
+
Path: @/src
4
4
5
5
### Overview
6
6
- Contains all source modules for the CLI: entry point, argument parsing, error formatting, pagination merging, fuzzy method suggestion, the known-methods catalog, and the method metadata registry
7
7
- Compiles from `src/` to `dist/` via TypeScript (ES2022 target, Node16 module resolution)
8
8
9
9
### How it fits into the larger codebase
10
-
-[index.ts](index.ts) is the CLI entry point (shebang `#!/usr/bin/env node`), compiled to `dist/index.js` and exposed as the `nori-slack` binary via `package.json``bin` field
11
-
-[parse-args.ts](parse-args.ts), [errors.ts](errors.ts), and [paginate.ts](paginate.ts) are pure utility modules with no side effects -- they are independently testable and tested in [@/nori-slack-cli/test](../test/)
10
+
-[index.ts](index.ts) is the CLI entry point (shebang `#!/usr/bin/env node`), compiled to `dist/index.js` and exposed as the `nori-slack` binary via the `bin` field in [../package.json](../package.json). The compiled `dist/` directory is produced at pack time by the `prepare` script and shipped to the npm registry via the `files` allowlist -- see [../docs.md](../docs.md) for the full packaging chain
11
+
-[parse-args.ts](parse-args.ts), [errors.ts](errors.ts), and [paginate.ts](paginate.ts) are pure utility modules with no side effects -- they are independently testable and tested in [@/test](../test/)
12
12
-[methods.ts](methods.ts) is a static data file; it is only used by the `list-methods` subcommand and has no effect on which methods the CLI can actually call
Copy file name to clipboardExpand all lines: test/docs.md
+15-6Lines changed: 15 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,14 +1,16 @@
1
-
# Noridoc: nori-slack-cli/test
1
+
# Noridoc: test
2
2
3
-
Path: @/nori-slack-cli/test
3
+
Path: @/test
4
4
5
5
### Overview
6
-
- Unit tests for `parseArgs`, `formatError`, `mergePages`, and method metadata coverage, plus integration tests that invoke the CLI as a subprocess
7
-
- Uses Vitest as the test runner; integration tests in `cli.test.ts` use `tsx` to run TypeScript source directly, while `build.test.ts` compiles via `tsc` and runs the built `dist/index.js` artifact
6
+
- Unit tests for `parseArgs`, `formatError`, `mergePages`, and method metadata coverage, plus integration tests that invoke the CLI as a subprocess, plus an end-to-end packaging test that installs the npm tarball
7
+
- Uses Vitest as the test runner; integration tests in `cli.test.ts` use `tsx` to run TypeScript source directly, `build.test.ts` compiles via `tsc` and runs the built `dist/index.js` artifact, and `packaging.test.ts` runs `npm pack` and installs the tarball into a tmpdir
8
8
9
9
### How it fits into the larger codebase
10
-
- Tests cover the pure utility modules in [@/nori-slack-cli/src](../src/): argument parsing, error formatting, pagination merging, and method metadata
10
+
- Tests cover the pure utility modules in [@/src](../src/): argument parsing, error formatting, pagination merging, and method metadata
11
11
- Integration tests in [cli.test.ts](cli.test.ts) exercise the full CLI binary by spawning `npx tsx src/index.ts` as a child process, verifying end-to-end behavior including exit codes, stdout JSON structure, and stderr output
12
+
-[packaging.test.ts](packaging.test.ts) closes the loop on the npm distribution path documented in [@/docs.md](../docs.md) -- it is the guard against the `0.1.0` regression where `dist/` was missing from the published tarball
13
+
- All tests run on every PR and on every push to `main` via the workflows in [@/.github/workflows](../.github/workflows/)
12
14
- The test directory is excluded from TypeScript compilation via `tsconfig.json`
13
15
14
16
### Core Implementation
@@ -45,12 +47,19 @@ Path: @/nori-slack-cli/test
45
47
46
48
**`build.test.ts`** -- Build verification tests that exercise the compiled output:
47
49
-`beforeAll` runs `tsc` once; all tests share the resulting `dist/index.js`
48
-
- Uses `node dist/index.js` directly (unlike `cli.test.ts` which uses `npx tsx src/index.ts`), verifying the actual build artifact that `npm link` would expose
50
+
- Uses `node dist/index.js` directly (unlike `cli.test.ts` which uses `npx tsx src/index.ts`), verifying the actual build artifact that a global install would expose
**`packaging.test.ts`** -- End-to-end packaging test that validates the npm distribution path:
54
+
-`beforeAll` creates two tmpdirs, runs `npm pack` on the project root to produce a `.tgz`, then `npm init -y` + `npm install --no-save <tarball>` in the second tmpdir to simulate a downstream install
55
+
- Asserts the installed `node_modules/.bin/nori-slack` binary exists and that running it with `list-methods --namespace chat` returns exit 0 with JSON containing `chat.postMessage`
56
+
- Runs on every `npm test` invocation (not gated) so the tarball contents are continuously verified; the `beforeAll` has a 180s timeout because `npm pack` + `npm install` of the tarball is slow
57
+
- This test is the enforcement mechanism for the packaging invariant documented in [@/docs.md](../docs.md): if `prepare`, `files`, or `bin` regress, this test fails before a broken version can be published
58
+
51
59
### Things to Know
52
60
- Integration tests make real HTTP calls to Slack's API (with invalid tokens), so they require network access
53
61
- The `runCli` helper sets a 10-second timeout to prevent hangs
54
62
- Tests intentionally verify structure (JSON shape, field presence, field types) rather than exact string values, making them resilient to Slack API message changes
63
+
-`packaging.test.ts` shells out to `npm` and writes into `os.tmpdir()`, so CI runners must have npm available and writable tmp space
0 commit comments