import { parametersSchema as z, defineCustomTool, CustomToolContext } from "@roo-code/types" // @ts-ignore - Node built-ins import { spawnSync } from "child_process" export default defineCustomTool({ name: "brew_search", description: "Search Homebrew for formulae and/or casks matching a query. Returns structured JSON instead of raw terminal output.", parameters: z.object({ query: z.string().describe("Search term (e.g. 'python', 'docker', 'ffmpeg')"), casks: z.boolean().optional().describe("Include casks in search results (default: false, formula-only)"), }), async execute({ query, casks = false }, context: CustomToolContext) { try { // Search formulae const formulaResult = spawnSync("brew", ["search", "--formula", query], { encoding: "utf-8", timeout: 15_000, }) const formulae = (formulaResult.stdout || "") .split("\n") .map((l: string) => l.trim()) .filter((l: string) => l.length > 0 && !l.startsWith("==>")) let caskList: string[] = [] if (casks) { const caskResult = spawnSync("brew", ["search", "--cask", query], { encoding: "utf-8", timeout: 15_000, }) caskList = (caskResult.stdout || "") .split("\n") .map((l: string) => l.trim()) .filter((l: string) => l.length > 0 && !l.startsWith("==>")) } return JSON.stringify({ query, formulae, casks: caskList, totalCount: formulae.length + caskList.length, }, null, 2) } catch (err: any) { return JSON.stringify({ error: err.message ?? String(err) }, null, 2) } }, })