|
| 1 | +# AGENTS.md - bolt-js |
| 2 | + |
| 3 | +Instructions for AI coding agents working on this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +Slack Bolt for JavaScript -- a framework for building Slack apps, fast. |
| 8 | + |
| 9 | +- **Foundation:** Built on top of `@slack/web-api`, `@slack/oauth`, `@slack/socket-mode`, and other `@slack/*` packages (see `package.json` for the full list and versions). |
| 10 | +- **Language:** TypeScript-first, compiled to CommonJS. |
| 11 | +- **Node version:** See `engines` in `package.json` for minimum Node.js and npm versions. |
| 12 | +- **Repository**: <https://github.com/slackapi/bolt-js> |
| 13 | +- **Documentation**: <https://docs.slack.dev/tools/bolt-js/> |
| 14 | +- **npm**: <https://www.npmjs.com/package/@slack/bolt> |
| 15 | +- **Current version**: defined in `package.json` |
| 16 | +- **Examples**: Sample apps in `examples/` (Socket Mode, OAuth, Lambda, custom receivers, etc.) |
| 17 | + |
| 18 | +## Environment Setup |
| 19 | + |
| 20 | +```bash |
| 21 | +# Install all dependencies |
| 22 | +npm install |
| 23 | +``` |
| 24 | + |
| 25 | +## Common Commands |
| 26 | + |
| 27 | +Before considering any work complete, you MUST run `npm test` and confirm it passes. |
| 28 | + |
| 29 | +```bash |
| 30 | +npm test # Full pipeline: build -> lint -> type tests -> unit test coverage |
| 31 | +npm run build # Clean build (rm dist/ + tsc compilation) |
| 32 | +npm run lint # Biome check (formatting + linting) |
| 33 | +npm run lint:fix # Biome auto-fix |
| 34 | +npm run test:unit # Unit tests only (mocha) |
| 35 | +npm run test:coverage # Unit tests with coverage (c8) |
| 36 | +npm run test:types # Type definition tests (tsd) |
| 37 | +npm run watch # Watch mode for development (rebuilds on src/ changes) |
| 38 | +``` |
| 39 | + |
| 40 | +## Architecture |
| 41 | + |
| 42 | +### Event Flow |
| 43 | + |
| 44 | +Incoming events flow through a middleware chain before reaching listeners: |
| 45 | + |
| 46 | +1. **Receiver** ingests event from Slack (HTTP, Socket Mode, or Lambda) |
| 47 | +2. Receiver calls `App.processEvent(ReceiverEvent)` |
| 48 | +3. **Global middleware** chain executes (authorization, self-event ignoring, custom middleware) |
| 49 | +4. App determines event type and matches relevant **listeners** based on constraints |
| 50 | +5. **Listener-specific middleware** chains execute |
| 51 | +6. **Listener handler** runs with full context |
| 52 | + |
| 53 | +For FaaS environments (`processBeforeResponse: true`), acknowledgment happens after the handler executes. |
| 54 | + |
| 55 | +### Core Classes |
| 56 | + |
| 57 | +- **`App`** (`src/App.ts`) -- Central orchestrator. Registers listeners via methods (`app.event()`, `app.action()`, `app.command()`, etc.). Dispatches incoming events through middleware to matching listeners. Manages the Web API client pool, authorization, and error handling. |
| 58 | +- **`Receiver`** interface (`src/types/receiver.ts`) -- Pluggable transport layer abstraction. Methods: `init(app)`, `start()`, `stop()`. |
| 59 | + - `HTTPReceiver` (`src/receivers/HTTPReceiver.ts`) -- Express v5 HTTP server, default receiver |
| 60 | + - `SocketModeReceiver` (`src/receivers/SocketModeReceiver.ts`) -- WebSocket-based, no public URL needed |
| 61 | + - `ExpressReceiver` (`src/receivers/ExpressReceiver.ts`) -- Integrates with an existing Express v5 app |
| 62 | + - `AwsLambdaReceiver` (`src/receivers/AwsLambdaReceiver.ts`) -- AWS Lambda handler |
| 63 | +- **`Assistant`** (`src/Assistant.ts`) -- AI assistant thread handling middleware. Intercepts assistant thread events (`assistant_thread_started`, `assistant_thread_context_changed`, `message` in assistant threads) and dispatches them to registered sub-handlers. Provides utilities: `say`, `setStatus`, `setSuggestedPrompts`, `setTitle`, `getThreadContext`, `saveThreadContext`. Uses `AssistantThreadContextStore` (`src/AssistantThreadContextStore.ts`) for thread context persistence. |
| 64 | +- **`CustomFunction`** (`src/CustomFunction.ts`) -- Workflow custom function handler. Provides `complete()` and `fail()` utilities for function execution lifecycle. |
| 65 | +- **`WorkflowStep`** (`src/WorkflowStep.ts`) -- **Deprecated.** Use `CustomFunction` and `app.function()` instead. |
| 66 | + |
| 67 | +### Middleware System |
| 68 | + |
| 69 | +Middleware uses a chain-of-responsibility pattern. Each middleware receives args and calls `next()` to continue the chain. |
| 70 | + |
| 71 | +**Type:** `Middleware<Args> = (args: Args & AllMiddlewareArgs) => Promise<void>` |
| 72 | + |
| 73 | +**AllMiddlewareArgs** (always available): |
| 74 | + |
| 75 | +- `context` -- Event metadata (botToken, userToken, botId, botUserId, teamId, enterpriseId, etc.) |
| 76 | +- `logger` -- Logger instance |
| 77 | +- `client` -- Web API client (pre-authorized) |
| 78 | +- `next` -- Call to continue the middleware chain |
| 79 | + |
| 80 | +**Built-in middleware** in `src/middleware/builtin.ts` includes constraint matchers (event type, command name, message pattern, action/shortcut/view constraints), type guards (`onlyActions`, `onlyCommands`, etc.), `ignoreSelf`, and `autoAcknowledge`. |
| 81 | + |
| 82 | +### Listener Methods |
| 83 | + |
| 84 | +| Method | Description | Must `ack()`? | |
| 85 | +|--------|-------------|---------------| |
| 86 | +| `app.event(type, fn)` | Events API events | No | |
| 87 | +| `app.message([pattern,] fn)` | Message events (optional string/RegExp filter) | No | |
| 88 | +| `app.action(constraints, fn)` | Block Kit interactive actions (buttons, selects, etc.) | Yes | |
| 89 | +| `app.command(name, fn)` | Slash commands | Yes | |
| 90 | +| `app.shortcut(constraints, fn)` | Global and message shortcuts | Yes | |
| 91 | +| `app.view(constraints, fn)` | Modal view_submission / view_closed | Yes | |
| 92 | +| `app.options(constraints, fn)` | External data source requests | Yes | |
| 93 | +| `app.function(callbackId, fn)` | Custom workflow function executions | Auto-acknowledged | |
| 94 | + |
| 95 | +### Listener Arguments |
| 96 | + |
| 97 | +Listeners receive a single object with these properties (availability depends on event type): |
| 98 | + |
| 99 | +- `payload` / type-specific alias (`event`, `action`, `command`, `shortcut`, `view`, `options`, `message`) -- The incoming event data |
| 100 | +- `say` -- Send a message to the associated channel |
| 101 | +- `ack` -- Acknowledge receipt of the event (required for interactive events within 3 seconds) |
| 102 | +- `respond` -- Respond via `response_url` |
| 103 | +- `client` -- Pre-authorized Web API client |
| 104 | +- `context` -- Event metadata and authorization info |
| 105 | +- `body` -- Full request body from Slack |
| 106 | +- `complete` / `fail` -- Workflow function completion (for `app.function()`) |
| 107 | + |
| 108 | +### Authorization |
| 109 | + |
| 110 | +- **Single workspace:** Provide `token` in `AppOptions` -- used for all events. |
| 111 | +- **Multi-workspace:** Provide an `authorize` function that receives `AuthorizeSourceData` (teamId, enterpriseId, userId, conversationId, isEnterpriseInstall) and returns `AuthorizeResult` (botToken, userToken, botId, botUserId, etc.). |
| 112 | +- OAuth support via `@slack/oauth` -- configure `clientId`, `clientSecret`, `stateSecret`, `scopes`, `installationStore` in `AppOptions`. |
| 113 | + |
| 114 | +## Code Conventions |
| 115 | + |
| 116 | +- **TypeScript** throughout. Compiler options in `tsconfig.json` (extends `@tsconfig/node18`, CommonJS output). |
| 117 | +- **Biome** for formatting and linting. Configuration in `biome.json`. |
| 118 | +- **Testing:** See the Testing section below for test frameworks and conventions. |
| 119 | + |
| 120 | +## Critical Rules |
| 121 | + |
| 122 | +1. **Use Biome exclusively** -- never ESLint or Prettier. Config is in `biome.json`. |
| 123 | +2. **Run `npm test` before submitting** -- this runs the full pipeline (build + lint + types + coverage). |
| 124 | +3. **Follow existing patterns** -- when adding new listener types, middleware, or receivers, match the structure and style of existing implementations. |
| 125 | +4. **Don't duplicate `package.json` values** -- reference it for versions, engines, and dependency lists. |
| 126 | +5. **Don't add `WorkflowStep` code** -- it is deprecated. Use `CustomFunction` and `app.function()` instead. |
| 127 | +6. **Build before running unit tests directly** -- `npm test` handles this automatically, but `npm run test:unit` requires a build to exist first. |
| 128 | +7. **Keep the Receiver abstraction clean** -- receivers should only handle transport concerns (ingesting events, sending ack responses). Business logic belongs in middleware and listeners. |
| 129 | +8. **Prefer middleware for cross-cutting concerns** -- authorization, logging, validation, and feature-level request handling (like `Assistant`) all use the middleware pattern. |
| 130 | +9. **TypeScript types are part of the API** -- changes to exported types are breaking changes. Add type tests for new public types. |
| 131 | +10. **Every listener type needs four things:** type definitions, built-in middleware matchers, an App method, and tests. |
| 132 | + |
| 133 | +## Adding a New Listener Type |
| 134 | + |
| 135 | +This is one of the more complex contribution patterns. Follow these steps: |
| 136 | + |
| 137 | +1. **Define types** in `src/types/` -- create the event-specific middleware args interface (see existing patterns in `src/types/actions/`, `src/types/events/`, etc.). |
| 138 | +2. **Add built-in middleware matchers** in `src/middleware/builtin.ts` -- create a constraint-matching function and a type guard (e.g., `onlyMyType`). |
| 139 | +3. **Add the listener method** in `src/App.ts` -- follow the pattern of existing methods like `action()`, `command()`, etc. Wire up the middleware matchers and register listeners. |
| 140 | +4. **Export types** from `src/types/index.ts` and `src/index.ts`. |
| 141 | +5. **Add unit tests** in `test/unit/` mirroring the source structure. |
| 142 | +6. **Add type tests** in `test/types/` using tsd. |
| 143 | + |
| 144 | +## Adding Middleware |
| 145 | + |
| 146 | +1. Implement the middleware function with signature `(args: MiddlewareArgs & AllMiddlewareArgs) => Promise<void>`. Call `await next()` to continue the chain. |
| 147 | +2. For built-in middleware, add to `src/middleware/builtin.ts` and export from `src/middleware/`. |
| 148 | +3. For complex middleware (like `Assistant`), create a dedicated file in `src/` with a class that provides a `getMiddleware()` method returning the middleware function. |
| 149 | +4. Register in the App's middleware chain in `src/App.ts` where the default middleware is assembled. |
| 150 | +5. Add tests in `test/unit/middleware/`. |
| 151 | + |
| 152 | +## Testing |
| 153 | + |
| 154 | +### Test Structure |
| 155 | + |
| 156 | +Tests mirror the source directory structure: |
| 157 | + |
| 158 | +```text |
| 159 | +test/unit/ |
| 160 | + App/ # App class tests |
| 161 | + middleware/ # Middleware tests |
| 162 | + receivers/ # Receiver tests |
| 163 | + helpers/ # Helper function tests |
| 164 | + Assistant.spec.ts |
| 165 | + CustomFunction.spec.ts |
| 166 | + conversation-store.spec.ts |
| 167 | + ... |
| 168 | +test/types/ # tsd type tests |
| 169 | +``` |
| 170 | + |
| 171 | +### Test Conventions |
| 172 | + |
| 173 | +- **Test files** use `*.spec.ts` suffix |
| 174 | +- **Assertions** use chai (`expect`, `assert`) |
| 175 | +- **Mocking** uses sinon (`stub`, `spy`, `fake`) and proxyquire for module-level dependency replacement |
| 176 | +- **Test config** in `test/unit/.mocharc.json` |
| 177 | +- **Where to put new tests:** Mirror the source structure. For `src/Foo.ts`, add `test/unit/Foo.spec.ts`. For `src/receivers/Bar.ts`, add `test/unit/receivers/Bar.spec.ts`. |
| 178 | + |
| 179 | +### CI |
| 180 | + |
| 181 | +CI configuration is in `.github/workflows/ci-build.yml`. Tests run across multiple Node.js versions on every push to `main` and every PR. Coverage is uploaded to Codecov. |
| 182 | + |
| 183 | +## Security Considerations |
| 184 | + |
| 185 | +- **Request signature verification:** The built-in receivers validate `x-slack-signature` and `x-slack-request-timestamp` on every incoming HTTP request using `tsscmp` for timing-safe comparison. Never disable `signatureVerification` in production. |
| 186 | +- **Tokens and secrets:** `SLACK_SIGNING_SECRET`, `SLACK_BOT_TOKEN`, and `SLACK_APP_TOKEN` must come from environment variables. Never hardcode or commit secrets. |
| 187 | +- **Authorization middleware:** Verifies tokens and injects an authorized `WebClient` into the listener context. Do not bypass authorization. |
| 188 | +- **Tests:** Always use mock/stub values for tokens and secrets. Never use real credentials in tests. |
0 commit comments