| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; |
|
|
| export default function (pi: ExtensionAPI) { |
| const PATTERN = /!\{([^}]+)\}/g; |
| const TIMEOUT_MS = 30000; |
|
|
| pi.on("input", async (event, ctx) => { |
| const text = event.text; |
|
|
| |
| |
| if (text.trimStart().startsWith("!") && !text.trimStart().startsWith("!{")) { |
| return { action: "continue" }; |
| } |
|
|
| |
| if (!PATTERN.test(text)) { |
| return { action: "continue" }; |
| } |
|
|
| |
| PATTERN.lastIndex = 0; |
|
|
| let result = text; |
| const expansions: Array<{ command: string; output: string; error?: string }> = []; |
|
|
| |
| const matches: Array<{ full: string; command: string }> = []; |
| let match = PATTERN.exec(text); |
| while (match) { |
| matches.push({ full: match[0], command: match[1] }); |
| match = PATTERN.exec(text); |
| } |
|
|
| |
| for (const { full, command } of matches) { |
| try { |
| const bashResult = await pi.exec("bash", ["-c", command], { |
| timeout: TIMEOUT_MS, |
| }); |
|
|
| const output = bashResult.stdout || bashResult.stderr || ""; |
| const trimmed = output.trim(); |
|
|
| if (bashResult.code !== 0 && bashResult.stderr) { |
| expansions.push({ |
| command, |
| output: trimmed, |
| error: `exit code ${bashResult.code}`, |
| }); |
| } else { |
| expansions.push({ command, output: trimmed }); |
| } |
|
|
| result = result.replace(full, trimmed); |
| } catch (err) { |
| const errorMsg = err instanceof Error ? err.message : String(err); |
| expansions.push({ command, output: "", error: errorMsg }); |
| result = result.replace(full, `[error: ${errorMsg}]`); |
| } |
| } |
|
|
| |
| if (ctx.hasUI && expansions.length > 0) { |
| const summary = expansions |
| .map((e) => { |
| const status = e.error ? ` (${e.error})` : ""; |
| const preview = e.output.length > 50 ? `${e.output.slice(0, 50)}...` : e.output; |
| return `!{${e.command}}${status} -> "${preview}"`; |
| }) |
| .join("\n"); |
|
|
| ctx.ui.notify(`Expanded ${expansions.length} inline command(s):\n${summary}`, "info"); |
| } |
|
|
| return { action: "transform", text: result, images: event.images }; |
| }); |
| } |
|
|