Contributing

Thanks for your interest in contributing to Optio! This guide covers the development setup, project structure, workflow, and conventions.

Development Setup

Prerequisites

  • Node.js 22+
  • pnpm 10+ (npm install -g pnpm)
  • Docker Desktop with Kubernetes enabled
  • Helm 3+ and kubectl

Quick Start

terminal
git clone https://github.com/jonwiggins/optio.git
cd optio
pnpm install

# Start Kubernetes infrastructure
./scripts/setup-local.sh

# Start dev servers (API + Web) with hot reload
pnpm dev

The API runs on http://localhost:4000 and the web UI on http://localhost:3000.

Building the Agent Image

terminal
docker build -t optio-agent:latest -f Dockerfile.agent .

# Load into K8s containerd (Docker Desktop)
docker save optio-agent:latest | \
  docker exec -i desktop-control-plane \
  ctr -n k8s.io image import --digests -

Project Structure

apps/api/       Fastify API server + BullMQ workers
apps/web/       Next.js web UI
packages/       Shared libraries (types, runtime, adapters, providers)
helm/           Production Helm charts
images/         Agent container Dockerfiles
k8s/            Local dev K8s manifests

Key Directories

PathContents
apps/api/src/routes/API route handlers (tasks, repos, secrets, auth, etc.)
apps/api/src/services/Business logic (task-service, repo-pool, review, etc.)
apps/api/src/workers/BullMQ workers (task, PR watcher, cleanup, sync)
apps/api/src/db/Drizzle schema (~26 tables) and migrations (~28)
apps/web/src/app/Next.js App Router pages
apps/web/src/components/React components (task-card, log-viewer, etc.)
packages/shared/Types, state machine, prompt renderer, error classifier
packages/container-runtime/Kubernetes container runtime interface
packages/agent-adapters/Claude Code and Codex agent adapters
packages/ticket-providers/GitHub Issues and Linear ticket providers

Commands

terminal
# Development
pnpm dev              # Start API + Web with hot reload
pnpm dev:api          # Fastify API on :4000
pnpm dev:web          # Next.js on :3100

# Quality checks (same as CI + pre-commit hooks)
pnpm format:check     # Check formatting (Prettier)
pnpm format           # Auto-fix formatting
pnpm lint             # Lint with ESLint
pnpm turbo typecheck  # Typecheck all 6 packages
pnpm turbo test       # Run tests (Vitest)

# Build
cd apps/web && npx next build    # Verify production web build
./images/build.sh                # Build all agent image presets

# Database
cd apps/api && npx drizzle-kit generate  # Generate migration
cd apps/api && npx drizzle-kit migrate   # Apply migration

# Helm
helm lint helm/optio --set encryption.key=test
helm upgrade optio helm/optio -n optio --reuse-values

Development Workflow

Database Changes

The database schema is defined with Drizzle ORM. After editing the schema, generate and apply a migration:

terminal
# 1. Edit the schema
#    apps/api/src/db/schema.ts

# 2. Generate a migration
cd apps/api && npx drizzle-kit generate

# 3. Apply (migrations also auto-run on API startup)
cd apps/api && npx drizzle-kit migrate

Adding a New API Route

  1. Create the route handler in apps/api/src/routes/
  2. Register it in apps/api/src/server.ts
  3. Add the API client method in apps/web/src/lib/api-client.ts

Conventions

Code Style

  • TypeScript with strict mode everywhere
  • ESM modules — use .js extensions in imports (TypeScript resolves them to .ts)
  • Prettier for formatting (runs on commit via Husky)
  • Tailwind CSS v4 @import "tailwindcss" + @theme block in CSS, no tailwind.config file
  • Zustand for client state — use useStore.getState() in callbacks/effects, not hook selectors
  • Zod for API request validation
  • Drizzle ORM for database access

Important Patterns

  • State transitions — always go through taskService.transitionTask() which validates, updates DB, records an event, and publishes to WebSocket
  • Secrets — never log or return secret values. Only names and scopes are exposed via API. Encrypted at rest with AES-256-GCM
  • Cost tracking — stored as string (costUsd) to avoid floating-point precision issues
  • Error handling — use the error classifier (@optio/shared) for user-facing messages, raw errors in logs
  • WebSocket events — published to Redis pub/sub channels, relayed to browser clients
  • Next.js webpack config extensionAlias in next.config.ts resolves .js to .ts for workspace packages

Commit Conventions

Optio uses Conventional Commits, enforced by commitlint via a Husky commit-msg hook.

PrefixUsage
feat:A new feature
fix:A bug fix
docs:Documentation changes
style:Formatting, no code change
refactor:Code change that neither fixes nor adds
perf:Performance improvement
test:Add or update tests
build:Build system or dependencies
ci:CI configuration
chore:Maintenance

Pre-Commit Hooks

Husky runs the following checks before each commit, mirroring CI:

  1. lint-staged — runs ESLint + Prettier on staged files
  2. pnpm format:check — verifies formatting across the entire project
  3. pnpm turbo typecheck — typechecks all packages

Tip

Run pnpm format to auto-fix formatting issues before committing. This saves time when the pre-commit hook catches formatting problems.

Pull Requests

  1. Fork the repo and create a feature branch
  2. Make your changes with tests
  3. Ensure pnpm turbo typecheck and pnpm turbo test pass
  4. Submit a PR using the template
  5. Wait for CI to pass and a maintainer review

CI Checks

GitHub Actions runs the following on every PR:

  • Format check (Prettier)
  • Typecheck all packages
  • Run tests (Vitest)
  • Build web app (Next.js production build)
  • Build agent image

License

Optio is MIT licensed. See the LICENSE file for details.

Next Steps