Skip to content

Windows cold-start penalty: 44,835 files in node_modules triggers antivirus rescan on every first launch #19169

@genneth

Description

@genneth

Summary

Gemini CLI's npm package ships ~44,835 files (unbundled dist/ + full node_modules/). On Windows, this causes severe cold-start latency (~1-2 minutes) due to Windows Defender's real-time protection minifilter scanning every file on first access. Subsequent launches are fast because Defender caches scan results — but the cache expires after a few hours, so the penalty recurs daily.

The esbuild bundling infrastructure already exists in the repo (npm run bundlebundle/gemini.js), but the published npm package doesn't use it.

Evidence

File count comparison:

Tool Files on disk Cold start (Windows)
Gemini CLI (@google/gemini-cli) 44,835 ~60-120s
Claude Code 1 (compiled Bun binary) ~3s

The "first launch of the day" pattern:
Cold starts are slow (~1-2 min), repeat launches within the same session are fast (~4-8s), and after a few hours of inactivity the slowness returns. This exactly matches Windows Defender's scan cache behaviour — results are cached per (file path, file hash) with a TTL of a few hours, after which every file is rescanned on next access.

Built-in profiling confirms the app code itself is fast:
Using GEMINI_DEBUG_LOG_FILE and the startup profiler, the entire cli_startup phase takes only ~3.8 seconds internally:

cli_startup:          3,825ms
  initialize_app:     2,124ms
  authenticate:         201ms
  load_builtin_cmds:    215ms
  discover_tools:       113ms
  load_settings:         28ms
  parse_arguments:       19ms
  load_cli_config:        9ms

The ~1-2 minute gap is entirely before any application code runs — during Node.js module resolution and OS-level file scanning of the 44,835 files in node_modules/.

Workaround confirms the diagnosis:
Adding node.exe to Windows Defender's process exclusion list (Add-MpPreference -ExclusionProcess "node.exe") eliminates the cold-start penalty entirely.

The bundle infrastructure already exists

The repo already has esbuild bundling:

  • npm run bundle produces bundle/gemini.js
  • The root package.json declares "bin": { "gemini": "bundle/gemini.js" } and "files": ["bundle"]

However, the published npm package (packages/cli) ships with "main": "dist/index.js" and full dependencies, resulting in the 44,835-file install. The bundling is currently only used for the npx execution path.

Proposed fix

Ship the esbuild bundle as the primary npm distribution:

  • Replace dist/ + dependencies with the pre-built bundle/gemini.js in the published package
  • Keep platform-specific native optional dependencies (node-pty, keytar) that can't be bundled
  • This would reduce the on-disk file count from ~44,835 to a handful, eliminating the antivirus scan overhead on all platforms

This is essentially what Claude Code does — they ship zero npm dependencies by bundling everything into the package.

Environment

  • Windows 11 Home (10.0.26200)
  • Node.js 24.13.0 (via Volta)
  • Gemini CLI 0.28.x (npm global install)
  • Windows Defender real-time protection enabled (default)

Related issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/coreIssues related to User Interface, OS Support, Core Functionalitystatus/need-triageIssues that need to be triaged by the triage automation.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions