mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-25 23:47:20 +00:00
refactor(cli): dedupe skills command report loading
This commit is contained in:
124
src/cli/skills-cli.commands.test.ts
Normal file
124
src/cli/skills-cli.commands.test.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { Command } from "commander";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const loadConfigMock = vi.fn();
|
||||
const resolveAgentWorkspaceDirMock = vi.fn();
|
||||
const resolveDefaultAgentIdMock = vi.fn();
|
||||
const buildWorkspaceSkillStatusMock = vi.fn();
|
||||
const formatSkillsListMock = vi.fn();
|
||||
const formatSkillInfoMock = vi.fn();
|
||||
const formatSkillsCheckMock = vi.fn();
|
||||
|
||||
const runtime = {
|
||||
log: vi.fn(),
|
||||
error: vi.fn(),
|
||||
exit: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mock("../config/config.js", () => ({
|
||||
loadConfig: loadConfigMock,
|
||||
}));
|
||||
|
||||
vi.mock("../agents/agent-scope.js", () => ({
|
||||
resolveAgentWorkspaceDir: resolveAgentWorkspaceDirMock,
|
||||
resolveDefaultAgentId: resolveDefaultAgentIdMock,
|
||||
}));
|
||||
|
||||
vi.mock("../agents/skills-status.js", () => ({
|
||||
buildWorkspaceSkillStatus: buildWorkspaceSkillStatusMock,
|
||||
}));
|
||||
|
||||
vi.mock("./skills-cli.format.js", () => ({
|
||||
formatSkillsList: formatSkillsListMock,
|
||||
formatSkillInfo: formatSkillInfoMock,
|
||||
formatSkillsCheck: formatSkillsCheckMock,
|
||||
}));
|
||||
|
||||
vi.mock("../runtime.js", () => ({
|
||||
defaultRuntime: runtime,
|
||||
}));
|
||||
|
||||
let registerSkillsCli: typeof import("./skills-cli.js").registerSkillsCli;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ registerSkillsCli } = await import("./skills-cli.js"));
|
||||
});
|
||||
|
||||
describe("registerSkillsCli", () => {
|
||||
const report = {
|
||||
workspaceDir: "/tmp/workspace",
|
||||
managedSkillsDir: "/tmp/workspace/.skills",
|
||||
skills: [],
|
||||
};
|
||||
|
||||
async function runCli(args: string[]) {
|
||||
const program = new Command();
|
||||
registerSkillsCli(program);
|
||||
await program.parseAsync(args, { from: "user" });
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
loadConfigMock.mockReturnValue({ gateway: {} });
|
||||
resolveDefaultAgentIdMock.mockReturnValue("main");
|
||||
resolveAgentWorkspaceDirMock.mockReturnValue("/tmp/workspace");
|
||||
buildWorkspaceSkillStatusMock.mockReturnValue(report);
|
||||
formatSkillsListMock.mockReturnValue("skills-list-output");
|
||||
formatSkillInfoMock.mockReturnValue("skills-info-output");
|
||||
formatSkillsCheckMock.mockReturnValue("skills-check-output");
|
||||
});
|
||||
|
||||
it("runs list command with resolved report and formatter options", async () => {
|
||||
await runCli(["skills", "list", "--eligible", "--verbose", "--json"]);
|
||||
|
||||
expect(buildWorkspaceSkillStatusMock).toHaveBeenCalledWith("/tmp/workspace", {
|
||||
config: { gateway: {} },
|
||||
});
|
||||
expect(formatSkillsListMock).toHaveBeenCalledWith(
|
||||
report,
|
||||
expect.objectContaining({
|
||||
eligible: true,
|
||||
verbose: true,
|
||||
json: true,
|
||||
}),
|
||||
);
|
||||
expect(runtime.log).toHaveBeenCalledWith("skills-list-output");
|
||||
});
|
||||
|
||||
it("runs info command and forwards skill name", async () => {
|
||||
await runCli(["skills", "info", "peekaboo", "--json"]);
|
||||
|
||||
expect(formatSkillInfoMock).toHaveBeenCalledWith(
|
||||
report,
|
||||
"peekaboo",
|
||||
expect.objectContaining({ json: true }),
|
||||
);
|
||||
expect(runtime.log).toHaveBeenCalledWith("skills-info-output");
|
||||
});
|
||||
|
||||
it("runs check command and writes formatter output", async () => {
|
||||
await runCli(["skills", "check"]);
|
||||
|
||||
expect(formatSkillsCheckMock).toHaveBeenCalledWith(report, expect.any(Object));
|
||||
expect(runtime.log).toHaveBeenCalledWith("skills-check-output");
|
||||
});
|
||||
|
||||
it("uses list formatter for default skills action", async () => {
|
||||
await runCli(["skills"]);
|
||||
|
||||
expect(formatSkillsListMock).toHaveBeenCalledWith(report, {});
|
||||
expect(runtime.log).toHaveBeenCalledWith("skills-list-output");
|
||||
});
|
||||
|
||||
it("reports runtime errors when report loading fails", async () => {
|
||||
loadConfigMock.mockImplementationOnce(() => {
|
||||
throw new Error("config exploded");
|
||||
});
|
||||
|
||||
await runCli(["skills", "list"]);
|
||||
|
||||
expect(runtime.error).toHaveBeenCalledWith("Error: config exploded");
|
||||
expect(runtime.exit).toHaveBeenCalledWith(1);
|
||||
expect(buildWorkspaceSkillStatusMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -13,6 +13,27 @@ export type {
|
||||
} from "./skills-cli.format.js";
|
||||
export { formatSkillInfo, formatSkillsCheck, formatSkillsList } from "./skills-cli.format.js";
|
||||
|
||||
type SkillStatusReport = Awaited<
|
||||
ReturnType<(typeof import("../agents/skills-status.js"))["buildWorkspaceSkillStatus"]>
|
||||
>;
|
||||
|
||||
async function loadSkillsStatusReport(): Promise<SkillStatusReport> {
|
||||
const config = loadConfig();
|
||||
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
|
||||
const { buildWorkspaceSkillStatus } = await import("../agents/skills-status.js");
|
||||
return buildWorkspaceSkillStatus(workspaceDir, { config });
|
||||
}
|
||||
|
||||
async function runSkillsAction(render: (report: SkillStatusReport) => string): Promise<void> {
|
||||
try {
|
||||
const report = await loadSkillsStatusReport();
|
||||
defaultRuntime.log(render(report));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the skills CLI commands
|
||||
*/
|
||||
@@ -33,16 +54,7 @@ export function registerSkillsCli(program: Command) {
|
||||
.option("--eligible", "Show only eligible (ready to use) skills", false)
|
||||
.option("-v, --verbose", "Show more details including missing requirements", false)
|
||||
.action(async (opts) => {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
|
||||
const { buildWorkspaceSkillStatus } = await import("../agents/skills-status.js");
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, { config });
|
||||
defaultRuntime.log(formatSkillsList(report, opts));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
await runSkillsAction((report) => formatSkillsList(report, opts));
|
||||
});
|
||||
|
||||
skills
|
||||
@@ -51,16 +63,7 @@ export function registerSkillsCli(program: Command) {
|
||||
.argument("<name>", "Skill name")
|
||||
.option("--json", "Output as JSON", false)
|
||||
.action(async (name, opts) => {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
|
||||
const { buildWorkspaceSkillStatus } = await import("../agents/skills-status.js");
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, { config });
|
||||
defaultRuntime.log(formatSkillInfo(report, name, opts));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
await runSkillsAction((report) => formatSkillInfo(report, name, opts));
|
||||
});
|
||||
|
||||
skills
|
||||
@@ -68,29 +71,11 @@ export function registerSkillsCli(program: Command) {
|
||||
.description("Check which skills are ready vs missing requirements")
|
||||
.option("--json", "Output as JSON", false)
|
||||
.action(async (opts) => {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
|
||||
const { buildWorkspaceSkillStatus } = await import("../agents/skills-status.js");
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, { config });
|
||||
defaultRuntime.log(formatSkillsCheck(report, opts));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
await runSkillsAction((report) => formatSkillsCheck(report, opts));
|
||||
});
|
||||
|
||||
// Default action (no subcommand) - show list
|
||||
skills.action(async () => {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
|
||||
const { buildWorkspaceSkillStatus } = await import("../agents/skills-status.js");
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, { config });
|
||||
defaultRuntime.log(formatSkillsList(report, {}));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
await runSkillsAction((report) => formatSkillsList(report, {}));
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user