@luan.sh/pi-exec-command
Bounded shell execution with persistent PTY sessions for Pi
Package details
Install @luan.sh/pi-exec-command from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@luan.sh/pi-exec-command- Package
@luan.sh/pi-exec-command- Version
0.3.8- Published
- Sep 12, 2026
- Downloads
- not available
- Author
- cfcluan
- License
- MIT
- Types
- extension
- Size
- 27.8 MB
- Dependencies
- 4 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./src/extension.ts",
"./node_modules/@luan.sh/pi-libtui/src/extension.ts"
],
"image": "https://pi.luan.sh/media/previews/pi-exec-command-bdc338239909.png",
"video": "https://pi.luan.sh/media/previews/pi-exec-command-00eaa4fd94c5.mp4"
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@luan.sh/pi-exec-command
Bounded shell execution for Pi with persistent PTY sessions, backed by the
native terminal-bridge binary. exec_command starts a shell command and
returns output or a session ID; write_stdin sends input to, or polls, a
running command. A Process Hub lists the commands owned by the current agent
and its subagents, shows their bounded output, attaches to retained PTY state,
forwards terminal input, and exposes interrupt and terminate actions.
Preview

Install
pi install npm:@luan.sh/pi-exec-command
Requires a Rust toolchain (https://rustup.rs). The terminal-bridge binary
builds itself on first use under Pi's agent directory
(native/terminal-bridge/<version>/). Set PI_TERMINAL_BRIDGE_BINARY to use
a prebuilt binary; it must point to an executable file.
Optional companions:
pi install npm:@luan.sh/pi-xsettingsadds a/xsettingsUI for the settings below; without it the compiled defaults apply.pi install npm:@luan.sh/pi-panelslets the Process Hub open as a side panel tab; without it the hub opens as a fullscreen overlay.pi install npm:@luan.sh/pi-subagentslets the hub include processes owned by descendant agents; without it the hub shows only the current session.
exec_command
cmd is required. Optional arguments:
| Argument | Behavior |
|---|---|
workdir |
Working directory, resolved relative to the current Pi session directory. It does not persist to later calls. |
shell |
Shell executable. Defaults to Pi's configured shell, then $SHELL. Fish is replaced by zsh/bash/sh (macOS prefers /bin/zsh, other platforms /bin/bash) because exec_command accepts POSIX shell command grammar. On Windows, Git Bash is located under Program Files or %LOCALAPPDATA%. |
tty |
Allocate a PTY and keep stdin open. Default false. |
yield_time_ms |
How long to wait for the first result. Clamped to 250–30,000 ms; default 10,000 ms. |
max_output_tokens |
Approximate output limit (four characters per token). The newest output is kept when exceeded. Default 10,000. |
login |
Use login-shell arguments on POSIX shells. Default true. |
command and cwd are accepted as aliases for cmd and workdir.
POSIX shells run as shell -lc command when login is true and shell -c command otherwise. cmd.exe uses /d /s /c; PowerShell and pwsh use
-NoLogo -NoProfile -Command.
For a pipe command, stdout and stderr are combined and terminal control
sequences are removed. PTY output is kept as received so interactive programs
work. While a call waits, new output is published as bounded partial results:
a syntax-colored $ command header, a live indicator, right-aligned metadata,
and the newest output rows, with older rows behind a disclosure control.
tty: true renders through a terminal projection, so carriage-return progress,
cursor motion, erases, colors, and wide glyphs display as terminal state rather
than raw control bytes. The final result replaces the preview.
Commands that only read, list, or search (cat, sed -n, head, ls,
tree, rg, grep, fd, find, git grep, and similar, including
cd/bash -c prefixes and pipelines through formatting stages) render as
Exploring/Explored with one row per step (Read app.rs, lib.rs,
Search query in path, List path). Commands with output redirection,
in-place edits, -exec/-delete, control flow, or substitutions render as a
plain command.
If the command exits during the wait, the result includes its exit code.
Otherwise it includes a numeric session_id:
{ "cmd": "python -m http.server 8000", "tty": true, "yield_time_ms": 1000 }
Use that ID with write_stdin:
{ "session_id": 1, "chars": "\u0003", "yield_time_ms": 1000 }
Every result also reports elapsed wall time, a chunk ID, output_truncated,
and the approximate original token count. A single call never waits longer
than 30 seconds, even for a command that keeps producing output.
write_stdin
session_id is required. chars is optional:
- Omit
chars, or pass an empty string, to poll for output. - Input is only accepted by sessions started with
tty: true; pipe sessions have no writable stdin. - A write waits 250 ms by default. An empty poll starts at 30 seconds and
backs off up to five minutes while the process stays active. Pass
yield_time_msto choose a wait within those bounds. - Polling a completed session replays its retained output. Up to 32 completed sessions and 64 KiB of output per session are retained.
- Writing to a completed or unknown session fails. At most 64 sessions may be active at once.
Successful write_stdin calls are transcript-silent: their output stays
model-visible and the live terminal state stays available in the Process Hub.
Failures still render. Aborting a call or shutting down the extension
terminates the command's process group and clears all sessions; the bridge
process is reaped only after its final output has been read.
Process Hub
Open the hub with the /ps command or the processes.open action. Inside a
side-panel host, Processes also appears as an empty-panel action. The hub
aggregates the exec sessions of the current agent and its descendants; it does
not infer processes from transcript text or own process lifetime, and the
native bridge stays authoritative for input, resize, interrupt, termination,
exit, and reaping.
The original exec_command transcript row stays live. When it is offscreen, a
compact above-editor widget and a processes status item show the running
processes; both disappear when none remain. Clicking a process in the widget
opens it in the hub.
Process list keys:
j/k, arrows,ctrl+u/ctrl+d, page keys,home/end,gg/Gmove;enteropens pipe output or attaches to a PTY;isendsSIGINTto the process group;xterminates the process group;alt+s,q, orescapecloses the hub.
Pipe output view: j/k scroll, ctrl+u/ctrl+d page, G follows the
tail, i/x interrupt/terminate, and ctrl+], escape, q, h, or left
return to the list. PTY view: input is forwarded to the terminal, the native
PTY is resized to the visible viewport (capped at 500x200), and ctrl+]
returns to the list. Completed processes follow the same 32-session retention
as write_stdin. Embedded terminals advertise TERM=xterm-256color and
COLORTERM=truecolor even when Pi was launched from a reduced environment.
Keybindings
processes.open has no default key. Bind it in Pi's agent directory, normally
~/.pi/agent/keybindings.json, where each property is an action ID and each
value is a key ID string or an array of them:
{
"processes.open": "alt+s"
}
Settings
Settings live in the @luan.sh/pi-exec-command namespace and are edited via
/xsettings when @luan.sh/pi-xsettings is installed; otherwise the defaults
apply. Changes apply live and republish the tool definitions.
| Key | Default | Choices |
|---|---|---|
defaultOutputTokens |
10000 |
1000, 2500, 5000, 10000, 20000, 50000, 100000 |
defaultExecYieldMs |
10000 |
1000, 5000, 10000, 30000 |
defaultLoginShell |
true |
true, false |
activityIndicator |
"inherit" |
"inherit" or any shared TUI indicator style |
processWidgetIndicator |
"inherit" |
"inherit" or any shared TUI indicator style |
processHubPresentation |
"side-panel" |
"side-panel", "fullscreen" |
activityIndicator overrides the indicator on running exec_command rows and
takes effect for new renderers; processWidgetIndicator updates the compact
widget live. "side-panel" falls back to the fullscreen overlay when no
side-panel host is present.
Layout
| Responsibility | File |
|---|---|
Pi registration, lifecycle, /ps command |
src/extension.ts |
| Tool schemas and calls | src/tools/exec-command/, src/tools/write-stdin/ |
| Sessions, waits, limits, replay, process snapshots | src/session-manager.ts |
| Shell resolution and Fish fallback | src/runtime-shell.ts |
| Output bounding and control-sequence stripping | src/output.ts |
| Result/details contract | src/tools/result.ts, src/tools/presentation.ts |
| Read/list/search classification | src/core/shell-summary.ts |
| Transcript rendering | src/ui/presentation.ts, src/ui/command-transcript.ts, src/ui/shell-command-action.ts |
| Process Hub, widget, store | src/ui/process-hub*.ts, src/ui/process-store.ts, src/ui/process-widget.ts |
processes.open action, session hierarchy, settings |
src/contributions/ |
| Code Mode adapters | src/code-mode-adapters.ts |
| Public exports (presentation contract only) | src/index.ts |
Develop
Source: https://github.com/luan/agents, directory
harnesses/pi/agent/packages/pi-exec-command. Run bun run typecheck and
bun test test in that directory.