From 8292d7ce5da1480c9a3df24ed26a3bc6ab169bf0 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Tue, 3 Feb 2026 12:20:37 +0100 Subject: [PATCH] feat(coding-agent): add commands.ts example and export SlashCommandInfo types - Add example extension demonstrating pi.getCommands() API - Export SlashCommandInfo, SlashCommandLocation, SlashCommandSource from main index.ts Follow-up to #1210 --- .../examples/extensions/commands.ts | 72 +++++++++++++++++++ packages/coding-agent/src/index.ts | 3 + 2 files changed, 75 insertions(+) create mode 100644 packages/coding-agent/examples/extensions/commands.ts diff --git a/packages/coding-agent/examples/extensions/commands.ts b/packages/coding-agent/examples/extensions/commands.ts new file mode 100644 index 00000000..0a2056bc --- /dev/null +++ b/packages/coding-agent/examples/extensions/commands.ts @@ -0,0 +1,72 @@ +/** + * Commands Extension + * + * Demonstrates the pi.getCommands() API by providing a /commands command + * that lists all available slash commands in the current session. + * + * Usage: + * 1. Copy this file to ~/.pi/agent/extensions/ or your project's .pi/extensions/ + * 2. Use /commands to see available commands + * 3. Use /commands extensions to filter by source + */ + +import type { ExtensionAPI, SlashCommandInfo } from "@mariozechner/pi-coding-agent"; + +export default function commandsExtension(pi: ExtensionAPI) { + pi.registerCommand("commands", { + description: "List available slash commands", + getArgumentCompletions: (prefix) => { + const sources = ["extension", "template", "skill"]; + const filtered = sources.filter((s) => s.startsWith(prefix)); + return filtered.length > 0 ? filtered.map((s) => ({ value: s, label: s })) : null; + }, + handler: async (args, ctx) => { + const commands = pi.getCommands(); + const sourceFilter = args.trim() as "extension" | "template" | "skill" | ""; + + // Filter by source if specified + const filtered = sourceFilter ? commands.filter((c) => c.source === sourceFilter) : commands; + + if (filtered.length === 0) { + ctx.ui.notify(sourceFilter ? `No ${sourceFilter} commands found` : "No commands found", "info"); + return; + } + + // Build selection items grouped by source + const formatCommand = (cmd: SlashCommandInfo): string => { + const desc = cmd.description ? ` - ${cmd.description}` : ""; + return `/${cmd.name}${desc}`; + }; + + const items: string[] = []; + const sources: Array<{ key: "extension" | "template" | "skill"; label: string }> = [ + { key: "extension", label: "Extensions" }, + { key: "template", label: "Templates" }, + { key: "skill", label: "Skills" }, + ]; + + for (const { key, label } of sources) { + const cmds = filtered.filter((c) => c.source === key); + if (cmds.length > 0) { + items.push(`--- ${label} ---`); + items.push(...cmds.map(formatCommand)); + } + } + + // Show in a selector (user can scroll and see all commands) + const selected = await ctx.ui.select("Available Commands", items); + + // If user selected a command (not a header), offer to show its path + if (selected && !selected.startsWith("---")) { + const cmdName = selected.split(" - ")[0].slice(1); // Remove leading / + const cmd = commands.find((c) => c.name === cmdName); + if (cmd?.path) { + const showPath = await ctx.ui.confirm(cmd.name, `View source path?\n${cmd.path}`); + if (showPath) { + ctx.ui.notify(cmd.path, "info"); + } + } + } + }, + }); +} diff --git a/packages/coding-agent/src/index.ts b/packages/coding-agent/src/index.ts index b335f343..772afd39 100644 --- a/packages/coding-agent/src/index.ts +++ b/packages/coding-agent/src/index.ts @@ -97,6 +97,9 @@ export type { SessionStartEvent, SessionSwitchEvent, SessionTreeEvent, + SlashCommandInfo, + SlashCommandLocation, + SlashCommandSource, ToolCallEvent, ToolDefinition, ToolInfo,