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 3ea8826ef8bd..052f33ca54d6 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/frecency.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/frecency.tsx @@ -1,10 +1,9 @@ import path from "path" import { Global } from "@/global" -import { Filesystem } from "@/util/filesystem" import { onMount } from "solid-js" import { createStore } from "solid-js/store" import { createSimpleContext } from "../../context/helper" -import { appendFile, writeFile } from "fs/promises" +import { appendFile } from "fs/promises" function calculateFrecency(entry?: { frequency: number; lastOpen: number }): number { if (!entry) return 0 @@ -20,7 +19,9 @@ export const { use: useFrecency, provider: FrecencyProvider } = createSimpleCont init: () => { const frecencyPath = path.join(Global.Path.state, "frecency.jsonl") onMount(async () => { - const text = await Filesystem.readText(frecencyPath).catch(() => "") + const text = await Bun.file(frecencyPath) + .text() + .catch(() => "") const lines = text .split("\n") .filter(Boolean) @@ -54,7 +55,7 @@ export const { use: useFrecency, provider: FrecencyProvider } = createSimpleCont if (sorted.length > 0) { const content = sorted.map((entry) => JSON.stringify(entry)).join("\n") + "\n" - writeFile(frecencyPath, content).catch(() => {}) + void Bun.write(frecencyPath, content) } }) @@ -77,7 +78,7 @@ export const { use: useFrecency, provider: FrecencyProvider } = createSimpleCont .slice(0, MAX_FRECENCY_ENTRIES) setStore("data", Object.fromEntries(sorted)) const content = sorted.map(([path, entry]) => JSON.stringify({ path, ...entry })).join("\n") + "\n" - writeFile(frecencyPath, content).catch(() => {}) + void Bun.write(frecencyPath, content) } } 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 d49dd5c7b699..f9aa97f9e302 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/history.tsx @@ -1,10 +1,9 @@ import path from "path" import { Global } from "@/global" -import { Filesystem } from "@/util/filesystem" import { onMount } from "solid-js" import { createStore, produce, unwrap } from "solid-js/store" import { createSimpleContext } from "../../context/helper" -import { appendFile, writeFile } from "fs/promises" +import { appendFile } from "fs/promises" import type { AgentPart, FilePart, TextPart } from "@opencode-ai/sdk/v2" export type PromptInfo = { @@ -32,7 +31,9 @@ export const { use: usePromptHistory, provider: PromptHistoryProvider } = create init: () => { const historyPath = path.join(Global.Path.state, "prompt-history.jsonl") onMount(async () => { - const text = await Filesystem.readText(historyPath).catch(() => "") + const text = await Bun.file(historyPath) + .text() + .catch(() => "") const lines = text .split("\n") .filter(Boolean) @@ -51,7 +52,7 @@ export const { use: usePromptHistory, provider: PromptHistoryProvider } = create // Rewrite file with only valid entries to self-heal corruption if (lines.length > 0) { const content = lines.map((line) => JSON.stringify(line)).join("\n") + "\n" - writeFile(historyPath, content).catch(() => {}) + void Bun.write(historyPath, content) } }) @@ -97,7 +98,7 @@ export const { use: usePromptHistory, provider: PromptHistoryProvider } = create if (trimmed) { const content = store.history.map((line) => JSON.stringify(line)).join("\n") + "\n" - writeFile(historyPath, content).catch(() => {}) + void Bun.write(historyPath, content) return } 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 ef3eb329a995..8b155ecc1200 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/stash.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/stash.tsx @@ -1,10 +1,9 @@ import path from "path" import { Global } from "@/global" -import { Filesystem } from "@/util/filesystem" import { onMount } from "solid-js" import { createStore, produce, unwrap } from "solid-js/store" import { createSimpleContext } from "../../context/helper" -import { appendFile, writeFile } from "fs/promises" +import { appendFile } from "fs/promises" import type { PromptInfo } from "./history" export type StashEntry = { @@ -20,7 +19,9 @@ export const { use: usePromptStash, provider: PromptStashProvider } = createSimp init: () => { const stashPath = path.join(Global.Path.state, "prompt-stash.jsonl") onMount(async () => { - const text = await Filesystem.readText(stashPath).catch(() => "") + const text = await Bun.file(stashPath) + .text() + .catch(() => "") const lines = text .split("\n") .filter(Boolean) @@ -39,7 +40,7 @@ export const { use: usePromptStash, provider: PromptStashProvider } = createSimp // Rewrite file with only valid entries to self-heal corruption if (lines.length > 0) { const content = lines.map((line) => JSON.stringify(line)).join("\n") + "\n" - writeFile(stashPath, content).catch(() => {}) + void Bun.write(stashPath, content) } }) @@ -66,7 +67,7 @@ export const { use: usePromptStash, provider: PromptStashProvider } = createSimp if (trimmed) { const content = store.entries.map((line) => JSON.stringify(line)).join("\n") + "\n" - writeFile(stashPath, content).catch(() => {}) + void Bun.write(stashPath, content) return } @@ -82,7 +83,7 @@ export const { use: usePromptStash, provider: PromptStashProvider } = createSimp ) const content = store.entries.length > 0 ? store.entries.map((line) => JSON.stringify(line)).join("\n") + "\n" : "" - writeFile(stashPath, content).catch(() => {}) + void Bun.write(stashPath, content) return entry }, remove(index: number) { @@ -94,7 +95,7 @@ export const { use: usePromptStash, provider: PromptStashProvider } = createSimp ) const content = store.entries.length > 0 ? store.entries.map((line) => JSON.stringify(line)).join("\n") + "\n" : "" - writeFile(stashPath, content).catch(() => {}) + void Bun.write(stashPath, content) }, } }, diff --git a/packages/opencode/src/cli/cmd/tui/context/kv.tsx b/packages/opencode/src/cli/cmd/tui/context/kv.tsx index 7a52156f8823..6016a3fee1bb 100644 --- a/packages/opencode/src/cli/cmd/tui/context/kv.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/kv.tsx @@ -1,5 +1,4 @@ import { Global } from "@/global" -import { Filesystem } from "@/util/filesystem" import { createSignal, type Setter } from "solid-js" import { createStore } from "solid-js/store" import { createSimpleContext } from "./helper" @@ -12,9 +11,10 @@ export const { use: useKV, provider: KVProvider } = createSimpleContext({ const [store, setStore] = createStore>() const filePath = path.join(Global.Path.state, "kv.json") - Filesystem.readJson(filePath) + Bun.file(filePath) + .json() .then((x) => { - setStore(x) + if (typeof x === "object" && x !== null) setStore(x as Record) }) .catch(() => {}) .finally(() => { @@ -44,7 +44,7 @@ export const { use: useKV, provider: KVProvider } = createSimpleContext({ }, set(key: string, value: any) { setStore(key, value) - Filesystem.writeJson(filePath, store) + void Bun.write(filePath, JSON.stringify(store, null, 2)) }, } return result diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx index ec3931b209ad..7c58f195e048 100644 --- a/packages/opencode/src/cli/cmd/tui/context/local.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx @@ -12,7 +12,6 @@ import { Provider } from "@/provider/provider" import { useArgs } from "./args" import { useSDK } from "./sdk" import { RGBA } from "@opentui/core" -import { Filesystem } from "@/util/filesystem" export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ name: "Local", @@ -131,14 +130,22 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ return } state.pending = false - Filesystem.writeJson(filePath, { - recent: modelStore.recent, - favorite: modelStore.favorite, - variant: modelStore.variant, - }) + void Bun.write( + filePath, + JSON.stringify( + { + recent: modelStore.recent, + favorite: modelStore.favorite, + variant: modelStore.variant, + }, + null, + 2, + ), + ) } - Filesystem.readJson(filePath) + Bun.file(filePath) + .json() .then((x: any) => { if (Array.isArray(x.recent)) setModelStore("recent", x.recent) if (Array.isArray(x.favorite)) setModelStore("favorite", x.favorite) 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 c7790006f41a..584d0edcc64e 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -74,8 +74,8 @@ 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 { Global } from "@/global" +import { AppFileSystem } from "@opencode-ai/shared/filesystem" import { PermissionPrompt } from "./permission" import { QuestionPrompt } from "./question" import { DialogExportOptions } from "../../ui/dialog-export-options" @@ -916,12 +916,12 @@ export function Session() { const filename = options.filename.trim() const filepath = path.join(exportDir, filename) - await Filesystem.write(filepath, transcript) + await Bun.write(filepath, transcript) // Open with EDITOR if available const result = await Editor.open({ value: transcript, renderer }) if (result !== undefined) { - await Filesystem.write(filepath, result) + await Bun.write(filepath, result) } toast.show({ message: `Session exported to ${filename}`, variant: "success" }) @@ -2236,7 +2236,7 @@ function Skill(props: ToolProps) { function Diagnostics(props: { diagnostics?: Record[]>; filePath: string }) { const { theme } = useTheme() const errors = createMemo(() => { - const normalized = Filesystem.normalizePath(props.filePath) + const normalized = AppFileSystem.normalizePath(props.filePath) const arr = props.diagnostics?.[normalized] ?? [] return arr.filter((x) => x.severity === 1).slice(0, 3) }) diff --git a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts index 87c0a63abc82..4533ff919464 100644 --- a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts +++ b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts @@ -4,7 +4,6 @@ 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 { which } from "../../../../util/which" @@ -58,8 +57,7 @@ export namespace Clipboard { ], { nothrow: true }, ) - const buffer = await Filesystem.readBytes(tmpfile) - return { data: buffer.toString("base64"), mime: "image/png" } + return { data: Buffer.from(await Bun.file(tmpfile).arrayBuffer()).toString("base64"), mime: "image/png" } } catch { } finally { await fs.rm(tmpfile, { force: true }).catch(() => {}) diff --git a/packages/opencode/src/cli/cmd/tui/util/editor.ts b/packages/opencode/src/cli/cmd/tui/util/editor.ts index 9eaae99fce78..15bb07c579aa 100644 --- a/packages/opencode/src/cli/cmd/tui/util/editor.ts +++ b/packages/opencode/src/cli/cmd/tui/util/editor.ts @@ -3,7 +3,6 @@ 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" export namespace Editor { @@ -14,7 +13,7 @@ export namespace Editor { const filepath = join(tmpdir(), `${Date.now()}.md`) await using _ = defer(async () => rm(filepath, { force: true })) - await Filesystem.write(filepath, opts.value) + await Bun.write(filepath, opts.value) opts.renderer.suspend() opts.renderer.currentRenderBuffer.clear() try { @@ -26,7 +25,7 @@ export namespace Editor { shell: process.platform === "win32", }) await proc.exited - const content = await Filesystem.readText(filepath) + const content = await Bun.file(filepath).text() return content || undefined } finally { opts.renderer.currentRenderBuffer.clear()