@tduyng/pi-shell 0.0.2 → 0.0.4

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.
Files changed (2) hide show
  1. package/README.md +28 -46
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,62 +1,39 @@
1
1
  # @tduyng/pi-shell
2
2
 
3
- Background shell execution for pi. One `bg` tool covers the full lifecycle: start, read output, kill, and list tasks.
3
+ 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.
4
4
 
5
- ## The bg tool
5
+ 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.
6
6
 
7
- A single tool with an `action` field. Each `bg` task runs in the current working directory with a fresh shell, like pi's own bash. There is no persistent shell session.
7
+ ## The tool
8
8
 
9
- ### start
10
-
11
- Spawns a detached command. Returns `task_id` (8-char UUID prefix), `pid`, and the absolute `log` path. stdout and stderr stream to a log file at `<agentDir>/shell-logs/<taskId>.log`. The task runs silently until you ask about it. Optional `timeout` caps the task (max 600 seconds). Default is no timeout.
12
-
13
- ### output
14
-
15
- Reads new bytes since the last read using a byte cursor. The `wait` parameter controls behavior:
16
-
17
- - `"now"`: returns immediately with whatever is available.
18
- - `"next_write"`: parks the agent until the first new byte appears, then wakes it with the new output.
19
- - `"exit"`: parks the agent until the process ends, then delivers the verdict plus the unread tail.
20
-
21
- The cursor never advances on wake reads, so a later `bg` output call still sees every byte from the stored offset onward.
22
-
23
- ### kill
24
-
25
- Sends SIGTERM to the process group, polls every 50ms for up to 1 second, then escalates to SIGKILL. Accepts `task_id`, raw `pid`, or `port` (resolved to a pid via `lsof -ti`).
26
-
27
- ### status
28
-
29
- Lists all tasks with id, pid, state (running, exited, killed), cursor offset, and the first 80 characters of the command.
30
-
31
- ## Honest exits
32
-
33
- Pi 0.99 already reports a signal-killed command as a failure. Its bash backend returns `128 + signal` for a child that died without an exit code, and raises when a command ends with neither an exit code nor a signal. This extension does not override that backend.
34
-
35
- `shell-process.test.ts` runs `kill -9` against pi's own bash tool and asserts a nonzero result in the 128+n family, so a pi release that reintroduces the silent success fails here.
36
-
37
- A `tool_result` handler annotates the two cases where the exit code alone cannot tell the model what happened. An exit 1 from `grep`, `rg`, or `find` means no matches, and a command killed by its timeout did not finish cleanly. Each note stops the model from retrying a search that already succeeded.
38
-
39
- ## Middle-out output view
9
+ ```text
10
+ bg({action: "start", command: "bun run dev"})
11
+ bg({action: "output", task_id: "t1", wait: "next_write"})
12
+ bg({action: "kill", port: 3000})
13
+ bg({action: "status"})
14
+ ```
40
15
 
41
- Large logs are truncated to a head and tail, defaulting to 8KB total, with an exact omitted-byte count in between. The read window per side is 16KB, split on UTF-8 sequence boundaries so characters are never cut mid-byte. The full log stays on disk at the path always shown in results.
16
+ `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.
42
17
 
43
- ## Wake on exit
18
+ One tool with an action field instead of four tools, because four tools would cost four schemas in every request.
44
19
 
45
- Only a task the model parked on (via `wait: "exit"` or `wait: "next_write"`) injects a user message when it finishes. A burst of exits coalesces into one message sharing a byte budget: default 8KB total, minimum 1KB per task. The wake carries the exit verdict, the unread tail, and the full log path per task, so the model gets the result without a read-back.
20
+ ## Cursor reads
46
21
 
47
- The wake fires from the `onExit` callback via `queueMicrotask`, not from polling. If the agent is mid-turn, the wake lands in the follow-up queue and is delivered at the next `agent_settled` boundary.
22
+ `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.
48
23
 
49
- ## Stall check
24
+ The task list is rebuilt from session entries after a reload, so a task whose pid is gone is reported as gone.
50
25
 
51
- A task the model parked on with `wait: "exit"` also gets a silence check. Every 30 seconds it reads the log size. After 10 minutes with no new bytes, the agent is woken once with the elapsed time and the log path. The wake fires again only after output resumes and stops again. No model call runs for the check.
26
+ ## The exit code pi reports wrong
52
27
 
53
- ## Footer indicator
28
+ 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.
54
29
 
55
- The footer shows a dot and the live task count (e.g. "2 bg running") while any background task is active. It clears when the last task exits.
30
+ ## Settings
56
31
 
57
- ## Task lifecycle
32
+ | Variable | Default | Effect |
33
+ | --- | --- | --- |
34
+ | `SHELL` | unset | `0`, `false`, `no` or `off` disables the extension |
58
35
 
59
- Each `bg` task is in-memory only. It dies with the pi process. Persisted session entries record the command, log path, and start time so the model can read the log after a reload. The live process handle is gone. Detached grandchildren that inherit the shell's stdout keep writing to the log after the shell exits. The log stream stays open until both the stdout and stderr pipes close. Ending it on the first close would drop data the second pipe still delivers.
36
+ `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.
60
37
 
61
38
  ## Install
62
39
 
@@ -66,6 +43,11 @@ pi install npm:@tduyng/pi-shell
66
43
 
67
44
  Local checkout: `pi install /path/to/dotfiles/pi/agent/extensions/pi-shell`
68
45
 
69
- ## Configure
46
+ `kill` by port uses `lsof`, which is on macOS and most Linux images. Kill by id or pid needs nothing.
47
+
48
+ ## Limits
70
49
 
71
- - `SHELL`: set to `0`, `false`, `no`, or `off` to disable the extension entirely.
50
+ - A start can set a timeout up to 600 seconds. The default is no timeout.
51
+ - Logs are kept 7 days, with a 512 MB cap across the log directory.
52
+ - The `output` view returns at most 8 KB per call. Use `from_offset` to continue.
53
+ - A wake is sent only when the session is idle, so a process that exits mid-turn shows up at the next settle.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tduyng/pi-shell",
3
- "version": "0.0.2",
4
- "description": "Background processes the agent parks on: one bg tool with start/output/kill/status, cursor reads that never re-read bytes, and a runtime wake-up when a task exits.",
3
+ "version": "0.0.4",
4
+ "description": "One bg tool for background processes, woken on exit, with cursor reads that never re-read bytes.",
5
5
  "keywords": [
6
6
  "pi",
7
7
  "pi-package",