import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"; import { extensionCommandSpecs, formatSlashUsage, getExtensionCommandSpec, livePackageCommandGroups, readPromptSpecs, } from "../../metadata/commands.mjs"; import { APP_ROOT } from "./shared.js"; type HelpCommand = { usage: string; description: string }; type HelpSection = { title: string; commands: HelpCommand[] }; function buildHelpSections(pi: ExtensionAPI): HelpSection[] { const liveCommands = new Map(pi.getCommands().map((command) => [command.name, command])); const promptSpecs = readPromptSpecs(APP_ROOT); const sections = new Map(); for (const command of promptSpecs.filter((entry) => entry.section !== "Internal")) { const live = liveCommands.get(command.name); if (!live) continue; const items = sections.get(command.section) ?? []; items.push({ usage: formatSlashUsage(command), description: live.description ?? command.description, }); sections.set(command.section, items); } for (const command of extensionCommandSpecs.filter((entry) => entry.publicDocs)) { const live = liveCommands.get(command.name); if (!live) continue; const items = sections.get(command.section) ?? []; items.push({ usage: formatSlashUsage(command), description: live.description ?? command.description, }); sections.set(command.section, items); } const ownedNames = new Set([ ...promptSpecs.filter((entry) => entry.section !== "Internal").map((entry) => entry.name), ...extensionCommandSpecs.filter((entry) => entry.publicDocs).map((entry) => entry.name), ]); for (const group of livePackageCommandGroups) { const commands: HelpCommand[] = []; for (const spec of group.commands) { const command = liveCommands.get(spec.name); if (!command || ownedNames.has(command.name)) continue; commands.push({ usage: spec.usage, description: command.description ?? "", }); } if (commands.length > 0) { sections.set(group.title, commands); } } return [ "Research Workflows", "Project & Session", "Setup", "Agents & Delegation", "Bundled Package Commands", ] .map((title) => ({ title, commands: sections.get(title) ?? [] })) .filter((section) => section.commands.length > 0); } export function registerHelpCommand(pi: ExtensionAPI): void { pi.registerCommand("help", { description: getExtensionCommandSpec("help")?.description ?? "Show grouped Feynman commands and prefill the editor with a selected command.", handler: async (_args, ctx) => { const sections = buildHelpSections(pi); const items = sections.flatMap((section) => [ `--- ${section.title} ---`, ...section.commands.map((cmd) => `${cmd.usage} — ${cmd.description}`), ]); const selected = await ctx.ui.select("Feynman Help", items); if (!selected || selected.startsWith("---")) return; const usage = selected.split(" — ")[0]; ctx.ui.setEditorText(usage); ctx.ui.notify(`Prefilled ${usage}`, "info"); }, }); }