From 30d7c0805e3ddc81800b0f76fb41f38b04d1dea6 Mon Sep 17 00:00:00 2001 From: Li JiangHeng <1794551825@qq.com> Date: Thu, 3 Sep 2026 15:09:34 +0800 Subject: [PATCH 1/3] fix(cli/doctor-pi): skip unrelated local packages when probing embedding runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit piPluginDirCandidates treated every non-npm: entry in Pi packages[] as a candidate plugin tree, so local dev-path extensions (any package.json, regardless of name) were probed for the embedding runtime. The first broken candidate made doctor report 'native runtime and WASM fallback both unavailable' and stop, even when the real magic-context install was healthy. Now only directories whose package.json names @cortexkit/pi-magic-context qualify as candidates, and broken candidates no longer abort the scan — a stale local dev tree cannot mask a healthy managed install. Repro: register any local-path Pi extension (D:\repo\my-extension) in settings.json packages[], run 'doctor --harness pi' — doctor blamed the extension's package.json for missing onnxruntime-web deps instead of reporting the actual plugin install. --- packages/cli/src/commands/doctor-pi.test.ts | 45 +++++++++++++++++++++ packages/cli/src/commands/doctor-pi.ts | 45 ++++++++++++++++----- 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/commands/doctor-pi.test.ts b/packages/cli/src/commands/doctor-pi.test.ts index a25721f26..2133cfdff 100644 --- a/packages/cli/src/commands/doctor-pi.test.ts +++ b/packages/cli/src/commands/doctor-pi.test.ts @@ -374,6 +374,51 @@ describe("Pi doctor", () => { expect(output).toContain("WARN 2"); }); + it("skips unrelated local dev-path packages and broken trees when probing the embedding runtime", async () => { + const root = makeTempRoot(); + const cwd = makeTempRoot("mc-pi-doctor-cwd-"); + const agentDir = setEnv(root, cwd); + writeHealthyFiles(agentDir, cwd); + + // Unrelated local extension: has a package.json but is NOT the + // magic-context plugin. Must not be probed as an embedding candidate. + const unrelatedPlugin = makeTempRoot("mc-pi-doctor-unrelated-"); + writeFileSync( + join(unrelatedPlugin, "package.json"), + JSON.stringify({ name: "pi-tree-git-checkpoint", version: "0.0.0" }), + ); + // Local dev tree of the actual plugin that is missing all embedding deps. + const brokenDevTree = makeTempRoot("mc-pi-doctor-dev-"); + writeFileSync( + join(brokenDevTree, "package.json"), + JSON.stringify({ name: "@cortexkit/pi-magic-context", version: "0.0.0-dev" }), + ); + + writeFileSync( + join(agentDir, "settings.json"), + JSON.stringify({ + packages: [ + "npm:@cortexkit/pi-magic-context", + unrelatedPlugin, + brokenDevTree, + ], + }), + ); + createInstalledPiPlugin(agentDir, true); + const prompts = new MockPrompts(); + + const code = await runDoctor(baseOptions(root, cwd, prompts)); + + expect(code).toBe(0); + const output = prompts.messages.join("\n"); + expect(output).toContain( + "PASS Embedding provider: local (native runtime selected and OK)", + ); + expect(output).not.toContain( + "WARN Embedding provider: local — native runtime and WASM fallback both unavailable", + ); + }); + it("reports the WASM fallback when onnxruntime-node is completely absent", async () => { const root = makeTempRoot(); const cwd = makeTempRoot("mc-pi-doctor-cwd-"); diff --git a/packages/cli/src/commands/doctor-pi.ts b/packages/cli/src/commands/doctor-pi.ts index 4fd52d1b1..22ac6c264 100644 --- a/packages/cli/src/commands/doctor-pi.ts +++ b/packages/cli/src/commands/doctor-pi.ts @@ -319,18 +319,35 @@ function packagesFrom(settings: Record): unknown[] { * /.pi/npm/node_modules/ (project). We collect every plausible dir * with a package.json; the resolver stays SILENT for any that don't exist. */ +/** True when the directory's package.json declares the magic-context Pi plugin. */ +function isPiMagicContextPackageDir(dir: string): boolean { + const packageJson = join(dir, "package.json"); + if (!existsSync(packageJson)) return false; + try { + const pkg = JSON.parse(readFileSync(packageJson, "utf-8")) as { + name?: unknown; + }; + return typeof pkg.name === "string" && pkg.name === PACKAGE_NAME; + } catch { + return false; + } +} + function piPluginDirCandidates(packages: unknown[], cwd: string): string[] { const dirs: string[] = []; const agentDir = getPiAgentConfigDir(); // Local dev-path entries: a string spec that is NOT an npm: specifier and // resolves to a directory on disk. Relative entries are resolved against the - // Pi agent dir (Pi's settings.packages base). + // Pi agent dir (Pi's settings.packages base). Only directories whose + // package.json names the magic-context plugin itself are candidates — other + // local extensions registered in packages[] must not be probed for the + // embedding runtime. for (const entry of packages) { const spec = typeof entry === "string" ? entry.trim() : ""; if (!spec || spec.startsWith("npm:")) continue; const resolved = isAbsolute(spec) ? spec : join(agentDir, spec); - dirs.push(resolved); + if (isPiMagicContextPackageDir(resolved)) dirs.push(resolved); } // Managed npm install roots (hoisted): /node_modules/. @@ -851,6 +868,9 @@ async function runHealthChecks(options: { // persistence-capable Node WASM fallback. Resolution starts from the // installed plugin dir and stays silent when no tree can be inspected. let runtimeReported = false; + let firstBroken: ReturnType< + typeof checkLocalEmbeddingRuntimeByResolution + > | null = null; let runtimeUnverifiedReason = "no installed plugin tree found to inspect"; for (const pluginDir of piPluginDirCandidates(packages, options.cwd)) { const runtime = checkLocalEmbeddingRuntimeByResolution( @@ -883,18 +903,23 @@ async function runHealthChecks(options: { break; } if (isLocalEmbeddingRuntimeBroken(runtime)) { - add(results, "warn", formatLocalEmbeddingRuntimeDoctorWarning(runtime)); - runtimeReported = true; - break; + // Keep probing: an earlier broken candidate (e.g. a stale local + // dev-path tree) must not mask a healthy managed install. + firstBroken ??= runtime; + continue; } if (runtime.state === "unknown") runtimeUnverifiedReason = runtime.reason; } if (!runtimeReported) { - add( - results, - "warn", - `Embedding provider ${loadedConfig.config.embedding.provider}: selected runtime unverified (${runtimeUnverifiedReason})`, - ); + if (firstBroken) { + add(results, "warn", formatLocalEmbeddingRuntimeDoctorWarning(firstBroken)); + } else { + add( + results, + "warn", + `Embedding provider ${loadedConfig.config.embedding.provider}: selected runtime unverified (${runtimeUnverifiedReason})`, + ); + } } } From 0d8848f5798c537df5c5c56b16e37b3cea1f5123 Mon Sep 17 00:00:00 2001 From: Li JiangHeng <1794551825@qq.com> Date: Thu, 3 Sep 2026 15:38:06 +0800 Subject: [PATCH 2/3] fix(cli/doctor-pi): don't let a WASM fallback mask a native-capable install Address review findings (Greptile P1, cubic-dev-ai P2): the loop still stopped at the first candidate with a working WASM fallback, reporting a degraded runtime even when a later managed install had the native binding. Record the best degraded candidate and keep probing; only report the fallback WARN when no candidate is fully OK. Tests: add regression coverage for (1) unrelated local packages never probed (unverified, not a broken-runtime WARN, when only unrelated packages are registered), and (2) a WASM-only dev tree not masking a later native-capable install. --- packages/cli/src/commands/doctor-pi.test.ts | 87 +++++++++++++++++++++ packages/cli/src/commands/doctor-pi.ts | 15 +++- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/commands/doctor-pi.test.ts b/packages/cli/src/commands/doctor-pi.test.ts index 2133cfdff..b70c802a6 100644 --- a/packages/cli/src/commands/doctor-pi.test.ts +++ b/packages/cli/src/commands/doctor-pi.test.ts @@ -419,6 +419,93 @@ describe("Pi doctor", () => { ); }); + it("prefers a later native-capable install over an earlier WASM fallback", async () => { + const root = makeTempRoot(); + const cwd = makeTempRoot("mc-pi-doctor-cwd-"); + const agentDir = setEnv(root, cwd); + writeHealthyFiles(agentDir, cwd); + + // Local dev tree of the actual plugin with only a WASM fallback + // (no native binding) — probing it alone would report a degraded + // runtime. + const wasmDevTree = makeTempRoot("mc-pi-doctor-wasm-dev-"); + mkdirSync(join(wasmDevTree, "node_modules", "onnxruntime-web"), { + recursive: true, + }); + writeFileSync( + join(wasmDevTree, "node_modules", "onnxruntime-web", "package.json"), + JSON.stringify({ name: "onnxruntime-web", main: "index.js" }), + ); + writeFileSync( + join(wasmDevTree, "node_modules", "onnxruntime-web", "index.js"), + "module.exports = {};\n", + ); + mkdirSync(join(wasmDevTree, "dist"), { recursive: true }); + writeFileSync( + join(wasmDevTree, "dist", "transformers-node-wasm.js"), + "export {};\n", + ); + writeFileSync( + join(wasmDevTree, "package.json"), + JSON.stringify({ name: "@cortexkit/pi-magic-context", version: "0.0.0-dev" }), + ); + + writeFileSync( + join(agentDir, "settings.json"), + JSON.stringify({ + packages: ["npm:@cortexkit/pi-magic-context", wasmDevTree], + }), + ); + createInstalledPiPlugin(agentDir, true); + const prompts = new MockPrompts(); + + const code = await runDoctor(baseOptions(root, cwd, prompts)); + + expect(code).toBe(0); + const output = prompts.messages.join("\n"); + expect(output).toContain( + "PASS Embedding provider: local (native runtime selected and OK)", + ); + expect(output).not.toContain( + "WARN Embedding provider: local — onnxruntime-node native binding failed", + ); + }); + + it("reports unverified, not a broken-runtime WARN, when only unrelated local packages are registered", async () => { + const root = makeTempRoot(); + const cwd = makeTempRoot("mc-pi-doctor-cwd-"); + const agentDir = setEnv(root, cwd); + writeHealthyFiles(agentDir, cwd); + + // Only an unrelated local extension is registered; the magic-context + // managed install tree is absent. The unrelated package must not be + // probed as an embedding candidate, so doctor reports unverified + // instead of blaming it for a missing onnxruntime. + const unrelatedPlugin = makeTempRoot("mc-pi-doctor-unrelated-"); + writeFileSync( + join(unrelatedPlugin, "package.json"), + JSON.stringify({ name: "pi-tree-git-checkpoint", version: "0.0.0" }), + ); + writeFileSync( + join(agentDir, "settings.json"), + JSON.stringify({ + packages: ["npm:@cortexkit/pi-magic-context", unrelatedPlugin], + }), + ); + const prompts = new MockPrompts(); + + const code = await runDoctor(baseOptions(root, cwd, prompts)); + + expect(code).toBe(0); + const output = prompts.messages.join("\n"); + expect(output).toContain( + "selected runtime unverified (no installed plugin tree found to inspect)", + ); + expect(output).not.toContain( + "WARN Embedding provider: local — native runtime and WASM fallback both unavailable", + ); + }); + it("reports the WASM fallback when onnxruntime-node is completely absent", async () => { const root = makeTempRoot(); const cwd = makeTempRoot("mc-pi-doctor-cwd-"); diff --git a/packages/cli/src/commands/doctor-pi.ts b/packages/cli/src/commands/doctor-pi.ts index 22ac6c264..cf5eb1870 100644 --- a/packages/cli/src/commands/doctor-pi.ts +++ b/packages/cli/src/commands/doctor-pi.ts @@ -868,6 +868,9 @@ async function runHealthChecks(options: { // persistence-capable Node WASM fallback. Resolution starts from the // installed plugin dir and stays silent when no tree can be inspected. let runtimeReported = false; + let firstFallback: ReturnType< + typeof checkLocalEmbeddingRuntimeByResolution + > | null = null; let firstBroken: ReturnType< typeof checkLocalEmbeddingRuntimeByResolution > | null = null; @@ -898,9 +901,11 @@ async function runHealthChecks(options: { break; } if (runtime.state === "wasm-fallback") { - add(results, "warn", formatLocalEmbeddingRuntimeWasmFallback(runtime)); - runtimeReported = true; - break; + // Remember the best degraded candidate but keep probing: a WASM + // fallback in an earlier tree must not mask a later native-capable + // install. + firstFallback ??= runtime; + continue; } if (isLocalEmbeddingRuntimeBroken(runtime)) { // Keep probing: an earlier broken candidate (e.g. a stale local @@ -911,7 +916,9 @@ async function runHealthChecks(options: { if (runtime.state === "unknown") runtimeUnverifiedReason = runtime.reason; } if (!runtimeReported) { - if (firstBroken) { + if (firstFallback) { + add(results, "warn", formatLocalEmbeddingRuntimeWasmFallback(firstFallback)); + } else if (firstBroken) { add(results, "warn", formatLocalEmbeddingRuntimeDoctorWarning(firstBroken)); } else { add( From 8ba851211fdcc7692525147e866871945903a568 Mon Sep 17 00:00:00 2001 From: Li JiangHeng <1794551825@qq.com> Date: Mon, 14 Sep 2026 10:48:37 +0800 Subject: [PATCH 3/3] fix(cli/doctor-pi): report broken candidates and local load conflicts --- packages/cli/src/commands/doctor-pi.test.ts | 88 +++++++++++++++++---- packages/cli/src/commands/doctor-pi.ts | 43 ++++++---- 2 files changed, 99 insertions(+), 32 deletions(-) diff --git a/packages/cli/src/commands/doctor-pi.test.ts b/packages/cli/src/commands/doctor-pi.test.ts index b70c802a6..924d38fc3 100644 --- a/packages/cli/src/commands/doctor-pi.test.ts +++ b/packages/cli/src/commands/doctor-pi.test.ts @@ -397,11 +397,7 @@ describe("Pi doctor", () => { writeFileSync( join(agentDir, "settings.json"), JSON.stringify({ - packages: [ - "npm:@cortexkit/pi-magic-context", - unrelatedPlugin, - brokenDevTree, - ], + packages: ["npm:@cortexkit/pi-magic-context", unrelatedPlugin, brokenDevTree], }), ); createInstalledPiPlugin(agentDir, true); @@ -409,11 +405,10 @@ describe("Pi doctor", () => { const code = await runDoctor(baseOptions(root, cwd, prompts)); - expect(code).toBe(0); + expect(code).toBe(1); const output = prompts.messages.join("\n"); - expect(output).toContain( - "PASS Embedding provider: local (native runtime selected and OK)", - ); + expect(output).toContain("Multiple magic-context entries in Pi packages[]"); + expect(output).toContain("PASS Embedding provider: local (native runtime selected and OK)"); expect(output).not.toContain( "WARN Embedding provider: local — native runtime and WASM fallback both unavailable", ); @@ -441,10 +436,7 @@ describe("Pi doctor", () => { "module.exports = {};\n", ); mkdirSync(join(wasmDevTree, "dist"), { recursive: true }); - writeFileSync( - join(wasmDevTree, "dist", "transformers-node-wasm.js"), - "export {};\n", - ); + writeFileSync(join(wasmDevTree, "dist", "transformers-node-wasm.js"), "export {};\n"); writeFileSync( join(wasmDevTree, "package.json"), JSON.stringify({ name: "@cortexkit/pi-magic-context", version: "0.0.0-dev" }), @@ -461,11 +453,10 @@ describe("Pi doctor", () => { const code = await runDoctor(baseOptions(root, cwd, prompts)); - expect(code).toBe(0); + expect(code).toBe(1); const output = prompts.messages.join("\n"); - expect(output).toContain( - "PASS Embedding provider: local (native runtime selected and OK)", - ); + expect(output).toContain("Multiple magic-context entries in Pi packages[]"); + expect(output).toContain("PASS Embedding provider: local (native runtime selected and OK)"); expect(output).not.toContain( "WARN Embedding provider: local — onnxruntime-node native binding failed", ); @@ -506,6 +497,69 @@ describe("Pi doctor", () => { ); }); + it("reports every broken candidate with its native and WASM reasons", async () => { + const root = makeTempRoot(); + const cwd = makeTempRoot("mc-pi-doctor-cwd-"); + const agentDir = setEnv(root, cwd); + writeHealthyFiles(agentDir, cwd); + const brokenTrees = [makeTempRoot("mc-broken-first-"), makeTempRoot("mc-broken-second-")]; + for (const tree of brokenTrees) { + writeFileSync( + join(tree, "package.json"), + JSON.stringify({ name: "@cortexkit/pi-magic-context" }), + ); + } + writeFileSync( + join(agentDir, "settings.json"), + JSON.stringify({ packages: ["npm:@cortexkit/pi-magic-context", ...brokenTrees] }), + ); + const prompts = new MockPrompts(); + await runDoctor(baseOptions(root, cwd, prompts)); + const warnings = prompts.messages.filter((message) => + message.includes("WARN Embedding provider: local"), + ); + for (const tree of brokenTrees) { + expect( + warnings.some( + (warning) => + warning.includes(tree) && + warning.includes("native:") && + warning.includes("WASM:"), + ), + ).toBe(true); + } + }); + + it.each([ + false, + true, + ])("detects npm plus local Magic Context identity (object source: %s)", async (objectSource) => { + const root = makeTempRoot(); + const cwd = makeTempRoot("mc-pi-doctor-cwd-"); + const agentDir = setEnv(root, cwd); + writeHealthyFiles(agentDir, cwd); + const localDir = join(agentDir, "local-plugin"); + mkdirSync(localDir); + writeFileSync( + join(localDir, "package.json"), + JSON.stringify({ name: "@cortexkit/pi-magic-context" }), + ); + const source = "./local-plugin"; + writeFileSync( + join(agentDir, "settings.json"), + JSON.stringify({ + packages: ["npm:@cortexkit/pi-magic-context", objectSource ? { source } : source], + }), + ); + const prompts = new MockPrompts(); + expect(await runDoctor(baseOptions(root, cwd, prompts))).toBe(1); + const output = prompts.messages.join("\n"); + expect(output).toContain("Multiple magic-context entries in Pi packages[]"); + expect(output).toContain(source); + expect(output).not.toContain("Other Pi extensions registered:"); + expect(output).not.toContain("selected runtime unverified"); + }); + it("reports the WASM fallback when onnxruntime-node is completely absent", async () => { const root = makeTempRoot(); const cwd = makeTempRoot("mc-pi-doctor-cwd-"); diff --git a/packages/cli/src/commands/doctor-pi.ts b/packages/cli/src/commands/doctor-pi.ts index cf5eb1870..c7b532785 100644 --- a/packages/cli/src/commands/doctor-pi.ts +++ b/packages/cli/src/commands/doctor-pi.ts @@ -333,6 +333,23 @@ function isPiMagicContextPackageDir(dir: string): boolean { } } +function localPiMagicContextPackageDir(entry: unknown): string | null { + const source = + typeof entry === "string" + ? entry + : entry && typeof entry === "object" && "source" in entry + ? entry.source + : null; + const spec = typeof source === "string" ? source.trim() : ""; + if (!spec || spec.startsWith("npm:")) return null; + const dir = isAbsolute(spec) ? spec : join(getPiAgentConfigDir(), spec); + return isPiMagicContextPackageDir(dir) ? dir : null; +} + +function isConfiguredPiMagicContextEntry(entry: unknown): boolean { + return isPiMagicContextPackageEntry(entry) || localPiMagicContextPackageDir(entry) !== null; +} + function piPluginDirCandidates(packages: unknown[], cwd: string): string[] { const dirs: string[] = []; const agentDir = getPiAgentConfigDir(); @@ -344,10 +361,8 @@ function piPluginDirCandidates(packages: unknown[], cwd: string): string[] { // local extensions registered in packages[] must not be probed for the // embedding runtime. for (const entry of packages) { - const spec = typeof entry === "string" ? entry.trim() : ""; - if (!spec || spec.startsWith("npm:")) continue; - const resolved = isAbsolute(spec) ? spec : join(agentDir, spec); - if (isPiMagicContextPackageDir(resolved)) dirs.push(resolved); + const resolved = localPiMagicContextPackageDir(entry); + if (resolved) dirs.push(resolved); } // Managed npm install roots (hoisted): /node_modules/. @@ -868,12 +883,8 @@ async function runHealthChecks(options: { // persistence-capable Node WASM fallback. Resolution starts from the // installed plugin dir and stays silent when no tree can be inspected. let runtimeReported = false; - let firstFallback: ReturnType< - typeof checkLocalEmbeddingRuntimeByResolution - > | null = null; - let firstBroken: ReturnType< - typeof checkLocalEmbeddingRuntimeByResolution - > | null = null; + let firstFallback: ReturnType | null = null; + const brokenWarnings: string[] = []; let runtimeUnverifiedReason = "no installed plugin tree found to inspect"; for (const pluginDir of piPluginDirCandidates(packages, options.cwd)) { const runtime = checkLocalEmbeddingRuntimeByResolution( @@ -910,7 +921,9 @@ async function runHealthChecks(options: { if (isLocalEmbeddingRuntimeBroken(runtime)) { // Keep probing: an earlier broken candidate (e.g. a stale local // dev-path tree) must not mask a healthy managed install. - firstBroken ??= runtime; + brokenWarnings.push( + `${formatLocalEmbeddingRuntimeDoctorWarning(runtime)} Candidate: ${pluginDir}`, + ); continue; } if (runtime.state === "unknown") runtimeUnverifiedReason = runtime.reason; @@ -918,8 +931,8 @@ async function runHealthChecks(options: { if (!runtimeReported) { if (firstFallback) { add(results, "warn", formatLocalEmbeddingRuntimeWasmFallback(firstFallback)); - } else if (firstBroken) { - add(results, "warn", formatLocalEmbeddingRuntimeDoctorWarning(firstBroken)); + } else if (brokenWarnings.length > 0) { + for (const warning of brokenWarnings) add(results, "warn", warning); } else { add( results, @@ -934,7 +947,7 @@ async function runHealthChecks(options: { // extensions today, but we still check for self-conflicts that the user // can hit (e.g. accidentally registering both an npm entry AND a local // dev-path entry, which causes duplicate plugin loading). - const piEntries = packages.filter(isPiMagicContextPackageEntry).map(describePiPackageEntry); + const piEntries = packages.filter(isConfiguredPiMagicContextEntry).map(describePiPackageEntry); if (piEntries.length > 1) { add( results, @@ -946,7 +959,7 @@ async function runHealthChecks(options: { } const otherExtensions = packages - .filter((entry) => !isPiMagicContextPackageEntry(entry)) + .filter((entry) => !isConfiguredPiMagicContextEntry(entry)) .map(describePiPackageEntry); if (otherExtensions.length > 0) { add(results, "info", `Other Pi extensions registered: ${otherExtensions.join(", ")}`);