This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Skiphooks is a Next.js 15 full-stack app that:
- Receives GitHub webhooks and forwards them to Slashwork groups via GraphQL
- Provides AI-powered weekly summaries and cross-group digests (Anthropic Claude)
- Auto-discovers Slashwork groups and syncs them to a local DB
- Polls Google Calendar for event reminders
bun install # Install dependencies
bun run dev # Next.js dev server
bun run build # Production build
bun test # Run all tests
bun test --filter "handler" # Match test file name
bun test src/calendar/calendar.test.ts # Run single file
bunx tsc --noEmit # Type-check without emitting
./test-webhook.sh skipper # Send test webhook to a route
./summarize-group.sh skjs week10 # CLI: summarize a group's weekDefault to Bun for everything. Bun auto-loads .env — don't use dotenv.
bun:testfor testing — don't use Jest/Vitestbun install/bun run/bunx— not npm/yarn/npx
Pages and API routes live in app/. Clerk auth protects all pages except webhooks and health check.
Pages:
/slashwork— Database state viewer (auth tokens, groups, routes, discovered groups, digest status)/summary— Per-group weekly summary with AI (pick group, week, prompt → summarize → publish)/digest— Cross-group weekly digest (config, manual trigger, auto-post toggle)/scout— Reddit Scout dashboard
API routes:
POST /github/[route]— GitHub webhook receiver (signature verify → handler → Slashwork post)POST /api/summary— Generate single-group summary (acceptsgroupIdorgroupname)POST /api/summary/publish— Publish summary to a groupGET/PUT /api/digest/config— Digest configurationPOST /api/digest/run— Generate cross-group digestPOST /api/digest/publish— Publish digest to configured target groupGET /health— Health check
PostgreSQL via pg library. Connection string: POSTGRESQL_ADDON_URI. Migrations run automatically on startup via instrumentation.ts.
Tables: auth_tokens, groups, routes, slashwork_groups (discovered), weekly_digest_config, calendar_users, plus Scout tables.
Key functions: resolveRouteFromDb(), getGroups(), getAuthToken(), getDiscoveredGroups(), getDigestConfig(), upsertDigestConfig()
POST /github/[route] → resolveRouteFromDb(routeName) → verify HMAC-SHA256 signature → look up handler by x-github-event → format to markdown → postToSlashwork()
Each implements EventHandler interface: isRelevantAction(action?) + format(payload) → { markdown }. Handlers: pull-request.ts, issues.ts, issue-comment.ts, push.ts, release.ts.
GraphQL client with Bearer token auth. postToSlashwork(connection, streamId, markdown). validateConnection() checks auth on startup.
Shared utilities for fetching and formatting Slashwork posts:
fetchGroupPosts(graphqlUrl, authToken, groupId, start, end)— paginated post fetcher (up to 10 pages of 100)formatPosts(posts)— hierarchical text formatting (post → comments → replies)generateDigest()— per-group summaries via Haiku, then meta-summarycomputeDigestWindow()— Thursday 2pm UTC → next Thursday 2pm UTC
Three background tasks started on app boot:
- Route validation — validates all Slashwork auth tokens
- Group sync (
src/slashwork-sync.ts) — discovers all Slashwork groups viagroupSearchAPI every 24h - Weekly digest (
src/weekly-digest.ts) — checks hourly, auto-posts digest every Thursday at 2pm UTC
Google Calendar polling that posts event reminders. Config from DB (calendar_users table). Requires GOOGLE_SERVICE_ACCOUNT_KEY env var.
loadAppConfig() loads GITHUB_WEBHOOK_SECRET and SLASHWORK_GRAPHQL_URL. Calendar and Scout configs are separate.
Test files live next to source. Pattern: import { test, expect, describe } from "bun:test".
bun test # All tests
bun test --filter "handler" # Match test file name
bun test src/calendar/calendar.test.ts # Run single fileSee .env.example. Key vars:
GITHUB_WEBHOOK_SECRET— webhook signature verificationSLASHWORK_GRAPHQL_URL— GraphQL endpointPOSTGRESQL_ADDON_URI— PostgreSQL connection stringANTHROPIC_API_KEY— for AI summaries and digests (usesclaude-haiku-4-5)NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY/CLERK_SECRET_KEY— Clerk authGOOGLE_SERVICE_ACCOUNT_KEY— calendar feature (optional)PORT— server port (default 3000)
Auth tokens for Slashwork groups are stored in the DB (auth_tokens table), not env vars.
Strict mode with noUncheckedIndexedAccess: true. Target ESNext with bundler module resolution. Path alias: @/* → project root.
Clever Cloud via GitHub Actions (.github/workflows/deploy.yml). Push to main → build → typecheck → tests → force-push to Clever Cloud → auto-deploy. Migrations run automatically on each deploy via instrumentation.ts.
Vanilla CSS with CSS variables (defined in app/globals.css). No component library. Each page has its own CSS file with a prefix: sw- (slashwork), sum- (summary), dig- (digest), scout- (scout), nav- (navigation).