1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
import type { ExtensionAPI } from "@mariozechner/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;
// Preserve the existing whole-line !command behavior.
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((entry) => {
const status = entry.error ? ` (${entry.error})` : "";
const preview =
entry.output.length > 50 ? `${entry.output.slice(0, 50)}...` : entry.output;
return `!{${entry.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 };
});
}
|