@tduyng/pi-shell
One bg tool for background processes, woken on exit, with cursor reads that never re-read bytes.
Package details
Install @tduyng/pi-shell from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@tduyng/pi-shell- Package
@tduyng/pi-shell- Version
0.0.4- Published
- Nov 5, 2026
- Downloads
- 472/mo · 472/wk
- Author
- tienduy-nguyen
- License
- Apache-2.0
- Types
- extension
- Size
- 45.3 KB
- Dependencies
- 1 dependency · 2 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@tduyng/pi-shell
Background processes with one tool: start, read new bytes, kill, list. The turn moves on, and an exiting process wakes the session instead of being polled.
A foreground command blocks the turn. & plus sleep is a guess, and the log has to be read from the top to find one line.
The tool
bg({action: "start", command: "bun run dev"})
bg({action: "output", task_id: "t1", wait: "next_write"})
bg({action: "kill", port: 3000})
bg({action: "status"})
output waits three ways: now returns what is there, next_write returns on the next line written, and exit returns when the process ends. kill takes a task_id, a pid, or a port, whichever is known. Logs land in <agent-dir>/shell-logs/, and the start result names the path.
One tool with an action field instead of four tools, because four tools would cost four schemas in every request.
Cursor reads
output takes a byte offset and returns only new bytes. A build that prints 200,000 lines costs the 40 that matter, and a line already read is never re-billed.
The task list is rebuilt from session entries after a reload, so a task whose pid is gone is reported as gone.
The exit code pi reports wrong
A tool_result handler patches pi's silent signal-death bug. A child killed by a signal reports code === null, which pi reads as success, so a command that was killed looks like a command that passed. The handler reports 128+n and adds a note for a benign exit.
Settings
| Variable | Default | Effect |
|---|---|---|
SHELL |
unset | 0, false, no or off disables the extension |
SHELL is the variable a login shell sets to its own path, so the switch only fires on the four falsey spellings. The name is a compromise: any other name would break profiles that already set it.
Install
pi install npm:@tduyng/pi-shell
Local checkout: pi install /path/to/dotfiles/pi/agent/extensions/pi-shell
kill by port uses lsof, which is on macOS and most Linux images. Kill by id or pid needs nothing.
Limits
- A start can set a timeout up to 600 seconds. The default is no timeout.
- Logs are kept 7 days, with a 512 MB cap across the log directory.
- The
outputview returns at most 8 KB per call. Usefrom_offsetto continue. - A wake is sent only when the session is idle, so a process that exits mid-turn shows up at the next settle.