refactor: normalize cli command hints
This commit is contained in:
@@ -11,6 +11,7 @@ import { defaultRuntime } from "../runtime.js";
|
||||
import { movePathToTrash } from "../browser/trash.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
|
||||
function bundledExtensionRootDir() {
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
@@ -103,7 +104,7 @@ export function registerBrowserExtensionCommands(
|
||||
defaultRuntime.error(
|
||||
danger(
|
||||
[
|
||||
'Chrome extension is not installed. Run: "clawdbot browser extension install"',
|
||||
`Chrome extension is not installed. Run: "${formatCliCommand("clawdbot browser extension install")}"`,
|
||||
`Docs: ${formatDocsLink("/tools/chrome-extension", "docs.clawd.bot/tools/chrome-extension")}`,
|
||||
].join("\n"),
|
||||
),
|
||||
|
||||
@@ -4,6 +4,7 @@ import { danger } from "../globals.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { registerBrowserActionInputCommands } from "./browser-cli-actions-input.js";
|
||||
import { registerBrowserActionObserveCommands } from "./browser-cli-actions-observe.js";
|
||||
import { registerBrowserDebugCommands } from "./browser-cli-debug.js";
|
||||
@@ -32,7 +33,9 @@ export function registerBrowserCli(program: Command) {
|
||||
)
|
||||
.action(() => {
|
||||
browser.outputHelp();
|
||||
defaultRuntime.error(danger('Missing subcommand. Try: "clawdbot browser status"'));
|
||||
defaultRuntime.error(
|
||||
danger(`Missing subcommand. Try: "${formatCliCommand("clawdbot browser status")}"`),
|
||||
);
|
||||
defaultRuntime.exit(1);
|
||||
});
|
||||
|
||||
|
||||
16
src/cli/command-format.ts
Normal file
16
src/cli/command-format.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { normalizeProfileName } from "./profile-utils.js";
|
||||
|
||||
const CLI_PREFIX_RE = /^(?:pnpm|npm|bunx|npx)\s+clawdbot\b|^clawdbot\b/;
|
||||
const PROFILE_FLAG_RE = /\b--profile\b/;
|
||||
const DEV_FLAG_RE = /\b--dev\b/;
|
||||
|
||||
export function formatCliCommand(
|
||||
command: string,
|
||||
env: Record<string, string | undefined> = process.env as Record<string, string | undefined>,
|
||||
): string {
|
||||
const profile = normalizeProfileName(env.CLAWDBOT_PROFILE);
|
||||
if (!profile) return command;
|
||||
if (!CLI_PREFIX_RE.test(command)) return command;
|
||||
if (PROFILE_FLAG_RE.test(command) || DEV_FLAG_RE.test(command)) return command;
|
||||
return command.replace(CLI_PREFIX_RE, (match) => `${match} --profile ${profile}`);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
|
||||
import { danger, info } from "../globals.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
|
||||
type PathSegment = string;
|
||||
@@ -171,7 +172,7 @@ async function loadValidConfig() {
|
||||
for (const issue of snapshot.issues) {
|
||||
defaultRuntime.error(`- ${issue.path || "<root>"}: ${issue.message}`);
|
||||
}
|
||||
defaultRuntime.error("Run `clawdbot doctor` to repair, then retry.");
|
||||
defaultRuntime.error(`Run \`${formatCliCommand("clawdbot doctor")}\` to repair, then retry.`);
|
||||
defaultRuntime.exit(1);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { loadConfig, resolveGatewayPort } from "../../config/config.js";
|
||||
import { resolveIsNixMode } from "../../config/paths.js";
|
||||
import { resolveGatewayService } from "../../daemon/service.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
import { buildDaemonServiceSnapshot, createNullWriter, emitDaemonActionJson } from "./response.js";
|
||||
import { parsePort } from "./shared.js";
|
||||
import type { DaemonInstallOptions } from "./types.js";
|
||||
@@ -82,7 +83,9 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
|
||||
});
|
||||
if (!json) {
|
||||
defaultRuntime.log(`Gateway service already ${service.loadedText}.`);
|
||||
defaultRuntime.log("Reinstall with: clawdbot daemon install --force");
|
||||
defaultRuntime.log(
|
||||
`Reinstall with: ${formatCliCommand("clawdbot daemon install --force")}`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "../../daemon/constants.js";
|
||||
import { resolveGatewayLogPaths } from "../../daemon/launchd.js";
|
||||
import { getResolvedLoggerSettings } from "../../logging.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
|
||||
export function parsePort(raw: unknown): number | null {
|
||||
if (raw === undefined || raw === null) return null;
|
||||
@@ -122,7 +123,7 @@ export function renderRuntimeHints(
|
||||
}
|
||||
})();
|
||||
if (runtime.missingUnit) {
|
||||
hints.push("Service not installed. Run: clawdbot daemon install");
|
||||
hints.push(`Service not installed. Run: ${formatCliCommand("clawdbot daemon install", env)}`);
|
||||
if (fileLog) hints.push(`File logs: ${fileLog}`);
|
||||
return hints;
|
||||
}
|
||||
@@ -144,7 +145,10 @@ export function renderRuntimeHints(
|
||||
}
|
||||
|
||||
export function renderGatewayServiceStartHints(env: NodeJS.ProcessEnv = process.env): string[] {
|
||||
const base = ["clawdbot daemon install", "clawdbot gateway"];
|
||||
const base = [
|
||||
formatCliCommand("clawdbot daemon install", env),
|
||||
formatCliCommand("clawdbot gateway", env),
|
||||
];
|
||||
const profile = env.CLAWDBOT_PROFILE;
|
||||
switch (process.platform) {
|
||||
case "darwin": {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { isWSLEnv } from "../../infra/wsl.js";
|
||||
import { getResolvedLoggerSettings } from "../../logging.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
import { formatRuntimeStatus, renderRuntimeHints, safeDaemonEnv } from "./shared.js";
|
||||
import {
|
||||
type DaemonStatus,
|
||||
@@ -70,7 +71,9 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
||||
defaultRuntime.error(`${warnText("Service config issue:")} ${issue.message}${detail}`);
|
||||
}
|
||||
defaultRuntime.error(
|
||||
warnText('Recommendation: run "clawdbot doctor" (or "clawdbot doctor --repair").'),
|
||||
warnText(
|
||||
`Recommendation: run "${formatCliCommand("clawdbot doctor")}" (or "${formatCliCommand("clawdbot doctor --repair")}").`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -103,7 +106,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
||||
);
|
||||
defaultRuntime.error(
|
||||
errorText(
|
||||
"Fix: rerun `clawdbot daemon install --force` from the same --profile / CLAWDBOT_STATE_DIR you expect.",
|
||||
`Fix: rerun \`${formatCliCommand("clawdbot daemon install --force")}\` from the same --profile / CLAWDBOT_STATE_DIR you expect.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -205,7 +208,9 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
||||
`LaunchAgent label cached but plist missing. Clear with: launchctl bootout gui/$UID/${labelValue}`,
|
||||
),
|
||||
);
|
||||
defaultRuntime.error(errorText("Then reinstall: clawdbot daemon install"));
|
||||
defaultRuntime.error(
|
||||
errorText(`Then reinstall: ${formatCliCommand("clawdbot daemon install")}`),
|
||||
);
|
||||
spacer();
|
||||
}
|
||||
|
||||
@@ -259,7 +264,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
||||
for (const svc of legacyServices) {
|
||||
defaultRuntime.error(`- ${errorText(svc.label)} (${svc.detail})`);
|
||||
}
|
||||
defaultRuntime.error(errorText("Cleanup: clawdbot doctor"));
|
||||
defaultRuntime.error(errorText(`Cleanup: ${formatCliCommand("clawdbot doctor")}`));
|
||||
spacer();
|
||||
}
|
||||
|
||||
@@ -288,6 +293,6 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
||||
spacer();
|
||||
}
|
||||
|
||||
defaultRuntime.log(`${label("Troubles:")} run clawdbot status`);
|
||||
defaultRuntime.log(`${label("Troubles:")} run ${formatCliCommand("clawdbot status")}`);
|
||||
defaultRuntime.log(`${label("Troubleshooting:")} https://docs.clawd.bot/troubleshooting`);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { formatPortDiagnostics, inspectPortUsage } from "../../infra/ports.js";
|
||||
import { setConsoleSubsystemFilter, setConsoleTimestampPrefix } from "../../logging/console.js";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
import { forceFreePortAndWait } from "../ports.js";
|
||||
import { ensureDevGatewayConfig } from "./dev.js";
|
||||
import { runGatewayLoop } from "./run-loop.js";
|
||||
@@ -161,7 +162,7 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
|
||||
if (!opts.allowUnconfigured && mode !== "local") {
|
||||
if (!configExists) {
|
||||
defaultRuntime.error(
|
||||
"Missing config. Run `clawdbot setup` or set gateway.mode=local (or pass --allow-unconfigured).",
|
||||
`Missing config. Run \`${formatCliCommand("clawdbot setup")}\` or set gateway.mode=local (or pass --allow-unconfigured).`,
|
||||
);
|
||||
} else {
|
||||
defaultRuntime.error(
|
||||
@@ -277,7 +278,7 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
|
||||
) {
|
||||
const errMessage = describeUnknownError(err);
|
||||
defaultRuntime.error(
|
||||
`Gateway failed to start: ${errMessage}\nIf the gateway is supervised, stop it with: clawdbot daemon stop`,
|
||||
`Gateway failed to start: ${errMessage}\nIf the gateway is supervised, stop it with: ${formatCliCommand("clawdbot daemon stop")}`,
|
||||
);
|
||||
try {
|
||||
const diagnostics = await inspectPortUsage(port);
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "../../daemon/constants.js";
|
||||
import { resolveGatewayService } from "../../daemon/service.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
|
||||
export function parsePort(raw: unknown): number | null {
|
||||
if (raw === undefined || raw === null) return null;
|
||||
@@ -67,21 +68,21 @@ export function renderGatewayServiceStopHints(env: NodeJS.ProcessEnv = process.e
|
||||
switch (process.platform) {
|
||||
case "darwin":
|
||||
return [
|
||||
"Tip: clawdbot daemon stop",
|
||||
`Tip: ${formatCliCommand("clawdbot daemon stop")}`,
|
||||
`Or: launchctl bootout gui/$UID/${resolveGatewayLaunchAgentLabel(profile)}`,
|
||||
];
|
||||
case "linux":
|
||||
return [
|
||||
"Tip: clawdbot daemon stop",
|
||||
`Tip: ${formatCliCommand("clawdbot daemon stop")}`,
|
||||
`Or: systemctl --user stop ${resolveGatewaySystemdServiceName(profile)}.service`,
|
||||
];
|
||||
case "win32":
|
||||
return [
|
||||
"Tip: clawdbot daemon stop",
|
||||
`Tip: ${formatCliCommand("clawdbot daemon stop")}`,
|
||||
`Or: schtasks /End /TN "${resolveGatewayWindowsTaskName(profile)}"`,
|
||||
];
|
||||
default:
|
||||
return ["Tip: clawdbot daemon stop"];
|
||||
return [`Tip: ${formatCliCommand("clawdbot daemon stop")}`];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import { buildPluginStatusReport } from "../plugins/status.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
|
||||
export type HooksListOptions = {
|
||||
@@ -150,7 +151,7 @@ export function formatHooksList(report: HookStatusReport, opts: HooksListOptions
|
||||
|
||||
if (hooks.length === 0) {
|
||||
const message = opts.eligible
|
||||
? "No eligible hooks found. Run `clawdbot hooks list` to see all hooks."
|
||||
? `No eligible hooks found. Run \`${formatCliCommand("clawdbot hooks list")}\` to see all hooks.`
|
||||
: "No hooks found.";
|
||||
return message;
|
||||
}
|
||||
@@ -194,7 +195,7 @@ export function formatHookInfo(
|
||||
if (opts.json) {
|
||||
return JSON.stringify({ error: "not found", hook: hookName }, null, 2);
|
||||
}
|
||||
return `Hook "${hookName}" not found. Run \`clawdbot hooks list\` to see available hooks.`;
|
||||
return `Hook "${hookName}" not found. Run \`${formatCliCommand("clawdbot hooks list")}\` to see available hooks.`;
|
||||
}
|
||||
|
||||
if (opts.json) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { parseLogLine } from "../logging/parse-log-line.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { colorize, isRich, theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { addGatewayClientOptions, callGatewayFromCli } from "./gateway-rpc.js";
|
||||
|
||||
type LogsTailPayload = {
|
||||
@@ -117,7 +118,7 @@ function emitGatewayError(
|
||||
) {
|
||||
const details = buildGatewayConnectionDetails({ url: opts.url });
|
||||
const message = "Gateway not reachable. Is it running and accessible?";
|
||||
const hint = "Hint: run `clawdbot doctor`.";
|
||||
const hint = `Hint: run \`${formatCliCommand("clawdbot doctor")}\`.`;
|
||||
const errorText = err instanceof Error ? err.message : String(err);
|
||||
|
||||
if (mode === "json") {
|
||||
|
||||
@@ -18,6 +18,7 @@ import { isWSL } from "../../infra/wsl.js";
|
||||
import { loadNodeHostConfig } from "../../node-host/config.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
import {
|
||||
buildDaemonServiceSnapshot,
|
||||
createNullWriter,
|
||||
@@ -46,7 +47,10 @@ type NodeDaemonStatusOptions = {
|
||||
};
|
||||
|
||||
function renderNodeServiceStartHints(): string[] {
|
||||
const base = ["clawdbot node service install", "clawdbot node start"];
|
||||
const base = [
|
||||
formatCliCommand("clawdbot node service install"),
|
||||
formatCliCommand("clawdbot node start"),
|
||||
];
|
||||
switch (process.platform) {
|
||||
case "darwin":
|
||||
return [
|
||||
@@ -168,7 +172,9 @@ export async function runNodeDaemonInstall(opts: NodeDaemonInstallOptions) {
|
||||
});
|
||||
if (!json) {
|
||||
defaultRuntime.log(`Node service already ${service.loadedText}.`);
|
||||
defaultRuntime.log("Reinstall with: clawdbot node service install --force");
|
||||
defaultRuntime.log(
|
||||
`Reinstall with: ${formatCliCommand("clawdbot node service install --force")}`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from "../pairing/pairing-store.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
|
||||
/** Parse channel, allowing extension channels not in core registry. */
|
||||
function parseChannel(raw: unknown, channels: PairingChannel[]): PairingChannel {
|
||||
@@ -95,12 +96,12 @@ export function registerPairingCli(program: Command) {
|
||||
const resolvedCode = opts.channel ? codeOrChannel : code;
|
||||
if (!opts.channel && !code) {
|
||||
throw new Error(
|
||||
`Usage: clawdbot pairing approve <channel> <code> (or: clawdbot pairing approve --channel <channel> <code>)`,
|
||||
`Usage: ${formatCliCommand("clawdbot pairing approve <channel> <code>")} (or: ${formatCliCommand("clawdbot pairing approve --channel <channel> <code>")})`,
|
||||
);
|
||||
}
|
||||
if (opts.channel && code != null) {
|
||||
throw new Error(
|
||||
`Too many arguments. Use: clawdbot pairing approve --channel <channel> <code>`,
|
||||
`Too many arguments. Use: ${formatCliCommand("clawdbot pairing approve --channel <channel> <code>")}`,
|
||||
);
|
||||
}
|
||||
const channel = parseChannel(channelRaw, channels);
|
||||
|
||||
15
src/cli/profile-utils.ts
Normal file
15
src/cli/profile-utils.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
const PROFILE_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
||||
|
||||
export function isValidProfileName(value: string): boolean {
|
||||
if (!value) return false;
|
||||
// Keep it path-safe + shell-friendly.
|
||||
return PROFILE_NAME_RE.test(value);
|
||||
}
|
||||
|
||||
export function normalizeProfileName(raw?: string | null): string | null {
|
||||
const profile = raw?.trim();
|
||||
if (!profile) return null;
|
||||
if (profile.toLowerCase() === "default") return null;
|
||||
if (!isValidProfileName(profile)) return null;
|
||||
return profile;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { applyCliProfileEnv, parseCliProfileArgs } from "./profile.js";
|
||||
|
||||
describe("parseCliProfileArgs", () => {
|
||||
@@ -76,3 +77,63 @@ describe("applyCliProfileEnv", () => {
|
||||
expect(env.CLAWDBOT_CONFIG_PATH).toBe(path.join("/custom", "clawdbot.json"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatCliCommand", () => {
|
||||
it("returns command unchanged when no profile is set", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", {})).toBe("clawdbot doctor --fix");
|
||||
});
|
||||
|
||||
it("returns command unchanged when profile is default", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "default" })).toBe(
|
||||
"clawdbot doctor --fix",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns command unchanged when profile is Default (case-insensitive)", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "Default" })).toBe(
|
||||
"clawdbot doctor --fix",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns command unchanged when profile is invalid", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "bad profile" })).toBe(
|
||||
"clawdbot doctor --fix",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns command unchanged when --profile is already present", () => {
|
||||
expect(
|
||||
formatCliCommand("clawdbot --profile work doctor --fix", { CLAWDBOT_PROFILE: "work" }),
|
||||
).toBe("clawdbot --profile work doctor --fix");
|
||||
});
|
||||
|
||||
it("returns command unchanged when --dev is already present", () => {
|
||||
expect(formatCliCommand("clawdbot --dev doctor", { CLAWDBOT_PROFILE: "dev" })).toBe(
|
||||
"clawdbot --dev doctor",
|
||||
);
|
||||
});
|
||||
|
||||
it("inserts --profile flag when profile is set", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "work" })).toBe(
|
||||
"clawdbot --profile work doctor --fix",
|
||||
);
|
||||
});
|
||||
|
||||
it("trims whitespace from profile", () => {
|
||||
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: " jbclawd " })).toBe(
|
||||
"clawdbot --profile jbclawd doctor --fix",
|
||||
);
|
||||
});
|
||||
|
||||
it("handles command with no args after clawdbot", () => {
|
||||
expect(formatCliCommand("clawdbot", { CLAWDBOT_PROFILE: "test" })).toBe(
|
||||
"clawdbot --profile test",
|
||||
);
|
||||
});
|
||||
|
||||
it("handles pnpm wrapper", () => {
|
||||
expect(formatCliCommand("pnpm clawdbot doctor", { CLAWDBOT_PROFILE: "work" })).toBe(
|
||||
"pnpm clawdbot --profile work doctor",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import { isValidProfileName } from "./profile-utils.js";
|
||||
|
||||
export type CliProfileParseResult =
|
||||
| { ok: true; profile: string | null; argv: string[] }
|
||||
| { ok: false; error: string };
|
||||
@@ -21,12 +23,6 @@ function takeValue(
|
||||
return { value: trimmed || null, consumedNext: Boolean(next) };
|
||||
}
|
||||
|
||||
function isValidProfileName(value: string): boolean {
|
||||
if (!value) return false;
|
||||
// Keep it path-safe + shell-friendly.
|
||||
return /^[a-z0-9][a-z0-9_-]{0,63}$/i.test(value);
|
||||
}
|
||||
|
||||
export function parseCliProfileArgs(argv: string[]): CliProfileParseResult {
|
||||
if (argv.length < 2) return { ok: true, profile: null, argv };
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { loadAndMaybeMigrateDoctorConfig } from "../../commands/doctor-config-fl
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||
import { loadClawdbotPlugins } from "../../plugins/loader.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
|
||||
const ALLOWED_INVALID_COMMANDS = new Set(["doctor", "logs", "health", "help", "status", "service"]);
|
||||
|
||||
@@ -72,7 +73,9 @@ export async function ensureConfigReady(params: {
|
||||
params.runtime.error(pluginIssues.map((issue) => ` ${error(issue)}`).join("\n"));
|
||||
}
|
||||
params.runtime.error("");
|
||||
params.runtime.error(`${muted("Run:")} ${commandText("clawdbot doctor --fix")}`);
|
||||
params.runtime.error(
|
||||
`${muted("Run:")} ${commandText(formatCliCommand("clawdbot doctor --fix"))}`,
|
||||
);
|
||||
if (!allowInvalid) {
|
||||
params.runtime.exit(1);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { runSecurityAudit } from "../security/audit.js";
|
||||
import { fixSecurityFootguns } from "../security/fix.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { isRich, theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
|
||||
type SecurityAuditOptions = {
|
||||
json?: boolean;
|
||||
@@ -67,10 +68,10 @@ export function registerSecurityCli(program: Command) {
|
||||
const lines: string[] = [];
|
||||
lines.push(heading("Clawdbot security audit"));
|
||||
lines.push(muted(`Summary: ${formatSummary(report.summary)}`));
|
||||
lines.push(muted(`Run deeper: clawdbot security audit --deep`));
|
||||
lines.push(muted(`Run deeper: ${formatCliCommand("clawdbot security audit --deep")}`));
|
||||
|
||||
if (opts.fix) {
|
||||
lines.push(muted(`Fix: clawdbot security audit --fix`));
|
||||
lines.push(muted(`Fix: ${formatCliCommand("clawdbot security audit --fix")}`));
|
||||
if (!fixResult) {
|
||||
lines.push(muted("Fixes: failed to apply (unexpected error)"));
|
||||
} else if (
|
||||
|
||||
@@ -10,6 +10,7 @@ import { loadConfig } from "../config/config.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
|
||||
export type SkillsListOptions = {
|
||||
json?: boolean;
|
||||
@@ -101,7 +102,7 @@ export function formatSkillsList(report: SkillStatusReport, opts: SkillsListOpti
|
||||
|
||||
if (skills.length === 0) {
|
||||
const message = opts.eligible
|
||||
? "No eligible skills found. Run `clawdbot skills list` to see all skills."
|
||||
? `No eligible skills found. Run \`${formatCliCommand("clawdbot skills list")}\` to see all skills.`
|
||||
: "No skills found.";
|
||||
return appendClawdHubHint(message, opts.json);
|
||||
}
|
||||
@@ -148,7 +149,7 @@ export function formatSkillInfo(
|
||||
return JSON.stringify({ error: "not found", skill: skillName }, null, 2);
|
||||
}
|
||||
return appendClawdHubHint(
|
||||
`Skill "${skillName}" not found. Run \`clawdbot skills list\` to see available skills.`,
|
||||
`Skill "${skillName}" not found. Run \`${formatCliCommand("clawdbot skills list")}\` to see available skills.`,
|
||||
opts.json,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from "../infra/update-runner.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { stylePromptMessage } from "../terminal/prompt-style.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
|
||||
@@ -373,7 +374,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
||||
if (result.reason === "not-git-install") {
|
||||
defaultRuntime.log(
|
||||
theme.warn(
|
||||
"Skipped: this Clawdbot install isn't a git checkout, and the package manager couldn't be detected. Update via your package manager, then run `clawdbot doctor` and `clawdbot daemon restart`.",
|
||||
`Skipped: this Clawdbot install isn't a git checkout, and the package manager couldn't be detected. Update via your package manager, then run \`${formatCliCommand("clawdbot doctor")}\` and \`${formatCliCommand("clawdbot daemon restart")}\`.`,
|
||||
),
|
||||
);
|
||||
defaultRuntime.log(
|
||||
@@ -410,7 +411,9 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
||||
if (!opts.json) {
|
||||
defaultRuntime.log(theme.warn(`Daemon restart failed: ${String(err)}`));
|
||||
defaultRuntime.log(
|
||||
theme.muted("You may need to restart the daemon manually: clawdbot daemon restart"),
|
||||
theme.muted(
|
||||
`You may need to restart the daemon manually: ${formatCliCommand("clawdbot daemon restart")}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -419,12 +422,14 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
|
||||
if (result.mode === "npm" || result.mode === "pnpm") {
|
||||
defaultRuntime.log(
|
||||
theme.muted(
|
||||
"Tip: Run `clawdbot doctor`, then `clawdbot daemon restart` to apply updates to a running gateway.",
|
||||
`Tip: Run \`${formatCliCommand("clawdbot doctor")}\`, then \`${formatCliCommand("clawdbot daemon restart")}\` to apply updates to a running gateway.`,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
defaultRuntime.log(
|
||||
theme.muted("Tip: Run `clawdbot daemon restart` to apply updates to a running gateway."),
|
||||
theme.muted(
|
||||
`Tip: Run \`${formatCliCommand("clawdbot daemon restart")}\` to apply updates to a running gateway.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user