Compare commits

..

1 Commits

Author SHA1 Message Date
Kit Langton 042e63fdc3 refactor(opencode): switch cli file io to AppFileSystem 2026-04-15 11:27:54 -04:00
15 changed files with 101 additions and 143 deletions
+9 -2
View File
@@ -33,7 +33,6 @@ import {
import { Log } from "../util/log"
import { pathToFileURL } from "url"
import { Filesystem } from "../util/filesystem"
import { Hash } from "../util/hash"
import { ACPSessionManager } from "./session"
import type { ACPConfig } from "./types"
@@ -48,7 +47,9 @@ import { Todo } from "@/session/todo"
import { z } from "zod"
import { LoadAPIKeyError } from "ai"
import type { AssistantMessage, Event, OpencodeClient, SessionMessageResponse, ToolPart } from "@opencode-ai/sdk/v2"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { applyPatch } from "diff"
import { Effect } from "effect"
type ModeOption = { id: string; name: string; description?: string }
type ModelOption = { modelId: string; name: string }
@@ -238,7 +239,13 @@ export namespace ACP {
const metadata = permission.metadata || {}
const filepath = typeof metadata["filepath"] === "string" ? metadata["filepath"] : ""
const diff = typeof metadata["diff"] === "string" ? metadata["diff"] : ""
const content = (await Filesystem.exists(filepath)) ? await Filesystem.readText(filepath) : ""
const content = await AppRuntime.runPromise(
AppFileSystem.Service.use((fs) =>
fs
.existsSafe(filepath)
.pipe(Effect.flatMap((exists) => (exists ? fs.readFileString(filepath) : Effect.succeed("")))),
),
)
const newContent = getNewContent(content, diff)
if (newContent) {
+3 -3
View File
@@ -7,11 +7,11 @@ import { Agent } from "../../agent/agent"
import { Provider } from "../../provider/provider"
import path from "path"
import fs from "fs/promises"
import { Filesystem } from "../../util/filesystem"
import matter from "gray-matter"
import { Instance } from "../../project/instance"
import { EOL } from "os"
import type { Argv } from "yargs"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
type AgentMode = "all" | "primary" | "subagent"
@@ -194,7 +194,7 @@ const AgentCreateCommand = cmd({
await fs.mkdir(targetPath, { recursive: true })
if (await Filesystem.exists(filePath)) {
if (await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.existsSafe(filePath)))) {
if (isFullyNonInteractive) {
console.error(`Error: Agent file already exists: ${filePath}`)
process.exit(1)
@@ -203,7 +203,7 @@ const AgentCreateCommand = cmd({
throw new UI.CancelledError()
}
await Filesystem.write(filePath, content)
await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.writeWithDirs(filePath, content)))
if (isFullyNonInteractive) {
console.log(filePath)
+8 -4
View File
@@ -1,6 +1,5 @@
import path from "path"
import { exec } from "child_process"
import { Filesystem } from "../../util/filesystem"
import * as prompts from "@clack/prompts"
import { map, pipe, sortBy, values } from "remeda"
import { Octokit } from "@octokit/rest"
@@ -34,6 +33,7 @@ import { Git } from "@/git"
import { setTimeout as sleep } from "node:timers/promises"
import { Process } from "@/util/process"
import { Effect } from "effect"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
type GitHubAuthor = {
login: string
@@ -381,9 +381,11 @@ export const GithubInstallCommand = cmd({
? ""
: `\n env:${providers[provider].env.map((e) => `\n ${e}: \${{ secrets.${e} }}`).join("")}`
await Filesystem.write(
path.join(app.root, WORKFLOW_FILE),
`name: opencode
await AppRuntime.runPromise(
AppFileSystem.Service.use((fs) =>
fs.writeWithDirs(
path.join(app.root, WORKFLOW_FILE),
`name: opencode
on:
issue_comment:
@@ -414,6 +416,8 @@ jobs:
uses: anomalyco/opencode/github@latest${envStr}
with:
model: ${provider}/${model}`,
),
),
)
prompts.log.success(`Added workflow file: "${WORKFLOW_FILE}"`)
+4 -2
View File
@@ -9,8 +9,8 @@ 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 { AppRuntime } from "@/effect/app-runtime"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
/** Discriminated union returned by the ShareNext API (GET /api/shares/:id/data) */
export type ShareData =
@@ -140,7 +140,9 @@ export const ImportCommand = cmd({
exportData = transformed
} else {
exportData = await Filesystem.readJson<NonNullable<typeof exportData>>(args.file).catch(() => undefined)
exportData = (await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.readJson(args.file))).catch(
() => undefined,
)) as NonNullable<typeof exportData> | undefined
if (!exportData) {
process.stdout.write(`File not found: ${args.file}`)
process.stdout.write(EOL)
+5 -5
View File
@@ -13,10 +13,10 @@ import { Installation } from "../../installation"
import path from "path"
import { Global } from "../../global"
import { modify, applyEdits } from "jsonc-parser"
import { Filesystem } from "../../util/filesystem"
import { Bus } from "../../bus"
import { AppRuntime } from "../../effect/app-runtime"
import { Effect } from "effect"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
function getAuthStatusIcon(status: MCP.AuthStatus): string {
switch (status) {
@@ -416,7 +416,7 @@ async function resolveConfigPath(baseDir: string, global = false) {
}
for (const candidate of candidates) {
if (await Filesystem.exists(candidate)) {
if (await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.existsSafe(candidate)))) {
return candidate
}
}
@@ -427,8 +427,8 @@ async function resolveConfigPath(baseDir: string, global = false) {
async function addMcpToConfig(name: string, mcpConfig: Config.Mcp, configPath: string) {
let text = "{}"
if (await Filesystem.exists(configPath)) {
text = await Filesystem.readText(configPath)
if (await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.existsSafe(configPath)))) {
text = await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.readFileString(configPath)))
}
// Use jsonc-parser to modify while preserving comments
@@ -437,7 +437,7 @@ async function addMcpToConfig(name: string, mcpConfig: Config.Mcp, configPath: s
})
const result = applyEdits(text, edits)
await Filesystem.write(configPath, result)
await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.writeWithDirs(configPath, result)))
return configPath
}
+5 -3
View File
@@ -1,12 +1,12 @@
import type { Argv } from "yargs"
import path from "path"
import { pathToFileURL } from "url"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { UI } from "../ui"
import { cmd } from "./cmd"
import { Flag } from "../../flag/flag"
import { bootstrap } from "../bootstrap"
import { EOL } from "os"
import { Filesystem } from "../../util/filesystem"
import { createOpencodeClient, type OpencodeClient, type ToolPart } from "@opencode-ai/sdk/v2"
import { Server } from "../../server/server"
import { Provider } from "../../provider/provider"
@@ -332,12 +332,14 @@ export const RunCommand = cmd({
for (const filePath of list) {
const resolvedPath = path.resolve(process.cwd(), filePath)
if (!(await Filesystem.exists(resolvedPath))) {
if (!(await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.existsSafe(resolvedPath))))) {
UI.error(`File not found: ${filePath}`)
process.exit(1)
}
const mime = (await Filesystem.isDir(resolvedPath)) ? "application/x-directory" : "text/plain"
const mime = (await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.isDir(resolvedPath))))
? "application/x-directory"
: "text/plain"
files.push({
type: "file",
@@ -1,6 +1,5 @@
import { CliRenderEvents, SyntaxStyle, RGBA, type TerminalColors } from "@opentui/core"
import path from "path"
import { existsSync } from "fs"
import { createEffect, createMemo, onCleanup, onMount } from "solid-js"
import { createSimpleContext } from "./helper"
import { Glob } from "@opencode-ai/shared/util/glob"
@@ -41,6 +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 { useTuiConfig } from "./tui-config"
import { isRecord } from "@/util/record"
import type { TuiThemeCurrent } from "@opencode-ai/plugin/tui"
@@ -477,19 +477,15 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
})
async function getCustomThemes() {
const ups = (start: string) => {
const out: string[] = []
let dir = start
while (true) {
const next = path.join(dir, ".opencode")
if (existsSync(next)) out.push(next)
const parent = path.dirname(dir)
if (parent === dir) return out
dir = parent
}
}
const directories = [Global.Path.config, ...ups(process.cwd())]
const directories = [
Global.Path.config,
...(await Array.fromAsync(
Filesystem.up({
targets: [".opencode"],
start: process.cwd(),
}),
)),
]
const result: Record<string, ThemeJson> = {}
for (const dir of directories) {
@@ -500,7 +496,7 @@ async function getCustomThemes() {
symlink: true,
})) {
const name = path.basename(item, ".json")
result[name] = (await Bun.file(item).json()) as ThemeJson
result[name] = await Filesystem.readJson(item)
}
}
return result
+6 -4
View File
@@ -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 { AppFileSystem } from "@opencode-ai/shared/filesystem"
interface UninstallArgs {
keepConfig: boolean
@@ -266,7 +266,9 @@ async function getShellConfigFile(): Promise<string | null> {
.catch(() => false)
if (!exists) continue
const content = await Filesystem.readText(file).catch(() => "")
const content = await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.readFileString(file))).catch(
() => "",
)
if (content.includes("# opencode") || content.includes(".opencode/bin")) {
return file
}
@@ -276,7 +278,7 @@ async function getShellConfigFile(): Promise<string | null> {
}
async function cleanShellConfig(file: string) {
const content = await Filesystem.readText(file)
const content = await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.readFileString(file)))
const lines = content.split("\n")
const filtered: string[] = []
@@ -312,7 +314,7 @@ async function cleanShellConfig(file: string) {
}
const output = filtered.join("\n") + "\n"
await Filesystem.write(file, output)
await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.writeWithDirs(file, output)))
}
async function getDirectorySize(dir: string): Promise<number> {
+3 -2
View File
@@ -1,7 +1,8 @@
import { NamedError } from "@opencode-ai/shared/util/error"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import matter from "gray-matter"
import { z } from "zod"
import { Filesystem } from "../util/filesystem"
import { AppRuntime } from "@/effect/app-runtime"
export namespace ConfigMarkdown {
export const FILE_REGEX = /(?<![\w`])@(\.?[^\s`,.]*(?:\.[^\s`,.]+)*)/g
@@ -69,7 +70,7 @@ export namespace ConfigMarkdown {
}
export async function parse(filePath: string) {
const template = await Filesystem.readText(filePath)
const template = await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.readFileString(filePath)))
try {
const md = matter(template)
+10 -54
View File
@@ -2,74 +2,30 @@ import path from "path"
import os from "os"
import z from "zod"
import { type ParseError as JsoncParseError, parse as parseJsonc, printParseErrorCode } from "jsonc-parser"
import { Effect } from "effect"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { NamedError } from "@opencode-ai/shared/util/error"
import { Filesystem } from "@/util/filesystem"
import { Flag } from "@/flag/flag"
import { Global } from "@/global"
import { AppRuntime } from "@/effect/app-runtime"
async function withFs<A>(fn: (fs: AppFileSystem.Interface) => Effect.Effect<A, AppFileSystem.Error>) {
return AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
return yield* fn(fs)
}),
)
}
function missing(err: unknown) {
if (typeof err !== "object" || err === null) return false
if ("code" in err && err.code === "ENOENT") return true
return (
"reason" in err &&
typeof err.reason === "object" &&
err.reason !== null &&
"_tag" in err.reason &&
err.reason._tag === "NotFound"
)
}
export namespace ConfigPaths {
export async function projectFiles(name: string, directory: string, worktree: string) {
return withFs(
Effect.fn("ConfigPaths.projectFiles")(function* (fs) {
const dirs = [directory]
let dir = directory
while (true) {
if (worktree === dir) break
const parent = path.dirname(dir)
if (parent === dir) break
dirs.push(parent)
dir = parent
}
const out: string[] = []
for (const dir of dirs.toReversed()) {
for (const target of [`${name}.json`, `${name}.jsonc`]) {
const file = path.join(dir, target)
if (yield* fs.existsSafe(file)) out.push(file)
}
}
return out
}),
)
return Filesystem.findUp([`${name}.json`, `${name}.jsonc`], directory, worktree, { rootFirst: true })
}
export async function directories(directory: string, worktree: string) {
return [
Global.Path.config,
...(!Flag.OPENCODE_DISABLE_PROJECT_CONFIG
? await withFs((fs) =>
fs.up({
? await Array.fromAsync(
Filesystem.up({
targets: [".opencode"],
start: directory,
stop: worktree,
}),
)
: []),
...(await withFs((fs) =>
fs.up({
...(await Array.fromAsync(
Filesystem.up({
targets: [".opencode"],
start: Global.Path.home,
stop: Global.Path.home,
@@ -102,8 +58,8 @@ export namespace ConfigPaths {
/** Read a config file, returning undefined for missing files and throwing JsonError for other failures. */
export async function readFile(filepath: string) {
return withFs((fs) => fs.readFileString(filepath)).catch((err: unknown) => {
if (missing(err)) return
return Filesystem.readText(filepath).catch((err: NodeJS.ErrnoException) => {
if (err.code === "ENOENT") return
throw new JsonError({ path: filepath }, { cause: err })
})
}
@@ -152,11 +108,11 @@ export namespace ConfigPaths {
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(configDir, filePath)
const fileContent = (
await withFs((fs) => fs.readFileString(resolvedPath)).catch((error: unknown) => {
await Filesystem.readText(resolvedPath).catch((error: NodeJS.ErrnoException) => {
if (missing === "empty") return ""
const errMsg = `bad file reference: "${token}"`
if (missing(error)) {
if (error.code === "ENOENT") {
throw new InvalidError(
{
path: configSource,
@@ -9,9 +9,9 @@ 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 { ProjectID } from "@/project/schema"
import { Slug } from "@opencode-ai/shared/util/slug"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { WorkspaceTable } from "./workspace.sql"
import { getAdaptor } from "./adaptors"
import { WorkspaceInfo } from "./types"
@@ -418,7 +418,7 @@ export namespace Workspace {
if (!Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) return
if (space.type === "worktree") {
void Filesystem.exists(space.directory!).then((exists) => {
void AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.existsSafe(space.directory!))).then((exists) => {
setStatus(space.id, exists ? "connected" : "error", exists ? undefined : "directory does not exist")
})
return
+6 -5
View File
@@ -1,11 +1,11 @@
import fs from "fs/promises"
import path from "path"
import { fileURLToPath } from "url"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import z from "zod"
import { Cause, Context, Effect, Layer, Queue, Stream } from "effect"
import { ripgrep } from "ripgrep"
import { makeRuntime } from "@/effect/run-service"
import { Filesystem } from "@/util/filesystem"
import { Log } from "@/util/log"
export namespace Ripgrep {
@@ -275,10 +275,11 @@ export namespace Ripgrep {
return Effect.succeed(OPENCODE_RIPGREP_WORKER_PATH)
}
const js = new URL("./ripgrep.worker.js", import.meta.url)
return Effect.tryPromise({
try: () => Filesystem.exists(fileURLToPath(js)),
catch: toError,
}).pipe(Effect.map((exists) => (exists ? js : new URL("./ripgrep.worker.ts", import.meta.url))))
return Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
const exists = yield* fs.exists(fileURLToPath(js)).pipe(Effect.orElseSucceed(() => false))
return exists ? js : new URL("./ripgrep.worker.ts", import.meta.url)
})
}
function worker() {
+14 -37
View File
@@ -1,33 +1,10 @@
import { Effect } from "effect"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { Npm } from "../npm"
import { Instance } from "../project/instance"
import { Filesystem } from "../util/filesystem"
import { Process } from "../util/process"
import { which } from "../util/which"
import { AppRuntime } from "@/effect/app-runtime"
import { Flag } from "@/flag/flag"
async function withFs<A>(fn: (fs: AppFileSystem.Interface) => Effect.Effect<A, AppFileSystem.Error>) {
return AppRuntime.runPromise(
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
return yield* fn(fs)
}),
)
}
async function find(target: string) {
return withFs((fs) => fs.findUp(target, Instance.directory, Instance.worktree))
}
async function readJson<T>(file: string) {
return withFs((fs) => fs.readJson(file).pipe(Effect.map((json) => json as T)))
}
async function readText(file: string) {
return withFs((fs) => fs.readFileString(file))
}
export interface Info {
name: string
environment?: Record<string, string>
@@ -89,9 +66,9 @@ export const prettier: Info = {
".gql",
],
async enabled() {
const items = await find("package.json")
const items = await Filesystem.findUp("package.json", Instance.directory, Instance.worktree)
for (const item of items) {
const json = await readJson<{
const json = await Filesystem.readJson<{
dependencies?: Record<string, string>
devDependencies?: Record<string, string>
}>(item)
@@ -112,9 +89,9 @@ export const oxfmt: Info = {
extensions: [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts"],
async enabled() {
if (!Flag.OPENCODE_EXPERIMENTAL_OXFMT) return false
const items = await find("package.json")
const items = await Filesystem.findUp("package.json", Instance.directory, Instance.worktree)
for (const item of items) {
const json = await readJson<{
const json = await Filesystem.readJson<{
dependencies?: Record<string, string>
devDependencies?: Record<string, string>
}>(item)
@@ -163,7 +140,7 @@ export const biome: Info = {
async enabled() {
const configs = ["biome.json", "biome.jsonc"]
for (const config of configs) {
const found = await find(config)
const found = await Filesystem.findUp(config, Instance.directory, Instance.worktree)
if (found.length > 0) {
const bin = await Npm.which("@biomejs/biome")
if (bin) return [bin, "format", "--write", "$FILE"]
@@ -187,7 +164,7 @@ export const clang: Info = {
name: "clang-format",
extensions: [".c", ".cc", ".cpp", ".cxx", ".c++", ".h", ".hh", ".hpp", ".hxx", ".h++", ".ino", ".C", ".H"],
async enabled() {
const items = await find(".clang-format")
const items = await Filesystem.findUp(".clang-format", Instance.directory, Instance.worktree)
if (items.length > 0) {
const match = which("clang-format")
if (match) return [match, "-i", "$FILE"]
@@ -213,10 +190,10 @@ export const ruff: Info = {
if (!which("ruff")) return false
const configs = ["pyproject.toml", "ruff.toml", ".ruff.toml"]
for (const config of configs) {
const found = await find(config)
const found = await Filesystem.findUp(config, Instance.directory, Instance.worktree)
if (found.length > 0) {
if (config === "pyproject.toml") {
const content = await readText(found[0])
const content = await Filesystem.readText(found[0])
if (content.includes("[tool.ruff]")) return ["ruff", "format", "$FILE"]
} else {
return ["ruff", "format", "$FILE"]
@@ -225,9 +202,9 @@ export const ruff: Info = {
}
const deps = ["requirements.txt", "pyproject.toml", "Pipfile"]
for (const dep of deps) {
const found = await find(dep)
const found = await Filesystem.findUp(dep, Instance.directory, Instance.worktree)
if (found.length > 0) {
const content = await readText(found[0])
const content = await Filesystem.readText(found[0])
if (content.includes("ruff")) return ["ruff", "format", "$FILE"]
}
}
@@ -311,7 +288,7 @@ export const ocamlformat: Info = {
extensions: [".ml", ".mli"],
async enabled() {
if (!which("ocamlformat")) return false
const items = await find(".ocamlformat")
const items = await Filesystem.findUp(".ocamlformat", Instance.directory, Instance.worktree)
if (items.length > 0) return ["ocamlformat", "-i", "$FILE"]
return false
},
@@ -381,9 +358,9 @@ export const pint: Info = {
name: "pint",
extensions: [".php"],
async enabled() {
const items = await find("composer.json")
const items = await Filesystem.findUp("composer.json", Instance.directory, Instance.worktree)
for (const item of items) {
const json = await readJson<{
const json = await Filesystem.readJson<{
require?: Record<string, string>
"require-dev"?: Record<string, string>
}>(item)
+12 -3
View File
@@ -1,8 +1,9 @@
import fs from "fs/promises"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { xdgData, xdgCache, xdgConfig, xdgState } from "xdg-basedir"
import path from "path"
import os from "os"
import { Filesystem } from "../util/filesystem"
import { Effect } from "effect"
const app = "opencode"
@@ -36,7 +37,11 @@ await Promise.all([
const CACHE_VERSION = "21"
const version = await Filesystem.readText(path.join(Global.Path.cache, "version")).catch(() => "0")
const version = await Effect.runPromise(
AppFileSystem.Service.use((fs) => fs.readFileString(path.join(Global.Path.cache, "version"))).pipe(
Effect.provide(AppFileSystem.defaultLayer),
),
).catch(() => "0")
if (version !== CACHE_VERSION) {
try {
@@ -50,5 +55,9 @@ if (version !== CACHE_VERSION) {
),
)
} catch (e) {}
await Filesystem.write(path.join(Global.Path.cache, "version"), CACHE_VERSION)
await Effect.runPromise(
AppFileSystem.Service.use((fs) => fs.writeWithDirs(path.join(Global.Path.cache, "version"), CACHE_VERSION)).pipe(
Effect.provide(AppFileSystem.defaultLayer),
),
)
}
+3 -2
View File
@@ -11,10 +11,10 @@ import { UninstallCommand } from "./cli/cmd/uninstall"
import { ModelsCommand } from "./cli/cmd/models"
import { UI } from "./cli/ui"
import { Installation } from "./installation"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
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 { DebugCommand } from "./cli/cmd/debug"
import { StatsCommand } from "./cli/cmd/stats"
import { McpCommand } from "./cli/cmd/mcp"
@@ -37,6 +37,7 @@ import { errorMessage } from "./util/error"
import { PluginCommand } from "./cli/cmd/plug"
import { Heap } from "./cli/heap"
import { drizzle } from "drizzle-orm/bun-sqlite"
import { AppRuntime } from "@/effect/app-runtime"
process.on("unhandledRejection", (e) => {
Log.Default.error("rejection", {
@@ -110,7 +111,7 @@ const cli = yargs(args)
})
const marker = path.join(Global.Path.data, "opencode.db")
if (!(await Filesystem.exists(marker))) {
if (!(await AppRuntime.runPromise(AppFileSystem.Service.use((fs) => fs.existsSafe(marker))))) {
const tty = process.stderr.isTTY
process.stderr.write("Performing one time database migration, may take a few minutes..." + EOL)
const width = 36