11 KiB
CLAUDE.md
This file provides guidance to Claude Code when working with code in this monorepo.
Note: Many packages and apps have their own CLAUDE.md files with specific implementation details and patterns. Always check for a local CLAUDE.md when working in a specific directory.
Monorepo Structure
This is a pnpm-based monorepo using Turborepo with the following structure:
Apps (@buster-app/*
)
apps/web
- Next.js frontend applicationapps/server
- Node.js/Hono backend serverapps/trigger
- Background job processing with Trigger.dev v3apps/electric-server
- Electric SQL sync serverapps/api
- Rust backend API (legacy)apps/cli
- Command-line tools (Rust)
Packages (@buster/*
)
packages/ai
- AI agents, tools, and workflows using Mastra frameworkpackages/database
- Database schema, migrations, and utilities (Drizzle ORM)packages/data-source
- Data source adapters (PostgreSQL, MySQL, BigQuery, Snowflake, etc.)packages/access-controls
- Permission and access control logicpackages/stored-values
- Stored values managementpackages/rerank
- Document reranking functionalitypackages/server-shared
- Shared server types and utilitiespackages/test-utils
- Shared testing utilitiespackages/vitest-config
- Shared Vitest configurationpackages/typescript-config
- Shared TypeScript configurationpackages/web-tools
- Web scraping and research toolspackages/slack
- Standalone Slack integration (OAuth, messaging, channels)packages/supabase
- Supabase setup and configurationpackages/sandbox
- Sandboxed code execution using Daytona SDK
Plan & Review
Before starting work
- Always in plan mode to make a plan
- After get the plan, make sure you write the plan to
.claude/tasks/TASK_NAME.md
- The plan should be a detailed implementation plan and the reasoning behind them, as well as tasks broken down.
- If the task require external knowledge or certain packages, also research to get latest knowledge (Use Task tool for research)
- Don't over plan it, always think MVP.
- Once you write the plan, firstly ask me to review it. Do not continue until I approve the plan.
While implementing
- You should update the plan as you work.
- After you complete tasks in the plan, you should update and append detailed descriptions of the changes you made, so following tasks can be easily hand over to other engineers.
Development Workflow
When writing code, follow this workflow to ensure code quality:
1. Write Modular, Testable Functions
- Create small, focused functions with single responsibilities
- Design functions to be easily testable with clear inputs/outputs
- Use dependency injection for external dependencies
- IMPORTANT: Write functional, composable code - avoid classes
- All features should be composed of testable functions
- Follow existing patterns in the codebase
2. Build Features by Composing Functions
- Combine modular functions to create complete features
- Keep business logic separate from infrastructure concerns
- Use proper error handling at each level
Environment Variables
This project uses a centralized environment variable system:
- All environment variables are defined at the root level in a single
.env
file - Turbo passes these variables to all packages via the
globalEnv
configuration inturbo.json
- Individual packages validate their required environment variables using the shared
@buster/env-utils
package
Setting Up Environment Variables
-
Copy
.env.example
to.env
at the project root:cp .env.example .env
-
Fill in the required values in
.env
-
All packages will automatically have access to these variables through Turbo
Adding New Environment Variables
When adding new environment variables:
- Add the variable to
.env.example
with a descriptive comment - Add the variable name to the
globalEnv
array inturbo.json
- Update the package's
validate-env.js
script to include the new variable - Update the package's
env.d.ts
file with the TypeScript type definition
Migrating Packages to Centralized Env
To migrate an existing package to use the centralized environment system:
- Remove any local
.env
files from the package - Add
@buster/env-utils
as a dependency:"@buster/env-utils": "workspace:*"
- Update the package's
scripts/validate-env.js
to use the shared utilities:import { loadRootEnv, validateEnv } from '@buster/env-utils'; loadRootEnv(); const requiredEnv = { DATABASE_URL: process.env.DATABASE_URL, // ... other required variables }; const { hasErrors } = validateEnv(requiredEnv); if (hasErrors) process.exit(1);
3. Ensure Type Safety
# Build entire monorepo to check types
turbo run build
# Build specific package/app
turbo run build --filter=@buster/ai
turbo run build --filter=@buster-app/web
# Type check without building
turbo run typecheck
turbo run typecheck --filter=@buster/database
4. Run Biome for Linting & Formatting
# Check files with Biome
pnpm run check path/to/file.ts
pnpm run check packages/ai
# Auto-fix linting, formatting, and import organization
pnpm run check:fix path/to/file.ts
pnpm run check:fix packages/ai
5. Run Tests with Vitest
Important: Always run unit tests before completing any task to ensure code changes don't break existing functionality.
# Run unit tests (always run these when working locally)
turbo run test:unit
# Run unit tests for specific package
turbo run test:unit --filter=@buster/ai
# Run integration tests ONLY for specific features/packages you're working on
turbo run test:integration --filter=@buster/database
# Run specific test file
pnpm run test path/to/file.test.ts
# Watch mode for development
pnpm run test:watch
6. Pre-Completion Checklist
IMPORTANT: Before finishing any task or creating PRs, always run:
# 1. Run unit tests for the entire monorepo
turbo run test:unit
# 2. Build the entire monorepo to ensure everything compiles
turbo run build
# 3. Run linting for the entire monorepo
turbo run lint
Key Testing Guidelines:
- Always run unit tests, build, and lint when working locally before considering a task complete
- Unit tests should be run for the entire monorepo to catch any breaking changes
- Build must pass for the entire monorepo to ensure type safety
- Integration tests should only be run for specific packages/features you're working on (NOT the entire monorepo)
- Fix all failing tests, build errors, and lint errors before completing any task
- Heavily bias toward unit tests - they are faster and cheaper to run
- Mock everything you can in unit tests for isolation and speed
Code Quality Standards
TypeScript Configuration
- Strict mode enabled - All strict checks are on
- No implicit any - Always use specific types
- Strict null checks - Handle null/undefined explicitly
- No implicit returns - All code paths must return
- Consistent file casing - Enforced by TypeScript
Type Safety and Zod Best Practices
- We care deeply about type safety and we use Zod schemas and then export them as types
- We prefer using type abstractions over
.parse()
method calls - Always export Zod schemas as TypeScript types to leverage static type checking
- Avoid runtime type checking when compile-time type checks are sufficient
Biome Rules (Key Enforcements)
useImportType: "warn"
- Use type-only imports when possiblenoExplicitAny: "error"
- Never useany
typenoUnusedVariables: "error"
- Remove unused codenoNonNullAssertion: "error"
- No!
assertionsnoConsoleLog: "warn"
- Avoid console.log in productionuseNodejsImportProtocol: "error"
- Usenode:
prefix for Node.js imports
Logging Guidelines
- Never use
console.log
- Use appropriate console methods:
console.info
for general informationconsole.warn
for warning messagesconsole.error
for error messages
Error Handling and Logging Philosophy
- We care deeply about error handling and logging
- Key principles for error management:
- Catch errors effectively and thoughtfully
- Consider the state errors put the system into
- Implement comprehensive unit tests for error scenarios
- Log errors strategically for effective debugging
- Avoid over-logging while ensuring sufficient context for troubleshooting
Hono API Development Guidelines
API Structure and Organization
- Version-based organization - APIs are organized under
/api/v2/
directory - Feature-based folders - Each feature gets its own folder (e.g.,
chats/
,security/
) - Separate handler files - Each endpoint handler must be in its own file
- Functional handlers - All handlers should be pure functions that accept request data and return response data
Request/Response Type Safety
- Use shared types - All request and response types must be defined in
@buster/server-shared
- Zod schemas - Define schemas in server-shared and export both the schema and inferred types
- zValidator middleware - Always use
zValidator
from@hono/zod-validator
for request validation - Type imports - Import types from server-shared packages for consistency
Handler Pattern
// Handler file (e.g., get-workspace-settings.ts)
import type { GetWorkspaceSettingsResponse } from '@buster/server-shared/security';
import type { User } from '@buster/database';
export async function getWorkspaceSettingsHandler(
user: User
): Promise<GetWorkspaceSettingsResponse> {
// Implementation
}
// Route definition (index.ts)
.get('/workspace-settings', async (c) => {
const user = c.get('busterUser');
const response = await getWorkspaceSettingsHandler(user);
return c.json(response);
})
Authentication and User Context
- Use requireAuth middleware - Apply to all protected routes
- Extract user context - Use
c.get('busterUser')
to get the authenticated user - Type as User - Import
User
type from@buster/database
for handler parameters
Database Operations
Query Organization
- All database queries must be created as helper functions in
@packages/database/src/queries/
- Organize by table - Each table should have its own subdirectory (e.g.,
assets/
,chats/
,users/
) - Type all queries - Every query function must have properly typed parameters and return types
- Export from index - Each subdirectory should have an
index.ts
that exports all queries for that table - Reusable and composable - Write queries as small, focused functions that can be composed together
Soft Delete and Upsert Practices
- In our database, we never hard delete, we always use soft deletes with the
deleted_at
field - For update operations, we should almost always perform an upsert unless otherwise specified
Test Running Guidelines
- When running tests, use the following Turbo commands:
turbo test:unit
for unit teststurbo test:integration
for integration teststurbo test
for running all tests
Pre-Completion Workflow
- Always run
turbo test:unit, lint, and build:dry-run
before making any pull request or finishing a feature, bugfix, etc. to ensure things make it through CI/CD - You can run all these checks simultaneously with
turbo build:dry-run lint test:unit