fix(cli): guide lookup misses

This commit is contained in:
Vincent Koc
2026-05-09 08:00:16 +08:00
parent f09bb6d75c
commit bb0332bfbf
6 changed files with 69 additions and 24 deletions

View File

@@ -5,12 +5,14 @@ import {
setConfiguredMcpServer,
unsetConfiguredMcpServer,
} from "../config/mcp-config.js";
import { formatErrorMessage } from "../infra/errors.js";
import { serveOpenClawChannelMcp } from "../mcp/channel-server.js";
import { defaultRuntime } from "../runtime.js";
import {
normalizeLowercaseStringOrEmpty,
normalizeStringifiedOptionalString,
} from "../shared/string-coerce.js";
import { formatCliCommand } from "./command-format.js";
import { resolveGatewayAuthOptions } from "./gateway-secret-options.js";
import { applyParentDefaultHelpAction } from "./program/parent-default-help.js";
@@ -52,7 +54,7 @@ export function registerMcpCli(program: Command) {
claudeChannelMode !== "on" &&
claudeChannelMode !== "off"
) {
throw new Error("Invalid --claude-channel-mode value. Use auto, on, or off.");
throw new Error('Invalid --claude-channel-mode value. Use "auto", "on", or "off".');
}
await serveOpenClawChannelMcp({
gatewayUrl: opts.url as string | undefined,
@@ -62,7 +64,9 @@ export function registerMcpCli(program: Command) {
verbose: Boolean(opts.verbose),
});
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.error(
`MCP server failed to start: ${formatErrorMessage(err)}. Run ${formatCliCommand("openclaw mcp list")} to inspect configured servers.`,
);
defaultRuntime.exit(1);
}
});
@@ -82,7 +86,9 @@ export function registerMcpCli(program: Command) {
}
const names = Object.keys(loaded.mcpServers).toSorted();
if (names.length === 0) {
defaultRuntime.log(`No MCP servers configured in ${loaded.path}.`);
defaultRuntime.log(
`No MCP servers configured in ${loaded.path}. Add one with ${formatCliCommand('openclaw mcp set <name> \'{"command":"uvx","args":["context7-mcp"]}\'')}.`,
);
return;
}
defaultRuntime.log(`MCP servers (${loaded.path}):`);
@@ -103,7 +109,9 @@ export function registerMcpCli(program: Command) {
}
const value = name ? loaded.mcpServers[name] : loaded.mcpServers;
if (name && !value) {
fail(`No MCP server named "${name}" in ${loaded.path}.`);
fail(
`No MCP server named "${name}" in ${loaded.path}. Run ${formatCliCommand("openclaw mcp list")} to see configured servers.`,
);
}
if (opts.json) {
printJson(value ?? {});
@@ -144,7 +152,9 @@ export function registerMcpCli(program: Command) {
fail(result.error);
}
if (!result.removed) {
fail(`No MCP server named "${name}" in ${result.path}.`);
fail(
`No MCP server named "${name}" in ${result.path}. Run ${formatCliCommand("openclaw mcp list")} to see configured servers.`,
);
}
defaultRuntime.log(`Removed MCP server "${name}" from ${result.path}.`);
});

View File

@@ -1,4 +1,5 @@
import { listAgentEntries, resolveDefaultAgentId } from "../agents/agent-scope.js";
import { formatCliCommand } from "../cli/command-format.js";
import { isRouteBinding, listRouteBindings } from "../config/bindings.js";
import { replaceConfigFile } from "../config/config.js";
import { logConfigUpdated } from "../config/logging.js";
@@ -80,12 +81,16 @@ function resolveTargetAgentIdOrExit(params: {
fallbackToDefault: true,
});
if (!agentId) {
params.runtime.error("Unable to resolve agent id.");
params.runtime.error(
`Unable to resolve agent id. Run ${formatCliCommand("openclaw agents list")} to choose one.`,
);
params.runtime.exit(1);
return null;
}
if (!hasAgent(params.cfg, agentId)) {
params.runtime.error(`Agent "${agentId}" not found.`);
params.runtime.error(
`Agent "${agentId}" not found. Run ${formatCliCommand("openclaw agents list")} to see configured agents.`,
);
params.runtime.exit(1);
return null;
}
@@ -178,12 +183,16 @@ export async function agentsBindingsCommand(
const filterAgentId = resolveAgentId(cfg, opts.agent?.trim());
if (opts.agent && !filterAgentId) {
runtime.error("Agent id is required.");
runtime.error(
`Agent id is required. Run ${formatCliCommand("openclaw agents list")} to choose one.`,
);
runtime.exit(1);
return;
}
if (filterAgentId && !hasAgent(cfg, filterAgentId)) {
runtime.error(`Agent "${filterAgentId}" not found.`);
runtime.error(
`Agent "${filterAgentId}" not found. Run ${formatCliCommand("openclaw agents list")} to see configured agents.`,
);
runtime.exit(1);
return;
}

View File

@@ -1,5 +1,6 @@
import { findOverlappingWorkspaceAgentIds } from "../agents/agent-delete-safety.js";
import { resolveAgentDir, resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
import { formatCliCommand } from "../cli/command-format.js";
import { replaceConfigFile } from "../config/config.js";
import { logConfigUpdated } from "../config/logging.js";
import {
@@ -64,7 +65,9 @@ export async function agentsDeleteCommand(
const input = opts.id?.trim();
if (!input) {
runtime.error("Agent id is required.");
runtime.error(
`Agent id is required. Run ${formatCliCommand("openclaw agents list")} to choose one.`,
);
runtime.exit(1);
return;
}
@@ -80,7 +83,9 @@ export async function agentsDeleteCommand(
}
if (findAgentEntryIndex(listAgentEntries(cfg), agentId) < 0) {
runtime.error(`Agent "${agentId}" not found.`);
runtime.error(
`Agent "${agentId}" not found. Run ${formatCliCommand("openclaw agents list")} to see configured agents.`,
);
runtime.exit(1);
return;
}

View File

@@ -1,4 +1,5 @@
import path from "node:path";
import { formatCliCommand } from "../cli/command-format.js";
import {
resolveDefaultSessionStorePath,
resolveSessionFilePath,
@@ -86,7 +87,9 @@ export async function exportTrajectoryCommand(
}
const sessionKey = resolvedOpts.sessionKey?.trim();
if (!sessionKey) {
runtime.error("--session-key is required");
runtime.error(
`--session-key is required. Run ${formatCliCommand("openclaw sessions list")} to choose a session.`,
);
runtime.exit(1);
return;
}
@@ -97,7 +100,9 @@ export async function exportTrajectoryCommand(
const store = loadSessionStore(storePath, { skipCache: true });
const entry = store[sessionKey] as SessionEntry | undefined;
if (!entry?.sessionId) {
runtime.error(`Session not found: ${sessionKey}`);
runtime.error(
`Session not found: ${sessionKey}. Run ${formatCliCommand("openclaw sessions list")} to see available sessions.`,
);
runtime.exit(1);
return;
}
@@ -115,7 +120,9 @@ export async function exportTrajectoryCommand(
return;
}
if (!(await pathExists(sessionFile))) {
runtime.error("Session file not found.");
runtime.error(
`Session file not found for ${sessionKey}. Run ${formatCliCommand("openclaw doctor")} to inspect session storage.`,
);
runtime.exit(1);
return;
}

View File

@@ -1,3 +1,4 @@
import { formatCliCommand } from "../cli/command-format.js";
import { getRuntimeConfig } from "../config/config.js";
import { info } from "../globals.js";
import type { RuntimeEnv } from "../runtime.js";
@@ -19,6 +20,10 @@ const MODE_PAD = 14;
const REV_PAD = 6;
const CTRL_PAD = 20;
function formatFlowLookupMiss(lookup: string): string {
return `TaskFlow not found: ${lookup}. Run ${formatCliCommand("openclaw tasks flow list")} to see recent flow ids.`;
}
function truncate(value: string, maxChars: number) {
if (value.length <= maxChars) {
return value;
@@ -173,7 +178,9 @@ export async function flowsListCommand(
runtime.log(info(`Status filter: ${statusFilter}`));
}
if (flows.length === 0) {
runtime.log("No TaskFlows found.");
runtime.log(
`No TaskFlows found. Run ${formatCliCommand("openclaw tasks list")} to inspect standalone background tasks.`,
);
return;
}
const rich = isRich();
@@ -188,7 +195,7 @@ export async function flowsShowCommand(
) {
const flow = resolveTaskFlowForLookupToken(opts.lookup);
if (!flow) {
runtime.error(`TaskFlow not found: ${opts.lookup}`);
runtime.error(formatFlowLookupMiss(opts.lookup));
runtime.exit(1);
return;
}
@@ -245,7 +252,7 @@ export async function flowsShowCommand(
export async function flowsCancelCommand(opts: { lookup: string }, runtime: RuntimeEnv) {
const flow = resolveTaskFlowForLookupToken(opts.lookup);
if (!flow) {
runtime.error(`Flow not found: ${opts.lookup}`);
runtime.error(formatFlowLookupMiss(opts.lookup));
runtime.exit(1);
return;
}
@@ -254,7 +261,7 @@ export async function flowsCancelCommand(opts: { lookup: string }, runtime: Runt
flowId: flow.flowId,
});
if (!result.found) {
runtime.error(result.reason ?? `Flow not found: ${opts.lookup}`);
runtime.error(result.reason ?? formatFlowLookupMiss(opts.lookup));
runtime.exit(1);
return;
}

View File

@@ -1,3 +1,4 @@
import { formatCliCommand } from "../cli/command-format.js";
import { getRuntimeConfig } from "../config/config.js";
import { resolveCronStorePath } from "../cron/store.js";
import type { RuntimeEnv } from "../runtime.js";
@@ -46,6 +47,10 @@ const RUN_PAD = 10;
const info = theme.info;
function formatTaskLookupMiss(lookup: string): string {
return `Task not found: ${lookup}. Run ${formatCliCommand("openclaw tasks list")} to see recent task ids.`;
}
async function loadTaskCancelConfig() {
return getRuntimeConfig();
}
@@ -313,7 +318,9 @@ export async function tasksListCommand(
runtime.log(info(`Status filter: ${statusFilter}`));
}
if (tasks.length === 0) {
runtime.log("No background tasks found.");
runtime.log(
`No background tasks found. Run ${formatCliCommand("openclaw tasks audit")} to check for stale task state.`,
);
return;
}
const rich = isRich();
@@ -328,7 +335,7 @@ export async function tasksShowCommand(
) {
const task = reconcileTaskLookupToken(opts.lookup);
if (!task) {
runtime.error(`Task not found: ${opts.lookup}`);
runtime.error(formatTaskLookupMiss(opts.lookup));
runtime.exit(1);
return;
}
@@ -374,7 +381,7 @@ export async function tasksNotifyCommand(
) {
const task = reconcileTaskLookupToken(opts.lookup);
if (!task) {
runtime.error(`Task not found: ${opts.lookup}`);
runtime.error(formatTaskLookupMiss(opts.lookup));
runtime.exit(1);
return;
}
@@ -383,7 +390,7 @@ export async function tasksNotifyCommand(
notifyPolicy: opts.notify,
});
if (!updated) {
runtime.error(`Task not found: ${opts.lookup}`);
runtime.error(formatTaskLookupMiss(opts.lookup));
runtime.exit(1);
return;
}
@@ -393,7 +400,7 @@ export async function tasksNotifyCommand(
export async function tasksCancelCommand(opts: { lookup: string }, runtime: RuntimeEnv) {
const task = reconcileTaskLookupToken(opts.lookup);
if (!task) {
runtime.error(`Task not found: ${opts.lookup}`);
runtime.error(formatTaskLookupMiss(opts.lookup));
runtime.exit(1);
return;
}
@@ -402,7 +409,7 @@ export async function tasksCancelCommand(opts: { lookup: string }, runtime: Runt
taskId: task.taskId,
});
if (!result.found) {
runtime.error(result.reason ?? `Task not found: ${opts.lookup}`);
runtime.error(result.reason ?? formatTaskLookupMiss(opts.lookup));
runtime.exit(1);
return;
}