packages/core/src/**/*.sql.ts.packages/core and are applied by core.bun dev from packages/opencode starts the live interactive TUI. Do not run it as a blocking foreground command when you need to inspect the result.tmux instead: tmux new-session -d -s opencode-dev 'bun dev'.tmux capture-pane -pt opencode-dev.tmux kill-session -t opencode-dev.Do not use export namespace Foo { ... } for module organization. It is not
standard ESM, it prevents tree-shaking, and it breaks Node's native TypeScript
runner. Use flat top-level exports combined with a self-reexport at the bottom
of the file:
// src/foo/foo.ts
export interface Interface { ... }
export class Service extends Context.Service<Service, Interface>()("@opencode/Foo") {}
export const layer = Layer.effect(Service, ...)
export const defaultLayer = layer.pipe(...)
export * as Foo from "./foo"
Consumers import the namespace projection:
import { Foo } from "@/foo/foo"
yield * Foo.Service
Foo.layer
Foo.defaultLayer
Namespace-private helpers stay as non-exported top-level declarations in the
same file — they remain inaccessible to consumers (they are not projected by
export * as) but are usable by the file's own code.
index.tsIf the module is foo/index.ts (single-namespace directory), use "." for
the self-reexport source rather than "./index":
// src/foo/index.ts
export const thing = ...
export * as Foo from "."
For directories with several independent modules (e.g. src/session/,
src/config/), keep each sibling as its own file with its own self-reexport,
and do not add a barrel index.ts. Consumers import the specific sibling:
import { SessionRetry } from "@/session/retry"
import { SessionStatus } from "@/session/status"
Barrels in multi-sibling directories force every import through the barrel to evaluate every sibling, which defeats tree-shaking and slows module load.
Use these rules when writing or migrating Effect code.
See specs/effect/migration.md for the compact pattern reference and examples.
Effect.gen(function* () { ... }) for composition.Effect.fn("Domain.method") for named/traced effects and Effect.fnUntraced for internal helpers.Effect.fn / Effect.fnUntraced accept pipeable operators as extra arguments, so avoid unnecessary outer .pipe() wrappers.Effect.callback for callback-based APIs.Effect.void instead of Effect.succeed(undefined) or Effect.succeed(void 0).DateTime.nowAsDate over new Date(yield* Clock.currentTimeMillis) when you need a Date.src/config, follow the existing self-export pattern at the top of the file (for example export * as ConfigAgent from "./agent") when adding a new config module.Schema.Class for multi-field data.Schema.brand) for single-value types.Schema.TaggedErrorClass for typed errors.Schema.Defect instead of unknown for defect-like causes.Effect.gen / Effect.fn, prefer yield* new MyError(...) over yield* Effect.fail(new MyError(...)) for direct early-failure branches.makeRuntime (from src/effect/run-service.ts) for all services. It returns { runPromise, runFork, runCallback } backed by a shared memoMap that deduplicates layers.InstanceState (from src/effect/instance-state.ts) for per-directory or per-project state that needs per-instance cleanup. It uses ScopedCache keyed by directory — each open project gets its own state, automatically cleaned up on disposal.InstanceState.InstanceState.make closure — ScopedCache handles run-once semantics. Don't add fibers, ensure() callbacks, or started flags on top.Effect.addFinalizer or Effect.acquireRelease inside the InstanceState.make closure for cleanup (subscriptions, process teardown, etc.).Effect.forkScoped inside the closure for background stream consumers — the fiber is interrupted when the instance is disposed.init() non-blocking, fork InstanceState.get(state) at the init() call site (e.g. Effect.forkIn(scope)), not by forking work inside the InstanceState.make closure. Forking inside the closure leaves state incomplete for other methods that read it.src/project/bootstrap.ts already wraps every service init() in Effect.forkDetach, so init() is fire-and-forget in production. Keep init() methods synchronous internally; the caller controls concurrency.Effect.fork and Effect.forkDaemon do not exist. Use Effect.forkIn(scope) to fork a fiber into a specific scope.FileSystem.FileSystem instead of raw fs/promises for effectful file I/O.ChildProcessSpawner.ChildProcessSpawner with ChildProcess.make(...) instead of custom process wrappers.HttpClient.HttpClient instead of raw fetch.Path.Path, Config, Clock, and DateTime when those concerns are already inside Effect code.Effect.repeat or Effect.schedule with Effect.forkScoped in the layer definition.Use Effect.cached when multiple concurrent callers should share a single in-flight computation rather than storing Fiber | undefined or Promise | undefined manually. See specs/effect/migration.md for the full pattern.
Use EffectBridge for native or external callbacks (@parcel/watcher, node-pty, native fs.watch, plugin callbacks, etc.) that need to re-enter Effect services with instance/workspace context.
Plain async code should pass explicit context or stay inside an Effect fiber; do not add ambient instance context shims.