Skip to content

chore(vscode): speed up extension launch builds - #14179

Merged
marius-kilocode merged 6 commits into
mainfrom
optimize-extension-performance
Sep 16, 2026
Merged

marius-kilocode merged 6 commits into
mainfrom
optimize-extension-performance

Conversation

@marius-kilocode

Copy link
Copy Markdown
Collaborator

What Problem This Solves

bun run extension spent most of its cold build time on sequential steps that do not depend on each other, and the Solid JSX transform cache was keyed by absolute path and mtime and stored inside each worktree. Every fresh Agent Manager worktree therefore started cold and re-ran Babel over every JSX file on its first build.

Why This Change Was Made

build:launch ran prepare:cli-binary, then prepare:sdk, then build:check:production. The CLI compile is mostly single threaded (~117% CPU) and writes only to packages/opencode/dist, bin/, and node_modules/.kilo-cli-version. The typecheck, lint, and bundle checks read src, webview-ui, and dist, then write dist. The two sets do not overlap, so they can run at the same time. prepare:sdk stays first because the CLI bundles packages/sdk/js/src, which the SDK build regenerates.

The transform cache key contained the file path and mtime, so it could not be shared between worktrees and missed on every new checkout. The transform output depends only on the file name (for the inline source map), the source text, and the toolchain, so the key is now a content hash that includes @babel/core, babel-preset-solid, babel-plugin-jsx-dom-expressions, @babel/preset-typescript, and @babel/plugin-transform-typescript. Cache entries live in a per-user directory under the OS temp dir and are written through a temp file and rename, so concurrent builds in different worktrees cannot read partial entries.

User Impact

Faster extension development only. The launched extension and all built artifacts are unchanged; the production bundles are byte-identical before and after the cache change.

Evidence

Measured on macOS arm64 with wall-clock time. Machine load varied (other agents were running), so the warm numbers are medians of repeated runs.

Scenario Before After
Cold build:launch after bun install 35.3s 23.4s
Warm build:launch 2.3-2.8s 2.4-2.4s (no change)
esbuild --production, cold vs warm transform cache 2.8s vs 1.5s fresh worktrees now hit the shared cache

Verification:

  • Production output compared with shasum -a 256 dist/*.js before and after the cache change: identical.
  • bun test tests/unit/launch.test.ts: 4 pass.
  • Cold run from a wiped bin/, opencode/dist, SDK cache, eslint cache, dist/, and transform cache: passed end to end.
  • Isolated VS Code launch in dev mode: extension activated, sidebar webview rendered, no [Kilo New] errors in the extension host log.

Known limitation: existing worktrees keep a now unused node_modules/.cache/esbuild-solid directory. It is harmless and disappears on the next node_modules reinstall.

Run prepare:sdk first, then compile the CLI binary in parallel with the
extension typecheck, lint, and bundle. The CLI compile does not depend on
those checks, and the SDK must still be generated before the CLI bundles
its source.

Key the cached Solid JSX transform by file content instead of path and
mtime, and store it in the OS temp dir so every worktree shares one warm
cache. Write cache entries through a temp file and rename so concurrent
builds cannot read partial entries.
Comment thread packages/kilo-vscode/esbuild.js Resolved
Comment thread packages/kilo-vscode/esbuild.js Resolved
@kilo-code-bot

kilo-code-bot Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (1 file)
  • packages/kilo-vscode/esbuild.js

Incremental scope: 44a6145a..5246dc20 (1 file, 15 insertions, 8 deletions). The remaining suggestion is resolved: the index now records ctimeMs alongside mtimeMs and size, so a file whose mtime was preserved (cp -p, rsync -t, touch -r, a restored snapshot) is re-read instead of reusing the previous transform, and the settling threshold is raised to 3s. Entries written by earlier revisions lack ctime and are dropped on load, so one build re-hashes and then re-records them. The cache-directory hardening (lstat, symlink/uid checks, realpath containment, 0700 chmod), the bounded sweep, and saveIndex pruning all remain in place and were not touched. No new correctness, security, or performance issue in the changed lines.

Previous Review Summaries (5 snapshots, latest commit 44a6145)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 44a6145)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/esbuild.js 194 The 2s age guard only covers recent edits; a same-length file whose mtime is preserved from before the window still reuses the recorded key and its stale transform.
Files Reviewed (1 file)
  • packages/kilo-vscode/esbuild.js - 1 issue

Incremental scope: 9e0bfa80..44a6145a (1 file, 11 insertions, 3 deletions). The stale-index finding is resolved: saveIndex now drops entries whose path no longer exists, and the comment matches that behavior. The cache-directory ownership warning is also resolved at HEAD (lstat, symlink/uid checks, realpath containment, 0700 chmod). The remaining suggestion is the narrower residual of the earlier mtime finding.

Fix these issues in Kilo Cloud

Previous review (commit 9e0bfa8)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 2
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/esbuild.js 128 The comment says dropped paths leave the index, but entries are never pruned (only added or overwritten)
packages/kilo-vscode/esbuild.js 187 The mtime + size fast path weakens the content-addressed guarantee, so a same-size edit that does not advance mtime reuses a stale transform
Files Reviewed (1 file)
  • packages/kilo-vscode/esbuild.js - 2 issues

Fix these issues in Kilo Cloud

Previous review (commit df9dc1e)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (1 file)
  • packages/kilo-vscode/esbuild.js - 0 issues (the two prior reclaim findings are fixed in df9dc1e; the earlier cache-directory ownership hardening is also in place)

Incremental scope: b94d43bc..df9dc1e9 (1 file, 20 insertions, 10 deletions). The sweep now wraps each entry in its own try/catch so one unreadable file no longer aborts the pass, and it age-bounds both .tmp (1h) and content-hashed .js (30d) entries. Concurrent readers degrade to a recompute on a missing entry, so deleting a still-needed .js entry is safe. No new correctness, security, or performance finding in the changed lines.

Previous review (commit b94d43b)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 2
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/esbuild.js 54 A single statSync/rmSync error aborts the whole orphan-reclaim loop, leaving remaining temp files behind.
packages/kilo-vscode/esbuild.js 55 Reclaim pass only removes .tmp files; content-hashed .js cache entries still accumulate without bound in the shared temp directory.
Files Reviewed (1 file)
  • packages/kilo-vscode/esbuild.js - 2 issues

Fix these issues in Kilo Cloud

Previous review (commit bdee7aa)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-vscode/esbuild.js 23 Predictable shared cache dir under os.tmpdir() can be pre-created by another local user and poisoned.

SUGGESTION

File Line Issue
packages/kilo-vscode/esbuild.js 106 Shared cache is never pruned; orphaned .tmp files from a killed build are not reclaimed.
Files Reviewed (2 files)
  • packages/kilo-vscode/esbuild.js - 2 issues
  • packages/kilo-vscode/package.json - 0 issues

The content-hash cache key (file name + source + toolchain versions) and the temp-file-plus-rename write are sound, and the prepare:sdk → parallel prepare:cli-binary / build:check:production split does not overlap outputs. No correctness issue found in the parallelization.

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

Only use the shared disk cache when the current user owns a directory
that other users cannot write to. os.tmpdir() is world writable on
Linux, so a predictable directory created by another local user must not
be trusted. Tighten permissions on an existing directory and fall back to
the in-memory cache when the directory is unusable.

Reclaim temp files left behind by a build killed between writing and
renaming.
Comment thread packages/kilo-vscode/esbuild.js Resolved
Comment thread packages/kilo-vscode/esbuild.js Resolved
Isolate failures to a single file so an entry that disappears between
readdir and stat cannot abort the sweep, and reclaim superseded .js
entries as well as orphaned temp files so the shared cache stays bounded.
Reject a symlinked or relocated cache path so the ownership check, the
chmod, and the sweep cannot be redirected into another directory.

Gate the sweep to at most once a day, since the directory is shared by
every worktree and retains entries for 30 days.

Record the content key for each source path while its size and mtime are
unchanged, so an unchanged file costs one stat instead of a read and a
hash. The index is per worktree and rebuilt from the files each build
loads.
Comment thread packages/kilo-vscode/esbuild.js Resolved
Comment thread packages/kilo-vscode/esbuild.js Resolved
Drop index entries whose file no longer exists so renames and deletions
do not accumulate, and correct the comment that claimed this already
happened.

Only trust a recorded key once the file is older than the coarsest
filesystem mtime granularity, so a same-length edit inside one timestamp
tick cannot reuse the previous transform.
Comment thread packages/kilo-vscode/esbuild.js Resolved
mtime alone can be preserved across a write by cp -p, rsync -t, touch -r
or a restored snapshot, which would reuse the previous transform for the
production bundle. ctime changes on every write and cannot be set by those
tools, so compare it too and read the file whenever it moved.

Raise the settling threshold above the coarsest filesystem mtime
granularity so a same-tick edit cannot be trusted.
@marius-kilocode
marius-kilocode merged commit fc08e31 into main Sep 16, 2026
23 checks passed
@marius-kilocode
marius-kilocode deleted the optimize-extension-performance branch September 16, 2026 08:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants