@monotykamary/pi-localterm
localterm <-> pi integration: Kitty graphics for the browser renderer, scrubbing localterm-managed secret env vars from the agent's bash-tool children, and OSC 9 desktop notifications when the agent finishes a long turn.
Package details
Install @monotykamary/pi-localterm from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@monotykamary/pi-localterm- Package
@monotykamary/pi-localterm- Version
0.4.0- Published
- Jul 25, 2026
- Downloads
- 1,118/mo · 360/wk
- Author
- monotykamary
- License
- MIT
- Types
- extension
- Size
- 36.6 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./extensions/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@monotykamary/pi-localterm
A pi extension that integrates localterm with pi. Three features, all inert outside localterm:
- Kitty graphics + OSC 8 links — localterm renders xterm.js with the Kitty graphics and web-links addons loaded, but sets
TERM=xterm-256colorand strips terminal-identity env vars (so Ink TUIs don't probe for a protocol xterm.js lacks). pi-tui therefore reports images/hyperlinks as unsupported. This extension detectsLOCALTERM=1(injected into every localterm PTY) and force-enables those capabilities, so images and links render in the browser. - Secret scrubbing for the agent's bash tool — localterm injects a secret only into the shimmed process's env (pi's), not its parent shell. But pi's bash tool spawns commands with
{ ...process.env }, so without this the agent's commands would inherit every secret pi received. This extension overrides thebashtool with a spawn hook that deletes thepiprocess's localterm-managed secret env vars from each command's child env only — pi's ownprocess.env(and its provider calls) keep them. - Desktop notifications on agent completion — pi's only notification primitive is
ctx.ui.notify, an in-TUI banner invisible once you switch away from the pi tab. localterm already has an OSC 9 (ESC ] 9 ; MESSAGE BEL) → browser desktop-notification pipeline (opt-in via "Desktop alerts" in Settings). The extension writes an OSC 9 onagent_settled, reusing that pipeline so a user who stepped away gets an OS notification only after the agent has no automatic retry, compaction, or queued continuation left. It also coordinates with@monotykamary/pi-retrythrough Pi's shared extension event bus so that extension's delayed hidden retries do not produce intermediate notifications. Threshold-gated (turns ≥ 30 s) so quick back-and-forth doesn't spam a focused user; TUI-mode-guarded sojson/rpc/-pstdout isn't polluted with OSC bytes. Note: emitting OSC 9 also fires localterm'snotificationautomation trigger, so anotification-event automation will fire on agent completion.
Install
From npm (published package):
pi install npm:@monotykamary/pi-localterm
From source (this monorepo, for a one-off run):
git clone https://github.com/monotykamary/localterm
pi -e ./localterm/packages/pi-extension
Clone and add the package to pi's global settings so it loads automatically:
git clone https://github.com/monotykamary/localterm ~/src/localterm
Then edit ~/.pi/agent/settings.json:
{
"packages": ["/Users/you/src/localterm/packages/pi-extension"]
}
Or, for a project-only install, add the same path to <cwd>/.pi/settings.json instead. Then /reload in pi.
The extension auto-activates only inside localterm (LOCALTERM=1); outside localterm it registers nothing and pi behaves exactly as default.
How the scrub works
localterm stores secret policy (names + the env var each exports) in ~/.localterm/secrets.json and per-process wiring (pi → which secret names it receives) in ~/.localterm/processes.json. Only names and env vars — never values (values live in the macOS Keychain). The extension reads those two files to find the env-var names the pi process is wired to, and strips exactly those from each bash-tool child's environment.
Resolution mirrors the shim 1:1: the shim injects the pi process's requestedSecrets (resolved to env vars); the scrub strips exactly that set. The strip set is recomputed on session_start (new / resume / fork / reload), so a policy change is picked up on the next session transition.
This extends localterm's existing least-privilege property — "the shimmed binary sees the key, its parent shell doesn't" — to "pi sees the key, the agent's bash commands don't." It converts silent env inheritance into an explicit, greppable localterm secret get <name> call when an agent's command genuinely needs a key.
What this is not
This is defense-in-depth, not a security boundary. The keys still live in pi's own process.env, so a command the agent generates can recover them via parent-process introspection (ps eww $PPID on macOS, /proc/$PPID/environ on Linux) or by shelling out to security find-generic-password directly (the shim's own resolution path). The scrub stops passive/accidental leakage (env, printenv, a script reading $VAR), not active exfiltration by a prompt-injected or adversarial agent.
For untrusted or unmonitored agents, don't wire secrets to the pi process at all — give pi its provider keys through pi's own config, and run pi in a container/VM/micro-VM with short-lived credentials.
Overriding the bash tool
The scrub overrides pi's built-in bash tool by name (extensions apply after the built-in in pi's tool registry). It reconstructs the tool via createBashToolDefinition and preserves a user's configured shellPath and shellCommandPrefix (read from ~/.pi/agent/settings.json + <cwd>/.pi/settings.json) so the override is behavior-identical to the built-in apart from the env scrub. If another extension also overrides bash, only the first-registered one wins — that's a pi-level constraint.
Requirements
- pi ≥ 0.80.7 (uses
agent_settled, the shared extension event bus, thespawnHooktool option, and thecreateBashToolDefinitionexport). - localterm with
LOCALTERM=1in the PTY environment (v0.7+). The scrub additionally needs localterm's secrets/processes policy files on the same machine.
License
MIT