Split the monolithic gitea/helpers.ts (which had Discord imports and created circular dependencies) into three focused libs: 1. **gitea/** — Pure API client: fetchPR, fetchReviewComments, convertUsername, threadName. No side effects or external deps. 2. **discord/** — Discord client setup: bot login, event listeners, slash commands. Now isolated from Gitea internals. 3. **bridge/** — New integration layer: webhook handler, DB mappings (Gitea PR ↔ Discord thread), Discord helpers, and createPRComment. Dependencies now flow one direction: bridge → gitea and bridge → discord. No circular imports. Added: - Barrel exports (index.ts) for each lib with public API - README.md for each lib documenting the barrel exports - Comprehensive spike README.md with setup guide and architecture explanation - Integration tests for webhooks (callback-based, no race conditions) - Unit tests for pure API functions - CLAUDE.md with links to each lib's README This architecture makes it possible for AI to understand a lib by reading just its README, keeping context focused and small. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2.1 KiB
2.1 KiB
Spike
Discord-Gitea bridge bot. Syncs PRs, comments, and code reviews between a Gitea server (git.nose.space) and Discord.
Architecture
src/
├── server.tsx — HTTP server (webhook endpoint, health check, error log)
├── config.ts — Dev/prod environment config (DB paths, channel IDs, username mappings)
├── discord/ — Discord bot client, event listeners, slash commands
├── gitea/ — Gitea API calls, types, username conversion
└── bridge/ — Wiring between Gitea and Discord (webhook handler, DB, Discord helpers)
Dependencies flow one way: bridge/ → gitea/, bridge/ → discord/. Neither gitea/ nor discord/ imports from the other.
Libs
Each directory with an index.ts barrel is a standalone lib. Read the lib's README for its public API — you don't need to read internals to use it.
- gitea/ — Pure Gitea API client and types. No side effects, no Discord, no DB.
- discord/ — Discord bot client, events, slash commands. Hands off to bridge for Gitea integration.
- bridge/ — The glue. Webhook handler, Discord helpers, SQLite DB for ID mappings.
Import rules:
- External code imports from the barrel only:
import { handleGiteaWebhook } from "./bridge" - Never reach into internal files:
import { handleGiteaWebhook } from "./bridge/webhook-handler" - Internal files within a lib can import from each other freely
This keeps AI context small. When working on code that uses a lib, only read the lib's README — not every internal file.
Running
bun run subdomain:dev— Start with hot reloadbun run subdomain:start— Start for productionbun test— Run integration tests (requires Tailscale funnel + test env vars)
Environment Variables
DISCORD_TOKEN— Discord bot tokenDISCORD_CLIENT_ID— Discord application client IDGITEA_API_TOKEN— Gitea API tokenDATA_DIR— Data directory (prod only)NODE_ENV—productionor omit for devPORT— Server port (default 3000)