From be9d6c00614ae45602c4f5d42f65f94573f2a57a Mon Sep 17 00:00:00 2001 From: popododo0720 Date: Wed, 21 Jan 2026 15:42:21 +0900 Subject: [PATCH] fix(doctor): improve AST-Grep NAPI detection for bunx environments Use dynamic import instead of require.resolve() to detect @ast-grep/napi installation. This fixes false negatives when running via bunx where the module exists in ~/.config/opencode/node_modules but isn't resolvable from the temporary execution directory. Also adds fallback path checks for common installation locations. Fixes #898 --- src/cli/doctor/checks/dependencies.test.ts | 6 ++--- src/cli/doctor/checks/dependencies.ts | 29 +++++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/cli/doctor/checks/dependencies.test.ts b/src/cli/doctor/checks/dependencies.test.ts index 523f9594..9b102487 100644 --- a/src/cli/doctor/checks/dependencies.test.ts +++ b/src/cli/doctor/checks/dependencies.test.ts @@ -16,10 +16,10 @@ describe("dependencies check", () => { }) describe("checkAstGrepNapi", () => { - it("returns dependency info", () => { + it("returns dependency info", async () => { // #given // #when checking ast-grep napi - const info = deps.checkAstGrepNapi() + const info = await deps.checkAstGrepNapi() // #then should return valid info expect(info.name).toBe("AST-Grep NAPI") @@ -95,7 +95,7 @@ describe("dependencies check", () => { it("returns pass when installed", async () => { // #given napi installed - checkSpy = spyOn(deps, "checkAstGrepNapi").mockReturnValue({ + checkSpy = spyOn(deps, "checkAstGrepNapi").mockResolvedValue({ name: "AST-Grep NAPI", required: false, installed: true, diff --git a/src/cli/doctor/checks/dependencies.ts b/src/cli/doctor/checks/dependencies.ts index 2a941a8f..09a476bc 100644 --- a/src/cli/doctor/checks/dependencies.ts +++ b/src/cli/doctor/checks/dependencies.ts @@ -56,9 +56,10 @@ export async function checkAstGrepCli(): Promise { } } -export function checkAstGrepNapi(): DependencyInfo { +export async function checkAstGrepNapi(): Promise { + // Try dynamic import first (works in bunx temporary environments) try { - require.resolve("@ast-grep/napi") + await import("@ast-grep/napi") return { name: "AST-Grep NAPI", required: false, @@ -67,6 +68,28 @@ export function checkAstGrepNapi(): DependencyInfo { path: null, } } catch { + // Fallback: check common installation paths + const { existsSync } = await import("fs") + const { join } = await import("path") + const { homedir } = await import("os") + + const pathsToCheck = [ + join(homedir(), ".config", "opencode", "node_modules", "@ast-grep", "napi"), + join(process.cwd(), "node_modules", "@ast-grep", "napi"), + ] + + for (const napiPath of pathsToCheck) { + if (existsSync(napiPath)) { + return { + name: "AST-Grep NAPI", + required: false, + installed: true, + version: null, + path: napiPath, + } + } + } + return { name: "AST-Grep NAPI", required: false, @@ -127,7 +150,7 @@ export async function checkDependencyAstGrepCli(): Promise { } export async function checkDependencyAstGrepNapi(): Promise { - const info = checkAstGrepNapi() + const info = await checkAstGrepNapi() return dependencyToCheckResult(info, CHECK_NAMES[CHECK_IDS.DEP_AST_GREP_NAPI]) }