pi-interactive-shell 0.4.7 → 0.4.9

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 CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  All notable changes to the `pi-interactive-shell` extension will be documented in this file.
4
4
 
5
+ ## [0.4.9] - 2026-01-21
6
+
7
+ ### Fixed
8
+ - **Multi-line command overflow in header** - Commands containing newlines (e.g., long prompts passed via `-f` flag) now properly collapse to a single line in the overlay header instead of overflowing and leaking behind the overlay.
9
+ - **Reason field overflow** - The `reason` field in the hint line is also sanitized to prevent newline overflow.
10
+ - **Session list overflow** - The `/attach` command's session list now sanitizes command and reason fields for proper display.
11
+
12
+ ## [0.4.8] - 2026-01-19
13
+
14
+ ### Changed
15
+ - **node-pty ^1.1.0** - Updated minimum version to 1.1.0 which includes prebuilt binaries for macOS (arm64, x64) and Windows (x64, arm64). No more Xcode or Visual Studio required for installation on these platforms. Linux still requires build tools (`build-essential`, `python3`).
16
+
17
+ ## [0.4.7] - 2026-01-18
18
+
19
+ ### Added
20
+ - **Incremental mode** - New `incremental: true` parameter for server-tracked pagination. Agent calls repeatedly and server tracks position automatically. Returns `hasMore` to indicate when more output is available.
21
+ - **hasMore in offset mode** - Offset pagination now returns `hasMore` field so agents can know when they've finished reading all output.
22
+
23
+ ### Fixed
24
+ - **Session ID leak on user takeover** - In streaming mode, session ID was unregistered but never released when user took over. Now properly releases ID since agent was notified and won't query.
25
+ - **Session ID leak in dispose()** - When overlay was disposed without going through finishWith* methods (error cases), session ID was never released. Now releases ID in all cleanup paths.
26
+
27
+ ### Changed
28
+ - **autoExitOnQuiet now defaults to false** - Sessions stay alive for multi-turn interaction by default. Enable with `handsFree: { autoExitOnQuiet: true }` for fire-and-forget single-task delegations.
29
+ - **Config documentation** - Fixed incorrect config path in README. Config files are `~/.pi/agent/interactive-shell.json` (global) and `.pi/interactive-shell.json` (project), not under `settings.json`. Added full settings table with all options documented.
30
+ - **Detach key** - Changed from double-Escape to Ctrl+Q for more reliable detection.
31
+
5
32
  ## [0.4.6] - 2026-01-18
6
33
 
7
34
  ### Added
package/README.md CHANGED
@@ -1,31 +1,22 @@
1
1
  # Pi Interactive Shell
2
2
 
3
- An extension for [Pi coding agent](https://github.com/badlogic/pi-mono/) that lets Pi run any interactive CLI in a TUI overlay - including other AI agents. Watch Pi drive Claude, Gemini, Codex, Cursor directly from Pi. Take over anytime. Real PTY, full terminal emulation, more token efficient than using tmux.
3
+ An extension for [Pi coding agent](https://github.com/badlogic/pi-mono/) that lets Pi autonomously run interactive CLIs in an observable TUI overlay. Pi controls the subprocess while you watch - take over anytime.
4
+
5
+ https://github.com/user-attachments/assets/76f56ecd-fc12-4d92-a01e-e6ae9ba65ff4
4
6
 
5
7
  ```typescript
6
- interactive_shell({ command: 'agent "fix all the bugs"', mode: "hands-free" })
7
- // Returns immediately with sessionId
8
- // User watches in overlay, you query for status
9
- // Session auto-closes when agent finishes
8
+ interactive_shell({ command: 'vim config.yaml' })
10
9
  ```
11
10
 
12
11
  ## Why
13
12
 
14
- AI agents delegating to other AI agents is powerful but messy:
15
-
16
- - **Visibility** - What's the subagent doing? Is it stuck?
17
- - **Control** - User needs to intervene. How?
18
- - **Integration** - When does the parent agent check in?
19
-
20
- Interactive Shell solves all three:
21
-
22
- **Real-Time Overlay** - User sees the subprocess in a TUI overlay. Full terminal emulation via xterm-headless. ANSI colors, cursor movement, everything.
23
-
24
- **Seamless Takeover** - Type anything to take control. Scroll with Shift+Up/Down. Double-Escape to detach.
13
+ Some tasks need interactive CLIs - editors, REPLs, database shells, long-running processes. Pi can launch them in an overlay where:
25
14
 
26
- **Non-Blocking API** - Start a session, get a sessionId, query for status. Rate-limited to prevent spam. Auto-exits when output stops.
15
+ - **User watches** - See exactly what's happening in real-time
16
+ - **User takes over** - Type anything to gain control
17
+ - **Agent monitors** - Query status, send input, decide when done
27
18
 
28
- **Any CLI** - Not just AI agents. Run `htop`, `vim`, `psql`, `ssh` - anything interactive.
19
+ Works with any CLI: `vim`, `htop`, `psql`, `ssh`, `docker logs -f`, `npm run dev`, `git rebase -i`, etc.
29
20
 
30
21
  ## Install
31
22
 
@@ -35,17 +26,32 @@ npx pi-interactive-shell
35
26
 
36
27
  Installs to `~/.pi/agent/extensions/interactive-shell/`.
37
28
 
29
+ The `interactive-shell` skill is automatically symlinked to `~/.pi/agent/skills/interactive-shell/`.
30
+
38
31
  **Requires:** Node.js, build tools for `node-pty` (Xcode CLI tools on macOS).
39
32
 
40
33
  ## Quick Start
41
34
 
42
- ### Hands-Free (Agent-to-Agent)
35
+ ### Interactive Mode
36
+
37
+ User controls the session directly:
38
+
39
+ ```typescript
40
+ interactive_shell({ command: 'vim package.json' })
41
+ interactive_shell({ command: 'psql -d mydb' })
42
+ interactive_shell({ command: 'ssh user@server' })
43
+ ```
44
+
45
+ ### Hands-Free Mode
46
+
47
+ Agent monitors while user watches. Returns immediately with sessionId:
43
48
 
44
49
  ```typescript
45
- // Start - returns immediately
50
+ // Start a long-running process
46
51
  interactive_shell({
47
- command: 'pi "Refactor the auth module"',
48
- mode: "hands-free"
52
+ command: 'npm run dev',
53
+ mode: "hands-free",
54
+ reason: "Dev server"
49
55
  })
50
56
  // → { sessionId: "calm-reef", status: "running" }
51
57
 
@@ -53,53 +59,57 @@ interactive_shell({
53
59
  interactive_shell({ sessionId: "calm-reef" })
54
60
  // → { status: "running", output: "...", runtime: 45000 }
55
61
 
56
- // Get more output
57
- interactive_shell({ sessionId: "calm-reef", outputLines: 100 })
62
+ // Send input if needed
63
+ interactive_shell({ sessionId: "calm-reef", input: { keys: ["ctrl+c"] } })
58
64
 
59
- // Kill when done (or let autoExitOnQuiet handle it)
65
+ // Kill when done
60
66
  interactive_shell({ sessionId: "calm-reef", kill: true })
61
67
  ```
62
68
 
63
- ### Interactive (User Control)
69
+ User sees the overlay in real-time. Type anything to take over control.
64
70
 
65
- ```typescript
66
- interactive_shell({ command: 'vim package.json' })
67
- ```
71
+ ### Timeout Mode
68
72
 
69
- ### Send Input
73
+ Capture output from TUI apps that don't exit cleanly:
70
74
 
71
75
  ```typescript
72
- interactive_shell({ sessionId: "calm-reef", input: "/help\n" })
73
- interactive_shell({ sessionId: "calm-reef", input: { keys: ["ctrl+c"] } })
74
- interactive_shell({ sessionId: "calm-reef", input: { keys: ["down", "down", "enter"] } })
76
+ interactive_shell({
77
+ command: "htop",
78
+ mode: "hands-free",
79
+ timeout: 3000 // Kill after 3s, return captured output
80
+ })
75
81
  ```
76
82
 
77
- ## CLI Reference
78
-
79
- | Agent | Interactive | With Prompt | Headless (use bash) |
80
- |-------|-------------|-------------|---------------------|
81
- | `claude` | `claude` | `claude "prompt"` | `claude -p "prompt"` |
82
- | `gemini` | `gemini` | `gemini -i "prompt"` | `gemini "prompt"` |
83
- | `codex` | `codex` | `codex "prompt"` | `codex exec "prompt"` |
84
- | `agent` | `agent` | `agent "prompt"` | `agent -p "prompt"` |
85
- | `pi` | `pi` | `pi "prompt"` | `pi -p "prompt"` |
86
-
87
83
  ## Features
88
84
 
89
85
  ### Auto-Exit on Quiet
90
86
 
91
- Sessions auto-close after 5s of silence. Disable with `handsFree: { autoExitOnQuiet: false }`.
92
-
93
- ### Timeout for TUI Capture
87
+ For fire-and-forget single-task delegations, enable auto-exit to kill the session after 5s of output silence:
94
88
 
95
89
  ```typescript
96
90
  interactive_shell({
97
- command: "pi --help",
91
+ command: 'cursor-agent -f "Fix the bug in auth.ts"',
98
92
  mode: "hands-free",
99
- timeout: 5000 // Kill after 5s, return captured output
93
+ handsFree: { autoExitOnQuiet: true }
100
94
  })
101
95
  ```
102
96
 
97
+ For multi-turn sessions where you need back-and-forth interaction, leave it disabled (default) and use `kill: true` when done.
98
+
99
+ ### Send Input
100
+
101
+ ```typescript
102
+ // Text
103
+ interactive_shell({ sessionId: "calm-reef", input: "SELECT * FROM users;\n" })
104
+
105
+ // Named keys
106
+ interactive_shell({ sessionId: "calm-reef", input: { keys: ["ctrl+c"] } })
107
+ interactive_shell({ sessionId: "calm-reef", input: { keys: ["down", "down", "enter"] } })
108
+
109
+ // Bracketed paste (multiline without execution)
110
+ interactive_shell({ sessionId: "calm-reef", input: { paste: "line1\nline2\nline3" } })
111
+ ```
112
+
103
113
  ### Configurable Output
104
114
 
105
115
  ```typescript
@@ -109,77 +119,67 @@ interactive_shell({ sessionId: "calm-reef" })
109
119
  // More lines (max: 200)
110
120
  interactive_shell({ sessionId: "calm-reef", outputLines: 100 })
111
121
 
112
- // More content (max: 50KB)
113
- interactive_shell({ sessionId: "calm-reef", outputMaxChars: 30000 })
114
- ```
122
+ // Incremental pagination (server tracks position)
123
+ interactive_shell({ sessionId: "calm-reef", outputLines: 50, incremental: true })
115
124
 
116
- ### Input Methods
117
-
118
- | Method | Example |
119
- |--------|---------|
120
- | Text | `input: "/model\n"` |
121
- | Keys | `input: { keys: ["enter", "ctrl+c"] }` |
122
- | Hex | `input: { hex: ["0x1b", "0x5b", "0x41"] }` |
123
- | Paste | `input: { paste: "multi\nline" }` |
125
+ // Drain mode (raw stream since last query)
126
+ interactive_shell({ sessionId: "calm-reef", drain: true })
127
+ ```
124
128
 
125
129
  ### Background Sessions
126
130
 
127
- 1. Double-Escape → "Run in background"
131
+ 1. Ctrl+Q → "Run in background"
128
132
  2. `/attach` or `/attach <id>` to reattach
129
133
 
130
- ## Config
131
-
132
- `~/.pi/agent/interactive-shell.json`
133
-
134
- ```json
135
- {
136
- "overlayHeightPercent": 45,
137
- "overlayWidthPercent": 95,
138
- "scrollbackLines": 5000,
139
- "minQueryIntervalSeconds": 60,
140
- "handsFreeQuietThreshold": 5000
141
- }
142
- ```
143
-
144
134
  ## Keys
145
135
 
146
136
  | Key | Action |
147
137
  |-----|--------|
148
- | Double-Escape | Detach dialog |
138
+ | Ctrl+Q | Detach dialog |
149
139
  | Shift+Up/Down | Scroll history |
150
140
  | Any key (hands-free) | Take over control |
151
141
 
152
- ## Token Efficiency
153
-
154
- Unlike the standard tmux workflow where you `capture-pane` the entire terminal on every poll, Interactive Shell minimizes token waste:
155
-
156
- **Incremental Aggregation** - Output is accumulated as it arrives, not re-captured on each query.
157
-
158
- **Tail by Default** - Status queries return only the last 20 lines (configurable), not the full history.
159
-
160
- **ANSI Stripping** - All escape codes are stripped before sending output to the agent. Clean text only.
142
+ ## Config
161
143
 
162
- **Drain Mode** - Use `drain: true` to get only NEW output since last query. No re-reading old content.
144
+ Configuration files (project overrides global):
145
+ - **Global:** `~/.pi/agent/interactive-shell.json`
146
+ - **Project:** `.pi/interactive-shell.json`
163
147
 
164
- ```typescript
165
- // First query: get recent output
166
- interactive_shell({ sessionId: "calm-reef" })
167
- // → returns last 20 lines
168
-
169
- // Subsequent queries: get only new output (incremental)
170
- interactive_shell({ sessionId: "calm-reef", drain: true })
171
- // → returns only output since last query
148
+ ```json
149
+ {
150
+ "overlayWidthPercent": 95,
151
+ "overlayHeightPercent": 45,
152
+ "scrollbackLines": 5000,
153
+ "exitAutoCloseDelay": 10,
154
+ "minQueryIntervalSeconds": 60,
155
+ "handsFreeUpdateMode": "on-quiet",
156
+ "handsFreeUpdateInterval": 60000,
157
+ "handsFreeQuietThreshold": 5000,
158
+ "handsFreeUpdateMaxChars": 1500,
159
+ "handsFreeMaxTotalChars": 100000,
160
+ "handoffPreviewEnabled": true,
161
+ "handoffPreviewLines": 30,
162
+ "handoffPreviewMaxChars": 2000,
163
+ "handoffSnapshotEnabled": false,
164
+ "ansiReemit": true
165
+ }
172
166
  ```
173
167
 
174
- **Offset/Limit Pagination** - Read specific ranges of the full output log.
175
-
176
- ```typescript
177
- // Read lines 0-49
178
- interactive_shell({ sessionId: "calm-reef", outputOffset: 0, outputLines: 50 })
179
-
180
- // Read lines 50-99
181
- interactive_shell({ sessionId: "calm-reef", outputOffset: 50, outputLines: 50 })
182
- ```
168
+ | Setting | Default | Description |
169
+ |---------|---------|-------------|
170
+ | `overlayWidthPercent` | 95 | Overlay width (10-100%) |
171
+ | `overlayHeightPercent` | 45 | Overlay height (20-90%) |
172
+ | `scrollbackLines` | 5000 | Terminal scrollback buffer |
173
+ | `exitAutoCloseDelay` | 10 | Seconds before auto-close after exit |
174
+ | `minQueryIntervalSeconds` | 60 | Rate limit between agent queries |
175
+ | `handsFreeUpdateMode` | "on-quiet" | "on-quiet" or "interval" |
176
+ | `handsFreeQuietThreshold` | 5000 | Silence duration before update (ms) |
177
+ | `handsFreeUpdateInterval` | 60000 | Max interval between updates (ms) |
178
+ | `handsFreeUpdateMaxChars` | 1500 | Max chars per update |
179
+ | `handsFreeMaxTotalChars` | 100000 | Total char budget for updates |
180
+ | `handoffPreviewEnabled` | true | Include tail in tool result |
181
+ | `handoffSnapshotEnabled` | false | Write transcript on detach/exit |
182
+ | `ansiReemit` | true | Preserve ANSI colors in output |
183
183
 
184
184
  ## How It Works
185
185
 
@@ -193,6 +193,12 @@ interactive_shell → node-pty → subprocess
193
193
 
194
194
  Full PTY. The subprocess thinks it's in a real terminal.
195
195
 
196
+ ## Advanced: Multi-Agent Workflows
197
+
198
+ For orchestrating multi-agent chains (scout → planner → worker → reviewer) with file-based handoff and auto-continue support, see:
199
+
200
+ **[pi-foreground-chains](https://github.com/nicobailon/pi-foreground-chains)** - A separate skill that builds on interactive-shell for complex agent workflows.
201
+
196
202
  ## Limitations
197
203
 
198
204
  - macOS tested, Linux experimental
package/SKILL.md CHANGED
@@ -97,8 +97,8 @@ const result = interactive_shell({
97
97
  command: 'codex "Review this codebase"',
98
98
  mode: "hands-free"
99
99
  })
100
- // result.sessionId = "calm-reef"
101
- // result.status = "running"
100
+ // result.details.sessionId = "calm-reef"
101
+ // result.details.status = "running"
102
102
  ```
103
103
 
104
104
  The user sees the overlay immediately. You get control back to continue working.
@@ -122,21 +122,41 @@ interactive_shell({ sessionId: "calm-reef", kill: true })
122
122
 
123
123
  Kill when you see the task is complete in the output. Returns final status and output.
124
124
 
125
- **Auto-exit (default: enabled):** In hands-free mode, sessions auto-kill when output stops (after ~5 seconds of quiet). This means when an agent finishes its task and returns to its prompt, the session closes automatically.
125
+ ### Fire-and-Forget Tasks
126
126
 
127
- Since sessions auto-close, **always instruct the subagent to save results to a file** so you can read them:
127
+ For single-task delegations where you don't need multi-turn interaction, enable auto-exit so the session kills itself when the agent goes quiet:
128
128
 
129
129
  ```typescript
130
130
  interactive_shell({
131
131
  command: 'pi "Review this codebase for security issues. Save your findings to /tmp/security-review.md"',
132
132
  mode: "hands-free",
133
- reason: "Security review"
133
+ reason: "Security review",
134
+ handsFree: { autoExitOnQuiet: true }
134
135
  })
135
- // After session ends, read the results:
136
+ // Session auto-kills after ~5s of quiet
137
+ // Read results from file:
136
138
  // read("/tmp/security-review.md")
137
139
  ```
138
140
 
139
- To disable auto-exit (for long-running tasks or when you need to review output): `handsFree: { autoExitOnQuiet: false }`
141
+ **Instruct subagent to save results to a file** since the session closes automatically.
142
+
143
+ ### Multi-Turn Sessions (default)
144
+
145
+ For back-and-forth interaction, leave auto-exit disabled (the default). Query status and kill manually when done:
146
+
147
+ ```typescript
148
+ interactive_shell({
149
+ command: 'cursor-agent -f',
150
+ mode: "hands-free",
151
+ reason: "Interactive refactoring"
152
+ })
153
+
154
+ // Send follow-up prompts
155
+ interactive_shell({ sessionId: "calm-reef", input: "Now fix the tests\n" })
156
+
157
+ // Kill when done
158
+ interactive_shell({ sessionId: "calm-reef", kill: true })
159
+ ```
140
160
 
141
161
  ### Sending Input
142
162
  ```typescript
@@ -159,19 +179,32 @@ interactive_shell({ sessionId: "calm-reef", outputLines: 50 })
159
179
  interactive_shell({ sessionId: "calm-reef", outputLines: 100, outputMaxChars: 30000 })
160
180
  ```
161
181
 
162
- ### Reviewing Long Sessions (autoExitOnQuiet disabled)
182
+ ### Incremental Reading
163
183
 
164
- When you disable auto-exit for long-running tasks, progressively review more output as needed:
184
+ Use `incremental: true` to paginate through output without re-reading:
165
185
 
166
186
  ```typescript
167
- // Start a long session without auto-exit
168
- interactive_shell({
169
- command: 'pi "Refactor entire codebase"',
170
- mode: "hands-free",
171
- handsFree: { autoExitOnQuiet: false }
172
- })
187
+ // First call: get first 50 lines
188
+ interactive_shell({ sessionId: "calm-reef", outputLines: 50, incremental: true })
189
+ // { output: "...", hasMore: true }
190
+
191
+ // Next call: get next 50 lines (server tracks position)
192
+ interactive_shell({ sessionId: "calm-reef", outputLines: 50, incremental: true })
193
+ // → { output: "...", hasMore: true }
194
+
195
+ // Keep calling until hasMore: false
196
+ interactive_shell({ sessionId: "calm-reef", outputLines: 50, incremental: true })
197
+ // → { output: "...", hasMore: false }
198
+ ```
199
+
200
+ The server tracks your read position - just keep calling with `incremental: true` to get the next chunk.
201
+
202
+ ### Reviewing Output
173
203
 
174
- // Query returns last 20 lines by default
204
+ Query sessions to see progress. Increase limits when you need more context:
205
+
206
+ ```typescript
207
+ // Default: last 20 lines
175
208
  interactive_shell({ sessionId: "calm-reef" })
176
209
 
177
210
  // Get more lines when you need more context
package/config.ts CHANGED
@@ -3,7 +3,6 @@ import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
 
5
5
  export interface InteractiveShellConfig {
6
- doubleEscapeThreshold: number;
7
6
  exitAutoCloseDelay: number;
8
7
  overlayWidthPercent: number;
9
8
  overlayHeightPercent: number;
@@ -26,7 +25,6 @@ export interface InteractiveShellConfig {
26
25
  }
27
26
 
28
27
  const DEFAULT_CONFIG: InteractiveShellConfig = {
29
- doubleEscapeThreshold: 300,
30
28
  exitAutoCloseDelay: 10,
31
29
  overlayWidthPercent: 95,
32
30
  overlayHeightPercent: 45,