fix(tui): preserve slash command alias completions (#129039)

This commit is contained in:
Peter Steinberger
2026-08-24 23:55:17 -07:00
committed by GitHub
parent bbae605946
commit b6df91fdd9
2 changed files with 34 additions and 5 deletions
+30 -1
View File
@@ -64,6 +64,29 @@ describe("getSlashCommands", () => {
]);
});
it.each([
{ command: "think", alias: "thinking", level: "max" },
{ command: "think", alias: "t", level: "max" },
{ command: "think", alias: "t", level: "default" },
{ command: "verbose", alias: "v", level: "full" },
{ command: "reasoning", alias: "reason", level: "stream" },
{ command: "elevated", alias: "elev", level: "ask" },
])("keeps /$command $level completion on its /$alias alias", ({ command, alias, level }) => {
for (const local of [false, true]) {
const commands = getSlashCommands({
local,
thinkingLevels: [{ id: "max", label: "max" }],
});
const canonical = commands.find((candidate) => candidate.name === command);
const alternate = commands.find((candidate) => candidate.name === alias);
expect(alternate?.getArgumentCompletions?.(level)).toEqual(
canonical?.getArgumentCompletions?.(level),
);
expect(shouldSubmitExactArgumentCompletion(`/${alias} ${level}`, commands)).toBe(true);
}
});
it.each([{}, { local: true }])("exposes usage cost in completion and help", (options) => {
const commands = getSlashCommands(options);
const usage = commands.find((command) => command.name === "usage");
@@ -92,9 +115,12 @@ describe("getSlashCommands", () => {
it("keeps session status on the shared command path and exposes gateway status separately", () => {
const commands = getSlashCommands();
const status = commands.find((command) => command.name === "status");
const identityAlias = commands.find((command) => command.name === "id");
const gatewayStatus = commands.find((command) => command.name === "gateway-status");
const openclaw = commands.find((command) => command.name === "openclaw");
expect(status?.description).toBe("Show current status.");
expect(identityAlias?.description).toBe("Show your sender id.");
expect(identityAlias?.getArgumentCompletions?.("")).toBeUndefined();
expect(gatewayStatus?.description).toBe("Show gateway status summary");
expect(openclaw?.description).toBe("Return to OpenClaw");
});
@@ -172,7 +198,7 @@ describe("getSlashCommands", () => {
dynamicCommands: [
{
name: "dreaming",
textAliases: ["/dreaming"],
textAliases: ["/dreaming", "/dream"],
description: "Enable or disable memory dreaming.",
source: "plugin",
scope: "both",
@@ -184,6 +210,9 @@ describe("getSlashCommands", () => {
expect(commands.find((command) => command.name === "dreaming")?.description).toBe(
"Enable or disable memory dreaming.",
);
expect(
commands.find((command) => command.name === "dream")?.getArgumentCompletions?.(""),
).toBeUndefined();
});
it("only advertises shared commands that local mode can route", () => {
+4 -4
View File
@@ -203,7 +203,7 @@ function normalizeSlashCommandName(value: string): string {
function appendSlashCommand(
commands: SlashCommand[],
seen: Set<string>,
seen: Map<string, SlashCommand["getArgumentCompletions"]>,
name: string,
description: string,
getArgumentCompletions?: SlashCommand["getArgumentCompletions"],
@@ -212,7 +212,7 @@ function appendSlashCommand(
if (!normalizedName || seen.has(normalizedName)) {
return;
}
seen.add(normalizedName);
seen.set(normalizedName, getArgumentCompletions);
commands.push({ name: normalizedName, description, getArgumentCompletions });
}
@@ -247,7 +247,7 @@ export function getSlashCommands(options: SlashCommandOptions = {}): SlashComman
? options.thinkingLevels.map((level) => level.label)
: listThinkingLevelLabels(options.provider, options.model, undefined, options.agentRuntime);
const commands: SlashCommand[] = [];
const seen = new Set<string>();
const seen = new Map<string, SlashCommand["getArgumentCompletions"]>();
for (const command of TUI_COMMAND_DESCRIPTORS) {
if (
command.shared ||
@@ -287,7 +287,7 @@ export function getSlashCommands(options: SlashCommandOptions = {}): SlashComman
}
const aliases = command.textAliases.length > 0 ? command.textAliases : [`/${command.key}`];
for (const alias of aliases) {
appendSlashCommand(commands, seen, alias, command.description);
appendSlashCommand(commands, seen, alias, command.description, seen.get(command.key));
}
}