pi-interactive-shell 0.8.1 → 0.9.0

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,7 +2,33 @@
2
2
 
3
3
  All notable changes to the `pi-interactive-shell` extension will be documented in this file.
4
4
 
5
- ## [Unreleased]
5
+ ## [0.9.0] - 2026-02-23
6
+
7
+ ### Added
8
+ - `examples/skills/codex-5.3-prompting/` skill with GPT-5.3-Codex prompting guide -- self-contained best practices for verbosity control, scope discipline, forced upfront reading, plan mode, mid-task steering, context management, and reasoning effort recommendations.
9
+ - **`interactive-shell:update` event** — All hands-free update callbacks now emit `pi.events.emit("interactive-shell:update", update)` with the full `HandsFreeUpdate` payload. Extensions can listen for quiet, exit, kill, and user-takeover events regardless of which code path started the session (blocking, non-blocking, or reattach).
10
+ - **`triggerTurn` on terminal events** — Non-blocking hands-free sessions now send `pi.sendMessage` with `triggerTurn: true` when the session exits, is killed, or the user takes over. Periodic "running" updates emit only on the event bus (cheap for extensions) without waking the agent.
11
+
12
+ ### Fixed
13
+ - **Quiet detection broken for TUI apps** — Ink-based CLIs (Claude Code, etc.) emit periodic ANSI-only PTY data (cursor blink, frame redraws) that reset the quiet timer on every event, preventing quiet detection from ever triggering. Now filters data through `stripVTControlCharacters` and only resets the quiet timer when there's visible content. Fixed in both the overlay (`overlay-component.ts`) and headless dispatch monitor (`headless-monitor.ts`). Also seeds the quiet timer at startup when `autoExitOnQuiet` is enabled, so sessions that never produce visible output still get killed after the grace period.
14
+ - **Lifecycle guard decoupled from callback** — The overlay used `onHandsFreeUpdate` presence as a proxy for "blocking tool call" to decide whether to unregister sessions on completion. Wiring the callback in non-blocking paths (for event emission) would cause premature session cleanup. Introduced `streamingMode` flag to separate "has update callback" from "should unregister on completion," so non-blocking sessions stay queryable after the callback fires.
15
+ - **`autoExitOnQuiet` broken in interval update mode** — The `onData` handler only reset the quiet timer in `on-quiet` mode, so `autoExitOnQuiet` never fired with `updateMode: "interval"`. Also, the interval timer's safety-net flush unconditionally stopped the quiet timer, preventing `autoExitOnQuiet` from firing if the interval flushed before the quiet threshold. Both fixed: data handler now resets the timer whenever `autoExitOnQuiet` is enabled regardless of update mode, and the interval flush restarts (rather than stops) the quiet timer when `autoExitOnQuiet` is active.
16
+ - **RangeError on narrow terminals** — `render()` computed `width - 2` for border strings without a lower bound, causing `String.prototype.repeat()` to throw with negative counts when terminal width < 4. Clamped in both the main overlay and reattach overlay. Fixes #2.
17
+ - **Hardcoded `~/.pi/agent` path** — Config loading, snapshot writing, and the install script all hardcoded `~/.pi/agent`, ignoring `PI_CODING_AGENT_DIR`. Now uses `getAgentDir()` from pi's API in all runtime paths and reads the env var in the install script. Fixes #1.
18
+
19
+ ### Changed
20
+ - Default `handsFreeQuietThreshold` increased from 5000ms to 8000ms and `autoExitGracePeriod` reduced from 30000ms to 15000ms. Both remain adjustable per-call via `handsFree.quietThreshold` and `handsFree.gracePeriod`, and via config file.
21
+ - Dispatch mode is now the recommended default for delegated Codex runs. Updated `README.md`, `SKILL.md`, `tool-schema.ts`, `examples/skills/codex-cli/SKILL.md`, and all three codex prompt templates to prefer `mode: "dispatch"` over hands-free for fire-and-forget delegations.
22
+ - Rewrote `codex-5.3-prompting` skill from a descriptive model-behavior guide into a directive prompt-construction reference. Cut behavioral comparison, mid-task steering, and context management prose sections; reframed each prompt block with a one-line "include when X" directive so the agent knows what to inject and when.
23
+ - Added "Backwards compatibility hedging" section to `codex-5.3-prompting` skill covering the "cutover" keyword trick -- GPT-5.3-Codex inserts compatibility shims and fallback code even when told not to; using "cutover" + "no backwards compatibility" + "do not preserve legacy code" produces cleaner breaks than vague "don't worry about backwards compatibility" phrasing.
24
+ - Example prompts (`codex-implement-plan`, `codex-review-impl`, `codex-review-plan`) updated for GPT-5.3-Codex: load `codex-5.3-prompting` and `codex-cli` skills instead of fetching the 5.2 guide URL at runtime, added scope fencing instructions to counter 5.3's aggressive refactoring, added "don't ask clarifying questions" and "brief updates" constraints, strengthened `codex-review-plan` to force reading codebase files referenced in the plan and constrain edit scope.
25
+
26
+ ## [0.8.2] - 2026-02-10
27
+
28
+ ### Added
29
+ - `examples/prompts/` with three Codex CLI prompt templates: `codex-review-plan`, `codex-implement-plan`, `codex-review-impl`. Demonstrates a plan → implement → review workflow using meta-prompt generation and interactive shell overlays.
30
+ - `examples/skills/codex-cli/` skill that teaches pi Codex CLI flags, config, sandbox caveats, and interactive_shell usage patterns.
31
+ - README section documenting the workflow pipeline, installation, usage examples, and customization.
6
32
 
7
33
  ## [0.8.1] - 2026-02-08
8
34
 
package/README.md CHANGED
@@ -49,7 +49,7 @@ Three modes control how the agent engages with a session:
49
49
 
50
50
  **Hands-free** returns immediately so the agent can do other work, but the agent must poll periodically to discover output and completion. Good for processes the agent needs to monitor and react to mid-flight, like watching build output and sending follow-up commands.
51
51
 
52
- **Dispatch** also returns immediately, but the agent doesn't poll at all. When the session completes — whether by natural exit, quiet detection, timeout, or user intervention — the agent gets woken up with a notification containing the tail output. This is the right mode for delegating a task to a subagent and moving on. Add `background: true` to skip the overlay entirely and run headless.
52
+ **Dispatch** also returns immediately, but the agent doesn't poll at all. When the session completes — whether by natural exit, quiet detection, timeout, or user intervention — the agent gets woken up with a notification containing the tail output. This is the right mode for delegating a task to a subagent and moving on. For fire-and-forget delegated runs and QA checks, prefer dispatch by default. Add `background: true` to skip the overlay entirely and run headless.
53
53
 
54
54
  ## Quick Start
55
55
 
@@ -329,6 +329,82 @@ interactive_shell → node-pty → subprocess
329
329
 
330
330
  Full PTY. The subprocess thinks it's in a real terminal.
331
331
 
332
+ ## Example Workflow: Plan, Implement, Review
333
+
334
+ The `examples/prompts/` directory includes three prompt templates that chain together into a complete development workflow using Codex CLI. Each template instructs pi to gather context, generate a tailored meta prompt based on the [Codex prompting guide](https://developers.openai.com/cookbook/examples/gpt-5/gpt-5-2_prompting_guide.md), and launch Codex in an interactive overlay.
335
+
336
+ ### The Pipeline
337
+
338
+ ```
339
+ Write a plan
340
+
341
+ /codex-review-plan path/to/plan.md ← Codex verifies every assumption against the codebase
342
+
343
+ /codex-implement-plan path/to/plan.md ← Codex implements the reviewed plan faithfully
344
+
345
+ /codex-review-impl path/to/plan.md ← Codex reviews the diff against the plan, fixes issues
346
+ ```
347
+
348
+ ### Installing the Templates
349
+
350
+ Copy the prompt templates and Codex CLI skill to your pi config:
351
+
352
+ ```bash
353
+ # Prompt templates (slash commands)
354
+ cp ~/.pi/agent/extensions/interactive-shell/examples/prompts/*.md ~/.pi/agent/prompts/
355
+
356
+ # Codex CLI skill (teaches pi how to use codex flags, sandbox caveats, etc.)
357
+ cp -r ~/.pi/agent/extensions/interactive-shell/examples/skills/codex-cli ~/.pi/agent/skills/
358
+ ```
359
+
360
+ ### Usage
361
+
362
+ Say you have a plan at `docs/auth-redesign-plan.md`:
363
+
364
+ **Step 1: Review the plan** — Codex reads your plan, then verifies every file path, API shape, data flow, and integration point against the actual codebase. Fixes issues directly in the plan file.
365
+
366
+ ```
367
+ /codex-review-plan docs/auth-redesign-plan.md
368
+ /codex-review-plan docs/auth-redesign-plan.md pay attention to the migration steps
369
+ ```
370
+
371
+ **Step 2: Implement the plan** — Codex reads all relevant code first, then implements bottom-up: shared utilities first, then dependent modules, then integration code. No stubs, no TODOs.
372
+
373
+ ```
374
+ /codex-implement-plan docs/auth-redesign-plan.md
375
+ /codex-implement-plan docs/auth-redesign-plan.md skip test files for now
376
+ ```
377
+
378
+ **Step 3: Review the implementation** — Codex diffs the changes, reads every changed file in full (plus imports and dependents), traces code paths across file boundaries, and fixes every issue it finds. Pass the plan to verify completeness, or omit it to just review the diff.
379
+
380
+ ```
381
+ /codex-review-impl docs/auth-redesign-plan.md # review diff against plan
382
+ /codex-review-impl docs/auth-redesign-plan.md check cleanup ordering
383
+ /codex-review-impl # just review the diff, no plan
384
+ /codex-review-impl focus on error handling and race conditions
385
+ ```
386
+
387
+ ### How They Work
388
+
389
+ These templates demonstrate a "meta-prompt generation" pattern:
390
+
391
+ 1. **Pi gathers context** — reads the plan, runs git diff, fetches the Codex prompting guide
392
+ 2. **Pi generates a calibrated prompt** — tailored to the specific plan/diff, following the guide's best practices
393
+ 3. **Pi launches Codex in the overlay** — with explicit flags (`-m gpt-5.3-codex -c model_reasoning_effort="high" -a never`) and hands off control
394
+
395
+ The user watches Codex work in the overlay and can take over anytime (type to intervene, Ctrl+T to transfer output back to pi, Ctrl+Q for options).
396
+
397
+ ### Customizing
398
+
399
+ These are starting points. Fork them and adjust:
400
+
401
+ - **Model/flags** — swap `gpt-5.3-codex` for another model, change reasoning effort
402
+ - **Review criteria** — add project-specific checks (security policies, style rules)
403
+ - **Implementation rules** — change the 500-line file limit, add framework-specific patterns
404
+ - **Other agents** — adapt the pattern for Claude (`claude "prompt"`), Gemini (`gemini -i "prompt"`), or any CLI
405
+
406
+ See the [pi prompt templates docs](https://github.com/badlogic/pi-mono/) for the full `$1`, `$@` placeholder syntax.
407
+
332
408
  ## Advanced: Multi-Agent Workflows
333
409
 
334
410
  For orchestrating multi-agent chains (scout → planner → worker → reviewer) with file-based handoff and auto-continue support, see:
package/SKILL.md CHANGED
@@ -84,6 +84,8 @@ interactive_shell({
84
84
 
85
85
  Dispatch defaults `autoExitOnQuiet: true`. The agent can still query the sessionId if needed, but doesn't have to.
86
86
 
87
+ For fire-and-forget delegated runs (including QA-style delegated checks), prefer dispatch as the default mode.
88
+
87
89
  #### Background Dispatch (Headless)
88
90
  No overlay opens. Multiple headless dispatches can run concurrently:
89
91
 
package/config.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
- import { homedir } from "node:os";
3
2
  import { join } from "node:path";
3
+ import { getAgentDir } from "@mariozechner/pi-coding-agent";
4
4
 
5
5
  export interface InteractiveShellConfig {
6
6
  exitAutoCloseDelay: number;
@@ -52,8 +52,8 @@ const DEFAULT_CONFIG: InteractiveShellConfig = {
52
52
  // Hands-free mode defaults
53
53
  handsFreeUpdateMode: "on-quiet" as const,
54
54
  handsFreeUpdateInterval: 60000,
55
- handsFreeQuietThreshold: 5000,
56
- autoExitGracePeriod: 30000,
55
+ handsFreeQuietThreshold: 8000,
56
+ autoExitGracePeriod: 15000,
57
57
  handsFreeUpdateMaxChars: 1500,
58
58
  handsFreeMaxTotalChars: 100000,
59
59
  // Query rate limiting (default 60 seconds between queries)
@@ -62,7 +62,7 @@ const DEFAULT_CONFIG: InteractiveShellConfig = {
62
62
 
63
63
  export function loadConfig(cwd: string): InteractiveShellConfig {
64
64
  const projectPath = join(cwd, ".pi", "interactive-shell.json");
65
- const globalPath = join(homedir(), ".pi", "agent", "interactive-shell.json");
65
+ const globalPath = join(getAgentDir(), "interactive-shell.json");
66
66
 
67
67
  let globalConfig: Partial<InteractiveShellConfig> = {};
68
68
  let projectConfig: Partial<InteractiveShellConfig> = {};
@@ -0,0 +1,26 @@
1
+ ---
2
+ description: Launch Codex CLI in overlay to fully implement an existing plan/spec document
3
+ ---
4
+ Load the `codex-5.3-prompting` and `codex-cli` skills. Then read the plan at `$1`.
5
+
6
+ Analyze the plan to understand: how many files are created vs modified, whether there's a prescribed implementation order or prerequisites, what existing code is referenced, and roughly how large the implementation is.
7
+
8
+ Based on the prompting skill's best practices and the plan's content, generate a comprehensive meta prompt tailored for Codex CLI. The meta prompt should instruct Codex to:
9
+
10
+ 1. Read and internalize the full plan document. Identify every file to be created, every file to be modified, and any prerequisites or ordering constraints.
11
+ 2. Before writing any code, read all existing files that will be modified — in full, not just the sections mentioned in the plan. Also read key files they import from or that import them, to absorb the surrounding patterns, naming conventions, and architecture.
12
+ 3. If the plan specifies an implementation order or prerequisites (e.g., "extract module X before building Y"), follow that order exactly. Otherwise, implement bottom-up: shared utilities and types first, then the modules that depend on them, then integration/registration code last.
13
+ 4. Implement each piece completely. No stubs, no TODOs, no placeholder comments, no "implement this later" shortcuts. Every function body, every edge case handler, every error path described in the plan must be real code.
14
+ 5. Match existing code patterns exactly — same formatting, same import style, same error handling conventions, same naming. Read the surrounding codebase to absorb these patterns before writing. If the plan references patterns from specific files (e.g., "same pattern as X"), read those files and replicate the pattern faithfully.
15
+ 6. Stay within scope. Do not refactor, rename, or restructure adjacent code that the plan does not mention. No "while I'm here" improvements. If something adjacent looks wrong, note it in the summary but do not touch it.
16
+ 7. Keep files reasonably sized. If a file grows beyond ~500 lines, split it as the plan describes or refactor into logical sub-modules.
17
+ 8. After implementing all files, do a self-review pass: re-read the plan from top to bottom and verify every requirement, every edge case, every design decision is addressed in the code. Check for: missing imports, type mismatches, unreachable code paths, inconsistent field names between modules, and any plan requirement that was overlooked.
18
+ 9. Do NOT commit or push. Write a summary listing every file created or modified, what was implemented in each, and any plan ambiguities that required judgment calls.
19
+
20
+ The meta prompt should follow the prompting skill's patterns: clear system context, explicit scope and verbosity constraints, step-by-step instructions, and expected output format. Instruct Codex not to ask clarifying questions about things answerable by reading the plan or codebase — read first, then act. Keep progress updates brief and concrete (no narrating routine file reads or tool calls). Emphasize that the plan has already been thoroughly reviewed — the job is faithful execution, not second-guessing the design. Emphasize scope discipline — GPT-5.3-Codex is aggressive about refactoring adjacent code if not explicitly fenced in.
21
+
22
+ Then launch Codex CLI in the interactive shell overlay with that meta prompt using these flags: `-m gpt-5.3-codex -c model_reasoning_effort="high" -a never`.
23
+
24
+ Use `interactive_shell` with `mode: "dispatch"` for this delegated run (fire-and-forget with completion notification). Do NOT pass sandbox flags in interactive_shell. Dispatch mode only. End turn immediately. Do not poll. Wait for completion notification.
25
+
26
+ $@
@@ -0,0 +1,27 @@
1
+ ---
2
+ description: Launch Codex CLI in overlay to review implemented code changes (optionally against a plan)
3
+ ---
4
+ Load the `codex-5.3-prompting` and `codex-cli` skills. Then determine the review scope:
5
+
6
+ - If `$1` looks like a file path (contains `/` or ends in `.md`): read it as the plan/spec these changes were based on. The diff scope is uncommitted changes vs HEAD, or if clean, the current branch vs main.
7
+ - Otherwise: no plan file. Diff scope is the same. Treat all of `$@` as additional review context or focus areas.
8
+
9
+ Run the appropriate git diff to identify which files changed and how many lines are involved. This context helps you generate a better-calibrated meta prompt.
10
+
11
+ Based on the prompting skill's best practices, the diff scope, and the optional plan, generate a comprehensive meta prompt tailored for Codex CLI. The meta prompt should instruct Codex to:
12
+
13
+ 1. Identify all changed files via git diff, then read every changed file in full — not just the diff hunks. For each changed file, also read the files it imports from and key files that depend on it, to understand integration points and downstream effects.
14
+ 2. If a plan/spec was provided, read it and verify the implementation is complete — every requirement addressed, no steps skipped, nothing invented beyond scope, no partial stubs left behind.
15
+ 3. Review each changed file for: bugs, logic errors, race conditions, resource leaks (timers, event listeners, file handles, unclosed connections), null/undefined hazards, off-by-one errors, error handling gaps, type mismatches, dead code, unused imports/variables/parameters, unnecessary complexity, and inconsistency with surrounding code patterns and naming conventions.
16
+ 4. Trace key code paths end-to-end across function and file boundaries — verify data flows, state transitions, error propagation, and cleanup ordering. Don't evaluate functions in isolation.
17
+ 5. Check for missing or inadequate tests, stale documentation, and missing changelog entries.
18
+ 6. Fix every issue found with direct code edits. Keep fixes scoped to the actual issues identified — do not expand into refactoring or restructuring code that wasn't flagged in the review. If adjacent code looks problematic, note it in the summary but don't touch it.
19
+ 7. After all fixes, write a clear summary listing what was found, what was fixed, and any remaining concerns that require human judgment.
20
+
21
+ The meta prompt should follow the prompting skill's patterns: clear system context, explicit scope and verbosity constraints, step-by-step instructions, and expected output format. Instruct Codex not to ask clarifying questions — if intent is unclear, read the surrounding code for context instead of asking. Keep progress updates brief and concrete (no narrating routine file reads or tool calls). Emphasize thoroughness — read the actual code deeply before making judgments, question every assumption, and never rubber-stamp. GPT-5.3-Codex moves fast and can skim; the meta prompt must force it to slow down and read carefully before judging.
22
+
23
+ Then launch Codex CLI in the interactive shell overlay with that meta prompt using these flags: `-m gpt-5.3-codex -c model_reasoning_effort="high" -a never`.
24
+
25
+ Use `interactive_shell` with `mode: "dispatch"` for this delegated run (fire-and-forget with completion notification). Do NOT pass sandbox flags in interactive_shell. Dispatch mode only. End turn immediately. Do not poll. Wait for completion notification.
26
+
27
+ $@
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: Launch Codex CLI in overlay to review an implementation plan against the codebase
3
+ ---
4
+ Load the `codex-5.3-prompting` and `codex-cli` skills. Then read the plan at `$1`.
5
+
6
+ Based on the prompting skill's best practices and the plan's content, generate a comprehensive meta prompt tailored for Codex CLI. The meta prompt should instruct Codex to:
7
+
8
+ 1. Read and internalize the full plan. Then read every codebase file the plan references — in full, not just the sections mentioned. Also read key files adjacent to those (imports, dependents) to understand the real state of the code the plan targets.
9
+ 2. Systematically review the plan against what the code actually looks like, not what the plan assumes it looks like.
10
+ 3. Verify every assumption, file path, API shape, data flow, and integration point mentioned in the plan against the actual codebase.
11
+ 4. Check that the plan's approach is logically sound, complete, and accounts for edge cases.
12
+ 5. Identify any gaps, contradictions, incorrect assumptions, or missing steps.
13
+ 6. Make targeted edits to the plan file to fix issues found, adding inline notes where changes were made. Fix what's wrong — do not restructure or rewrite sections that are correct.
14
+
15
+ The meta prompt should follow the prompting skill's patterns (clear system context, explicit constraints, step-by-step instructions, expected output format). Instruct Codex not to ask clarifying questions — read the codebase to resolve ambiguities instead of asking. Keep progress updates brief and concrete. GPT-5.3-Codex is eager and may restructure the plan beyond what's needed; constrain edits to actual issues found.
16
+
17
+ Then launch Codex CLI in the interactive shell overlay with that meta prompt using these flags: `-m gpt-5.3-codex -c model_reasoning_effort="xhigh" -a never`.
18
+
19
+ Use `interactive_shell` with `mode: "dispatch"` for this delegated run (fire-and-forget with completion notification). Do NOT pass sandbox flags in interactive_shell. Dispatch mode only. End turn immediately. Do not poll. Wait for completion notification.
20
+
21
+ $@
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: codex-5.3-prompting
3
+ description: How to write system prompts and instructions for GPT-5.3-Codex. Use when constructing or tuning prompts targeting Codex 5.3.
4
+ ---
5
+
6
+ # GPT-5.3-Codex Prompting Guide
7
+
8
+ GPT-5.3-Codex is fast, capable, and eager. It moves quickly and will skip reading, over-refactor, and drift scope if prompts aren't tight. Explicit constraints matter more than with GPT-5.2-Codex. Include the following blocks as needed when constructing system prompts.
9
+
10
+ ## Output shape
11
+
12
+ Always include. Controls verbosity and response structure.
13
+
14
+ ```
15
+ <output_verbosity_spec>
16
+ - Default: 3-6 sentences or <=5 bullets for typical answers.
17
+ - Simple yes/no questions: <=2 sentences.
18
+ - Complex multi-step or multi-file tasks:
19
+ - 1 short overview paragraph
20
+ - then <=5 bullets tagged: What changed, Where, Risks, Next steps, Open questions.
21
+ - Avoid long narrative paragraphs; prefer compact bullets and short sections.
22
+ - Do not rephrase the user's request unless it changes semantics.
23
+ </output_verbosity_spec>
24
+ ```
25
+
26
+ ## Scope constraints
27
+
28
+ Always include. GPT-5.3-Codex will add features, refactor adjacent code, and invent UI elements if you don't fence it in.
29
+
30
+ ```
31
+ <design_and_scope_constraints>
32
+ - Explore any existing design systems and understand them deeply.
33
+ - Implement EXACTLY and ONLY what the user requests.
34
+ - No extra features, no added components, no UX embellishments.
35
+ - Style aligned to the design system at hand.
36
+ - Do NOT invent colors, shadows, tokens, animations, or new UI elements unless requested or necessary.
37
+ - If any instruction is ambiguous, choose the simplest valid interpretation.
38
+ </design_and_scope_constraints>
39
+ ```
40
+
41
+ ## Context loading
42
+
43
+ Always include. GPT-5.3-Codex skips reading and starts writing if you don't force it.
44
+
45
+ ```
46
+ <context_loading>
47
+ - Read ALL files that will be modified -- in full, not just the sections mentioned in the task.
48
+ - Also read key files they import from or that depend on them.
49
+ - Absorb surrounding patterns, naming conventions, error handling style, and architecture before writing any code.
50
+ - Do not ask clarifying questions about things that are answerable by reading the codebase.
51
+ </context_loading>
52
+ ```
53
+
54
+ ## Plan-first mode
55
+
56
+ Include for multi-file work, large refactors, or any task with ordering dependencies.
57
+
58
+ ```
59
+ <plan_first>
60
+ - Before writing any code, produce a brief implementation plan:
61
+ - Files to create vs. modify
62
+ - Implementation order and prerequisites
63
+ - Key design decisions and edge cases
64
+ - Acceptance criteria for "done"
65
+ - Get the plan right first. Then implement step by step following the plan.
66
+ - If the plan is provided externally, follow it faithfully -- the job is execution, not second-guessing the design.
67
+ </plan_first>
68
+ ```
69
+
70
+ ## Long-context handling
71
+
72
+ Include when inputs exceed ~10k tokens (multi-chapter docs, long threads, multiple PDFs).
73
+
74
+ ```
75
+ <long_context_handling>
76
+ - For inputs longer than ~10k tokens:
77
+ - First, produce a short internal outline of the key sections relevant to the task.
78
+ - Re-state the constraints explicitly before answering.
79
+ - Anchor claims to sections ("In the 'Data Retention' section...") rather than speaking generically.
80
+ - If the answer depends on fine details (dates, thresholds, clauses), quote or paraphrase them.
81
+ </long_context_handling>
82
+ ```
83
+
84
+ ## Uncertainty and ambiguity
85
+
86
+ Include when the task involves underspecified requirements or hallucination-prone domains.
87
+
88
+ ```
89
+ <uncertainty_and_ambiguity>
90
+ - If the question is ambiguous or underspecified:
91
+ - Ask up to 1-3 precise clarifying questions, OR
92
+ - Present 2-3 plausible interpretations with clearly labeled assumptions.
93
+ - Never fabricate exact figures, line numbers, or external references when uncertain.
94
+ - When unsure, prefer "Based on the provided context..." over absolute claims.
95
+ </uncertainty_and_ambiguity>
96
+ ```
97
+
98
+ ## User updates
99
+
100
+ Include for agentic / long-running tasks.
101
+
102
+ ```
103
+ <user_updates_spec>
104
+ - Send brief updates (1-2 sentences) only when:
105
+ - You start a new major phase of work, or
106
+ - You discover something that changes the plan.
107
+ - Avoid narrating routine tool calls ("reading file...", "running tests...").
108
+ - Each update must include at least one concrete outcome ("Found X", "Confirmed Y", "Updated Z").
109
+ - Do not expand the task beyond what was asked; if you notice new work, call it out as optional.
110
+ </user_updates_spec>
111
+ ```
112
+
113
+ ## Tool usage
114
+
115
+ Include when the prompt involves tool-calling agents.
116
+
117
+ ```
118
+ <tool_usage_rules>
119
+ - Prefer tools over internal knowledge whenever:
120
+ - You need fresh or user-specific data (tickets, orders, configs, logs).
121
+ - You reference specific IDs, URLs, or document titles.
122
+ - Parallelize independent reads (read_file, fetch_record, search_docs) when possible to reduce latency.
123
+ - After any write/update tool call, briefly restate:
124
+ - What changed
125
+ - Where (ID or path)
126
+ - Any follow-up validation performed
127
+ </tool_usage_rules>
128
+ ```
129
+
130
+ ## Reasoning effort
131
+
132
+ Set `model_reasoning_effort` via Codex CLI: `-c model_reasoning_effort="high"`
133
+
134
+ | Task type | Effort |
135
+ |---|---|
136
+ | Simple code generation, formatting | `low` or `medium` |
137
+ | Standard implementation from clear specs | `high` |
138
+ | Complex refactors, plan review, architecture | `xhigh` |
139
+ | Code review (thorough) | `high` or `xhigh` |
140
+
141
+ ## Backwards compatibility hedging
142
+
143
+ GPT-5.3-Codex has a strong tendency to preserve old patterns, add compatibility shims, and provide fallback code "just in case" -- even when explicitly told not to worry about backwards compatibility. Vague instructions like "don't worry about backwards compatibility" get interpreted weakly; the model may still hedge.
144
+
145
+ Use **"cutover"** to signal a clean, irreversible break. It's a precise industry term that conveys finality and intentional deprecation -- no dual-support phase, no gradual migration, no preserving old behavior.
146
+
147
+ Instead of:
148
+ > "Rewrite this and don't worry about backwards compatibility"
149
+
150
+ Say:
151
+ > "This is a cutover. No backwards compatibility. Rewrite using only Python 3.12+ features and current best practices. Do not preserve legacy code, polyfills, or deprecated patterns."
152
+
153
+ ## Quick reference
154
+
155
+ - **Force reading first.** "Read all necessary files before you ask any dumb question."
156
+ - **Use plan mode.** Draft the full task with acceptance criteria before implementing.
157
+ - **Steer aggressively mid-task.** GPT-5.3-Codex handles redirects without losing context. Be direct: "Stop. Fix the actual cause." / "Simplest valid implementation only."
158
+ - **Constrain scope hard.** GPT-5.3-Codex will refactor aggressively if you don't fence it in.
159
+ - **Watch context burn.** Faster model = faster context consumption. Start fresh at ~40%.
160
+ - **Use domain jargon.** "Cutover," "golden-path," "no fallbacks," "domain split" get cleaner, faster responses.
161
+ - **Download libraries locally.** Tell it to read them for better context than relying on training data.
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: codex-cli
3
+ description: OpenAI Codex CLI reference. Use when running codex in interactive_shell overlay or when user asks about codex CLI options.
4
+ ---
5
+
6
+ # Codex CLI (OpenAI)
7
+
8
+ ## Commands
9
+
10
+ | Command | Description |
11
+ |---------|-------------|
12
+ | `codex` | Start interactive TUI |
13
+ | `codex "prompt"` | TUI with initial prompt |
14
+ | `codex exec "prompt"` | Non-interactive (headless), streams to stdout. Supports `--output-schema <file>` for structured JSON output |
15
+ | `codex e "prompt"` | Shorthand for exec |
16
+ | `codex login` | Authenticate (OAuth, device auth, or API key) |
17
+ | `codex login status` | Show auth mode |
18
+ | `codex logout` | Remove credentials |
19
+ | `codex mcp` | Manage MCP servers |
20
+ | `codex completion` | Generate shell completions |
21
+
22
+ ## Key Flags
23
+
24
+ | Flag | Description |
25
+ |------|-------------|
26
+ | `-m, --model <model>` | Switch model (default: `gpt-5.3-codex`) |
27
+ | `-c <key=value>` | Override config.toml values (dotted paths, parsed as TOML) |
28
+ | `-p, --profile <name>` | Use config profile from config.toml |
29
+ | `-s, --sandbox <mode>` | Sandbox policy: `read-only`, `workspace-write`, `danger-full-access` |
30
+ | `-a, --ask-for-approval <policy>` | `untrusted`, `on-failure`, `on-request`, `never` |
31
+ | `--full-auto` | Alias for `-a on-request --sandbox workspace-write` |
32
+ | `--search` | Enable live web search tool |
33
+ | `-i, --image <file>` | Attach image(s) to initial prompt |
34
+ | `--add-dir <dir>` | Additional writable directories |
35
+ | `-C, --cd <dir>` | Set working root directory |
36
+ | `--no-alt-screen` | Inline mode (preserve terminal scrollback) |
37
+
38
+ ## Sandbox Modes
39
+
40
+ - `read-only` - Can only read files
41
+ - `workspace-write` - Can write to workspace
42
+ - `danger-full-access` - Full system access (use with caution)
43
+
44
+ ## Features
45
+
46
+ - **Image inputs** - Accepts screenshots and design specs
47
+ - **Code review** - Reviews changes before commit
48
+ - **Web search** - Can search for information
49
+ - **MCP integration** - Third-party tool support
50
+
51
+ ## Config
52
+
53
+ Config file: `~/.codex/config.toml`
54
+
55
+ Key config values (set in file or override with `-c`):
56
+ - `model` -- model name (e.g., `gpt-5.3-codex`)
57
+ - `model_reasoning_effort` -- `low`, `medium`, `high`, `xhigh`
58
+ - `model_reasoning_summary` -- `detailed`, `concise`, `none`
59
+ - `model_verbosity` -- `low`, `medium`, `high`
60
+ - `profile` -- default profile name
61
+ - `tool_output_token_limit` -- max tokens per tool output
62
+
63
+ Define profiles for different projects/modes with `[profiles.<name>]` sections. Override at runtime with `-p <name>` or `-c model_reasoning_effort="high"`.
64
+
65
+ ## In interactive_shell
66
+
67
+ Do NOT pass `-s` / `--sandbox` flags. Codex's `read-only` and `workspace-write` sandbox modes apply OS-level filesystem restrictions that break basic shell operations inside the PTY -- zsh can't even create temp files for here-documents, so every write attempt fails with "operation not permitted." The interactive shell overlay already provides supervision (user watches in real-time, Ctrl+Q to kill, Ctrl+T to transfer output), making Codex's sandbox redundant.
68
+
69
+ Use explicit flags to control model and behavior per-run.
70
+
71
+ For delegated fire-and-forget runs, prefer `mode: "dispatch"` so the agent is notified automatically when Codex completes.
72
+
73
+ ```typescript
74
+ // Delegated run with completion notification (recommended default)
75
+ interactive_shell({
76
+ command: 'codex -m gpt-5.3-codex -a never "Review this codebase for security issues"',
77
+ mode: "dispatch"
78
+ })
79
+
80
+ // Override reasoning effort for a single delegated run
81
+ interactive_shell({
82
+ command: 'codex -m gpt-5.3-codex -c model_reasoning_effort="xhigh" -a never "Complex refactor task"',
83
+ mode: "dispatch"
84
+ })
85
+
86
+ // Headless - use bash instead
87
+ bash({ command: 'codex exec "summarize the repo"' })
88
+ ```
@@ -1,3 +1,4 @@
1
+ import { stripVTControlCharacters } from "node:util";
1
2
  import type { PtyTerminalSession } from "./pty-session.js";
2
3
  import type { InteractiveShellConfig } from "./config.js";
3
4
 
@@ -40,6 +41,10 @@ export class HeadlessDispatchMonitor {
40
41
  ) {
41
42
  this.subscribe();
42
43
 
44
+ if (options.autoExitOnQuiet) {
45
+ this.resetQuietTimer();
46
+ }
47
+
43
48
  if (options.timeout && options.timeout > 0) {
44
49
  this.timeoutTimer = setTimeout(() => {
45
50
  this.handleCompletion(null, undefined, true);
@@ -57,9 +62,12 @@ export class HeadlessDispatchMonitor {
57
62
 
58
63
  private subscribe(): void {
59
64
  this.unsubscribe();
60
- this.unsubData = this.session.addDataListener(() => {
65
+ this.unsubData = this.session.addDataListener((data) => {
61
66
  if (this.options.autoExitOnQuiet) {
62
- this.resetQuietTimer();
67
+ const visible = stripVTControlCharacters(data);
68
+ if (visible.trim().length > 0) {
69
+ this.resetQuietTimer();
70
+ }
63
71
  }
64
72
  });
65
73
  this.unsubExit = this.session.addExitListener((exitCode, signal) => {
package/index.ts CHANGED
@@ -3,7 +3,7 @@ import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
3
3
  import { InteractiveShellOverlay } from "./overlay-component.js";
4
4
  import { ReattachOverlay } from "./reattach-overlay.js";
5
5
  import { PtyTerminalSession } from "./pty-session.js";
6
- import type { InteractiveShellResult } from "./types.js";
6
+ import type { InteractiveShellResult, HandsFreeUpdate } from "./types.js";
7
7
  import { sessionManager, generateSessionId, releaseSessionId } from "./session-manager.js";
8
8
  import type { OutputOptions, OutputResult } from "./session-manager.js";
9
9
  import { loadConfig } from "./config.js";
@@ -134,6 +134,36 @@ function registerHeadlessActive(
134
134
  });
135
135
  }
136
136
 
137
+ function makeNonBlockingUpdateHandler(pi: ExtensionAPI): (update: HandsFreeUpdate) => void {
138
+ return (update) => {
139
+ pi.events.emit("interactive-shell:update", update);
140
+
141
+ if (update.status !== "running") {
142
+ const tail = update.tail.length > 0 ? `\n\n${update.tail.join("\n")}` : "";
143
+ let statusLine: string;
144
+ switch (update.status) {
145
+ case "exited":
146
+ statusLine = `Session ${update.sessionId} exited (${formatDurationMs(update.runtime)})`;
147
+ break;
148
+ case "killed":
149
+ statusLine = `Session ${update.sessionId} killed (${formatDurationMs(update.runtime)})`;
150
+ break;
151
+ case "user-takeover":
152
+ statusLine = `Session ${update.sessionId}: user took over (${formatDurationMs(update.runtime)})`;
153
+ break;
154
+ default:
155
+ statusLine = `Session ${update.sessionId} update (${formatDurationMs(update.runtime)})`;
156
+ }
157
+ pi.sendMessage({
158
+ customType: "interactive-shell-update",
159
+ content: statusLine + tail,
160
+ display: true,
161
+ details: update,
162
+ }, { triggerTurn: true });
163
+ }
164
+ };
165
+ }
166
+
137
167
  let bgWidgetCleanup: (() => void) | null = null;
138
168
 
139
169
  function setupBackgroundWidget(ctx: { ui: { setWidget: Function }; hasUI?: boolean }) {
@@ -495,6 +525,9 @@ export default function interactiveShellExtension(pi: ExtensionAPI) {
495
525
  ? handsFree?.autoExitOnQuiet !== false
496
526
  : handsFree?.autoExitOnQuiet === true,
497
527
  autoExitGracePeriod: handsFree?.gracePeriod ?? config.autoExitGracePeriod,
528
+ onHandsFreeUpdate: mode === "hands-free"
529
+ ? makeNonBlockingUpdateHandler(pi)
530
+ : undefined,
498
531
  handoffPreviewEnabled: handoffPreview?.enabled,
499
532
  handoffPreviewLines: handoffPreview?.lines,
500
533
  handoffPreviewMaxChars: handoffPreview?.maxChars,
@@ -698,6 +731,9 @@ export default function interactiveShellExtension(pi: ExtensionAPI) {
698
731
  ? handsFree?.autoExitOnQuiet !== false
699
732
  : handsFree?.autoExitOnQuiet === true,
700
733
  autoExitGracePeriod: handsFree?.gracePeriod ?? config.autoExitGracePeriod,
734
+ onHandsFreeUpdate: mode === "hands-free"
735
+ ? makeNonBlockingUpdateHandler(pi)
736
+ : undefined,
701
737
  handoffPreviewEnabled: handoffPreview?.enabled,
702
738
  handoffPreviewLines: handoffPreview?.lines,
703
739
  handoffPreviewMaxChars: handoffPreview?.maxChars,
@@ -764,6 +800,7 @@ export default function interactiveShellExtension(pi: ExtensionAPI) {
764
800
  handsFreeMaxTotalChars: handsFree?.maxTotalChars,
765
801
  autoExitOnQuiet: handsFree?.autoExitOnQuiet,
766
802
  autoExitGracePeriod: handsFree?.gracePeriod ?? config.autoExitGracePeriod,
803
+ streamingMode: mode === "hands-free",
767
804
  onHandsFreeUpdate: mode === "hands-free"
768
805
  ? (update) => {
769
806
  let statusText: string;
@@ -774,6 +811,9 @@ export default function interactiveShellExtension(pi: ExtensionAPI) {
774
811
  case "exited":
775
812
  statusText = `Session ${update.sessionId} exited`;
776
813
  break;
814
+ case "killed":
815
+ statusText = `Session ${update.sessionId} killed`;
816
+ break;
777
817
  default: {
778
818
  const budgetInfo = update.budgetExhausted ? " [budget exhausted]" : "";
779
819
  statusText = `Session ${update.sessionId} running (${formatDurationMs(update.runtime)})${budgetInfo}`;
@@ -794,6 +834,7 @@ export default function interactiveShellExtension(pi: ExtensionAPI) {
794
834
  userTookOver: update.userTookOver,
795
835
  },
796
836
  });
837
+ pi.events.emit("interactive-shell:update", update);
797
838
  }
798
839
  : undefined,
799
840
  handoffPreviewEnabled: handoffPreview?.enabled,
@@ -1,9 +1,10 @@
1
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
- import { homedir } from "node:os";
3
2
  import { join } from "node:path";
3
+ import { stripVTControlCharacters } from "node:util";
4
4
  import type { Component, Focusable, TUI } from "@mariozechner/pi-tui";
5
5
  import { matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
6
6
  import type { Theme } from "@mariozechner/pi-coding-agent";
7
+ import { getAgentDir } from "@mariozechner/pi-coding-agent";
7
8
  import { PtyTerminalSession } from "./pty-session.js";
8
9
  import { sessionManager, generateSessionId } from "./session-manager.js";
9
10
  import type { InteractiveShellConfig } from "./config.js";
@@ -84,11 +85,16 @@ export class InteractiveShellOverlay implements Component, Focusable {
84
85
  const rows = Math.max(3, overlayHeight - (HEADER_LINES + FOOTER_LINES_COMPACT + 2));
85
86
 
86
87
  const ptyEvents = {
87
- onData: () => {
88
+ onData: (data: string) => {
88
89
  this.debouncedRender();
89
- if (this.state === "hands-free" && this.updateMode === "on-quiet") {
90
- this.hasUnsentData = true;
91
- this.resetQuietTimer();
90
+ if (this.state === "hands-free" && (this.updateMode === "on-quiet" || this.options.autoExitOnQuiet)) {
91
+ const visible = stripVTControlCharacters(data);
92
+ if (visible.trim().length > 0) {
93
+ if (this.updateMode === "on-quiet") {
94
+ this.hasUnsentData = true;
95
+ }
96
+ this.resetQuietTimer();
97
+ }
92
98
  }
93
99
  },
94
100
  onExit: () => {
@@ -427,13 +433,21 @@ export class InteractiveShellOverlay implements Component, Focusable {
427
433
  }
428
434
  }, 2000);
429
435
 
436
+ if (this.options.autoExitOnQuiet) {
437
+ this.resetQuietTimer();
438
+ }
439
+
430
440
  this.handsFreeInterval = setInterval(() => {
431
441
  if (this.state === "hands-free") {
432
442
  if (this.updateMode === "on-quiet") {
433
443
  if (this.hasUnsentData && this.options.onHandsFreeUpdate) {
434
444
  this.emitHandsFreeUpdate();
435
445
  this.hasUnsentData = false;
436
- this.stopQuietTimer();
446
+ if (this.options.autoExitOnQuiet) {
447
+ this.resetQuietTimer();
448
+ } else {
449
+ this.stopQuietTimer();
450
+ }
437
451
  }
438
452
  } else {
439
453
  this.emitHandsFreeUpdate();
@@ -442,7 +456,6 @@ export class InteractiveShellOverlay implements Component, Focusable {
442
456
  }, this.currentUpdateInterval);
443
457
  }
444
458
 
445
- /** Reset the quiet timer - called on each data event in on-quiet mode */
446
459
  private resetQuietTimer(): void {
447
460
  this.stopQuietTimer();
448
461
  this.quietTimer = setTimeout(() => {
@@ -510,7 +523,11 @@ export class InteractiveShellOverlay implements Component, Focusable {
510
523
  if (this.hasUnsentData && this.options.onHandsFreeUpdate) {
511
524
  this.emitHandsFreeUpdate();
512
525
  this.hasUnsentData = false;
513
- this.stopQuietTimer();
526
+ if (this.options.autoExitOnQuiet) {
527
+ this.resetQuietTimer();
528
+ } else {
529
+ this.stopQuietTimer();
530
+ }
514
531
  }
515
532
  } else {
516
533
  this.emitHandsFreeUpdate();
@@ -526,9 +543,7 @@ export class InteractiveShellOverlay implements Component, Focusable {
526
543
  if (clamped === this.currentQuietThreshold) return;
527
544
  this.currentQuietThreshold = clamped;
528
545
 
529
- // If a quiet timer is active, restart it with the new threshold
530
- // Use resetQuietTimer to ensure autoExitOnQuiet logic is included
531
- if (this.quietTimer && this.updateMode === "on-quiet") {
546
+ if (this.quietTimer) {
532
547
  this.resetQuietTimer();
533
548
  }
534
549
  }
@@ -624,8 +639,6 @@ export class InteractiveShellOverlay implements Component, Focusable {
624
639
  this.state = "running";
625
640
  this.userTookOver = true;
626
641
 
627
- // Notify agent that user took over (streaming mode)
628
- // In non-blocking mode, keep session registered so agent can query status
629
642
  if (this.options.onHandsFreeUpdate) {
630
643
  this.options.onHandsFreeUpdate({
631
644
  status: "user-takeover",
@@ -637,11 +650,12 @@ export class InteractiveShellOverlay implements Component, Focusable {
637
650
  totalCharsSent: this.totalCharsSent,
638
651
  budgetExhausted: this.budgetExhausted,
639
652
  });
640
- // Unregister and release ID in streaming mode - agent got notified, won't query
653
+ }
654
+ // In streaming mode (blocking tool call), unregister now since the agent
655
+ // gets the result via tool return. Otherwise keep registered for queries.
656
+ if (this.options.streamingMode) {
641
657
  this.unregisterActiveSession(true);
642
658
  }
643
- // In non-blocking mode (no onHandsFreeUpdate), keep session registered
644
- // so agent can query and see "user-takeover" status
645
659
 
646
660
  this.tui.requestRender();
647
661
  }
@@ -705,7 +719,7 @@ export class InteractiveShellOverlay implements Component, Focusable {
705
719
  const maxChars = this.options.handoffSnapshotMaxChars ?? this.config.handoffSnapshotMaxChars;
706
720
  if (lines <= 0 || maxChars <= 0) return undefined;
707
721
 
708
- const baseDir = join(homedir(), ".pi", "agent", "cache", "interactive-shell");
722
+ const baseDir = join(getAgentDir(), "cache", "interactive-shell");
709
723
  mkdirSync(baseDir, { recursive: true });
710
724
 
711
725
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
@@ -761,10 +775,9 @@ export class InteractiveShellOverlay implements Component, Focusable {
761
775
  this.completionResult = result;
762
776
  this.triggerCompleteCallbacks();
763
777
 
764
- // In non-blocking mode (no onHandsFreeUpdate), keep session registered
765
- // so agent can query completion result. Agent's query will unregister.
766
- // In streaming mode, unregister now since agent got final update.
767
- if (this.options.onHandsFreeUpdate) {
778
+ // In streaming mode (blocking tool call), unregister now since the agent
779
+ // gets the result via tool return. Otherwise keep registered for queries.
780
+ if (this.options.streamingMode) {
768
781
  this.unregisterActiveSession(true);
769
782
  }
770
783
 
@@ -801,10 +814,9 @@ export class InteractiveShellOverlay implements Component, Focusable {
801
814
  this.completionResult = result;
802
815
  this.triggerCompleteCallbacks();
803
816
 
804
- // In non-blocking mode (no onHandsFreeUpdate), keep session registered
805
- // so agent can query completion result. Agent's query will unregister.
806
- // Use releaseId=false because the background session now owns the ID.
807
- if (this.options.onHandsFreeUpdate) {
817
+ // In streaming mode (blocking tool call), unregister now since the agent
818
+ // gets the result via tool return. releaseId=false because background owns the ID.
819
+ if (this.options.streamingMode) {
808
820
  this.unregisterActiveSession(false);
809
821
  }
810
822
 
@@ -836,9 +848,9 @@ export class InteractiveShellOverlay implements Component, Focusable {
836
848
  this.completionResult = result;
837
849
  this.triggerCompleteCallbacks();
838
850
 
839
- // In non-blocking mode (no onHandsFreeUpdate), keep session registered
840
- // so agent can query completion result. Agent's query will unregister.
841
- if (this.options.onHandsFreeUpdate) {
851
+ // In streaming mode (blocking tool call), unregister now since the agent
852
+ // gets the result via tool return. Otherwise keep registered for queries.
853
+ if (this.options.streamingMode) {
842
854
  this.unregisterActiveSession(true);
843
855
  }
844
856
 
@@ -875,9 +887,9 @@ export class InteractiveShellOverlay implements Component, Focusable {
875
887
  this.completionResult = result;
876
888
  this.triggerCompleteCallbacks();
877
889
 
878
- // In non-blocking mode (no onHandsFreeUpdate), keep session registered
879
- // so agent can query completion result. Agent's query will unregister.
880
- if (this.options.onHandsFreeUpdate) {
890
+ // In streaming mode (blocking tool call), unregister now since the agent
891
+ // gets the result via tool return. Otherwise keep registered for queries.
892
+ if (this.options.streamingMode) {
881
893
  this.unregisterActiveSession(true);
882
894
  }
883
895
 
@@ -929,9 +941,9 @@ export class InteractiveShellOverlay implements Component, Focusable {
929
941
  this.completionResult = result;
930
942
  this.triggerCompleteCallbacks();
931
943
 
932
- // In non-blocking mode (no onHandsFreeUpdate), keep session registered
933
- // so agent can query completion result. Agent's query will unregister.
934
- if (this.options.onHandsFreeUpdate) {
944
+ // In streaming mode (blocking tool call), unregister now since the agent
945
+ // gets the result via tool return. Otherwise keep registered for queries.
946
+ if (this.options.streamingMode) {
935
947
  this.unregisterActiveSession(true);
936
948
  }
937
949
 
@@ -1040,6 +1052,7 @@ export class InteractiveShellOverlay implements Component, Focusable {
1040
1052
  }
1041
1053
 
1042
1054
  render(width: number): string[] {
1055
+ width = Math.max(4, width);
1043
1056
  const th = this.theme;
1044
1057
  const border = (s: string) => th.fg("border", s);
1045
1058
  const accent = (s: string) => th.fg("accent", s);
@@ -1176,10 +1189,9 @@ export class InteractiveShellOverlay implements Component, Focusable {
1176
1189
  if (!this.completionResult) {
1177
1190
  this.session.kill();
1178
1191
  this.session.dispose();
1179
- // Release ID since session is dead and agent can't query anymore
1180
1192
  this.unregisterActiveSession(true);
1181
- } else if (this.options.onHandsFreeUpdate) {
1182
- // Streaming mode already delivered result, safe to unregister and release
1193
+ } else if (this.options.streamingMode) {
1194
+ // Streaming mode already delivered result via tool return, safe to clean up
1183
1195
  this.unregisterActiveSession(true);
1184
1196
  }
1185
1197
  // Non-blocking mode with completion: keep registered so agent can query
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-interactive-shell",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Run AI coding agents as foreground subagents in pi TUI overlays with hands-free monitoring",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,6 +18,7 @@
18
18
  "headless-monitor.ts",
19
19
  "types.ts",
20
20
  "scripts/",
21
+ "examples/",
21
22
  "banner.png",
22
23
  "README.md",
23
24
  "SKILL.md",
@@ -1,9 +1,9 @@
1
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
- import { homedir } from "node:os";
3
2
  import { join } from "node:path";
4
3
  import type { Component, Focusable, TUI } from "@mariozechner/pi-tui";
5
4
  import { matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
6
5
  import type { Theme } from "@mariozechner/pi-coding-agent";
6
+ import { getAgentDir } from "@mariozechner/pi-coding-agent";
7
7
  import { PtyTerminalSession } from "./pty-session.js";
8
8
  import { sessionManager } from "./session-manager.js";
9
9
  import type { InteractiveShellConfig } from "./config.js";
@@ -156,7 +156,7 @@ export class ReattachOverlay implements Component, Focusable {
156
156
  const maxChars = this.config.handoffSnapshotMaxChars;
157
157
  if (lines <= 0 || maxChars <= 0) return undefined;
158
158
 
159
- const baseDir = join(homedir(), ".pi", "agent", "cache", "interactive-shell");
159
+ const baseDir = join(getAgentDir(), "cache", "interactive-shell");
160
160
  mkdirSync(baseDir, { recursive: true });
161
161
 
162
162
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
@@ -358,6 +358,7 @@ export class ReattachOverlay implements Component, Focusable {
358
358
  }
359
359
 
360
360
  render(width: number): string[] {
361
+ width = Math.max(4, width);
361
362
  const th = this.theme;
362
363
  const border = (s: string) => th.fg("border", s);
363
364
  const accent = (s: string) => th.fg("accent", s);
@@ -9,8 +9,19 @@ import { fileURLToPath } from "node:url";
9
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
10
  const packageRoot = join(__dirname, "..");
11
11
 
12
- const EXTENSION_DIR = join(homedir(), ".pi", "agent", "extensions", "interactive-shell");
13
- const SKILL_DIR = join(homedir(), ".pi", "agent", "skills", "interactive-shell");
12
+ function getAgentDir() {
13
+ const envDir = process.env.PI_CODING_AGENT_DIR;
14
+ if (envDir) {
15
+ if (envDir === "~") return homedir();
16
+ if (envDir.startsWith("~/")) return homedir() + envDir.slice(1);
17
+ return envDir;
18
+ }
19
+ return join(homedir(), ".pi", "agent");
20
+ }
21
+
22
+ const agentDir = getAgentDir();
23
+ const EXTENSION_DIR = join(agentDir, "extensions", "interactive-shell");
24
+ const SKILL_DIR = join(agentDir, "skills", "interactive-shell");
14
25
 
15
26
  function log(msg) {
16
27
  console.log(`[pi-interactive-shell] ${msg}`);
package/tool-schema.ts CHANGED
@@ -14,6 +14,10 @@ MODES:
14
14
  - hands-free: Agent monitors with periodic updates, user can take over anytime by typing
15
15
  - dispatch: Agent is notified on completion via triggerTurn (no polling needed)
16
16
 
17
+ RECOMMENDED DEFAULT FOR DELEGATED TASKS:
18
+ - For fire-and-forget delegations and QA-style checks, prefer mode="dispatch".
19
+ - Dispatch is the safest choice when the agent should continue immediately and be notified automatically on completion.
20
+
17
21
  The user will see the process in an overlay. They can:
18
22
  - Watch output in real-time
19
23
  - Scroll through output (Shift+Up/Down)
@@ -234,10 +238,10 @@ export const toolParameters = Type.Object({
234
238
  Type.Number({ description: "Max interval between updates in ms (default: 60000)" }),
235
239
  ),
236
240
  quietThreshold: Type.Optional(
237
- Type.Number({ description: "Silence duration before emitting update in on-quiet mode (default: 5000ms)" }),
241
+ Type.Number({ description: "Silence duration before emitting update in on-quiet mode (default: 8000ms)" }),
238
242
  ),
239
243
  gracePeriod: Type.Optional(
240
- Type.Number({ description: "Startup grace period before autoExitOnQuiet can kill the session (default: 30000ms)" }),
244
+ Type.Number({ description: "Startup grace period before autoExitOnQuiet can kill the session (default: 15000ms)" }),
241
245
  ),
242
246
  updateMaxChars: Type.Optional(
243
247
  Type.Number({ description: "Max chars per update (default: 1500)" }),
package/types.ts CHANGED
@@ -73,6 +73,9 @@ export interface InteractiveShellOptions {
73
73
  autoExitGracePeriod?: number;
74
74
  // Auto-kill timeout
75
75
  timeout?: number;
76
+ // When true, unregister active session on completion (blocking tool call path).
77
+ // When false/undefined, keep registered so agent can query result later.
78
+ streamingMode?: boolean;
76
79
  // Existing PTY session (for attach flow -- skip creating a new PTY)
77
80
  existingSession?: import("./pty-session.js").PtyTerminalSession;
78
81
  }