Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"permissions": {
"allow": [
"Bash(ncu:*)",
"Bash(pnpm install:*)",
"Bash(pnpm run:*)"
],
"deny": []
}
}
51 changes: 28 additions & 23 deletions .github/workflows/test.workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ concurrency:
jobs:
main:
name: Handover Bot
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
timeout-minutes: 20
services:
postgres:
image: cimg/postgres:14.2
image: postgres:17-alpine
env:
POSTGRES_PASSWORD: password
POSTGRES_DB: postgres
Expand All @@ -30,29 +30,21 @@ jobs:
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgres://postgres:password@localhost:5432/postgres
PORT: 9621
SLACK_APP_TOKEN: XXXX-X-XXXXXXXXXXX-XXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
SLACK_BOT_TOKEN: XXXX-XXXXXXXXXXXXX-XXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXX
SLACK_SIGNING_SECRET: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
HANDOVER_CHANNEL: XXXXXXXXXXX
HANDOVER_TITLE: Dev Handover
OPENAI_API_KEY: XX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Install Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20

- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
name: Install pnpm
id: pnpm-install
with:
version: 8
version: 9
run_install: false

- name: Get pnpm store directory
Expand All @@ -61,7 +53,7 @@ jobs:
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

- uses: actions/cache@v3
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
Expand All @@ -73,16 +65,29 @@ jobs:
run: pnpm install

- name: Generate DB Types
run: pnpm exec prisma generate
run: pnpm run prisma generate

- name: Check Code Formatting
run: pnpm exec xo
run: pnpm run tidy:check

- name: Typescript
run: pnpm exec tsc
run: pnpm run build

- name: Create test env file
run: |
cat > .env.test << EOF
PORT=9621
SLACK_APP_TOKEN=xxx
SLACK_BOT_TOKEN=xxx
SLACK_SIGNING_SECRET=xxx
HANDOVER_CHANNEL=C000
HANDOVER_TITLE=Dev Handover
DATABASE_URL=postgresql://postgres:password@localhost:5432/runn_handover_test?schema=public
OPENAI_API_KEY=sk-xxx
EOF

- name: Migrate Database
run: pnpm exec prisma db push
- name: Setup test database
run: pnpm run prisma:test db push

- name: Tests
run: pnpm exec vitest --watch=false
run: pnpm run test
57 changes: 57 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Common Commands

### Development
- `pnpm run dev` - Run the bot locally with hot reload
- `pnpm install` - Install dependencies
- `pnpm run build` - Build TypeScript to dist/
- `pnpm run start` - Start production build

### Code Quality
- `pnpm run tidy` - Auto-fix code style with XO
- `pnpm run tidy:check` - Check code style without fixing

### Testing
- `pnpm run test` - Run tests (requires .env.test file)
- `pnpm prisma:test db push` - Setup test database

### Database
- `pnpm run prisma db push` - Push schema changes to database
- `pnpm run prisma migrate dev --name=[name]` - Create and run new migration
- `pnpm run prisma generate` - Generate Prisma client
- `pnpm run prisma:test db push` - Setup test database

### Command Conventions
- Use `pnpm run [script]` for package.json scripts
- Use `pnpm exec [command]` for executables not in scripts
- Prisma commands use `pnpm run prisma [args]` wrapper

## Architecture

### Core Components
- **Slack Bot**: Built with @slack/bolt, listens to messages in socket mode
- **Message Processing**: Sequential queue-based processing to handle Slack events
- **Database**: PostgreSQL with Prisma ORM for users, posts, reminders, and formatting rules
- **Commands**: CLI-style commands triggered by @mentions in Slack

### Key Flows
1. **Message Handling**: `listen-to-message.ts` → `map-message-to-action.ts` → `actions.ts`
2. **Command Processing**: Commands are detected by @mention pattern and routed through `command/` directory
3. **Post Management**: Messages create/update PostItems linked to daily Posts per user
4. **Reminders**: Background job checks for users needing handover reminders based on workdays

### Database Schema
- **User**: Slack user info, timezone, reminder settings, workdays
- **Post**: Daily handover posts per user
- **PostItem**: Individual messages/items within a post
- **Reminder**: Reminder messages sent to users
- **Format**: Custom text formatting rules per user

### Environment Setup
Requires `.env` file with Slack tokens, database URL, and OpenAI API key. Use `.env.template` as reference.

### Testing
Tests use Vitest with separate `.env.test` file and test database setup.
20 changes: 20 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
services:
postgres:
image: postgres:17-alpine
container_name: handover-postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: handover
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5

volumes:
postgres_data:
36 changes: 18 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,29 @@
},
"homepage": "https://github.com/Runn-Fast/handover#readme",
"devDependencies": {
"@types/node": "20.12.7",
"del-cli": "5.1.0",
"@types/node": "24.0.0",
"del-cli": "6.0.0",
"env-cmd": "10.1.0",
"nodemon": "3.1.0",
"nodemon": "3.1.10",
"ts-node": "10.9.2",
"typescript": "5.4.5",
"vitest": "1.5.3",
"xo": "0.58.0"
"typescript": "5.8.3",
"vitest": "3.2.3",
"xo": "1.1.0"
},
"dependencies": {
"@prisma/client": "5.13.0",
"@slack/bolt": "3.18.0",
"@slack/web-api": "7.0.4",
"@stayradiated/error-boundary": "4.2.1",
"@prisma/client": "6.9.0",
"@slack/bolt": "4.4.0",
"@slack/web-api": "7.9.2",
"@stayradiated/error-boundary": "4.3.0",
"cilly": "1.0.25",
"date-fns": "3.6.0",
"date-fns-tz": "3.1.3",
"mem": "9.0.2",
"openai": "4.40.0",
"p-queue": "8.0.1",
"prisma": "5.13.0",
"regex-parser": "2.3.0",
"zod": "3.23.5"
"date-fns": "4.1.0",
"date-fns-tz": "3.2.0",
"mem": "10.0.0",
"openai": "5.3.0",
"p-queue": "8.1.0",
"prisma": "6.9.0",
"regex-parser": "2.3.1",
"zod": "3.25.61"
},
"xo": {
"space": true,
Expand Down
Loading