瀏覽代碼

refactor(cli): convert debug subcommands to effectCmd (#25479)

Kit Langton 3 月之前
父節點
當前提交
4de44bbbef

+ 13 - 9
packages/opencode/src/cli/cmd/debug/config.ts

@@ -1,17 +1,21 @@
 import { EOL } from "os"
+import { Effect } from "effect"
 import { Config } from "@/config/config"
-import { AppRuntime } from "@/effect/app-runtime"
-import { bootstrap } from "../../bootstrap"
-import { cmd } from "../cmd"
+import { effectCmd } from "../../effect-cmd"
+import { InstanceRef } from "@/effect/instance-ref"
+import { InstanceStore } from "@/project/instance-store"
 
-export const ConfigCommand = cmd({
+export const ConfigCommand = effectCmd({
   command: "config",
   describe: "show resolved configuration",
   builder: (yargs) => yargs,
-  async handler() {
-    await bootstrap(process.cwd(), async () => {
-      const config = await AppRuntime.runPromise(Config.Service.use((cfg) => cfg.get()))
+  handler: Effect.fn("Cli.debug.config")(function* () {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const config = yield* Config.Service.use((cfg) => cfg.get())
       process.stdout.write(JSON.stringify(config, null, 2) + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })

+ 49 - 32
packages/opencode/src/cli/cmd/debug/file.ts

@@ -1,11 +1,13 @@
 import { EOL } from "os"
-import { AppRuntime } from "@/effect/app-runtime"
+import { Effect } from "effect"
 import { File } from "../../../file"
 import { Ripgrep } from "@/file/ripgrep"
-import { bootstrap } from "../../bootstrap"
+import { effectCmd } from "../../effect-cmd"
 import { cmd } from "../cmd"
+import { InstanceRef } from "@/effect/instance-ref"
+import { InstanceStore } from "@/project/instance-store"
 
-const FileSearchCommand = cmd({
+const FileSearchCommand = effectCmd({
   command: "search <query>",
   describe: "search files by query",
   builder: (yargs) =>
@@ -14,15 +16,18 @@ const FileSearchCommand = cmd({
       demandOption: true,
       description: "Search query",
     }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
-      const results = await AppRuntime.runPromise(File.Service.use((svc) => svc.search({ query: args.query })))
+  handler: Effect.fn("Cli.debug.file.search")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const results = yield* File.Service.use((svc) => svc.search({ query: args.query }))
       process.stdout.write(results.join(EOL) + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })
 
-const FileReadCommand = cmd({
+const FileReadCommand = effectCmd({
   command: "read <path>",
   describe: "read file contents as JSON",
   builder: (yargs) =>
@@ -31,27 +36,33 @@ const FileReadCommand = cmd({
       demandOption: true,
       description: "File path to read",
     }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
-      const content = await AppRuntime.runPromise(File.Service.use((svc) => svc.read(args.path)))
+  handler: Effect.fn("Cli.debug.file.read")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const content = yield* File.Service.use((svc) => svc.read(args.path))
       process.stdout.write(JSON.stringify(content, null, 2) + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })
 
-const FileStatusCommand = cmd({
+const FileStatusCommand = effectCmd({
   command: "status",
   describe: "show file status information",
   builder: (yargs) => yargs,
-  async handler() {
-    await bootstrap(process.cwd(), async () => {
-      const status = await AppRuntime.runPromise(File.Service.use((svc) => svc.status()))
+  handler: Effect.fn("Cli.debug.file.status")(function* () {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const status = yield* File.Service.use((svc) => svc.status())
       process.stdout.write(JSON.stringify(status, null, 2) + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })
 
-const FileListCommand = cmd({
+const FileListCommand = effectCmd({
   command: "list <path>",
   describe: "list files in a directory",
   builder: (yargs) =>
@@ -60,15 +71,18 @@ const FileListCommand = cmd({
       demandOption: true,
       description: "File path to list",
     }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
-      const files = await AppRuntime.runPromise(File.Service.use((svc) => svc.list(args.path)))
+  handler: Effect.fn("Cli.debug.file.list")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const files = yield* File.Service.use((svc) => svc.list(args.path))
       process.stdout.write(JSON.stringify(files, null, 2) + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })
 
-const FileTreeCommand = cmd({
+const FileTreeCommand = effectCmd({
   command: "tree [dir]",
   describe: "show directory tree",
   builder: (yargs) =>
@@ -77,12 +91,15 @@ const FileTreeCommand = cmd({
       description: "Directory to tree",
       default: process.cwd(),
     }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
-      const tree = await AppRuntime.runPromise(Ripgrep.Service.use((svc) => svc.tree({ cwd: args.dir, limit: 200 })))
+  handler: Effect.fn("Cli.debug.file.tree")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const tree = yield* Effect.orDie(Ripgrep.Service.use((svc) => svc.tree({ cwd: args.dir, limit: 200 })))
       console.log(JSON.stringify(tree, null, 2))
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })
 
 export const FileCommand = cmd({

+ 34 - 26
packages/opencode/src/cli/cmd/debug/lsp.ts

@@ -1,10 +1,11 @@
 import { LSP } from "@/lsp/lsp"
-import { AppRuntime } from "../../../effect/app-runtime"
 import { Effect } from "effect"
-import { bootstrap } from "../../bootstrap"
+import { effectCmd } from "../../effect-cmd"
 import { cmd } from "../cmd"
 import * as Log from "@opencode-ai/core/util/log"
 import { EOL } from "os"
+import { InstanceRef } from "@/effect/instance-ref"
+import { InstanceStore } from "@/project/instance-store"
 
 export const LSPCommand = cmd({
   command: "lsp",
@@ -14,47 +15,54 @@ export const LSPCommand = cmd({
   async handler() {},
 })
 
-const DiagnosticsCommand = cmd({
+const DiagnosticsCommand = effectCmd({
   command: "diagnostics <file>",
   describe: "get diagnostics for a file",
   builder: (yargs) => yargs.positional("file", { type: "string", demandOption: true }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
-      const out = await AppRuntime.runPromise(
-        LSP.Service.use((lsp) =>
-          Effect.gen(function* () {
-            yield* lsp.touchFile(args.file, "full")
-            return yield* lsp.diagnostics()
-          }),
-        ),
+  handler: Effect.fn("Cli.debug.lsp.diagnostics")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const out = yield* LSP.Service.use((lsp) =>
+        Effect.gen(function* () {
+          yield* lsp.touchFile(args.file, "full")
+          return yield* lsp.diagnostics()
+        }),
       )
       process.stdout.write(JSON.stringify(out, null, 2) + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })
 
-export const SymbolsCommand = cmd({
+export const SymbolsCommand = effectCmd({
   command: "symbols <query>",
   describe: "search workspace symbols",
   builder: (yargs) => yargs.positional("query", { type: "string", demandOption: true }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
+  handler: Effect.fn("Cli.debug.lsp.symbols")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
       using _ = Log.Default.time("symbols")
-      const results = await AppRuntime.runPromise(LSP.Service.use((lsp) => lsp.workspaceSymbol(args.query)))
+      const results = yield* LSP.Service.use((lsp) => lsp.workspaceSymbol(args.query))
       process.stdout.write(JSON.stringify(results, null, 2) + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })
 
-export const DocumentSymbolsCommand = cmd({
+export const DocumentSymbolsCommand = effectCmd({
   command: "document-symbols <uri>",
   describe: "get symbols from a document",
   builder: (yargs) => yargs.positional("uri", { type: "string", demandOption: true }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
+  handler: Effect.fn("Cli.debug.lsp.documentSymbols")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
       using _ = Log.Default.time("document-symbols")
-      const results = await AppRuntime.runPromise(LSP.Service.use((lsp) => lsp.documentSymbol(args.uri)))
+      const results = yield* LSP.Service.use((lsp) => lsp.documentSymbol(args.uri))
       process.stdout.write(JSON.stringify(results, null, 2) + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })

+ 42 - 38
packages/opencode/src/cli/cmd/debug/ripgrep.ts

@@ -1,10 +1,10 @@
 import { EOL } from "os"
 import { Effect, Stream } from "effect"
-import { AppRuntime } from "../../../effect/app-runtime"
 import { Ripgrep } from "../../../file/ripgrep"
-import { Instance } from "../../../project/instance"
-import { bootstrap } from "../../bootstrap"
+import { effectCmd } from "../../effect-cmd"
 import { cmd } from "../cmd"
+import { InstanceRef } from "@/effect/instance-ref"
+import { InstanceStore } from "@/project/instance-store"
 
 export const RipgrepCommand = cmd({
   command: "rg",
@@ -13,24 +13,25 @@ export const RipgrepCommand = cmd({
   async handler() {},
 })
 
-const TreeCommand = cmd({
+const TreeCommand = effectCmd({
   command: "tree",
   describe: "show file tree using ripgrep",
   builder: (yargs) =>
     yargs.option("limit", {
       type: "number",
     }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
-      const tree = await AppRuntime.runPromise(
-        Ripgrep.Service.use((svc) => svc.tree({ cwd: Instance.directory, limit: args.limit })),
-      )
+  handler: Effect.fn("Cli.debug.rg.tree")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const tree = yield* Effect.orDie(Ripgrep.Service.use((svc) => svc.tree({ cwd: ctx.directory, limit: args.limit })))
       process.stdout.write(tree + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })
 
-const FilesCommand = cmd({
+const FilesCommand = effectCmd({
   command: "files",
   describe: "list files using ripgrep",
   builder: (yargs) =>
@@ -47,29 +48,29 @@ const FilesCommand = cmd({
         type: "number",
         description: "Limit number of results",
       }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
-      const files = await AppRuntime.runPromise(
-        Effect.gen(function* () {
-          const rg = yield* Ripgrep.Service
-          return yield* rg
-            .files({
-              cwd: Instance.directory,
-              glob: args.glob ? [args.glob] : undefined,
-            })
-            .pipe(
-              Stream.take(args.limit ?? Infinity),
-              Stream.runCollect,
-              Effect.map((c) => [...c]),
-            )
-        }),
-      )
+  handler: Effect.fn("Cli.debug.rg.files")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const rg = yield* Ripgrep.Service
+      const files = yield* rg
+        .files({
+          cwd: ctx.directory,
+          glob: args.glob ? [args.glob] : undefined,
+        })
+        .pipe(
+          Stream.take(args.limit ?? Infinity),
+          Stream.runCollect,
+          Effect.map((c) => [...c]),
+          Effect.orDie,
+        )
       process.stdout.write(files.join(EOL) + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })
 
-const SearchCommand = cmd({
+const SearchCommand = effectCmd({
   command: "search <pattern>",
   describe: "search file contents using ripgrep",
   builder: (yargs) =>
@@ -87,12 +88,15 @@ const SearchCommand = cmd({
         type: "number",
         description: "Limit number of results",
       }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
-      const results = await AppRuntime.runPromise(
+  handler: Effect.fn("Cli.debug.rg.search")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const results = yield* Effect.orDie(
         Ripgrep.Service.use((svc) =>
           svc.search({
-            cwd: Instance.directory,
+            cwd: ctx.directory,
             pattern: args.pattern,
             glob: args.glob as string[] | undefined,
             limit: args.limit,
@@ -100,6 +104,6 @@ const SearchCommand = cmd({
         ),
       )
       process.stdout.write(JSON.stringify(results.items, null, 2) + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })

+ 13 - 14
packages/opencode/src/cli/cmd/debug/skill.ts

@@ -1,23 +1,22 @@
 import { EOL } from "os"
 import { Effect } from "effect"
-import { AppRuntime } from "@/effect/app-runtime"
 import { Skill } from "../../../skill"
-import { bootstrap } from "../../bootstrap"
-import { cmd } from "../cmd"
+import { effectCmd } from "../../effect-cmd"
+import { InstanceRef } from "@/effect/instance-ref"
+import { InstanceStore } from "@/project/instance-store"
 
-export const SkillCommand = cmd({
+export const SkillCommand = effectCmd({
   command: "skill",
   describe: "list all available skills",
   builder: (yargs) => yargs,
-  async handler() {
-    await bootstrap(process.cwd(), async () => {
-      const skills = await AppRuntime.runPromise(
-        Effect.gen(function* () {
-          const skill = yield* Skill.Service
-          return yield* skill.all()
-        }),
-      )
+  handler: Effect.fn("Cli.debug.skill")(function* () {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const skill = yield* Skill.Service
+      const skills = yield* skill.all()
       process.stdout.write(JSON.stringify(skills, null, 2) + EOL)
-    })
-  },
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })

+ 34 - 20
packages/opencode/src/cli/cmd/debug/snapshot.ts

@@ -1,7 +1,9 @@
-import { AppRuntime } from "@/effect/app-runtime"
+import { Effect } from "effect"
 import { Snapshot } from "../../../snapshot"
-import { bootstrap } from "../../bootstrap"
+import { effectCmd } from "../../effect-cmd"
 import { cmd } from "../cmd"
+import { InstanceRef } from "@/effect/instance-ref"
+import { InstanceStore } from "@/project/instance-store"
 
 export const SnapshotCommand = cmd({
   command: "snapshot",
@@ -10,17 +12,21 @@ export const SnapshotCommand = cmd({
   async handler() {},
 })
 
-const TrackCommand = cmd({
+const TrackCommand = effectCmd({
   command: "track",
   describe: "track current snapshot state",
-  async handler() {
-    await bootstrap(process.cwd(), async () => {
-      console.log(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())))
-    })
-  },
+  handler: Effect.fn("Cli.debug.snapshot.track")(function* () {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const out = yield* Snapshot.Service.use((svc) => svc.track())
+      console.log(out)
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })
 
-const PatchCommand = cmd({
+const PatchCommand = effectCmd({
   command: "patch <hash>",
   describe: "show patch for a snapshot hash",
   builder: (yargs) =>
@@ -29,14 +35,18 @@ const PatchCommand = cmd({
       description: "hash",
       demandOption: true,
     }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
-      console.log(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(args.hash))))
-    })
-  },
+  handler: Effect.fn("Cli.debug.snapshot.patch")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const out = yield* Snapshot.Service.use((svc) => svc.patch(args.hash))
+      console.log(out)
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })
 
-const DiffCommand = cmd({
+const DiffCommand = effectCmd({
   command: "diff <hash>",
   describe: "show diff for a snapshot hash",
   builder: (yargs) =>
@@ -45,9 +55,13 @@ const DiffCommand = cmd({
       description: "hash",
       demandOption: true,
     }),
-  async handler(args) {
-    await bootstrap(process.cwd(), async () => {
-      console.log(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diff(args.hash))))
-    })
-  },
+  handler: Effect.fn("Cli.debug.snapshot.diff")(function* (args) {
+    const ctx = yield* InstanceRef
+    if (!ctx) return
+    const store = yield* InstanceStore.Service
+    return yield* Effect.gen(function* () {
+      const out = yield* Snapshot.Service.use((svc) => svc.diff(args.hash))
+      console.log(out)
+    }).pipe(Effect.ensuring(store.dispose(ctx)))
+  }),
 })