From ab0042a6664ae2b873e06332701001432dc18608 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sat, 27 Jun 2026 01:16:30 -0400 Subject: [PATCH] docs(opencode): mark package as v1 --- packages/opencode/AGENTS.md | 132 +----------------------------------- 1 file changed, 3 insertions(+), 129 deletions(-) diff --git a/packages/opencode/AGENTS.md b/packages/opencode/AGENTS.md index f07170c585..7e7052bd07 100644 --- a/packages/opencode/AGENTS.md +++ b/packages/opencode/AGENTS.md @@ -1,131 +1,5 @@ -# opencode database guide +# opencode package guidance -## Database +`packages/opencode` is the V1 version of this project. -- **Schema**: Drizzle schema lives in `packages/core/src/**/*.sql.ts`. -- **Migrations**: database migrations live in `packages/core` and are applied by core. - -## Development server - -- Running `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. -- Start it in `tmux` instead: `tmux new-session -d -s opencode-dev 'bun dev'`. -- Capture the current TUI output with: `tmux capture-pane -pt opencode-dev`. -- Stop the session explicitly when done: `tmux kill-session -t opencode-dev`. - -# Module shape - -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: - -```ts -// src/foo/foo.ts -export interface Interface { ... } -export class Service extends Context.Service()("@opencode/Foo") {} -export const layer = Layer.effect(Service, ...) -export const defaultLayer = layer.pipe(...) - -export * as Foo from "./foo" -``` - -Consumers import the namespace projection: - -```ts -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. - -## When the file is an `index.ts` - -If the module is `foo/index.ts` (single-namespace directory), use `"."` for -the self-reexport source rather than `"./index"`: - -```ts -// src/foo/index.ts -export const thing = ... - -export * as Foo from "." -``` - -## Multi-sibling directories - -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: - -```ts -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. - -# opencode Effect rules - -Use these rules when writing or migrating Effect code. - -See `specs/effect/migration.md` for the compact pattern reference and examples. - -## Core - -- Use `Effect.gen(function* () { ... })` for composition. -- Use `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. -- Use `Effect.callback` for callback-based APIs. -- Use `Effect.void` instead of `Effect.succeed(undefined)` or `Effect.succeed(void 0)`. -- Prefer `DateTime.nowAsDate` over `new Date(yield* Clock.currentTimeMillis)` when you need a `Date`. - -## Module conventions - -- In `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. - -## Schemas and errors - -- Use `Schema.Class` for multi-field data. -- Use branded schemas (`Schema.brand`) for single-value types. -- Use `Schema.TaggedErrorClass` for typed errors. -- Use `Schema.Defect` instead of `unknown` for defect-like causes. -- In `Effect.gen` / `Effect.fn`, prefer `yield* new MyError(...)` over `yield* Effect.fail(new MyError(...))` for direct early-failure branches. - -## Runtime vs InstanceState - -- Use `makeRuntime` (from `src/effect/run-service.ts`) for all services. It returns `{ runPromise, runFork, runCallback }` backed by a shared `memoMap` that deduplicates layers. -- Use `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. -- If two open directories should not share one copy of the service, it needs `InstanceState`. -- Do the work directly in the `InstanceState.make` closure — `ScopedCache` handles run-once semantics. Don't add fibers, `ensure()` callbacks, or `started` flags on top. -- Use `Effect.addFinalizer` or `Effect.acquireRelease` inside the `InstanceState.make` closure for cleanup (subscriptions, process teardown, etc.). -- Use `Effect.forkScoped` inside the closure for background stream consumers — the fiber is interrupted when the instance is disposed. -- To make a service's `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 v4 beta API - -- `Effect.fork` and `Effect.forkDaemon` do not exist. Use `Effect.forkIn(scope)` to fork a fiber into a specific scope. - -## Preferred Effect services - -- In effectified services, prefer yielding existing Effect services over dropping down to ad hoc platform APIs. -- Prefer `FileSystem.FileSystem` instead of raw `fs/promises` for effectful file I/O. -- Prefer `ChildProcessSpawner.ChildProcessSpawner` with `ChildProcess.make(...)` instead of custom process wrappers. -- Prefer `HttpClient.HttpClient` instead of raw `fetch`. -- Prefer `Path.Path`, `Config`, `Clock`, and `DateTime` when those concerns are already inside Effect code. -- For background loops or scheduled tasks, use `Effect.repeat` or `Effect.schedule` with `Effect.forkScoped` in the layer definition. - -## Effect.cached for deduplication - -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. - -## Callback boundaries - -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. +We are moving to V2, which is split across the `core`, `tui`, and `cli` packages. It is okay to read code in this package to understand how V1 worked, but do not make changes here unless explicitly instructed.