import { existsSync, readFileSync } from "node:fs"; import { homedir } from "node:os"; import { resolve } from "node:path"; import type { ExtensionAPI, SlashCommandInfo, ToolInfo } from "@mariozechner/pi-coding-agent"; function resolveFeynmanSettingsPath(): string { const configured = process.env.PI_CODING_AGENT_DIR?.trim(); const agentDir = configured ? configured.startsWith("~/") ? resolve(homedir(), configured.slice(2)) : resolve(configured) : resolve(homedir(), ".feynman", "agent"); return resolve(agentDir, "settings.json"); } function readConfiguredPackages(): string[] { const settingsPath = resolveFeynmanSettingsPath(); if (!existsSync(settingsPath)) return []; try { const parsed = JSON.parse(readFileSync(settingsPath, "utf8")) as { packages?: unknown[] }; return Array.isArray(parsed.packages) ? parsed.packages .map((entry) => { if (typeof entry === "string") return entry; if (!entry || typeof entry !== "object") return undefined; const record = entry as { source?: unknown }; return typeof record.source === "string" ? record.source : undefined; }) .filter((entry): entry is string => Boolean(entry)) : []; } catch { return []; } } function formatSourceLabel(sourceInfo: { source: string; path: string }): string { if (sourceInfo.source === "local") { if (sourceInfo.path.includes("/prompts/")) return "workflow"; if (sourceInfo.path.includes("/extensions/")) return "extension"; return "local"; } return sourceInfo.source.replace(/^npm:/, "").replace(/^git:/, ""); } function formatCommandLine(command: SlashCommandInfo): string { const source = formatSourceLabel(command.sourceInfo); return `/${command.name} — ${command.description ?? ""} [${source}]`; } function summarizeToolParameters(tool: ToolInfo): string { const properties = tool.parameters && typeof tool.parameters === "object" && "properties" in tool.parameters && tool.parameters.properties && typeof tool.parameters.properties === "object" ? Object.keys(tool.parameters.properties as Record) : []; return properties.length > 0 ? properties.join(", ") : "no parameters"; } function formatToolLine(tool: ToolInfo): string { const source = formatSourceLabel(tool.sourceInfo); return `${tool.name} — ${tool.description ?? ""} [${source}]`; } export function registerDiscoveryCommands(pi: ExtensionAPI): void { pi.registerCommand("commands", { description: "Browse all available slash commands, including package and built-in commands.", handler: async (_args, ctx) => { const commands = pi .getCommands() .slice() .sort((left, right) => left.name.localeCompare(right.name)); const items = commands.map((command) => formatCommandLine(command)); const selected = await ctx.ui.select("Slash Commands", items); if (!selected) return; ctx.ui.setEditorText(selected.split(" — ")[0] ?? ""); ctx.ui.notify(`Prefilled ${selected.split(" — ")[0]}`, "info"); }, }); pi.registerCommand("tools", { description: "Browse all callable tools with their source and parameter summary.", handler: async (_args, ctx) => { const tools = pi .getAllTools() .slice() .sort((left, right) => left.name.localeCompare(right.name)); const selected = await ctx.ui.select("Tools", tools.map((tool) => formatToolLine(tool))); if (!selected) return; const toolName = selected.split(" — ")[0] ?? selected; const tool = tools.find((entry) => entry.name === toolName); if (!tool) return; ctx.ui.notify(`${tool.name}: ${summarizeToolParameters(tool)}`, "info"); }, }); pi.registerCommand("capabilities", { description: "Show installed packages, discovery entrypoints, and high-level runtime capability counts.", handler: async (_args, ctx) => { const commands = pi.getCommands(); const tools = pi.getAllTools(); const workflows = commands.filter((command) => formatSourceLabel(command.sourceInfo) === "workflow"); const packages = readConfiguredPackages(); const items = [ `Commands: ${commands.length}`, `Workflows: ${workflows.length}`, `Tools: ${tools.length}`, `Packages: ${packages.length}`, "--- Discovery ---", "/commands — browse slash commands", "/tools — inspect callable tools", "/hotkeys — view keyboard shortcuts", "/service-tier — set request tier for supported providers", "--- Installed Packages ---", ...packages.map((pkg) => pkg), ]; const selected = await ctx.ui.select("Capabilities", items); if (!selected || selected.startsWith("---")) return; if (selected.startsWith("/")) { ctx.ui.setEditorText(selected.split(" — ")[0] ?? selected); ctx.ui.notify(`Prefilled ${selected.split(" — ")[0]}`, "info"); } }, }); }