Revert "perf: lazy-load CLI commands via dynamic import for code splitting"

This reverts commit e320484774.
This commit is contained in:
Claude
2026-04-16 13:11:30 +00:00
parent e320484774
commit 8766c80910
2 changed files with 46 additions and 253 deletions

View File

@@ -5,29 +5,3 @@ type WithDoubleDash<T> = T & { "--"?: string[] }
export function cmd<T, U>(input: CommandModule<T, WithDoubleDash<U>>) {
return input
}
/**
* Create a lazily-loaded command. The command metadata (name, describe,
* builder options) is defined inline — lightweight and synchronous.
* The handler dynamically imports the real command module only when
* that specific command is invoked, avoiding loading heavy transitive
* dependencies (AI SDKs, MCP, TUI, etc.) until needed.
*/
export function lazyCmd<T = {}, U = {}>(
meta: {
command: string
describe: string
builder?: CommandModule<T, WithDoubleDash<U>>["builder"]
},
load: () => Promise<{ handler: Function }>,
): CommandModule<T, WithDoubleDash<U>> {
return {
command: meta.command,
describe: meta.describe,
builder: meta.builder ?? ((yargs: any) => yargs),
handler: async (args: any) => {
const cmd = await load()
return cmd.handler(args)
},
} as any
}

View File

@@ -1,19 +1,41 @@
import yargs from "yargs"
import { hideBin } from "yargs/helpers"
import { lazyCmd } from "./cli/cmd/cmd"
import { RunCommand } from "./cli/cmd/run"
import { GenerateCommand } from "./cli/cmd/generate"
import { Log } from "./util"
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 { InstallationVersion } from "./installation/version"
import { NamedError } from "@opencode-ai/shared/util/error"
import { FormatError } from "./cli/error"
import { ServeCommand } from "./cli/cmd/serve"
import { Filesystem } from "./util"
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"
import { Database } from "./storage"
import { errorMessage } from "./util/error"
import { PluginCommand } from "./cli/cmd/plug"
import { Heap } from "./cli/heap"
import { drizzle } from "drizzle-orm/bun-sqlite"
@@ -41,15 +63,6 @@ function show(out: string) {
process.stderr.write(out)
}
// Shared network options used by TUI, serve, acp, web commands
const networkOptions = {
port: { type: "number" as const, describe: "port to listen on", default: 0 },
hostname: { type: "string" as const, describe: "hostname to listen on", default: "127.0.0.1" },
mdns: { type: "boolean" as const, describe: "enable mDNS service discovery (defaults hostname to 0.0.0.0)", default: false },
"mdns-domain": { type: "string" as const, describe: "custom domain name for mDNS service (default: opencode.local)", default: "opencode.local" },
cors: { type: "string" as const, array: true as const, describe: "additional domains to allow for CORS", default: [] as string[] },
} as const
const cli = yargs(args)
.parserConfiguration({ "populate--": true })
.scriptName("opencode")
@@ -136,223 +149,29 @@ const cli = yargs(args)
})
.usage("")
.completion("completion", "generate shell completion script")
// ── Default command (TUI) ──
.command(lazyCmd(
{
command: "$0 [project]",
describe: "start opencode tui",
builder: (yargs: any) =>
yargs.options(networkOptions)
.positional("project", { type: "string", describe: "path to start opencode in" })
.option("model", { type: "string", alias: ["m"], describe: "model to use in the format of provider/model" })
.option("continue", { alias: ["c"], describe: "continue the last session", type: "boolean" })
.option("session", { alias: ["s"], type: "string", describe: "session id to continue" })
.option("fork", { type: "boolean", describe: "fork the session when continuing (use with --continue or --session)" })
.option("prompt", { type: "string", describe: "prompt to use" })
.option("agent", { type: "string", describe: "agent to use" }),
},
() => import("./cli/cmd/tui/thread").then((m) => m.TuiThreadCommand),
))
// ── Heavy commands — lazy-loaded handlers ──
.command(lazyCmd(
{
command: "run [message..]",
describe: "run opencode with a message",
builder: (yargs: any) =>
yargs
.positional("message", { describe: "message to send", type: "string", array: true, default: [] })
.option("command", { describe: "the command to run, use message for args", type: "string" })
.option("continue", { alias: ["c"], describe: "continue the last session", type: "boolean" })
.option("session", { alias: ["s"], describe: "session id to continue", type: "string" })
.option("fork", { type: "boolean", describe: "fork the session before continuing" })
.option("share", { type: "boolean", describe: "share the session" })
.option("model", { alias: ["m"], describe: "model to use", type: "string" })
.option("agent", { describe: "agent to use", type: "string" })
.option("format", { describe: "format", type: "string", choices: ["default", "json"], default: "default" })
.option("file", { alias: ["f"], describe: "file(s) to attach", type: "string", array: true })
.option("title", { describe: "title for the session", type: "string" })
.option("attach", { describe: "attach to a running opencode server", type: "string" })
.option("password", { alias: ["p"], describe: "basic auth password", type: "string" })
.option("dir", { describe: "directory to run in", type: "string" })
.option("port", { describe: "port for the local server", type: "number" })
.option("variant", { describe: "model variant", type: "string" })
.option("thinking", { describe: "show thinking blocks", type: "boolean", default: false })
.option("dangerously-skip-permissions", { describe: "auto-approve permissions", type: "boolean", default: false }),
},
() => import("./cli/cmd/run").then((m) => m.RunCommand),
))
.command(lazyCmd(
{
command: "attach <url>",
describe: "attach to a running opencode server",
builder: (yargs: any) =>
yargs
.positional("url", { describe: "http://localhost:4096", type: "string", demandOption: true })
.option("dir", { describe: "directory to run in", type: "string" })
.option("continue", { alias: ["c"], describe: "continue the last session", type: "boolean" })
.option("session", { alias: ["s"], describe: "session id to continue", type: "string" })
.option("fork", { type: "boolean", describe: "fork the session when continuing" })
.option("password", { alias: ["p"], describe: "basic auth password", type: "string" }),
},
() => import("./cli/cmd/tui/attach").then((m) => m.AttachCommand),
))
.command(lazyCmd(
{
command: "serve",
describe: "starts a headless opencode server",
builder: (yargs: any) => yargs.options(networkOptions),
},
() => import("./cli/cmd/serve").then((m) => m.ServeCommand),
))
.command(lazyCmd(
{
command: "web",
describe: "start opencode server and open web interface",
builder: (yargs: any) => yargs.options(networkOptions),
},
() => import("./cli/cmd/web").then((m) => m.WebCommand),
))
.command(lazyCmd(
{
command: "acp",
describe: "start ACP (Agent Client Protocol) server",
builder: (yargs: any) =>
yargs.options(networkOptions).option("cwd", { type: "string", describe: "working directory", default: process.cwd() }),
},
() => import("./cli/cmd/acp").then((m) => m.AcpCommand),
))
// ── Parent commands with subcommands — load full module when matched ──
.command(lazyCmd(
{ command: "mcp", describe: "manage MCP (Model Context Protocol) servers" },
() => import("./cli/cmd/mcp").then((m) => m.McpCommand),
))
.command(lazyCmd(
{ command: "console", describe: false as any },
() => import("./cli/cmd/account").then((m) => m.ConsoleCommand),
))
.command(lazyCmd(
{ command: "providers", describe: "manage AI providers and credentials" },
() => import("./cli/cmd/providers").then((m) => m.ProvidersCommand),
))
.command(lazyCmd(
{ command: "agent", describe: "manage agents" },
() => import("./cli/cmd/agent").then((m) => m.AgentCommand),
))
.command(lazyCmd(
{ command: "debug", describe: "debugging and troubleshooting tools" },
() => import("./cli/cmd/debug").then((m) => m.DebugCommand),
))
.command(lazyCmd(
{ command: "github", describe: "manage GitHub agent" },
() => import("./cli/cmd/github").then((m) => m.GithubCommand),
))
.command(lazyCmd(
{ command: "session", describe: "manage sessions" },
() => import("./cli/cmd/session").then((m) => m.SessionCommand),
))
.command(lazyCmd(
{ command: "db", describe: "database tools" },
() => import("./cli/cmd/db").then((m) => m.DbCommand),
))
// ── Lightweight commands — still lazy for consistency ──
.command(lazyCmd(
{ command: "generate", describe: false as any },
() => import("./cli/cmd/generate").then((m) => m.GenerateCommand),
))
.command(lazyCmd(
{
command: "upgrade [target]",
describe: "upgrade opencode to the latest or a specific version",
builder: (yargs: any) =>
yargs
.positional("target", { describe: "version to upgrade to, for ex '0.1.48' or 'v0.1.48'", type: "string" })
.option("method", { alias: "m", describe: "installation method to use", type: "string", choices: ["curl", "npm", "pnpm", "bun", "brew", "choco", "scoop"] }),
},
() => import("./cli/cmd/upgrade").then((m) => m.UpgradeCommand),
))
.command(lazyCmd(
{
command: "uninstall",
describe: "uninstall opencode and remove all related files",
builder: (yargs: any) =>
yargs
.option("keep-config", { alias: "c", describe: "keep configuration files", type: "boolean", default: false })
.option("keep-data", { alias: "d", describe: "keep session data and snapshots", type: "boolean", default: false })
.option("dry-run", { describe: "show what would be removed", type: "boolean", default: false })
.option("force", { alias: "f", describe: "skip confirmation prompts", type: "boolean", default: false }),
},
() => import("./cli/cmd/uninstall").then((m) => m.UninstallCommand),
))
.command(lazyCmd(
{
command: "models [provider]",
describe: "list all available models",
builder: (yargs: any) =>
yargs
.positional("provider", { describe: "provider ID to filter models by", type: "string", array: false })
.option("verbose", { describe: "use more verbose model output (includes metadata like costs)", type: "boolean" })
.option("refresh", { describe: "refresh the models cache from models.dev", type: "boolean" }),
},
() => import("./cli/cmd/models").then((m) => m.ModelsCommand),
))
.command(lazyCmd(
{
command: "stats",
describe: "show token usage and cost statistics",
builder: (yargs: any) =>
yargs
.option("days", { describe: "show stats for the last N days", type: "number" })
.option("tools", { describe: "number of tools to show", type: "number" })
.option("models", { describe: "show model statistics", type: "boolean" })
.option("project", { describe: "filter by project", type: "string" }),
},
() => import("./cli/cmd/stats").then((m) => m.StatsCommand),
))
.command(lazyCmd(
{
command: "export [sessionID]",
describe: "export session data as JSON",
builder: (yargs: any) =>
yargs
.positional("sessionID", { describe: "session id to export", type: "string" })
.option("sanitize", { describe: "redact sensitive transcript and file data", type: "boolean" }),
},
() => import("./cli/cmd/export").then((m) => m.ExportCommand),
))
.command(lazyCmd(
{
command: "import <file>",
describe: "import session data from JSON file or URL",
builder: (yargs: any) =>
yargs.positional("file", { describe: "path to JSON file or share URL", type: "string", demandOption: true }),
},
() => import("./cli/cmd/import").then((m) => m.ImportCommand),
))
.command(lazyCmd(
{
command: "pr <number>",
describe: "fetch and checkout a GitHub PR branch, then run opencode",
builder: (yargs: any) =>
yargs.positional("number", { describe: "PR number to checkout", type: "number", demandOption: true }),
},
() => import("./cli/cmd/pr").then((m) => m.PrCommand),
))
.command(lazyCmd(
{
command: "plugin <module>",
describe: "install plugin and update config",
builder: (yargs: any) =>
yargs
.positional("module", { describe: "npm module name", type: "string" })
.option("global", { alias: ["g"], describe: "install in global config", type: "boolean", default: false })
.option("force", { alias: ["f"], describe: "replace existing plugin version", type: "boolean", default: false }),
},
() => import("./cli/cmd/plug").then((m) => m.PluginCommand),
))
.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)
.fail((msg, err) => {
if (
msg?.startsWith("Unknown argument") ||