fix(regression): align doctor plugin status with runtime

This commit is contained in:
Tak Hoffman
2026-03-27 18:58:36 -05:00
parent d343b11bf1
commit 7da92cc618
2 changed files with 55 additions and 24 deletions

View File

@@ -9,7 +9,8 @@ import * as noteModule from "../terminal/note.js";
const resolveAgentWorkspaceDirMock = vi.fn();
const resolveDefaultAgentIdMock = vi.fn();
const buildWorkspaceSkillStatusMock = vi.fn();
const loadOpenClawPluginsMock = vi.fn();
const buildPluginStatusReportMock = vi.fn();
const buildPluginCompatibilityWarningsMock = vi.fn();
vi.mock("../agents/agent-scope.js", () => ({
resolveAgentWorkspaceDir: (...args: unknown[]) => resolveAgentWorkspaceDirMock(...args),
@@ -20,19 +21,26 @@ vi.mock("../agents/skills-status.js", () => ({
buildWorkspaceSkillStatus: (...args: unknown[]) => buildWorkspaceSkillStatusMock(...args),
}));
vi.mock("../plugins/loader.js", () => ({
loadOpenClawPlugins: (...args: unknown[]) => loadOpenClawPluginsMock(...args),
vi.mock("../plugins/status.js", () => ({
buildPluginStatusReport: (...args: unknown[]) => buildPluginStatusReportMock(...args),
buildPluginCompatibilityWarnings: (...args: unknown[]) =>
buildPluginCompatibilityWarningsMock(...args),
}));
async function runNoteWorkspaceStatusForTest(
loadResult: ReturnType<typeof createPluginLoadResult>,
compatibilityWarnings: string[] = [],
) {
resolveDefaultAgentIdMock.mockReturnValue("default");
resolveAgentWorkspaceDirMock.mockReturnValue("/workspace");
buildWorkspaceSkillStatusMock.mockReturnValue({
skills: [],
});
loadOpenClawPluginsMock.mockReturnValue(loadResult);
buildPluginStatusReportMock.mockReturnValue({
workspaceDir: "/workspace",
...loadResult,
});
buildPluginCompatibilityWarningsMock.mockReturnValue(compatibilityWarnings);
const noteSpy = vi.spyOn(noteModule, "note").mockImplementation(() => {});
const { noteWorkspaceStatus } = await import("./doctor-workspace-status.js");
@@ -57,16 +65,14 @@ describe("noteWorkspaceStatus", () => {
}),
);
try {
expect(buildPluginStatusReportMock).toHaveBeenCalledWith({
config: {},
workspaceDir: "/workspace",
});
const compatibilityCalls = noteSpy.mock.calls.filter(
([, title]) => title === "Plugin compatibility",
);
expect(compatibilityCalls).toHaveLength(1);
expect(String(compatibilityCalls[0]?.[0])).toContain(
"legacy-plugin still uses legacy before_agent_start",
);
expect(String(compatibilityCalls[0]?.[0])).toContain(
"legacy-plugin is hook-only. This remains a supported compatibility path",
);
expect(compatibilityCalls).toHaveLength(0);
} finally {
noteSpy.mockRestore();
}
@@ -116,4 +122,39 @@ describe("noteWorkspaceStatus", () => {
noteSpy.mockRestore();
}
});
it("passes the shared status report into compatibility warnings", async () => {
const loadResult = createPluginLoadResult({
plugins: [
createPluginRecord({
id: "legacy-plugin",
name: "Legacy Plugin",
hookCount: 1,
}),
],
typedHooks: [createTypedHook({ pluginId: "legacy-plugin", hookName: "before_agent_start" })],
});
const noteSpy = await runNoteWorkspaceStatusForTest(loadResult, [
"legacy-plugin still uses legacy before_agent_start",
]);
try {
expect(buildPluginCompatibilityWarningsMock).toHaveBeenCalledWith({
config: {},
workspaceDir: "/workspace",
report: {
workspaceDir: "/workspace",
...loadResult,
},
});
const compatibilityCalls = noteSpy.mock.calls.filter(
([, title]) => title === "Plugin compatibility",
);
expect(compatibilityCalls).toHaveLength(1);
expect(String(compatibilityCalls[0]?.[0])).toContain(
"legacy-plugin still uses legacy before_agent_start",
);
} finally {
noteSpy.mockRestore();
}
});
});

View File

@@ -1,8 +1,7 @@
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
import type { OpenClawConfig } from "../config/config.js";
import { loadOpenClawPlugins } from "../plugins/loader.js";
import { buildPluginCompatibilityWarnings } from "../plugins/status.js";
import { buildPluginCompatibilityWarnings, buildPluginStatusReport } from "../plugins/status.js";
import { note } from "../terminal/note.js";
import { detectLegacyWorkspaceDirs, formatLegacyWorkspaceWarning } from "./doctor-workspace.js";
@@ -26,15 +25,9 @@ export function noteWorkspaceStatus(cfg: OpenClawConfig) {
"Skills status",
);
const pluginRegistry = loadOpenClawPlugins({
const pluginRegistry = buildPluginStatusReport({
config: cfg,
workspaceDir,
logger: {
info: () => {},
warn: () => {},
error: () => {},
debug: () => {},
},
});
if (pluginRegistry.plugins.length > 0) {
const loaded = pluginRegistry.plugins.filter((p) => p.status === "loaded");
@@ -66,10 +59,7 @@ export function noteWorkspaceStatus(cfg: OpenClawConfig) {
const compatibilityWarnings = buildPluginCompatibilityWarnings({
config: cfg,
workspaceDir,
report: {
workspaceDir,
...pluginRegistry,
},
report: pluginRegistry,
});
if (compatibilityWarnings.length > 0) {
note(compatibilityWarnings.map((line) => `- ${line}`).join("\n"), "Plugin compatibility");