# Process Shell

> For agents: start with the [agent guide](https://pi-tui.ratstack.sh/llms.txt). Every page here has a Markdown twin, and every pattern has a JSON record.

Lifecycle / Lifecycle and mounting · `process-shell` · [HTML](https://pi-tui.ratstack.sh/patterns/process-shell/) · [JSON](https://pi-tui.ratstack.sh/patterns/process-shell.json) · [all patterns](https://pi-tui.ratstack.sh/patterns.md)

Also known as Terminal process view.

## Intent

Bind a temporary terminal process view to one custom interaction.

## Motivation

Nico's interactive shell mounts or attaches a process inside a custom UI with an exit result.

## Applicability

- Use this when a command needs interactive terminal input instead of captured output.

## Structure

```text
process / attachment -> view
exit / cancel -> done(result)
finish -> owned cleanup
```

## Participants

- [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays): Mounts one interaction and resolves through done.
- [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Renders width-bounded lines and invalidates cached output.
- [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model): Requests a coalesced redraw after state changes.
- `Process owner`: Constructs or attaches the terminal job and owns failure cleanup.

Pi screen APIs: [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `custom`, `Text`, `matchesKey`

## Consequences

- An interactive process can have a temporary terminal view.
- PTY construction and failure cleanup remain outside Pi's UI API.

## Implementation

- Resolve process exit and cancellation exactly once.
- Clean up handlers and process ownership if initialization fails.
- Pi does not supply the PTY implementation.

## States

Each state was rendered at 40, 60, 80 and 120 columns in Pi's dark and light themes. Each frame links one WebP. An animated frame links its first image, then the animation.

### Process attached

`running`: Show synthetic process output in a terminal-like custom view.

Checks at every width and theme:

- width: ✓ pass
- style-leak: ✓ pass
- hard-coded-colour: ✓ pass
- height: ✓ pass

Frames:

- 40 columns: ![Process Shell, Process attached, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/running-40-dark.862ea8761875.webp) ![Process Shell, Process attached, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/running-40-light.81a89e9683fb.webp)
- 60 columns: ![Process Shell, Process attached, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/running-60-dark.030bb9164f35.webp) ![Process Shell, Process attached, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/running-60-light.43032130591e.webp)
- 80 columns: ![Process Shell, Process attached, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/running-80-dark.cb16e79b3916.webp) ![Process Shell, Process attached, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/running-80-light.f93c22e5a425.webp)
- 120 columns: ![Process Shell, Process attached, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/running-120-dark.ce0ae05f90f8.webp) ![Process Shell, Process attached, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/running-120-light.95e36a10d1c5.webp)

### Process exit

`exited`: Resolve an exit result and return to the host.

Checks at every width and theme:

- width: ✓ pass
- style-leak: ✓ pass
- hard-coded-colour: ✓ pass
- height: ✓ pass

Frames:

- 40 columns: ![Process Shell, Process exit, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/exited-40-dark.69f3a7d808cf.webp) ![Process Shell, Process exit, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/exited-40-light.21d19d5513ed.webp)
- 60 columns: ![Process Shell, Process exit, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/exited-60-dark.870a20dee668.webp) ![Process Shell, Process exit, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/exited-60-light.8694341cb4b0.webp)
- 80 columns: ![Process Shell, Process exit, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/exited-80-dark.92695938bfd4.webp) ![Process Shell, Process exit, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/exited-80-light.2912192cda46.webp)
- 120 columns: ![Process Shell, Process exit, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/exited-120-dark.355a935e9c65.webp) ![Process Shell, Process exit, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/exited-120-light.0775fc89e92e.webp)

### Cancelled view

`cancelled`: Resolve cancellation and clean up temporary input ownership.

Checks at every width and theme:

- width: ✓ pass
- style-leak: ✓ pass
- hard-coded-colour: ✓ pass
- height: ✓ pass

Frames:

- 40 columns: ![Process Shell, Cancelled view, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/cancelled-40-dark.8587d8264f88.webp) ![Process Shell, Cancelled view, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/cancelled-40-light.4032caaa188e.webp)
- 60 columns: ![Process Shell, Cancelled view, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/cancelled-60-dark.259f4356c709.webp) ![Process Shell, Cancelled view, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/cancelled-60-light.11dd53616210.webp)
- 80 columns: ![Process Shell, Cancelled view, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/cancelled-80-dark.9c0cdd03f5dc.webp) ![Process Shell, Cancelled view, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/cancelled-80-light.fdb0435985a7.webp)
- 120 columns: ![Process Shell, Cancelled view, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/process-shell/cancelled-120-dark.1e692c14ebb9.webp) ![Process Shell, Cancelled view, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/process-shell/cancelled-120-light.7af960743566.webp)

## Sample code

`stories/patterns/lifecycle/process-shell.ts`, the story the frames above were rendered from.

```ts
// Process Shell: bind one synthetic process view to one custom interaction.
import { Text, matchesKey, Key } from "@earendil-works/pi-tui";
import type { PatternStory } from "../../../src/pattern.ts";

type ProcessResult = { kind: "exit"; code: number } | { kind: "cancelled" };

export const story: PatternStory = {
  id: "process-shell", title: "Process Shell", kind: "screen",
  apis: ["custom", "Text", "TUI.requestRender", "matchesKey"],
  states: [
    { id: "running", label: "Process attached" },
    { id: "exited", label: "Process exit", steps: [{ type: "action", name: "exit" }] },
    { id: "cancelled", label: "Cancelled view", steps: [
      { type: "action", name: "attach" }, { type: "keys", data: "\x1b" },
    ] },
  ],
  setup({ ui, theme, action }) {
    let processExit: () => void = () => {};
    ui.setEditorText("Summarize the check output.");
    const attach = () => {
      ui.setWidget("process", [theme.fg("accent", "Attached to synthetic parser checks")]);
      void ui.custom<ProcessResult>((tui, _theme, _keys, done) => {
        // Synthetic driver only. Pi supplies the view, not a PTY.
        let state: "attached" | "closed" = "attached";
        const finish = (result: ProcessResult) => {
          if (state === "closed") return;
          state = "closed";
          done(result);
        };
        processExit = () => finish({ kind: "exit", code: 0 });
        const output = new Text(theme.fg("toolTitle", "$ check parser") + "\n" +
          theme.fg("toolOutput", "PASS token stream\nPASS empty input\nWaiting for final assertion…") + "\n" +
          theme.fg("muted", "Esc cancels the attached view"), 0, 0);
        tui.requestRender();
        return {
          render: width => output.render(width), invalidate: () => output.invalidate(),
          handleInput: data => { if (matchesKey(data, Key.escape)) finish({ kind: "cancelled" }); },
          dispose: () => { state = "closed"; processExit = () => {}; },
        };
      }).then(result => ui.setWidget("process", [result.kind === "exit"
        ? theme.fg("success", "Process exited · code " + result.code)
        : theme.fg("warning", "View cancelled · input owner released")]));
    };
    action("exit", () => processExit());
    action("attach", attach);
    attach();
  },
};
```

## Known uses: seen in Nico's repos

- [**pi-interactive-shell**](https://github.com/nicobailon/pi-interactive-shell): Interactive shell overlay lifecycle and terminal sizing
  - [overlay-component.ts:20-205](https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/overlay-component.ts#L20-L205) @77df9a81
- [**dot314**](https://github.com/nicobailon/dot314): Host an interactive shell inside a custom TUI lifecycle
  - [extensions/interactive-shell.ts:145-180](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/interactive-shell.ts#L145-L180) @17cce138

## For agents: choose and check

Choose Process Shell when your job matches its intent and applicability above. Its neighbours in Lifecycle and mounting are listed below. Read the one whose intent fits your job more closely before you commit.

- [Elastic Overlay](https://pi-tui.ratstack.sh/patterns/elastic-overlay.md): Resolve overlay size and placement from current terminal dimensions.
- [Event Relay](https://pi-tui.ratstack.sh/patterns/event-relay.md): Update visible UI from named extension event channels.
- [Output Vault](https://pi-tui.ratstack.sh/patterns/output-vault.md): Keep completed job output available after its foreground view closes.
- [Deferred Crest](https://pi-tui.ratstack.sh/patterns/deferred-crest.md): Mount optional startup content only after deferred discovery remains eligible.
- [Session Memento](https://pi-tui.ratstack.sh/patterns/session-memento.md): Reconstruct deliberate view state from typed custom session entries.
- [Mode Fence](https://pi-tui.ratstack.sh/patterns/mode-fence.md): Keep terminal components separate from dialog-capable and no-UI modes.
- [Snapshot Lens](https://pi-tui.ratstack.sh/patterns/snapshot-lens.md): Derive compact status and bounded detail from lifecycle snapshots.

Check your version:

- Render your version at 40, 60, 80 and 120 columns in the dark and light themes.
- Check width: no rendered line is wider than the terminal.
- Check style-leak: no line ends with colour, bold or a link still switched on.
- Check hard-coded-colour: every colour on screen comes from the active theme.
- Check height: the output fits in the rows the terminal has.
- Read the Pi 1.0.3 docs for [`ctx.ui.custom`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays), [`Component`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), [`TUI.requestRender`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model), `custom`, `Text`, `matchesKey` before you use them.
- Every reference frame gets the verdict its state expects.

Next actions: `related({ id: "process-shell" })` lists what to read next, and `states({ id: "process-shell", width: 40 })` returns every frame and verdict at one width. The [build-pi-tui-component skill](https://pi-tui.ratstack.sh/skills/build-pi-tui-component.md) walks through the whole loop.

## Related

- [Output Vault](https://pi-tui.ratstack.sh/patterns/output-vault.md): keeps output beyond the view

## Linked from

- [Output Vault](https://pi-tui.ratstack.sh/patterns/output-vault.md): owns the temporary foreground interaction
