summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-15 23:15:58 -0400
committerGitHub <[email protected]>2026-04-15 23:15:58 -0400
commit343a564183d3c1aa3fc4f46896c2350bda2d2058 (patch)
tree73699a01611cb9379be96634dca218d8896c3fca /packages
parentb0eae5e12f271ee53e6ac269c12c8eeee7f65496 (diff)
downloadopencode-343a564183d3c1aa3fc4f46896c2350bda2d2058.tar.gz
opencode-343a564183d3c1aa3fc4f46896c2350bda2d2058.zip
feat: unwrap 11 util namespaces to flat exports + barrel (#22739)
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/acp/agent.ts4
-rw-r--r--packages/opencode/src/acp/session.ts2
-rw-r--r--packages/opencode/src/bus/bus.ts2
-rw-r--r--packages/opencode/src/cli/cmd/acp.ts2
-rw-r--r--packages/opencode/src/cli/cmd/agent.ts2
-rw-r--r--packages/opencode/src/cli/cmd/debug/lsp.ts2
-rw-r--r--packages/opencode/src/cli/cmd/debug/scrap.ts2
-rw-r--r--packages/opencode/src/cli/cmd/github.ts4
-rw-r--r--packages/opencode/src/cli/cmd/import.ts2
-rw-r--r--packages/opencode/src/cli/cmd/mcp.ts2
-rw-r--r--packages/opencode/src/cli/cmd/plug.ts4
-rw-r--r--packages/opencode/src/cli/cmd/pr.ts2
-rw-r--r--packages/opencode/src/cli/cmd/providers.ts2
-rw-r--r--packages/opencode/src/cli/cmd/run.ts4
-rw-r--r--packages/opencode/src/cli/cmd/session.ts6
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx4
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/dialog-stash.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/frecency.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx4
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/prompt/stash.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/component/textarea-keybindings.ts2
-rw-r--r--packages/opencode/src/cli/cmd/tui/context/keybind.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/context/kv.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/context/local.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/context/sync.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/context/theme.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/feature-plugins/system/plugins.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/plugin/runtime.ts6
-rw-r--r--packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/routes/session/index.tsx4
-rw-r--r--packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx4
-rw-r--r--packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/thread.ts6
-rw-r--r--packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx2
-rw-r--r--packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx4
-rw-r--r--packages/opencode/src/cli/cmd/tui/util/clipboard.ts4
-rw-r--r--packages/opencode/src/cli/cmd/tui/util/editor.ts4
-rw-r--r--packages/opencode/src/cli/cmd/tui/util/sound.ts2
-rw-r--r--packages/opencode/src/cli/cmd/tui/util/transcript.ts2
-rw-r--r--packages/opencode/src/cli/cmd/tui/worker.ts4
-rw-r--r--packages/opencode/src/cli/cmd/uninstall.ts4
-rw-r--r--packages/opencode/src/cli/heap.ts2
-rw-r--r--packages/opencode/src/config/config.ts4
-rw-r--r--packages/opencode/src/config/markdown.ts2
-rw-r--r--packages/opencode/src/config/paths.ts2
-rw-r--r--packages/opencode/src/config/tui-migrate.ts4
-rw-r--r--packages/opencode/src/config/tui.ts2
-rw-r--r--packages/opencode/src/control-plane/workspace-context.ts2
-rw-r--r--packages/opencode/src/control-plane/workspace.ts4
-rw-r--r--packages/opencode/src/effect/bridge.ts2
-rw-r--r--packages/opencode/src/effect/instance-state.ts2
-rw-r--r--packages/opencode/src/effect/logger.ts2
-rw-r--r--packages/opencode/src/effect/run-service.ts2
-rw-r--r--packages/opencode/src/file/file.ts2
-rw-r--r--packages/opencode/src/file/ripgrep.ts4
-rw-r--r--packages/opencode/src/file/time.ts2
-rw-r--r--packages/opencode/src/file/watcher.ts2
-rw-r--r--packages/opencode/src/format/format.ts2
-rw-r--r--packages/opencode/src/format/formatter.ts4
-rw-r--r--packages/opencode/src/global/global.ts2
-rw-r--r--packages/opencode/src/ide/ide.ts4
-rw-r--r--packages/opencode/src/index.ts4
-rw-r--r--packages/opencode/src/installation/installation.ts2
-rw-r--r--packages/opencode/src/lsp/client.ts6
-rw-r--r--packages/opencode/src/lsp/index.ts4
-rw-r--r--packages/opencode/src/lsp/launch.ts2
-rw-r--r--packages/opencode/src/lsp/server.ts6
-rw-r--r--packages/opencode/src/mcp/mcp.ts2
-rw-r--r--packages/opencode/src/mcp/oauth-callback.ts2
-rw-r--r--packages/opencode/src/mcp/oauth-provider.ts2
-rw-r--r--packages/opencode/src/node.ts2
-rw-r--r--packages/opencode/src/npm/npm.ts4
-rw-r--r--packages/opencode/src/patch/patch.ts2
-rw-r--r--packages/opencode/src/permission/evaluate.ts2
-rw-r--r--packages/opencode/src/permission/permission.ts4
-rw-r--r--packages/opencode/src/plugin/codex.ts2
-rw-r--r--packages/opencode/src/plugin/github-copilot/copilot.ts2
-rw-r--r--packages/opencode/src/plugin/install.ts2
-rw-r--r--packages/opencode/src/plugin/meta.ts2
-rw-r--r--packages/opencode/src/plugin/plugin.ts2
-rw-r--r--packages/opencode/src/plugin/shared.ts2
-rw-r--r--packages/opencode/src/project/bootstrap.ts2
-rw-r--r--packages/opencode/src/project/instance.ts4
-rw-r--r--packages/opencode/src/project/project.ts2
-rw-r--r--packages/opencode/src/project/vcs.ts2
-rw-r--r--packages/opencode/src/provider/models.ts4
-rw-r--r--packages/opencode/src/provider/provider.ts2
-rw-r--r--packages/opencode/src/pty/service.ts2
-rw-r--r--packages/opencode/src/question/index.ts2
-rw-r--r--packages/opencode/src/server/control/index.ts2
-rw-r--r--packages/opencode/src/server/fence.ts2
-rw-r--r--packages/opencode/src/server/instance/event.ts2
-rw-r--r--packages/opencode/src/server/instance/global.ts2
-rw-r--r--packages/opencode/src/server/instance/httpapi/server.ts2
-rw-r--r--packages/opencode/src/server/instance/middleware.ts2
-rw-r--r--packages/opencode/src/server/instance/session.ts2
-rw-r--r--packages/opencode/src/server/instance/sync.ts2
-rw-r--r--packages/opencode/src/server/instance/workspace.ts2
-rw-r--r--packages/opencode/src/server/mdns.ts2
-rw-r--r--packages/opencode/src/server/middleware.ts2
-rw-r--r--packages/opencode/src/server/proxy.ts2
-rw-r--r--packages/opencode/src/server/server.ts2
-rw-r--r--packages/opencode/src/session/compaction.ts4
-rw-r--r--packages/opencode/src/session/instruction.ts2
-rw-r--r--packages/opencode/src/session/llm.ts4
-rw-r--r--packages/opencode/src/session/processor.ts2
-rw-r--r--packages/opencode/src/session/projectors.ts2
-rw-r--r--packages/opencode/src/session/prompt.ts4
-rw-r--r--packages/opencode/src/session/revert.ts2
-rw-r--r--packages/opencode/src/session/session.ts2
-rw-r--r--packages/opencode/src/share/share-next.ts2
-rw-r--r--packages/opencode/src/shell/shell.ts2
-rw-r--r--packages/opencode/src/skill/discovery.ts2
-rw-r--r--packages/opencode/src/skill/skill.ts2
-rw-r--r--packages/opencode/src/snapshot/snapshot.ts2
-rw-r--r--packages/opencode/src/storage/db.ts4
-rw-r--r--packages/opencode/src/storage/json-migration.ts4
-rw-r--r--packages/opencode/src/storage/storage.ts2
-rw-r--r--packages/opencode/src/tool/bash.ts2
-rw-r--r--packages/opencode/src/tool/registry.ts2
-rw-r--r--packages/opencode/src/tool/truncate.ts2
-rw-r--r--packages/opencode/src/util/archive.ts2
-rw-r--r--packages/opencode/src/util/color.ts30
-rw-r--r--packages/opencode/src/util/filesystem.ts388
-rw-r--r--packages/opencode/src/util/index.ts11
-rw-r--r--packages/opencode/src/util/keybind.ts170
-rw-r--r--packages/opencode/src/util/local-context.ts36
-rw-r--r--packages/opencode/src/util/locale.ts128
-rw-r--r--packages/opencode/src/util/lock.ts142
-rw-r--r--packages/opencode/src/util/log.ts314
-rw-r--r--packages/opencode/src/util/process.ts282
-rw-r--r--packages/opencode/src/util/rpc.ts108
-rw-r--r--packages/opencode/src/util/token.ts8
-rw-r--r--packages/opencode/src/util/wildcard.ts88
-rw-r--r--packages/opencode/src/worktree/worktree.ts2
-rw-r--r--packages/opencode/test/cli/tui/plugin-loader.test.ts2
-rw-r--r--packages/opencode/test/cli/tui/thread.test.ts2
-rw-r--r--packages/opencode/test/config/agent-color.test.ts2
-rw-r--r--packages/opencode/test/config/config.test.ts2
-rw-r--r--packages/opencode/test/config/tui.test.ts2
-rw-r--r--packages/opencode/test/file/index.test.ts2
-rw-r--r--packages/opencode/test/file/path-traversal.test.ts2
-rw-r--r--packages/opencode/test/file/time.test.ts2
-rw-r--r--packages/opencode/test/fixture/plug-worker.ts2
-rw-r--r--packages/opencode/test/keybind.test.ts2
-rw-r--r--packages/opencode/test/lsp/client.test.ts2
-rw-r--r--packages/opencode/test/plugin/install-concurrency.test.ts4
-rw-r--r--packages/opencode/test/plugin/install.test.ts2
-rw-r--r--packages/opencode/test/plugin/loader-shared.test.ts2
-rw-r--r--packages/opencode/test/plugin/meta.test.ts4
-rw-r--r--packages/opencode/test/preload.ts2
-rw-r--r--packages/opencode/test/project/migrate-global.test.ts2
-rw-r--r--packages/opencode/test/project/project.test.ts2
-rw-r--r--packages/opencode/test/provider/amazon-bedrock.test.ts2
-rw-r--r--packages/opencode/test/provider/provider.test.ts2
-rw-r--r--packages/opencode/test/server/global-session-list.test.ts2
-rw-r--r--packages/opencode/test/server/project-init-git.test.ts4
-rw-r--r--packages/opencode/test/server/session-actions.test.ts2
-rw-r--r--packages/opencode/test/server/session-list.test.ts2
-rw-r--r--packages/opencode/test/server/session-messages.test.ts2
-rw-r--r--packages/opencode/test/server/session-select.test.ts2
-rw-r--r--packages/opencode/test/session/compaction.test.ts4
-rw-r--r--packages/opencode/test/session/llm.test.ts2
-rw-r--r--packages/opencode/test/session/messages-pagination.test.ts2
-rw-r--r--packages/opencode/test/session/processor-effect.test.ts2
-rw-r--r--packages/opencode/test/session/prompt-effect.test.ts2
-rw-r--r--packages/opencode/test/session/prompt.test.ts2
-rw-r--r--packages/opencode/test/session/revert-compact.test.ts2
-rw-r--r--packages/opencode/test/session/session.test.ts2
-rw-r--r--packages/opencode/test/session/snapshot-tool-race.test.ts2
-rw-r--r--packages/opencode/test/session/structured-output-integration.test.ts2
-rw-r--r--packages/opencode/test/shell/shell.test.ts2
-rw-r--r--packages/opencode/test/skill/discovery.test.ts2
-rw-r--r--packages/opencode/test/snapshot/snapshot.test.ts2
-rw-r--r--packages/opencode/test/tool/bash.test.ts2
-rw-r--r--packages/opencode/test/tool/external-directory.test.ts2
-rw-r--r--packages/opencode/test/tool/read.test.ts2
-rw-r--r--packages/opencode/test/tool/truncation.test.ts4
-rw-r--r--packages/opencode/test/util/filesystem.test.ts2
-rw-r--r--packages/opencode/test/util/lock.test.ts2
-rw-r--r--packages/opencode/test/util/log.test.ts2
-rw-r--r--packages/opencode/test/util/module.test.ts2
-rw-r--r--packages/opencode/test/util/process.test.ts2
-rw-r--r--packages/opencode/test/util/wildcard.test.ts2
188 files changed, 1068 insertions, 1079 deletions
diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts
index 5f0bcdc24..669462772 100644
--- a/packages/opencode/src/acp/agent.ts
+++ b/packages/opencode/src/acp/agent.ts
@@ -31,9 +31,9 @@ import {
type Usage,
} from "@agentclientprotocol/sdk"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { pathToFileURL } from "url"
-import { Filesystem } from "../util/filesystem"
+import { Filesystem } from "../util"
import { Hash } from "@opencode-ai/shared/util/hash"
import { ACPSessionManager } from "./session"
import type { ACPConfig } from "./types"
diff --git a/packages/opencode/src/acp/session.ts b/packages/opencode/src/acp/session.ts
index b96ebc1c8..523b03737 100644
--- a/packages/opencode/src/acp/session.ts
+++ b/packages/opencode/src/acp/session.ts
@@ -1,6 +1,6 @@
import { RequestError, type McpServer } from "@agentclientprotocol/sdk"
import type { ACPSessionState } from "./types"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import type { OpencodeClient } from "@opencode-ai/sdk/v2"
const log = Log.create({ service: "acp-session-manager" })
diff --git a/packages/opencode/src/bus/bus.ts b/packages/opencode/src/bus/bus.ts
index 12d7f246c..beac80992 100644
--- a/packages/opencode/src/bus/bus.ts
+++ b/packages/opencode/src/bus/bus.ts
@@ -1,7 +1,7 @@
import z from "zod"
import { Effect, Exit, Layer, PubSub, Scope, Context, Stream } from "effect"
import { EffectBridge } from "@/effect"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { BusEvent } from "./bus-event"
import { GlobalBus } from "./global"
import { InstanceState } from "@/effect"
diff --git a/packages/opencode/src/cli/cmd/acp.ts b/packages/opencode/src/cli/cmd/acp.ts
index 2fb9038b0..8141adc4f 100644
--- a/packages/opencode/src/cli/cmd/acp.ts
+++ b/packages/opencode/src/cli/cmd/acp.ts
@@ -1,4 +1,4 @@
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { bootstrap } from "../bootstrap"
import { cmd } from "./cmd"
import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk"
diff --git a/packages/opencode/src/cli/cmd/agent.ts b/packages/opencode/src/cli/cmd/agent.ts
index 0e93946a2..fd559935f 100644
--- a/packages/opencode/src/cli/cmd/agent.ts
+++ b/packages/opencode/src/cli/cmd/agent.ts
@@ -7,7 +7,7 @@ import { Agent } from "../../agent/agent"
import { Provider } from "../../provider"
import path from "path"
import fs from "fs/promises"
-import { Filesystem } from "../../util/filesystem"
+import { Filesystem } from "../../util"
import matter from "gray-matter"
import { Instance } from "../../project/instance"
import { EOL } from "os"
diff --git a/packages/opencode/src/cli/cmd/debug/lsp.ts b/packages/opencode/src/cli/cmd/debug/lsp.ts
index 18f67b391..185cab9c7 100644
--- a/packages/opencode/src/cli/cmd/debug/lsp.ts
+++ b/packages/opencode/src/cli/cmd/debug/lsp.ts
@@ -3,7 +3,7 @@ import { AppRuntime } from "../../../effect/app-runtime"
import { Effect } from "effect"
import { bootstrap } from "../../bootstrap"
import { cmd } from "../cmd"
-import { Log } from "../../../util/log"
+import { Log } from "../../../util"
import { EOL } from "os"
export const LSPCommand = cmd({
diff --git a/packages/opencode/src/cli/cmd/debug/scrap.ts b/packages/opencode/src/cli/cmd/debug/scrap.ts
index f4b96e883..464b165d7 100644
--- a/packages/opencode/src/cli/cmd/debug/scrap.ts
+++ b/packages/opencode/src/cli/cmd/debug/scrap.ts
@@ -1,6 +1,6 @@
import { EOL } from "os"
import { Project } from "../../../project/project"
-import { Log } from "../../../util/log"
+import { Log } from "../../../util"
import { cmd } from "../cmd"
export const ScrapCommand = cmd({
diff --git a/packages/opencode/src/cli/cmd/github.ts b/packages/opencode/src/cli/cmd/github.ts
index 46d091642..d7863c548 100644
--- a/packages/opencode/src/cli/cmd/github.ts
+++ b/packages/opencode/src/cli/cmd/github.ts
@@ -1,6 +1,6 @@
import path from "path"
import { exec } from "child_process"
-import { Filesystem } from "../../util/filesystem"
+import { Filesystem } from "../../util"
import * as prompts from "@clack/prompts"
import { map, pipe, sortBy, values } from "remeda"
import { Octokit } from "@octokit/rest"
@@ -32,7 +32,7 @@ import { SessionPrompt } from "@/session/prompt"
import { AppRuntime } from "@/effect/app-runtime"
import { Git } from "@/git"
import { setTimeout as sleep } from "node:timers/promises"
-import { Process } from "@/util/process"
+import { Process } from "@/util"
import { Effect } from "effect"
type GitHubAuthor = {
diff --git a/packages/opencode/src/cli/cmd/import.ts b/packages/opencode/src/cli/cmd/import.ts
index 1232f0742..bb8a1f63f 100644
--- a/packages/opencode/src/cli/cmd/import.ts
+++ b/packages/opencode/src/cli/cmd/import.ts
@@ -9,7 +9,7 @@ import { SessionTable, MessageTable, PartTable } from "../../session/session.sql
import { Instance } from "../../project/instance"
import { ShareNext } from "../../share/share-next"
import { EOL } from "os"
-import { Filesystem } from "../../util/filesystem"
+import { Filesystem } from "../../util"
import { AppRuntime } from "@/effect/app-runtime"
/** Discriminated union returned by the ShareNext API (GET /api/shares/:id/data) */
diff --git a/packages/opencode/src/cli/cmd/mcp.ts b/packages/opencode/src/cli/cmd/mcp.ts
index b9e4b0421..06c03d9f4 100644
--- a/packages/opencode/src/cli/cmd/mcp.ts
+++ b/packages/opencode/src/cli/cmd/mcp.ts
@@ -13,7 +13,7 @@ import { Installation } from "../../installation"
import path from "path"
import { Global } from "../../global"
import { modify, applyEdits } from "jsonc-parser"
-import { Filesystem } from "../../util/filesystem"
+import { Filesystem } from "../../util"
import { Bus } from "../../bus"
import { AppRuntime } from "../../effect/app-runtime"
import { Effect } from "effect"
diff --git a/packages/opencode/src/cli/cmd/plug.ts b/packages/opencode/src/cli/cmd/plug.ts
index 692c556b2..42d06ff47 100644
--- a/packages/opencode/src/cli/cmd/plug.ts
+++ b/packages/opencode/src/cli/cmd/plug.ts
@@ -7,8 +7,8 @@ import { installPlugin, patchPluginConfig, readPluginManifest } from "../../plug
import { resolvePluginTarget } from "../../plugin/shared"
import { Instance } from "../../project/instance"
import { errorMessage } from "../../util/error"
-import { Filesystem } from "../../util/filesystem"
-import { Process } from "../../util/process"
+import { Filesystem } from "../../util"
+import { Process } from "../../util"
import { UI } from "../ui"
import { cmd } from "./cmd"
diff --git a/packages/opencode/src/cli/cmd/pr.ts b/packages/opencode/src/cli/cmd/pr.ts
index f392bab4c..6141ef90a 100644
--- a/packages/opencode/src/cli/cmd/pr.ts
+++ b/packages/opencode/src/cli/cmd/pr.ts
@@ -3,7 +3,7 @@ import { cmd } from "./cmd"
import { AppRuntime } from "@/effect/app-runtime"
import { Git } from "@/git"
import { Instance } from "@/project/instance"
-import { Process } from "@/util/process"
+import { Process } from "@/util"
export const PrCommand = cmd({
command: "pr <number>",
diff --git a/packages/opencode/src/cli/cmd/providers.ts b/packages/opencode/src/cli/cmd/providers.ts
index 5b7f5a1a0..47a5c37e8 100644
--- a/packages/opencode/src/cli/cmd/providers.ts
+++ b/packages/opencode/src/cli/cmd/providers.ts
@@ -12,7 +12,7 @@ import { Global } from "../../global"
import { Plugin } from "../../plugin"
import { Instance } from "../../project/instance"
import type { Hooks } from "@opencode-ai/plugin"
-import { Process } from "../../util/process"
+import { Process } from "../../util"
import { text } from "node:stream/consumers"
import { Effect } from "effect"
diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts
index e94ba5d11..da7237237 100644
--- a/packages/opencode/src/cli/cmd/run.ts
+++ b/packages/opencode/src/cli/cmd/run.ts
@@ -6,7 +6,7 @@ import { cmd } from "./cmd"
import { Flag } from "../../flag/flag"
import { bootstrap } from "../bootstrap"
import { EOL } from "os"
-import { Filesystem } from "../../util/filesystem"
+import { Filesystem } from "../../util"
import { createOpencodeClient, type OpencodeClient, type ToolPart } from "@opencode-ai/sdk/v2"
import { Server } from "../../server/server"
import { Provider } from "../../provider"
@@ -25,7 +25,7 @@ import { TaskTool } from "../../tool/task"
import { SkillTool } from "../../tool/skill"
import { BashTool } from "../../tool/bash"
import { TodoWriteTool } from "../../tool/todo"
-import { Locale } from "../../util/locale"
+import { Locale } from "../../util"
import { AppRuntime } from "@/effect/app-runtime"
type ToolProps<T> = {
diff --git a/packages/opencode/src/cli/cmd/session.ts b/packages/opencode/src/cli/cmd/session.ts
index 6f79e726f..8537a74d4 100644
--- a/packages/opencode/src/cli/cmd/session.ts
+++ b/packages/opencode/src/cli/cmd/session.ts
@@ -4,10 +4,10 @@ import { Session } from "../../session"
import { SessionID } from "../../session/schema"
import { bootstrap } from "../bootstrap"
import { UI } from "../ui"
-import { Locale } from "../../util/locale"
+import { Locale } from "../../util"
import { Flag } from "../../flag/flag"
-import { Filesystem } from "../../util/filesystem"
-import { Process } from "../../util/process"
+import { Filesystem } from "../../util"
+import { Process } from "../../util"
import { EOL } from "os"
import path from "path"
import { which } from "../../util/which"
diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx
index 173c5ff60..e3e80c0fd 100644
--- a/packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx
@@ -4,7 +4,7 @@ import { useSync } from "@tui/context/sync"
import { map, pipe, entries, sortBy } from "remeda"
import { DialogSelect, type DialogSelectRef, type DialogSelectOption } from "@tui/ui/dialog-select"
import { useTheme } from "../context/theme"
-import { Keybind } from "@/util/keybind"
+import { Keybind } from "@/util"
import { TextAttributes } from "@opentui/core"
import { useSDK } from "@tui/context/sdk"
diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx
index 9ecb21e82..a42755bee 100644
--- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx
@@ -3,14 +3,14 @@ import { DialogSelect } from "@tui/ui/dialog-select"
import { useRoute } from "@tui/context/route"
import { useSync } from "@tui/context/sync"
import { createMemo, createResource, createSignal, onMount } from "solid-js"
-import { Locale } from "@/util/locale"
+import { Locale } from "@/util"
import { useProject } from "@tui/context/project"
import { useKeybind } from "../context/keybind"
import { useTheme } from "../context/theme"
import { useSDK } from "../context/sdk"
import { Flag } from "@/flag/flag"
import { DialogSessionRename } from "./dialog-session-rename"
-import { Keybind } from "@/util/keybind"
+import { Keybind } from "@/util"
import { createDebouncedSignal } from "../util/signal"
import { useToast } from "../ui/toast"
import { DialogWorkspaceCreate, openWorkspaceSession } from "./dialog-workspace-create"
diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-stash.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-stash.tsx
index e8664f628..8a6e69145 100644
--- a/packages/opencode/src/cli/cmd/tui/component/dialog-stash.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/dialog-stash.tsx
@@ -1,7 +1,7 @@
import { useDialog } from "@tui/ui/dialog"
import { DialogSelect } from "@tui/ui/dialog-select"
import { createMemo, createSignal } from "solid-js"
-import { Locale } from "@/util/locale"
+import { Locale } from "@/util"
import { useTheme } from "../context/theme"
import { useKeybind } from "../context/keybind"
import { usePromptStash, type StashEntry } from "./prompt/stash"
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
index 7ca73310b..305d07622 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx
@@ -12,7 +12,7 @@ import { useTheme, selectedForeground } from "@tui/context/theme"
import { SplitBorder } from "@tui/component/border"
import { useCommandDialog } from "@tui/component/dialog-command"
import { useTerminalDimensions } from "@opentui/solid"
-import { Locale } from "@/util/locale"
+import { Locale } from "@/util"
import type { PromptInfo } from "./history"
import { useFrecency } from "./frecency"
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/frecency.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/frecency.tsx
index 3ea8826ef..929f3a07d 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/frecency.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/frecency.tsx
@@ -1,6 +1,6 @@
import path from "path"
import { Global } from "@/global"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { onMount } from "solid-js"
import { createStore } from "solid-js/store"
import { createSimpleContext } from "../../context/helper"
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx
index d49dd5c7b..03db74de9 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx
@@ -1,6 +1,6 @@
import path from "path"
import { Global } from "@/global"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { onMount } from "solid-js"
import { createStore, produce, unwrap } from "solid-js/store"
import { createSimpleContext } from "../../context/helper"
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
index 87440d0e2..c361e48c9 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
@@ -3,7 +3,7 @@ import { createEffect, createMemo, onMount, createSignal, onCleanup, on, Show, S
import "opentui-spinner/solid"
import path from "path"
import { fileURLToPath } from "url"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { useLocal } from "@tui/context/local"
import { useTheme } from "@tui/context/theme"
import { EmptyBorder, SplitBorder } from "@tui/component/border"
@@ -27,7 +27,7 @@ import { Clipboard } from "../../util/clipboard"
import type { AssistantMessage, FilePart, UserMessage } from "@opencode-ai/sdk/v2"
import { TuiEvent } from "../../event"
import { iife } from "@/util/iife"
-import { Locale } from "@/util/locale"
+import { Locale } from "@/util"
import { formatDuration } from "@/util/format"
import { createColors, createFrames } from "../../ui/spinner.ts"
import { useDialog } from "@tui/ui/dialog"
diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/stash.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/stash.tsx
index ef3eb329a..84ba62338 100644
--- a/packages/opencode/src/cli/cmd/tui/component/prompt/stash.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/prompt/stash.tsx
@@ -1,6 +1,6 @@
import path from "path"
import { Global } from "@/global"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { onMount } from "solid-js"
import { createStore, produce, unwrap } from "solid-js/store"
import { createSimpleContext } from "../../context/helper"
diff --git a/packages/opencode/src/cli/cmd/tui/component/textarea-keybindings.ts b/packages/opencode/src/cli/cmd/tui/component/textarea-keybindings.ts
index 36ab03de5..eb7b622c6 100644
--- a/packages/opencode/src/cli/cmd/tui/component/textarea-keybindings.ts
+++ b/packages/opencode/src/cli/cmd/tui/component/textarea-keybindings.ts
@@ -1,7 +1,7 @@
import { createMemo } from "solid-js"
import type { KeyBinding } from "@opentui/core"
import { useKeybind } from "../context/keybind"
-import { Keybind } from "@/util/keybind"
+import { Keybind } from "@/util"
const TEXTAREA_ACTIONS = [
"submit",
diff --git a/packages/opencode/src/cli/cmd/tui/context/keybind.tsx b/packages/opencode/src/cli/cmd/tui/context/keybind.tsx
index 8d3fe487d..9c883aa20 100644
--- a/packages/opencode/src/cli/cmd/tui/context/keybind.tsx
+++ b/packages/opencode/src/cli/cmd/tui/context/keybind.tsx
@@ -1,5 +1,5 @@
import { createMemo } from "solid-js"
-import { Keybind } from "@/util/keybind"
+import { Keybind } from "@/util"
import { pipe, mapValues } from "remeda"
import type { TuiConfig } from "@/config/tui"
import type { ParsedKey, Renderable } from "@opentui/core"
diff --git a/packages/opencode/src/cli/cmd/tui/context/kv.tsx b/packages/opencode/src/cli/cmd/tui/context/kv.tsx
index 7a52156f8..dc0b96c62 100644
--- a/packages/opencode/src/cli/cmd/tui/context/kv.tsx
+++ b/packages/opencode/src/cli/cmd/tui/context/kv.tsx
@@ -1,5 +1,5 @@
import { Global } from "@/global"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { createSignal, type Setter } from "solid-js"
import { createStore } from "solid-js/store"
import { createSimpleContext } from "./helper"
diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx
index 29f95141c..612f2b717 100644
--- a/packages/opencode/src/cli/cmd/tui/context/local.tsx
+++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx
@@ -12,7 +12,7 @@ import { Provider } from "@/provider"
import { useArgs } from "./args"
import { useSDK } from "./sdk"
import { RGBA } from "@opentui/core"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
name: "Local",
diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx
index cab162f8f..a0a59199b 100644
--- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx
+++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx
@@ -28,7 +28,7 @@ import type { Snapshot } from "@/snapshot"
import { useExit } from "./exit"
import { useArgs } from "./args"
import { batch, createEffect, on } from "solid-js"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { ConsoleState, emptyConsoleState, type ConsoleState as ConsoleStateType } from "@/config/console-state"
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
diff --git a/packages/opencode/src/cli/cmd/tui/context/theme.tsx b/packages/opencode/src/cli/cmd/tui/context/theme.tsx
index 0c0658e74..179dc9370 100644
--- a/packages/opencode/src/cli/cmd/tui/context/theme.tsx
+++ b/packages/opencode/src/cli/cmd/tui/context/theme.tsx
@@ -40,7 +40,7 @@ import { useKV } from "./kv"
import { useRenderer } from "@opentui/solid"
import { createStore, produce } from "solid-js/store"
import { Global } from "@/global"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { useTuiConfig } from "./tui-config"
import { isRecord } from "@/util/record"
import type { TuiThemeCurrent } from "@opencode-ai/plugin/tui"
diff --git a/packages/opencode/src/cli/cmd/tui/feature-plugins/system/plugins.tsx b/packages/opencode/src/cli/cmd/tui/feature-plugins/system/plugins.tsx
index f2fd25ffb..f391eb24a 100644
--- a/packages/opencode/src/cli/cmd/tui/feature-plugins/system/plugins.tsx
+++ b/packages/opencode/src/cli/cmd/tui/feature-plugins/system/plugins.tsx
@@ -1,4 +1,4 @@
-import { Keybind } from "@/util/keybind"
+import { Keybind } from "@/util"
import type { TuiPlugin, TuiPluginApi, TuiPluginModule, TuiPluginStatus } from "@opencode-ai/plugin/tui"
import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
import { fileURLToPath } from "url"
diff --git a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts
index bd7eac771..dd873b753 100644
--- a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts
+++ b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts
@@ -15,7 +15,7 @@ import { fileURLToPath } from "url"
import { Config } from "@/config"
import { TuiConfig } from "@/config/tui"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { errorData, errorMessage } from "@/util/error"
import { isRecord } from "@/util/record"
import { Instance } from "@/project/instance"
@@ -32,8 +32,8 @@ import { PluginMeta } from "@/plugin/meta"
import { installPlugin as installModulePlugin, patchPluginConfig, readPluginManifest } from "@/plugin/install"
import { hasTheme, upsertTheme } from "../context/theme"
import { Global } from "@/global"
-import { Filesystem } from "@/util/filesystem"
-import { Process } from "@/util/process"
+import { Filesystem } from "@/util"
+import { Process } from "@/util"
import { Flock } from "@opencode-ai/shared/util/flock"
import { Flag } from "@/flag/flag"
import { INTERNAL_TUI_PLUGINS, type InternalTuiPlugin } from "./internal"
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx
index 742d51be2..0ce33a59a 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx
@@ -2,7 +2,7 @@ import { createMemo, onMount } from "solid-js"
import { useSync } from "@tui/context/sync"
import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select"
import type { TextPart } from "@opencode-ai/sdk/v2"
-import { Locale } from "@/util/locale"
+import { Locale } from "@/util"
import { useSDK } from "@tui/context/sdk"
import { useRoute } from "@tui/context/route"
import { useDialog } from "../../ui/dialog"
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx
index 87248a6a8..c0052f25f 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx
@@ -2,7 +2,7 @@ import { createMemo, onMount } from "solid-js"
import { useSync } from "@tui/context/sync"
import { DialogSelect, type DialogSelectOption } from "@tui/ui/dialog-select"
import type { TextPart } from "@opencode-ai/sdk/v2"
-import { Locale } from "@/util/locale"
+import { Locale } from "@/util"
import { DialogMessage } from "./dialog-message"
import { useDialog } from "../../ui/dialog"
import type { PromptInfo } from "../../component/prompt/history"
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
index 9f0dfa603..58b5d6626 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
@@ -33,7 +33,7 @@ import type {
ReasoningPart,
} from "@opencode-ai/sdk/v2"
import { useLocal } from "@tui/context/local"
-import { Locale } from "@/util/locale"
+import { Locale } from "@/util"
import type { Tool } from "@/tool/tool"
import type { ReadTool } from "@/tool/read"
import type { WriteTool } from "@/tool/write"
@@ -73,7 +73,7 @@ import { Editor } from "../../util/editor"
import stripAnsi from "strip-ansi"
import { usePromptRef } from "../../context/prompt"
import { useExit } from "../../context/exit"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { Global } from "@/global"
import { PermissionPrompt } from "./permission"
import { QuestionPrompt } from "./question"
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx
index ad824fe48..3554ab44c 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx
@@ -11,8 +11,8 @@ import { useSync } from "../../context/sync"
import { useTextareaKeybindings } from "../../component/textarea-keybindings"
import path from "path"
import { LANGUAGE_EXTENSIONS } from "@/lsp/language"
-import { Keybind } from "@/util/keybind"
-import { Locale } from "@/util/locale"
+import { Keybind } from "@/util"
+import { Locale } from "@/util"
import { Global } from "@/global"
import { useDialog } from "../../ui/dialog"
import { getScrollAcceleration } from "../../util/scroll"
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx
index c857937d4..556959960 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/subagent-footer.tsx
@@ -6,7 +6,7 @@ import { SplitBorder } from "@tui/component/border"
import type { AssistantMessage } from "@opencode-ai/sdk/v2"
import { useCommandDialog } from "@tui/component/dialog-command"
import { useKeybind } from "../../context/keybind"
-import { Locale } from "@/util/locale"
+import { Locale } from "@/util"
import { useTerminalDimensions } from "@opentui/solid"
export function SubagentFooter() {
diff --git a/packages/opencode/src/cli/cmd/tui/thread.ts b/packages/opencode/src/cli/cmd/tui/thread.ts
index 972e67d10..3aaa5a54f 100644
--- a/packages/opencode/src/cli/cmd/tui/thread.ts
+++ b/packages/opencode/src/cli/cmd/tui/thread.ts
@@ -1,15 +1,15 @@
import { cmd } from "@/cli/cmd/cmd"
import { tui } from "./app"
-import { Rpc } from "@/util/rpc"
+import { Rpc } from "@/util"
import { type rpc } from "./worker"
import path from "path"
import { fileURLToPath } from "url"
import { UI } from "@/cli/ui"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { errorMessage } from "@/util/error"
import { withTimeout } from "@/util/timeout"
import { withNetworkOptions, resolveNetworkOptions } from "@/cli/network"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import type { GlobalEvent } from "@opencode-ai/sdk/v2"
import type { EventSource } from "./context/sdk"
import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32"
diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx
index 48adddaed..526f3a61b 100644
--- a/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx
+++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx
@@ -4,7 +4,7 @@ import { useDialog, type DialogContext } from "./dialog"
import { createStore } from "solid-js/store"
import { For } from "solid-js"
import { useKeyboard } from "@opentui/solid"
-import { Locale } from "@/util/locale"
+import { Locale } from "@/util"
export type DialogConfirmProps = {
title: string
diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
index b6c937f41..dda9a5a8e 100644
--- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
+++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
@@ -8,8 +8,8 @@ import * as fuzzysort from "fuzzysort"
import { isDeepEqual } from "remeda"
import { useDialog, type DialogContext } from "@tui/ui/dialog"
import { useKeybind } from "@tui/context/keybind"
-import { Keybind } from "@/util/keybind"
-import { Locale } from "@/util/locale"
+import { Keybind } from "@/util"
+import { Locale } from "@/util"
import { getScrollAcceleration } from "../util/scroll"
import { useTuiConfig } from "../context/tui-config"
diff --git a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts
index 87c0a63ab..a67eb04f6 100644
--- a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts
+++ b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts
@@ -4,8 +4,8 @@ import { lazy } from "../../../../util/lazy.js"
import { tmpdir } from "os"
import path from "path"
import fs from "fs/promises"
-import { Filesystem } from "../../../../util/filesystem"
-import { Process } from "../../../../util/process"
+import { Filesystem } from "../../../../util"
+import { Process } from "../../../../util"
import { which } from "../../../../util/which"
/**
diff --git a/packages/opencode/src/cli/cmd/tui/util/editor.ts b/packages/opencode/src/cli/cmd/tui/util/editor.ts
index 9eaae99fc..540cf6f49 100644
--- a/packages/opencode/src/cli/cmd/tui/util/editor.ts
+++ b/packages/opencode/src/cli/cmd/tui/util/editor.ts
@@ -3,8 +3,8 @@ import { rm } from "node:fs/promises"
import { tmpdir } from "node:os"
import { join } from "node:path"
import { CliRenderer } from "@opentui/core"
-import { Filesystem } from "@/util/filesystem"
-import { Process } from "@/util/process"
+import { Filesystem } from "@/util"
+import { Process } from "@/util"
export namespace Editor {
export async function open(opts: { value: string; renderer: CliRenderer }): Promise<string | undefined> {
diff --git a/packages/opencode/src/cli/cmd/tui/util/sound.ts b/packages/opencode/src/cli/cmd/tui/util/sound.ts
index d3a8db8b4..1be35eecb 100644
--- a/packages/opencode/src/cli/cmd/tui/util/sound.ts
+++ b/packages/opencode/src/cli/cmd/tui/util/sound.ts
@@ -2,7 +2,7 @@ import { Player } from "cli-sound"
import { mkdirSync } from "node:fs"
import { tmpdir } from "node:os"
import { basename, join } from "node:path"
-import { Process } from "@/util/process"
+import { Process } from "@/util"
import { which } from "@/util/which"
import pulseA from "../asset/pulse-a.wav" with { type: "file" }
import pulseB from "../asset/pulse-b.wav" with { type: "file" }
diff --git a/packages/opencode/src/cli/cmd/tui/util/transcript.ts b/packages/opencode/src/cli/cmd/tui/util/transcript.ts
index a89559c95..8fa0bc426 100644
--- a/packages/opencode/src/cli/cmd/tui/util/transcript.ts
+++ b/packages/opencode/src/cli/cmd/tui/util/transcript.ts
@@ -1,5 +1,5 @@
import type { AssistantMessage, Part, Provider, UserMessage } from "@opencode-ai/sdk/v2"
-import { Locale } from "@/util/locale"
+import { Locale } from "@/util"
import * as Model from "./model"
export type TranscriptOptions = {
diff --git a/packages/opencode/src/cli/cmd/tui/worker.ts b/packages/opencode/src/cli/cmd/tui/worker.ts
index da9e3985b..393a407eb 100644
--- a/packages/opencode/src/cli/cmd/tui/worker.ts
+++ b/packages/opencode/src/cli/cmd/tui/worker.ts
@@ -1,9 +1,9 @@
import { Installation } from "@/installation"
import { Server } from "@/server/server"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { Instance } from "@/project/instance"
import { InstanceBootstrap } from "@/project/bootstrap"
-import { Rpc } from "@/util/rpc"
+import { Rpc } from "@/util"
import { upgrade } from "@/cli/upgrade"
import { Config } from "@/config"
import { GlobalBus } from "@/bus/global"
diff --git a/packages/opencode/src/cli/cmd/uninstall.ts b/packages/opencode/src/cli/cmd/uninstall.ts
index 31830f085..c0517d491 100644
--- a/packages/opencode/src/cli/cmd/uninstall.ts
+++ b/packages/opencode/src/cli/cmd/uninstall.ts
@@ -7,8 +7,8 @@ import { Global } from "../../global"
import fs from "fs/promises"
import path from "path"
import os from "os"
-import { Filesystem } from "../../util/filesystem"
-import { Process } from "../../util/process"
+import { Filesystem } from "../../util"
+import { Process } from "../../util"
interface UninstallArgs {
keepConfig: boolean
diff --git a/packages/opencode/src/cli/heap.ts b/packages/opencode/src/cli/heap.ts
index bb5a3d093..cf1cffa80 100644
--- a/packages/opencode/src/cli/heap.ts
+++ b/packages/opencode/src/cli/heap.ts
@@ -2,7 +2,7 @@ import path from "path"
import { writeHeapSnapshot } from "node:v8"
import { Flag } from "@/flag/flag"
import { Global } from "@/global"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
const log = Log.create({ service: "heap" })
const MINUTE = 60_000
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
index 7eeacf1ff..ee1c755eb 100644
--- a/packages/opencode/src/config/config.ts
+++ b/packages/opencode/src/config/config.ts
@@ -1,8 +1,8 @@
-import { Log } from "../util/log"
+import { Log } from "../util"
import path from "path"
import { pathToFileURL } from "url"
import os from "os"
-import { Process } from "../util/process"
+import { Process } from "../util"
import z from "zod"
import { mergeDeep, pipe, unique } from "remeda"
import { Global } from "../global"
diff --git a/packages/opencode/src/config/markdown.ts b/packages/opencode/src/config/markdown.ts
index 2f1483dca..8b5392be5 100644
--- a/packages/opencode/src/config/markdown.ts
+++ b/packages/opencode/src/config/markdown.ts
@@ -1,7 +1,7 @@
import { NamedError } from "@opencode-ai/shared/util/error"
import matter from "gray-matter"
import { z } from "zod"
-import { Filesystem } from "../util/filesystem"
+import { Filesystem } from "../util"
export namespace ConfigMarkdown {
export const FILE_REGEX = /(?<![\w`])@(\.?[^\s`,.]*(?:\.[^\s`,.]+)*)/g
diff --git a/packages/opencode/src/config/paths.ts b/packages/opencode/src/config/paths.ts
index 884a77449..c5eb105c9 100644
--- a/packages/opencode/src/config/paths.ts
+++ b/packages/opencode/src/config/paths.ts
@@ -3,7 +3,7 @@ import os from "os"
import z from "zod"
import { type ParseError as JsoncParseError, parse as parseJsonc, printParseErrorCode } from "jsonc-parser"
import { NamedError } from "@opencode-ai/shared/util/error"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { Flag } from "@/flag/flag"
import { Global } from "@/global"
diff --git a/packages/opencode/src/config/tui-migrate.ts b/packages/opencode/src/config/tui-migrate.ts
index dbe33ffb4..f9d37e479 100644
--- a/packages/opencode/src/config/tui-migrate.ts
+++ b/packages/opencode/src/config/tui-migrate.ts
@@ -6,8 +6,8 @@ import { ConfigPaths } from "./paths"
import { TuiInfo, TuiOptions } from "./tui-schema"
import { Instance } from "@/project/instance"
import { Flag } from "@/flag/flag"
-import { Log } from "@/util/log"
-import { Filesystem } from "@/util/filesystem"
+import { Log } from "@/util"
+import { Filesystem } from "@/util"
import { Global } from "@/global"
const log = Log.create({ service: "tui.migrate" })
diff --git a/packages/opencode/src/config/tui.ts b/packages/opencode/src/config/tui.ts
index 24ccefecd..c1e2b6e6b 100644
--- a/packages/opencode/src/config/tui.ts
+++ b/packages/opencode/src/config/tui.ts
@@ -7,7 +7,7 @@ import { ConfigPaths } from "./paths"
import { migrateTuiConfig } from "./tui-migrate"
import { TuiInfo } from "./tui-schema"
import { Flag } from "@/flag/flag"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { isRecord } from "@/util/record"
import { Global } from "@/global"
import { InstanceState } from "@/effect"
diff --git a/packages/opencode/src/control-plane/workspace-context.ts b/packages/opencode/src/control-plane/workspace-context.ts
index 273adbb24..565472a24 100644
--- a/packages/opencode/src/control-plane/workspace-context.ts
+++ b/packages/opencode/src/control-plane/workspace-context.ts
@@ -1,4 +1,4 @@
-import { LocalContext } from "../util/local-context"
+import { LocalContext } from "../util"
import type { WorkspaceID } from "../control-plane/schema"
export interface WorkspaceContext {
diff --git a/packages/opencode/src/control-plane/workspace.ts b/packages/opencode/src/control-plane/workspace.ts
index 4fef4f932..a0d4c1680 100644
--- a/packages/opencode/src/control-plane/workspace.ts
+++ b/packages/opencode/src/control-plane/workspace.ts
@@ -8,8 +8,8 @@ import { GlobalBus } from "@/bus/global"
import { SyncEvent } from "@/sync"
import { EventTable } from "@/sync/event.sql"
import { Flag } from "@/flag/flag"
-import { Log } from "@/util/log"
-import { Filesystem } from "@/util/filesystem"
+import { Log } from "@/util"
+import { Filesystem } from "@/util"
import { ProjectID } from "@/project/schema"
import { Slug } from "@opencode-ai/shared/util/slug"
import { WorkspaceTable } from "./workspace.sql"
diff --git a/packages/opencode/src/effect/bridge.ts b/packages/opencode/src/effect/bridge.ts
index 9ca7b50ad..d79fc74f4 100644
--- a/packages/opencode/src/effect/bridge.ts
+++ b/packages/opencode/src/effect/bridge.ts
@@ -1,7 +1,7 @@
import { Effect, Fiber } from "effect"
import { WorkspaceContext } from "@/control-plane/workspace-context"
import { Instance, type InstanceContext } from "@/project/instance"
-import { LocalContext } from "@/util/local-context"
+import { LocalContext } from "@/util"
import { InstanceRef, WorkspaceRef } from "./instance-ref"
import { attachWith } from "./run-service"
diff --git a/packages/opencode/src/effect/instance-state.ts b/packages/opencode/src/effect/instance-state.ts
index 2681d5feb..b6249db04 100644
--- a/packages/opencode/src/effect/instance-state.ts
+++ b/packages/opencode/src/effect/instance-state.ts
@@ -1,7 +1,7 @@
import { Effect, Fiber, ScopedCache, Scope, Context } from "effect"
import { EffectLogger } from "@/effect/logger"
import { Instance, type InstanceContext } from "@/project/instance"
-import { LocalContext } from "@/util/local-context"
+import { LocalContext } from "@/util"
import { InstanceRef, WorkspaceRef } from "./instance-ref"
import { registerDisposer } from "./instance-registry"
import { WorkspaceContext } from "@/control-plane/workspace-context"
diff --git a/packages/opencode/src/effect/logger.ts b/packages/opencode/src/effect/logger.ts
index 7a7f0a541..7f21084dd 100644
--- a/packages/opencode/src/effect/logger.ts
+++ b/packages/opencode/src/effect/logger.ts
@@ -1,5 +1,5 @@
import { Cause, Effect, Logger, References } from "effect"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
export namespace EffectLogger {
type Fields = Record<string, unknown>
diff --git a/packages/opencode/src/effect/run-service.ts b/packages/opencode/src/effect/run-service.ts
index 3de82e0d1..9553e7a3a 100644
--- a/packages/opencode/src/effect/run-service.ts
+++ b/packages/opencode/src/effect/run-service.ts
@@ -1,7 +1,7 @@
import { Effect, Layer, ManagedRuntime } from "effect"
import * as Context from "effect/Context"
import { Instance } from "@/project/instance"
-import { LocalContext } from "@/util/local-context"
+import { LocalContext } from "@/util"
import { InstanceRef, WorkspaceRef } from "./instance-ref"
import { Observability } from "./observability"
import { WorkspaceContext } from "@/control-plane/workspace-context"
diff --git a/packages/opencode/src/file/file.ts b/packages/opencode/src/file/file.ts
index 35f2a8740..226906591 100644
--- a/packages/opencode/src/file/file.ts
+++ b/packages/opencode/src/file/file.ts
@@ -12,7 +12,7 @@ import path from "path"
import z from "zod"
import { Global } from "../global"
import { Instance } from "../project/instance"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { Protected } from "./protected"
import { Ripgrep } from "./ripgrep"
diff --git a/packages/opencode/src/file/ripgrep.ts b/packages/opencode/src/file/ripgrep.ts
index fee9cf443..9a78c5b7f 100644
--- a/packages/opencode/src/file/ripgrep.ts
+++ b/packages/opencode/src/file/ripgrep.ts
@@ -5,8 +5,8 @@ import z from "zod"
import { Cause, Context, Effect, Layer, Queue, Stream } from "effect"
import { ripgrep } from "ripgrep"
-import { Filesystem } from "@/util/filesystem"
-import { Log } from "@/util/log"
+import { Filesystem } from "@/util"
+import { Log } from "@/util"
export namespace Ripgrep {
const log = Log.create({ service: "ripgrep" })
diff --git a/packages/opencode/src/file/time.ts b/packages/opencode/src/file/time.ts
index 86b6b4116..327eadbef 100644
--- a/packages/opencode/src/file/time.ts
+++ b/packages/opencode/src/file/time.ts
@@ -3,7 +3,7 @@ import { InstanceState } from "@/effect"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Flag } from "@/flag/flag"
import type { SessionID } from "@/session/schema"
-import { Log } from "../util/log"
+import { Log } from "../util"
export namespace FileTime {
const log = Log.create({ service: "file.time" })
diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts
index ab5942547..f11cf88a6 100644
--- a/packages/opencode/src/file/watcher.ts
+++ b/packages/opencode/src/file/watcher.ts
@@ -15,7 +15,7 @@ import { lazy } from "@/util/lazy"
import { Config } from "../config"
import { FileIgnore } from "./ignore"
import { Protected } from "./protected"
-import { Log } from "../util/log"
+import { Log } from "../util"
declare const OPENCODE_LIBC: string | undefined
diff --git a/packages/opencode/src/format/format.ts b/packages/opencode/src/format/format.ts
index 2ce922495..40855636f 100644
--- a/packages/opencode/src/format/format.ts
+++ b/packages/opencode/src/format/format.ts
@@ -6,7 +6,7 @@ import path from "path"
import { mergeDeep } from "remeda"
import z from "zod"
import { Config } from "../config"
-import { Log } from "../util/log"
+import { Log } from "../util"
import * as Formatter from "./formatter"
const log = Log.create({ service: "format" })
diff --git a/packages/opencode/src/format/formatter.ts b/packages/opencode/src/format/formatter.ts
index 6c17310ff..36249db7d 100644
--- a/packages/opencode/src/format/formatter.ts
+++ b/packages/opencode/src/format/formatter.ts
@@ -1,7 +1,7 @@
import { Npm } from "../npm"
import { Instance } from "../project/instance"
-import { Filesystem } from "../util/filesystem"
-import { Process } from "../util/process"
+import { Filesystem } from "../util"
+import { Process } from "../util"
import { which } from "../util/which"
import { Flag } from "@/flag/flag"
diff --git a/packages/opencode/src/global/global.ts b/packages/opencode/src/global/global.ts
index 1bbb5968c..3633e0855 100644
--- a/packages/opencode/src/global/global.ts
+++ b/packages/opencode/src/global/global.ts
@@ -2,7 +2,7 @@ import fs from "fs/promises"
import { xdgData, xdgCache, xdgConfig, xdgState } from "xdg-basedir"
import path from "path"
import os from "os"
-import { Filesystem } from "../util/filesystem"
+import { Filesystem } from "../util"
import { Flock } from "@opencode-ai/shared/util/flock"
const app = "opencode"
diff --git a/packages/opencode/src/ide/ide.ts b/packages/opencode/src/ide/ide.ts
index cbced9c3d..65e80d7f2 100644
--- a/packages/opencode/src/ide/ide.ts
+++ b/packages/opencode/src/ide/ide.ts
@@ -1,8 +1,8 @@
import { BusEvent } from "@/bus/bus-event"
import z from "zod"
import { NamedError } from "@opencode-ai/shared/util/error"
-import { Log } from "../util/log"
-import { Process } from "@/util/process"
+import { Log } from "../util"
+import { Process } from "@/util"
const SUPPORTED_IDES = [
{ name: "Windsurf" as const, cmd: "windsurf" },
diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts
index 641411461..ab3ccb712 100644
--- a/packages/opencode/src/index.ts
+++ b/packages/opencode/src/index.ts
@@ -2,7 +2,7 @@ import yargs from "yargs"
import { hideBin } from "yargs/helpers"
import { RunCommand } from "./cli/cmd/run"
import { GenerateCommand } from "./cli/cmd/generate"
-import { Log } from "./util/log"
+import { Log } from "./util"
import { ConsoleCommand } from "./cli/cmd/account"
import { ProvidersCommand } from "./cli/cmd/providers"
import { AgentCommand } from "./cli/cmd/agent"
@@ -14,7 +14,7 @@ import { Installation } from "./installation"
import { NamedError } from "@opencode-ai/shared/util/error"
import { FormatError } from "./cli/error"
import { ServeCommand } from "./cli/cmd/serve"
-import { Filesystem } from "./util/filesystem"
+import { Filesystem } from "./util"
import { DebugCommand } from "./cli/cmd/debug"
import { StatsCommand } from "./cli/cmd/stats"
import { McpCommand } from "./cli/cmd/mcp"
diff --git a/packages/opencode/src/installation/installation.ts b/packages/opencode/src/installation/installation.ts
index 898af9269..dcaa0cd72 100644
--- a/packages/opencode/src/installation/installation.ts
+++ b/packages/opencode/src/installation/installation.ts
@@ -7,7 +7,7 @@ import path from "path"
import z from "zod"
import { BusEvent } from "@/bus/bus-event"
import { Flag } from "../flag/flag"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { CHANNEL as channel, VERSION as version } from "./meta"
import semver from "semver"
diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts
index fe5a9ab18..50051b390 100644
--- a/packages/opencode/src/lsp/client.ts
+++ b/packages/opencode/src/lsp/client.ts
@@ -4,15 +4,15 @@ import path from "path"
import { pathToFileURL, fileURLToPath } from "url"
import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc/node"
import type { Diagnostic as VSCodeDiagnostic } from "vscode-languageserver-types"
-import { Log } from "../util/log"
-import { Process } from "../util/process"
+import { Log } from "../util"
+import { Process } from "../util"
import { LANGUAGE_EXTENSIONS } from "./language"
import z from "zod"
import type { LSPServer } from "./server"
import { NamedError } from "@opencode-ai/shared/util/error"
import { withTimeout } from "../util/timeout"
import { Instance } from "../project/instance"
-import { Filesystem } from "../util/filesystem"
+import { Filesystem } from "../util"
const DIAGNOSTICS_DEBOUNCE_MS = 150
diff --git a/packages/opencode/src/lsp/index.ts b/packages/opencode/src/lsp/index.ts
index f567868f6..a55ac1840 100644
--- a/packages/opencode/src/lsp/index.ts
+++ b/packages/opencode/src/lsp/index.ts
@@ -1,6 +1,6 @@
import { BusEvent } from "@/bus/bus-event"
import { Bus } from "@/bus"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { LSPClient } from "./client"
import path from "path"
import { pathToFileURL, fileURLToPath } from "url"
@@ -9,7 +9,7 @@ import z from "zod"
import { Config } from "../config"
import { Instance } from "../project/instance"
import { Flag } from "@/flag/flag"
-import { Process } from "../util/process"
+import { Process } from "../util"
import { spawn as lspspawn } from "./launch"
import { Effect, Layer, Context } from "effect"
import { InstanceState } from "@/effect"
diff --git a/packages/opencode/src/lsp/launch.ts b/packages/opencode/src/lsp/launch.ts
index 51a7c209b..fb84666b0 100644
--- a/packages/opencode/src/lsp/launch.ts
+++ b/packages/opencode/src/lsp/launch.ts
@@ -1,5 +1,5 @@
import type { ChildProcessWithoutNullStreams } from "child_process"
-import { Process } from "../util/process"
+import { Process } from "../util"
type Child = Process.Child & ChildProcessWithoutNullStreams
diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts
index 769880ef0..8110e8608 100644
--- a/packages/opencode/src/lsp/server.ts
+++ b/packages/opencode/src/lsp/server.ts
@@ -2,14 +2,14 @@ import type { ChildProcessWithoutNullStreams } from "child_process"
import path from "path"
import os from "os"
import { Global } from "../global"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { text } from "node:stream/consumers"
import fs from "fs/promises"
-import { Filesystem } from "../util/filesystem"
+import { Filesystem } from "../util"
import { Instance } from "../project/instance"
import { Flag } from "../flag/flag"
import { Archive } from "../util"
-import { Process } from "../util/process"
+import { Process } from "../util"
import { which } from "../util/which"
import { Module } from "@opencode-ai/shared/util/module"
import { spawn } from "./launch"
diff --git a/packages/opencode/src/mcp/mcp.ts b/packages/opencode/src/mcp/mcp.ts
index f5179b224..8a57bcff7 100644
--- a/packages/opencode/src/mcp/mcp.ts
+++ b/packages/opencode/src/mcp/mcp.ts
@@ -10,7 +10,7 @@ import {
ToolListChangedNotificationSchema,
} from "@modelcontextprotocol/sdk/types.js"
import { Config } from "../config"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { NamedError } from "@opencode-ai/shared/util/error"
import z from "zod/v4"
import { Instance } from "../project/instance"
diff --git a/packages/opencode/src/mcp/oauth-callback.ts b/packages/opencode/src/mcp/oauth-callback.ts
index 6babccd77..3e6169517 100644
--- a/packages/opencode/src/mcp/oauth-callback.ts
+++ b/packages/opencode/src/mcp/oauth-callback.ts
@@ -1,6 +1,6 @@
import { createConnection } from "net"
import { createServer } from "http"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { OAUTH_CALLBACK_PORT, OAUTH_CALLBACK_PATH, parseRedirectUri } from "./oauth-provider"
const log = Log.create({ service: "mcp.oauth-callback" })
diff --git a/packages/opencode/src/mcp/oauth-provider.ts b/packages/opencode/src/mcp/oauth-provider.ts
index 4fdc192df..fe09e14a5 100644
--- a/packages/opencode/src/mcp/oauth-provider.ts
+++ b/packages/opencode/src/mcp/oauth-provider.ts
@@ -7,7 +7,7 @@ import type {
} from "@modelcontextprotocol/sdk/shared/auth.js"
import { Effect } from "effect"
import { McpAuth } from "./auth"
-import { Log } from "../util/log"
+import { Log } from "../util"
const log = Log.create({ service: "mcp.oauth" })
diff --git a/packages/opencode/src/node.ts b/packages/opencode/src/node.ts
index 6f020576d..a30783fb2 100644
--- a/packages/opencode/src/node.ts
+++ b/packages/opencode/src/node.ts
@@ -1,6 +1,6 @@
export { Config } from "./config"
export { Server } from "./server/server"
export { bootstrap } from "./cli/bootstrap"
-export { Log } from "./util/log"
+export { Log } from "./util"
export { Database } from "./storage/db"
export { JsonMigration } from "./storage/json-migration"
diff --git a/packages/opencode/src/npm/npm.ts b/packages/opencode/src/npm/npm.ts
index f90513071..7f1744605 100644
--- a/packages/opencode/src/npm/npm.ts
+++ b/packages/opencode/src/npm/npm.ts
@@ -2,10 +2,10 @@ import semver from "semver"
import z from "zod"
import { NamedError } from "@opencode-ai/shared/util/error"
import { Global } from "../global"
-import { Log } from "../util/log"
+import { Log } from "../util"
import path from "path"
import { readdir, rm } from "fs/promises"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { Flock } from "@opencode-ai/shared/util/flock"
import { Arborist } from "@npmcli/arborist"
diff --git a/packages/opencode/src/patch/patch.ts b/packages/opencode/src/patch/patch.ts
index 749efd911..1dc99b4da 100644
--- a/packages/opencode/src/patch/patch.ts
+++ b/packages/opencode/src/patch/patch.ts
@@ -2,7 +2,7 @@ import z from "zod"
import * as path from "path"
import * as fs from "fs/promises"
import { readFileSync } from "fs"
-import { Log } from "../util/log"
+import { Log } from "../util"
const log = Log.create({ service: "patch" })
diff --git a/packages/opencode/src/permission/evaluate.ts b/packages/opencode/src/permission/evaluate.ts
index 2b0604f4b..bcc4e5811 100644
--- a/packages/opencode/src/permission/evaluate.ts
+++ b/packages/opencode/src/permission/evaluate.ts
@@ -1,4 +1,4 @@
-import { Wildcard } from "@/util/wildcard"
+import { Wildcard } from "@/util"
type Rule = {
permission: string
diff --git a/packages/opencode/src/permission/permission.ts b/packages/opencode/src/permission/permission.ts
index e2dead8fe..a8463510c 100644
--- a/packages/opencode/src/permission/permission.ts
+++ b/packages/opencode/src/permission/permission.ts
@@ -7,9 +7,9 @@ import { MessageID, SessionID } from "@/session/schema"
import { PermissionTable } from "@/session/session.sql"
import { Database, eq } from "@/storage/db"
import { zod } from "@/util/effect-zod"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { withStatics } from "@/util/schema"
-import { Wildcard } from "@/util/wildcard"
+import { Wildcard } from "@/util"
import { Deferred, Effect, Layer, Schema, Context } from "effect"
import os from "os"
import { evaluate as evalRule } from "./evaluate"
diff --git a/packages/opencode/src/plugin/codex.ts b/packages/opencode/src/plugin/codex.ts
index ea356d55d..e0f1afa63 100644
--- a/packages/opencode/src/plugin/codex.ts
+++ b/packages/opencode/src/plugin/codex.ts
@@ -1,5 +1,5 @@
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { Installation } from "../installation"
import { OAUTH_DUMMY_KEY } from "../auth"
import os from "os"
diff --git a/packages/opencode/src/plugin/github-copilot/copilot.ts b/packages/opencode/src/plugin/github-copilot/copilot.ts
index e12d182e4..eeea21924 100644
--- a/packages/opencode/src/plugin/github-copilot/copilot.ts
+++ b/packages/opencode/src/plugin/github-copilot/copilot.ts
@@ -2,7 +2,7 @@ import type { Hooks, PluginInput } from "@opencode-ai/plugin"
import type { Model } from "@opencode-ai/sdk/v2"
import { Installation } from "@/installation"
import { iife } from "@/util/iife"
-import { Log } from "../../util/log"
+import { Log } from "../../util"
import { setTimeout as sleep } from "node:timers/promises"
import { CopilotModels } from "./models"
import { MessageV2 } from "@/session/message-v2"
diff --git a/packages/opencode/src/plugin/install.ts b/packages/opencode/src/plugin/install.ts
index 8dd821296..0a6256d6f 100644
--- a/packages/opencode/src/plugin/install.ts
+++ b/packages/opencode/src/plugin/install.ts
@@ -9,7 +9,7 @@ import {
import { ConfigPaths } from "@/config/paths"
import { Global } from "@/global"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { Flock } from "@opencode-ai/shared/util/flock"
import { isRecord } from "@/util/record"
diff --git a/packages/opencode/src/plugin/meta.ts b/packages/opencode/src/plugin/meta.ts
index 3f02f543e..89955d1df 100644
--- a/packages/opencode/src/plugin/meta.ts
+++ b/packages/opencode/src/plugin/meta.ts
@@ -3,7 +3,7 @@ import { fileURLToPath } from "url"
import { Flag } from "@/flag/flag"
import { Global } from "@/global"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { Flock } from "@opencode-ai/shared/util/flock"
import { parsePluginSpecifier, pluginSource } from "./shared"
diff --git a/packages/opencode/src/plugin/plugin.ts b/packages/opencode/src/plugin/plugin.ts
index 23c807ebe..ec1cf1e31 100644
--- a/packages/opencode/src/plugin/plugin.ts
+++ b/packages/opencode/src/plugin/plugin.ts
@@ -7,7 +7,7 @@ import type {
} from "@opencode-ai/plugin"
import { Config } from "../config"
import { Bus } from "../bus"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { createOpencodeClient } from "@opencode-ai/sdk"
import { Flag } from "../flag/flag"
import { CodexAuthPlugin } from "./codex"
diff --git a/packages/opencode/src/plugin/shared.ts b/packages/opencode/src/plugin/shared.ts
index 54cc32af5..11f36c41a 100644
--- a/packages/opencode/src/plugin/shared.ts
+++ b/packages/opencode/src/plugin/shared.ts
@@ -3,7 +3,7 @@ import { fileURLToPath, pathToFileURL } from "url"
import npa from "npm-package-arg"
import semver from "semver"
import { Npm } from "../npm"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { isRecord } from "@/util/record"
// Old npm package names for plugins that are now built-in
diff --git a/packages/opencode/src/project/bootstrap.ts b/packages/opencode/src/project/bootstrap.ts
index a1f2a8cb0..f00d8ffd9 100644
--- a/packages/opencode/src/project/bootstrap.ts
+++ b/packages/opencode/src/project/bootstrap.ts
@@ -8,7 +8,7 @@ import { Vcs } from "./vcs"
import { Bus } from "../bus"
import { Command } from "../command"
import { Instance } from "./instance"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { FileWatcher } from "@/file/watcher"
import { ShareNext } from "@/share/share-next"
import * as Effect from "effect/Effect"
diff --git a/packages/opencode/src/project/instance.ts b/packages/opencode/src/project/instance.ts
index 2a20ecac9..a8a521875 100644
--- a/packages/opencode/src/project/instance.ts
+++ b/packages/opencode/src/project/instance.ts
@@ -3,8 +3,8 @@ import { disposeInstance } from "@/effect/instance-registry"
import { makeRuntime } from "@/effect/run-service"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { iife } from "@/util/iife"
-import { Log } from "@/util/log"
-import { LocalContext } from "../util/local-context"
+import { Log } from "@/util"
+import { LocalContext } from "../util"
import { Project } from "./project"
import { WorkspaceContext } from "@/control-plane/workspace-context"
diff --git a/packages/opencode/src/project/project.ts b/packages/opencode/src/project/project.ts
index d20bf4249..9c4ed58ce 100644
--- a/packages/opencode/src/project/project.ts
+++ b/packages/opencode/src/project/project.ts
@@ -2,7 +2,7 @@ import z from "zod"
import { and, Database, eq } from "../storage/db"
import { ProjectTable } from "./project.sql"
import { SessionTable } from "../session/session.sql"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { Flag } from "@/flag/flag"
import { BusEvent } from "@/bus/bus-event"
import { GlobalBus } from "@/bus/global"
diff --git a/packages/opencode/src/project/vcs.ts b/packages/opencode/src/project/vcs.ts
index 187c61660..cb0b46adc 100644
--- a/packages/opencode/src/project/vcs.ts
+++ b/packages/opencode/src/project/vcs.ts
@@ -7,7 +7,7 @@ import { InstanceState } from "@/effect"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { FileWatcher } from "@/file/watcher"
import { Git } from "@/git"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { Instance } from "./instance"
import z from "zod"
diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts
index 55f137aa0..59d629a37 100644
--- a/packages/opencode/src/provider/models.ts
+++ b/packages/opencode/src/provider/models.ts
@@ -1,11 +1,11 @@
import { Global } from "../global"
-import { Log } from "../util/log"
+import { Log } from "../util"
import path from "path"
import z from "zod"
import { Installation } from "../installation"
import { Flag } from "../flag/flag"
import { lazy } from "@/util/lazy"
-import { Filesystem } from "../util/filesystem"
+import { Filesystem } from "../util"
import { Flock } from "@opencode-ai/shared/util/flock"
import { Hash } from "@opencode-ai/shared/util/hash"
diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts
index ef6cbd61e..432dbab34 100644
--- a/packages/opencode/src/provider/provider.ts
+++ b/packages/opencode/src/provider/provider.ts
@@ -4,7 +4,7 @@ import fuzzysort from "fuzzysort"
import { Config } from "../config"
import { mapValues, mergeDeep, omit, pickBy, sortBy } from "remeda"
import { NoSuchModelError, type Provider as SDK } from "ai"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { Npm } from "../npm"
import { Hash } from "@opencode-ai/shared/util/hash"
import { Plugin } from "../plugin"
diff --git a/packages/opencode/src/pty/service.ts b/packages/opencode/src/pty/service.ts
index ff52095b4..a4ebd0696 100644
--- a/packages/opencode/src/pty/service.ts
+++ b/packages/opencode/src/pty/service.ts
@@ -4,7 +4,7 @@ import { InstanceState } from "@/effect"
import { Instance } from "@/project/instance"
import type { Proc } from "#pty"
import z from "zod"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { lazy } from "@opencode-ai/shared/util/lazy"
import { Shell } from "@/shell/shell"
import { Plugin } from "@/plugin"
diff --git a/packages/opencode/src/question/index.ts b/packages/opencode/src/question/index.ts
index 8d023c18b..627d04564 100644
--- a/packages/opencode/src/question/index.ts
+++ b/packages/opencode/src/question/index.ts
@@ -4,7 +4,7 @@ import { BusEvent } from "@/bus/bus-event"
import { InstanceState } from "@/effect"
import { SessionID, MessageID } from "@/session/schema"
import { zod } from "@/util/effect-zod"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { withStatics } from "@/util/schema"
import { QuestionID } from "./schema"
diff --git a/packages/opencode/src/server/control/index.ts b/packages/opencode/src/server/control/index.ts
index cf8949c95..737f958d6 100644
--- a/packages/opencode/src/server/control/index.ts
+++ b/packages/opencode/src/server/control/index.ts
@@ -1,6 +1,6 @@
import { Auth } from "@/auth"
import { AppRuntime } from "@/effect/app-runtime"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { Effect } from "effect"
import { ProviderID } from "@/provider/schema"
import { Hono } from "hono"
diff --git a/packages/opencode/src/server/fence.ts b/packages/opencode/src/server/fence.ts
index b6dbde008..87771745c 100644
--- a/packages/opencode/src/server/fence.ts
+++ b/packages/opencode/src/server/fence.ts
@@ -3,7 +3,7 @@ import { Database, inArray } from "@/storage/db"
import { EventSequenceTable } from "@/sync/event.sql"
import { Workspace } from "@/control-plane/workspace"
import type { WorkspaceID } from "@/control-plane/schema"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
const HEADER = "x-opencode-sync"
type State = Record<string, number>
diff --git a/packages/opencode/src/server/instance/event.ts b/packages/opencode/src/server/instance/event.ts
index f13ed035e..103d3d7cf 100644
--- a/packages/opencode/src/server/instance/event.ts
+++ b/packages/opencode/src/server/instance/event.ts
@@ -2,7 +2,7 @@ import z from "zod"
import { Hono } from "hono"
import { describeRoute, resolver } from "hono-openapi"
import { streamSSE } from "hono/streaming"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { BusEvent } from "@/bus/bus-event"
import { Bus } from "@/bus"
import { AsyncQueue } from "../../util/queue"
diff --git a/packages/opencode/src/server/instance/global.ts b/packages/opencode/src/server/instance/global.ts
index b69f35a64..ac73bb64d 100644
--- a/packages/opencode/src/server/instance/global.ts
+++ b/packages/opencode/src/server/instance/global.ts
@@ -10,7 +10,7 @@ import { AppRuntime } from "@/effect/app-runtime"
import { AsyncQueue } from "@/util/queue"
import { Instance } from "../../project/instance"
import { Installation } from "@/installation"
-import { Log } from "../../util/log"
+import { Log } from "../../util"
import { lazy } from "../../util/lazy"
import { Config } from "../../config"
import { errors } from "../error"
diff --git a/packages/opencode/src/server/instance/httpapi/server.ts b/packages/opencode/src/server/instance/httpapi/server.ts
index 9ecefe291..62ffb5940 100644
--- a/packages/opencode/src/server/instance/httpapi/server.ts
+++ b/packages/opencode/src/server/instance/httpapi/server.ts
@@ -9,7 +9,7 @@ import { Flag } from "@/flag/flag"
import { InstanceBootstrap } from "@/project/bootstrap"
import { Instance } from "@/project/instance"
import { lazy } from "@/util/lazy"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { PermissionApi, permissionHandlers } from "./permission"
import { ProviderApi, providerHandlers } from "./provider"
import { QuestionApi, questionHandlers } from "./question"
diff --git a/packages/opencode/src/server/instance/middleware.ts b/packages/opencode/src/server/instance/middleware.ts
index 5fd1fc25e..7b66072c2 100644
--- a/packages/opencode/src/server/instance/middleware.ts
+++ b/packages/opencode/src/server/instance/middleware.ts
@@ -11,7 +11,7 @@ import { Session } from "@/session"
import { SessionID } from "@/session/schema"
import { WorkspaceContext } from "@/control-plane/workspace-context"
import { AppRuntime } from "@/effect/app-runtime"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
type Rule = { method?: string; path: string; exact?: boolean; action: "local" | "forward" }
diff --git a/packages/opencode/src/server/instance/session.ts b/packages/opencode/src/server/instance/session.ts
index c606af854..1b2755fb8 100644
--- a/packages/opencode/src/server/instance/session.ts
+++ b/packages/opencode/src/server/instance/session.ts
@@ -18,7 +18,7 @@ import { AppRuntime } from "../../effect/app-runtime"
import { Agent } from "../../agent/agent"
import { Snapshot } from "@/snapshot"
import { Command } from "../../command"
-import { Log } from "../../util/log"
+import { Log } from "../../util"
import { Permission } from "@/permission"
import { PermissionID } from "@/permission/schema"
import { ModelID, ProviderID } from "@/provider/schema"
diff --git a/packages/opencode/src/server/instance/sync.ts b/packages/opencode/src/server/instance/sync.ts
index c22969130..2513e519e 100644
--- a/packages/opencode/src/server/instance/sync.ts
+++ b/packages/opencode/src/server/instance/sync.ts
@@ -5,7 +5,7 @@ import { SyncEvent } from "@/sync"
import { Database, asc, and, not, or, lte, eq } from "@/storage/db"
import { EventTable } from "@/sync/event.sql"
import { lazy } from "@/util/lazy"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { errors } from "../error"
const ReplayEvent = z.object({
diff --git a/packages/opencode/src/server/instance/workspace.ts b/packages/opencode/src/server/instance/workspace.ts
index a4ff4eda8..59369ef8e 100644
--- a/packages/opencode/src/server/instance/workspace.ts
+++ b/packages/opencode/src/server/instance/workspace.ts
@@ -6,7 +6,7 @@ import { Workspace } from "../../control-plane/workspace"
import { Instance } from "../../project/instance"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { errorData } from "@/util/error"
const log = Log.create({ service: "server.workspace" })
diff --git a/packages/opencode/src/server/mdns.ts b/packages/opencode/src/server/mdns.ts
index 778afa26a..2011771a2 100644
--- a/packages/opencode/src/server/mdns.ts
+++ b/packages/opencode/src/server/mdns.ts
@@ -1,4 +1,4 @@
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { Bonjour } from "bonjour-service"
const log = Log.create({ service: "mdns" })
diff --git a/packages/opencode/src/server/middleware.ts b/packages/opencode/src/server/middleware.ts
index 880c432c7..e0958196a 100644
--- a/packages/opencode/src/server/middleware.ts
+++ b/packages/opencode/src/server/middleware.ts
@@ -5,7 +5,7 @@ import { Session } from "../session"
import type { ContentfulStatusCode } from "hono/utils/http-status"
import type { ErrorHandler, MiddlewareHandler } from "hono"
import { HTTPException } from "hono/http-exception"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { Flag } from "@/flag/flag"
import { basicAuth } from "hono/basic-auth"
import { cors } from "hono/cors"
diff --git a/packages/opencode/src/server/proxy.ts b/packages/opencode/src/server/proxy.ts
index 5effa5d05..07edcc2bb 100644
--- a/packages/opencode/src/server/proxy.ts
+++ b/packages/opencode/src/server/proxy.ts
@@ -1,6 +1,6 @@
import { Hono } from "hono"
import type { UpgradeWebSocket } from "hono/ws"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import * as Fence from "./fence"
import type { WorkspaceID } from "@/control-plane/schema"
import { Workspace } from "@/control-plane/workspace"
diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts
index c6c37ee43..fc3b399f7 100644
--- a/packages/opencode/src/server/server.ts
+++ b/packages/opencode/src/server/server.ts
@@ -7,7 +7,7 @@ import { AuthMiddleware, CompressionMiddleware, CorsMiddleware, ErrorMiddleware,
import { FenceMiddleware } from "./fence"
import { InstanceRoutes } from "./instance"
import { initProjectors } from "./projectors"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { Flag } from "@/flag/flag"
import { ControlPlaneRoutes } from "./control"
import { UIRoutes } from "./ui"
diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts
index 3d39a6055..72b996321 100644
--- a/packages/opencode/src/session/compaction.ts
+++ b/packages/opencode/src/session/compaction.ts
@@ -5,8 +5,8 @@ import { SessionID, MessageID, PartID } from "./schema"
import { Provider } from "../provider"
import { MessageV2 } from "./message-v2"
import z from "zod"
-import { Token } from "../util/token"
-import { Log } from "../util/log"
+import { Token } from "../util"
+import { Log } from "../util"
import { SessionProcessor } from "./processor"
import { Agent } from "@/agent/agent"
import { Plugin } from "@/plugin"
diff --git a/packages/opencode/src/session/instruction.ts b/packages/opencode/src/session/instruction.ts
index 076c81ec7..cd2050adf 100644
--- a/packages/opencode/src/session/instruction.ts
+++ b/packages/opencode/src/session/instruction.ts
@@ -9,7 +9,7 @@ import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { withTransientReadRetry } from "@/util/effect-http-client"
import { Global } from "../global"
import { Instance } from "../project/instance"
-import { Log } from "../util/log"
+import { Log } from "../util"
import type { MessageV2 } from "./message-v2"
import type { MessageID } from "./schema"
diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts
index bde36d263..8f93bd5e1 100644
--- a/packages/opencode/src/session/llm.ts
+++ b/packages/opencode/src/session/llm.ts
@@ -1,5 +1,5 @@
import { Provider } from "@/provider"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { Context, Effect, Layer, Record } from "effect"
import * as Stream from "effect/Stream"
import { streamText, wrapLanguageModel, type ModelMessage, type Tool, tool, jsonSchema } from "ai"
@@ -16,7 +16,7 @@ import { Flag } from "@/flag/flag"
import { Permission } from "@/permission"
import { PermissionID } from "@/permission/schema"
import { Bus } from "@/bus"
-import { Wildcard } from "@/util/wildcard"
+import { Wildcard } from "@/util"
import { SessionID } from "@/session/schema"
import { Auth } from "@/auth"
import { Installation } from "@/installation"
diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts
index 1ae70c3c6..72b27403b 100644
--- a/packages/opencode/src/session/processor.ts
+++ b/packages/opencode/src/session/processor.ts
@@ -18,7 +18,7 @@ import { SessionSummary } from "./summary"
import type { Provider } from "@/provider"
import { Question } from "@/question"
import { errorMessage } from "@/util/error"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { isRecord } from "@/util/record"
export namespace SessionProcessor {
diff --git a/packages/opencode/src/session/projectors.ts b/packages/opencode/src/session/projectors.ts
index bc083105c..1e092b07e 100644
--- a/packages/opencode/src/session/projectors.ts
+++ b/packages/opencode/src/session/projectors.ts
@@ -3,7 +3,7 @@ import { SyncEvent } from "@/sync"
import { Session } from "."
import { MessageV2 } from "./message-v2"
import { SessionTable, MessageTable, PartTable } from "./session.sql"
-import { Log } from "../util/log"
+import { Log } from "../util"
const log = Log.create({ service: "session.projector" })
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index f04ea8cde..a072633aa 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -3,7 +3,7 @@ import os from "os"
import z from "zod"
import { SessionID, MessageID, PartID } from "./schema"
import { MessageV2 } from "./message-v2"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { SessionRevert } from "./revert"
import { Session } from "."
import { Agent } from "../agent/agent"
@@ -42,7 +42,7 @@ import { Shell } from "@/shell/shell"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Truncate } from "@/tool/truncate"
import { decodeDataUrl } from "@/util/data-url"
-import { Process } from "@/util/process"
+import { Process } from "@/util"
import { Cause, Effect, Exit, Layer, Option, Scope, Context } from "effect"
import { EffectLogger } from "@/effect/logger"
import { InstanceState } from "@/effect"
diff --git a/packages/opencode/src/session/revert.ts b/packages/opencode/src/session/revert.ts
index 7a7f847ad..383fe08e8 100644
--- a/packages/opencode/src/session/revert.ts
+++ b/packages/opencode/src/session/revert.ts
@@ -4,7 +4,7 @@ import { Bus } from "../bus"
import { Snapshot } from "../snapshot"
import { Storage } from "@/storage/storage"
import { SyncEvent } from "../sync"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { Session } from "."
import { MessageV2 } from "./message-v2"
import { SessionID, MessageID, PartID } from "./schema"
diff --git a/packages/opencode/src/session/session.ts b/packages/opencode/src/session/session.ts
index 0b82d8b99..a4bf446a1 100644
--- a/packages/opencode/src/session/session.ts
+++ b/packages/opencode/src/session/session.ts
@@ -14,7 +14,7 @@ import type { SQL } from "../storage/db"
import { PartTable, SessionTable } from "./session.sql"
import { ProjectTable } from "../project/project.sql"
import { Storage } from "@/storage/storage"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { updateSchema } from "../util/update-schema"
import { MessageV2 } from "./message-v2"
import { Instance } from "../project/instance"
diff --git a/packages/opencode/src/share/share-next.ts b/packages/opencode/src/share/share-next.ts
index 9b345ac8e..bcb1fcc96 100644
--- a/packages/opencode/src/share/share-next.ts
+++ b/packages/opencode/src/share/share-next.ts
@@ -11,7 +11,7 @@ import { MessageV2 } from "@/session/message-v2"
import type { SessionID } from "@/session/schema"
import { Database, eq } from "@/storage/db"
import { Config } from "@/config"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { SessionShareTable } from "./share.sql"
export namespace ShareNext {
diff --git a/packages/opencode/src/shell/shell.ts b/packages/opencode/src/shell/shell.ts
index 0044dda89..056a794dc 100644
--- a/packages/opencode/src/shell/shell.ts
+++ b/packages/opencode/src/shell/shell.ts
@@ -1,6 +1,6 @@
import { Flag } from "@/flag/flag"
import { lazy } from "@/util/lazy"
-import { Filesystem } from "@/util/filesystem"
+import { Filesystem } from "@/util"
import { which } from "@/util/which"
import path from "path"
import { spawn, type ChildProcess } from "child_process"
diff --git a/packages/opencode/src/skill/discovery.ts b/packages/opencode/src/skill/discovery.ts
index 0323f250f..eff64ed2b 100644
--- a/packages/opencode/src/skill/discovery.ts
+++ b/packages/opencode/src/skill/discovery.ts
@@ -4,7 +4,7 @@ import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } fr
import { withTransientReadRetry } from "@/util/effect-http-client"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Global } from "../global"
-import { Log } from "../util/log"
+import { Log } from "../util"
export namespace Discovery {
const skillConcurrency = 4
diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts
index 3122115cd..ef9f661cb 100644
--- a/packages/opencode/src/skill/skill.ts
+++ b/packages/opencode/src/skill/skill.ts
@@ -14,7 +14,7 @@ import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Config } from "../config"
import { ConfigMarkdown } from "../config/markdown"
import { Glob } from "@opencode-ai/shared/util/glob"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { Discovery } from "./discovery"
const log = Log.create({ service: "skill" })
diff --git a/packages/opencode/src/snapshot/snapshot.ts b/packages/opencode/src/snapshot/snapshot.ts
index 7aa3a4deb..8d8118131 100644
--- a/packages/opencode/src/snapshot/snapshot.ts
+++ b/packages/opencode/src/snapshot/snapshot.ts
@@ -9,7 +9,7 @@ import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Hash } from "@opencode-ai/shared/util/hash"
import { Config } from "../config"
import { Global } from "../global"
-import { Log } from "../util/log"
+import { Log } from "../util"
export const Patch = z.object({
hash: z.string(),
diff --git a/packages/opencode/src/storage/db.ts b/packages/opencode/src/storage/db.ts
index 247cb347c..ee53182f3 100644
--- a/packages/opencode/src/storage/db.ts
+++ b/packages/opencode/src/storage/db.ts
@@ -2,10 +2,10 @@ import { type SQLiteBunDatabase } from "drizzle-orm/bun-sqlite"
import { migrate } from "drizzle-orm/bun-sqlite/migrator"
import { type SQLiteTransaction } from "drizzle-orm/sqlite-core"
export * from "drizzle-orm"
-import { LocalContext } from "../util/local-context"
+import { LocalContext } from "../util"
import { lazy } from "../util/lazy"
import { Global } from "../global"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { NamedError } from "@opencode-ai/shared/util/error"
import z from "zod"
import path from "path"
diff --git a/packages/opencode/src/storage/json-migration.ts b/packages/opencode/src/storage/json-migration.ts
index c13a005ca..4bf75f5a1 100644
--- a/packages/opencode/src/storage/json-migration.ts
+++ b/packages/opencode/src/storage/json-migration.ts
@@ -1,13 +1,13 @@
import type { SQLiteBunDatabase } from "drizzle-orm/bun-sqlite"
import type { NodeSQLiteDatabase } from "drizzle-orm/node-sqlite"
import { Global } from "../global"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { ProjectTable } from "../project/project.sql"
import { SessionTable, MessageTable, PartTable, TodoTable, PermissionTable } from "../session/session.sql"
import { SessionShareTable } from "../share/share.sql"
import path from "path"
import { existsSync } from "fs"
-import { Filesystem } from "../util/filesystem"
+import { Filesystem } from "../util"
import { Glob } from "@opencode-ai/shared/util/glob"
export namespace JsonMigration {
diff --git a/packages/opencode/src/storage/storage.ts b/packages/opencode/src/storage/storage.ts
index 359c750ce..f4793c620 100644
--- a/packages/opencode/src/storage/storage.ts
+++ b/packages/opencode/src/storage/storage.ts
@@ -1,4 +1,4 @@
-import { Log } from "../util/log"
+import { Log } from "../util"
import path from "path"
import { Global } from "../global"
import { NamedError } from "@opencode-ai/shared/util/error"
diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts
index 0ab130130..1edd75414 100644
--- a/packages/opencode/src/tool/bash.ts
+++ b/packages/opencode/src/tool/bash.ts
@@ -4,7 +4,7 @@ import { createWriteStream } from "node:fs"
import { Tool } from "./tool"
import path from "path"
import DESCRIPTION from "./bash.txt"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { Instance } from "../project/instance"
import { lazy } from "@/util/lazy"
import { Language, type Node } from "web-tree-sitter"
diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts
index 6171e4366..80115884d 100644
--- a/packages/opencode/src/tool/registry.ts
+++ b/packages/opencode/src/tool/registry.ts
@@ -22,7 +22,7 @@ import { ProviderID, type ModelID } from "../provider/schema"
import { WebSearchTool } from "./websearch"
import { CodeSearchTool } from "./codesearch"
import { Flag } from "@/flag/flag"
-import { Log } from "@/util/log"
+import { Log } from "@/util"
import { LspTool } from "./lsp"
import { Truncate } from "./truncate"
import { ApplyPatchTool } from "./apply_patch"
diff --git a/packages/opencode/src/tool/truncate.ts b/packages/opencode/src/tool/truncate.ts
index d607e22f2..d2aa944a8 100644
--- a/packages/opencode/src/tool/truncate.ts
+++ b/packages/opencode/src/tool/truncate.ts
@@ -5,7 +5,7 @@ import type { Agent } from "../agent/agent"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { evaluate } from "@/permission/evaluate"
import { Identifier } from "../id/id"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { ToolID } from "./schema"
import { TRUNCATION_DIR } from "./truncation-dir"
diff --git a/packages/opencode/src/util/archive.ts b/packages/opencode/src/util/archive.ts
index cf2563684..21d014c6a 100644
--- a/packages/opencode/src/util/archive.ts
+++ b/packages/opencode/src/util/archive.ts
@@ -1,5 +1,5 @@
import path from "path"
-import { Process } from "./process"
+import { Process } from "."
export async function extractZip(zipPath: string, destDir: string) {
if (process.platform === "win32") {
diff --git a/packages/opencode/src/util/color.ts b/packages/opencode/src/util/color.ts
index b96deaec4..43408295f 100644
--- a/packages/opencode/src/util/color.ts
+++ b/packages/opencode/src/util/color.ts
@@ -1,19 +1,17 @@
-export namespace Color {
- export function isValidHex(hex?: string): hex is string {
- if (!hex) return false
- return /^#[0-9a-fA-F]{6}$/.test(hex)
- }
+export function isValidHex(hex?: string): hex is string {
+ if (!hex) return false
+ return /^#[0-9a-fA-F]{6}$/.test(hex)
+}
- export function hexToRgb(hex: string): { r: number; g: number; b: number } {
- const r = parseInt(hex.slice(1, 3), 16)
- const g = parseInt(hex.slice(3, 5), 16)
- const b = parseInt(hex.slice(5, 7), 16)
- return { r, g, b }
- }
+export function hexToRgb(hex: string): { r: number; g: number; b: number } {
+ const r = parseInt(hex.slice(1, 3), 16)
+ const g = parseInt(hex.slice(3, 5), 16)
+ const b = parseInt(hex.slice(5, 7), 16)
+ return { r, g, b }
+}
- export function hexToAnsiBold(hex?: string): string | undefined {
- if (!isValidHex(hex)) return undefined
- const { r, g, b } = hexToRgb(hex)
- return `\x1b[38;2;${r};${g};${b}m\x1b[1m`
- }
+export function hexToAnsiBold(hex?: string): string | undefined {
+ if (!isValidHex(hex)) return undefined
+ const { r, g, b } = hexToRgb(hex)
+ return `\x1b[38;2;${r};${g};${b}m\x1b[1m`
}
diff --git a/packages/opencode/src/util/filesystem.ts b/packages/opencode/src/util/filesystem.ts
index b4aef0545..c3f59d329 100644
--- a/packages/opencode/src/util/filesystem.ts
+++ b/packages/opencode/src/util/filesystem.ts
@@ -7,239 +7,237 @@ import { Readable } from "stream"
import { pipeline } from "stream/promises"
import { Glob } from "@opencode-ai/shared/util/glob"
-export namespace Filesystem {
- // Fast sync version for metadata checks
- export async function exists(p: string): Promise<boolean> {
- return existsSync(p)
- }
+// Fast sync version for metadata checks
+export async function exists(p: string): Promise<boolean> {
+ return existsSync(p)
+}
- export async function isDir(p: string): Promise<boolean> {
- try {
- return statSync(p).isDirectory()
- } catch {
- return false
- }
+export async function isDir(p: string): Promise<boolean> {
+ try {
+ return statSync(p).isDirectory()
+ } catch {
+ return false
}
+}
- export function stat(p: string): ReturnType<typeof statSync> | undefined {
- return statSync(p, { throwIfNoEntry: false }) ?? undefined
- }
+export function stat(p: string): ReturnType<typeof statSync> | undefined {
+ return statSync(p, { throwIfNoEntry: false }) ?? undefined
+}
- export async function statAsync(p: string): Promise<ReturnType<typeof statSync> | undefined> {
- return statFile(p).catch((e) => {
- if (isEnoent(e)) return undefined
- throw e
- })
- }
+export async function statAsync(p: string): Promise<ReturnType<typeof statSync> | undefined> {
+ return statFile(p).catch((e) => {
+ if (isEnoent(e)) return undefined
+ throw e
+ })
+}
- export async function size(p: string): Promise<number> {
- const s = stat(p)?.size ?? 0
- return typeof s === "bigint" ? Number(s) : s
- }
+export async function size(p: string): Promise<number> {
+ const s = stat(p)?.size ?? 0
+ return typeof s === "bigint" ? Number(s) : s
+}
- export async function readText(p: string): Promise<string> {
- return readFile(p, "utf-8")
- }
+export async function readText(p: string): Promise<string> {
+ return readFile(p, "utf-8")
+}
- export async function readJson<T = any>(p: string): Promise<T> {
- return JSON.parse(await readFile(p, "utf-8"))
- }
+export async function readJson<T = any>(p: string): Promise<T> {
+ return JSON.parse(await readFile(p, "utf-8"))
+}
- export async function readBytes(p: string): Promise<Buffer> {
- return readFile(p)
- }
+export async function readBytes(p: string): Promise<Buffer> {
+ return readFile(p)
+}
- export async function readArrayBuffer(p: string): Promise<ArrayBuffer> {
- const buf = await readFile(p)
- return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer
- }
+export async function readArrayBuffer(p: string): Promise<ArrayBuffer> {
+ const buf = await readFile(p)
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer
+}
- function isEnoent(e: unknown): e is { code: "ENOENT" } {
- return typeof e === "object" && e !== null && "code" in e && (e as { code: string }).code === "ENOENT"
- }
+function isEnoent(e: unknown): e is { code: "ENOENT" } {
+ return typeof e === "object" && e !== null && "code" in e && (e as { code: string }).code === "ENOENT"
+}
- export async function write(p: string, content: string | Buffer | Uint8Array, mode?: number): Promise<void> {
- try {
+export async function write(p: string, content: string | Buffer | Uint8Array, mode?: number): Promise<void> {
+ try {
+ if (mode) {
+ await writeFile(p, content, { mode })
+ } else {
+ await writeFile(p, content)
+ }
+ } catch (e) {
+ if (isEnoent(e)) {
+ await mkdir(dirname(p), { recursive: true })
if (mode) {
await writeFile(p, content, { mode })
} else {
await writeFile(p, content)
}
- } catch (e) {
- if (isEnoent(e)) {
- await mkdir(dirname(p), { recursive: true })
- if (mode) {
- await writeFile(p, content, { mode })
- } else {
- await writeFile(p, content)
- }
- return
- }
- throw e
+ return
}
+ throw e
}
+}
- export async function writeJson(p: string, data: unknown, mode?: number): Promise<void> {
- return write(p, JSON.stringify(data, null, 2), mode)
- }
-
- export async function writeStream(
- p: string,
- stream: ReadableStream<Uint8Array> | Readable,
- mode?: number,
- ): Promise<void> {
- const dir = dirname(p)
- if (!existsSync(dir)) {
- await mkdir(dir, { recursive: true })
- }
-
- const nodeStream = stream instanceof ReadableStream ? Readable.fromWeb(stream as any) : stream
- const writeStream = createWriteStream(p)
- await pipeline(nodeStream, writeStream)
+export async function writeJson(p: string, data: unknown, mode?: number): Promise<void> {
+ return write(p, JSON.stringify(data, null, 2), mode)
+}
- if (mode) {
- await chmod(p, mode)
- }
+export async function writeStream(
+ p: string,
+ stream: ReadableStream<Uint8Array> | Readable,
+ mode?: number,
+): Promise<void> {
+ const dir = dirname(p)
+ if (!existsSync(dir)) {
+ await mkdir(dir, { recursive: true })
}
- export function mimeType(p: string): string {
- return lookup(p) || "application/octet-stream"
- }
+ const nodeStream = stream instanceof ReadableStream ? Readable.fromWeb(stream as any) : stream
+ const writeStream = createWriteStream(p)
+ await pipeline(nodeStream, writeStream)
- /**
- * On Windows, normalize a path to its canonical casing using the filesystem.
- * This is needed because Windows paths are case-insensitive but LSP servers
- * may return paths with different casing than what we send them.
- */
- export function normalizePath(p: string): string {
- if (process.platform !== "win32") return p
- const resolved = win32.normalize(win32.resolve(windowsPath(p)))
- try {
- return realpathSync.native(resolved)
- } catch {
- return resolved
- }
+ if (mode) {
+ await chmod(p, mode)
}
+}
- export function normalizePathPattern(p: string): string {
- if (process.platform !== "win32") return p
- if (p === "*") return p
- const match = p.match(/^(.*)[\\/]\*$/)
- if (!match) return normalizePath(p)
- const dir = /^[A-Za-z]:$/.test(match[1]) ? match[1] + "\\" : match[1]
- return join(normalizePath(dir), "*")
- }
+export function mimeType(p: string): string {
+ return lookup(p) || "application/octet-stream"
+}
- // We cannot rely on path.resolve() here because git.exe may come from Git Bash, Cygwin, or MSYS2, so we need to translate these paths at the boundary.
- // Also resolves symlinks so that callers using the result as a cache key
- // always get the same canonical path for a given physical directory.
- export function resolve(p: string): string {
- const resolved = pathResolve(windowsPath(p))
- try {
- return normalizePath(realpathSync(resolved))
- } catch (e) {
- if (isEnoent(e)) return normalizePath(resolved)
- throw e
- }
+/**
+ * On Windows, normalize a path to its canonical casing using the filesystem.
+ * This is needed because Windows paths are case-insensitive but LSP servers
+ * may return paths with different casing than what we send them.
+ */
+export function normalizePath(p: string): string {
+ if (process.platform !== "win32") return p
+ const resolved = win32.normalize(win32.resolve(windowsPath(p)))
+ try {
+ return realpathSync.native(resolved)
+ } catch {
+ return resolved
}
+}
- export function windowsPath(p: string): string {
- if (process.platform !== "win32") return p
- return (
- p
- .replace(/^\/([a-zA-Z]):(?:[\\/]|$)/, (_, drive) => `${drive.toUpperCase()}:/`)
- // Git Bash for Windows paths are typically /<drive>/...
- .replace(/^\/([a-zA-Z])(?:\/|$)/, (_, drive) => `${drive.toUpperCase()}:/`)
- // Cygwin git paths are typically /cygdrive/<drive>/...
- .replace(/^\/cygdrive\/([a-zA-Z])(?:\/|$)/, (_, drive) => `${drive.toUpperCase()}:/`)
- // WSL paths are typically /mnt/<drive>/...
- .replace(/^\/mnt\/([a-zA-Z])(?:\/|$)/, (_, drive) => `${drive.toUpperCase()}:/`)
- )
- }
- export function overlaps(a: string, b: string) {
- const relA = relative(a, b)
- const relB = relative(b, a)
- return !relA || !relA.startsWith("..") || !relB || !relB.startsWith("..")
- }
+export function normalizePathPattern(p: string): string {
+ if (process.platform !== "win32") return p
+ if (p === "*") return p
+ const match = p.match(/^(.*)[\\/]\*$/)
+ if (!match) return normalizePath(p)
+ const dir = /^[A-Za-z]:$/.test(match[1]) ? match[1] + "\\" : match[1]
+ return join(normalizePath(dir), "*")
+}
- export function contains(parent: string, child: string) {
- return !relative(parent, child).startsWith("..")
+// We cannot rely on path.resolve() here because git.exe may come from Git Bash, Cygwin, or MSYS2, so we need to translate these paths at the boundary.
+// Also resolves symlinks so that callers using the result as a cache key
+// always get the same canonical path for a given physical directory.
+export function resolve(p: string): string {
+ const resolved = pathResolve(windowsPath(p))
+ try {
+ return normalizePath(realpathSync(resolved))
+ } catch (e) {
+ if (isEnoent(e)) return normalizePath(resolved)
+ throw e
}
+}
- export async function findUp(
- target: string,
- start: string,
- stop?: string,
- options?: { rootFirst?: boolean },
- ): Promise<string[]>
- export async function findUp(
- target: string[],
- start: string,
- stop?: string,
- options?: { rootFirst?: boolean },
- ): Promise<string[]>
- export async function findUp(
- target: string | string[],
- start: string,
- stop?: string,
- options?: { rootFirst?: boolean },
- ) {
- const dirs = [start]
- let current = start
- while (true) {
- if (stop === current) break
- const parent = dirname(current)
- if (parent === current) break
- dirs.push(parent)
- current = parent
- }
+export function windowsPath(p: string): string {
+ if (process.platform !== "win32") return p
+ return (
+ p
+ .replace(/^\/([a-zA-Z]):(?:[\\/]|$)/, (_, drive) => `${drive.toUpperCase()}:/`)
+ // Git Bash for Windows paths are typically /<drive>/...
+ .replace(/^\/([a-zA-Z])(?:\/|$)/, (_, drive) => `${drive.toUpperCase()}:/`)
+ // Cygwin git paths are typically /cygdrive/<drive>/...
+ .replace(/^\/cygdrive\/([a-zA-Z])(?:\/|$)/, (_, drive) => `${drive.toUpperCase()}:/`)
+ // WSL paths are typically /mnt/<drive>/...
+ .replace(/^\/mnt\/([a-zA-Z])(?:\/|$)/, (_, drive) => `${drive.toUpperCase()}:/`)
+ )
+}
+export function overlaps(a: string, b: string) {
+ const relA = relative(a, b)
+ const relB = relative(b, a)
+ return !relA || !relA.startsWith("..") || !relB || !relB.startsWith("..")
+}
- const targets = Array.isArray(target) ? target : [target]
- const result = []
- for (const dir of options?.rootFirst ? dirs.toReversed() : dirs) {
- for (const item of targets) {
- const search = join(dir, item)
- if (await exists(search)) result.push(search)
- }
+export function contains(parent: string, child: string) {
+ return !relative(parent, child).startsWith("..")
+}
+
+export async function findUp(
+ target: string,
+ start: string,
+ stop?: string,
+ options?: { rootFirst?: boolean },
+): Promise<string[]>
+export async function findUp(
+ target: string[],
+ start: string,
+ stop?: string,
+ options?: { rootFirst?: boolean },
+): Promise<string[]>
+export async function findUp(
+ target: string | string[],
+ start: string,
+ stop?: string,
+ options?: { rootFirst?: boolean },
+) {
+ const dirs = [start]
+ let current = start
+ while (true) {
+ if (stop === current) break
+ const parent = dirname(current)
+ if (parent === current) break
+ dirs.push(parent)
+ current = parent
+ }
+
+ const targets = Array.isArray(target) ? target : [target]
+ const result = []
+ for (const dir of options?.rootFirst ? dirs.toReversed() : dirs) {
+ for (const item of targets) {
+ const search = join(dir, item)
+ if (await exists(search)) result.push(search)
}
- return result
}
+ return result
+}
- export async function* up(options: { targets: string[]; start: string; stop?: string }) {
- const { targets, start, stop } = options
- let current = start
- while (true) {
- for (const target of targets) {
- const search = join(current, target)
- if (await exists(search)) yield search
- }
- if (stop === current) break
- const parent = dirname(current)
- if (parent === current) break
- current = parent
+export async function* up(options: { targets: string[]; start: string; stop?: string }) {
+ const { targets, start, stop } = options
+ let current = start
+ while (true) {
+ for (const target of targets) {
+ const search = join(current, target)
+ if (await exists(search)) yield search
}
+ if (stop === current) break
+ const parent = dirname(current)
+ if (parent === current) break
+ current = parent
}
+}
- export async function globUp(pattern: string, start: string, stop?: string) {
- let current = start
- const result = []
- while (true) {
- try {
- const matches = await Glob.scan(pattern, {
- cwd: current,
- absolute: true,
- include: "file",
- dot: true,
- })
- result.push(...matches)
- } catch {
- // Skip invalid glob patterns
- }
- if (stop === current) break
- const parent = dirname(current)
- if (parent === current) break
- current = parent
+export async function globUp(pattern: string, start: string, stop?: string) {
+ let current = start
+ const result = []
+ while (true) {
+ try {
+ const matches = await Glob.scan(pattern, {
+ cwd: current,
+ absolute: true,
+ include: "file",
+ dot: true,
+ })
+ result.push(...matches)
+ } catch {
+ // Skip invalid glob patterns
}
- return result
+ if (stop === current) break
+ const parent = dirname(current)
+ if (parent === current) break
+ current = parent
}
+ return result
}
diff --git a/packages/opencode/src/util/index.ts b/packages/opencode/src/util/index.ts
index 157bb8e52..f051ad964 100644
--- a/packages/opencode/src/util/index.ts
+++ b/packages/opencode/src/util/index.ts
@@ -1 +1,12 @@
export * as Archive from "./archive"
+export * as Color from "./color"
+export * as Filesystem from "./filesystem"
+export * as Keybind from "./keybind"
+export * as LocalContext from "./local-context"
+export * as Locale from "./locale"
+export * as Lock from "./lock"
+export * as Log from "./log"
+export * as Process from "./process"
+export * as Rpc from "./rpc"
+export * as Token from "./token"
+export * as Wildcard from "./wildcard"
diff --git a/packages/opencode/src/util/keybind.ts b/packages/opencode/src/util/keybind.ts
index 83c7945ae..10a68c4b2 100644
--- a/packages/opencode/src/util/keybind.ts
+++ b/packages/opencode/src/util/keybind.ts
@@ -1,103 +1,101 @@
import { isDeepEqual } from "remeda"
import type { ParsedKey } from "@opentui/core"
-export namespace Keybind {
- /**
- * Keybind info derived from OpenTUI's ParsedKey with our custom `leader` field.
- * This ensures type compatibility and catches missing fields at compile time.
- */
- export type Info = Pick<ParsedKey, "name" | "ctrl" | "meta" | "shift" | "super"> & {
- leader: boolean // our custom field
- }
+/**
+ * Keybind info derived from OpenTUI's ParsedKey with our custom `leader` field.
+ * This ensures type compatibility and catches missing fields at compile time.
+ */
+export type Info = Pick<ParsedKey, "name" | "ctrl" | "meta" | "shift" | "super"> & {
+ leader: boolean // our custom field
+}
- export function match(a: Info | undefined, b: Info): boolean {
- if (!a) return false
- const normalizedA = { ...a, super: a.super ?? false }
- const normalizedB = { ...b, super: b.super ?? false }
- return isDeepEqual(normalizedA, normalizedB)
- }
+export function match(a: Info | undefined, b: Info): boolean {
+ if (!a) return false
+ const normalizedA = { ...a, super: a.super ?? false }
+ const normalizedB = { ...b, super: b.super ?? false }
+ return isDeepEqual(normalizedA, normalizedB)
+}
- /**
- * Convert OpenTUI's ParsedKey to our Keybind.Info format.
- * This helper ensures all required fields are present and avoids manual object creation.
- */
- export function fromParsedKey(key: ParsedKey, leader = false): Info {
- return {
- name: key.name === " " ? "space" : key.name,
- ctrl: key.ctrl,
- meta: key.meta,
- shift: key.shift,
- super: key.super ?? false,
- leader,
- }
+/**
+ * Convert OpenTUI's ParsedKey to our Keybind.Info format.
+ * This helper ensures all required fields are present and avoids manual object creation.
+ */
+export function fromParsedKey(key: ParsedKey, leader = false): Info {
+ return {
+ name: key.name === " " ? "space" : key.name,
+ ctrl: key.ctrl,
+ meta: key.meta,
+ shift: key.shift,
+ super: key.super ?? false,
+ leader,
}
+}
- export function toString(info: Info | undefined): string {
- if (!info) return ""
- const parts: string[] = []
-
- if (info.ctrl) parts.push("ctrl")
- if (info.meta) parts.push("alt")
- if (info.super) parts.push("super")
- if (info.shift) parts.push("shift")
- if (info.name) {
- if (info.name === "delete") parts.push("del")
- else parts.push(info.name)
- }
+export function toString(info: Info | undefined): string {
+ if (!info) return ""
+ const parts: string[] = []
- let result = parts.join("+")
+ if (info.ctrl) parts.push("ctrl")
+ if (info.meta) parts.push("alt")
+ if (info.super) parts.push("super")
+ if (info.shift) parts.push("shift")
+ if (info.name) {
+ if (info.name === "delete") parts.push("del")
+ else parts.push(info.name)
+ }
- if (info.leader) {
- result = result ? `<leader> ${result}` : `<leader>`
- }
+ let result = parts.join("+")
- return result
+ if (info.leader) {
+ result = result ? `<leader> ${result}` : `<leader>`
}
- export function parse(key: string): Info[] {
- if (key === "none") return []
+ return result
+}
- return key.split(",").map((combo) => {
- // Handle <leader> syntax by replacing with leader+
- const normalized = combo.replace(/<leader>/g, "leader+")
- const parts = normalized.toLowerCase().split("+")
- const info: Info = {
- ctrl: false,
- meta: false,
- shift: false,
- leader: false,
- name: "",
- }
+export function parse(key: string): Info[] {
+ if (key === "none") return []
- for (const part of parts) {
- switch (part) {
- case "ctrl":
- info.ctrl = true
- break
- case "alt":
- case "meta":
- case "option":
- info.meta = true
- break
- case "super":
- info.super = true
- break
- case "shift":
- info.shift = true
- break
- case "leader":
- info.leader = true
- break
- case "esc":
- info.name = "escape"
- break
- default:
- info.name = part
- break
- }
+ return key.split(",").map((combo) => {
+ // Handle <leader> syntax by replacing with leader+
+ const normalized = combo.replace(/<leader>/g, "leader+")
+ const parts = normalized.toLowerCase().split("+")
+ const info: Info = {
+ ctrl: false,
+ meta: false,
+ shift: false,
+ leader: false,
+ name: "",
+ }
+
+ for (const part of parts) {
+ switch (part) {
+ case "ctrl":
+ info.ctrl = true
+ break
+ case "alt":
+ case "meta":
+ case "option":
+ info.meta = true
+ break
+ case "super":
+ info.super = true
+ break
+ case "shift":
+ info.shift = true
+ break
+ case "leader":
+ info.leader = true
+ break
+ case "esc":
+ info.name = "escape"
+ break
+ default:
+ info.name = part
+ break
}
+ }
- return info
- })
- }
+ return info
+ })
}
diff --git a/packages/opencode/src/util/local-context.ts b/packages/opencode/src/util/local-context.ts
index 26f88ab09..c1aef946f 100644
--- a/packages/opencode/src/util/local-context.ts
+++ b/packages/opencode/src/util/local-context.ts
@@ -1,25 +1,23 @@
import { AsyncLocalStorage } from "async_hooks"
-export namespace LocalContext {
- export class NotFound extends Error {
- constructor(public override readonly name: string) {
- super(`No context found for ${name}`)
- }
+export class NotFound extends Error {
+ constructor(public override readonly name: string) {
+ super(`No context found for ${name}`)
}
+}
- export function create<T>(name: string) {
- const storage = new AsyncLocalStorage<T>()
- return {
- use() {
- const result = storage.getStore()
- if (!result) {
- throw new NotFound(name)
- }
- return result
- },
- provide<R>(value: T, fn: () => R) {
- return storage.run(value, fn)
- },
- }
+export function create<T>(name: string) {
+ const storage = new AsyncLocalStorage<T>()
+ return {
+ use() {
+ const result = storage.getStore()
+ if (!result) {
+ throw new NotFound(name)
+ }
+ return result
+ },
+ provide<R>(value: T, fn: () => R) {
+ return storage.run(value, fn)
+ },
}
}
diff --git a/packages/opencode/src/util/locale.ts b/packages/opencode/src/util/locale.ts
index 653da09a0..202e856b2 100644
--- a/packages/opencode/src/util/locale.ts
+++ b/packages/opencode/src/util/locale.ts
@@ -1,81 +1,79 @@
-export namespace Locale {
- export function titlecase(str: string) {
- return str.replace(/\b\w/g, (c) => c.toUpperCase())
- }
+export function titlecase(str: string) {
+ return str.replace(/\b\w/g, (c) => c.toUpperCase())
+}
- export function time(input: number): string {
- const date = new Date(input)
- return date.toLocaleTimeString(undefined, { timeStyle: "short" })
- }
+export function time(input: number): string {
+ const date = new Date(input)
+ return date.toLocaleTimeString(undefined, { timeStyle: "short" })
+}
- export function datetime(input: number): string {
- const date = new Date(input)
- const localTime = time(input)
- const localDate = date.toLocaleDateString()
- return `${localTime} · ${localDate}`
- }
+export function datetime(input: number): string {
+ const date = new Date(input)
+ const localTime = time(input)
+ const localDate = date.toLocaleDateString()
+ return `${localTime} · ${localDate}`
+}
- export function todayTimeOrDateTime(input: number): string {
- const date = new Date(input)
- const now = new Date()
- const isToday =
- date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate()
+export function todayTimeOrDateTime(input: number): string {
+ const date = new Date(input)
+ const now = new Date()
+ const isToday =
+ date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate()
- if (isToday) {
- return time(input)
- } else {
- return datetime(input)
- }
+ if (isToday) {
+ return time(input)
+ } else {
+ return datetime(input)
}
+}
- export function number(num: number): string {
- if (num >= 1000000) {
- return (num / 1000000).toFixed(1) + "M"
- } else if (num >= 1000) {
- return (num / 1000).toFixed(1) + "K"
- }
- return num.toString()
+export function number(num: number): string {
+ if (num >= 1000000) {
+ return (num / 1000000).toFixed(1) + "M"
+ } else if (num >= 1000) {
+ return (num / 1000).toFixed(1) + "K"
}
+ return num.toString()
+}
- export function duration(input: number) {
- if (input < 1000) {
- return `${input}ms`
- }
- if (input < 60000) {
- return `${(input / 1000).toFixed(1)}s`
- }
- if (input < 3600000) {
- const minutes = Math.floor(input / 60000)
- const seconds = Math.floor((input % 60000) / 1000)
- return `${minutes}m ${seconds}s`
- }
- if (input < 86400000) {
- const hours = Math.floor(input / 3600000)
- const minutes = Math.floor((input % 3600000) / 60000)
- return `${hours}h ${minutes}m`
- }
+export function duration(input: number) {
+ if (input < 1000) {
+ return `${input}ms`
+ }
+ if (input < 60000) {
+ return `${(input / 1000).toFixed(1)}s`
+ }
+ if (input < 3600000) {
+ const minutes = Math.floor(input / 60000)
+ const seconds = Math.floor((input % 60000) / 1000)
+ return `${minutes}m ${seconds}s`
+ }
+ if (input < 86400000) {
const hours = Math.floor(input / 3600000)
- const days = Math.floor((input % 3600000) / 86400000)
- return `${days}d ${hours}h`
+ const minutes = Math.floor((input % 3600000) / 60000)
+ return `${hours}h ${minutes}m`
}
+ const hours = Math.floor(input / 3600000)
+ const days = Math.floor((input % 3600000) / 86400000)
+ return `${days}d ${hours}h`
+}
- export function truncate(str: string, len: number): string {
- if (str.length <= len) return str
- return str.slice(0, len - 1) + "…"
- }
+export function truncate(str: string, len: number): string {
+ if (str.length <= len) return str
+ return str.slice(0, len - 1) + "…"
+}
- export function truncateMiddle(str: string, maxLength: number = 35): string {
- if (str.length <= maxLength) return str
+export function truncateMiddle(str: string, maxLength: number = 35): string {
+ if (str.length <= maxLength) return str
- const ellipsis = "…"
- const keepStart = Math.ceil((maxLength - ellipsis.length) / 2)
- const keepEnd = Math.floor((maxLength - ellipsis.length) / 2)
+ const ellipsis = "…"
+ const keepStart = Math.ceil((maxLength - ellipsis.length) / 2)
+ const keepEnd = Math.floor((maxLength - ellipsis.length) / 2)
- return str.slice(0, keepStart) + ellipsis + str.slice(-keepEnd)
- }
+ return str.slice(0, keepStart) + ellipsis + str.slice(-keepEnd)
+}
- export function pluralize(count: number, singular: string, plural: string): string {
- const template = count === 1 ? singular : plural
- return template.replace("{}", count.toString())
- }
+export function pluralize(count: number, singular: string, plural: string): string {
+ const template = count === 1 ? singular : plural
+ return template.replace("{}", count.toString())
}
diff --git a/packages/opencode/src/util/lock.ts b/packages/opencode/src/util/lock.ts
index 3aea64394..3f8e60937 100644
--- a/packages/opencode/src/util/lock.ts
+++ b/packages/opencode/src/util/lock.ts
@@ -1,54 +1,62 @@
-export namespace Lock {
- const locks = new Map<
- string,
- {
- readers: number
- writer: boolean
- waitingReaders: (() => void)[]
- waitingWriters: (() => void)[]
- }
- >()
+const locks = new Map<
+ string,
+ {
+ readers: number
+ writer: boolean
+ waitingReaders: (() => void)[]
+ waitingWriters: (() => void)[]
+ }
+>()
- function get(key: string) {
- if (!locks.has(key)) {
- locks.set(key, {
- readers: 0,
- writer: false,
- waitingReaders: [],
- waitingWriters: [],
- })
- }
- return locks.get(key)!
+function get(key: string) {
+ if (!locks.has(key)) {
+ locks.set(key, {
+ readers: 0,
+ writer: false,
+ waitingReaders: [],
+ waitingWriters: [],
+ })
}
+ return locks.get(key)!
+}
- function process(key: string) {
- const lock = locks.get(key)
- if (!lock || lock.writer || lock.readers > 0) return
+function process(key: string) {
+ const lock = locks.get(key)
+ if (!lock || lock.writer || lock.readers > 0) return
- // Prioritize writers to prevent starvation
- if (lock.waitingWriters.length > 0) {
- const nextWriter = lock.waitingWriters.shift()!
- nextWriter()
- return
- }
+ // Prioritize writers to prevent starvation
+ if (lock.waitingWriters.length > 0) {
+ const nextWriter = lock.waitingWriters.shift()!
+ nextWriter()
+ return
+ }
- // Wake up all waiting readers
- while (lock.waitingReaders.length > 0) {
- const nextReader = lock.waitingReaders.shift()!
- nextReader()
- }
+ // Wake up all waiting readers
+ while (lock.waitingReaders.length > 0) {
+ const nextReader = lock.waitingReaders.shift()!
+ nextReader()
+ }
- // Clean up empty locks
- if (lock.readers === 0 && !lock.writer && lock.waitingReaders.length === 0 && lock.waitingWriters.length === 0) {
- locks.delete(key)
- }
+ // Clean up empty locks
+ if (lock.readers === 0 && !lock.writer && lock.waitingReaders.length === 0 && lock.waitingWriters.length === 0) {
+ locks.delete(key)
}
+}
- export async function read(key: string): Promise<Disposable> {
- const lock = get(key)
+export async function read(key: string): Promise<Disposable> {
+ const lock = get(key)
- return new Promise((resolve) => {
- if (!lock.writer && lock.waitingWriters.length === 0) {
+ return new Promise((resolve) => {
+ if (!lock.writer && lock.waitingWriters.length === 0) {
+ lock.readers++
+ resolve({
+ [Symbol.dispose]: () => {
+ lock.readers--
+ process(key)
+ },
+ })
+ } else {
+ lock.waitingReaders.push(() => {
lock.readers++
resolve({
[Symbol.dispose]: () => {
@@ -56,25 +64,25 @@ export namespace Lock {
process(key)
},
})
- } else {
- lock.waitingReaders.push(() => {
- lock.readers++
- resolve({
- [Symbol.dispose]: () => {
- lock.readers--
- process(key)
- },
- })
- })
- }
- })
- }
+ })
+ }
+ })
+}
- export async function write(key: string): Promise<Disposable> {
- const lock = get(key)
+export async function write(key: string): Promise<Disposable> {
+ const lock = get(key)
- return new Promise((resolve) => {
- if (!lock.writer && lock.readers === 0) {
+ return new Promise((resolve) => {
+ if (!lock.writer && lock.readers === 0) {
+ lock.writer = true
+ resolve({
+ [Symbol.dispose]: () => {
+ lock.writer = false
+ process(key)
+ },
+ })
+ } else {
+ lock.waitingWriters.push(() => {
lock.writer = true
resolve({
[Symbol.dispose]: () => {
@@ -82,17 +90,7 @@ export namespace Lock {
process(key)
},
})
- } else {
- lock.waitingWriters.push(() => {
- lock.writer = true
- resolve({
- [Symbol.dispose]: () => {
- lock.writer = false
- process(key)
- },
- })
- })
- }
- })
- }
+ })
+ }
+ })
}
diff --git a/packages/opencode/src/util/log.ts b/packages/opencode/src/util/log.ts
index 781263276..6be9816a8 100644
--- a/packages/opencode/src/util/log.ts
+++ b/packages/opencode/src/util/log.ts
@@ -5,183 +5,181 @@ import { Global } from "../global"
import z from "zod"
import { Glob } from "@opencode-ai/shared/util/glob"
-export namespace Log {
- export const Level = z.enum(["DEBUG", "INFO", "WARN", "ERROR"]).meta({ ref: "LogLevel", description: "Log level" })
- export type Level = z.infer<typeof Level>
-
- const levelPriority: Record<Level, number> = {
- DEBUG: 0,
- INFO: 1,
- WARN: 2,
- ERROR: 3,
- }
- const keep = 10
+export const Level = z.enum(["DEBUG", "INFO", "WARN", "ERROR"]).meta({ ref: "LogLevel", description: "Log level" })
+export type Level = z.infer<typeof Level>
+
+const levelPriority: Record<Level, number> = {
+ DEBUG: 0,
+ INFO: 1,
+ WARN: 2,
+ ERROR: 3,
+}
+const keep = 10
- let level: Level = "INFO"
+let level: Level = "INFO"
- function shouldLog(input: Level): boolean {
- return levelPriority[input] >= levelPriority[level]
- }
+function shouldLog(input: Level): boolean {
+ return levelPriority[input] >= levelPriority[level]
+}
- export type Logger = {
- debug(message?: any, extra?: Record<string, any>): void
- info(message?: any, extra?: Record<string, any>): void
- error(message?: any, extra?: Record<string, any>): void
- warn(message?: any, extra?: Record<string, any>): void
- tag(key: string, value: string): Logger
- clone(): Logger
- time(
- message: string,
- extra?: Record<string, any>,
- ): {
- stop(): void
- [Symbol.dispose](): void
- }
+export type Logger = {
+ debug(message?: any, extra?: Record<string, any>): void
+ info(message?: any, extra?: Record<string, any>): void
+ error(message?: any, extra?: Record<string, any>): void
+ warn(message?: any, extra?: Record<string, any>): void
+ tag(key: string, value: string): Logger
+ clone(): Logger
+ time(
+ message: string,
+ extra?: Record<string, any>,
+ ): {
+ stop(): void
+ [Symbol.dispose](): void
}
+}
- const loggers = new Map<string, Logger>()
+const loggers = new Map<string, Logger>()
- export const Default = create({ service: "default" })
+export const Default = create({ service: "default" })
- export interface Options {
- print: boolean
- dev?: boolean
- level?: Level
- }
+export interface Options {
+ print: boolean
+ dev?: boolean
+ level?: Level
+}
- let logpath = ""
- export function file() {
- return logpath
- }
- let write = (msg: any) => {
- process.stderr.write(msg)
- return msg.length
- }
+let logpath = ""
+export function file() {
+ return logpath
+}
+let write = (msg: any) => {
+ process.stderr.write(msg)
+ return msg.length
+}
- export async function init(options: Options) {
- if (options.level) level = options.level
- cleanup(Global.Path.log)
- if (options.print) return
- logpath = path.join(
- Global.Path.log,
- options.dev ? "dev.log" : new Date().toISOString().split(".")[0].replace(/:/g, "") + ".log",
- )
- await fs.truncate(logpath).catch(() => {})
- const stream = createWriteStream(logpath, { flags: "a" })
- write = async (msg: any) => {
- return new Promise((resolve, reject) => {
- stream.write(msg, (err) => {
- if (err) reject(err)
- else resolve(msg.length)
- })
+export async function init(options: Options) {
+ if (options.level) level = options.level
+ cleanup(Global.Path.log)
+ if (options.print) return
+ logpath = path.join(
+ Global.Path.log,
+ options.dev ? "dev.log" : new Date().toISOString().split(".")[0].replace(/:/g, "") + ".log",
+ )
+ await fs.truncate(logpath).catch(() => {})
+ const stream = createWriteStream(logpath, { flags: "a" })
+ write = async (msg: any) => {
+ return new Promise((resolve, reject) => {
+ stream.write(msg, (err) => {
+ if (err) reject(err)
+ else resolve(msg.length)
})
- }
+ })
}
+}
- async function cleanup(dir: string) {
- const files = (
- await Glob.scan("????-??-??T??????.log", {
- cwd: dir,
- absolute: false,
- include: "file",
- }).catch(() => [])
- )
- .filter((file) => path.basename(file) === file)
- .sort()
- if (files.length <= keep) return
-
- const doomed = files.slice(0, -keep)
- await Promise.all(doomed.map((file) => fs.unlink(path.join(dir, file)).catch(() => {})))
- }
+async function cleanup(dir: string) {
+ const files = (
+ await Glob.scan("????-??-??T??????.log", {
+ cwd: dir,
+ absolute: false,
+ include: "file",
+ }).catch(() => [])
+ )
+ .filter((file) => path.basename(file) === file)
+ .sort()
+ if (files.length <= keep) return
+
+ const doomed = files.slice(0, -keep)
+ await Promise.all(doomed.map((file) => fs.unlink(path.join(dir, file)).catch(() => {})))
+}
- function formatError(error: Error, depth = 0): string {
- const result = error.message
- return error.cause instanceof Error && depth < 10
- ? result + " Caused by: " + formatError(error.cause, depth + 1)
- : result
- }
+function formatError(error: Error, depth = 0): string {
+ const result = error.message
+ return error.cause instanceof Error && depth < 10
+ ? result + " Caused by: " + formatError(error.cause, depth + 1)
+ : result
+}
- let last = Date.now()
- export function create(tags?: Record<string, any>) {
- tags = tags || {}
+let last = Date.now()
+export function create(tags?: Record<string, any>) {
+ tags = tags || {}
- const service = tags["service"]
- if (service && typeof service === "string") {
- const cached = loggers.get(service)
- if (cached) {
- return cached
- }
+ const service = tags["service"]
+ if (service && typeof service === "string") {
+ const cached = loggers.get(service)
+ if (cached) {
+ return cached
}
+ }
- function build(message: any, extra?: Record<string, any>) {
- const prefix = Object.entries({
- ...tags,
- ...extra,
+ function build(message: any, extra?: Record<string, any>) {
+ const prefix = Object.entries({
+ ...tags,
+ ...extra,
+ })
+ .filter(([_, value]) => value !== undefined && value !== null)
+ .map(([key, value]) => {
+ const prefix = `${key}=`
+ if (value instanceof Error) return prefix + formatError(value)
+ if (typeof value === "object") return prefix + JSON.stringify(value)
+ return prefix + value
})
- .filter(([_, value]) => value !== undefined && value !== null)
- .map(([key, value]) => {
- const prefix = `${key}=`
- if (value instanceof Error) return prefix + formatError(value)
- if (typeof value === "object") return prefix + JSON.stringify(value)
- return prefix + value
+ .join(" ")
+ const next = new Date()
+ const diff = next.getTime() - last
+ last = next.getTime()
+ return [next.toISOString().split(".")[0], "+" + diff + "ms", prefix, message].filter(Boolean).join(" ") + "\n"
+ }
+ const result: Logger = {
+ debug(message?: any, extra?: Record<string, any>) {
+ if (shouldLog("DEBUG")) {
+ write("DEBUG " + build(message, extra))
+ }
+ },
+ info(message?: any, extra?: Record<string, any>) {
+ if (shouldLog("INFO")) {
+ write("INFO " + build(message, extra))
+ }
+ },
+ error(message?: any, extra?: Record<string, any>) {
+ if (shouldLog("ERROR")) {
+ write("ERROR " + build(message, extra))
+ }
+ },
+ warn(message?: any, extra?: Record<string, any>) {
+ if (shouldLog("WARN")) {
+ write("WARN " + build(message, extra))
+ }
+ },
+ tag(key: string, value: string) {
+ if (tags) tags[key] = value
+ return result
+ },
+ clone() {
+ return create({ ...tags })
+ },
+ time(message: string, extra?: Record<string, any>) {
+ const now = Date.now()
+ result.info(message, { status: "started", ...extra })
+ function stop() {
+ result.info(message, {
+ status: "completed",
+ duration: Date.now() - now,
+ ...extra,
})
- .join(" ")
- const next = new Date()
- const diff = next.getTime() - last
- last = next.getTime()
- return [next.toISOString().split(".")[0], "+" + diff + "ms", prefix, message].filter(Boolean).join(" ") + "\n"
- }
- const result: Logger = {
- debug(message?: any, extra?: Record<string, any>) {
- if (shouldLog("DEBUG")) {
- write("DEBUG " + build(message, extra))
- }
- },
- info(message?: any, extra?: Record<string, any>) {
- if (shouldLog("INFO")) {
- write("INFO " + build(message, extra))
- }
- },
- error(message?: any, extra?: Record<string, any>) {
- if (shouldLog("ERROR")) {
- write("ERROR " + build(message, extra))
- }
- },
- warn(message?: any, extra?: Record<string, any>) {
- if (shouldLog("WARN")) {
- write("WARN " + build(message, extra))
- }
- },
- tag(key: string, value: string) {
- if (tags) tags[key] = value
- return result
- },
- clone() {
- return Log.create({ ...tags })
- },
- time(message: string, extra?: Record<string, any>) {
- const now = Date.now()
- result.info(message, { status: "started", ...extra })
- function stop() {
- result.info(message, {
- status: "completed",
- duration: Date.now() - now,
- ...extra,
- })
- }
- return {
- stop,
- [Symbol.dispose]() {
- stop()
- },
- }
- },
- }
-
- if (service && typeof service === "string") {
- loggers.set(service, result)
- }
+ }
+ return {
+ stop,
+ [Symbol.dispose]() {
+ stop()
+ },
+ }
+ },
+ }
- return result
+ if (service && typeof service === "string") {
+ loggers.set(service, result)
}
+
+ return result
}
diff --git a/packages/opencode/src/util/process.ts b/packages/opencode/src/util/process.ts
index e45ceb471..96c35e5d6 100644
--- a/packages/opencode/src/util/process.ts
+++ b/packages/opencode/src/util/process.ts
@@ -3,174 +3,172 @@ import launch from "cross-spawn"
import { buffer } from "node:stream/consumers"
import { errorMessage } from "./error"
-export namespace Process {
- export type Stdio = "inherit" | "pipe" | "ignore"
- export type Shell = boolean | string
-
- export interface Options {
- cwd?: string
- env?: NodeJS.ProcessEnv | null
- stdin?: Stdio
- stdout?: Stdio
- stderr?: Stdio
- shell?: Shell
- abort?: AbortSignal
- kill?: NodeJS.Signals | number
- timeout?: number
- }
+export type Stdio = "inherit" | "pipe" | "ignore"
+export type Shell = boolean | string
+
+export interface Options {
+ cwd?: string
+ env?: NodeJS.ProcessEnv | null
+ stdin?: Stdio
+ stdout?: Stdio
+ stderr?: Stdio
+ shell?: Shell
+ abort?: AbortSignal
+ kill?: NodeJS.Signals | number
+ timeout?: number
+}
- export interface RunOptions extends Omit<Options, "stdout" | "stderr"> {
- nothrow?: boolean
- }
+export interface RunOptions extends Omit<Options, "stdout" | "stderr"> {
+ nothrow?: boolean
+}
- export interface Result {
- code: number
- stdout: Buffer
- stderr: Buffer
- }
+export interface Result {
+ code: number
+ stdout: Buffer
+ stderr: Buffer
+}
- export interface TextResult extends Result {
- text: string
- }
+export interface TextResult extends Result {
+ text: string
+}
- export class RunFailedError extends Error {
- readonly cmd: string[]
- readonly code: number
- readonly stdout: Buffer
- readonly stderr: Buffer
-
- constructor(cmd: string[], code: number, stdout: Buffer, stderr: Buffer) {
- const text = stderr.toString().trim()
- super(
- text
- ? `Command failed with code ${code}: ${cmd.join(" ")}\n${text}`
- : `Command failed with code ${code}: ${cmd.join(" ")}`,
- )
- this.name = "ProcessRunFailedError"
- this.cmd = [...cmd]
- this.code = code
- this.stdout = stdout
- this.stderr = stderr
- }
+export class RunFailedError extends Error {
+ readonly cmd: string[]
+ readonly code: number
+ readonly stdout: Buffer
+ readonly stderr: Buffer
+
+ constructor(cmd: string[], code: number, stdout: Buffer, stderr: Buffer) {
+ const text = stderr.toString().trim()
+ super(
+ text
+ ? `Command failed with code ${code}: ${cmd.join(" ")}\n${text}`
+ : `Command failed with code ${code}: ${cmd.join(" ")}`,
+ )
+ this.name = "ProcessRunFailedError"
+ this.cmd = [...cmd]
+ this.code = code
+ this.stdout = stdout
+ this.stderr = stderr
}
+}
- export type Child = ChildProcess & { exited: Promise<number> }
-
- export function spawn(cmd: string[], opts: Options = {}): Child {
- if (cmd.length === 0) throw new Error("Command is required")
- opts.abort?.throwIfAborted()
-
- const proc = launch(cmd[0], cmd.slice(1), {
- cwd: opts.cwd,
- shell: opts.shell,
- env: opts.env === null ? {} : opts.env ? { ...process.env, ...opts.env } : undefined,
- stdio: [opts.stdin ?? "ignore", opts.stdout ?? "ignore", opts.stderr ?? "ignore"],
- windowsHide: process.platform === "win32",
- })
-
- let closed = false
- let timer: ReturnType<typeof setTimeout> | undefined
+export type Child = ChildProcess & { exited: Promise<number> }
- const abort = () => {
- if (closed) return
- if (proc.exitCode !== null || proc.signalCode !== null) return
- closed = true
+export function spawn(cmd: string[], opts: Options = {}): Child {
+ if (cmd.length === 0) throw new Error("Command is required")
+ opts.abort?.throwIfAborted()
- proc.kill(opts.kill ?? "SIGTERM")
+ const proc = launch(cmd[0], cmd.slice(1), {
+ cwd: opts.cwd,
+ shell: opts.shell,
+ env: opts.env === null ? {} : opts.env ? { ...process.env, ...opts.env } : undefined,
+ stdio: [opts.stdin ?? "ignore", opts.stdout ?? "ignore", opts.stderr ?? "ignore"],
+ windowsHide: process.platform === "win32",
+ })
- const ms = opts.timeout ?? 5_000
- if (ms <= 0) return
- timer = setTimeout(() => proc.kill("SIGKILL"), ms)
- }
+ let closed = false
+ let timer: ReturnType<typeof setTimeout> | undefined
- const exited = new Promise<number>((resolve, reject) => {
- const done = () => {
- opts.abort?.removeEventListener("abort", abort)
- if (timer) clearTimeout(timer)
- }
+ const abort = () => {
+ if (closed) return
+ if (proc.exitCode !== null || proc.signalCode !== null) return
+ closed = true
- proc.once("exit", (code, signal) => {
- done()
- resolve(code ?? (signal ? 1 : 0))
- })
+ proc.kill(opts.kill ?? "SIGTERM")
- proc.once("error", (error) => {
- done()
- reject(error)
- })
- })
- void exited.catch(() => undefined)
+ const ms = opts.timeout ?? 5_000
+ if (ms <= 0) return
+ timer = setTimeout(() => proc.kill("SIGKILL"), ms)
+ }
- if (opts.abort) {
- opts.abort.addEventListener("abort", abort, { once: true })
- if (opts.abort.aborted) abort()
+ const exited = new Promise<number>((resolve, reject) => {
+ const done = () => {
+ opts.abort?.removeEventListener("abort", abort)
+ if (timer) clearTimeout(timer)
}
- const child = proc as Child
- child.exited = exited
- return child
- }
+ proc.once("exit", (code, signal) => {
+ done()
+ resolve(code ?? (signal ? 1 : 0))
+ })
- export async function run(cmd: string[], opts: RunOptions = {}): Promise<Result> {
- const proc = spawn(cmd, {
- cwd: opts.cwd,
- env: opts.env,
- stdin: opts.stdin,
- shell: opts.shell,
- abort: opts.abort,
- kill: opts.kill,
- timeout: opts.timeout,
- stdout: "pipe",
- stderr: "pipe",
+ proc.once("error", (error) => {
+ done()
+ reject(error)
})
+ })
+ void exited.catch(() => undefined)
- if (!proc.stdout || !proc.stderr) throw new Error("Process output not available")
-
- const out = await Promise.all([proc.exited, buffer(proc.stdout), buffer(proc.stderr)])
- .then(([code, stdout, stderr]) => ({
- code,
- stdout,
- stderr,
- }))
- .catch((err: unknown) => {
- if (!opts.nothrow) throw err
- return {
- code: 1,
- stdout: Buffer.alloc(0),
- stderr: Buffer.from(errorMessage(err)),
- }
- })
- if (out.code === 0 || opts.nothrow) return out
- throw new RunFailedError(cmd, out.code, out.stdout, out.stderr)
+ if (opts.abort) {
+ opts.abort.addEventListener("abort", abort, { once: true })
+ if (opts.abort.aborted) abort()
}
- // Duplicated in `packages/sdk/js/src/process.ts` because the SDK cannot import
- // `opencode` without creating a cycle. Keep both copies in sync.
- export async function stop(proc: ChildProcess) {
- if (proc.exitCode !== null || proc.signalCode !== null) return
-
- if (process.platform !== "win32" || !proc.pid) {
- proc.kill()
- return
- }
+ const child = proc as Child
+ child.exited = exited
+ return child
+}
- const out = await run(["taskkill", "/pid", String(proc.pid), "/T", "/F"], {
- nothrow: true,
+export async function run(cmd: string[], opts: RunOptions = {}): Promise<Result> {
+ const proc = spawn(cmd, {
+ cwd: opts.cwd,
+ env: opts.env,
+ stdin: opts.stdin,
+ shell: opts.shell,
+ abort: opts.abort,
+ kill: opts.kill,
+ timeout: opts.timeout,
+ stdout: "pipe",
+ stderr: "pipe",
+ })
+
+ if (!proc.stdout || !proc.stderr) throw new Error("Process output not available")
+
+ const out = await Promise.all([proc.exited, buffer(proc.stdout), buffer(proc.stderr)])
+ .then(([code, stdout, stderr]) => ({
+ code,
+ stdout,
+ stderr,
+ }))
+ .catch((err: unknown) => {
+ if (!opts.nothrow) throw err
+ return {
+ code: 1,
+ stdout: Buffer.alloc(0),
+ stderr: Buffer.from(errorMessage(err)),
+ }
})
+ if (out.code === 0 || opts.nothrow) return out
+ throw new RunFailedError(cmd, out.code, out.stdout, out.stderr)
+}
+
+// Duplicated in `packages/sdk/js/src/process.ts` because the SDK cannot import
+// `opencode` without creating a cycle. Keep both copies in sync.
+export async function stop(proc: ChildProcess) {
+ if (proc.exitCode !== null || proc.signalCode !== null) return
- if (out.code === 0) return
+ if (process.platform !== "win32" || !proc.pid) {
proc.kill()
+ return
}
- export async function text(cmd: string[], opts: RunOptions = {}): Promise<TextResult> {
- const out = await run(cmd, opts)
- return {
- ...out,
- text: out.stdout.toString(),
- }
- }
+ const out = await run(["taskkill", "/pid", String(proc.pid), "/T", "/F"], {
+ nothrow: true,
+ })
- export async function lines(cmd: string[], opts: RunOptions = {}): Promise<string[]> {
- return (await text(cmd, opts)).text.split(/\r?\n/).filter(Boolean)
+ if (out.code === 0) return
+ proc.kill()
+}
+
+export async function text(cmd: string[], opts: RunOptions = {}): Promise<TextResult> {
+ const out = await run(cmd, opts)
+ return {
+ ...out,
+ text: out.stdout.toString(),
}
}
+
+export async function lines(cmd: string[], opts: RunOptions = {}): Promise<string[]> {
+ return (await text(cmd, opts)).text.split(/\r?\n/).filter(Boolean)
+}
diff --git a/packages/opencode/src/util/rpc.ts b/packages/opencode/src/util/rpc.ts
index ebd8be40e..98f3a09d4 100644
--- a/packages/opencode/src/util/rpc.ts
+++ b/packages/opencode/src/util/rpc.ts
@@ -1,66 +1,64 @@
-export namespace Rpc {
- type Definition = {
- [method: string]: (input: any) => any
- }
+type Definition = {
+ [method: string]: (input: any) => any
+}
- export function listen(rpc: Definition) {
- onmessage = async (evt) => {
- const parsed = JSON.parse(evt.data)
- if (parsed.type === "rpc.request") {
- const result = await rpc[parsed.method](parsed.input)
- postMessage(JSON.stringify({ type: "rpc.result", result, id: parsed.id }))
- }
+export function listen(rpc: Definition) {
+ onmessage = async (evt) => {
+ const parsed = JSON.parse(evt.data)
+ if (parsed.type === "rpc.request") {
+ const result = await rpc[parsed.method](parsed.input)
+ postMessage(JSON.stringify({ type: "rpc.result", result, id: parsed.id }))
}
}
+}
- export function emit(event: string, data: unknown) {
- postMessage(JSON.stringify({ type: "rpc.event", event, data }))
- }
+export function emit(event: string, data: unknown) {
+ postMessage(JSON.stringify({ type: "rpc.event", event, data }))
+}
- export function client<T extends Definition>(target: {
- postMessage: (data: string) => void | null
- onmessage: ((this: Worker, ev: MessageEvent<any>) => any) | null
- }) {
- const pending = new Map<number, (result: any) => void>()
- const listeners = new Map<string, Set<(data: any) => void>>()
- let id = 0
- target.onmessage = async (evt) => {
- const parsed = JSON.parse(evt.data)
- if (parsed.type === "rpc.result") {
- const resolve = pending.get(parsed.id)
- if (resolve) {
- resolve(parsed.result)
- pending.delete(parsed.id)
- }
- }
- if (parsed.type === "rpc.event") {
- const handlers = listeners.get(parsed.event)
- if (handlers) {
- for (const handler of handlers) {
- handler(parsed.data)
- }
- }
+export function client<T extends Definition>(target: {
+ postMessage: (data: string) => void | null
+ onmessage: ((this: Worker, ev: MessageEvent<any>) => any) | null
+}) {
+ const pending = new Map<number, (result: any) => void>()
+ const listeners = new Map<string, Set<(data: any) => void>>()
+ let id = 0
+ target.onmessage = async (evt) => {
+ const parsed = JSON.parse(evt.data)
+ if (parsed.type === "rpc.result") {
+ const resolve = pending.get(parsed.id)
+ if (resolve) {
+ resolve(parsed.result)
+ pending.delete(parsed.id)
}
}
- return {
- call<Method extends keyof T>(method: Method, input: Parameters<T[Method]>[0]): Promise<ReturnType<T[Method]>> {
- const requestId = id++
- return new Promise((resolve) => {
- pending.set(requestId, resolve)
- target.postMessage(JSON.stringify({ type: "rpc.request", method, input, id: requestId }))
- })
- },
- on<Data>(event: string, handler: (data: Data) => void) {
- let handlers = listeners.get(event)
- if (!handlers) {
- handlers = new Set()
- listeners.set(event, handlers)
+ if (parsed.type === "rpc.event") {
+ const handlers = listeners.get(parsed.event)
+ if (handlers) {
+ for (const handler of handlers) {
+ handler(parsed.data)
}
- handlers.add(handler)
- return () => {
- handlers!.delete(handler)
- }
- },
+ }
}
}
+ return {
+ call<Method extends keyof T>(method: Method, input: Parameters<T[Method]>[0]): Promise<ReturnType<T[Method]>> {
+ const requestId = id++
+ return new Promise((resolve) => {
+ pending.set(requestId, resolve)
+ target.postMessage(JSON.stringify({ type: "rpc.request", method, input, id: requestId }))
+ })
+ },
+ on<Data>(event: string, handler: (data: Data) => void) {
+ let handlers = listeners.get(event)
+ if (!handlers) {
+ handlers = new Set()
+ listeners.set(event, handlers)
+ }
+ handlers.add(handler)
+ return () => {
+ handlers!.delete(handler)
+ }
+ },
+ }
}
diff --git a/packages/opencode/src/util/token.ts b/packages/opencode/src/util/token.ts
index cee5adc37..52951c4cf 100644
--- a/packages/opencode/src/util/token.ts
+++ b/packages/opencode/src/util/token.ts
@@ -1,7 +1,5 @@
-export namespace Token {
- const CHARS_PER_TOKEN = 4
+const CHARS_PER_TOKEN = 4
- export function estimate(input: string) {
- return Math.max(0, Math.round((input || "").length / CHARS_PER_TOKEN))
- }
+export function estimate(input: string) {
+ return Math.max(0, Math.round((input || "").length / CHARS_PER_TOKEN))
}
diff --git a/packages/opencode/src/util/wildcard.ts b/packages/opencode/src/util/wildcard.ts
index f54b6c85f..0efb94e91 100644
--- a/packages/opencode/src/util/wildcard.ts
+++ b/packages/opencode/src/util/wildcard.ts
@@ -1,59 +1,57 @@
import { sortBy, pipe } from "remeda"
-export namespace Wildcard {
- export function match(str: string, pattern: string) {
- if (str) str = str.replaceAll("\\", "/")
- if (pattern) pattern = pattern.replaceAll("\\", "/")
- let escaped = pattern
- .replace(/[.+^${}()|[\]\\]/g, "\\$&") // escape special regex chars
- .replace(/\*/g, ".*") // * becomes .*
- .replace(/\?/g, ".") // ? becomes .
+export function match(str: string, pattern: string) {
+ if (str) str = str.replaceAll("\\", "/")
+ if (pattern) pattern = pattern.replaceAll("\\", "/")
+ let escaped = pattern
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&") // escape special regex chars
+ .replace(/\*/g, ".*") // * becomes .*
+ .replace(/\?/g, ".") // ? becomes .
- // If pattern ends with " *" (space + wildcard), make the trailing part optional
- // This allows "ls *" to match both "ls" and "ls -la"
- if (escaped.endsWith(" .*")) {
- escaped = escaped.slice(0, -3) + "( .*)?"
- }
-
- const flags = process.platform === "win32" ? "si" : "s"
- return new RegExp("^" + escaped + "$", flags).test(str)
+ // If pattern ends with " *" (space + wildcard), make the trailing part optional
+ // This allows "ls *" to match both "ls" and "ls -la"
+ if (escaped.endsWith(" .*")) {
+ escaped = escaped.slice(0, -3) + "( .*)?"
}
- export function all(input: string, patterns: Record<string, any>) {
- const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"]))
- let result = undefined
- for (const [pattern, value] of sorted) {
- if (match(input, pattern)) {
- result = value
- continue
- }
+ const flags = process.platform === "win32" ? "si" : "s"
+ return new RegExp("^" + escaped + "$", flags).test(str)
+}
+
+export function all(input: string, patterns: Record<string, any>) {
+ const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"]))
+ let result = undefined
+ for (const [pattern, value] of sorted) {
+ if (match(input, pattern)) {
+ result = value
+ continue
}
- return result
}
+ return result
+}
- export function allStructured(input: { head: string; tail: string[] }, patterns: Record<string, any>) {
- const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"]))
- let result = undefined
- for (const [pattern, value] of sorted) {
- const parts = pattern.split(/\s+/)
- if (!match(input.head, parts[0])) continue
- if (parts.length === 1 || matchSequence(input.tail, parts.slice(1))) {
- result = value
- continue
- }
+export function allStructured(input: { head: string; tail: string[] }, patterns: Record<string, any>) {
+ const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"]))
+ let result = undefined
+ for (const [pattern, value] of sorted) {
+ const parts = pattern.split(/\s+/)
+ if (!match(input.head, parts[0])) continue
+ if (parts.length === 1 || matchSequence(input.tail, parts.slice(1))) {
+ result = value
+ continue
}
- return result
}
+ return result
+}
- function matchSequence(items: string[], patterns: string[]): boolean {
- if (patterns.length === 0) return true
- const [pattern, ...rest] = patterns
- if (pattern === "*") return matchSequence(items, rest)
- for (let i = 0; i < items.length; i++) {
- if (match(items[i], pattern) && matchSequence(items.slice(i + 1), rest)) {
- return true
- }
+function matchSequence(items: string[], patterns: string[]): boolean {
+ if (patterns.length === 0) return true
+ const [pattern, ...rest] = patterns
+ if (pattern === "*") return matchSequence(items, rest)
+ for (let i = 0; i < items.length; i++) {
+ if (match(items[i], pattern) && matchSequence(items.slice(i + 1), rest)) {
+ return true
}
- return false
}
+ return false
}
diff --git a/packages/opencode/src/worktree/worktree.ts b/packages/opencode/src/worktree/worktree.ts
index fab9ce57f..86ef95f0e 100644
--- a/packages/opencode/src/worktree/worktree.ts
+++ b/packages/opencode/src/worktree/worktree.ts
@@ -7,7 +7,7 @@ import { Project } from "../project/project"
import { Database, eq } from "../storage/db"
import { ProjectTable } from "../project/project.sql"
import type { ProjectID } from "../project/schema"
-import { Log } from "../util/log"
+import { Log } from "../util"
import { Slug } from "@opencode-ai/shared/util/slug"
import { errorMessage } from "../util/error"
import { BusEvent } from "@/bus/bus-event"
diff --git a/packages/opencode/test/cli/tui/plugin-loader.test.ts b/packages/opencode/test/cli/tui/plugin-loader.test.ts
index 119517b10..8446570cc 100644
--- a/packages/opencode/test/cli/tui/plugin-loader.test.ts
+++ b/packages/opencode/test/cli/tui/plugin-loader.test.ts
@@ -6,7 +6,7 @@ import { tmpdir } from "../../fixture/fixture"
import { createTuiPluginApi } from "../../fixture/tui-plugin"
import { Global } from "../../../src/global"
import { TuiConfig } from "../../../src/config/tui"
-import { Filesystem } from "../../../src/util/filesystem"
+import { Filesystem } from "../../../src/util"
const { allThemes, addTheme } = await import("../../../src/cli/cmd/tui/context/theme")
const { TuiPluginRuntime } = await import("../../../src/cli/cmd/tui/plugin/runtime")
diff --git a/packages/opencode/test/cli/tui/thread.test.ts b/packages/opencode/test/cli/tui/thread.test.ts
index 176c2575a..1c5c7e65e 100644
--- a/packages/opencode/test/cli/tui/thread.test.ts
+++ b/packages/opencode/test/cli/tui/thread.test.ts
@@ -3,7 +3,7 @@ import fs from "fs/promises"
import path from "path"
import { tmpdir } from "../../fixture/fixture"
import * as App from "../../../src/cli/cmd/tui/app"
-import { Rpc } from "../../../src/util/rpc"
+import { Rpc } from "../../../src/util"
import { UI } from "../../../src/cli/ui"
import * as Timeout from "../../../src/util/timeout"
import * as Network from "../../../src/cli/network"
diff --git a/packages/opencode/test/config/agent-color.test.ts b/packages/opencode/test/config/agent-color.test.ts
index d77782354..bfa948619 100644
--- a/packages/opencode/test/config/agent-color.test.ts
+++ b/packages/opencode/test/config/agent-color.test.ts
@@ -5,7 +5,7 @@ import { provideInstance, tmpdir } from "../fixture/fixture"
import { Instance } from "../../src/project/instance"
import { Config } from "../../src/config"
import { Agent as AgentSvc } from "../../src/agent/agent"
-import { Color } from "../../src/util/color"
+import { Color } from "../../src/util"
import { AppRuntime } from "../../src/effect/app-runtime"
const load = () => AppRuntime.runPromise(Config.Service.use((svc) => svc.get()))
diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts
index 8cf410c3d..bc9fe5b01 100644
--- a/packages/opencode/test/config/config.test.ts
+++ b/packages/opencode/test/config/config.test.ts
@@ -23,7 +23,7 @@ import fs from "fs/promises"
import { pathToFileURL } from "url"
import { Global } from "../../src/global"
import { ProjectID } from "../../src/project/schema"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import * as Network from "../../src/util/network"
import { Npm } from "../../src/npm"
diff --git a/packages/opencode/test/config/tui.test.ts b/packages/opencode/test/config/tui.test.ts
index 4767e94b0..c80905cd1 100644
--- a/packages/opencode/test/config/tui.test.ts
+++ b/packages/opencode/test/config/tui.test.ts
@@ -6,7 +6,7 @@ import { Instance } from "../../src/project/instance"
import { Config } from "../../src/config"
import { TuiConfig } from "../../src/config/tui"
import { Global } from "../../src/global"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { AppRuntime } from "../../src/effect/app-runtime"
const managedConfigDir = process.env.OPENCODE_TEST_MANAGED_CONFIG_DIR!
diff --git a/packages/opencode/test/file/index.test.ts b/packages/opencode/test/file/index.test.ts
index 877e2ae0a..28fd2c838 100644
--- a/packages/opencode/test/file/index.test.ts
+++ b/packages/opencode/test/file/index.test.ts
@@ -5,7 +5,7 @@ import path from "path"
import fs from "fs/promises"
import { File } from "../../src/file"
import { Instance } from "../../src/project/instance"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { provideInstance, tmpdir } from "../fixture/fixture"
afterEach(async () => {
diff --git a/packages/opencode/test/file/path-traversal.test.ts b/packages/opencode/test/file/path-traversal.test.ts
index 1f2e45a6a..119005394 100644
--- a/packages/opencode/test/file/path-traversal.test.ts
+++ b/packages/opencode/test/file/path-traversal.test.ts
@@ -2,7 +2,7 @@ import { test, expect, describe } from "bun:test"
import { Effect } from "effect"
import path from "path"
import fs from "fs/promises"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { File } from "../../src/file"
import { Instance } from "../../src/project/instance"
import { provideInstance, tmpdir } from "../fixture/fixture"
diff --git a/packages/opencode/test/file/time.test.ts b/packages/opencode/test/file/time.test.ts
index 7f65d05ea..cb6390df8 100644
--- a/packages/opencode/test/file/time.test.ts
+++ b/packages/opencode/test/file/time.test.ts
@@ -6,7 +6,7 @@ import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
import { FileTime } from "../../src/file/time"
import { Instance } from "../../src/project/instance"
import { SessionID } from "../../src/session/schema"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { provideInstance, provideTmpdirInstance, tmpdirScoped } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
diff --git a/packages/opencode/test/fixture/plug-worker.ts b/packages/opencode/test/fixture/plug-worker.ts
index e4b80c5dc..c9afcd39f 100644
--- a/packages/opencode/test/fixture/plug-worker.ts
+++ b/packages/opencode/test/fixture/plug-worker.ts
@@ -1,7 +1,7 @@
import path from "path"
import { createPlugTask, type PlugCtx, type PlugDeps } from "../../src/cli/cmd/plug"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
type Msg = {
dir: string
diff --git a/packages/opencode/test/keybind.test.ts b/packages/opencode/test/keybind.test.ts
index 4ca1f1697..1e900a602 100644
--- a/packages/opencode/test/keybind.test.ts
+++ b/packages/opencode/test/keybind.test.ts
@@ -1,5 +1,5 @@
import { describe, test, expect } from "bun:test"
-import { Keybind } from "../src/util/keybind"
+import { Keybind } from "../src/util"
describe("Keybind.toString", () => {
test("should convert simple key to string", () => {
diff --git a/packages/opencode/test/lsp/client.test.ts b/packages/opencode/test/lsp/client.test.ts
index c2ba3ac5b..414d11f8e 100644
--- a/packages/opencode/test/lsp/client.test.ts
+++ b/packages/opencode/test/lsp/client.test.ts
@@ -3,7 +3,7 @@ import path from "path"
import { LSPClient } from "../../src/lsp/client"
import { LSPServer } from "../../src/lsp/server"
import { Instance } from "../../src/project/instance"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
// Minimal fake LSP server that speaks JSON-RPC over stdio
function spawnFakeServer() {
diff --git a/packages/opencode/test/plugin/install-concurrency.test.ts b/packages/opencode/test/plugin/install-concurrency.test.ts
index cf3e8692e..06ec2fc99 100644
--- a/packages/opencode/test/plugin/install-concurrency.test.ts
+++ b/packages/opencode/test/plugin/install-concurrency.test.ts
@@ -2,8 +2,8 @@ import { describe, expect, test } from "bun:test"
import fs from "fs/promises"
import path from "path"
-import { Process } from "../../src/util/process"
-import { Filesystem } from "../../src/util/filesystem"
+import { Process } from "../../src/util"
+import { Filesystem } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
const root = path.join(import.meta.dir, "../..")
diff --git a/packages/opencode/test/plugin/install.test.ts b/packages/opencode/test/plugin/install.test.ts
index 5ce21c4cf..f125f188a 100644
--- a/packages/opencode/test/plugin/install.test.ts
+++ b/packages/opencode/test/plugin/install.test.ts
@@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test"
import fs from "fs/promises"
import path from "path"
import { parse as parseJsonc } from "jsonc-parser"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { createPlugTask, type PlugCtx, type PlugDeps } from "../../src/cli/cmd/plug"
import { tmpdir } from "../fixture/fixture"
diff --git a/packages/opencode/test/plugin/loader-shared.test.ts b/packages/opencode/test/plugin/loader-shared.test.ts
index 4265e83c5..5072c1e74 100644
--- a/packages/opencode/test/plugin/loader-shared.test.ts
+++ b/packages/opencode/test/plugin/loader-shared.test.ts
@@ -4,7 +4,7 @@ import fs from "fs/promises"
import path from "path"
import { pathToFileURL } from "url"
import { tmpdir } from "../fixture/fixture"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
const disableDefault = process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS
process.env.OPENCODE_DISABLE_DEFAULT_PLUGINS = "1"
diff --git a/packages/opencode/test/plugin/meta.test.ts b/packages/opencode/test/plugin/meta.test.ts
index 057174066..3e2d4c617 100644
--- a/packages/opencode/test/plugin/meta.test.ts
+++ b/packages/opencode/test/plugin/meta.test.ts
@@ -4,8 +4,8 @@ import path from "path"
import { pathToFileURL } from "url"
import { tmpdir } from "../fixture/fixture"
-import { Process } from "../../src/util/process"
-import { Filesystem } from "../../src/util/filesystem"
+import { Process } from "../../src/util"
+import { Filesystem } from "../../src/util"
const { PluginMeta } = await import("../../src/plugin/meta")
const root = path.join(import.meta.dir, "../..")
diff --git a/packages/opencode/test/preload.ts b/packages/opencode/test/preload.ts
index 0ddc797fa..ba5df4f1e 100644
--- a/packages/opencode/test/preload.ts
+++ b/packages/opencode/test/preload.ts
@@ -78,7 +78,7 @@ delete process.env["OPENCODE_SERVER_USERNAME"]
process.env["OPENCODE_DB"] = ":memory:"
// Now safe to import from src/
-const { Log } = await import("../src/util/log")
+const { Log } = await import("../src/util")
const { initProjectors } = await import("../src/server/projectors")
Log.init({
diff --git a/packages/opencode/test/project/migrate-global.test.ts b/packages/opencode/test/project/migrate-global.test.ts
index d4313c12f..d645fb25b 100644
--- a/packages/opencode/test/project/migrate-global.test.ts
+++ b/packages/opencode/test/project/migrate-global.test.ts
@@ -5,7 +5,7 @@ import { SessionTable } from "../../src/session/session.sql"
import { ProjectTable } from "../../src/project/project.sql"
import { ProjectID } from "../../src/project/schema"
import { SessionID } from "../../src/session/schema"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { $ } from "bun"
import { tmpdir } from "../fixture/fixture"
import { Effect } from "effect"
diff --git a/packages/opencode/test/project/project.test.ts b/packages/opencode/test/project/project.test.ts
index ba253a920..a579a2335 100644
--- a/packages/opencode/test/project/project.test.ts
+++ b/packages/opencode/test/project/project.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import { Project } from "../../src/project/project"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { $ } from "bun"
import path from "path"
import { tmpdir } from "../fixture/fixture"
diff --git a/packages/opencode/test/provider/amazon-bedrock.test.ts b/packages/opencode/test/provider/amazon-bedrock.test.ts
index 6809e4d17..03f83601d 100644
--- a/packages/opencode/test/provider/amazon-bedrock.test.ts
+++ b/packages/opencode/test/provider/amazon-bedrock.test.ts
@@ -8,7 +8,7 @@ import { Instance } from "../../src/project/instance"
import { Provider } from "../../src/provider"
import { Env } from "../../src/env"
import { Global } from "../../src/global"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { Effect } from "effect"
import { AppRuntime } from "../../src/effect/app-runtime"
import { makeRuntime } from "../../src/effect/run-service"
diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts
index a6a93e809..300a5b903 100644
--- a/packages/opencode/test/provider/provider.test.ts
+++ b/packages/opencode/test/provider/provider.test.ts
@@ -9,7 +9,7 @@ import { Plugin } from "../../src/plugin/index"
import { ModelsDev } from "../../src/provider/models"
import { Provider } from "../../src/provider"
import { ProviderID, ModelID } from "../../src/provider/schema"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { Env } from "../../src/env"
import { Effect } from "effect"
import { AppRuntime } from "../../src/effect/app-runtime"
diff --git a/packages/opencode/test/server/global-session-list.test.ts b/packages/opencode/test/server/global-session-list.test.ts
index a1e374b4f..c029fd933 100644
--- a/packages/opencode/test/server/global-session-list.test.ts
+++ b/packages/opencode/test/server/global-session-list.test.ts
@@ -4,7 +4,7 @@ import z from "zod"
import { Instance } from "../../src/project/instance"
import { Project } from "../../src/project/project"
import { Session as SessionNs } from "../../src/session"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
Log.init({ print: false })
diff --git a/packages/opencode/test/server/project-init-git.test.ts b/packages/opencode/test/server/project-init-git.test.ts
index 406b3d6d8..c3ee18e73 100644
--- a/packages/opencode/test/server/project-init-git.test.ts
+++ b/packages/opencode/test/server/project-init-git.test.ts
@@ -5,8 +5,8 @@ import { GlobalBus } from "../../src/bus/global"
import { Snapshot } from "../../src/snapshot"
import { Instance } from "../../src/project/instance"
import { Server } from "../../src/server/server"
-import { Filesystem } from "../../src/util/filesystem"
-import { Log } from "../../src/util/log"
+import { Filesystem } from "../../src/util"
+import { Log } from "../../src/util"
import { resetDatabase } from "../fixture/db"
import { provideInstance, tmpdir } from "../fixture/fixture"
diff --git a/packages/opencode/test/server/session-actions.test.ts b/packages/opencode/test/server/session-actions.test.ts
index 301691ae2..3209ebff3 100644
--- a/packages/opencode/test/server/session-actions.test.ts
+++ b/packages/opencode/test/server/session-actions.test.ts
@@ -4,7 +4,7 @@ import { Instance } from "../../src/project/instance"
import { Server } from "../../src/server/server"
import { Session as SessionNs } from "../../src/session"
import type { SessionID } from "../../src/session/schema"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
Log.init({ print: false })
diff --git a/packages/opencode/test/server/session-list.test.ts b/packages/opencode/test/server/session-list.test.ts
index 75adb7f9f..9af60b9bd 100644
--- a/packages/opencode/test/server/session-list.test.ts
+++ b/packages/opencode/test/server/session-list.test.ts
@@ -2,7 +2,7 @@ import { afterEach, describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { Instance } from "../../src/project/instance"
import { Session as SessionNs } from "../../src/session"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
Log.init({ print: false })
diff --git a/packages/opencode/test/server/session-messages.test.ts b/packages/opencode/test/server/session-messages.test.ts
index 24ee6a1b4..d558d4324 100644
--- a/packages/opencode/test/server/session-messages.test.ts
+++ b/packages/opencode/test/server/session-messages.test.ts
@@ -5,7 +5,7 @@ import { Server } from "../../src/server/server"
import { Session as SessionNs } from "../../src/session"
import { MessageV2 } from "../../src/session/message-v2"
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
Log.init({ print: false })
diff --git a/packages/opencode/test/server/session-select.test.ts b/packages/opencode/test/server/session-select.test.ts
index 12552538d..c53448dfd 100644
--- a/packages/opencode/test/server/session-select.test.ts
+++ b/packages/opencode/test/server/session-select.test.ts
@@ -2,7 +2,7 @@ import { afterEach, describe, expect, test } from "bun:test"
import { Effect } from "effect"
import { Session as SessionNs } from "../../src/session"
import type { SessionID } from "../../src/session/schema"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { Instance } from "../../src/project/instance"
import { Server } from "../../src/server/server"
import { tmpdir } from "../fixture/fixture"
diff --git a/packages/opencode/test/session/compaction.test.ts b/packages/opencode/test/session/compaction.test.ts
index 7711d3193..ee0193221 100644
--- a/packages/opencode/test/session/compaction.test.ts
+++ b/packages/opencode/test/session/compaction.test.ts
@@ -8,9 +8,9 @@ import { Config } from "../../src/config"
import { Agent } from "../../src/agent/agent"
import { LLM } from "../../src/session/llm"
import { SessionCompaction } from "../../src/session/compaction"
-import { Token } from "../../src/util/token"
+import { Token } from "../../src/util"
import { Instance } from "../../src/project/instance"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { Permission } from "../../src/permission"
import { Plugin } from "../../src/plugin"
import { provideTmpdirInstance, tmpdir } from "../fixture/fixture"
diff --git a/packages/opencode/test/session/llm.test.ts b/packages/opencode/test/session/llm.test.ts
index f25ecc356..d1d53f605 100644
--- a/packages/opencode/test/session/llm.test.ts
+++ b/packages/opencode/test/session/llm.test.ts
@@ -10,7 +10,7 @@ import { Provider } from "../../src/provider"
import { ProviderTransform } from "../../src/provider/transform"
import { ModelsDev } from "../../src/provider/models"
import { ProviderID, ModelID } from "../../src/provider/schema"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
import type { Agent } from "../../src/agent/agent"
import { MessageV2 } from "../../src/session/message-v2"
diff --git a/packages/opencode/test/session/messages-pagination.test.ts b/packages/opencode/test/session/messages-pagination.test.ts
index f728bd364..804076dd4 100644
--- a/packages/opencode/test/session/messages-pagination.test.ts
+++ b/packages/opencode/test/session/messages-pagination.test.ts
@@ -6,7 +6,7 @@ import { Session as SessionNs } from "../../src/session"
import { MessageV2 } from "../../src/session/message-v2"
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
import { ModelID, ProviderID } from "../../src/provider/schema"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
const root = path.join(__dirname, "../..")
Log.init({ print: false })
diff --git a/packages/opencode/test/session/processor-effect.test.ts b/packages/opencode/test/session/processor-effect.test.ts
index 982399d6d..87ff40c70 100644
--- a/packages/opencode/test/session/processor-effect.test.ts
+++ b/packages/opencode/test/session/processor-effect.test.ts
@@ -18,7 +18,7 @@ import { MessageID, PartID, SessionID } from "../../src/session/schema"
import { SessionStatus } from "../../src/session/status"
import { SessionSummary } from "../../src/session/summary"
import { Snapshot } from "../../src/snapshot"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
import { provideTmpdirServer } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
diff --git a/packages/opencode/test/session/prompt-effect.test.ts b/packages/opencode/test/session/prompt-effect.test.ts
index 5ff8bf342..0a750352a 100644
--- a/packages/opencode/test/session/prompt-effect.test.ts
+++ b/packages/opencode/test/session/prompt-effect.test.ts
@@ -36,7 +36,7 @@ import { Shell } from "../../src/shell/shell"
import { Snapshot } from "../../src/snapshot"
import { ToolRegistry } from "../../src/tool/registry"
import { Truncate } from "../../src/tool/truncate"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
import { Ripgrep } from "../../src/file/ripgrep"
import { Format } from "../../src/format"
diff --git a/packages/opencode/test/session/prompt.test.ts b/packages/opencode/test/session/prompt.test.ts
index 4f5b19bca..acf305f3f 100644
--- a/packages/opencode/test/session/prompt.test.ts
+++ b/packages/opencode/test/session/prompt.test.ts
@@ -8,7 +8,7 @@ import { ModelID, ProviderID } from "../../src/provider/schema"
import { Session } from "../../src/session"
import { MessageV2 } from "../../src/session/message-v2"
import { SessionPrompt } from "../../src/session/prompt"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
Log.init({ print: false })
diff --git a/packages/opencode/test/session/revert-compact.test.ts b/packages/opencode/test/session/revert-compact.test.ts
index 679f6166f..211fcde9a 100644
--- a/packages/opencode/test/session/revert-compact.test.ts
+++ b/packages/opencode/test/session/revert-compact.test.ts
@@ -7,7 +7,7 @@ import { ModelID, ProviderID } from "../../src/provider/schema"
import { SessionRevert } from "../../src/session/revert"
import { MessageV2 } from "../../src/session/message-v2"
import { Snapshot } from "../../src/snapshot"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { MessageID, PartID, SessionID } from "../../src/session/schema"
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
import { provideTmpdirInstance } from "../fixture/fixture"
diff --git a/packages/opencode/test/session/session.test.ts b/packages/opencode/test/session/session.test.ts
index 15132a270..9c4686cba 100644
--- a/packages/opencode/test/session/session.test.ts
+++ b/packages/opencode/test/session/session.test.ts
@@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test"
import path from "path"
import { Session as SessionNs } from "../../src/session"
import { Bus } from "../../src/bus"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { Instance } from "../../src/project/instance"
import { MessageV2 } from "../../src/session/message-v2"
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
diff --git a/packages/opencode/test/session/snapshot-tool-race.test.ts b/packages/opencode/test/session/snapshot-tool-race.test.ts
index 3681b14f7..cb7fe4568 100644
--- a/packages/opencode/test/session/snapshot-tool-race.test.ts
+++ b/packages/opencode/test/session/snapshot-tool-race.test.ts
@@ -22,7 +22,7 @@ import { SessionPrompt } from "../../src/session/prompt"
import { SessionRevert } from "../../src/session/revert"
import { SessionSummary } from "../../src/session/summary"
import { MessageV2 } from "../../src/session/message-v2"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { provideTmpdirServer } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
import { TestLLMServer } from "../lib/llm-server"
diff --git a/packages/opencode/test/session/structured-output-integration.test.ts b/packages/opencode/test/session/structured-output-integration.test.ts
index 64266de47..346705bf2 100644
--- a/packages/opencode/test/session/structured-output-integration.test.ts
+++ b/packages/opencode/test/session/structured-output-integration.test.ts
@@ -3,7 +3,7 @@ import path from "path"
import { Effect, Layer } from "effect"
import { Session } from "../../src/session"
import { SessionPrompt } from "../../src/session/prompt"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { Instance } from "../../src/project/instance"
import { MessageV2 } from "../../src/session/message-v2"
diff --git a/packages/opencode/test/shell/shell.test.ts b/packages/opencode/test/shell/shell.test.ts
index 760d6dc05..6d7a77d72 100644
--- a/packages/opencode/test/shell/shell.test.ts
+++ b/packages/opencode/test/shell/shell.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, test } from "bun:test"
import path from "path"
import { Shell } from "../../src/shell/shell"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
const withShell = async (shell: string | undefined, fn: () => void | Promise<void>) => {
const prev = process.env.SHELL
diff --git a/packages/opencode/test/skill/discovery.test.ts b/packages/opencode/test/skill/discovery.test.ts
index de356ef15..175500862 100644
--- a/packages/opencode/test/skill/discovery.test.ts
+++ b/packages/opencode/test/skill/discovery.test.ts
@@ -2,7 +2,7 @@ import { describe, test, expect, beforeAll, afterAll } from "bun:test"
import { Effect } from "effect"
import { Discovery } from "../../src/skill/discovery"
import { Global } from "../../src/global"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { rm } from "fs/promises"
import path from "path"
diff --git a/packages/opencode/test/snapshot/snapshot.test.ts b/packages/opencode/test/snapshot/snapshot.test.ts
index 0c480a97c..3330b497c 100644
--- a/packages/opencode/test/snapshot/snapshot.test.ts
+++ b/packages/opencode/test/snapshot/snapshot.test.ts
@@ -5,7 +5,7 @@ import path from "path"
import { Effect } from "effect"
import { Snapshot } from "../../src/snapshot"
import { Instance } from "../../src/project/instance"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { provideInstance, tmpdir } from "../fixture/fixture"
// Git always outputs /-separated paths internally. Snapshot.patch() joins them
diff --git a/packages/opencode/test/tool/bash.test.ts b/packages/opencode/test/tool/bash.test.ts
index 19135ba98..6a3eac15e 100644
--- a/packages/opencode/test/tool/bash.test.ts
+++ b/packages/opencode/test/tool/bash.test.ts
@@ -5,7 +5,7 @@ import path from "path"
import { Shell } from "../../src/shell/shell"
import { BashTool } from "../../src/tool/bash"
import { Instance } from "../../src/project/instance"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
import type { Permission } from "../../src/permission"
import { Agent } from "../../src/agent/agent"
diff --git a/packages/opencode/test/tool/external-directory.test.ts b/packages/opencode/test/tool/external-directory.test.ts
index 727ab74f1..ee8cb5396 100644
--- a/packages/opencode/test/tool/external-directory.test.ts
+++ b/packages/opencode/test/tool/external-directory.test.ts
@@ -4,7 +4,7 @@ import { Effect } from "effect"
import type { Tool } from "../../src/tool/tool"
import { Instance } from "../../src/project/instance"
import { assertExternalDirectory } from "../../src/tool/external-directory"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
import type { Permission } from "../../src/permission"
import { SessionID, MessageID } from "../../src/session/schema"
diff --git a/packages/opencode/test/tool/read.test.ts b/packages/opencode/test/tool/read.test.ts
index f14ec3310..fa65068f8 100644
--- a/packages/opencode/test/tool/read.test.ts
+++ b/packages/opencode/test/tool/read.test.ts
@@ -13,7 +13,7 @@ import { Instruction } from "../../src/session/instruction"
import { ReadTool } from "../../src/tool/read"
import { Truncate } from "../../src/tool/truncate"
import { Tool } from "../../src/tool/tool"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { provideInstance, tmpdirScoped } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
diff --git a/packages/opencode/test/tool/truncation.test.ts b/packages/opencode/test/tool/truncation.test.ts
index c9ef0d82a..d0873046d 100644
--- a/packages/opencode/test/tool/truncation.test.ts
+++ b/packages/opencode/test/tool/truncation.test.ts
@@ -3,8 +3,8 @@ import { NodeFileSystem } from "@effect/platform-node"
import { Effect, FileSystem, Layer } from "effect"
import { Truncate } from "../../src/tool/truncate"
import { Identifier } from "../../src/id/id"
-import { Process } from "../../src/util/process"
-import { Filesystem } from "../../src/util/filesystem"
+import { Process } from "../../src/util"
+import { Filesystem } from "../../src/util"
import path from "path"
import { testEffect } from "../lib/effect"
import { writeFileStringScoped } from "../lib/filesystem"
diff --git a/packages/opencode/test/util/filesystem.test.ts b/packages/opencode/test/util/filesystem.test.ts
index 3abcf011b..1f3a66b95 100644
--- a/packages/opencode/test/util/filesystem.test.ts
+++ b/packages/opencode/test/util/filesystem.test.ts
@@ -1,7 +1,7 @@
import { describe, test, expect } from "bun:test"
import path from "path"
import fs from "fs/promises"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
describe("filesystem", () => {
diff --git a/packages/opencode/test/util/lock.test.ts b/packages/opencode/test/util/lock.test.ts
index b877311e3..d51b93648 100644
--- a/packages/opencode/test/util/lock.test.ts
+++ b/packages/opencode/test/util/lock.test.ts
@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test"
-import { Lock } from "../../src/util/lock"
+import { Lock } from "../../src/util"
function tick() {
return new Promise<void>((r) => queueMicrotask(r))
diff --git a/packages/opencode/test/util/log.test.ts b/packages/opencode/test/util/log.test.ts
index 33e64fcd0..336b16a17 100644
--- a/packages/opencode/test/util/log.test.ts
+++ b/packages/opencode/test/util/log.test.ts
@@ -2,7 +2,7 @@ import { afterEach, expect, test } from "bun:test"
import fs from "fs/promises"
import path from "path"
import { Global } from "../../src/global"
-import { Log } from "../../src/util/log"
+import { Log } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
const log = Global.Path.log
diff --git a/packages/opencode/test/util/module.test.ts b/packages/opencode/test/util/module.test.ts
index 6f8539bfb..6725149c7 100644
--- a/packages/opencode/test/util/module.test.ts
+++ b/packages/opencode/test/util/module.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, test } from "bun:test"
import path from "path"
import { Module } from "@opencode-ai/shared/util/module"
-import { Filesystem } from "../../src/util/filesystem"
+import { Filesystem } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
describe("util.module", () => {
diff --git a/packages/opencode/test/util/process.test.ts b/packages/opencode/test/util/process.test.ts
index 1d08cba6b..544202570 100644
--- a/packages/opencode/test/util/process.test.ts
+++ b/packages/opencode/test/util/process.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, test } from "bun:test"
import fs from "fs/promises"
import path from "path"
-import { Process } from "../../src/util/process"
+import { Process } from "../../src/util"
import { tmpdir } from "../fixture/fixture"
function node(script: string) {
diff --git a/packages/opencode/test/util/wildcard.test.ts b/packages/opencode/test/util/wildcard.test.ts
index 56e753d12..7c9b1e4ac 100644
--- a/packages/opencode/test/util/wildcard.test.ts
+++ b/packages/opencode/test/util/wildcard.test.ts
@@ -1,5 +1,5 @@
import { test, expect } from "bun:test"
-import { Wildcard } from "../../src/util/wildcard"
+import { Wildcard } from "../../src/util"
test("match handles glob tokens", () => {
expect(Wildcard.match("file1.txt", "file?.txt")).toBe(true)