Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/opencode/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ See `specs/effect-migration.md` for the compact pattern reference and examples.
- 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.

## Instance.bind — ALS for native callbacks

`Instance.bind(fn)` captures the current Instance AsyncLocalStorage context and restores it synchronously when called.
Expand Down
27 changes: 26 additions & 1 deletion packages/opencode/specs/effect-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,31 @@ yield *

The key insight: don't split init into a separate method with a `started` flag. Put everything in the `InstanceState.make` closure and let `ScopedCache` handle the run-once semantics.

## Effect.cached for deduplication

Use `Effect.cached` when multiple concurrent callers should share a single in-flight computation. It memoizes the result and deduplicates concurrent fibers — second caller joins the first caller's fiber instead of starting a new one.

```ts
// Inside the layer — yield* to initialize the memo
let cached = yield* Effect.cached(loadExpensive())

const get = Effect.fn("Foo.get")(function* () {
return yield* cached // concurrent callers share the same fiber
})

// To invalidate: swap in a fresh memo
const invalidate = Effect.fn("Foo.invalidate")(function* () {
cached = yield* Effect.cached(loadExpensive())
})
```

Prefer `Effect.cached` over these patterns:
- Storing a `Fiber.Fiber | undefined` with manual check-and-fork (e.g. `file/index.ts` `ensure`)
- Storing a `Promise<void>` task for deduplication (e.g. `skill/index.ts` `ensure`)
- `let cached: X | undefined` with check-and-load (races when two callers see `undefined` before either resolves)

`Effect.cached` handles the run-once + concurrent-join semantics automatically. For invalidatable caches, reassign with `yield* Effect.cached(...)` — the old memo is discarded.

## Scheduled Tasks

For loops or periodic work, use `Effect.repeat` or `Effect.schedule` with `Effect.forkScoped` in the layer definition.
Expand Down Expand Up @@ -179,7 +204,7 @@ Still open and likely worth migrating:
- [x] `Worktree`
- [x] `Bus`
- [x] `Command`
- [ ] `Config`
- [x] `Config`
- [ ] `Session`
- [ ] `SessionProcessor`
- [ ] `SessionPrompt`
Expand Down
3 changes: 1 addition & 2 deletions packages/opencode/src/cli/cmd/tui/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,7 @@ export const rpc = {
})
},
async reload() {
Config.global.reset()
await Instance.disposeAll()
await Config.invalidate(true)
},
async setWorkspace(input: { workspaceID?: string }) {
startEventStream({ directory: process.cwd(), workspaceID: input.workspaceID })
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/cli/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function withNetworkOptions<T>(yargs: Argv<T>) {
}

export async function resolveNetworkOptions(args: NetworkOptions) {
const config = await Config.global()
const config = await Config.getGlobal()
const portExplicitlySet = process.argv.includes("--port")
const hostnameExplicitlySet = process.argv.includes("--hostname")
const mdnsExplicitlySet = process.argv.includes("--mdns")
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/cli/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Flag } from "@/flag/flag"
import { Installation } from "@/installation"

export async function upgrade() {
const config = await Config.global()
const config = await Config.getGlobal()
const method = await Installation.method()
const latest = await Installation.latest(method).catch(() => {})
if (!latest) return
Expand Down
Loading
Loading