OpenCode local development uses Bun (≥1.3) as its package manager; start development with bun install && bun dev, which runs the CLI from packages/opencode against the current directory or a specified path. The dev setup comprises multiple entry points: bun dev for the CLI, bun dev serve for the headless API on port 4096, bun run --cwd packages/app dev for the web UI (port 5173), and bun run --cwd packages/desktop dev for the desktop app, plus debugging via bun run --inspect.
Local development requires Bun 1.3+; the root package.json pins the package manager to bun@1.3.14.[1][2] Install dependencies and start the dev server from the repo root with bun install && bun dev.[1] After every bun install, the postinstall script in package.json automatically runs fix-node-pty in packages/core.[2] The following dependencies are listed as trusted in package.json, meaning their install scripts are allowed to run: esbuild, node-pty, protobufjs, tree-sitter, tree-sitter-bash, tree-sitter-powershell, web-tree-sitter, and electron.[2]
The root dev script in package.json runs the opencode CLI as bun run --cwd packages/opencode --conditions=browser src/index.ts; the per-package equivalent in packages/opencode/package.json is bun run --conditions=browser ./src/index.ts.[2][3] During development, bun dev is the local equivalent of the built opencode command, exposing the same CLI interface including serve, web, and directory arguments.[1] By default, bun dev runs opencode in the packages/opencode directory; pass a directory path (e.g., bun dev .) to run it against a different location.[1] The --conditions=browser flag passed in the dev script instructs Bun to resolve package exports using the browser condition rather than node, targeting the correct runtime environment for OpenCode's bundling.
bun dev serve starts the headless API server on port 4096 by default; a different port can be specified with --port.[1] To test UI changes, first start the opencode server with bun dev serve, then run bun run --cwd packages/app dev; the web dev server starts at http://localhost:5173.[[1]](https://github.com/anomalyco/opencode/blob/dc4449df0d52199704ea4989a5a993ebbc605612/CONTRIBUTING.md) After changing the API or SDK (e.g., packages/opencode/src/server/server.ts), run ./script/generate.ts to regenerate the SDK and related files.[1]
The desktop app runs in development with bun run --cwd packages/desktop dev; production builds use the build then package subcommands in the same package.[1]
To build a standalone executable, run ./packages/opencode/script/build.ts --single (also invokable as bun run script/build.ts via the build script in packages/opencode/package.json), then execute ./packages/opencode/dist/opencode-<platform>/bin/opencode.[1][3]
The most reliable way to debug opencode is to run bun run --inspect=<url> dev ... in a terminal and attach the debugger via that URL; other methods such as VSCode launch configs or the JS Debug Terminal can cause breakpoints to be mapped incorrectly.[1] When debugging with breakpoints in server code while running the TUI, use bun dev spawn instead of bun dev; the default bun dev runs the server in a worker thread where breakpoints may not fire.[1] To debug the server and TUI separately, start the server with bun run --inspect=ws://localhost:6499/ --cwd packages/opencode ./src/index.ts serve --port 4096, then attach the TUI with opencode attach http://localhost:4096.[1]
The repo uses oxlint for linting, runnable via bun run lint.[2] Type-checking across the monorepo is run with bun turbo typecheck (via bun run typecheck); within packages/opencode, type-checking uses tsgo --noEmit (the TypeScript native-preview binary), not tsc.[2][3] Running bun test from the repo root is intentionally blocked — the root test script in package.json prints an error and exits with code 1.[2] The test command in packages/opencode/package.json is bun test --timeout 30000 --only-failures, setting a 30-second per-test timeout and printing only failing tests.[3] The HTTP API test suite (test:httpapi in packages/opencode/package.json) runs script/httpapi-exercise.ts three times in sequence — coverage mode, auth mode, and effect mode — each with --fail-on-missing and --fail-on-skip.[3]
Sources