Просмотр исходного кода

Refactor application path handling and data storage architecture

Replace simple directory-based path system with git-aware data management that uses global data directories and proper workspace detection.

🤖 Generated with opencode
Co-Authored-By: opencode <[email protected]>
Dax Raad 10 месяцев назад
Родитель
Сommit
526a8ea19a

+ 19 - 14
packages/opencode/src/app/app.ts

@@ -1,7 +1,9 @@
 import fs from "fs/promises"
-import { AppPath } from "./path"
 import { Log } from "../util/log"
 import { Context } from "../util/context"
+import { Filesystem } from "../util/filesystem"
+import { Global } from "../global"
+import path from "path"
 
 export namespace App {
   const log = Log.create({ service: "app" })
@@ -10,12 +12,13 @@ export namespace App {
 
   const ctx = Context.create<Info>("app")
 
-  async function create(input: { directory: string }) {
-    const dataDir = AppPath.data(input.directory)
-    await fs.mkdir(dataDir, { recursive: true })
-    await Log.file(input.directory)
+  async function create(input: { cwd: string; version: string }) {
+    let root = await Filesystem.findUp(".git", input.cwd).then((x) =>
+      x ? path.dirname(x) : input.cwd,
+    )
 
-    log.info("created", { path: dataDir })
+    const data = path.join(Global.data(), root)
+    await Bun.write(path.join(data, "version"), input.version)
 
     const services = new Map<
       any,
@@ -25,14 +28,16 @@ export namespace App {
       }
     >()
 
-    const result = {
-      get services() {
-        return services
-      },
-      get root() {
-        return input.directory
+    await Log.file(path.join(data, "log"))
+
+    const result = Object.freeze({
+      services,
+      path: {
+        data,
+        root,
+        cwd: input.cwd,
       },
-    }
+    })
 
     return result
   }
@@ -61,7 +66,7 @@ export namespace App {
   }
 
   export async function provide<T extends (app: Info) => any>(
-    input: { directory: string },
+    input: { cwd: string; version: string },
     cb: T,
   ) {
     const app = await create(input)

+ 0 - 11
packages/opencode/src/app/path.ts

@@ -1,11 +0,0 @@
-import path from "path"
-
-export namespace AppPath {
-  export function data(input: string) {
-    return path.join(input, ".opencode")
-  }
-
-  export function storage(input: string) {
-    return path.join(data(input), "storage")
-  }
-}

+ 1 - 1
packages/opencode/src/config/config.ts

@@ -8,7 +8,7 @@ export namespace Config {
   const log = Log.create({ service: "config" })
 
   export const state = App.state("config", async (app) => {
-    const result = await load(app.root)
+    const result = await load(app.path.root)
     return result
   })
 

+ 4 - 0
packages/opencode/src/global/index.ts

@@ -17,4 +17,8 @@ export namespace Global {
   export function cache() {
     return paths.cache
   }
+
+  export function data() {
+    return paths.data
+  }
 }

+ 1 - 0
packages/opencode/src/id/id.ts

@@ -5,6 +5,7 @@ export namespace Identifier {
   const prefixes = {
     session: "ses",
     message: "msg",
+    user: "usr",
   } as const
 
   export function schema(prefix: keyof typeof prefixes) {

+ 4 - 3
packages/opencode/src/index.ts

@@ -16,9 +16,10 @@ declare global {
 }
 
 const cli = cac("opencode")
+const version = typeof OPENCODE_VERSION === "string" ? OPENCODE_VERSION : "dev"
 
 cli.command("", "Start the opencode in interactive mode").action(async () => {
-  await App.provide({ directory: process.cwd() }, async () => {
+  await App.provide({ cwd: process.cwd(), version }, async () => {
     await Share.init()
     const server = Server.listen()
 
@@ -66,14 +67,14 @@ cli
   .command("run [...message]", "Run a chat message")
   .option("--session <id>", "Session ID")
   .action(async (message: string[], options) => {
-    await App.provide({ directory: process.cwd() }, async () => {
+    await App.provide({ cwd: process.cwd(), version }, async () => {
       await Share.init()
       const session = options.session
         ? await Session.get(options.session)
         : await Session.create()
       console.log("Session:", session.id)
 
-      Bus.subscribe(Message.Event.Updated, async (message) => {
+      Bus.subscribe(Message.Event.Updated, async () => {
         console.log("Thinking...")
       })
 

+ 2 - 2
packages/opencode/src/lsp/client.ts

@@ -36,7 +36,7 @@ export namespace LSPClient {
     const [command, ...args] = input.cmd
     const server = spawn(command, args, {
       stdio: ["pipe", "pipe", "pipe"],
-      cwd: app.root,
+      cwd: app.path.cwd,
     })
 
     const connection = createMessageConnection(
@@ -64,7 +64,7 @@ export namespace LSPClient {
         workspaceFolders: [
           {
             name: "workspace",
-            uri: "file://" + app.root,
+            uri: "file://" + app.path.cwd,
           },
         ],
         tsserver: {

+ 1 - 1
packages/opencode/src/session/session.ts

@@ -220,7 +220,7 @@ export namespace Session {
           tool: {},
         },
       }
-      const contextFile = Bun.file(path.join(app.root, "CONTEXT.md"))
+      const contextFile = Bun.file(path.join(app.path.root, "CONTEXT.md"))
       if (await contextFile.exists()) {
         const context = await contextFile.text()
         system.parts.push({

+ 2 - 2
packages/opencode/src/storage/storage.ts

@@ -3,8 +3,8 @@ import { LocalStorageAdapter } from "@flystorage/local-fs"
 import fs from "fs/promises"
 import { Log } from "../util/log"
 import { App } from "../app/app"
-import { AppPath } from "../app/path"
 import { Bus } from "../bus"
+import path from "path"
 import z from "zod"
 
 export namespace Storage {
@@ -19,7 +19,7 @@ export namespace Storage {
 
   const state = App.state("storage", async () => {
     const app = await App.use()
-    const storageDir = AppPath.storage(app.root)
+    const storageDir = path.join(app.path.data, "storage")
     await fs.mkdir(storageDir, { recursive: true })
     const storage = new FileStorage(new LocalStorageAdapter(storageDir))
     log.info("created", { path: storageDir })

+ 1 - 1
packages/opencode/src/tool/glob.ts

@@ -51,7 +51,7 @@ export const GlobTool = Tool.define({
   }),
   async execute(params) {
     const app = await App.use()
-    const search = params.path || app.root
+    const search = params.path || app.path.cwd
     const limit = 100
     const glob = new Bun.Glob(params.pattern)
     const files = []

+ 1 - 1
packages/opencode/src/tool/grep.ts

@@ -287,7 +287,7 @@ export const GrepTool = Tool.define({
     }
 
     const app = await App.use()
-    const searchPath = params.path || app.root
+    const searchPath = params.path || app.path.cwd
 
     // If literalText is true, escape the pattern
     const searchPattern = params.literalText

+ 1 - 1
packages/opencode/src/tool/ls.ts

@@ -26,7 +26,7 @@ export const ListTool = Tool.define({
   }),
   async execute(params) {
     const app = await App.use()
-    const searchPath = path.resolve(app.root, params.path || ".")
+    const searchPath = path.resolve(app.path.cwd, params.path || ".")
 
     const glob = new Bun.Glob("**/*")
     const files = []

+ 1 - 1
packages/opencode/src/tool/lsp-diagnostics.ts

@@ -37,7 +37,7 @@ TIPS:
     const app = await App.use()
     const normalized = path.isAbsolute(args.path)
       ? args.path
-      : path.join(app.root, args.path)
+      : path.join(app.path.cwd, args.path)
     await LSP.file(normalized)
     const diagnostics = await LSP.diagnostics()
     const file = diagnostics[normalized]

+ 1 - 1
packages/opencode/src/tool/lsp-hover.ts

@@ -21,7 +21,7 @@ export const LspHoverTool = Tool.define({
     const app = await App.use()
     const file = path.isAbsolute(args.file)
       ? args.file
-      : path.join(app.root, args.file)
+      : path.join(app.path.cwd, args.file)
     await LSP.file(file)
     const result = await LSP.hover({
       ...args,

+ 18 - 0
packages/opencode/src/util/filesystem.ts

@@ -0,0 +1,18 @@
+import { exists } from "fs/promises"
+import { dirname, join } from "path"
+
+export namespace Filesystem {
+  export async function findUp(target: string, start: string) {
+    let currentDir = start
+    while (true) {
+      const targetPath = join(currentDir, target)
+      if (await exists(targetPath)) return targetPath
+      const parentDir = dirname(currentDir)
+      if (parentDir === currentDir) {
+        return
+      }
+      currentDir = parentDir
+    }
+  }
+}
+

+ 3 - 3
packages/opencode/src/util/log.ts

@@ -1,5 +1,4 @@
 import path from "path"
-import { AppPath } from "../app/path"
 import fs from "fs/promises"
 export namespace Log {
   const write = {
@@ -12,8 +11,9 @@ export namespace Log {
   }
 
   export async function file(directory: string) {
-    const outPath = path.join(AppPath.data(directory), "opencode.out.log")
-    const errPath = path.join(AppPath.data(directory), "opencode.err.log")
+    await fs.mkdir(directory, { recursive: true })
+    const outPath = path.join(directory, "opencode.out.log")
+    const errPath = path.join(directory, "opencode.err.log")
     await fs.truncate(outPath).catch(() => {})
     await fs.truncate(errPath).catch(() => {})
     const out = Bun.file(outPath)

+ 9 - 0
packages/opencode/sst-env.d.ts

@@ -0,0 +1,9 @@
+/* This file is auto-generated by SST. Do not edit. */
+/* tslint:disable */
+/* eslint-disable */
+/* deno-fmt-ignore-file */
+
+/// <reference path="../../sst-env.d.ts" />
+
+import "sst"
+export {}