diff --git a/CHANGELOG.md b/CHANGELOG.md index e401cbfa7..1a19d24c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Providers: add Microsoft Teams provider with polling, attachments, and CLI send support. (#404) — thanks @onutc - Slack: honor reply tags + replyToMode while keeping threaded replies in-thread. (#574) — thanks @bolismauro - Commands: accept /models as an alias for /model. +- Commands: add `/usage` as an alias for `/status`. (#492) — thanks @lc0rp - Models/Auth: show per-agent auth candidates in `/model status`, and add `clawdbot models auth order {get,set,clear}` (per-agent auth rotation overrides). — thanks @steipete - Debugging: add raw model stream logging flags and document gateway watch mode. - Gateway: decode dns-sd escaped UTF-8 in discovery output and show scan progress immediately. — thanks @steipete diff --git a/docs/cli/index.md b/docs/cli/index.md index 272a3a64f..5abfdf62c 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -377,7 +377,7 @@ Options: Clawdbot can surface provider usage/quota when OAuth/API creds are available. Surfaces: -- `/status` (adds a short usage line when available) +- `/status` (alias: `/usage`; adds a short usage line when available) - `clawdbot status --usage` (prints full provider breakdown) - macOS menu bar (Usage section under Context) diff --git a/docs/tools/slash-commands.md b/docs/tools/slash-commands.md index f35c0db70..33e3fff6b 100644 --- a/docs/tools/slash-commands.md +++ b/docs/tools/slash-commands.md @@ -35,7 +35,7 @@ Directives (`/think`, `/verbose`, `/reasoning`, `/elevated`) are parsed even whe Text + native (when enabled): - `/help` -- `/status` +- `/status` (alias: `/usage`) - `/debug show|set|unset|reset` (runtime overrides, owner-only) - `/cost on|off` (toggle per-response usage line) - `/stop` diff --git a/src/auto-reply/command-detection.test.ts b/src/auto-reply/command-detection.test.ts index e8a14a898..fe8ec2f92 100644 --- a/src/auto-reply/command-detection.test.ts +++ b/src/auto-reply/command-detection.test.ts @@ -44,6 +44,8 @@ describe("control command parsing", () => { expect(hasControlCommand("help")).toBe(false); expect(hasControlCommand("/status")).toBe(true); expect(hasControlCommand("/status:")).toBe(true); + expect(hasControlCommand("/usage")).toBe(true); + expect(hasControlCommand("/usage:")).toBe(true); expect(hasControlCommand("status")).toBe(false); }); diff --git a/src/auto-reply/commands-registry.test.ts b/src/auto-reply/commands-registry.test.ts index fc270ab08..3d0a8eae6 100644 --- a/src/auto-reply/commands-registry.test.ts +++ b/src/auto-reply/commands-registry.test.ts @@ -24,6 +24,8 @@ describe("commands registry", () => { expect(detection.exact.has("/help")).toBe(true); expect(detection.regex.test("/status")).toBe(true); expect(detection.regex.test("/status:")).toBe(true); + expect(detection.regex.test("/usage")).toBe(true); + expect(detection.regex.test("/usage:")).toBe(true); expect(detection.regex.test("/stop")).toBe(true); expect(detection.regex.test("/send:")).toBe(true); expect(detection.regex.test("/debug set foo=bar")).toBe(true); diff --git a/src/auto-reply/commands-registry.ts b/src/auto-reply/commands-registry.ts index 4f3ec4ef4..bbdda249f 100644 --- a/src/auto-reply/commands-registry.ts +++ b/src/auto-reply/commands-registry.ts @@ -25,7 +25,7 @@ const CHAT_COMMANDS: ChatCommandDefinition[] = [ key: "status", nativeName: "status", description: "Show current status.", - textAliases: ["/status"], + textAliases: ["/status", "/usage"], }, { key: "debug", diff --git a/src/auto-reply/reply.directive.parse.test.ts b/src/auto-reply/reply.directive.parse.test.ts index ad03d8fa6..6d036ccbc 100644 --- a/src/auto-reply/reply.directive.parse.test.ts +++ b/src/auto-reply/reply.directive.parse.test.ts @@ -144,6 +144,12 @@ describe("directive parsing", () => { expect(res.cleaned).toBe("thats not /tmp/hello"); }); + it("preserves spacing when stripping usage directives before paths", () => { + const res = extractStatusDirective("thats not /usage:/tmp/hello"); + expect(res.hasDirective).toBe(true); + expect(res.cleaned).toBe("thats not /tmp/hello"); + }); + it("parses queue options and modes", () => { const res = extractQueueDirective( "please /queue steer+backlog debounce:2s cap:5 drop:summarize now", diff --git a/src/auto-reply/reply.triggers.test.ts b/src/auto-reply/reply.triggers.test.ts index 38f60125d..ae8154278 100644 --- a/src/auto-reply/reply.triggers.test.ts +++ b/src/auto-reply/reply.triggers.test.ts @@ -242,6 +242,23 @@ describe("trigger handling", () => { }); }); + it("reports status via /usage without invoking the agent", async () => { + await withTempHome(async (home) => { + const res = await getReplyFromConfig( + { + Body: "/usage", + From: "+1002", + To: "+2000", + }, + {}, + makeCfg(home), + ); + const text = Array.isArray(res) ? res[0]?.text : res?.text; + expect(text).toContain("ClawdBot"); + expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); + }); + }); + it("reports active auth profile and key snippet in status", async () => { await withTempHome(async (home) => { const cfg = makeCfg(home); diff --git a/src/auto-reply/reply/commands.ts b/src/auto-reply/reply/commands.ts index 7299a846d..1d2ba748b 100644 --- a/src/auto-reply/reply/commands.ts +++ b/src/auto-reply/reply/commands.ts @@ -594,7 +594,8 @@ export async function handleCommands(params: { const statusRequested = directives.hasStatusDirective || - command.commandBodyNormalized === "/status"; + command.commandBodyNormalized === "/status" || + command.commandBodyNormalized === "/usage"; if (allowTextCommands && statusRequested) { const reply = await buildStatusReply({ cfg, diff --git a/src/auto-reply/reply/directives.ts b/src/auto-reply/reply/directives.ts index d04180411..c6f431b30 100644 --- a/src/auto-reply/reply/directives.ts +++ b/src/auto-reply/reply/directives.ts @@ -170,7 +170,7 @@ export function extractStatusDirective(body?: string): { hasDirective: boolean; } { if (!body) return { cleaned: "", hasDirective: false }; - return extractSimpleDirective(body, ["status"]); + return extractSimpleDirective(body, ["status", "usage"]); } export type { ElevatedLevel, ReasoningLevel, ThinkLevel, VerboseLevel };