This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
- ALWAYS fix ALL errors, including pre-existing ones. Never dismiss an error as "pre-existing" or "not related to our changes". If you encounter it, you fix it.
- GPU acceleration is mandatory. Never suggest CPU-only as a solution.
- Android builds take 5+ minutes. Never compile without thorough code verification first.
- After 3 failed attempts on the same problem, STOP. Write the full diagnosis and propose 2-3 alternative approaches BEFORE coding anything.
- Before any fix, write in 2 lines: the root cause and why this approach solves it. If you can't, you don't understand the problem.
- Never use sed/regex on source code. Use str_replace with textual anchors or refactor cleanly.
- When a test fails: diagnose first (1 message), propose the plan (1 message), implement after. No trial-and-error loops.
- When measured performance is far from vendor specs (>3x gap), look for integration bugs FIRST (wrong parameter, wrong endpoint, wrong format) before optimizing infrastructure.
- Read the actual API documentation/source before building optimization layers on top.
- After any fix, grep the corrected pattern across the ENTIRE project to find other occurrences of the same bug. Never fix just the first occurrence found.
- Desktop build:
cd packages/desktop && bun tauri build - Desktop deploy: copy
packages/desktop/src-tauri/target/release/OpenCode.exetoC:/Users/barat/AppData/Local/OpenCode/OpenCode.exe - Android build:
cd packages/mobile && bun tauri android build --target aarch64(requiresORT_LIB_LOCATION=D:/tmp/ort-android) - Sidecar (required before desktop build):
cd packages/opencode && bun run build --single --baseline, then copy topackages/desktop/sidecars/opencode-cli-x86_64-pc-windows-msvc.exe - NEVER touch Antigravity (the IDE). NEVER kill processes that aren't ours.
Cible pour tout nouveau code TypeScript :
| Métrique | Cible | Alerte | Bloquant |
|---|---|---|---|
| LOC par fonction | ≤ 50 | > 100 | > 200 |
| LOC par fichier (packages/app/) | ≤ 500 | > 800 | > 1500 |
Technique : si une fonction dépasse 50 LOC, extraire via le pattern Factory with Deps (ADR-0001).
Exceptions documentées : coordinateurs (session.tsx ~1010 LOC, layout.tsx ~1127 LOC) — voir ADR-0002.
Avant toute extraction de module ou refactoring majeur :
- Rédiger un mini-ADR (2 phrases : contexte + décision) dans
docs/adr/ - Vérifier que l'extraction respecte Single Responsibility
- Identifier les dépendances circulaires potentielles avant de coder
Package manager: Bun 1.3.11 (exact lock enforced). Do not use npm/yarn/pnpm.
# Dev servers
bun run dev # TUI CLI dev mode (root)
bun run dev:desktop # Tauri desktop with hot reload
bun run dev:mobile-android # Android dev build
# Build
cd packages/opencode && bun run build --single --baseline # CLI sidecar
cd packages/desktop && bun tauri build # Desktop release
cd packages/mobile && bun tauri android build --target aarch64 # Android APK
# Type checking (run before any build)
bun run typecheck
# Testing — MUST run from the package directory, not root
cd packages/opencode && bun test --timeout 30000
cd packages/opencode && bun test --filter <name> --timeout 30000
cd packages/app && bun test --preload ./happydom.ts ./src
# Linting / formatting
bun run lint
bun run formatCritical: bun tauri build does NOT rebuild the TypeScript sidecar. Always run bun run build --single --baseline in packages/opencode first and copy the output manually.
packages/
├── opencode/ # Core TypeScript sidecar: agent engine, REST server, CLI, all providers
├── app/ # SolidJS frontend (shared by desktop, web, mobile WebView)
├── desktop/ # Tauri 2.0 desktop — Rust backend (TLS, speech, local LLM orchestration)
├── mobile/ # Tauri 2.0 Android — Rust + Kotlin (LlamaService JNI, on-device inference)
├── ui/ # Shared Kobalte + Tailwind components
├── sdk/js/ # Public TypeScript SDK (generated from OpenAPI spec)
├── console/ # Web dashboard (SolidJS Start + Cloudflare)
└── util/ # Shared Zod schemas and utilities
crates/
└── opencode-kokoro-shared/ # Rust: Kokoro TTS ONNX engine
SolidJS UI → POST /session/:id/stream (SSE, Hono server)
→ Session.send() → SessionProcessor → LLM.stream()
→ Provider resolution (cloud or local-llm pseudo-provider)
→ Vercel AI SDK streamText()
→ Cloud API OR llama-server:14097 (local, C++ GPU sidecar)
| Module | Role |
|---|---|
session/session.ts |
Session FSM, message storage, event bus |
session/processor.ts |
Tool call orchestration, doom-loop detection |
session/llm.ts |
LLM streaming, adaptive context limits |
session/compaction.ts |
Auto-pruning and summarization |
provider/provider.ts |
20+ cloud providers + local-llm pseudo-provider (65 KB) |
provider/transform.ts |
Normalizes provider options, prompt caching, error handling (39 KB) |
local-llm-server/index.ts |
llama-server lifecycle: single-flight lock, health poll, model swap |
mcp/ |
Model Context Protocol, OAuth provider framework |
storage/ |
Drizzle ORM (SQLite), auth tokens, config cascade |
server.ts |
Hono REST + SSE server |
tls.rs— self-signed cert generation (rcgen, 10-year, SHA-256)server.rs— RemoteConfig (UUID + password), TLS togglespeech.rs— Parakeet STT + Kokoro TTS (ONNX) + Pocket voice clone sidecarllm.rs— local model Tauri commands bridging tolocal-llm-server
lib.rs— Tauri mobile entry, logcat logging (tag:OpenCode)llm.rs—load_llm_model,set_llm_config,get_memory_info,llm_idle_tickruntime.rs— Alpine rootfs setup, toolchain wrappers (Rust/Python/etc.), embedded sidecar envproxy.rs— LAN port proxy (atomic port allocation)- Java:
LlamaService.kt— foreground service owning llama-server process (API 34+)
SolidJS 1.9.10 + Tailwind 4. Entry: entry.tsx. Key dirs: pages/, components/. Uses SolidJS stores + localStorage, event bus via solid-primitives/event-bus.
ensureRunning(modelID) in local-llm-server/index.ts is the single entry point. It:
- Acquires
start.lock(O_EXCL) to prevent concurrent spawns - Writes
owner.pidJSON atomically - Polls
/healthup to 120 s - Validates loaded model matches requested; kills and respawns if not
- Tracks subscribers via
refs/{pid}.reffiles; prunes stale refs on startup
Android only: llama-server is owned by LlamaService (Kotlin JNI), not spawned by the sidecar. Gate all llama-server spawn logic with process.env.OPENCODE_CLIENT === "mobile-embedded".
~/.opencode/config.json → ./opencode.json → MDM profile (macOS) → environment variables
connect-src must whitelist http://ipc.localhost (Tauri IPC) and http://asset.localhost (static assets). Missing entries cause silent IPC failures.
- typecheck: bun turbo typecheck
- lint: bunx biome check .
- test: cd packages/opencode && bun test --timeout 30000
- deadcode: bunx knip --no-progress
- shell: shellcheck scripts/*.sh
- rust: cargo check --manifest-path packages/desktop/src-tauri/Cargo.toml