From 36214bc79c0b3140d275ae04cb47a0785c154462 Mon Sep 17 00:00:00 2001 From: bowenliang123 Date: Tue, 28 Jul 2026 11:28:20 +0800 Subject: [PATCH] fix(cli): detect bun global installs in ~/.bun/node_modules layout --- .../fix-bun-global-install-detection.md | 5 ++++ apps/kimi-code/src/cli/update/source.ts | 6 +++-- apps/kimi-code/test/cli/update/source.test.ts | 27 +++++++++++++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 .changeset/fix-bun-global-install-detection.md diff --git a/.changeset/fix-bun-global-install-detection.md b/.changeset/fix-bun-global-install-detection.md new file mode 100644 index 0000000000..296072b9f3 --- /dev/null +++ b/.changeset/fix-bun-global-install-detection.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix install-source detection for bun global installs by recognizing the `~/.bun/node_modules/` layout used by recent bun versions. diff --git a/apps/kimi-code/src/cli/update/source.ts b/apps/kimi-code/src/cli/update/source.ts index 7d6904b673..2a896ce80c 100644 --- a/apps/kimi-code/src/cli/update/source.ts +++ b/apps/kimi-code/src/cli/update/source.ts @@ -39,7 +39,7 @@ export function detectNativeInstall(): boolean { // Path heuristic markers (compared in lowercase; both forward and backward slashes accepted). const PNPM_PATH_SEGMENT = 'pnpm/global/'; const YARN_PATH_SEGMENTS = ['.config/yarn/global/', '/.yarn/global/']; -const BUN_PATH_SEGMENT = '.bun/install/global/'; +const BUN_PATH_SEGMENTS = ['/.bun/install/global/', '/.bun/node_modules/']; // Homebrew installs formulae under its Cellar directory. Avoid matching the // broader /homebrew/ prefix — on Apple Silicon, npm itself lives under // /opt/homebrew/, so `npm install -g` paths also contain /homebrew/. @@ -60,7 +60,9 @@ export function classifyByPathHeuristic(packageRoot: string): InstallSource | nu for (const seg of YARN_PATH_SEGMENTS) { if (normalized.includes(seg)) return 'yarn-global'; } - if (normalized.includes(BUN_PATH_SEGMENT)) return 'bun-global'; + for (const seg of BUN_PATH_SEGMENTS) { + if (normalized.includes(seg)) return 'bun-global'; + } if (normalized.includes(HOMEBREW_PATH_SEGMENT)) return 'homebrew'; return null; } diff --git a/apps/kimi-code/test/cli/update/source.test.ts b/apps/kimi-code/test/cli/update/source.test.ts index dd88d32c3c..bd6c12d5f0 100644 --- a/apps/kimi-code/test/cli/update/source.test.ts +++ b/apps/kimi-code/test/cli/update/source.test.ts @@ -41,12 +41,24 @@ describe('classifyByPathHeuristic', () => { ).toBe('yarn-global'); }); - it('detects bun global', () => { + it('detects bun global (install/global layout)', () => { expect( classifyByPathHeuristic('/Users/me/.bun/install/global/node_modules/@moonshot-ai/kimi-code'), ).toBe('bun-global'); }); + it('detects bun global (node_modules layout)', () => { + expect( + classifyByPathHeuristic('/Users/me/.bun/node_modules/@moonshot-ai/kimi-code'), + ).toBe('bun-global'); + }); + + it('does not treat a local project named foo.bun as bun global', () => { + expect( + classifyByPathHeuristic('/work/foo.bun/node_modules/@moonshot-ai/kimi-code'), + ).toBeNull(); + }); + it('detects homebrew on macOS (Cellar path)', () => { expect( classifyByPathHeuristic('/opt/homebrew/Cellar/kimi-code/0.5.0/libexec/lib/node_modules/@moonshot-ai/kimi-code'), @@ -108,7 +120,7 @@ describe('detectInstallSource', () => { ).resolves.toBe('yarn-global'); }); - it('returns bun-global when packageRoot matches bun heuristic', async () => { + it('returns bun-global when packageRoot matches bun heuristic (install/global layout)', async () => { await expect( detectInstallSource({ getPackageRoot: () => '/Users/me/.bun/install/global/node_modules/@moonshot-ai/kimi-code', @@ -119,6 +131,17 @@ describe('detectInstallSource', () => { ).resolves.toBe('bun-global'); }); + it('returns bun-global when packageRoot matches bun heuristic (node_modules layout)', async () => { + await expect( + detectInstallSource({ + getPackageRoot: () => '/Users/me/.bun/node_modules/@moonshot-ai/kimi-code', + getGlobalPrefix: async () => '/usr/local', + detectNative: () => false, + platform: 'darwin', + }), + ).resolves.toBe('bun-global'); + }); + it('returns npm-global when packageRoot matches npm prefix', async () => { await expect( detectInstallSource({