From e30a4a2202f804f149a05eb9f6ba65d4341b04bd Mon Sep 17 00:00:00 2001 From: Ramiro Date: Sat, 30 May 2026 05:21:04 +0200 Subject: [PATCH] fix(platform): search mise shims dir so mise-installed CLIs are detected (#3319) * fix(platform): search mise shims dir so mise-installed CLIs are detected - Add ~/.local/share/mise/shims (and MISE_DATA_DIR override + legacy ~/.mise/shims) to wellKnownUserToolchainBins. - This makes Pi, Kimi, and other mise-managed coding agents visible to the daemon even when launched from GUI contexts with stripped PATH. - Added tests for default and MISE_DATA_DIR cases. - Also pinned pnpm@10.33.2 in root mise.toml for better mise ergonomics. Before/after: more local CLIs now appear in the runtime picker (Kimi, Pi, Antigravity, Kilo, etc.). Refs: discussion in session around improving detection for common mise users. * fix(platform): address Copilot review on mise shims logic - Generalize the shims comment (no hard-coded CLI examples). - Make per-version Node toolchain scanning respect MISE_DATA_DIR (use the same mise root for installs as for shims). - Avoid duplicate shims entries when MISE_DATA_DIR makes legacy path identical to the primary one. Addresses the three inline comments from copilot-pull-request-reviewer on PR #3319. * test(platform): extend MISE_DATA_DIR test to cover installs scanning Addresses non-blocking review feedback from @nettee on PR #3319. The previous test only asserted shims behavior under a custom MISE_DATA_DIR. This extends it to also create fixture trees under customMise/installs/node/... and customMise/installs/npm-openai-codex/... and assert that the install paths are discovered while default-root paths are excluded. This makes the test robust against regressions in the installs scanning logic (existingMiseNpmPackageBinDirs + node version dirs). * fix(platform): only fall back to ~/.mise/shims when no MISE_DATA_DIR is set Addresses the remaining non-blocking review comment from @nettee on PR #3319. When an explicit MISE_DATA_DIR is provided, we no longer inject the legacy ~/.mise/shims path. This prevents stale shims from a previous mise layout from being re-introduced into detection. Also added a regression assertion in the MISE_DATA_DIR test. * fix(daemon): make claude-stream dedup robust when final assistant wrapper lacks msgId Prevents duplicated text and thinking output (especially visible during design system generation with AMR/Vela). Root cause: the textStreamed guard fell back to whenever the final message arrived without a string uid=501(ramarivera) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),399(com.apple.access_ssh),33(_appstore),98(_lpadmin),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),400(com.apple.access_remote_ae) (common in some AMR flows and design system tasks), causing the full content to be re-emitted even if it had already been delivered via streaming deltas. Fix: track whether any text or thinking was streamed via deltas for the current message and use that as a reliable fallback for the final wrapper instead of only trusting presence. * revert: remove dedup from claude-stream (PR #3319 should stay clean) --- mise.toml | 3 ++ packages/platform/src/index.ts | 20 +++++++++-- packages/platform/tests/index.test.ts | 50 +++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 mise.toml diff --git a/mise.toml b/mise.toml new file mode 100644 index 000000000..8e269c080 --- /dev/null +++ b/mise.toml @@ -0,0 +1,3 @@ +[tools] +node = "24" +pnpm = "10.33.2" diff --git a/packages/platform/src/index.ts b/packages/platform/src/index.ts index 546f0182e..50a9a2119 100644 --- a/packages/platform/src/index.ts +++ b/packages/platform/src/index.ts @@ -942,16 +942,32 @@ export function wellKnownUserToolchainBins( join(home, ".npm-global", "bin"), join(home, ".npm-packages", "bin"), ); + + // Mise shims: makes every tool installed with `mise install` visible to + // GUI-launched daemons even when the process inherits a stripped PATH. + // Respect MISE_DATA_DIR (the official way to relocate the whole mise tree). + // We only fall back to the legacy ~/.mise/shims path when no explicit + // MISE_DATA_DIR override is provided. + const miseDataOverride = resolveUserScopedHome(env.MISE_DATA_DIR, home); + const miseData = miseDataOverride || join(home, ".local", "share", "mise"); + dirs.push(join(miseData, "shims")); + + if (!miseDataOverride) { + dirs.push(join(home, ".mise", "shims")); + } + if (includeSystemBins) { dirs.push("/opt/homebrew/bin", "/usr/local/bin"); } // Per-version Node toolchains: scan the install root and surface every // version directory's bin folder. Best-effort — missing roots simply // contribute nothing. - dirs.push(...existingMiseNpmPackageBinDirs(join(home, ".local", "share", "mise", "installs"))); + // When MISE_DATA_DIR is set we use the same root for consistency with shims. + const miseInstalls = join(miseData, "installs"); + dirs.push(...existingMiseNpmPackageBinDirs(miseInstalls)); for (const installRoot of [ { - root: join(home, ".local", "share", "mise", "installs", "node"), + root: join(miseInstalls, "node"), segments: ["bin"], }, { diff --git a/packages/platform/tests/index.test.ts b/packages/platform/tests/index.test.ts index b67d73e91..f5e6b3563 100644 --- a/packages/platform/tests/index.test.ts +++ b/packages/platform/tests/index.test.ts @@ -826,6 +826,56 @@ describe("wellKnownUserToolchainBins", () => { } }); + it("includes the default mise shims dir so mise-installed CLIs (pi, kimi, etc.) are visible to GUI daemons", () => { + const home = mkdtempSync(join(tmpdir(), "wkutb-mise-shims-")); + try { + const dirs = wellKnownUserToolchainBins({ home, env: {}, includeSystemBins: false }); + expect(dirs).toContain(join(home, ".local", "share", "mise", "shims")); + // Legacy location too + expect(dirs).toContain(join(home, ".mise", "shims")); + } finally { + rmSync(home, { recursive: true, force: true }); + } + }); + + it("respects $MISE_DATA_DIR for the shims location (custom mise root)", () => { + const home = mkdtempSync(join(tmpdir(), "wkutb-mise-data-")); + const customMise = mkdtempSync(join(tmpdir(), "wkutb-custom-mise-")); + try { + // Create fixture install trees under the custom root so the installs + // scanning logic is exercised (this catches regressions that only + // affect shims but not the Node / npm-openai-codex install paths). + const customNodeBin = join(customMise, "installs", "node", "24.16.0", "bin"); + mkdirSync(customNodeBin, { recursive: true }); + writeFileSync(join(customNodeBin, "marker"), ""); + + const customCodexBin = join(customMise, "installs", "npm-openai-codex", "latest", "bin"); + mkdirSync(customCodexBin, { recursive: true }); + writeFileSync(join(customCodexBin, "codex"), ""); + + const dirs = wellKnownUserToolchainBins({ + home, + env: { MISE_DATA_DIR: customMise }, + includeSystemBins: false, + }); + + expect(dirs).toContain(join(customMise, "shims")); + // Install paths under custom root should be discovered + expect(dirs).toContain(customNodeBin); + expect(dirs).toContain(customCodexBin); + + // Default-root paths must not leak in when MISE_DATA_DIR is set + expect(dirs).not.toContain(join(home, ".local", "share", "mise", "shims")); + expect(dirs).not.toContain(join(home, ".local", "share", "mise", "installs", "node", "24.16.0", "bin")); + + // Legacy ~/.mise/shims must also be excluded when an explicit override is present + expect(dirs).not.toContain(join(home, ".mise", "shims")); + } finally { + rmSync(home, { recursive: true, force: true }); + rmSync(customMise, { recursive: true, force: true }); + } + }); + it("does not append a prefix entry when neither env var is set", () => { const home = mkdtempSync(join(tmpdir(), "wkutb-noprefix-")); try {