diff --git a/src/skillkit.ts b/src/skillkit.ts index 2c2551d..0c452a7 100644 --- a/src/skillkit.ts +++ b/src/skillkit.ts @@ -27,6 +27,16 @@ const IS_WIN = platform() === "win32"; const DB_PATH = join(HOME, ".skillkit", "analytics.db"); const BIN_NAMES = IS_WIN ? ["skillkit.cmd", "skillkit.exe", "skillkit"] : ["skillkit"]; +export function getPackageManagerBinDirs(home = HOME): string[] { + return [ + join(home, ".bun", "bin"), join(home, ".local", "share", "mise", "shims"), + join(home, ".local", "share", "pnpm"), join(home, ".volta", "bin"), + join(home, ".yarn", "bin"), join(home, ".config", "yarn", "global", "node_modules", ".bin"), + join(home, ".fnm", "aliases", "default", "bin"), join(home, ".asdf", "shims"), + join(home, ".proto", "bin"), + ]; +} + function buildPath(): string { const extra: string[] = []; if (IS_WIN) { @@ -100,15 +110,7 @@ function findSkillkitBin(): string | null { "/usr/local/bin", "/opt/homebrew/bin", join(HOME, ".local", "bin"), - join(HOME, ".bun", "bin"), - join(HOME, ".local", "share", "mise", "shims"), - join(HOME, ".local", "share", "pnpm"), // pnpm global bin - join(HOME, ".volta", "bin"), // Volta - join(HOME, ".yarn", "bin"), // Yarn classic - join(HOME, ".config", "yarn", "global", "node_modules", ".bin"), // Yarn modern - join(HOME, ".fnm", "aliases", "default", "bin"), // fnm - join(HOME, ".asdf", "shims"), // asdf - join(HOME, ".proto", "bin"), // proto + ...getPackageManagerBinDirs(), ); } for (const dir of searchDirs) { diff --git a/src/tool-configs.ts b/src/tool-configs.ts index 03fabd9..34a3859 100644 --- a/src/tool-configs.ts +++ b/src/tool-configs.ts @@ -2,6 +2,7 @@ import { homedir, platform } from "os"; import { existsSync, readdirSync } from "fs"; import { join } from "path"; import type { ToolConfig } from "./types"; +import { getPackageManagerBinDirs } from "./skillkit"; const HOME = homedir(); const IS_WIN = platform() === "win32"; @@ -36,7 +37,7 @@ function appExists(name: string): boolean { ); } -function cliExists(name: string): boolean { +export function cliExists(name: string): boolean { const names = IS_WIN ? [`${name}.cmd`, `${name}.exe`, name] : [name]; const dirs: string[] = []; if (IS_WIN) { @@ -51,6 +52,7 @@ function cliExists(name: string): boolean { "/usr/local/bin", "/opt/homebrew/bin", join(HOME, ".local", "bin"), + ...getPackageManagerBinDirs(), ); } for (const dir of dirs) { diff --git a/tests/tool-configs.test.ts b/tests/tool-configs.test.ts new file mode 100644 index 0000000..7dc7445 --- /dev/null +++ b/tests/tool-configs.test.ts @@ -0,0 +1,18 @@ +import { expect, mock, test } from "bun:test"; +import { homedir } from "os"; +import { join } from "path"; + +const existsSync = mock(() => false); +mock.module("fs", () => ({ existsSync, readdirSync: () => [] })); +const { cliExists } = await import("../src/tool-configs"); + +test("finds CLIs installed by supported package managers", () => { + for (const parts of [ + [".local", "share", "pnpm"], [".volta", "bin"], + [".fnm", "aliases", "default", "bin"], [".asdf", "shims"], [".proto", "bin"], + ]) { + const expected = join(homedir(), ...parts, "example-cli"); + existsSync.mockImplementation((path) => path === expected); + expect(cliExists("example-cli")).toBe(true); + } +});