@mariozechner/pi-coding-agent 0.49.0 → 0.49.2
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 +42 -0
- package/README.md +14 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +6 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/export-html/template.css +19 -0
- package/dist/core/export-html/template.js +70 -5
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +2 -2
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +49 -24
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +10 -3
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/model-registry.d.ts +2 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +36 -5
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +9 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +2 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts +5 -2
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +9 -0
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +5 -2
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +9 -0
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +14 -2
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +94 -39
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +5 -2
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +9 -0
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +23 -5
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +327 -55
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +10 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +5 -2
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +23 -0
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +54 -29
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/photon.d.ts +3 -2
- package/dist/utils/photon.d.ts.map +1 -1
- package/dist/utils/photon.js +85 -3
- package/dist/utils/photon.js.map +1 -1
- package/docs/extensions.md +5 -3
- package/docs/session.md +6 -0
- package/docs/tui.md +36 -3
- package/examples/extensions/README.md +1 -0
- package/examples/extensions/question.ts +9 -22
- package/examples/extensions/widget-placement.ts +17 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +5 -5
package/dist/utils/photon.js
CHANGED
|
@@ -8,12 +8,89 @@
|
|
|
8
8
|
* The challenge: photon-node's CJS entry uses fs.readFileSync(__dirname + '/photon_rs_bg.wasm')
|
|
9
9
|
* which bakes the build machine's absolute path into Bun compiled binaries.
|
|
10
10
|
*
|
|
11
|
-
* Solution:
|
|
12
|
-
*
|
|
11
|
+
* Solution:
|
|
12
|
+
* 1. Patch fs.readFileSync to redirect missing photon_rs_bg.wasm reads
|
|
13
|
+
* 2. Copy photon_rs_bg.wasm next to the executable in build:binary
|
|
13
14
|
*/
|
|
15
|
+
import { createRequire } from "module";
|
|
16
|
+
import * as path from "path";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
const require = createRequire(import.meta.url);
|
|
19
|
+
const fs = require("fs");
|
|
20
|
+
const WASM_FILENAME = "photon_rs_bg.wasm";
|
|
14
21
|
// Lazy-loaded photon module
|
|
15
22
|
let photonModule = null;
|
|
16
23
|
let loadPromise = null;
|
|
24
|
+
function pathOrNull(file) {
|
|
25
|
+
if (typeof file === "string") {
|
|
26
|
+
return file;
|
|
27
|
+
}
|
|
28
|
+
if (file instanceof URL) {
|
|
29
|
+
return fileURLToPath(file);
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
function getFallbackWasmPaths() {
|
|
34
|
+
const execDir = path.dirname(process.execPath);
|
|
35
|
+
return [
|
|
36
|
+
path.join(execDir, WASM_FILENAME),
|
|
37
|
+
path.join(execDir, "photon", WASM_FILENAME),
|
|
38
|
+
path.join(process.cwd(), WASM_FILENAME),
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
function patchPhotonWasmRead() {
|
|
42
|
+
const originalReadFileSync = fs.readFileSync.bind(fs);
|
|
43
|
+
const fallbackPaths = getFallbackWasmPaths();
|
|
44
|
+
const mutableFs = fs;
|
|
45
|
+
const patchedReadFileSync = ((...args) => {
|
|
46
|
+
const [file, options] = args;
|
|
47
|
+
const resolvedPath = pathOrNull(file);
|
|
48
|
+
if (resolvedPath?.endsWith(WASM_FILENAME)) {
|
|
49
|
+
try {
|
|
50
|
+
return originalReadFileSync(...args);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
const err = error;
|
|
54
|
+
if (err?.code && err.code !== "ENOENT") {
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
for (const fallbackPath of fallbackPaths) {
|
|
58
|
+
if (!fs.existsSync(fallbackPath)) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (options === undefined) {
|
|
62
|
+
return originalReadFileSync(fallbackPath);
|
|
63
|
+
}
|
|
64
|
+
return originalReadFileSync(fallbackPath, options);
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return originalReadFileSync(...args);
|
|
70
|
+
});
|
|
71
|
+
try {
|
|
72
|
+
mutableFs.readFileSync = patchedReadFileSync;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
Object.defineProperty(fs, "readFileSync", {
|
|
76
|
+
value: patchedReadFileSync,
|
|
77
|
+
writable: true,
|
|
78
|
+
configurable: true,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return () => {
|
|
82
|
+
try {
|
|
83
|
+
mutableFs.readFileSync = originalReadFileSync;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
Object.defineProperty(fs, "readFileSync", {
|
|
87
|
+
value: originalReadFileSync,
|
|
88
|
+
writable: true,
|
|
89
|
+
configurable: true,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
17
94
|
/**
|
|
18
95
|
* Load the photon module asynchronously.
|
|
19
96
|
* Returns cached module on subsequent calls.
|
|
@@ -26,13 +103,18 @@ export async function loadPhoton() {
|
|
|
26
103
|
return loadPromise;
|
|
27
104
|
}
|
|
28
105
|
loadPromise = (async () => {
|
|
106
|
+
const restoreReadFileSync = patchPhotonWasmRead();
|
|
29
107
|
try {
|
|
30
108
|
photonModule = await import("@silvia-odwyer/photon-node");
|
|
109
|
+
return photonModule;
|
|
31
110
|
}
|
|
32
111
|
catch {
|
|
33
112
|
photonModule = null;
|
|
113
|
+
return photonModule;
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
restoreReadFileSync();
|
|
34
117
|
}
|
|
35
|
-
return photonModule;
|
|
36
118
|
})();
|
|
37
119
|
return loadPromise;
|
|
38
120
|
}
|
package/dist/utils/photon.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"photon.js","sourceRoot":"","sources":["../../src/utils/photon.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"photon.js","sourceRoot":"","sources":["../../src/utils/photon.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Photon image processing wrapper.\n *\n * This module provides a unified interface to @silvia-odwyer/photon-node that works in:\n * 1. Node.js (development, npm run build)\n * 2. Bun compiled binaries (standalone distribution)\n *\n * The challenge: photon-node's CJS entry uses fs.readFileSync(__dirname + '/photon_rs_bg.wasm')\n * which bakes the build machine's absolute path into Bun compiled binaries.\n *\n * Solution:\n * 1. Patch fs.readFileSync to redirect missing photon_rs_bg.wasm reads\n * 2. Copy photon_rs_bg.wasm next to the executable in build:binary\n */\n\nimport type { PathOrFileDescriptor } from \"fs\";\nimport { createRequire } from \"module\";\nimport * as path from \"path\";\nimport { fileURLToPath } from \"url\";\n\nconst require = createRequire(import.meta.url);\nconst fs = require(\"fs\") as typeof import(\"fs\");\n\n// Re-export types from the main package\nexport type { PhotonImage as PhotonImageType } from \"@silvia-odwyer/photon-node\";\n\ntype ReadFileSync = typeof fs.readFileSync;\n\nconst WASM_FILENAME = \"photon_rs_bg.wasm\";\n\n// Lazy-loaded photon module\nlet photonModule: typeof import(\"@silvia-odwyer/photon-node\") | null = null;\nlet loadPromise: Promise<typeof import(\"@silvia-odwyer/photon-node\") | null> | null = null;\n\nfunction pathOrNull(file: PathOrFileDescriptor): string | null {\n\tif (typeof file === \"string\") {\n\t\treturn file;\n\t}\n\tif (file instanceof URL) {\n\t\treturn fileURLToPath(file);\n\t}\n\treturn null;\n}\n\nfunction getFallbackWasmPaths(): string[] {\n\tconst execDir = path.dirname(process.execPath);\n\treturn [\n\t\tpath.join(execDir, WASM_FILENAME),\n\t\tpath.join(execDir, \"photon\", WASM_FILENAME),\n\t\tpath.join(process.cwd(), WASM_FILENAME),\n\t];\n}\n\nfunction patchPhotonWasmRead(): () => void {\n\tconst originalReadFileSync: ReadFileSync = fs.readFileSync.bind(fs);\n\tconst fallbackPaths = getFallbackWasmPaths();\n\tconst mutableFs = fs as { readFileSync: ReadFileSync };\n\n\tconst patchedReadFileSync: ReadFileSync = ((...args: Parameters<ReadFileSync>) => {\n\t\tconst [file, options] = args;\n\t\tconst resolvedPath = pathOrNull(file);\n\n\t\tif (resolvedPath?.endsWith(WASM_FILENAME)) {\n\t\t\ttry {\n\t\t\t\treturn originalReadFileSync(...args);\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error as NodeJS.ErrnoException;\n\t\t\t\tif (err?.code && err.code !== \"ENOENT\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\n\t\t\t\tfor (const fallbackPath of fallbackPaths) {\n\t\t\t\t\tif (!fs.existsSync(fallbackPath)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (options === undefined) {\n\t\t\t\t\t\treturn originalReadFileSync(fallbackPath);\n\t\t\t\t\t}\n\t\t\t\t\treturn originalReadFileSync(fallbackPath, options);\n\t\t\t\t}\n\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\treturn originalReadFileSync(...args);\n\t}) as ReadFileSync;\n\n\ttry {\n\t\tmutableFs.readFileSync = patchedReadFileSync;\n\t} catch {\n\t\tObject.defineProperty(fs, \"readFileSync\", {\n\t\t\tvalue: patchedReadFileSync,\n\t\t\twritable: true,\n\t\t\tconfigurable: true,\n\t\t});\n\t}\n\n\treturn () => {\n\t\ttry {\n\t\t\tmutableFs.readFileSync = originalReadFileSync;\n\t\t} catch {\n\t\t\tObject.defineProperty(fs, \"readFileSync\", {\n\t\t\t\tvalue: originalReadFileSync,\n\t\t\t\twritable: true,\n\t\t\t\tconfigurable: true,\n\t\t\t});\n\t\t}\n\t};\n}\n\n/**\n * Load the photon module asynchronously.\n * Returns cached module on subsequent calls.\n */\nexport async function loadPhoton(): Promise<typeof import(\"@silvia-odwyer/photon-node\") | null> {\n\tif (photonModule) {\n\t\treturn photonModule;\n\t}\n\n\tif (loadPromise) {\n\t\treturn loadPromise;\n\t}\n\n\tloadPromise = (async () => {\n\t\tconst restoreReadFileSync = patchPhotonWasmRead();\n\t\ttry {\n\t\t\tphotonModule = await import(\"@silvia-odwyer/photon-node\");\n\t\t\treturn photonModule;\n\t\t} catch {\n\t\t\tphotonModule = null;\n\t\t\treturn photonModule;\n\t\t} finally {\n\t\t\trestoreReadFileSync();\n\t\t}\n\t})();\n\n\treturn loadPromise;\n}\n"]}
|
package/docs/extensions.md
CHANGED
|
@@ -184,7 +184,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
184
184
|
const ok = await ctx.ui.confirm("Title", "Are you sure?");
|
|
185
185
|
ctx.ui.notify("Done!", "success");
|
|
186
186
|
ctx.ui.setStatus("my-ext", "Processing..."); // Footer status
|
|
187
|
-
ctx.ui.setWidget("my-ext", ["Line 1", "Line 2"]); // Widget above editor
|
|
187
|
+
ctx.ui.setWidget("my-ext", ["Line 1", "Line 2"]); // Widget above editor (default)
|
|
188
188
|
});
|
|
189
189
|
|
|
190
190
|
// Register tools, commands, shortcuts, flags
|
|
@@ -1366,7 +1366,7 @@ Extensions can interact with users via `ctx.ui` methods and customize how messag
|
|
|
1366
1366
|
- Settings toggles (SettingsList)
|
|
1367
1367
|
- Status indicators (setStatus)
|
|
1368
1368
|
- Working message during streaming (setWorkingMessage)
|
|
1369
|
-
- Widgets above editor (setWidget)
|
|
1369
|
+
- Widgets above/below editor (setWidget)
|
|
1370
1370
|
- Custom footers (setFooter)
|
|
1371
1371
|
|
|
1372
1372
|
### Dialogs
|
|
@@ -1456,8 +1456,10 @@ ctx.ui.setStatus("my-ext", undefined); // Clear
|
|
|
1456
1456
|
ctx.ui.setWorkingMessage("Thinking deeply...");
|
|
1457
1457
|
ctx.ui.setWorkingMessage(); // Restore default
|
|
1458
1458
|
|
|
1459
|
-
// Widget above editor (
|
|
1459
|
+
// Widget above editor (default)
|
|
1460
1460
|
ctx.ui.setWidget("my-widget", ["Line 1", "Line 2"]);
|
|
1461
|
+
// Widget below editor
|
|
1462
|
+
ctx.ui.setWidget("my-widget", ["Line 1", "Line 2"], { placement: "belowEditor" });
|
|
1461
1463
|
ctx.ui.setWidget("my-widget", (tui, theme) => new Text(theme.fg("accent", "Custom"), 0, 0));
|
|
1462
1464
|
ctx.ui.setWidget("my-widget", undefined); // Clear
|
|
1463
1465
|
|
package/docs/session.md
CHANGED
|
@@ -10,6 +10,12 @@ Sessions are stored as JSONL (JSON Lines) files. Each line is a JSON object with
|
|
|
10
10
|
|
|
11
11
|
Where `<path>` is the working directory with `/` replaced by `-`.
|
|
12
12
|
|
|
13
|
+
## Deleting Sessions
|
|
14
|
+
|
|
15
|
+
Sessions can be removed by deleting their `.jsonl` files under `~/.pi/agent/sessions/`.
|
|
16
|
+
|
|
17
|
+
Pi also supports deleting sessions interactively from `/resume` (select a session and press `Ctrl+D`, then confirm). When available, pi uses the `trash` CLI to avoid permanent deletion.
|
|
18
|
+
|
|
13
19
|
## Session Version
|
|
14
20
|
|
|
15
21
|
Sessions have a version field in the header:
|
package/docs/tui.md
CHANGED
|
@@ -52,6 +52,36 @@ When a `Focusable` component has focus, TUI:
|
|
|
52
52
|
|
|
53
53
|
This enables IME candidate windows to appear at the correct position for CJK input methods. The `Editor` and `Input` built-in components already implement this interface.
|
|
54
54
|
|
|
55
|
+
### Container Components with Embedded Inputs
|
|
56
|
+
|
|
57
|
+
When a container component (dialog, selector, etc.) contains an `Input` or `Editor` child, the container must implement `Focusable` and propagate the focus state to the child. Otherwise, the hardware cursor won't be positioned correctly for IME input.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { Container, type Focusable, Input } from "@mariozechner/pi-tui";
|
|
61
|
+
|
|
62
|
+
class SearchDialog extends Container implements Focusable {
|
|
63
|
+
private searchInput: Input;
|
|
64
|
+
|
|
65
|
+
// Focusable implementation - propagate to child input for IME cursor positioning
|
|
66
|
+
private _focused = false;
|
|
67
|
+
get focused(): boolean {
|
|
68
|
+
return this._focused;
|
|
69
|
+
}
|
|
70
|
+
set focused(value: boolean) {
|
|
71
|
+
this._focused = value;
|
|
72
|
+
this.searchInput.focused = value;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
constructor() {
|
|
76
|
+
super();
|
|
77
|
+
this.searchInput = new Input();
|
|
78
|
+
this.addChild(this.searchInput);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Without this propagation, typing with an IME (Chinese, Japanese, Korean, etc.) will show the candidate window in the wrong position on screen.
|
|
84
|
+
|
|
55
85
|
## Using Components
|
|
56
86
|
|
|
57
87
|
**In hooks** via `ctx.ui.custom()`:
|
|
@@ -674,14 +704,17 @@ ctx.ui.setStatus("my-ext", undefined);
|
|
|
674
704
|
|
|
675
705
|
**Examples:** [status-line.ts](../examples/extensions/status-line.ts), [plan-mode.ts](../examples/extensions/plan-mode.ts), [preset.ts](../examples/extensions/preset.ts)
|
|
676
706
|
|
|
677
|
-
### Pattern 5:
|
|
707
|
+
### Pattern 5: Widgets Above/Below Editor
|
|
678
708
|
|
|
679
|
-
Show persistent content above the input editor. Good for todo lists, progress.
|
|
709
|
+
Show persistent content above or below the input editor. Good for todo lists, progress.
|
|
680
710
|
|
|
681
711
|
```typescript
|
|
682
|
-
// Simple string array
|
|
712
|
+
// Simple string array (above editor by default)
|
|
683
713
|
ctx.ui.setWidget("my-widget", ["Line 1", "Line 2"]);
|
|
684
714
|
|
|
715
|
+
// Render below the editor
|
|
716
|
+
ctx.ui.setWidget("my-widget", ["Line 1", "Line 2"], { placement: "belowEditor" });
|
|
717
|
+
|
|
685
718
|
// Or with theme
|
|
686
719
|
ctx.ui.setWidget("my-widget", (_tui, theme) => {
|
|
687
720
|
const lines = items.map((item, i) =>
|
|
@@ -47,6 +47,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
|
|
|
47
47
|
| `handoff.ts` | Transfer context to a new focused session via `/handoff <goal>` |
|
|
48
48
|
| `qna.ts` | Extracts questions from last response into editor via `ctx.ui.setEditorText()` |
|
|
49
49
|
| `status-line.ts` | Shows turn progress in footer via `ctx.ui.setStatus()` with themed colors |
|
|
50
|
+
| `widget-placement.ts` | Shows widgets above and below the editor via `ctx.ui.setWidget()` placement |
|
|
50
51
|
| `model-status.ts` | Shows model changes in status bar via `model_select` hook |
|
|
51
52
|
| `snake.ts` | Snake game with custom UI, keyboard handling, and session persistence |
|
|
52
53
|
| `send-user-message.ts` | Demonstrates `pi.sendUserMessage()` for sending user messages from extensions |
|
|
@@ -22,28 +22,17 @@ interface QuestionDetails {
|
|
|
22
22
|
wasCustom?: boolean;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
//
|
|
26
|
-
const OptionSchema = Type.
|
|
27
|
-
Type.String(),
|
|
28
|
-
Type.
|
|
29
|
-
|
|
30
|
-
description: Type.Optional(Type.String({ description: "Optional description shown below label" })),
|
|
31
|
-
}),
|
|
32
|
-
]);
|
|
25
|
+
// Options with labels and optional descriptions
|
|
26
|
+
const OptionSchema = Type.Object({
|
|
27
|
+
label: Type.String({ description: "Display label for the option" }),
|
|
28
|
+
description: Type.Optional(Type.String({ description: "Optional description shown below label" })),
|
|
29
|
+
});
|
|
33
30
|
|
|
34
31
|
const QuestionParams = Type.Object({
|
|
35
32
|
question: Type.String({ description: "The question to ask the user" }),
|
|
36
33
|
options: Type.Array(OptionSchema, { description: "Options for the user to choose from" }),
|
|
37
34
|
});
|
|
38
35
|
|
|
39
|
-
// Normalize option to { label, description? }
|
|
40
|
-
function normalizeOption(opt: string | { label: string; description?: string }): OptionWithDesc {
|
|
41
|
-
if (typeof opt === "string") {
|
|
42
|
-
return { label: opt };
|
|
43
|
-
}
|
|
44
|
-
return opt;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
36
|
export default function question(pi: ExtensionAPI) {
|
|
48
37
|
pi.registerTool({
|
|
49
38
|
name: "question",
|
|
@@ -57,7 +46,7 @@ export default function question(pi: ExtensionAPI) {
|
|
|
57
46
|
content: [{ type: "text", text: "Error: UI not available (running in non-interactive mode)" }],
|
|
58
47
|
details: {
|
|
59
48
|
question: params.question,
|
|
60
|
-
options: params.options.map((o) =>
|
|
49
|
+
options: params.options.map((o) => o.label),
|
|
61
50
|
answer: null,
|
|
62
51
|
} as QuestionDetails,
|
|
63
52
|
};
|
|
@@ -70,9 +59,7 @@ export default function question(pi: ExtensionAPI) {
|
|
|
70
59
|
};
|
|
71
60
|
}
|
|
72
61
|
|
|
73
|
-
|
|
74
|
-
const normalizedOptions = params.options.map(normalizeOption);
|
|
75
|
-
const allOptions: DisplayOption[] = [...normalizedOptions, { label: "Type something.", isOther: true }];
|
|
62
|
+
const allOptions: DisplayOption[] = [...params.options, { label: "Type something.", isOther: true }];
|
|
76
63
|
|
|
77
64
|
const result = await ctx.ui.custom<{ answer: string; wasCustom: boolean; index?: number } | null>(
|
|
78
65
|
(tui, theme, _kb, done) => {
|
|
@@ -209,7 +196,7 @@ export default function question(pi: ExtensionAPI) {
|
|
|
209
196
|
);
|
|
210
197
|
|
|
211
198
|
// Build simple options list for details
|
|
212
|
-
const simpleOptions =
|
|
199
|
+
const simpleOptions = params.options.map((o) => o.label);
|
|
213
200
|
|
|
214
201
|
if (!result) {
|
|
215
202
|
return {
|
|
@@ -244,7 +231,7 @@ export default function question(pi: ExtensionAPI) {
|
|
|
244
231
|
let text = theme.fg("toolTitle", theme.bold("question ")) + theme.fg("muted", args.question);
|
|
245
232
|
const opts = Array.isArray(args.options) ? args.options : [];
|
|
246
233
|
if (opts.length) {
|
|
247
|
-
const labels = opts.map((o:
|
|
234
|
+
const labels = opts.map((o: OptionWithDesc) => o.label);
|
|
248
235
|
const numbered = [...labels, "Type something."].map((o, i) => `${i + 1}. ${o}`);
|
|
249
236
|
text += `\n${theme.fg("dim", ` Options: ${numbered.join(", ")}`)}`;
|
|
250
237
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
const applyWidgets = (ctx: ExtensionContext) => {
|
|
4
|
+
if (!ctx.hasUI) return;
|
|
5
|
+
ctx.ui.setWidget("widget-above", ["Above editor widget"]);
|
|
6
|
+
ctx.ui.setWidget("widget-below", ["Below editor widget"], { placement: "belowEditor" });
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default function widgetPlacementExtension(pi: ExtensionAPI) {
|
|
10
|
+
pi.on("session_start", (_event, ctx) => {
|
|
11
|
+
applyWidgets(ctx);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
pi.on("session_switch", (_event, ctx) => {
|
|
15
|
+
applyWidgets(ctx);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-with-deps",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-with-deps",
|
|
9
|
-
"version": "1.13.
|
|
9
|
+
"version": "1.13.2",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"ms": "^2.1.3"
|
|
12
12
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mariozechner/pi-coding-agent",
|
|
3
|
-
"version": "0.49.
|
|
3
|
+
"version": "0.49.2",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
"build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js && npm run copy-assets",
|
|
34
34
|
"build:binary": "npm run build && bun build --compile ./dist/cli.js --outfile dist/pi && npm run copy-binary-assets",
|
|
35
35
|
"copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
|
|
36
|
-
"copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/",
|
|
36
|
+
"copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/",
|
|
37
37
|
"test": "vitest --run",
|
|
38
38
|
"prepublishOnly": "npm run clean && npm run build"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@mariozechner/clipboard": "^0.3.0",
|
|
42
42
|
"@mariozechner/jiti": "^2.6.2",
|
|
43
|
-
"@mariozechner/pi-agent-core": "^0.49.
|
|
44
|
-
"@mariozechner/pi-ai": "^0.49.
|
|
45
|
-
"@mariozechner/pi-tui": "^0.49.
|
|
43
|
+
"@mariozechner/pi-agent-core": "^0.49.2",
|
|
44
|
+
"@mariozechner/pi-ai": "^0.49.2",
|
|
45
|
+
"@mariozechner/pi-tui": "^0.49.2",
|
|
46
46
|
"@silvia-odwyer/photon-node": "^0.3.4",
|
|
47
47
|
"chalk": "^5.5.0",
|
|
48
48
|
"cli-highlight": "^2.1.11",
|