diff --git a/src/auto-reply/reply/commands.test.ts b/src/auto-reply/reply/commands.test.ts
new file mode 100644
index 000000000..d17e7f25c
--- /dev/null
+++ b/src/auto-reply/reply/commands.test.ts
@@ -0,0 +1,66 @@
+import { describe, expect, it } from "vitest";
+
+import type { ClawdbotConfig } from "../../config/config.js";
+import type { MsgContext } from "../templating.js";
+import { buildCommandContext, handleCommands } from "./commands.js";
+import { parseInlineDirectives } from "./directive-handling.js";
+
+function buildParams(commandBody: string, cfg: ClawdbotConfig) {
+ const ctx = {
+ Body: commandBody,
+ CommandBody: commandBody,
+ CommandSource: "text",
+ CommandAuthorized: true,
+ Provider: "whatsapp",
+ Surface: "whatsapp",
+ } as MsgContext;
+
+ const command = buildCommandContext({
+ ctx,
+ cfg,
+ isGroup: false,
+ triggerBodyNormalized: commandBody.trim().toLowerCase(),
+ commandAuthorized: true,
+ });
+
+ return {
+ ctx,
+ cfg,
+ command,
+ directives: parseInlineDirectives(commandBody),
+ sessionKey: "agent:main:main",
+ workspaceDir: "/tmp",
+ defaultGroupActivation: () => "mention",
+ resolvedVerboseLevel: "off" as const,
+ resolvedReasoningLevel: "off" as const,
+ resolveDefaultThinkingLevel: async () => undefined,
+ provider: "whatsapp",
+ model: "test-model",
+ contextTokens: 0,
+ isGroup: false,
+ };
+}
+
+describe("handleCommands gating", () => {
+ it("blocks /config when disabled", async () => {
+ const cfg = {
+ commands: { config: false, debug: false, text: true },
+ whatsapp: { allowFrom: ["*"] },
+ } as ClawdbotConfig;
+ const params = buildParams("/config show", cfg);
+ const result = await handleCommands(params);
+ expect(result.shouldContinue).toBe(false);
+ expect(result.reply?.text).toContain("/config is disabled");
+ });
+
+ it("blocks /debug when disabled", async () => {
+ const cfg = {
+ commands: { config: false, debug: false, text: true },
+ whatsapp: { allowFrom: ["*"] },
+ } as ClawdbotConfig;
+ const params = buildParams("/debug show", cfg);
+ const result = await handleCommands(params);
+ expect(result.shouldContinue).toBe(false);
+ expect(result.reply?.text).toContain("/debug is disabled");
+ });
+});
diff --git a/src/auto-reply/reply/commands.ts b/src/auto-reply/reply/commands.ts
index cd93458fe..f0f5c6292 100644
--- a/src/auto-reply/reply/commands.ts
+++ b/src/auto-reply/reply/commands.ts
@@ -613,7 +613,10 @@ export async function handleCommands(params: {
);
return { shouldContinue: false };
}
- return { shouldContinue: false, reply: { text: buildCommandsMessage(cfg) } };
+ return {
+ shouldContinue: false,
+ reply: { text: buildCommandsMessage(cfg) },
+ };
}
const statusRequested =
diff --git a/src/cli/daemon-cli.ts b/src/cli/daemon-cli.ts
index 6e09a6a24..766af3027 100644
--- a/src/cli/daemon-cli.ts
+++ b/src/cli/daemon-cli.ts
@@ -336,10 +336,7 @@ function renderGatewayServiceStartHints(
}
case "linux": {
const unit = resolveGatewaySystemdServiceName(profile);
- return [
- ...base,
- `systemctl --user start ${unit}.service`,
- ];
+ return [...base, `systemctl --user start ${unit}.service`];
}
case "win32": {
const task = resolveGatewayWindowsTaskName(profile);
@@ -726,7 +723,8 @@ function printDaemonStatus(status: DaemonStatus, opts: { json: boolean }) {
spacer();
}
if (service.runtime?.cachedLabel) {
- const env = (service.command?.environment ?? process.env) as NodeJS.ProcessEnv;
+ const env = (service.command?.environment ??
+ process.env) as NodeJS.ProcessEnv;
const label = resolveGatewayLaunchAgentLabel(env.CLAWDBOT_PROFILE);
defaultRuntime.error(
errorText(
@@ -782,7 +780,8 @@ function printDaemonStatus(status: DaemonStatus, opts: { json: boolean }) {
);
}
if (process.platform === "linux") {
- const env = (service.command?.environment ?? process.env) as NodeJS.ProcessEnv;
+ const env = (service.command?.environment ??
+ process.env) as NodeJS.ProcessEnv;
const unit = resolveGatewaySystemdServiceName(env.CLAWDBOT_PROFILE);
defaultRuntime.error(
errorText(
diff --git a/src/commands/onboard-non-interactive.ts b/src/commands/onboard-non-interactive.ts
index da379a14d..ec99b0709 100644
--- a/src/commands/onboard-non-interactive.ts
+++ b/src/commands/onboard-non-interactive.ts
@@ -505,15 +505,15 @@ export async function runNonInteractiveOnboarding(
runtime: daemonRuntimeRaw,
nodePath,
});
- const environment = buildServiceEnvironment({
- env: process.env,
- port,
- token: gatewayToken,
- launchdLabel:
- process.platform === "darwin"
- ? resolveGatewayLaunchAgentLabel(process.env.CLAWDBOT_PROFILE)
- : undefined,
- });
+ const environment = buildServiceEnvironment({
+ env: process.env,
+ port,
+ token: gatewayToken,
+ launchdLabel:
+ process.platform === "darwin"
+ ? resolveGatewayLaunchAgentLabel(process.env.CLAWDBOT_PROFILE)
+ : undefined,
+ });
await service.install({
env: process.env,
stdout: process.stdout,
diff --git a/src/daemon/constants.test.ts b/src/daemon/constants.test.ts
index 20476c405..5c3fd742d 100644
--- a/src/daemon/constants.test.ts
+++ b/src/daemon/constants.test.ts
@@ -1,9 +1,9 @@
import { describe, expect, it } from "vitest";
import {
+ formatGatewayServiceDescription,
GATEWAY_LAUNCH_AGENT_LABEL,
GATEWAY_SYSTEMD_SERVICE_NAME,
GATEWAY_WINDOWS_TASK_NAME,
- formatGatewayServiceDescription,
resolveGatewayLaunchAgentLabel,
resolveGatewaySystemdServiceName,
resolveGatewayWindowsTaskName,
@@ -149,15 +149,15 @@ describe("formatGatewayServiceDescription", () => {
});
it("includes profile when set", () => {
- expect(
- formatGatewayServiceDescription({ profile: "work" }),
- ).toBe("Clawdbot Gateway (profile: work)");
+ expect(formatGatewayServiceDescription({ profile: "work" })).toBe(
+ "Clawdbot Gateway (profile: work)",
+ );
});
it("includes version when set", () => {
- expect(
- formatGatewayServiceDescription({ version: "2026.1.10" }),
- ).toBe("Clawdbot Gateway (v2026.1.10)");
+ expect(formatGatewayServiceDescription({ version: "2026.1.10" })).toBe(
+ "Clawdbot Gateway (v2026.1.10)",
+ );
});
it("includes profile and version when set", () => {
diff --git a/src/daemon/inspect.ts b/src/daemon/inspect.ts
index 82e759457..cbd45ecb7 100644
--- a/src/daemon/inspect.ts
+++ b/src/daemon/inspect.ts
@@ -103,7 +103,9 @@ function isClawdbotGatewayTaskName(name: string): boolean {
const normalized = name.trim().toLowerCase();
if (!normalized) return false;
const defaultName = resolveGatewayWindowsTaskName().toLowerCase();
- return normalized === defaultName || normalized.startsWith("clawdbot gateway");
+ return (
+ normalized === defaultName || normalized.startsWith("clawdbot gateway")
+ );
}
function tryExtractPlistLabel(contents: string): string | null {
diff --git a/src/daemon/launchd.ts b/src/daemon/launchd.ts
index 11827c76e..ad57d1f4e 100644
--- a/src/daemon/launchd.ts
+++ b/src/daemon/launchd.ts
@@ -5,9 +5,9 @@ import { promisify } from "node:util";
import { colorize, isRich, theme } from "../terminal/theme.js";
import {
+ formatGatewayServiceDescription,
GATEWAY_LAUNCH_AGENT_LABEL,
LEGACY_GATEWAY_LAUNCH_AGENT_LABELS,
- formatGatewayServiceDescription,
resolveGatewayLaunchAgentLabel,
} from "./constants.js";
import { parseKeyValueOutput } from "./runtime-parse.js";
@@ -190,12 +190,11 @@ export function buildLaunchAgentPlist({
WorkingDirectory
${plistEscape(workingDirectory)}`
: "";
- const commentXml =
- comment && comment.trim()
- ? `
+ const commentXml = comment?.trim()
+ ? `
Comment
${plistEscape(comment.trim())}`
- : "";
+ : "";
const envXml = renderEnvDict(environment);
return `
diff --git a/src/daemon/schtasks.ts b/src/daemon/schtasks.ts
index 65b2b7617..58eb2f420 100644
--- a/src/daemon/schtasks.ts
+++ b/src/daemon/schtasks.ts
@@ -5,8 +5,8 @@ import { promisify } from "node:util";
import { colorize, isRich, theme } from "../terminal/theme.js";
import {
- LEGACY_GATEWAY_WINDOWS_TASK_NAMES,
formatGatewayServiceDescription,
+ LEGACY_GATEWAY_WINDOWS_TASK_NAMES,
resolveGatewayWindowsTaskName,
} from "./constants.js";
import { parseKeyValueOutput } from "./runtime-parse.js";
diff --git a/src/daemon/service.ts b/src/daemon/service.ts
index 973474c15..e1722618d 100644
--- a/src/daemon/service.ts
+++ b/src/daemon/service.ts
@@ -103,7 +103,10 @@ export function resolveGatewayService(): GatewayService {
await uninstallSystemdService(args);
},
stop: async (args) => {
- await stopSystemdService({ stdout: args.stdout, profile: args.profile });
+ await stopSystemdService({
+ stdout: args.stdout,
+ profile: args.profile,
+ });
},
restart: async (args) => {
await restartSystemdService({
diff --git a/src/daemon/systemd.ts b/src/daemon/systemd.ts
index 03d5afd9c..5d0d35f87 100644
--- a/src/daemon/systemd.ts
+++ b/src/daemon/systemd.ts
@@ -6,8 +6,8 @@ import { promisify } from "node:util";
import { runCommandWithTimeout, runExec } from "../process/exec.js";
import { colorize, isRich, theme } from "../terminal/theme.js";
import {
- LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES,
formatGatewayServiceDescription,
+ LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES,
resolveGatewaySystemdServiceName,
} from "./constants.js";
import { parseKeyValueOutput } from "./runtime-parse.js";