refactor: rename clawdbot to moltbot with legacy compat

This commit is contained in:
Peter Steinberger
2026-01-27 12:19:58 +00:00
parent 83460df96f
commit 6d16a658e5
1839 changed files with 11250 additions and 11199 deletions

View File

@@ -15,119 +15,117 @@ import {
describe("argv helpers", () => {
it("detects help/version flags", () => {
expect(hasHelpOrVersion(["node", "clawdbot", "--help"])).toBe(true);
expect(hasHelpOrVersion(["node", "clawdbot", "-V"])).toBe(true);
expect(hasHelpOrVersion(["node", "clawdbot", "status"])).toBe(false);
expect(hasHelpOrVersion(["node", "moltbot", "--help"])).toBe(true);
expect(hasHelpOrVersion(["node", "moltbot", "-V"])).toBe(true);
expect(hasHelpOrVersion(["node", "moltbot", "status"])).toBe(false);
});
it("extracts command path ignoring flags and terminator", () => {
expect(getCommandPath(["node", "clawdbot", "status", "--json"], 2)).toEqual(["status"]);
expect(getCommandPath(["node", "clawdbot", "agents", "list"], 2)).toEqual(["agents", "list"]);
expect(getCommandPath(["node", "clawdbot", "status", "--", "ignored"], 2)).toEqual(["status"]);
expect(getCommandPath(["node", "moltbot", "status", "--json"], 2)).toEqual(["status"]);
expect(getCommandPath(["node", "moltbot", "agents", "list"], 2)).toEqual(["agents", "list"]);
expect(getCommandPath(["node", "moltbot", "status", "--", "ignored"], 2)).toEqual(["status"]);
});
it("returns primary command", () => {
expect(getPrimaryCommand(["node", "clawdbot", "agents", "list"])).toBe("agents");
expect(getPrimaryCommand(["node", "clawdbot"])).toBeNull();
expect(getPrimaryCommand(["node", "moltbot", "agents", "list"])).toBe("agents");
expect(getPrimaryCommand(["node", "moltbot"])).toBeNull();
});
it("parses boolean flags and ignores terminator", () => {
expect(hasFlag(["node", "clawdbot", "status", "--json"], "--json")).toBe(true);
expect(hasFlag(["node", "clawdbot", "--", "--json"], "--json")).toBe(false);
expect(hasFlag(["node", "moltbot", "status", "--json"], "--json")).toBe(true);
expect(hasFlag(["node", "moltbot", "--", "--json"], "--json")).toBe(false);
});
it("extracts flag values with equals and missing values", () => {
expect(getFlagValue(["node", "clawdbot", "status", "--timeout", "5000"], "--timeout")).toBe(
expect(getFlagValue(["node", "moltbot", "status", "--timeout", "5000"], "--timeout")).toBe(
"5000",
);
expect(getFlagValue(["node", "clawdbot", "status", "--timeout=2500"], "--timeout")).toBe(
"2500",
);
expect(getFlagValue(["node", "clawdbot", "status", "--timeout"], "--timeout")).toBeNull();
expect(getFlagValue(["node", "clawdbot", "status", "--timeout", "--json"], "--timeout")).toBe(
expect(getFlagValue(["node", "moltbot", "status", "--timeout=2500"], "--timeout")).toBe("2500");
expect(getFlagValue(["node", "moltbot", "status", "--timeout"], "--timeout")).toBeNull();
expect(getFlagValue(["node", "moltbot", "status", "--timeout", "--json"], "--timeout")).toBe(
null,
);
expect(getFlagValue(["node", "clawdbot", "--", "--timeout=99"], "--timeout")).toBeUndefined();
expect(getFlagValue(["node", "moltbot", "--", "--timeout=99"], "--timeout")).toBeUndefined();
});
it("parses verbose flags", () => {
expect(getVerboseFlag(["node", "clawdbot", "status", "--verbose"])).toBe(true);
expect(getVerboseFlag(["node", "clawdbot", "status", "--debug"])).toBe(false);
expect(getVerboseFlag(["node", "clawdbot", "status", "--debug"], { includeDebug: true })).toBe(
expect(getVerboseFlag(["node", "moltbot", "status", "--verbose"])).toBe(true);
expect(getVerboseFlag(["node", "moltbot", "status", "--debug"])).toBe(false);
expect(getVerboseFlag(["node", "moltbot", "status", "--debug"], { includeDebug: true })).toBe(
true,
);
});
it("parses positive integer flag values", () => {
expect(getPositiveIntFlagValue(["node", "clawdbot", "status"], "--timeout")).toBeUndefined();
expect(getPositiveIntFlagValue(["node", "moltbot", "status"], "--timeout")).toBeUndefined();
expect(
getPositiveIntFlagValue(["node", "clawdbot", "status", "--timeout"], "--timeout"),
getPositiveIntFlagValue(["node", "moltbot", "status", "--timeout"], "--timeout"),
).toBeNull();
expect(
getPositiveIntFlagValue(["node", "clawdbot", "status", "--timeout", "5000"], "--timeout"),
getPositiveIntFlagValue(["node", "moltbot", "status", "--timeout", "5000"], "--timeout"),
).toBe(5000);
expect(
getPositiveIntFlagValue(["node", "clawdbot", "status", "--timeout", "nope"], "--timeout"),
getPositiveIntFlagValue(["node", "moltbot", "status", "--timeout", "nope"], "--timeout"),
).toBeUndefined();
});
it("builds parse argv from raw args", () => {
const nodeArgv = buildParseArgv({
programName: "clawdbot",
rawArgs: ["node", "clawdbot", "status"],
programName: "moltbot",
rawArgs: ["node", "moltbot", "status"],
});
expect(nodeArgv).toEqual(["node", "clawdbot", "status"]);
expect(nodeArgv).toEqual(["node", "moltbot", "status"]);
const versionedNodeArgv = buildParseArgv({
programName: "clawdbot",
rawArgs: ["node-22", "clawdbot", "status"],
programName: "moltbot",
rawArgs: ["node-22", "moltbot", "status"],
});
expect(versionedNodeArgv).toEqual(["node-22", "clawdbot", "status"]);
expect(versionedNodeArgv).toEqual(["node-22", "moltbot", "status"]);
const versionedNodeWindowsArgv = buildParseArgv({
programName: "clawdbot",
rawArgs: ["node-22.2.0.exe", "clawdbot", "status"],
programName: "moltbot",
rawArgs: ["node-22.2.0.exe", "moltbot", "status"],
});
expect(versionedNodeWindowsArgv).toEqual(["node-22.2.0.exe", "clawdbot", "status"]);
expect(versionedNodeWindowsArgv).toEqual(["node-22.2.0.exe", "moltbot", "status"]);
const versionedNodePatchlessArgv = buildParseArgv({
programName: "clawdbot",
rawArgs: ["node-22.2", "clawdbot", "status"],
programName: "moltbot",
rawArgs: ["node-22.2", "moltbot", "status"],
});
expect(versionedNodePatchlessArgv).toEqual(["node-22.2", "clawdbot", "status"]);
expect(versionedNodePatchlessArgv).toEqual(["node-22.2", "moltbot", "status"]);
const versionedNodeWindowsPatchlessArgv = buildParseArgv({
programName: "clawdbot",
rawArgs: ["node-22.2.exe", "clawdbot", "status"],
programName: "moltbot",
rawArgs: ["node-22.2.exe", "moltbot", "status"],
});
expect(versionedNodeWindowsPatchlessArgv).toEqual(["node-22.2.exe", "clawdbot", "status"]);
expect(versionedNodeWindowsPatchlessArgv).toEqual(["node-22.2.exe", "moltbot", "status"]);
const versionedNodeWithPathArgv = buildParseArgv({
programName: "clawdbot",
rawArgs: ["/usr/bin/node-22.2.0", "clawdbot", "status"],
programName: "moltbot",
rawArgs: ["/usr/bin/node-22.2.0", "moltbot", "status"],
});
expect(versionedNodeWithPathArgv).toEqual(["/usr/bin/node-22.2.0", "clawdbot", "status"]);
expect(versionedNodeWithPathArgv).toEqual(["/usr/bin/node-22.2.0", "moltbot", "status"]);
const nodejsArgv = buildParseArgv({
programName: "clawdbot",
rawArgs: ["nodejs", "clawdbot", "status"],
programName: "moltbot",
rawArgs: ["nodejs", "moltbot", "status"],
});
expect(nodejsArgv).toEqual(["nodejs", "clawdbot", "status"]);
expect(nodejsArgv).toEqual(["nodejs", "moltbot", "status"]);
const nonVersionedNodeArgv = buildParseArgv({
programName: "clawdbot",
rawArgs: ["node-dev", "clawdbot", "status"],
programName: "moltbot",
rawArgs: ["node-dev", "moltbot", "status"],
});
expect(nonVersionedNodeArgv).toEqual(["node", "clawdbot", "node-dev", "clawdbot", "status"]);
expect(nonVersionedNodeArgv).toEqual(["node", "moltbot", "node-dev", "moltbot", "status"]);
const directArgv = buildParseArgv({
programName: "clawdbot",
rawArgs: ["clawdbot", "status"],
programName: "moltbot",
rawArgs: ["moltbot", "status"],
});
expect(directArgv).toEqual(["node", "clawdbot", "status"]);
expect(directArgv).toEqual(["node", "moltbot", "status"]);
const bunArgv = buildParseArgv({
programName: "clawdbot",
programName: "moltbot",
rawArgs: ["bun", "src/entry.ts", "status"],
});
expect(bunArgv).toEqual(["bun", "src/entry.ts", "status"]);
@@ -135,20 +133,20 @@ describe("argv helpers", () => {
it("builds parse argv from fallback args", () => {
const fallbackArgv = buildParseArgv({
programName: "clawdbot",
programName: "moltbot",
fallbackArgv: ["status"],
});
expect(fallbackArgv).toEqual(["node", "clawdbot", "status"]);
expect(fallbackArgv).toEqual(["node", "moltbot", "status"]);
});
it("decides when to migrate state", () => {
expect(shouldMigrateState(["node", "clawdbot", "status"])).toBe(false);
expect(shouldMigrateState(["node", "clawdbot", "health"])).toBe(false);
expect(shouldMigrateState(["node", "clawdbot", "sessions"])).toBe(false);
expect(shouldMigrateState(["node", "clawdbot", "memory", "status"])).toBe(false);
expect(shouldMigrateState(["node", "clawdbot", "agent", "--message", "hi"])).toBe(false);
expect(shouldMigrateState(["node", "clawdbot", "agents", "list"])).toBe(true);
expect(shouldMigrateState(["node", "clawdbot", "message", "send"])).toBe(true);
expect(shouldMigrateState(["node", "moltbot", "status"])).toBe(false);
expect(shouldMigrateState(["node", "moltbot", "health"])).toBe(false);
expect(shouldMigrateState(["node", "moltbot", "sessions"])).toBe(false);
expect(shouldMigrateState(["node", "moltbot", "memory", "status"])).toBe(false);
expect(shouldMigrateState(["node", "moltbot", "agent", "--message", "hi"])).toBe(false);
expect(shouldMigrateState(["node", "moltbot", "agents", "list"])).toBe(true);
expect(shouldMigrateState(["node", "moltbot", "message", "send"])).toBe(true);
});
it("reuses command path for migrate state decisions", () => {

View File

@@ -91,14 +91,14 @@ export function buildParseArgv(params: {
const normalizedArgv =
programName && baseArgv[0] === programName
? baseArgv.slice(1)
: baseArgv[0]?.endsWith("clawdbot")
: baseArgv[0]?.endsWith("moltbot")
? baseArgv.slice(1)
: baseArgv;
const executable = (normalizedArgv[0]?.split(/[/\\]/).pop() ?? "").toLowerCase();
const looksLikeNode =
normalizedArgv.length >= 2 && (isNodeExecutable(executable) || isBunExecutable(executable));
if (looksLikeNode) return normalizedArgv;
return ["node", programName || "clawdbot", ...normalizedArgv];
return ["node", programName || "moltbot", ...normalizedArgv];
}
const nodeExecutablePattern = /^node-\d+(?:\.\d+)*(?:\.exe)?$/;

View File

@@ -39,7 +39,7 @@ export function formatCliBannerLine(version: string, options: BannerOptions = {}
const tagline = pickTagline(options);
const rich = options.richTty ?? isRich();
const cliName = resolveCliName(options.argv ?? process.argv, options.env);
const title = cliName === "clawdbot" ? "🦞 Clawdbot" : "🦞 Moltbot";
const title = cliName === "moltbot" ? "🦞 Moltbot" : "🦞 Moltbot";
const prefix = "🦞 ";
const columns = options.columns ?? process.stdout.columns ?? 120;
const plainFullLine = `${title} ${version} (${commitLabel}) — ${tagline}`;

View File

@@ -57,7 +57,7 @@ export function registerBrowserFilesAndDownloadsCommands(
browser
.command("waitfordownload")
.description("Wait for the next download (and save it)")
.argument("[path]", "Save path (default: /tmp/clawdbot/downloads/...)")
.argument("[path]", "Save path (default: /tmp/moltbot/downloads/...)")
.option("--target-id <id>", "CDP target id (or unique prefix)")
.option(
"--timeout-ms <ms>",

View File

@@ -1,34 +1,34 @@
export const browserCoreExamples = [
"clawdbot browser status",
"clawdbot browser start",
"clawdbot browser stop",
"clawdbot browser tabs",
"clawdbot browser open https://example.com",
"clawdbot browser focus abcd1234",
"clawdbot browser close abcd1234",
"clawdbot browser screenshot",
"clawdbot browser screenshot --full-page",
"clawdbot browser screenshot --ref 12",
"clawdbot browser snapshot",
"clawdbot browser snapshot --format aria --limit 200",
"clawdbot browser snapshot --efficient",
"clawdbot browser snapshot --labels",
"moltbot browser status",
"moltbot browser start",
"moltbot browser stop",
"moltbot browser tabs",
"moltbot browser open https://example.com",
"moltbot browser focus abcd1234",
"moltbot browser close abcd1234",
"moltbot browser screenshot",
"moltbot browser screenshot --full-page",
"moltbot browser screenshot --ref 12",
"moltbot browser snapshot",
"moltbot browser snapshot --format aria --limit 200",
"moltbot browser snapshot --efficient",
"moltbot browser snapshot --labels",
];
export const browserActionExamples = [
"clawdbot browser navigate https://example.com",
"clawdbot browser resize 1280 720",
"clawdbot browser click 12 --double",
'clawdbot browser type 23 "hello" --submit',
"clawdbot browser press Enter",
"clawdbot browser hover 44",
"clawdbot browser drag 10 11",
"clawdbot browser select 9 OptionA OptionB",
"clawdbot browser upload /tmp/file.pdf",
'clawdbot browser fill --fields \'[{"ref":"1","value":"Ada"}]\'',
"clawdbot browser dialog --accept",
'clawdbot browser wait --text "Done"',
"clawdbot browser evaluate --fn '(el) => el.textContent' --ref 7",
"clawdbot browser console --level error",
"clawdbot browser pdf",
"moltbot browser navigate https://example.com",
"moltbot browser resize 1280 720",
"moltbot browser click 12 --double",
'moltbot browser type 23 "hello" --submit',
"moltbot browser press Enter",
"moltbot browser hover 44",
"moltbot browser drag 10 11",
"moltbot browser select 9 OptionA OptionB",
"moltbot browser upload /tmp/file.pdf",
'moltbot browser fill --fields \'[{"ref":"1","value":"Ada"}]\'',
"moltbot browser dialog --accept",
'moltbot browser wait --text "Done"',
"moltbot browser evaluate --fn '(el) => el.textContent' --ref 7",
"moltbot browser console --level error",
"moltbot browser pdf",
];

View File

@@ -21,7 +21,7 @@ vi.mock("../runtime.js", () => ({
describe("browser extension install", () => {
it("installs into the state dir (never node_modules)", async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-ext-"));
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "moltbot-ext-"));
const { installChromeExtension } = await import("./browser-cli-extension.js");
const sourceDir = path.resolve(process.cwd(), "assets/chrome-extension");
@@ -34,7 +34,7 @@ describe("browser extension install", () => {
it("copies extension path to clipboard", async () => {
const prev = process.env.CLAWDBOT_STATE_DIR;
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-ext-path-"));
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "moltbot-ext-path-"));
process.env.CLAWDBOT_STATE_DIR = tmp;
try {

View File

@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
import type { Command } from "commander";
import { STATE_DIR_CLAWDBOT } from "../config/paths.js";
import { STATE_DIR } from "../config/paths.js";
import { danger, info } from "../globals.js";
import { copyToClipboard } from "../infra/clipboard.js";
import { defaultRuntime } from "../runtime.js";
@@ -20,7 +20,7 @@ function bundledExtensionRootDir() {
}
function installedExtensionRootDir() {
return path.join(STATE_DIR_CLAWDBOT, "browser", "chrome-extension");
return path.join(STATE_DIR, "browser", "chrome-extension");
}
function hasManifest(dir: string) {
@@ -33,10 +33,10 @@ export async function installChromeExtension(opts?: {
}): Promise<{ path: string }> {
const src = opts?.sourceDir ?? bundledExtensionRootDir();
if (!hasManifest(src)) {
throw new Error("Bundled Chrome extension is missing. Reinstall Clawdbot and try again.");
throw new Error("Bundled Chrome extension is missing. Reinstall Moltbot and try again.");
}
const stateDir = opts?.stateDir ?? STATE_DIR_CLAWDBOT;
const stateDir = opts?.stateDir ?? STATE_DIR;
const dest = path.join(stateDir, "browser", "chrome-extension");
fs.mkdirSync(path.dirname(dest), { recursive: true });
@@ -88,7 +88,7 @@ export function registerBrowserExtensionCommands(
"Next:",
`- Chrome → chrome://extensions → enable “Developer mode”`,
`- “Load unpacked” → select: ${displayPath}`,
`- Pin “Clawdbot Browser Relay”, then click it on the tab (badge shows ON)`,
`- Pin “Moltbot Browser Relay”, then click it on the tab (badge shows ON)`,
"",
`${theme.muted("Docs:")} ${formatDocsLink("/tools/chrome-extension", "docs.molt.bot/tools/chrome-extension")}`,
].join("\n"),
@@ -106,7 +106,7 @@ export function registerBrowserExtensionCommands(
defaultRuntime.error(
danger(
[
`Chrome extension is not installed. Run: "${formatCliCommand("clawdbot browser extension install")}"`,
`Chrome extension is not installed. Run: "${formatCliCommand("moltbot browser extension install")}"`,
`Docs: ${formatDocsLink("/tools/chrome-extension", "docs.molt.bot/tools/chrome-extension")}`,
].join("\n"),
),

View File

@@ -37,7 +37,7 @@ export function registerBrowserCli(program: Command) {
.action(() => {
browser.outputHelp();
defaultRuntime.error(
danger(`Missing subcommand. Try: "${formatCliCommand("clawdbot browser status")}"`),
danger(`Missing subcommand. Try: "${formatCliCommand("moltbot browser status")}"`),
);
defaultRuntime.exit(1);
});

28
src/cli/cli-name.ts Normal file
View File

@@ -0,0 +1,28 @@
import path from "node:path";
export const DEFAULT_CLI_NAME = "moltbot";
export const LEGACY_CLI_NAME = "moltbot";
const KNOWN_CLI_NAMES = new Set([DEFAULT_CLI_NAME, LEGACY_CLI_NAME]);
const CLI_PREFIX_RE = /^(?:((?:pnpm|npm|bunx|npx)\s+))?(moltbot|moltbot)\b/;
export function resolveCliName(
argv: string[] = process.argv,
env: Record<string, string | undefined> = process.env as Record<string, string | undefined>,
): string {
const override = env.MOLTBOT_CLI_NAME?.trim() || env.CLAWDBOT_CLI_NAME?.trim();
if (override) return override;
const argv1 = argv[1];
if (!argv1) return DEFAULT_CLI_NAME;
const base = path.basename(argv1).trim();
if (KNOWN_CLI_NAMES.has(base)) return base;
return DEFAULT_CLI_NAME;
}
export function replaceCliName(command: string, cliName = resolveCliName()): string {
if (!command.trim()) return command;
if (!CLI_PREFIX_RE.test(command)) return command;
return command.replace(CLI_PREFIX_RE, (_match, runner: string | undefined) => {
return `${runner ?? ""}${cliName}`;
});
}

View File

@@ -1,7 +1,7 @@
import { normalizeProfileName } from "./profile-utils.js";
import { replaceCliName, resolveCliName } from "./cli-name.js";
const CLI_PREFIX_RE = /^(?:pnpm|npm|bunx|npx)\s+(?:clawdbot|moltbot)\b|^(?:clawdbot|moltbot)\b/;
const CLI_PREFIX_RE = /^(?:pnpm|npm|bunx|npx)\s+(?:moltbot|moltbot)\b|^(?:moltbot|moltbot)\b/;
const PROFILE_FLAG_RE = /(?:^|\s)--profile(?:\s|=|$)/;
const DEV_FLAG_RE = /(?:^|\s)--dev(?:\s|$)/;

View File

@@ -173,7 +173,7 @@ async function loadValidConfig() {
for (const issue of snapshot.issues) {
defaultRuntime.error(`- ${issue.path || "<root>"}: ${issue.message}`);
}
defaultRuntime.error(`Run \`${formatCliCommand("clawdbot doctor")}\` to repair, then retry.`);
defaultRuntime.error(`Run \`${formatCliCommand("moltbot doctor")}\` to repair, then retry.`);
defaultRuntime.exit(1);
return snapshot;
}

View File

@@ -88,8 +88,8 @@ describe("daemon-cli coverage", () => {
};
beforeEach(() => {
process.env.CLAWDBOT_STATE_DIR = "/tmp/clawdbot-cli-state";
process.env.CLAWDBOT_CONFIG_PATH = "/tmp/clawdbot-cli-state/clawdbot.json";
process.env.CLAWDBOT_STATE_DIR = "/tmp/moltbot-cli-state";
process.env.CLAWDBOT_CONFIG_PATH = "/tmp/moltbot-cli-state/moltbot.json";
delete process.env.CLAWDBOT_GATEWAY_PORT;
delete process.env.CLAWDBOT_PROFILE;
serviceReadCommand.mockResolvedValue(null);
@@ -141,8 +141,8 @@ describe("daemon-cli coverage", () => {
programArguments: ["/bin/node", "cli", "gateway", "--port", "19001"],
environment: {
CLAWDBOT_PROFILE: "dev",
CLAWDBOT_STATE_DIR: "/tmp/clawdbot-daemon-state",
CLAWDBOT_CONFIG_PATH: "/tmp/clawdbot-daemon-state/clawdbot.json",
CLAWDBOT_STATE_DIR: "/tmp/moltbot-daemon-state",
CLAWDBOT_CONFIG_PATH: "/tmp/moltbot-daemon-state/moltbot.json",
CLAWDBOT_GATEWAY_PORT: "19001",
},
sourcePath: "/tmp/com.clawdbot.gateway.plist",

View File

@@ -84,7 +84,7 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
if (!json) {
defaultRuntime.log(`Gateway service already ${service.loadedText}.`);
defaultRuntime.log(
`Reinstall with: ${formatCliCommand("clawdbot gateway install --force")}`,
`Reinstall with: ${formatCliCommand("moltbot gateway install --force")}`,
);
}
return;

View File

@@ -129,7 +129,7 @@ export function renderRuntimeHints(
}
})();
if (runtime.missingUnit) {
hints.push(`Service not installed. Run: ${formatCliCommand("clawdbot gateway install", env)}`);
hints.push(`Service not installed. Run: ${formatCliCommand("moltbot gateway install", env)}`);
if (fileLog) hints.push(`File logs: ${fileLog}`);
return hints;
}
@@ -152,8 +152,8 @@ export function renderRuntimeHints(
export function renderGatewayServiceStartHints(env: NodeJS.ProcessEnv = process.env): string[] {
const base = [
formatCliCommand("clawdbot gateway install", env),
formatCliCommand("clawdbot gateway", env),
formatCliCommand("moltbot gateway install", env),
formatCliCommand("moltbot gateway", env),
];
const profile = env.CLAWDBOT_PROFILE;
switch (process.platform) {

View File

@@ -100,7 +100,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
}
defaultRuntime.error(
warnText(
`Recommendation: run "${formatCliCommand("clawdbot doctor")}" (or "${formatCliCommand("clawdbot doctor --repair")}").`,
`Recommendation: run "${formatCliCommand("moltbot doctor")}" (or "${formatCliCommand("moltbot doctor --repair")}").`,
),
);
}
@@ -134,7 +134,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
);
defaultRuntime.error(
errorText(
`Fix: rerun \`${formatCliCommand("clawdbot gateway install --force")}\` from the same --profile / CLAWDBOT_STATE_DIR you expect.`,
`Fix: rerun \`${formatCliCommand("moltbot gateway install --force")}\` from the same --profile / CLAWDBOT_STATE_DIR you expect.`,
),
);
}
@@ -237,7 +237,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
),
);
defaultRuntime.error(
errorText(`Then reinstall: ${formatCliCommand("clawdbot gateway install")}`),
errorText(`Then reinstall: ${formatCliCommand("moltbot gateway install")}`),
);
spacer();
}
@@ -292,7 +292,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: ${formatCliCommand("clawdbot doctor")}`));
defaultRuntime.error(errorText(`Cleanup: ${formatCliCommand("moltbot doctor")}`));
spacer();
}
@@ -321,6 +321,6 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
spacer();
}
defaultRuntime.log(`${label("Troubles:")} run ${formatCliCommand("clawdbot status")}`);
defaultRuntime.log(`${label("Troubles:")} run ${formatCliCommand("moltbot status")}`);
defaultRuntime.log(`${label("Troubleshooting:")} https://docs.molt.bot/troubleshooting`);
}

View File

@@ -9,6 +9,6 @@ describe("dns cli", () => {
await program.parseAsync(["dns", "setup"], { from: "user" });
const output = log.mock.calls.map((call) => call.join(" ")).join("\n");
expect(output).toContain("DNS setup");
expect(output).toContain("clawdbot.internal");
expect(output).toContain("moltbot.internal");
});
});

View File

@@ -102,7 +102,7 @@ export function registerDnsCli(program: Command) {
dns
.command("setup")
.description("Set up CoreDNS to serve clawdbot.internal for unicast DNS-SD (Wide-Area Bonjour)")
.description("Set up CoreDNS to serve moltbot.internal for unicast DNS-SD (Wide-Area Bonjour)")
.option(
"--apply",
"Install/update CoreDNS config and (re)start the service (requires sudo)",
@@ -134,7 +134,7 @@ export function registerDnsCli(program: Command) {
}).trimEnd(),
);
defaultRuntime.log("");
defaultRuntime.log(theme.heading("Recommended ~/.clawdbot/clawdbot.json:"));
defaultRuntime.log(theme.heading("Recommended ~/.clawdbot/moltbot.json:"));
defaultRuntime.log(
JSON.stringify(
{
@@ -150,7 +150,7 @@ export function registerDnsCli(program: Command) {
defaultRuntime.log(
theme.muted(`- Add nameserver: ${tailnetIPv4 ?? "<this machine's tailnet IPv4>"}`),
);
defaultRuntime.log(theme.muted("- Restrict to domain (Split DNS): clawdbot.internal"));
defaultRuntime.log(theme.muted("- Restrict to domain (Split DNS): moltbot.internal"));
if (!opts.apply) {
defaultRuntime.log("");
@@ -170,7 +170,7 @@ export function registerDnsCli(program: Command) {
const corefilePath = path.join(etcDir, "Corefile");
const confDir = path.join(etcDir, "conf.d");
const importGlob = path.join(confDir, "*.server");
const serverPath = path.join(confDir, "clawdbot.internal.server");
const serverPath = path.join(confDir, "moltbot.internal.server");
run("brew", ["list", "coredns"], { allowFailure: true });
run("brew", ["install", "coredns"], {
@@ -210,7 +210,7 @@ export function registerDnsCli(program: Command) {
const serial = `${y}${m}${d}01`;
const zoneLines = [
`; created by clawdbot dns setup (will be overwritten by the gateway when wide-area discovery is enabled)`,
`; created by moltbot dns setup (will be overwritten by the gateway when wide-area discovery is enabled)`,
`$ORIGIN ${WIDE_AREA_DISCOVERY_DOMAIN}`,
`$TTL 60`,
`@ IN SOA ns1 hostmaster ${serial} 7200 3600 1209600 60`,
@@ -233,7 +233,7 @@ export function registerDnsCli(program: Command) {
defaultRuntime.log("");
defaultRuntime.log(
theme.muted(
"Note: enable discovery.wideArea.enabled in ~/.clawdbot/clawdbot.json on the gateway and restart the gateway so it writes the DNS-SD zone.",
"Note: enable discovery.wideArea.enabled in ~/.clawdbot/moltbot.json on the gateway and restart the gateway so it writes the DNS-SD zone.",
),
);
}

View File

@@ -9,7 +9,7 @@ import { runCommandWithRuntime } from "./cli-utils.js";
export function registerDocsCli(program: Command) {
program
.command("docs")
.description("Search the live Clawdbot docs")
.description("Search the live Moltbot docs")
.argument("[query...]", "Search query")
.addHelpText(
"after",

View File

@@ -326,16 +326,16 @@ export function registerExecApprovalsCli(program: Command) {
"after",
() =>
`\n${theme.heading("Examples:")}\n${formatExample(
'clawdbot approvals allowlist add "~/Projects/**/bin/rg"',
'moltbot approvals allowlist add "~/Projects/**/bin/rg"',
"Allowlist a local binary pattern for the main agent.",
)}\n${formatExample(
'clawdbot approvals allowlist add --agent main --node <id|name|ip> "/usr/bin/uptime"',
'moltbot approvals allowlist add --agent main --node <id|name|ip> "/usr/bin/uptime"',
"Allowlist on a specific node/agent.",
)}\n${formatExample(
'clawdbot approvals allowlist add --agent "*" "/usr/bin/uname"',
'moltbot approvals allowlist add --agent "*" "/usr/bin/uname"',
"Allowlist for all agents (wildcard).",
)}\n${formatExample(
'clawdbot approvals allowlist remove "~/Projects/**/bin/rg"',
'moltbot approvals allowlist remove "~/Projects/**/bin/rg"',
"Remove an allowlist pattern.",
)}\n\n${theme.muted("Docs:")} ${formatDocsLink("/cli/approvals", "docs.molt.bot/cli/approvals")}\n`,
);

View File

@@ -139,7 +139,7 @@ describe("gateway-cli coverage", () => {
discoverGatewayBeacons.mockReset();
discoverGatewayBeacons.mockResolvedValueOnce([
{
instanceName: "Studio (Clawdbot)",
instanceName: "Studio (Moltbot)",
displayName: "Studio",
domain: "local.",
host: "studio.local",
@@ -171,9 +171,9 @@ describe("gateway-cli coverage", () => {
discoverGatewayBeacons.mockReset();
discoverGatewayBeacons.mockResolvedValueOnce([
{
instanceName: "Studio (Clawdbot)",
instanceName: "Studio (Moltbot)",
displayName: "Studio",
domain: "clawdbot.internal.",
domain: "moltbot.internal.",
host: "studio.clawdbot.internal",
lanHost: "studio.local",
tailnetDns: "studio.tailnet.ts.net",
@@ -194,7 +194,7 @@ describe("gateway-cli coverage", () => {
const out = runtimeLogs.join("\n");
expect(out).toContain("Gateway Discovery");
expect(out).toContain("Found 1 gateway(s)");
expect(out).toContain("- Studio clawdbot.internal.");
expect(out).toContain("- Studio moltbot.internal.");
expect(out).toContain(" tailnet: studio.tailnet.ts.net");
expect(out).toContain(" host: studio.clawdbot.internal");
expect(out).toContain(" ws: ws://studio.tailnet.ts.net:18789");

View File

@@ -4,7 +4,7 @@ import path from "node:path";
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
import { handleReset } from "../../commands/onboard-helpers.js";
import { CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../../config/config.js";
import { CONFIG_PATH, writeConfigFile } from "../../config/config.js";
import { defaultRuntime } from "../../runtime.js";
import { resolveUserPath, shortenHomePath } from "../../utils.js";
@@ -56,7 +56,7 @@ async function ensureDevWorkspace(dir: string) {
const [agents, soul, tools, identity, user] = await Promise.all([
loadDevTemplate(
"AGENTS.dev.md",
`# AGENTS.md - Clawdbot Dev Workspace\n\nDefault dev workspace for clawdbot gateway --dev.\n`,
`# AGENTS.md - Moltbot Dev Workspace\n\nDefault dev workspace for moltbot gateway --dev.\n`,
),
loadDevTemplate(
"SOUL.dev.md",
@@ -89,7 +89,7 @@ export async function ensureDevGatewayConfig(opts: { reset?: boolean }) {
await handleReset("full", workspace, defaultRuntime);
}
const configExists = fs.existsSync(CONFIG_PATH_CLAWDBOT);
const configExists = fs.existsSync(CONFIG_PATH);
if (!opts.reset && configExists) return;
await writeConfigFile({
@@ -117,6 +117,6 @@ export async function ensureDevGatewayConfig(opts: { reset?: boolean }) {
},
});
await ensureDevWorkspace(workspace);
defaultRuntime.log(`Dev config ready: ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`);
defaultRuntime.log(`Dev config ready: ${shortenHomePath(CONFIG_PATH)}`);
defaultRuntime.log(`Dev workspace ready: ${shortenHomePath(resolveUserPath(workspace))}`);
}

View File

@@ -3,7 +3,7 @@ import fs from "node:fs";
import type { Command } from "commander";
import type { GatewayAuthMode } from "../../config/config.js";
import {
CONFIG_PATH_CLAWDBOT,
CONFIG_PATH,
loadConfig,
readConfigFileSnapshot,
resolveGatewayPort,
@@ -157,12 +157,12 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
const passwordRaw = toOptionString(opts.password);
const tokenRaw = toOptionString(opts.token);
const configExists = fs.existsSync(CONFIG_PATH_CLAWDBOT);
const configExists = fs.existsSync(CONFIG_PATH);
const mode = cfg.gateway?.mode;
if (!opts.allowUnconfigured && mode !== "local") {
if (!configExists) {
defaultRuntime.error(
`Missing config. Run \`${formatCliCommand("clawdbot setup")}\` or set gateway.mode=local (or pass --allow-unconfigured).`,
`Missing config. Run \`${formatCliCommand("moltbot setup")}\` or set gateway.mode=local (or pass --allow-unconfigured).`,
);
} else {
defaultRuntime.error(
@@ -286,7 +286,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: ${formatCliCommand("clawdbot gateway stop")}`,
`Gateway failed to start: ${errMessage}\nIf the gateway is supervised, stop it with: ${formatCliCommand("moltbot gateway stop")}`,
);
try {
const diagnostics = await inspectPortUsage(port);

View File

@@ -68,21 +68,21 @@ export function renderGatewayServiceStopHints(env: NodeJS.ProcessEnv = process.e
switch (process.platform) {
case "darwin":
return [
`Tip: ${formatCliCommand("clawdbot gateway stop")}`,
`Tip: ${formatCliCommand("moltbot gateway stop")}`,
`Or: launchctl bootout gui/$UID/${resolveGatewayLaunchAgentLabel(profile)}`,
];
case "linux":
return [
`Tip: ${formatCliCommand("clawdbot gateway stop")}`,
`Tip: ${formatCliCommand("moltbot gateway stop")}`,
`Or: systemctl --user stop ${resolveGatewaySystemdServiceName(profile)}.service`,
];
case "win32":
return [
`Tip: ${formatCliCommand("clawdbot gateway stop")}`,
`Tip: ${formatCliCommand("moltbot gateway stop")}`,
`Or: schtasks /End /TN "${resolveGatewayWindowsTaskName(profile)}"`,
];
default:
return [`Tip: ${formatCliCommand("clawdbot gateway stop")}`];
return [`Tip: ${formatCliCommand("moltbot gateway stop")}`];
}
}

View File

@@ -77,7 +77,7 @@ describe("gateway SIGTERM", () => {
});
it("exits 0 on SIGTERM", { timeout: 180_000 }, async () => {
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-gateway-test-"));
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "moltbot-gateway-test-"));
const out: string[] = [];
const err: string[] = [];
@@ -92,7 +92,7 @@ describe("gateway SIGTERM", () => {
CLAWDBOT_SKIP_BROWSER_CONTROL_SERVER: "1",
CLAWDBOT_SKIP_CANVAS_HOST: "1",
};
const bootstrapPath = path.join(stateDir, "clawdbot-entry-bootstrap.mjs");
const bootstrapPath = path.join(stateDir, "moltbot-entry-bootstrap.mjs");
const runLoopPath = path.resolve("src/cli/gateway-cli/run-loop.ts");
const runtimePath = path.resolve("src/runtime.ts");
fs.writeFileSync(

View File

@@ -9,7 +9,7 @@ const report: HookStatusReport = {
{
name: "session-memory",
description: "Save session context to memory",
source: "clawdbot-bundled",
source: "moltbot-bundled",
pluginId: undefined,
filePath: "/tmp/hooks/session-memory/HOOK.md",
baseDir: "/tmp/hooks/session-memory",
@@ -62,7 +62,7 @@ describe("hooks cli formatting", () => {
{
name: "plugin-hook",
description: "Hook from plugin",
source: "clawdbot-plugin",
source: "moltbot-plugin",
pluginId: "voice-call",
filePath: "/tmp/hooks/plugin-hook/HOOK.md",
baseDir: "/tmp/hooks/plugin-hook",

View File

@@ -3,7 +3,7 @@ import fsp from "node:fs/promises";
import path from "node:path";
import type { Command } from "commander";
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { resolveArchiveKind } from "../infra/archive.js";
import {
buildWorkspaceHookStatus,
@@ -57,7 +57,7 @@ function mergeHookEntries(pluginEntries: HookEntry[], workspaceEntries: HookEntr
return Array.from(merged.values());
}
function buildHooksReport(config: ClawdbotConfig): HookStatusReport {
function buildHooksReport(config: MoltbotConfig): HookStatusReport {
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
const workspaceEntries = loadWorkspaceHookEntries(workspaceDir, { config });
const pluginReport = buildPluginStatusReport({ config, workspaceDir });
@@ -141,7 +141,7 @@ export function formatHooksList(report: HookStatusReport, opts: HooksListOptions
if (hooks.length === 0) {
const message = opts.eligible
? `No eligible hooks found. Run \`${formatCliCommand("clawdbot hooks list")}\` to see all hooks.`
? `No eligible hooks found. Run \`${formatCliCommand("moltbot hooks list")}\` to see all hooks.`
: "No hooks found.";
return message;
}
@@ -197,7 +197,7 @@ export function formatHookInfo(
if (opts.json) {
return JSON.stringify({ error: "not found", hook: hookName }, null, 2);
}
return `Hook "${hookName}" not found. Run \`${formatCliCommand("clawdbot hooks list")}\` to see available hooks.`;
return `Hook "${hookName}" not found. Run \`${formatCliCommand("moltbot hooks list")}\` to see available hooks.`;
}
if (opts.json) {
@@ -533,7 +533,7 @@ export function registerHooksCli(program: Command): void {
process.exit(1);
}
let next: ClawdbotConfig = {
let next: MoltbotConfig = {
...cfg,
hooks: {
...cfg.hooks,
@@ -594,7 +594,7 @@ export function registerHooksCli(program: Command): void {
process.exit(1);
}
let next: ClawdbotConfig = {
let next: MoltbotConfig = {
...cfg,
hooks: {
...cfg.hooks,
@@ -674,7 +674,7 @@ export function registerHooksCli(program: Command): void {
process.exit(1);
}
let next: ClawdbotConfig = {
let next: MoltbotConfig = {
...cfg,
hooks: {
...cfg.hooks,

View File

@@ -18,7 +18,7 @@ describe("logs cli", () => {
it("writes output directly to stdout/stderr", async () => {
callGatewayFromCli.mockResolvedValueOnce({
file: "/tmp/clawdbot.log",
file: "/tmp/moltbot.log",
cursor: 1,
size: 123,
lines: ["raw line"],
@@ -55,7 +55,7 @@ describe("logs cli", () => {
it("warns when the output pipe closes", async () => {
callGatewayFromCli.mockResolvedValueOnce({
file: "/tmp/clawdbot.log",
file: "/tmp/moltbot.log",
lines: ["line one"],
});

View File

@@ -113,7 +113,7 @@ function createLogWriters() {
onBrokenPipe: (err, stream) => {
const code = err.code ?? "EPIPE";
const target = stream === process.stdout ? "stdout" : "stderr";
const message = `clawdbot logs: output ${target} closed (${code}). Stopping tail.`;
const message = `moltbot logs: output ${target} closed (${code}). Stopping tail.`;
try {
clearActiveProgressLine();
process.stderr.write(`${message}\n`);
@@ -141,7 +141,7 @@ function emitGatewayError(
) {
const details = buildGatewayConnectionDetails({ url: opts.url });
const message = "Gateway not reachable. Is it running and accessible?";
const hint = `Hint: run \`${formatCliCommand("clawdbot doctor")}\`.`;
const hint = `Hint: run \`${formatCliCommand("moltbot doctor")}\`.`;
const errorText = err instanceof Error ? err.message : String(err);
if (mode === "json") {

View File

@@ -47,7 +47,7 @@ type NodeDaemonStatusOptions = {
};
function renderNodeServiceStartHints(): string[] {
const base = [formatCliCommand("clawdbot node install"), formatCliCommand("clawdbot node start")];
const base = [formatCliCommand("moltbot node install"), formatCliCommand("moltbot node start")];
switch (process.platform) {
case "darwin":
return [
@@ -169,7 +169,7 @@ export async function runNodeDaemonInstall(opts: NodeDaemonInstallOptions) {
});
if (!json) {
defaultRuntime.log(`Node service already ${service.loadedText}.`);
defaultRuntime.log(`Reinstall with: ${formatCliCommand("clawdbot node install --force")}`);
defaultRuntime.log(`Reinstall with: ${formatCliCommand("moltbot node install --force")}`);
}
return;
}

View File

@@ -221,7 +221,7 @@ export function registerNodesCanvasCommands(nodes: Command) {
const { version, messageCount } = validateA2UIJsonl(jsonl);
if (version === "v0.9") {
throw new Error(
"Detected A2UI v0.9 JSONL (createSurface). Clawdbot currently supports v0.8 only.",
"Detected A2UI v0.9 JSONL (createSurface). Moltbot currently supports v0.8 only.",
);
}
await invokeCanvas(opts, "canvas.a2ui.pushJSONL", { jsonl });

View File

@@ -33,6 +33,6 @@ describe("nodes screen helpers", () => {
tmpDir: "/tmp",
id: "id1",
});
expect(p).toBe(path.join("/tmp", "clawdbot-screen-record-id1.mp4"));
expect(p).toBe(path.join("/tmp", "moltbot-screen-record-id1.mp4"));
});
});

View File

@@ -42,7 +42,7 @@ export function screenRecordTempPath(opts: { ext: string; tmpDir?: string; id?:
const tmpDir = opts.tmpDir ?? os.tmpdir();
const id = opts.id ?? randomUUID();
const ext = opts.ext.startsWith(".") ? opts.ext : `.${opts.ext}`;
return path.join(tmpDir, `clawdbot-screen-record-${id}${ext}`);
return path.join(tmpDir, `moltbot-screen-record-${id}${ext}`);
}
export async function writeScreenRecordToFile(filePath: string, base64: string) {

View File

@@ -115,12 +115,12 @@ export function registerPairingCli(program: Command) {
const resolvedCode = opts.channel ? codeOrChannel : code;
if (!opts.channel && !code) {
throw new Error(
`Usage: ${formatCliCommand("clawdbot pairing approve <channel> <code>")} (or: ${formatCliCommand("clawdbot pairing approve --channel <channel> <code>")})`,
`Usage: ${formatCliCommand("moltbot pairing approve <channel> <code>")} (or: ${formatCliCommand("moltbot pairing approve --channel <channel> <code>")})`,
);
}
if (opts.channel && code != null) {
throw new Error(
`Too many arguments. Use: ${formatCliCommand("clawdbot pairing approve --channel <channel> <code>")}`,
`Too many arguments. Use: ${formatCliCommand("moltbot pairing approve --channel <channel> <code>")}`,
);
}
const channel = parseChannel(channelRaw, channels);

View File

@@ -1,7 +1,7 @@
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import { loadConfig } from "../config/config.js";
import { createSubsystemLogger } from "../logging.js";
import { loadClawdbotPlugins } from "../plugins/loader.js";
import { loadMoltbotPlugins } from "../plugins/loader.js";
import type { PluginLogger } from "../plugins/types.js";
const log = createSubsystemLogger("plugins");
@@ -17,7 +17,7 @@ export function ensurePluginRegistryLoaded(): void {
error: (msg) => log.error(msg),
debug: (msg) => log.debug(msg),
};
loadClawdbotPlugins({
loadMoltbotPlugins({
config,
workspaceDir,
logger,

View File

@@ -3,7 +3,7 @@ import path from "node:path";
import type { Command } from "commander";
import { loadConfig, writeConfigFile } from "../config/config.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { resolveArchiveKind } from "../infra/archive.js";
import { installPluginFromNpmSpec, installPluginFromPath } from "../plugins/install.js";
import { recordPluginInstall } from "../plugins/installs.js";
@@ -67,9 +67,9 @@ function formatPluginLine(plugin: PluginRecord, verbose = false): string {
}
function applySlotSelectionForPlugin(
config: ClawdbotConfig,
config: MoltbotConfig,
pluginId: string,
): { config: ClawdbotConfig; warnings: string[] } {
): { config: MoltbotConfig; warnings: string[] } {
const report = buildPluginStatusReport({ config });
const plugin = report.plugins.find((entry) => entry.id === pluginId);
if (!plugin) {
@@ -94,7 +94,7 @@ function logSlotWarnings(warnings: string[]) {
export function registerPluginsCli(program: Command) {
const plugins = program
.command("plugins")
.description("Manage Clawdbot plugins/extensions")
.description("Manage Moltbot plugins/extensions")
.addHelpText(
"after",
() =>
@@ -246,7 +246,7 @@ export function registerPluginsCli(program: Command) {
.argument("<id>", "Plugin id")
.action(async (id: string) => {
const cfg = loadConfig();
let next: ClawdbotConfig = {
let next: MoltbotConfig = {
...cfg,
plugins: {
...cfg.plugins,
@@ -308,7 +308,7 @@ export function registerPluginsCli(program: Command) {
process.exit(1);
}
let next: ClawdbotConfig = {
let next: MoltbotConfig = {
...cfg,
plugins: {
...cfg.plugins,
@@ -353,7 +353,7 @@ export function registerPluginsCli(program: Command) {
process.exit(1);
}
let next: ClawdbotConfig = {
let next: MoltbotConfig = {
...cfg,
plugins: {
...cfg.plugins,
@@ -417,7 +417,7 @@ export function registerPluginsCli(program: Command) {
process.exit(1);
}
let next: ClawdbotConfig = {
let next: MoltbotConfig = {
...cfg,
plugins: {
...cfg.plugins,

View File

@@ -7,42 +7,42 @@ describe("parseCliProfileArgs", () => {
it("leaves gateway --dev for subcommands", () => {
const res = parseCliProfileArgs([
"node",
"clawdbot",
"moltbot",
"gateway",
"--dev",
"--allow-unconfigured",
]);
if (!res.ok) throw new Error(res.error);
expect(res.profile).toBeNull();
expect(res.argv).toEqual(["node", "clawdbot", "gateway", "--dev", "--allow-unconfigured"]);
expect(res.argv).toEqual(["node", "moltbot", "gateway", "--dev", "--allow-unconfigured"]);
});
it("still accepts global --dev before subcommand", () => {
const res = parseCliProfileArgs(["node", "clawdbot", "--dev", "gateway"]);
const res = parseCliProfileArgs(["node", "moltbot", "--dev", "gateway"]);
if (!res.ok) throw new Error(res.error);
expect(res.profile).toBe("dev");
expect(res.argv).toEqual(["node", "clawdbot", "gateway"]);
expect(res.argv).toEqual(["node", "moltbot", "gateway"]);
});
it("parses --profile value and strips it", () => {
const res = parseCliProfileArgs(["node", "clawdbot", "--profile", "work", "status"]);
const res = parseCliProfileArgs(["node", "moltbot", "--profile", "work", "status"]);
if (!res.ok) throw new Error(res.error);
expect(res.profile).toBe("work");
expect(res.argv).toEqual(["node", "clawdbot", "status"]);
expect(res.argv).toEqual(["node", "moltbot", "status"]);
});
it("rejects missing profile value", () => {
const res = parseCliProfileArgs(["node", "clawdbot", "--profile"]);
const res = parseCliProfileArgs(["node", "moltbot", "--profile"]);
expect(res.ok).toBe(false);
});
it("rejects combining --dev with --profile (dev first)", () => {
const res = parseCliProfileArgs(["node", "clawdbot", "--dev", "--profile", "work", "status"]);
const res = parseCliProfileArgs(["node", "moltbot", "--dev", "--profile", "work", "status"]);
expect(res.ok).toBe(false);
});
it("rejects combining --dev with --profile (profile first)", () => {
const res = parseCliProfileArgs(["node", "clawdbot", "--profile", "work", "--dev", "status"]);
const res = parseCliProfileArgs(["node", "moltbot", "--profile", "work", "--dev", "status"]);
expect(res.ok).toBe(false);
});
});
@@ -58,7 +58,7 @@ describe("applyCliProfileEnv", () => {
const expectedStateDir = path.join("/home/peter", ".clawdbot-dev");
expect(env.CLAWDBOT_PROFILE).toBe("dev");
expect(env.CLAWDBOT_STATE_DIR).toBe(expectedStateDir);
expect(env.CLAWDBOT_CONFIG_PATH).toBe(path.join(expectedStateDir, "clawdbot.json"));
expect(env.CLAWDBOT_CONFIG_PATH).toBe(path.join(expectedStateDir, "moltbot.json"));
expect(env.CLAWDBOT_GATEWAY_PORT).toBe("19001");
});
@@ -74,65 +74,65 @@ describe("applyCliProfileEnv", () => {
});
expect(env.CLAWDBOT_STATE_DIR).toBe("/custom");
expect(env.CLAWDBOT_GATEWAY_PORT).toBe("19099");
expect(env.CLAWDBOT_CONFIG_PATH).toBe(path.join("/custom", "clawdbot.json"));
expect(env.CLAWDBOT_CONFIG_PATH).toBe(path.join("/custom", "moltbot.json"));
});
});
describe("formatCliCommand", () => {
it("returns command unchanged when no profile is set", () => {
expect(formatCliCommand("clawdbot doctor --fix", {})).toBe("moltbot doctor --fix");
expect(formatCliCommand("moltbot doctor --fix", {})).toBe("moltbot doctor --fix");
});
it("returns command unchanged when profile is default", () => {
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "default" })).toBe(
expect(formatCliCommand("moltbot doctor --fix", { CLAWDBOT_PROFILE: "default" })).toBe(
"moltbot doctor --fix",
);
});
it("returns command unchanged when profile is Default (case-insensitive)", () => {
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "Default" })).toBe(
expect(formatCliCommand("moltbot doctor --fix", { CLAWDBOT_PROFILE: "Default" })).toBe(
"moltbot doctor --fix",
);
});
it("returns command unchanged when profile is invalid", () => {
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "bad profile" })).toBe(
expect(formatCliCommand("moltbot doctor --fix", { CLAWDBOT_PROFILE: "bad profile" })).toBe(
"moltbot doctor --fix",
);
});
it("returns command unchanged when --profile is already present", () => {
expect(
formatCliCommand("clawdbot --profile work doctor --fix", { CLAWDBOT_PROFILE: "work" }),
formatCliCommand("moltbot --profile work doctor --fix", { CLAWDBOT_PROFILE: "work" }),
).toBe("moltbot --profile work doctor --fix");
});
it("returns command unchanged when --dev is already present", () => {
expect(formatCliCommand("clawdbot --dev doctor", { CLAWDBOT_PROFILE: "dev" })).toBe(
expect(formatCliCommand("moltbot --dev doctor", { CLAWDBOT_PROFILE: "dev" })).toBe(
"moltbot --dev doctor",
);
});
it("inserts --profile flag when profile is set", () => {
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: "work" })).toBe(
expect(formatCliCommand("moltbot doctor --fix", { CLAWDBOT_PROFILE: "work" })).toBe(
"moltbot --profile work doctor --fix",
);
});
it("trims whitespace from profile", () => {
expect(formatCliCommand("clawdbot doctor --fix", { CLAWDBOT_PROFILE: " jbclawd " })).toBe(
expect(formatCliCommand("moltbot doctor --fix", { CLAWDBOT_PROFILE: " jbclawd " })).toBe(
"moltbot --profile jbclawd doctor --fix",
);
});
it("handles command with no args after clawdbot", () => {
expect(formatCliCommand("clawdbot", { CLAWDBOT_PROFILE: "test" })).toBe(
it("handles command with no args after moltbot", () => {
expect(formatCliCommand("moltbot", { CLAWDBOT_PROFILE: "test" })).toBe(
"moltbot --profile test",
);
});
it("handles pnpm wrapper", () => {
expect(formatCliCommand("pnpm clawdbot doctor", { CLAWDBOT_PROFILE: "work" })).toBe(
expect(formatCliCommand("pnpm moltbot doctor", { CLAWDBOT_PROFILE: "work" })).toBe(
"pnpm moltbot --profile work doctor",
);
});

View File

@@ -102,7 +102,7 @@ export function applyCliProfileEnv(params: {
if (!env.CLAWDBOT_STATE_DIR?.trim()) env.CLAWDBOT_STATE_DIR = stateDir;
if (!env.CLAWDBOT_CONFIG_PATH?.trim()) {
env.CLAWDBOT_CONFIG_PATH = path.join(stateDir, "clawdbot.json");
env.CLAWDBOT_CONFIG_PATH = path.join(stateDir, "moltbot.json");
}
if (profile === "dev" && !env.CLAWDBOT_GATEWAY_PORT?.trim()) {

View File

@@ -71,9 +71,7 @@ export async function ensureConfigReady(params: {
params.runtime.error(legacyIssues.map((issue) => ` ${error(issue)}`).join("\n"));
}
params.runtime.error("");
params.runtime.error(
`${muted("Run:")} ${commandText(formatCliCommand("clawdbot doctor --fix"))}`,
);
params.runtime.error(`${muted("Run:")} ${commandText(formatCliCommand("moltbot doctor --fix"))}`);
if (!allowInvalid) {
params.runtime.exit(1);
}

View File

@@ -8,24 +8,21 @@ import type { ProgramContext } from "./context.js";
const CLI_NAME = resolveCliName();
const EXAMPLES = [
["moltbot channels login --verbose", "Link personal WhatsApp Web and show QR + connection logs."],
[
"clawdbot channels login --verbose",
"Link personal WhatsApp Web and show QR + connection logs.",
],
[
'clawdbot message send --target +15555550123 --message "Hi" --json',
'moltbot message send --target +15555550123 --message "Hi" --json',
"Send via your web session and print JSON result.",
],
["clawdbot gateway --port 18789", "Run the WebSocket Gateway locally."],
["clawdbot --dev gateway", "Run a dev Gateway (isolated state/config) on ws://127.0.0.1:19001."],
["clawdbot gateway --force", "Kill anything bound to the default gateway port, then start it."],
["clawdbot gateway ...", "Gateway control via WebSocket."],
["moltbot gateway --port 18789", "Run the WebSocket Gateway locally."],
["moltbot --dev gateway", "Run a dev Gateway (isolated state/config) on ws://127.0.0.1:19001."],
["moltbot gateway --force", "Kill anything bound to the default gateway port, then start it."],
["moltbot gateway ...", "Gateway control via WebSocket."],
[
'clawdbot agent --to +15555550123 --message "Run summary" --deliver',
'moltbot agent --to +15555550123 --message "Run summary" --deliver',
"Talk directly to the agent using the Gateway; optionally send the WhatsApp reply.",
],
[
'clawdbot message send --channel telegram --target @mychat --message "Hi"',
'moltbot message send --channel telegram --target @mychat --message "Hi"',
"Send via your Telegram bot.",
],
] as const;

View File

@@ -51,19 +51,19 @@ export function registerAgentCommands(program: Command, args: { agentChannelOpti
`
${theme.heading("Examples:")}
${formatHelpExamples([
['clawdbot agent --to +15555550123 --message "status update"', "Start a new session."],
['clawdbot agent --agent ops --message "Summarize logs"', "Use a specific agent."],
['moltbot agent --to +15555550123 --message "status update"', "Start a new session."],
['moltbot agent --agent ops --message "Summarize logs"', "Use a specific agent."],
[
'clawdbot agent --session-id 1234 --message "Summarize inbox" --thinking medium',
'moltbot agent --session-id 1234 --message "Summarize inbox" --thinking medium',
"Target a session with explicit thinking level.",
],
[
'clawdbot agent --to +15555550123 --message "Trace logs" --verbose on --json',
'moltbot agent --to +15555550123 --message "Trace logs" --verbose on --json',
"Enable verbose logging and JSON output.",
],
['clawdbot agent --to +15555550123 --message "Summon reply" --deliver', "Deliver reply."],
['moltbot agent --to +15555550123 --message "Summon reply" --deliver', "Deliver reply."],
[
'clawdbot agent --agent ops --message "Generate report" --deliver --reply-channel slack --reply-to "#reports"',
'moltbot agent --agent ops --message "Generate report" --deliver --reply-channel slack --reply-to "#reports"',
"Send reply to a different channel/target.",
],
])}
@@ -155,11 +155,11 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.molt.bot/cli/agent"
`
${theme.heading("Examples:")}
${formatHelpExamples([
['clawdbot agents set-identity --agent main --name "Clawd" --emoji "🦞"', "Set name + emoji."],
["clawdbot agents set-identity --agent main --avatar avatars/clawd.png", "Set avatar path."],
["clawdbot agents set-identity --workspace ~/clawd --from-identity", "Load from IDENTITY.md."],
['moltbot agents set-identity --agent main --name "Clawd" --emoji "🦞"', "Set name + emoji."],
["moltbot agents set-identity --agent main --avatar avatars/clawd.png", "Set avatar path."],
["moltbot agents set-identity --workspace ~/clawd --from-identity", "Load from IDENTITY.md."],
[
"clawdbot agents set-identity --identity-file ~/clawd/IDENTITY.md --agent main",
"moltbot agents set-identity --identity-file ~/clawd/IDENTITY.md --agent main",
"Use a specific IDENTITY.md.",
],
])}

View File

@@ -31,17 +31,17 @@ export function registerMessageCommands(program: Command, ctx: ProgramContext) {
`
${theme.heading("Examples:")}
${formatHelpExamples([
['clawdbot message send --target +15555550123 --message "Hi"', "Send a text message."],
['moltbot message send --target +15555550123 --message "Hi"', "Send a text message."],
[
'clawdbot message send --target +15555550123 --message "Hi" --media photo.jpg',
'moltbot message send --target +15555550123 --message "Hi" --media photo.jpg',
"Send a message with media.",
],
[
'clawdbot message poll --channel discord --target channel:123 --poll-question "Snack?" --poll-option Pizza --poll-option Sushi',
'moltbot message poll --channel discord --target channel:123 --poll-question "Snack?" --poll-option Pizza --poll-option Sushi',
"Create a Discord poll.",
],
[
'clawdbot message react --channel discord --target 123 --message-id 456 --emoji "✅"',
'moltbot message react --channel discord --target 123 --message-id 456 --emoji "✅"',
"React to a message.",
],
])}

View File

@@ -10,7 +10,7 @@ import { runCommandWithRuntime } from "../cli-utils.js";
export function registerSetupCommand(program: Command) {
program
.command("setup")
.description("Initialize ~/.clawdbot/clawdbot.json and the agent workspace")
.description("Initialize ~/.clawdbot/moltbot.json and the agent workspace")
.addHelpText(
"after",
() =>

View File

@@ -39,15 +39,15 @@ export function registerStatusHealthSessionsCommands(program: Command) {
"after",
() =>
`\n${theme.heading("Examples:")}\n${formatHelpExamples([
["clawdbot status", "Show channel health + session summary."],
["clawdbot status --all", "Full diagnosis (read-only)."],
["clawdbot status --json", "Machine-readable output."],
["clawdbot status --usage", "Show model provider usage/quota snapshots."],
["moltbot status", "Show channel health + session summary."],
["moltbot status --all", "Full diagnosis (read-only)."],
["moltbot status --json", "Machine-readable output."],
["moltbot status --usage", "Show model provider usage/quota snapshots."],
[
"clawdbot status --deep",
"moltbot status --deep",
"Run channel probes (WA + Telegram + Discord + Slack + Signal).",
],
["clawdbot status --deep --timeout 5000", "Tighten probe timeout."],
["moltbot status --deep --timeout 5000", "Tighten probe timeout."],
])}`,
)
.addHelpText(
@@ -119,10 +119,10 @@ export function registerStatusHealthSessionsCommands(program: Command) {
"after",
() =>
`\n${theme.heading("Examples:")}\n${formatHelpExamples([
["clawdbot sessions", "List all sessions."],
["clawdbot sessions --active 120", "Only last 2 hours."],
["clawdbot sessions --json", "Machine-readable output."],
["clawdbot sessions --store ./tmp/sessions.json", "Use a specific session store."],
["moltbot sessions", "List all sessions."],
["moltbot sessions --active 120", "Only last 2 hours."],
["moltbot sessions --json", "Machine-readable output."],
["moltbot sessions --store ./tmp/sessions.json", "Use a specific session store."],
])}\n\n${theme.muted(
"Shows token usage per session when the agent reports it; set agents.defaults.contextTokens to see % of your model window.",
)}`,

View File

@@ -42,7 +42,7 @@ describe("registerSubCliCommands", () => {
});
it("registers only the primary placeholder and dispatches", async () => {
process.argv = ["node", "clawdbot", "acp"];
process.argv = ["node", "moltbot", "acp"];
const program = new Command();
registerSubCliCommands(program, process.argv);
@@ -55,7 +55,7 @@ describe("registerSubCliCommands", () => {
});
it("registers placeholders for all subcommands when no primary", () => {
process.argv = ["node", "clawdbot"];
process.argv = ["node", "moltbot"];
const program = new Command();
registerSubCliCommands(program, process.argv);
@@ -66,9 +66,9 @@ describe("registerSubCliCommands", () => {
});
it("re-parses argv for lazy subcommands", async () => {
process.argv = ["node", "clawdbot", "nodes", "list"];
process.argv = ["node", "moltbot", "nodes", "list"];
const program = new Command();
program.name("clawdbot");
program.name("moltbot");
registerSubCliCommands(program, process.argv);
expect(program.commands.map((cmd) => cmd.name())).toEqual(["nodes"]);
@@ -80,9 +80,9 @@ describe("registerSubCliCommands", () => {
});
it("replaces placeholder when registering a subcommand by name", async () => {
process.argv = ["node", "clawdbot", "acp", "--help"];
process.argv = ["node", "moltbot", "acp", "--help"];
const program = new Command();
program.name("clawdbot");
program.name("moltbot");
registerSubCliCommands(program, process.argv);
await registerSubCliByName(program, "acp");
@@ -90,7 +90,7 @@ describe("registerSubCliCommands", () => {
const names = program.commands.map((cmd) => cmd.name());
expect(names.filter((name) => name === "acp")).toHaveLength(1);
await program.parseAsync(["node", "clawdbot", "acp"], { from: "user" });
await program.parseAsync(["node", "moltbot", "acp"], { from: "user" });
expect(registerAcpCli).toHaveBeenCalledTimes(1);
expect(acpAction).toHaveBeenCalledTimes(1);
});

View File

@@ -1,5 +1,5 @@
import type { Command } from "commander";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { isTruthyEnvValue } from "../../infra/env.js";
import { buildParseArgv, getPrimaryCommand, hasHelpOrVersion } from "../argv.js";
import { resolveActionArgs } from "./helpers.js";
@@ -22,7 +22,7 @@ const shouldEagerRegisterSubcommands = (_argv: string[]) => {
return isTruthyEnvValue(process.env.CLAWDBOT_DISABLE_LAZY_SUBCOMMANDS);
};
const loadConfig = async (): Promise<ClawdbotConfig> => {
const loadConfig = async (): Promise<MoltbotConfig> => {
const mod = await import("../../config/config.js");
return mod.loadConfig();
};

View File

@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
import { loadDotEnv } from "../infra/dotenv.js";
import { normalizeEnv } from "../infra/env.js";
import { isMainModule } from "../infra/is-main.js";
import { ensureClawdbotCliOnPath } from "../infra/path-env.js";
import { ensureMoltbotCliOnPath } from "../infra/path-env.js";
import { assertSupportedRuntime } from "../infra/runtime-guard.js";
import { formatUncaughtError } from "../infra/errors.js";
import { installUnhandledRejectionHandler } from "../infra/unhandled-rejections.js";
@@ -27,7 +27,7 @@ export async function runCli(argv: string[] = process.argv) {
const normalizedArgv = stripWindowsNodeExec(argv);
loadDotEnv({ quiet: true });
normalizeEnv();
ensureClawdbotCliOnPath();
ensureMoltbotCliOnPath();
// Enforce the minimum supported runtime before doing any work.
assertSupportedRuntime();
@@ -45,7 +45,7 @@ export async function runCli(argv: string[] = process.argv) {
installUnhandledRejectionHandler();
process.on("uncaughtException", (error) => {
console.error("[clawdbot] Uncaught exception:", formatUncaughtError(error));
console.error("[moltbot] Uncaught exception:", formatUncaughtError(error));
process.exit(1);
});

View File

@@ -15,30 +15,30 @@ type CommandOptions = Record<string, unknown>;
const SANDBOX_EXAMPLES = {
main: [
["clawdbot sandbox list", "List all sandbox containers."],
["clawdbot sandbox list --browser", "List only browser containers."],
["clawdbot sandbox recreate --all", "Recreate all containers."],
["clawdbot sandbox recreate --session main", "Recreate a specific session."],
["clawdbot sandbox recreate --agent mybot", "Recreate agent containers."],
["clawdbot sandbox explain", "Explain effective sandbox config."],
["moltbot sandbox list", "List all sandbox containers."],
["moltbot sandbox list --browser", "List only browser containers."],
["moltbot sandbox recreate --all", "Recreate all containers."],
["moltbot sandbox recreate --session main", "Recreate a specific session."],
["moltbot sandbox recreate --agent mybot", "Recreate agent containers."],
["moltbot sandbox explain", "Explain effective sandbox config."],
],
list: [
["clawdbot sandbox list", "List all sandbox containers."],
["clawdbot sandbox list --browser", "List only browser containers."],
["clawdbot sandbox list --json", "JSON output."],
["moltbot sandbox list", "List all sandbox containers."],
["moltbot sandbox list --browser", "List only browser containers."],
["moltbot sandbox list --json", "JSON output."],
],
recreate: [
["clawdbot sandbox recreate --all", "Recreate all containers."],
["clawdbot sandbox recreate --session main", "Recreate a specific session."],
["clawdbot sandbox recreate --agent mybot", "Recreate a specific agent (includes sub-agents)."],
["clawdbot sandbox recreate --browser --all", "Recreate only browser containers."],
["clawdbot sandbox recreate --all --force", "Skip confirmation."],
["moltbot sandbox recreate --all", "Recreate all containers."],
["moltbot sandbox recreate --session main", "Recreate a specific session."],
["moltbot sandbox recreate --agent mybot", "Recreate a specific agent (includes sub-agents)."],
["moltbot sandbox recreate --browser --all", "Recreate only browser containers."],
["moltbot sandbox recreate --all --force", "Skip confirmation."],
],
explain: [
["clawdbot sandbox explain", "Show effective sandbox config."],
["clawdbot sandbox explain --session agent:main:main", "Explain a specific session."],
["clawdbot sandbox explain --agent work", "Explain an agent sandbox."],
["clawdbot sandbox explain --json", "JSON output."],
["moltbot sandbox explain", "Show effective sandbox config."],
["moltbot sandbox explain --session agent:main:main", "Explain a specific session."],
["moltbot sandbox explain --agent work", "Explain an agent sandbox."],
["moltbot sandbox explain --json", "JSON output."],
],
} as const;

View File

@@ -66,12 +66,12 @@ export function registerSecurityCli(program: Command) {
const muted = (text: string) => (rich ? theme.muted(text) : text);
const lines: string[] = [];
lines.push(heading("Clawdbot security audit"));
lines.push(heading("Moltbot security audit"));
lines.push(muted(`Summary: ${formatSummary(report.summary)}`));
lines.push(muted(`Run deeper: ${formatCliCommand("clawdbot security audit --deep")}`));
lines.push(muted(`Run deeper: ${formatCliCommand("moltbot security audit --deep")}`));
if (opts.fix) {
lines.push(muted(`Fix: ${formatCliCommand("clawdbot security audit --fix")}`));
lines.push(muted(`Fix: ${formatCliCommand("moltbot security audit --fix")}`));
if (!fixResult) {
lines.push(muted("Fixes: failed to apply (unexpected error)"));
} else if (

View File

@@ -92,7 +92,7 @@ export function formatSkillsList(report: SkillStatusReport, opts: SkillsListOpti
if (skills.length === 0) {
const message = opts.eligible
? `No eligible skills found. Run \`${formatCliCommand("clawdbot skills list")}\` to see all skills.`
? `No eligible skills found. Run \`${formatCliCommand("moltbot skills list")}\` to see all skills.`
: "No skills found.";
return appendClawdHubHint(message, opts.json);
}
@@ -150,7 +150,7 @@ export function formatSkillInfo(
return JSON.stringify({ error: "not found", skill: skillName }, null, 2);
}
return appendClawdHubHint(
`Skill "${skillName}" not found. Run \`${formatCliCommand("clawdbot skills list")}\` to see available skills.`,
`Skill "${skillName}" not found. Run \`${formatCliCommand("moltbot skills list")}\` to see available skills.`,
opts.json,
);
}

View File

@@ -1,4 +1,4 @@
const DEFAULT_TAGLINE = "All your chats, one Clawdbot.";
const DEFAULT_TAGLINE = "All your chats, one Moltbot.";
const HOLIDAY_TAGLINES = {
newYear:

View File

@@ -22,8 +22,8 @@ vi.mock("../infra/update-runner.js", () => ({
runGatewayUpdate: vi.fn(),
}));
vi.mock("../infra/clawdbot-root.js", () => ({
resolveClawdbotPackageRoot: vi.fn(),
vi.mock("../infra/moltbot-root.js", () => ({
resolveMoltbotPackageRoot: vi.fn(),
}));
vi.mock("../config/config.js", () => ({
@@ -88,12 +88,12 @@ describe("update-cli", () => {
beforeEach(async () => {
vi.clearAllMocks();
const { resolveClawdbotPackageRoot } = await import("../infra/clawdbot-root.js");
const { resolveMoltbotPackageRoot } = await import("../infra/moltbot-root.js");
const { readConfigFileSnapshot } = await import("../config/config.js");
const { checkUpdateStatus, fetchNpmTagVersion, resolveNpmChannelTag } =
await import("../infra/update-check.js");
const { runCommandWithTimeout } = await import("../process/exec.js");
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue(process.cwd());
vi.mocked(resolveMoltbotPackageRoot).mockResolvedValue(process.cwd());
vi.mocked(readConfigFileSnapshot).mockResolvedValue(baseSnapshot);
vi.mocked(fetchNpmTagVersion).mockResolvedValue({
tag: "latest",
@@ -185,7 +185,7 @@ describe("update-cli", () => {
await updateStatusCommand({ json: false });
const logs = vi.mocked(defaultRuntime.log).mock.calls.map((call) => call[0]);
expect(logs.join("\n")).toContain("Clawdbot update status");
expect(logs.join("\n")).toContain("Moltbot update status");
});
it("updateStatusCommand emits JSON", async () => {
@@ -218,20 +218,20 @@ describe("update-cli", () => {
});
it("defaults to stable channel for package installs when unset", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-"));
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-update-"));
try {
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "clawdbot", version: "1.0.0" }),
JSON.stringify({ name: "moltbot", version: "1.0.0" }),
"utf-8",
);
const { resolveClawdbotPackageRoot } = await import("../infra/clawdbot-root.js");
const { resolveMoltbotPackageRoot } = await import("../infra/moltbot-root.js");
const { runGatewayUpdate } = await import("../infra/update-runner.js");
const { checkUpdateStatus } = await import("../infra/update-check.js");
const { updateCommand } = await import("./update-cli.js");
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(resolveMoltbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: tempDir,
installKind: "package",
@@ -283,22 +283,22 @@ describe("update-cli", () => {
});
it("falls back to latest when beta tag is older than release", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-"));
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-update-"));
try {
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "clawdbot", version: "1.0.0" }),
JSON.stringify({ name: "moltbot", version: "1.0.0" }),
"utf-8",
);
const { resolveClawdbotPackageRoot } = await import("../infra/clawdbot-root.js");
const { resolveMoltbotPackageRoot } = await import("../infra/moltbot-root.js");
const { readConfigFileSnapshot } = await import("../config/config.js");
const { resolveNpmChannelTag } = await import("../infra/update-check.js");
const { runGatewayUpdate } = await import("../infra/update-runner.js");
const { updateCommand } = await import("./update-cli.js");
const { checkUpdateStatus } = await import("../infra/update-check.js");
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(resolveMoltbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(readConfigFileSnapshot).mockResolvedValue({
...baseSnapshot,
config: { update: { channel: "beta" } },
@@ -336,19 +336,19 @@ describe("update-cli", () => {
});
it("honors --tag override", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-"));
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-update-"));
try {
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "clawdbot", version: "1.0.0" }),
JSON.stringify({ name: "moltbot", version: "1.0.0" }),
"utf-8",
);
const { resolveClawdbotPackageRoot } = await import("../infra/clawdbot-root.js");
const { resolveMoltbotPackageRoot } = await import("../infra/moltbot-root.js");
const { runGatewayUpdate } = await import("../infra/update-runner.js");
const { updateCommand } = await import("./update-cli.js");
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(resolveMoltbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(runGatewayUpdate).mockResolvedValue({
status: "ok",
mode: "npm",
@@ -514,23 +514,23 @@ describe("update-cli", () => {
});
it("requires confirmation on downgrade when non-interactive", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-"));
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-update-"));
try {
setTty(false);
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "clawdbot", version: "2.0.0" }),
JSON.stringify({ name: "moltbot", version: "2.0.0" }),
"utf-8",
);
const { resolveClawdbotPackageRoot } = await import("../infra/clawdbot-root.js");
const { resolveMoltbotPackageRoot } = await import("../infra/moltbot-root.js");
const { resolveNpmChannelTag } = await import("../infra/update-check.js");
const { runGatewayUpdate } = await import("../infra/update-runner.js");
const { defaultRuntime } = await import("../runtime.js");
const { updateCommand } = await import("./update-cli.js");
const { checkUpdateStatus } = await import("../infra/update-check.js");
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(resolveMoltbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: tempDir,
installKind: "package",
@@ -567,23 +567,23 @@ describe("update-cli", () => {
});
it("allows downgrade with --yes in non-interactive mode", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-"));
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-update-"));
try {
setTty(false);
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "clawdbot", version: "2.0.0" }),
JSON.stringify({ name: "moltbot", version: "2.0.0" }),
"utf-8",
);
const { resolveClawdbotPackageRoot } = await import("../infra/clawdbot-root.js");
const { resolveMoltbotPackageRoot } = await import("../infra/moltbot-root.js");
const { resolveNpmChannelTag } = await import("../infra/update-check.js");
const { runGatewayUpdate } = await import("../infra/update-runner.js");
const { defaultRuntime } = await import("../runtime.js");
const { updateCommand } = await import("./update-cli.js");
const { checkUpdateStatus } = await import("../infra/update-check.js");
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(resolveMoltbotPackageRoot).mockResolvedValue(tempDir);
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: tempDir,
installKind: "package",
@@ -636,7 +636,7 @@ describe("update-cli", () => {
});
it("updateWizardCommand offers dev checkout and forwards selections", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-wizard-"));
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-update-wizard-"));
const previousGitDir = process.env.CLAWDBOT_GIT_DIR;
try {
setTty(true);

View File

@@ -5,7 +5,7 @@ import path from "node:path";
import type { Command } from "commander";
import { readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
import { resolveClawdbotPackageRoot } from "../infra/clawdbot-root.js";
import { resolveMoltbotPackageRoot } from "../infra/moltbot-root.js";
import {
checkUpdateStatus,
compareSemverStrings,
@@ -81,7 +81,6 @@ const STEP_LABELS: Record<string, string> = {
"deps install": "Installing dependencies",
build: "Building",
"ui:build": "Building UI",
"clawdbot doctor": "Running doctor checks",
"moltbot doctor": "Running doctor checks",
"git rev-parse HEAD (after)": "Verifying update",
"global update": "Updating via package manager",
@@ -113,16 +112,16 @@ const UPDATE_QUIPS = [
const MAX_LOG_CHARS = 8000;
const DEFAULT_PACKAGE_NAME = "moltbot";
const CORE_PACKAGE_NAMES = new Set([DEFAULT_PACKAGE_NAME, "clawdbot"]);
const CORE_PACKAGE_NAMES = new Set([DEFAULT_PACKAGE_NAME, "moltbot"]);
const CLI_NAME = resolveCliName();
const CLAWDBOT_REPO_URL = "https://github.com/clawdbot/clawdbot.git";
const DEFAULT_GIT_DIR = path.join(os.homedir(), "clawdbot");
const CLAWDBOT_REPO_URL = "https://github.com/moltbot/moltbot.git";
const DEFAULT_GIT_DIR = path.join(os.homedir(), "moltbot");
function normalizeTag(value?: string | null): string | null {
if (!value) return null;
const trimmed = value.trim();
if (!trimmed) return null;
if (trimmed.startsWith("clawdbot@")) return trimmed.slice("clawdbot@".length);
if (trimmed.startsWith("moltbot@")) return trimmed.slice("moltbot@".length);
if (trimmed.startsWith(`${DEFAULT_PACKAGE_NAME}@`)) {
return trimmed.slice(`${DEFAULT_PACKAGE_NAME}@`.length);
}
@@ -272,7 +271,7 @@ async function ensureGitCheckout(params: {
const empty = await isEmptyDir(params.dir);
if (!empty) {
throw new Error(
`CLAWDBOT_GIT_DIR points at a non-git directory: ${params.dir}. Set CLAWDBOT_GIT_DIR to an empty folder or a clawdbot checkout.`,
`CLAWDBOT_GIT_DIR points at a non-git directory: ${params.dir}. Set CLAWDBOT_GIT_DIR to an empty folder or a moltbot checkout.`,
);
}
return await runUpdateStep({
@@ -337,7 +336,7 @@ export async function updateStatusCommand(opts: UpdateStatusOptions): Promise<vo
}
const root =
(await resolveClawdbotPackageRoot({
(await resolveMoltbotPackageRoot({
moduleUrl: import.meta.url,
argv1: process.argv[1],
cwd: process.cwd(),
@@ -412,7 +411,7 @@ export async function updateStatusCommand(opts: UpdateStatusOptions): Promise<vo
},
];
defaultRuntime.log(theme.heading("Clawdbot update status"));
defaultRuntime.log(theme.heading("Moltbot update status"));
defaultRuntime.log("");
defaultRuntime.log(
renderTable({
@@ -577,7 +576,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
}
const root =
(await resolveClawdbotPackageRoot({
(await resolveMoltbotPackageRoot({
moduleUrl: import.meta.url,
argv1: process.argv[1],
cwd: process.cwd(),
@@ -686,7 +685,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
const showProgress = !opts.json && process.stdout.isTTY;
if (!opts.json) {
defaultRuntime.log(theme.heading("Updating Clawdbot..."));
defaultRuntime.log(theme.heading("Updating Moltbot..."));
defaultRuntime.log("");
}
@@ -824,12 +823,12 @@ 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 \`${replaceCliName(formatCliCommand("clawdbot doctor"), CLI_NAME)}\` and \`${replaceCliName(formatCliCommand("clawdbot gateway restart"), CLI_NAME)}\`.`,
`Skipped: this Moltbot install isn't a git checkout, and the package manager couldn't be detected. Update via your package manager, then run \`${replaceCliName(formatCliCommand("moltbot doctor"), CLI_NAME)}\` and \`${replaceCliName(formatCliCommand("moltbot gateway restart"), CLI_NAME)}\`.`,
),
);
defaultRuntime.log(
theme.muted(
`Examples: \`${replaceCliName("npm i -g clawdbot@latest", CLI_NAME)}\` or \`${replaceCliName("pnpm add -g clawdbot@latest", CLI_NAME)}\``,
`Examples: \`${replaceCliName("npm i -g moltbot@latest", CLI_NAME)}\` or \`${replaceCliName("pnpm add -g moltbot@latest", CLI_NAME)}\``,
),
);
}
@@ -946,7 +945,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
defaultRuntime.log(theme.warn(`Daemon restart failed: ${String(err)}`));
defaultRuntime.log(
theme.muted(
`You may need to restart the service manually: ${replaceCliName(formatCliCommand("clawdbot gateway restart"), CLI_NAME)}`,
`You may need to restart the service manually: ${replaceCliName(formatCliCommand("moltbot gateway restart"), CLI_NAME)}`,
),
);
}
@@ -956,13 +955,13 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
if (result.mode === "npm" || result.mode === "pnpm") {
defaultRuntime.log(
theme.muted(
`Tip: Run \`${replaceCliName(formatCliCommand("clawdbot doctor"), CLI_NAME)}\`, then \`${replaceCliName(formatCliCommand("clawdbot gateway restart"), CLI_NAME)}\` to apply updates to a running gateway.`,
`Tip: Run \`${replaceCliName(formatCliCommand("moltbot doctor"), CLI_NAME)}\`, then \`${replaceCliName(formatCliCommand("moltbot gateway restart"), CLI_NAME)}\` to apply updates to a running gateway.`,
),
);
} else {
defaultRuntime.log(
theme.muted(
`Tip: Run \`${replaceCliName(formatCliCommand("clawdbot gateway restart"), CLI_NAME)}\` to apply updates to a running gateway.`,
`Tip: Run \`${replaceCliName(formatCliCommand("moltbot gateway restart"), CLI_NAME)}\` to apply updates to a running gateway.`,
),
);
}
@@ -976,7 +975,7 @@ export async function updateCommand(opts: UpdateCommandOptions): Promise<void> {
export async function updateWizardCommand(opts: UpdateWizardOptions = {}): Promise<void> {
if (!process.stdin.isTTY) {
defaultRuntime.error(
"Update wizard requires a TTY. Use `clawdbot update --channel <stable|beta|dev>` instead.",
"Update wizard requires a TTY. Use `moltbot update --channel <stable|beta|dev>` instead.",
);
defaultRuntime.exit(1);
return;
@@ -990,7 +989,7 @@ export async function updateWizardCommand(opts: UpdateWizardOptions = {}): Promi
}
const root =
(await resolveClawdbotPackageRoot({
(await resolveMoltbotPackageRoot({
moduleUrl: import.meta.url,
argv1: process.argv[1],
cwd: process.cwd(),
@@ -1067,7 +1066,7 @@ export async function updateWizardCommand(opts: UpdateWizardOptions = {}): Promi
const empty = await isEmptyDir(gitDir);
if (!empty) {
defaultRuntime.error(
`CLAWDBOT_GIT_DIR points at a non-git directory: ${gitDir}. Set CLAWDBOT_GIT_DIR to an empty folder or a clawdbot checkout.`,
`CLAWDBOT_GIT_DIR points at a non-git directory: ${gitDir}. Set CLAWDBOT_GIT_DIR to an empty folder or a moltbot checkout.`,
);
defaultRuntime.exit(1);
return;
@@ -1112,7 +1111,7 @@ export async function updateWizardCommand(opts: UpdateWizardOptions = {}): Promi
export function registerUpdateCli(program: Command) {
const update = program
.command("update")
.description("Update Clawdbot to the latest version")
.description("Update Moltbot to the latest version")
.option("--json", "Output result as JSON", false)
.option("--no-restart", "Skip restarting the gateway service after a successful update")
.option("--channel <stable|beta|dev>", "Persist update channel (git + npm)")
@@ -1121,15 +1120,15 @@ export function registerUpdateCli(program: Command) {
.option("--yes", "Skip confirmation prompts (non-interactive)", false)
.addHelpText("after", () => {
const examples = [
["clawdbot update", "Update a source checkout (git)"],
["clawdbot update --channel beta", "Switch to beta channel (git + npm)"],
["clawdbot update --channel dev", "Switch to dev channel (git + npm)"],
["clawdbot update --tag beta", "One-off update to a dist-tag or version"],
["clawdbot update --no-restart", "Update without restarting the service"],
["clawdbot update --json", "Output result as JSON"],
["clawdbot update --yes", "Non-interactive (accept downgrade prompts)"],
["clawdbot update wizard", "Interactive update wizard"],
["clawdbot --update", "Shorthand for clawdbot update"],
["moltbot update", "Update a source checkout (git)"],
["moltbot update --channel beta", "Switch to beta channel (git + npm)"],
["moltbot update --channel dev", "Switch to dev channel (git + npm)"],
["moltbot update --tag beta", "One-off update to a dist-tag or version"],
["moltbot update --no-restart", "Update without restarting the service"],
["moltbot update --json", "Output result as JSON"],
["moltbot update --yes", "Non-interactive (accept downgrade prompts)"],
["moltbot update wizard", "Interactive update wizard"],
["moltbot --update", "Shorthand for moltbot update"],
] as const;
const fmtExamples = examples
.map(([cmd, desc]) => ` ${theme.command(cmd)} ${theme.muted(`# ${desc}`)}`)
@@ -1141,7 +1140,7 @@ ${theme.heading("What this does:")}
${theme.heading("Switch channels:")}
- Use --channel stable|beta|dev to persist the update channel in config
- Run clawdbot update status to see the active channel and source
- Run moltbot update status to see the active channel and source
- Use --tag <dist-tag|version> for a one-off npm update without persisting
${theme.heading("Non-interactive:")}
@@ -1201,9 +1200,9 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/update", "docs.molt.bot/cli/updat
"after",
() =>
`\n${theme.heading("Examples:")}\n${formatHelpExamples([
["clawdbot update status", "Show channel + version status."],
["clawdbot update status --json", "JSON output."],
["clawdbot update status --timeout 10", "Custom timeout."],
["moltbot update status", "Show channel + version status."],
["moltbot update status --json", "JSON output."],
["moltbot update status --timeout 10", "Custom timeout."],
])}\n\n${theme.heading("Notes:")}\n${theme.muted(
"- Shows current update channel (stable/beta/dev) and source",
)}\n${theme.muted("- Includes git tag/branch/SHA for source checkouts")}\n\n${theme.muted(

View File

@@ -35,14 +35,14 @@ export function registerWebhooksCli(program: Command) {
gmail
.command("setup")
.description("Configure Gmail watch + Pub/Sub + Clawdbot hooks")
.description("Configure Gmail watch + Pub/Sub + Moltbot hooks")
.requiredOption("--account <email>", "Gmail account to watch")
.option("--project <id>", "GCP project id (OAuth client owner)")
.option("--topic <name>", "Pub/Sub topic name", DEFAULT_GMAIL_TOPIC)
.option("--subscription <name>", "Pub/Sub subscription name", DEFAULT_GMAIL_SUBSCRIPTION)
.option("--label <label>", "Gmail label to watch", DEFAULT_GMAIL_LABEL)
.option("--hook-url <url>", "Clawdbot hook URL")
.option("--hook-token <token>", "Clawdbot hook token")
.option("--hook-url <url>", "Moltbot hook URL")
.option("--hook-token <token>", "Moltbot hook token")
.option("--push-token <token>", "Push token for gog watch serve")
.option("--bind <host>", "gog watch serve bind host", DEFAULT_GMAIL_SERVE_BIND)
.option("--port <port>", "gog watch serve port", String(DEFAULT_GMAIL_SERVE_PORT))
@@ -79,8 +79,8 @@ export function registerWebhooksCli(program: Command) {
.option("--topic <topic>", "Pub/Sub topic path (projects/.../topics/..)")
.option("--subscription <name>", "Pub/Sub subscription name")
.option("--label <label>", "Gmail label to watch")
.option("--hook-url <url>", "Clawdbot hook URL")
.option("--hook-token <token>", "Clawdbot hook token")
.option("--hook-url <url>", "Moltbot hook URL")
.option("--hook-token <token>", "Moltbot hook token")
.option("--push-token <token>", "Push token for gog watch serve")
.option("--bind <host>", "gog watch serve bind host")
.option("--port <port>", "gog watch serve port")