Selaa lähdekoodia

opencode: lazy-load top-level CLI commands

The CLI imports every top-level command before argument parsing has
decided which handler will run. This makes simple invocations pay for
the full command graph up front and slows down the default startup path.

Parse the root argv first and load only the command module that matches
the selected top-level command. Keep falling back to the default TUI
path for non-command positionals, and preserve root help, version and
completion handling
Simon Klee 1 viikko sitten
vanhempi
sitoutus
75666b271b
1 muutettua tiedostoa jossa 244 lisäystä ja 46 poistoa
  1. 244 46
      packages/opencode/src/index.ts

+ 244 - 46
packages/opencode/src/index.ts

@@ -1,40 +1,17 @@
 import yargs from "yargs"
 import { hideBin } from "yargs/helpers"
-import { RunCommand } from "./cli/cmd/run"
-import { GenerateCommand } from "./cli/cmd/generate"
 import { Log } from "./util/log"
-import { ConsoleCommand } from "./cli/cmd/account"
-import { ProvidersCommand } from "./cli/cmd/providers"
-import { AgentCommand } from "./cli/cmd/agent"
-import { UpgradeCommand } from "./cli/cmd/upgrade"
-import { UninstallCommand } from "./cli/cmd/uninstall"
-import { ModelsCommand } from "./cli/cmd/models"
 import { UI } from "./cli/ui"
 import { Installation } from "./installation"
 import { NamedError } from "@opencode-ai/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"
-import { GithubCommand } from "./cli/cmd/github"
-import { ExportCommand } from "./cli/cmd/export"
-import { ImportCommand } from "./cli/cmd/import"
-import { AttachCommand } from "./cli/cmd/tui/attach"
-import { TuiThreadCommand } from "./cli/cmd/tui/thread"
-import { AcpCommand } from "./cli/cmd/acp"
 import { EOL } from "os"
-import { WebCommand } from "./cli/cmd/web"
-import { PrCommand } from "./cli/cmd/pr"
-import { SessionCommand } from "./cli/cmd/session"
-import { DbCommand } from "./cli/cmd/db"
 import path from "path"
 import { Global } from "./global"
 import { JsonMigration } from "./storage/json-migration"
 import { Database } from "./storage/db"
 import { errorMessage } from "./util/error"
-import { PluginCommand } from "./cli/cmd/plug"
 import { Heap } from "./cli/heap"
 import { drizzle } from "drizzle-orm/bun-sqlite"
 
@@ -52,6 +29,156 @@ process.on("uncaughtException", (e) => {
 
 const args = hideBin(process.argv)
 
+type Mode =
+  | "all"
+  | "none"
+  | "tui"
+  | "attach"
+  | "run"
+  | "acp"
+  | "mcp"
+  | "generate"
+  | "debug"
+  | "console"
+  | "providers"
+  | "agent"
+  | "upgrade"
+  | "uninstall"
+  | "serve"
+  | "web"
+  | "models"
+  | "stats"
+  | "export"
+  | "import"
+  | "github"
+  | "pr"
+  | "session"
+  | "plugin"
+  | "db"
+
+const map = new Map<string, Mode>([
+  ["attach", "attach"],
+  ["run", "run"],
+  ["acp", "acp"],
+  ["mcp", "mcp"],
+  ["generate", "generate"],
+  ["debug", "debug"],
+  ["console", "console"],
+  ["providers", "providers"],
+  ["auth", "providers"],
+  ["agent", "agent"],
+  ["upgrade", "upgrade"],
+  ["uninstall", "uninstall"],
+  ["serve", "serve"],
+  ["web", "web"],
+  ["models", "models"],
+  ["stats", "stats"],
+  ["export", "export"],
+  ["import", "import"],
+  ["github", "github"],
+  ["pr", "pr"],
+  ["session", "session"],
+  ["plugin", "plugin"],
+  ["plug", "plugin"],
+  ["db", "db"],
+])
+
+function flag(arg: string, name: string) {
+  return arg === `--${name}` || arg === `--no-${name}` || arg.startsWith(`--${name}=`)
+}
+
+function value(arg: string, name: string) {
+  return arg === `--${name}` || arg.startsWith(`--${name}=`)
+}
+
+// Match the root parser closely enough to decide which top-level module to load.
+function pick(argv: string[]): Mode {
+  for (let i = 0; i < argv.length; i++) {
+    const arg = argv[i]
+    if (!arg) continue
+    if (arg === "--") return "tui"
+    if (arg === "completion") return "all"
+    if (arg === "--help" || arg === "-h") return "all"
+    if (arg === "--version" || arg === "-v") return "none"
+    if (flag(arg, "print-logs") || flag(arg, "pure")) continue
+    if (value(arg, "log-level")) {
+      if (arg === "--log-level") i += 1
+      continue
+    }
+    if (arg.startsWith("-") && !arg.startsWith("--")) {
+      if (arg.includes("h")) return "all"
+      if (arg.includes("v")) return "none"
+      return "tui"
+    }
+    if (arg.startsWith("-")) return "tui"
+    return map.get(arg) ?? "tui"
+  }
+
+  return "tui"
+}
+
+const mode = pick(args)
+const all = mode === "all"
+const none = mode === "none"
+
+function load<T>(on: boolean, get: () => Promise<T>): Promise<T | undefined> {
+  if (!on) {
+    return Promise.resolve(undefined)
+  }
+
+  return get()
+}
+
+const [
+  TuiThreadCommand,
+  AttachCommand,
+  RunCommand,
+  AcpCommand,
+  McpCommand,
+  GenerateCommand,
+  DebugCommand,
+  ConsoleCommand,
+  ProvidersCommand,
+  AgentCommand,
+  UpgradeCommand,
+  UninstallCommand,
+  ServeCommand,
+  WebCommand,
+  ModelsCommand,
+  StatsCommand,
+  ExportCommand,
+  ImportCommand,
+  GithubCommand,
+  PrCommand,
+  SessionCommand,
+  PluginCommand,
+  DbCommand,
+] = await Promise.all([
+  load(!none && (all || mode === "tui"), () => import("./cli/cmd/tui/thread").then((x) => x.TuiThreadCommand)),
+  load(!none && (all || mode === "attach"), () => import("./cli/cmd/tui/attach").then((x) => x.AttachCommand)),
+  load(!none && (all || mode === "run"), () => import("./cli/cmd/run").then((x) => x.RunCommand)),
+  load(!none && (all || mode === "acp"), () => import("./cli/cmd/acp").then((x) => x.AcpCommand)),
+  load(!none && (all || mode === "mcp"), () => import("./cli/cmd/mcp").then((x) => x.McpCommand)),
+  load(!none && (all || mode === "generate"), () => import("./cli/cmd/generate").then((x) => x.GenerateCommand)),
+  load(!none && (all || mode === "debug"), () => import("./cli/cmd/debug").then((x) => x.DebugCommand)),
+  load(!none && (all || mode === "console"), () => import("./cli/cmd/account").then((x) => x.ConsoleCommand)),
+  load(!none && (all || mode === "providers"), () => import("./cli/cmd/providers").then((x) => x.ProvidersCommand)),
+  load(!none && (all || mode === "agent"), () => import("./cli/cmd/agent").then((x) => x.AgentCommand)),
+  load(!none && (all || mode === "upgrade"), () => import("./cli/cmd/upgrade").then((x) => x.UpgradeCommand)),
+  load(!none && (all || mode === "uninstall"), () => import("./cli/cmd/uninstall").then((x) => x.UninstallCommand)),
+  load(!none && (all || mode === "serve"), () => import("./cli/cmd/serve").then((x) => x.ServeCommand)),
+  load(!none && (all || mode === "web"), () => import("./cli/cmd/web").then((x) => x.WebCommand)),
+  load(!none && (all || mode === "models"), () => import("./cli/cmd/models").then((x) => x.ModelsCommand)),
+  load(!none && (all || mode === "stats"), () => import("./cli/cmd/stats").then((x) => x.StatsCommand)),
+  load(!none && (all || mode === "export"), () => import("./cli/cmd/export").then((x) => x.ExportCommand)),
+  load(!none && (all || mode === "import"), () => import("./cli/cmd/import").then((x) => x.ImportCommand)),
+  load(!none && (all || mode === "github"), () => import("./cli/cmd/github").then((x) => x.GithubCommand)),
+  load(!none && (all || mode === "pr"), () => import("./cli/cmd/pr").then((x) => x.PrCommand)),
+  load(!none && (all || mode === "session"), () => import("./cli/cmd/session").then((x) => x.SessionCommand)),
+  load(!none && (all || mode === "plugin"), () => import("./cli/cmd/plug").then((x) => x.PluginCommand)),
+  load(!none && (all || mode === "db"), () => import("./cli/cmd/db").then((x) => x.DbCommand)),
+])
+
 function show(out: string) {
   const text = out.trimStart()
   if (!text.startsWith("opencode ")) {
@@ -148,29 +275,100 @@ const cli = yargs(args)
   })
   .usage("")
   .completion("completion", "generate shell completion script")
-  .command(AcpCommand)
-  .command(McpCommand)
-  .command(TuiThreadCommand)
-  .command(AttachCommand)
-  .command(RunCommand)
-  .command(GenerateCommand)
-  .command(DebugCommand)
-  .command(ConsoleCommand)
-  .command(ProvidersCommand)
-  .command(AgentCommand)
-  .command(UpgradeCommand)
-  .command(UninstallCommand)
-  .command(ServeCommand)
-  .command(WebCommand)
-  .command(ModelsCommand)
-  .command(StatsCommand)
-  .command(ExportCommand)
-  .command(ImportCommand)
-  .command(GithubCommand)
-  .command(PrCommand)
-  .command(SessionCommand)
-  .command(PluginCommand)
-  .command(DbCommand)
+
+if (TuiThreadCommand) {
+  cli.command(TuiThreadCommand)
+}
+
+if (AttachCommand) {
+  cli.command(AttachCommand)
+}
+
+if (AcpCommand) {
+  cli.command(AcpCommand)
+}
+
+if (McpCommand) {
+  cli.command(McpCommand)
+}
+
+if (RunCommand) {
+  cli.command(RunCommand)
+}
+
+if (GenerateCommand) {
+  cli.command(GenerateCommand)
+}
+
+if (DebugCommand) {
+  cli.command(DebugCommand)
+}
+
+if (ConsoleCommand) {
+  cli.command(ConsoleCommand)
+}
+
+if (ProvidersCommand) {
+  cli.command(ProvidersCommand)
+}
+
+if (AgentCommand) {
+  cli.command(AgentCommand)
+}
+
+if (UpgradeCommand) {
+  cli.command(UpgradeCommand)
+}
+
+if (UninstallCommand) {
+  cli.command(UninstallCommand)
+}
+
+if (ServeCommand) {
+  cli.command(ServeCommand)
+}
+
+if (WebCommand) {
+  cli.command(WebCommand)
+}
+
+if (ModelsCommand) {
+  cli.command(ModelsCommand)
+}
+
+if (StatsCommand) {
+  cli.command(StatsCommand)
+}
+
+if (ExportCommand) {
+  cli.command(ExportCommand)
+}
+
+if (ImportCommand) {
+  cli.command(ImportCommand)
+}
+
+if (GithubCommand) {
+  cli.command(GithubCommand)
+}
+
+if (PrCommand) {
+  cli.command(PrCommand)
+}
+
+if (SessionCommand) {
+  cli.command(SessionCommand)
+}
+
+if (PluginCommand) {
+  cli.command(PluginCommand)
+}
+
+if (DbCommand) {
+  cli.command(DbCommand)
+}
+
+cli
   .fail((msg, err) => {
     if (
       msg?.startsWith("Unknown argument") ||