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 +27 -1
- package/README.md +77 -1
- package/SKILL.md +2 -0
- package/config.ts +4 -4
- package/examples/prompts/codex-implement-plan.md +26 -0
- package/examples/prompts/codex-review-impl.md +27 -0
- package/examples/prompts/codex-review-plan.md +21 -0
- package/examples/skills/codex-5.3-prompting/SKILL.md +161 -0
- package/examples/skills/codex-cli/SKILL.md +88 -0
- package/headless-monitor.ts +10 -2
- package/index.ts +42 -1
- package/overlay-component.ts +49 -37
- package/package.json +2 -1
- package/reattach-overlay.ts +3 -2
- package/scripts/install.js +13 -2
- package/tool-schema.ts +6 -2
- package/types.ts +3 -0
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
|
-
## [
|
|
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:
|
|
56
|
-
autoExitGracePeriod:
|
|
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(
|
|
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
|
+
```
|
package/headless-monitor.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|
package/overlay-component.ts
CHANGED
|
@@ -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
|
-
|
|
91
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
765
|
-
//
|
|
766
|
-
|
|
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
|
|
805
|
-
//
|
|
806
|
-
|
|
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
|
|
840
|
-
//
|
|
841
|
-
if (this.options.
|
|
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
|
|
879
|
-
//
|
|
880
|
-
if (this.options.
|
|
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
|
|
933
|
-
//
|
|
934
|
-
if (this.options.
|
|
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.
|
|
1182
|
-
// Streaming mode already delivered result, safe to
|
|
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.
|
|
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",
|
package/reattach-overlay.ts
CHANGED
|
@@ -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(
|
|
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);
|
package/scripts/install.js
CHANGED
|
@@ -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
|
-
|
|
13
|
-
const
|
|
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:
|
|
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:
|
|
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
|
}
|