Răsfoiți Sursa

refactor(cli): convert run command to effectCmd (#25519)

Kit Langton 3 luni în urmă
părinte
comite
7409dcc6bd

+ 1 - 1
packages/opencode/src/cli/cmd/cmd.ts

@@ -1,6 +1,6 @@
 import type { CommandModule } from "yargs"
 
-type WithDoubleDash<T> = T & { "--"?: string[] }
+export type WithDoubleDash<T> = T & { "--"?: string[] }
 
 export function cmd<T, U>(input: CommandModule<T, WithDoubleDash<U>>) {
   return input

+ 21 - 16
packages/opencode/src/cli/cmd/run.ts

@@ -1,10 +1,10 @@
 import type { Argv } from "yargs"
 import path from "path"
 import { pathToFileURL } from "url"
+import { Effect } from "effect"
 import { UI } from "../ui"
-import { cmd } from "./cmd"
+import { effectCmd } from "../effect-cmd"
 import { Flag } from "@opencode-ai/core/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"
@@ -203,11 +203,17 @@ function normalizePath(input?: string) {
   return input
 }
 
-export const RunCommand = cmd({
+export const RunCommand = effectCmd({
   command: "run [message..]",
   describe: "run opencode with a message",
-  builder: (yargs: Argv) => {
-    return yargs
+  // --attach connects to a remote server (no local instance needed); the
+  // default path runs an in-process server and needs the project instance.
+  instance: (args) => !args.attach,
+  // For --dir without --attach, load instance for the resolved target dir.
+  // The handler also chdirs (preserving the legacy order: chdir → file resolution).
+  directory: (args) => (args.dir && !args.attach ? path.resolve(process.cwd(), args.dir) : process.cwd()),
+  builder: (yargs: Argv) =>
+    yargs
       .positional("message", {
         describe: "message to send",
         type: "string",
@@ -291,9 +297,9 @@ export const RunCommand = cmd({
         type: "boolean",
         describe: "auto-approve permissions that are not explicitly denied (dangerous!)",
         default: false,
-      })
-  },
-  handler: async (args) => {
+      }),
+  handler: Effect.fn("Cli.run")(function* (args) {
+    yield* Effect.promise(async () => {
     let message = [...args.message, ...(args["--"] || [])]
       .map((arg) => (arg.includes(" ") ? `"${arg.replace(/"/g, '\\"')}"` : arg))
       .join(" ")
@@ -661,13 +667,12 @@ export const RunCommand = cmd({
       return await execute(sdk)
     }
 
-    await bootstrap(process.cwd(), async () => {
-      const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
-        const request = new Request(input, init)
-        return Server.Default().app.fetch(request)
-      }) as typeof globalThis.fetch
-      const sdk = createOpencodeClient({ baseUrl: "http://opencode.internal", fetch: fetchFn })
-      await execute(sdk)
+    const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
+      const request = new Request(input, init)
+      return Server.Default().app.fetch(request)
+    }) as typeof globalThis.fetch
+    const sdk = createOpencodeClient({ baseUrl: "http://opencode.internal", fetch: fetchFn })
+    await execute(sdk)
     })
-  },
+  }),
 })

+ 3 - 3
packages/opencode/src/cli/effect-cmd.ts

@@ -3,7 +3,7 @@ import { Effect, Schema } from "effect"
 import { AppRuntime, type AppServices } from "@/effect/app-runtime"
 import { InstanceStore } from "@/project/instance-store"
 import { InstanceRef } from "@/effect/instance-ref"
-import { cmd } from "./cmd/cmd"
+import { cmd, type WithDoubleDash } from "./cmd/cmd"
 
 /**
  * User-visible command failure. Throw via `fail("...")` from an effectCmd handler
@@ -47,7 +47,7 @@ interface EffectCmdOpts<Args, A> {
   instance?: boolean | ((args: Args) => boolean)
   /** Defaults to process.cwd(). Override for commands that take a directory positional. */
   directory?: (args: Args) => string
-  handler: (args: Args) => Effect.Effect<A, CliError, AppServices | InstanceStore.Service>
+  handler: (args: WithDoubleDash<Args>) => Effect.Effect<A, CliError, AppServices | InstanceStore.Service>
 }
 
 /**
@@ -75,7 +75,7 @@ export const effectCmd = <Args, A>(opts: EffectCmdOpts<Args, A>) =>
     builder: opts.builder as never,
     async handler(rawArgs) {
       // yargs typing wraps Args in ArgumentsCamelCase<WithDoubleDash<...>>; cast at the boundary.
-      const args = rawArgs as unknown as Args
+      const args = rawArgs as unknown as WithDoubleDash<Args>
       const useInstance = typeof opts.instance === "function" ? opts.instance(args) : opts.instance !== false
       if (!useInstance) {
         await AppRuntime.runPromise(opts.handler(args))