@bugabinga/pi-ext-inline 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - 2026-05-21
4
+
5
+ - a40a427 prepare extensions for npm release
6
+ - 133cb7d chore(pi): migrate extensions to earendil packages
7
+ - 5ca1296 Rework Pi agent extensions
8
+ - b87a61a feat(pi): monorepo workspace — all extensions are proper packages
9
+
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # inline
2
+
3
+ Inline shell expansion for Pi prompts.
4
+
5
+ Expands !{...} snippets before prompts reach the model.
6
+
7
+ ## Demo
8
+
9
+ <!-- demo:shell_suite:start -->
10
+ ![Shell suite](assets/shell_suite.gif)
11
+ <!-- demo:shell_suite:end -->
Binary file
package/index.ts ADDED
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Inline Bash Extension - expands inline bash commands in user prompts.
3
+ *
4
+ * Start pi with this extension:
5
+ * pi -e ./examples/extensions/inline-bash.ts
6
+ *
7
+ * Then type prompts with inline bash:
8
+ * What's in !{pwd}?
9
+ * The current branch is !{git branch --show-current} and status: !{git status --short}
10
+ * My node version is !{node --version}
11
+ *
12
+ * The !{command} patterns are executed and replaced with their output before
13
+ * the prompt is sent to the agent.
14
+ *
15
+ * Note: Regular !command syntax (whole-line bash) is preserved and works as before.
16
+ */
17
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
18
+
19
+ export default function (pi: ExtensionAPI) {
20
+ const PATTERN = /!\{([^}]+)\}/g;
21
+ const TIMEOUT_MS = 30000;
22
+
23
+ pi.on("input", async (event, ctx) => {
24
+ const text = event.text;
25
+
26
+ // Don't process if it's a whole-line bash command (starts with !)
27
+ // This preserves the existing !command behavior
28
+ if (text.trimStart().startsWith("!") && !text.trimStart().startsWith("!{")) {
29
+ return { action: "continue" };
30
+ }
31
+
32
+ // Check if there are any inline bash patterns
33
+ if (!PATTERN.test(text)) {
34
+ return { action: "continue" };
35
+ }
36
+
37
+ // Reset regex state after test()
38
+ PATTERN.lastIndex = 0;
39
+
40
+ let result = text;
41
+ const expansions: Array<{ command: string; output: string; error?: string }> = [];
42
+
43
+ // Find all matches first (to avoid issues with replacing while iterating)
44
+ const matches: Array<{ full: string; command: string }> = [];
45
+ let match = PATTERN.exec(text);
46
+ while (match) {
47
+ matches.push({ full: match[0], command: match[1] });
48
+ match = PATTERN.exec(text);
49
+ }
50
+
51
+ // Execute each command and collect results
52
+ for (const { full, command } of matches) {
53
+ try {
54
+ const bashResult = await pi.exec("bash", ["-c", command], {
55
+ timeout: TIMEOUT_MS,
56
+ });
57
+
58
+ const output = bashResult.stdout || bashResult.stderr || "";
59
+ const trimmed = output.trim();
60
+
61
+ if (bashResult.code !== 0 && bashResult.stderr) {
62
+ expansions.push({
63
+ command,
64
+ output: trimmed,
65
+ error: `exit code ${bashResult.code}`,
66
+ });
67
+ } else {
68
+ expansions.push({ command, output: trimmed });
69
+ }
70
+
71
+ result = result.replace(full, trimmed);
72
+ } catch (err) {
73
+ const errorMsg = err instanceof Error ? err.message : String(err);
74
+ expansions.push({ command, output: "", error: errorMsg });
75
+ result = result.replace(full, `[error: ${errorMsg}]`);
76
+ }
77
+ }
78
+
79
+ // Show what was expanded (if UI available)
80
+ if (ctx.hasUI && expansions.length > 0) {
81
+ const summary = expansions
82
+ .map((e) => {
83
+ const status = e.error ? ` (${e.error})` : "";
84
+ const preview = e.output.length > 50 ? `${e.output.slice(0, 50)}...` : e.output;
85
+ return `!{${e.command}}${status} -> "${preview}"`;
86
+ })
87
+ .join("\n");
88
+
89
+ ctx.ui.notify(`Expanded ${expansions.length} inline command(s):\n${summary}`, "info");
90
+ }
91
+
92
+ return { action: "transform", text: result, images: event.images };
93
+ });
94
+ }
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@bugabinga/pi-ext-inline",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "index.ts",
6
+ "peerDependencies": {
7
+ "@earendil-works/pi-coding-agent": "*"
8
+ },
9
+ "license": "MIT",
10
+ "description": "Inline shell expansion for Pi prompts.",
11
+ "keywords": [
12
+ "pi",
13
+ "pi-extension"
14
+ ]
15
+ }