Skip to content

CLI startup cost: --version/-h load the full 2.6MB bundle; package installs 238MB (231MB transitive deps) #896

Description

@pizofreude

Summary

cmdc --version and cmdc -h are far slower than a version/help print should be, and the
cost scales with package size rather than with the request. A first invocation after boot
takes ~32 seconds; warm invocations still take ~3.5 seconds.

Platform specification

Date: 2026-09-20
Version tested: command-code 1.58.0 (also observed on 1.54.2)
Platform: Windows 10 (19045), Git Bash / MSYS2 (MINGW64_NT-10.0-19045), Node v24.16.0
Node: Volta-managed, v24.16.0

Measurements

Node itself is not the problem:

Command Time
node --version 85-183 ms
node -e 0 110-144 ms

CommandCode cold and warm, three runs each:

Command Run 1 Run 2 Run 3
cmdc --version 32,240 ms 3,954 ms 3,529 ms
cmdc -h 3,489 ms 3,566 ms 3,484 ms

Isolating each layer:

Test Result
node <dist/index.mjs> --version (direct, no shim) 34,496 ms cold, then 3,970 / 3,152 ms
cmdc --no-auto-update --version 3,431-4,957 ms (no improvement)
Volta shim overhead (Volta/bin/cmdc vs direct bundle, warm) ~0-200 ms

So the cost is inside the CLI bundle, not the launcher, and it is not the background
auto-update check (--no-auto-update changes nothing).

Package size breakdown

command-code@1.58.0 installed size is 238 MB:

Component Size
transitive node_modules 231 MB
vsix/commandcode-vscode.vsix 3.6 MB
dist/ 3.4 MB (68 files)
CHANGELOG.md 80 KB

Largest individual files:

Size File
5.2 MB node_modules/@vscode/ripgrep-win32-x64/bin/rg.exe
3.6 MB vsix/commandcode-vscode.vsix
2.9 MB node_modules/jimp/dist/browser/index.js.map
2.6 MB node_modules/@crosscopy/clipboard-win32-x64-msvc/clipboard.win32-x64-msvc.node
2.5 MB dist/cli.mjs
1.8 MB node_modules/marked-terminal/index.cjs
1.5 MB node_modules/jiti/dist/babel.cjs
1.3 MB node_modules/gifwrap/test/fixtures/nburling-public.gif

dist/cli.mjs is 2.6 MB of minified (not obfuscated) JavaScript and is loaded on every
invocation.

Why the cost is unavoidable per-invocation

  1. --version and -h both load the full 2.6 MB cli.mjs bundle rather than
    short-circuiting. That accounts for the ~3.5 s warm floor.
  2. A cold first invocation also pays a first-touch disk read plus antivirus scan of the
    238 MB tree. That accounts for the ~32 s cold case. (Inferred, not yet proven: a Defender
    exclusion experiment was not run.)

Suggested improvements

  1. Short-circuit --version and -h. Print the version and help text without importing
    the full CLI bundle. This alone should remove the ~3.5 s warm floor.
  2. Trim the shipped dependency tree. 231 MB of transitive dependencies for a CLI is
    disproportionate:
    • jimp pulls in browser bundles and source maps that a Node CLI cannot use.
    • gifwrap ships test fixtures, including a 1.3 MB GIF.
    • Several packages ship .js.map files that are only useful to the maintainers.
  3. Drop vsix/ from the npm package, or make it an on-demand download. The CLI never
    reads it; it exists for IDE setup. It is 3.6 MB of the install.
  4. Consider lazy-loading optional feature areas. Image handling (jimp), the TUI
    (ink, react), and the OTel exporters are not needed for --version, -h, or many
    one-shot -p runs.

Environment note (separate issue, already worked around locally)

command-code was previously installed as a Volta-pinned package. Under Volta,
npm config get prefix points into Volta's internal node image, so a global npm install of
command-code produced a second copy that was only sometimes on PATH. That is a
Volta/Windows interaction rather than a CommandCode defect, but it made cmdc update
appear to succeed while a stale binary kept running. Reported here only as context: the
startup cost above reproduces with the direct bundle and is independent of any launcher.

Expected Behavior

Similar invocation timelapse across all OS platforms (Windows, MacOS, Linux, WSL2)

Actual Behavior

Windows costs ~30s per invocation of cmdc --version

Steps to reproduce the issue

# Warm vs cold, three runs each
for i in 1 2 3; do time cmdc --version; done

# Rule out the launcher and the auto-update check
node "$(dirname "$(readlink -f "$(command -v cmdc)")")/../dist/index.mjs" --version
cmdc --no-auto-update --version

# Size
du -sh "$(npm root -g)/command-code"
du -sh "$(npm root -g)/command-code"/*

Command Code Version

1.58.0

Operating System

Windows

Terminal/IDE

WezTerm

Shell

cmd.exe

Session file (optional)

No response

Fix prompt (optional)

No response

Additional context

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions