From 732c57435c981ddb4d444d823550a158682f801f Mon Sep 17 00:00:00 2001 From: MasterSwords1 <142840981+MasterSwords1@users.noreply.github.com> Date: Sat, 1 Aug 2026 06:54:37 +0100 Subject: [PATCH] fix(cli): update plugins doctor clean message (#115073) (#117160) * fix(cli): update plugins doctor clean message (#115073) Root cause: plugins doctor reports 'No plugin issues detected.' even when a selected context-engine plugin is quarantined at runtime, because doctor is a fast static control-plane check. Architectural owner: plugins-cli.runtime.ts Canonical fix: Change clean status message to direct operators to 'openclaw health' for active runtime quarantine/fallback status. Production LOC delta: +2 LOC * fix(cli): update plugins doctor clean message wording (#115073) Root cause: Refine the diagnostic text message to run openclaw health directly. Architectural owner: src/cli/plugins-cli.runtime.ts * fix(cli): clarify plugin doctor scope --------- Co-authored-by: Vincent Koc --- docs/cli/plugins.md | 2 +- src/cli/plugins-cli.list.test.ts | 28 ++++++++++++++++------------ src/cli/plugins-cli.runtime.ts | 5 ++++- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/cli/plugins.md b/docs/cli/plugins.md index 9b79f47b4877..099266347bf4 100644 --- a/docs/cli/plugins.md +++ b/docs/cli/plugins.md @@ -508,7 +508,7 @@ The `--json` flag outputs a machine-readable report suitable for scripting and a openclaw plugins doctor ``` -`doctor` reports plugin load errors, manifest/discovery diagnostics, compatibility notices, and stale plugin config references such as missing plugin slots. When the install tree and plugin config are clean it prints `No plugin issues detected.` If stale config remains but the install tree is otherwise healthy, the summary says so instead of implying full plugin health. +`doctor` reports plugin load errors, manifest/discovery diagnostics, compatibility notices, and stale plugin config references such as missing plugin slots. It loads plugin modules without activating plugins and does not query the running Gateway. When these local checks pass, it prints `Plugin discovery, module loading, compatibility, and configuration checks passed. Run "openclaw health" to check the running Gateway, including runtime quarantines and fallbacks.` The [health command](/cli/health) reads current runtime quarantine and fallback state from the Gateway. If stale config remains but the install tree is otherwise healthy, the summary says so instead of implying full plugin health. If a configured plugin is present on disk but blocked by the loader's path-safety checks, config validation keeps the plugin entry and reports it as `present but blocked`. Fix the preceding blocked-plugin diagnostic, such as path ownership or world-writable permissions, instead of removing the `plugins.entries.` or `plugins.allow` config. diff --git a/src/cli/plugins-cli.list.test.ts b/src/cli/plugins-cli.list.test.ts index 1a01113efbb4..b8df7ae7037c 100644 --- a/src/cli/plugins-cli.list.test.ts +++ b/src/cli/plugins-cli.list.test.ts @@ -22,6 +22,10 @@ const workshopMocks = vi.hoisted(() => ({ detectToolPolicyDiagnostic: vi.fn(), })); +const cleanDoctorMessage = + "Plugin discovery, module loading, compatibility, and configuration checks passed. " + + 'Run "openclaw health" to check the running Gateway, including runtime quarantines and fallbacks.'; + vi.mock("../skills/workshop/tool-policy-diagnostic.js", () => ({ detectSkillWorkshopToolPolicyDiagnostic: workshopMocks.detectToolPolicyDiagnostic, })); @@ -93,7 +97,7 @@ describe("plugins cli list", () => { await runPluginsCommand(["plugins", "doctor"]); expect(buildPluginDiagnosticsReport).toHaveBeenCalledWith({ config: {}, effectiveOnly: true }); - expect(runtimeLogs).toContain("No plugin issues detected."); + expect(runtimeLogs).toContain(cleanDoctorMessage); }); it("reports stale plugin config in doctor output without claiming full plugin health", async () => { @@ -145,7 +149,7 @@ describe("plugins cli list", () => { expect(output).toContain( "No plugin install-tree issues detected; configuration warnings remain.", ); - expect(output).not.toContain("No plugin issues detected."); + expect(output).not.toContain(cleanDoctorMessage); }); it("reports missing configured Codex runtime plugin in doctor output", async () => { @@ -191,7 +195,7 @@ describe("plugins cli list", () => { expect(output).toContain( "No plugin install-tree issues detected; configuration warnings remain.", ); - expect(output).not.toContain("No plugin issues detected."); + expect(output).not.toContain(cleanDoctorMessage); }); it("reports missing configured ACPX runtime plugin in doctor output", async () => { @@ -213,7 +217,7 @@ describe("plugins cli list", () => { expect(output).toContain('Configured runtime "acpx" requires the ACPX Runtime plugin'); expect(output).toContain("openclaw doctor --fix"); expect(output).toContain("openclaw plugins install @openclaw/acpx"); - expect(output).not.toContain("No plugin issues detected."); + expect(output).not.toContain(cleanDoctorMessage); }); it("reports blocked configured ACPX runtime with ACP-specific guidance", async () => { @@ -241,7 +245,7 @@ describe("plugins cli list", () => { expect(output).toContain("disable ACP/acpx in acp config"); expect(output).not.toContain('runtime policy to "openclaw"'); expect(output).not.toContain("openclaw plugins install @openclaw/acpx"); - expect(output).not.toContain("No plugin issues detected."); + expect(output).not.toContain(cleanDoctorMessage); }); it("reports disabled configured ACPX runtime with ACP-specific guidance", async () => { @@ -264,7 +268,7 @@ describe("plugins cli list", () => { expect(output).toContain("disable ACP/acpx in acp config"); expect(output).not.toContain('runtime policy to "openclaw"'); expect(output).not.toContain("openclaw plugins install @openclaw/acpx"); - expect(output).not.toContain("No plugin issues detected."); + expect(output).not.toContain(cleanDoctorMessage); }); it("does not report implicit OpenAI Codex preference as configured runtime", async () => { @@ -285,7 +289,7 @@ describe("plugins cli list", () => { const output = runtimeLogs.join("\n"); expect(output).not.toContain('Configured runtime "codex"'); - expect(output).toContain("No plugin issues detected."); + expect(output).toContain(cleanDoctorMessage); }); it("does not report configured Codex runtime when the plugin is enabled", async () => { @@ -308,7 +312,7 @@ describe("plugins cli list", () => { await runPluginsCommand(["plugins", "doctor"]); - expect(runtimeLogs).toContain("No plugin issues detected."); + expect(runtimeLogs).toContain(cleanDoctorMessage); }); it("reports configured Codex runtime when the plugin record is disabled", async () => { @@ -336,7 +340,7 @@ describe("plugins cli list", () => { expect(output).toContain('but "codex" is disabled'); expect(output).toContain('Enable the "codex" plugin'); expect(output).not.toContain("openclaw plugins install @openclaw/codex"); - expect(output).not.toContain("No plugin issues detected."); + expect(output).not.toContain(cleanDoctorMessage); }); it("reports blocked configured Codex runtime without install advice", async () => { @@ -368,7 +372,7 @@ describe("plugins cli list", () => { expect(output).toContain('Remove "codex" from plugins.deny'); expect(output).not.toContain('Run "openclaw doctor --fix" to install'); expect(output).not.toContain("openclaw plugins install @openclaw/codex"); - expect(output).not.toContain("No plugin issues detected."); + expect(output).not.toContain(cleanDoctorMessage); }); it("reports disabled configured Codex runtime entry without install advice", async () => { @@ -402,7 +406,7 @@ describe("plugins cli list", () => { expect(output).toContain("Set plugins.entries.codex.enabled=true"); expect(output).not.toContain('Run "openclaw doctor --fix" to install'); expect(output).not.toContain("openclaw plugins install @openclaw/codex"); - expect(output).not.toContain("No plugin issues detected."); + expect(output).not.toContain(cleanDoctorMessage); }); it("reports config-selected plugin source shadowing in doctor output", async () => { @@ -462,7 +466,7 @@ describe("plugins cli list", () => { await runPluginsCommand(["plugins", "doctor"]); - expect(runtimeLogs).toContain("No plugin issues detected."); + expect(runtimeLogs).toContain(cleanDoctorMessage); }); it("reports persisted plugin registry state without refreshing", async () => { diff --git a/src/cli/plugins-cli.runtime.ts b/src/cli/plugins-cli.runtime.ts index 380efafea4d4..50b9db1dd2da 100644 --- a/src/cli/plugins-cli.runtime.ts +++ b/src/cli/plugins-cli.runtime.ts @@ -396,7 +396,10 @@ export async function runPluginsDoctorCommand(): Promise { const pluginConfigWarnings = [...stalePluginConfigWarnings, ...configuredRuntimePluginWarnings]; if (!hasInstallTreeIssues && pluginConfigWarnings.length === 0) { - defaultRuntime.log("No plugin issues detected."); + defaultRuntime.log( + "Plugin discovery, module loading, compatibility, and configuration checks passed. " + + 'Run "openclaw health" to check the running Gateway, including runtime quarantines and fallbacks.', + ); return; }