fix(tui): consume extension slash commands locally
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed extension slash commands appearing as user prompts after being handled locally.
|
||||
|
||||
## [17.2.5] - 2026-08-03
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -752,6 +752,25 @@ export class InputController {
|
||||
}
|
||||
}
|
||||
|
||||
// Extension commands are local actions. Execute them before the normal
|
||||
// submission path creates an optimistic user message; otherwise a
|
||||
// consumed command remains rendered like a prompt sent to the model.
|
||||
const extensionCommandSpace = text.indexOf(" ");
|
||||
const isLocalExtensionCommand =
|
||||
text.startsWith("/") &&
|
||||
runner?.getCommand(extensionCommandSpace === -1 ? text.slice(1) : text.slice(1, extensionCommandSpace)) !==
|
||||
undefined;
|
||||
if (isLocalExtensionCommand) {
|
||||
this.ctx.editor.addToHistory(text);
|
||||
try {
|
||||
await this.ctx.session.prompt(text, { images: inputImages });
|
||||
} catch (error) {
|
||||
this.ctx.editor.setText(text);
|
||||
this.ctx.showError(error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle bash command (! for normal, !! for excluded from context)
|
||||
if (text.startsWith("!")) {
|
||||
const isExcluded = text.startsWith("!!");
|
||||
|
||||
@@ -15,6 +15,7 @@ function makeCtx(isStreaming = false) {
|
||||
const handleMCPCommand = vi.fn(async () => {});
|
||||
const followUp = vi.fn(async (_text: string, _images?: ImageContent[]) => {});
|
||||
const steer = vi.fn(async (_text: string, _images?: ImageContent[]) => {});
|
||||
const prompt = vi.fn(async () => false);
|
||||
const onInputCallback = vi.fn();
|
||||
let text = "";
|
||||
const editor = {
|
||||
@@ -44,6 +45,7 @@ function makeCtx(isStreaming = false) {
|
||||
extensionRunner: undefined,
|
||||
followUp,
|
||||
steer,
|
||||
prompt,
|
||||
},
|
||||
focusedAgentId: undefined,
|
||||
collabGuest: undefined,
|
||||
@@ -75,6 +77,7 @@ function makeCtx(isStreaming = false) {
|
||||
onInputCallback,
|
||||
handleMCPCommand,
|
||||
showStatus: ctx.showStatus,
|
||||
prompt,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -117,6 +120,23 @@ describe("input controller — slash command history (#3148)", () => {
|
||||
expect(addToHistory).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("executes extension commands without rendering them as user prompts", async () => {
|
||||
const { ctx, editor, addToHistory, onInputCallback, prompt } = makeCtx();
|
||||
Object.defineProperty(ctx.session, "extensionRunner", {
|
||||
value: {
|
||||
getCommand: (name: string) => (name === "id" ? { name } : undefined),
|
||||
hasHandlers: () => false,
|
||||
},
|
||||
});
|
||||
controllerFor(ctx);
|
||||
|
||||
await editor.onSubmit?.("/id");
|
||||
|
||||
expect(prompt).toHaveBeenCalledWith("/id", { images: undefined });
|
||||
expect(addToHistory).toHaveBeenCalledWith("/id");
|
||||
expect(onInputCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("routes /queue through the yield-only follow-up queue while streaming", async () => {
|
||||
const { ctx, editor, addToHistory, followUp, showStatus } = makeCtx(true);
|
||||
controllerFor(ctx);
|
||||
|
||||
Reference in New Issue
Block a user