Skip to content

Commit a337370

Browse files
committed
fix(commands): exclude skills and mcp prompts from /commands
1 parent ef6ac71 commit a337370

2 files changed

Lines changed: 44 additions & 3 deletions

File tree

src/bot/commands/commands.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,12 @@ async function getCommandList(projectDirectory: string): Promise<CommandItem[]>
286286
}
287287

288288
return data
289-
.filter((command) => typeof command.name === "string" && command.name.trim().length > 0)
289+
.filter((command) => {
290+
const source = (command as { source?: unknown }).source;
291+
return (
292+
typeof command.name === "string" && command.name.trim().length > 0 && source === "command"
293+
);
294+
})
290295
.map((command) => ({
291296
name: command.name,
292297
description: command.description,

tests/bot/commands/commands.test.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ describe("bot/commands/commands", () => {
213213
it("shows commands list and starts custom interaction", async () => {
214214
mocked.commandListMock.mockResolvedValue({
215215
data: [
216-
{ name: "init", description: "create/update AGENTS.md" },
217-
{ name: "poem", description: "write a poem" },
216+
{ name: "init", description: "create/update AGENTS.md", source: "command" },
217+
{ name: "poem", description: "write a poem", source: "command" },
218218
],
219219
error: null,
220220
});
@@ -370,6 +370,7 @@ describe("bot/commands/commands", () => {
370370
const commands = Array.from({ length: 11 }, (_, i) => ({
371371
name: `cmd${i + 1}`,
372372
description: `Command ${i + 1} description`,
373+
source: "command",
373374
}));
374375

375376
mocked.commandListMock.mockResolvedValueOnce({
@@ -397,6 +398,41 @@ describe("bot/commands/commands", () => {
397398
expect(options.reply_markup.inline_keyboard[11]?.[0]?.callback_data).toBe("commands:cancel");
398399
});
399400

401+
it("filters out non-command sources from command list", async () => {
402+
mocked.commandListMock.mockResolvedValue({
403+
data: [
404+
{ name: "init", description: "create/update AGENTS.md", source: "command" },
405+
{ name: "review", description: "review changes", source: "command" },
406+
{ name: "borsch", description: "Borsch recipe", source: "skill" },
407+
{ name: "from-mcp", description: "MCP prompt", source: "mcp" },
408+
],
409+
error: null,
410+
});
411+
412+
const ctx = createCommandContext(750);
413+
await commandsCommand(ctx as never);
414+
415+
expect(ctx.reply).toHaveBeenCalledTimes(1);
416+
417+
const [, options] = (ctx.reply as ReturnType<typeof vi.fn>).mock.calls[0] as [
418+
string,
419+
{ reply_markup: { inline_keyboard: Array<Array<{ callback_data?: string }>> } },
420+
];
421+
422+
expect(options.reply_markup.inline_keyboard[0]?.[0]?.callback_data).toBe("commands:select:0");
423+
expect(options.reply_markup.inline_keyboard[1]?.[0]?.callback_data).toBe("commands:select:1");
424+
expect(options.reply_markup.inline_keyboard[2]?.[0]?.callback_data).toBe("commands:cancel");
425+
426+
const state = interactionManager.getSnapshot();
427+
expect(state?.kind).toBe("custom");
428+
expect(state?.metadata.flow).toBe("commands");
429+
expect(state?.metadata.stage).toBe("list");
430+
expect(state?.metadata.commands).toEqual([
431+
{ name: "init", description: "create/update AGENTS.md" },
432+
{ name: "review", description: "review changes" },
433+
]);
434+
});
435+
400436
it("handles next-page callback and renders second page with prev button", async () => {
401437
const commands = Array.from({ length: 12 }, (_, i) => ({
402438
name: `cmd${i + 1}`,

0 commit comments

Comments
 (0)