* feat(launcher): support --max-old-space-size-percentage heap sizing
Convert Node's percentage heap flag into --max-old-space-size from
constrained or total RAM so small containers and large workstations can
avoid the fixed 8192 MB cap on every supported Node version.
Fixes#2213
* fix(launcher): reject unconstrained memory sentinels in heap percentage
Ignore libuv UINT64_MAX constrainedMemory values, clamp sub-megabyte
percentage results to 1 MB, and keep spaced heap-flag values on
background Node launches.
* fix(launcher): report unresolved heap percentage instead of silent MB fallback
A valid percentage with unknown available memory no longer looks like an
unrelated megabyte env or default success; the launcher warns once, docs
state full precedence, and child NODE_OPTIONS tests call the real helper.
* fix(launcher): strip launcher heap flags when Node already has a cap
Commander rejected --max-old-space-size-percentage on native heap+gc
and disable-relaunch paths. Strip those argv tokens before import, and
do not treat a following non-percentage word as the flag value.
* fix(launcher): propagate execArgv heap caps to subprocess NODE_OPTIONS
Child tools inherit NODE_OPTIONS, not the parent process.execArgv. When the
CLI already started with a numeric --max-old-space-size cap there, copy that
value into NODE_OPTIONS instead of defaulting to 8192 MB. Also strip
launcher-only heap flags on the OPENCLAUDE_HEAP_RELAUNCHED early-return path
and harden launcher integration tests with Node 22-safe execArgv fixtures.
* perf(cli): enable Node module compile cache
Warm CLI invocations spend substantial time compiling the bundled ESM entrypoint. Enable Node's optional on-disk compile cache only in the process that imports the bundle, while preserving early Node 22 compatibility and making cache failures non-fatal.
Add deterministic launcher coverage, packaging checks, and a reproducible benchmark procedure so the startup benefit can be measured without flaky CI thresholds.
* fix(ci): isolate minimum Node launcher check
The full validation suite depends on knip and oxc-parser behavior unavailable in Node 22.0.0. Keep full CI on the active Node 22 line and exercise the declared runtime floor in a dedicated build-and-launch job.
* fix(benchmark): harden startup measurements
Keep environment setup outside the timed process window, document the API's Node 22.8 floor, and preserve completed benchmark results when git metadata is unavailable.
* test(cli): verify compile cache disable behavior
Pair NODE_DISABLE_COMPILE_CACHE with a temporary cache directory and assert that supported Node releases leave it empty while preserving normal launcher output.
`/update` and `openclaude update` reported "Auto-update is unavailable for
a development build." even when OpenClaude was correctly installed from npm,
as long as the launching shell had `NODE_ENV=development` exported.
Root cause: `getCurrentInstallationType()` checked `NODE_ENV === 'development'`
as its first branch, before any path-based detection. A user's shell env var
then downgraded a real npm install to 'development', which routed
`resolveUpdateStrategy()` to `{ action: 'blocked', reason: 'development' }`.
Two-part fix:
1. doctorDiagnostic.ts — move the `NODE_ENV === 'development'` check to a
fallback position after all real-install path markers (bundled mode, local
npm, npm-global paths, /npm/, /nvm/, `npm config get prefix`). Path
detection runs first; NODE_ENV only classifies as 'development' when no
install path matches (i.e. an actual source-tree `bun run dev` run).
2. bin/openclaude — the heap-sizing relaunch previously used
`fileURLToPath(import.meta.url)`, which resolves symlinks. After relaunch,
`process.argv[1]` pointed at the real file target (repo path for
`npm install -g .`, package path inside node_modules for real installs),
defeating path-based detection. Preserve `process.argv[1]` (the original
invocation path, e.g. /usr/local/bin/openclaude or nvm bin symlink) so
npm-global path markers can match correctly.
Verified: `bun run typecheck` passes; `openclaude doctor` now reports
npm-global (not development) with NODE_ENV=development set on a real
npm global install.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
Relaunch the package executable before loading dist/cli.mjs so OpenClaude starts with an effective V8 heap cap instead of setting NODE_OPTIONS after the current process has already started.
The launcher now adds a default 8192 MB max-old-space-size and --expose-gc when they are missing, preserves flags supplied through process.execArgv or NODE_OPTIONS, and provides OPENCLAUDE_DISABLE_HEAP_RELAUNCH plus OPENCLAUDE_NODE_MAX_OLD_SPACE_SIZE_MB escape hatches.
Update the headless loop GC hook to use Node global.gc when the launcher exposed it, while preserving the existing Bun.gc path. Clarify the entrypoint NODE_OPTIONS comment so it reflects child-process propagation rather than current-process heap sizing.
Add scripts/openclaude-bin-heap.test.ts to guard launcher ordering and user override handling.
Validation: bun test scripts/openclaude-bin-heap.test.ts src/entrypoints/cli.test.ts; node bin/openclaude --version returned 0.13.0 (OpenClaude). Earlier full build passed after bun install --frozen-lockfile. bun run typecheck remains blocked by existing repo-wide type errors unrelated to this change.
- package.json with all 70+ dependencies
- Bun build script with feature flag shims, native module stubs, otel externals
- Stubs for ~15 missing source files (snapshot gaps)
- tsconfig.json for TypeScript
- bin/openclaude entry point
- Builds to single 19MB dist/cli.mjs
- Verified: --version and --help work
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>