This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# Development
npm run start:dev # Start with file watch
npm run start:debug:watch # Start with debugger + file watch
# Build
npm run build # Compile TypeScript to dist/
# Tests
npm test # Run all unit tests (requires --experimental-vm-modules)
npm run test:watch # Watch mode
npm run test:cov # With coverage
npm run test:e2e # E2E tests (./test/jest-e2e.json)
# Code quality
npm run lint # ESLint check
npm run format # Prettier formatTo run a single test file:
npx jest src/domain/services/slack-message-processor.spec.tsperigee-bot is a NestJS microservice that listens for Slack app_mention events (via Slack Bolt socket mode) and saves thread message data to a Notion database.
src/
βββ application/slack/ # Slack event listeners (entry point)
βββ domain/
β βββ models/ # Interfaces: SlackMessage, ExtractedMessageData
β βββ services/ # Business logic (functional style)
βββ infrastructure/notion/ # Notion API client (uses raw fetch, not @notionhq/client)
βββ config/ # NestJS ConfigModule setup, validation schema, Swagger
- User mentions bot in a Slack thread
SlackController(application layer) receives the event, validates it's in a thread, fetches the parent messageextractMessageData()(domain layer) extracts URL, title, user, content, and permalink from the messageNotionService(infrastructure layer) creates a new page in the configured Notion database
All config is loaded via NestJS ConfigModule from environment variables. The ConfigValues object (in src/config/config.ts) is the canonical typed config shape used throughout the app. Key env vars:
| Variable | Required | Notes |
|---|---|---|
NOTION_INTEGRATION_TOKEN |
Yes | |
NOTION_DB_ID |
Yes | Target database |
NOTION_API_VERSION |
Yes | e.g. 2025-09-03 |
SLACK_PERIGEE_BOT_TOKEN |
Yes | Bot OAuth token |
SLACK_PERIGEE_APP_TOKEN |
Yes | Socket mode token |
SLACK_PERIGEE_SIGNING_SECRET |
Yes | |
SLACK_WORKSPACE_DOMAIN |
No | Used for permalink construction |
See .env.tmp for a full template.
- Functional style: Domain services use higher-order functions and avoid mutation.
extractMessageData(client)returns an async function (curried). Prefermap/reduce/filterover loops. - Notion API:
NotionServiceuses rawfetch()(not the@notionhq/clientSDK) for all API calls. - Slack: Uses
@slack/boltsocket mode β no HTTP webhooks required. Bot connects via websocket using the App Token. - Logging:
nestjs-pinowith pretty-print in development, JSON in production.
tsconfig.json has relaxed settings (strictNullChecks: false, noImplicitAny: false). ESLint also allows any. These are intentional trade-offs for this project β do not tighten them without explicit instruction.
Multi-stage Dockerfile builds to a minimal Alpine image running as unprivileged user obuser (UID 1001) on port 4000. Use docker-run.sh for local container runs.
Commitlint enforces conventional commits (Husky hook). Format: type: description (e.g., feat:, fix:, chore:).