diff --git a/CHANGELOG.md b/CHANGELOG.md index 534142f5a..8adee6045 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ metadata and the backend fallback mirror it. ### Fixed +- Agent CLI discovery prefers the newest installed NVM Node release when launched from the desktop — thanks @rudycelekli! (#2644) + - MCP speech tools wait through model loading and progress-extended CPU renders instead of timing out before the backend (#2609) ## [0.5.7] — 2026-10-05 diff --git a/docs/electron-repair.md b/docs/electron-repair.md index 74eae5bcd..341ccd4aa 100644 --- a/docs/electron-repair.md +++ b/docs/electron-repair.md @@ -125,3 +125,7 @@ Automatic renderer-crash repair uses the source workspace. Without an attached checkout, it opens the source-folder controls and preserves the request; choose a checkout and press Send to continue. Explicit app action requests continue to use the app workspace without a checkout. + +For a GUI launch on macOS or Linux, CLI discovery searches inherited PATH entries +first, then standard user install locations. NVM release folders are searched +from the newest numeric Node version to the oldest. diff --git a/electron/src/main/tool-path.test.ts b/electron/src/main/tool-path.test.ts index 849475e05..f89cdf94d 100644 --- a/electron/src/main/tool-path.test.ts +++ b/electron/src/main/tool-path.test.ts @@ -43,3 +43,22 @@ describe('toolSearchDirs', () => { expect(env.Path).toContain('npm'); }); }); + +it('searches NVM releases by numeric version, keeping an inherited Node first', async () => { + const { mkdtempSync, mkdirSync, rmSync } = await import('node:fs'); + const { tmpdir } = await import('node:os'); + const home = mkdtempSync(join(tmpdir(), 'voice-tool-path-')); + try { + for (const version of ['v9.11.2', 'v22.9.0', 'v22.10.0', 'v24.1.0']) + mkdirSync(join(home, '.nvm', 'versions', 'node', version, 'bin'), { recursive: true }); + const dirs = toolSearchDirs({ env: { PATH: '/chosen/node/bin' }, platform: 'darwin', home }); + expect(dirs[0]).toBe('/chosen/node/bin'); + expect(dirs.filter((path) => path.includes('/.nvm/'))).toEqual( + ['v24.1.0', 'v22.10.0', 'v22.9.0', 'v9.11.2'].map((version) => + join(home, '.nvm', 'versions', 'node', version, 'bin'), + ), + ); + } finally { + rmSync(home, { recursive: true, force: true }); + } +}); diff --git a/electron/src/main/tool-path.ts b/electron/src/main/tool-path.ts index 74c2a0bf0..c82c1cea6 100644 --- a/electron/src/main/tool-path.ts +++ b/electron/src/main/tool-path.ts @@ -12,8 +12,8 @@ function nvmBinDirs(home: string): string[] { const root = join(home, '.nvm', 'versions', 'node'); try { return readdirSync(root) - .sort() - .reverse() + .filter((version) => /^v\d+\.\d+\.\d+$/.test(version)) + .sort((left, right) => right.localeCompare(left, 'en', { numeric: true })) .map((version) => join(root, version, 'bin')); } catch { return [];