fix(commands): wire /usage to status (#492) (thanks @lc0rp)

This commit is contained in:
Peter Steinberger
2026-01-09 17:10:53 +01:00
parent 08caf7b9fc
commit 68ad27e31c
6 changed files with 47 additions and 3 deletions

View File

@@ -9,6 +9,7 @@
- Providers: add Microsoft Teams provider with polling, attachments, and CLI send support. (#404) — thanks @onutc - 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 - Slack: honor reply tags + replyToMode while keeping threaded replies in-thread. (#574) — thanks @bolismauro
- Commands: accept /models as an alias for /model. - 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 - 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. - 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 - Gateway: decode dns-sd escaped UTF-8 in discovery output and show scan progress immediately. — thanks @steipete

View File

@@ -36,6 +36,7 @@ Directives (`/think`, `/verbose`, `/reasoning`, `/elevated`) are parsed even whe
Text + native (when enabled): Text + native (when enabled):
- `/help` - `/help`
- `/status` (alias: `/usage`) - `/status` (alias: `/usage`)
- `/debug show|set|unset|reset` (runtime overrides, owner-only)
- `/cost on|off` (toggle per-response usage line) - `/cost on|off` (toggle per-response usage line)
- `/stop` - `/stop`
- `/restart` - `/restart`
@@ -46,7 +47,7 @@ Text + native (when enabled):
- `/verbose on|off` (alias: `/v`) - `/verbose on|off` (alias: `/v`)
- `/reasoning on|off|stream` (alias: `/reason`; `stream` = Telegram draft only) - `/reasoning on|off|stream` (alias: `/reason`; `stream` = Telegram draft only)
- `/elevated on|off` (alias: `/elev`) - `/elevated on|off` (alias: `/elev`)
- `/model <name>` (or `/<alias>` from `agent.models.*.alias`) - `/model <name>` (or `/<alias>` from `agents.defaults.models.*.alias`)
- `/queue <mode>` (plus options like `debounce:2s cap:25 drop:summarize`; send `/queue` to see current settings) - `/queue <mode>` (plus options like `debounce:2s cap:25 drop:summarize`; send `/queue` to see current settings)
Text-only: Text-only:
@@ -59,6 +60,24 @@ Notes:
- `/verbose` is meant for debugging and extra visibility; keep it **off** in normal use. - `/verbose` is meant for debugging and extra visibility; keep it **off** in normal use.
- `/reasoning` (and `/verbose`) are risky in group settings: they may reveal internal reasoning or tool output you did not intend to expose. Prefer leaving them off, especially in group chats. - `/reasoning` (and `/verbose`) are risky in group settings: they may reveal internal reasoning or tool output you did not intend to expose. Prefer leaving them off, especially in group chats.
## Debug overrides
`/debug` lets you set **runtime-only** config overrides (memory, not disk). Owner-only.
Examples:
```
/debug show
/debug set messages.responsePrefix="[clawdbot]"
/debug set whatsapp.allowFrom=["+1555","+4477"]
/debug unset messages.responsePrefix
/debug reset
```
Notes:
- Overrides apply immediately to new config reads, but do **not** write to `clawdbot.json`.
- Use `/debug reset` to clear all overrides and return to the on-disk config.
## Surface notes ## Surface notes
- **Text commands** run in the normal chat session (DMs share `main`, groups have their own session). - **Text commands** run in the normal chat session (DMs share `main`, groups have their own session).

View File

@@ -144,6 +144,12 @@ describe("directive parsing", () => {
expect(res.cleaned).toBe("thats not /tmp/hello"); 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", () => { it("parses queue options and modes", () => {
const res = extractQueueDirective( const res = extractQueueDirective(
"please /queue steer+backlog debounce:2s cap:5 drop:summarize now", "please /queue steer+backlog debounce:2s cap:5 drop:summarize now",

View File

@@ -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 () => { it("reports active auth profile and key snippet in status", async () => {
await withTempHome(async (home) => { await withTempHome(async (home) => {
const cfg = makeCfg(home); const cfg = makeCfg(home);

View File

@@ -594,7 +594,8 @@ export async function handleCommands(params: {
const statusRequested = const statusRequested =
directives.hasStatusDirective || directives.hasStatusDirective ||
command.commandBodyNormalized === "/status"; command.commandBodyNormalized === "/status" ||
command.commandBodyNormalized === "/usage";
if (allowTextCommands && statusRequested) { if (allowTextCommands && statusRequested) {
const reply = await buildStatusReply({ const reply = await buildStatusReply({
cfg, cfg,

View File

@@ -170,7 +170,7 @@ export function extractStatusDirective(body?: string): {
hasDirective: boolean; hasDirective: boolean;
} { } {
if (!body) return { cleaned: "", hasDirective: false }; if (!body) return { cleaned: "", hasDirective: false };
return extractSimpleDirective(body, ["status"]); return extractSimpleDirective(body, ["status", "usage"]);
} }
export type { ElevatedLevel, ReasoningLevel, ThinkLevel, VerboseLevel }; export type { ElevatedLevel, ReasoningLevel, ThinkLevel, VerboseLevel };