pi-cockpit 0.16.0 → 0.17.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/README.md CHANGED
@@ -1,196 +1,196 @@
1
- # pi-cockpit
2
-
3
- A responsive **agent cockpit** for [Pi](https://pi.dev): wide terminals get a docked Maestro operations sidebar, narrow terminals automatically fall back to the existing Todo and Agent widgets, and every layout keeps the Starship-style footer.
4
-
5
- It is the third plugin of the `pi-maestro-flow` project (alongside `pi-maestro-flow` and `pi-maestro-teammate`). It is installed and registered with `pi-maestro-flow`, but it also runs standalone. Current version: **0.9.1**.
6
-
7
- The dock is a non-capturing top-right overlay. Cockpit reserves its columns by wrapping the active TUI renderer at runtime, so the Pi workspace reflows instead of rendering underneath it. No Pi source files are modified.
8
-
9
- ```
10
- ┌─ AGENTS · 2 running ─────────────────────────────┐ ← setWidget(aboveEditor)
11
- │ ⠋ explorer #a1f3c2 map auth read routes.ts │
12
- │ ⠋ explorer #b9e014 trace jwt read jwt.ts │
13
- ├─ TODO · 1/4 ─────────────────────────────────────┤
14
- │ 01 ✓ map auth entrypoints │
15
- │ 02 ⠋ trace jwt verify │
16
- │ 03 · implement refresh │
17
- │ 04 · add tests │
18
- └──────────────────────────────────────────────────┘
19
- > dispatch a goal… ← editor (Pi native)
20
- pi/stream-70b · ctx [████░░░░] 42% · $0.52 · 01:23 ← setFooter
21
- ```
22
-
23
- ## Install
24
-
25
- `pi-cockpit` comes automatically with the orchestration layer — installing `pi-maestro-flow` pulls `pi-cockpit` and registers it into `settings.packages` on postinstall, no manual setup required. To use it on its own:
26
-
27
- ```bash
28
- pi install npm:pi-cockpit@0.9.1 # standalone from npm
29
- # or, for one run:
30
- pi -e ./packages/pi-cockpit
31
- ```
32
-
33
- ## What it shows
34
-
35
- - **SIDEBAR** — Workflow Session/Run progress, Goal state and budget, Todo tasks, Teammate roster, background jobs, and read-only Team Swarm progress. Empty sections disappear and constrained heights retain current or failed work before secondary detail.
36
- - **AGENTS** — every running teammate as a table row (status spinner · role · id · label · live tail), sorted running-first. Completed/failed agents show their total duration. On narrow terminals this returns to the below-editor widget.
37
- - **TODO** — the active plan as numbered rows with four states (done ✓ / in-progress spinner / blocked ! / pending ·). On narrow terminals this returns to the above-editor widget.
38
- - **Footer** — `provider/model · context gauge · ↑in ↓out · $cost · elapsed · git branch`. `bash_bg` background-job state lives on a **dedicated second footer row** so it no longer competes with the primary line.
39
- - **Thinking timer** — while the model is thinking, the folded thinking row shows a spinner and running elapsed time; when the run ends, it settles to the actual duration (e.g. `thoughts · 8.4s`).
40
- - **Quiet mode** — compresses the seven built-in tool calls (read/bash/edit/write/grep/find/ls) into single-line ✓/✗/⋯ summaries and folds thinking blocks. Two glyph sets available: `check` (✓/✗/⋯) and `dot` (●/○/◌). Toggle with `/cockpit quiet`; turning off requires `/reload` to restore native tool renderers.
41
-
42
- Toggle each block between list and compact with `/cockpit`.
43
-
44
- ## Data sources (and the dependency this implies)
45
-
46
- | Block | Source | Available on bare Pi? |
47
- |-------|--------|------------------------|
48
- | MAESTRO | Versioned `cockpit:maestro-query` / `maestro:ui-snapshot` full snapshots from **pi-maestro-flow** | **No** — Workflow, Goal, and Swarm sections stay hidden |
49
- | AGENTS | `pi.events` channels `teammate:started` / `teammate:message` / `teammate:complete`, broadcast by **pi-maestro-teammate** | **No** — without that extension no events fire, the block stays hidden |
50
- | TODO | the `todo-state` snapshot the **pi-maestro-flow** `todo` tool persists after every mutation (re-read on each `tool_execution_end` and on `session_start`) | **No** — without the `todo` tool the block stays hidden |
51
- | Footer | `ctx.model`, `ctx.getContextUsage()`, session usage totals, `footerData.getGitBranch()` | **Yes** |
52
-
53
- So on a stock Pi the extension loads without error, the Maestro sections stay empty, and the footer remains available. The roster is **self-accumulated from event deltas**; Todo is back-filled from the latest durable `todo-state`. Workflow, Goal, and Swarm use a versioned full-replacement snapshot with generation fencing and a query path for cold-start recovery.
54
-
55
- Cockpit has no package dependency on `pi-maestro-flow`. It observes optional producers through public event contracts; removing a producer hides only the matching section.
56
-
57
- ## Configuration
58
-
59
- `~/.pi/agent/cockpit.json` (created on first run):
60
-
61
- ```json
62
- {
63
- "enabled": true,
64
- "quietMode": false,
65
- "quietSymbols": "check",
66
- "agentsMode": "list",
67
- "todoMode": "list",
68
- "todoExpanded": false,
69
- "stackStyle": "classic",
70
- "hideNativeAgents": true,
71
- "icons": { "mode": "auto" },
72
- "sidebar": {
73
- "mode": "auto",
74
- "width": 40,
75
- "density": "comfortable"
76
- },
77
- "title": {
78
- "enabled": true,
79
- "showSession": true,
80
- "showCwd": false,
81
- "showModel": false,
82
- "showThinking": false,
83
- "showGit": false,
84
- "showMaestro": false,
85
- "maxLength": 80
86
- },
87
- "theme": ""
88
- }
89
- ```
90
-
91
- - `quietMode`: when `true`, compresses built-in tool rendering and folds thinking blocks.
92
- - `quietSymbols`: `"check"` (✓/✗/⋯) or `"dot"` (●/○/◌) lifecycle glyphs for quiet tool rows.
93
- - `agentsMode` / `todoMode`: `"list"` or `"compact"`.
94
- - `todoExpanded`: when `true`, expands the todo widget by default.
95
- - `stackStyle`: `"classic"` keeps the separate Todo/Agent widgets; `"zen"` projects MISSION, WORK, and ACTORS into one borderless stack. The default remains `"classic"`.
96
- - `hideNativeAgents`: when `true`, clears the teammate extension's own `teammate-agents` widget (it draws a similar list *below* the editor) so the two don't duplicate. On by default.
97
- - `sidebar.mode`: `"auto"` or `"on"` enables the dock when at least 72 main columns plus 32 sidebar columns fit; `"off"` always uses widgets.
98
- - `sidebar.width`: persisted dock width, rounded and clamped to `32..56`; default `40`.
99
- - `sidebar.density`: `"comfortable"` or `"compact"`.
100
- - `icons.mode`: `"auto"` (detect Nerd Font), `"nerd"`, or `"ascii"`.
101
- - `title.enabled`: master switch for the terminal tab title (session summary + working state + opt-in tags). On by default.
102
- - `title.showSession`: include the session summary (the `session_info` name, else a short session id) right after `pi`. On by default.
103
- - `title.showCwd`: include the working directory after the session. Off by default — the title stays short, since the tab strip has no room for a wall of tags.
104
- - `title.showModel`: include the active model tag (`m:gpt-5.6-sol`). Off by default.
105
- - `title.showThinking`: include the thinking level tag (`t:high`); skipped while off. Off by default.
106
- - `title.showGit`: include the git branch tag (`git:main`, `git:detached`); read synchronously from `.git/HEAD`, so it costs no process spawn. Off by default.
107
- - `title.showMaestro`: include the Maestro workflow status tag (`wf:running`, `wf:done`). Off by default.
108
- - `title.maxLength`: hard cap on the composed title; the middle is ellided to keep the head and the working-state tail. Clamped to `20..200`; default `80`.
109
- - `title.generationModel`: `"provider/model"` (e.g. `"maestro-qwen/qwen3.8-max"`) of a model registered with the `/api-manager` command (or any other provider pi resolves). When set, the session title is generated by that model over the first completed turn (user prompt + assistant reply) through its OpenAI-compatible endpoint, with a 10s timeout. Empty (default) uses the offline rule-based extractor instead. Generation is best-effort — on any failure the title silently falls back to the rule-based one. Editable from the `/cockpit` settings overlay (`z` or cursor to the `title gen model` row, Enter, type, Enter to save; empty clears back to rule-based).
110
-
111
- The tab title is `frame + pi - <session> - <working state>`. The session part follows Claude Code's chain (`sessionTitle ?? agentTitle ?? haikuTitle ?? default`): the `/session name` title wins, otherwise the generated title, otherwise a short session id. The frame is Claude Code's title chrome: `⠂`/`⠐` braille spinner while a turn runs, static `✳` when idle; failure replaces it with `✗`. On quit, Cockpit clears the title so no stale tab label lingers (Claude Code's `CLEAR_TERMINAL_TITLE`).
112
- - `theme`: named theme override; empty string follows the Pi session theme.
113
-
114
- ## Commands
115
-
116
- - `/cockpit` — opens the settings overlay.
117
- - `/maestro-settings` — opens the unified settings shell (Cockpit, Flow, Teammate and integrations).
118
- - `Alt+J` — opens the background Bash jobs overlay (live status, command, cwd, duration, output tail).
119
- - `Alt+L` — browses the visible Cockpit surface. In Zen widget mode, arrows or `j/k` select rows, first Enter expands inline details, second Enter opens the entity sheet/overlay, and Esc steps back. In the Agent overlay, `m` makes the selected agent the editor input target.
120
- - `/cockpit sidebar` — reports the current sidebar mode, width, and density.
121
- - `/cockpit sidebar auto|on|off` — selects dock behavior.
122
- - `/cockpit sidebar resize` or `Ctrl+Shift+R` — enters temporary Resize mode. Left/Right adjusts one column, Shift+Left/Shift+Right adjusts four, Enter accepts, and Escape rolls back. Mouse reporting is active only during Resize mode.
123
- - `/cockpit quiet` — toggles quiet mode; `/cockpit bg` shows background jobs.
124
- - `/theme` — switch theme with live preview; `/theme <name>` applies directly. `cockpit-zen` is the restrained warm-gold theme designed for the Zen stack; selecting it does not change `stackStyle`. Pi ships no standalone theme command; cockpit provides one.
125
-
126
- ## Sidebar compatibility
127
-
128
- The split-pane wrapper depends on Pi's current TUI renderer shape and is verified against Pi `0.83.0`. A render integration failure disables the split and retries the original renderer at full width.
129
-
130
- Do not enable `pi-cockpit`'s dock and `pi-atelier@0.7.0`'s sidebar together. Both reserve columns by wrapping the same renderer, and `pi-atelier@0.7.0` does not participate in Cockpit's split-owner marker protocol. Use `"sidebar": { "mode": "off" }` when running Atelier.
131
-
132
- ## Terminal feasibility — what this design deliberately does NOT do
133
-
134
- The original mockup was a browser page; a terminal is an ANSI stream with no DOM, no CSS, no focus, no hover. Four mockup effects are **out of scope** here, with replacements:
135
-
136
- | Mockup effect | Why it can't work in a TUI | Replacement |
137
- |---------------|----------------------------|-------------|
138
- | Input-focus "power-up" glow (`:has(:focus)`) | no focus pseudo-class; `render(width)` can't see focus | the stack's header dot turns accent-green while an agent is running |
139
- | Hover-to-expand chips/rows | no hover in a terminal | list mode shows everything; compact mode is one line; `/cockpit` toggles |
140
- | Scanlines / logo light-up animation | no overlay/animation layer | a braille spinner frame, advanced on each redraw |
141
- | In-stream thinking-collapse / edit progress bar / colored bash stdout | built-in message & built-in tool rendering is **not** replaceable by extensions (`renderCall`/`renderResult` only apply to tools *you* register) | the conversation stream is left to Pi's native renderer — it is context, not a cockpit deliverable |
142
-
143
- ## Claude Code-style interactions (all opt-in, all default off)
144
-
145
- Three independent settings, each stored in `cockpit.json` and exposed bilingually in `/cockpit` and `/maestro-settings`:
146
-
147
- | Setting | Default | Applies | What it does |
148
- |---|---|---|---|
149
- | `doubleEscapeClearInput` | off | after `/reload` | Press Escape twice quickly to clear a non-empty input draft. The first Escape keeps its native meaning; an empty-draft double-Escape stays pi's rewind/tree action. Requires the Cockpit custom editor (`/reload`). |
150
- | `fullscreenInput` | off | after `/reload` | Alternate screen with the editor fixed at the bottom and an application-scrolled transcript. Wheel scrolls history while the editor stays put; `↑ n new · click to bottom` appears when new output arrives while you are scrolled up. Replaces terminal-native scrollback/search inside fullscreen. Requires the Cockpit custom editor (`/reload`). |
151
- | `copyOnSelect` | off | live | Drag inside the fullscreen transcript to select; the visible text is copied to the clipboard on release. Effective only while `fullscreenInput` is active. |
152
-
153
- Interaction rules:
154
-
155
- - The three settings are independent; `copyOnSelect` is inert without `fullscreenInput`.
156
- - The legacy `pinEditorBottom` keeps working in normal mode and is ignored inside fullscreen.
157
- - If another extension already owns a custom editor, `doubleEscapeClearInput` and `fullscreenInput` fail closed with one warning (they share the Cockpit custom editor) and never overwrite it.
158
- - A clipboard failure shows a warning and keeps the selection so you can retry.
159
-
160
- ### Terminal capability matrix
161
-
162
- Fullscreen needs an alternate screen (`?1049`) and SGR mouse reporting (`1006`/`1002`); copy-on-select additionally needs drag selection to be application-owned. No universal support is claimed — this is best effort, opt-in, and `TERM=dumb`/unset is refused with a warning.
163
-
164
- | Terminal | Alternate screen | SGR mouse | Copy-on-select | Verified |
165
- |---|---|---|---|---|
166
- | iTerm2 | ✓ | ✓ | ✓ | expected |
167
- | Ghostty | ✓ | ✓ | ✓ | expected |
168
- | WezTerm | ✓ | ✓ | ✓ | expected |
169
- | kitty | ✓ | ✓ | ✓ | expected |
170
- | Windows Terminal | ✓ | ✓ | ✓ | expected |
171
- | VS Code integrated terminal | ✓ | ✓ | ✓ | expected |
172
- | tmux (no `-T` config) | depends | depends | depends | not claimed |
173
- | `TERM=dumb` | ✗ | ✗ | ✗ | refused with a warning |
174
-
175
- ### Manual smoke checklist
176
-
177
- 1. `/maestro-settings` → Cockpit → enable `doubleEscapeClearInput`, then `/reload`; type a draft and press Escape twice — the draft clears; a single Escape does nothing; with an empty draft two Escapes still open the tree selector.
178
- 2. Enable `fullscreenInput`, `/reload`; scroll the transcript with the mouse wheel — the editor stays fixed at the bottom; while scrolled up, new output shows the `↑ n new` hint; clicking it returns to the bottom.
179
- 3. Enable `copyOnSelect` (live, no reload); drag across transcript lines — the visible text lands on the clipboard (verify with `pbpaste` on macOS); a plain click does not copy; a copy failure (e.g. headless) shows a warning and keeps the selection.
180
- 4. Toggle everything off and `/reload` — behavior returns to stock pi.
181
- 5. If a terminal ever gets stuck in a blank alternate screen (crash while fullscreen), run `reset` to restore the normal screen.
182
-
183
- ## Local development
184
-
185
- ```bash
186
- cd packages/pi-cockpit
187
- npm test
188
- npm run typecheck
189
- npm pack --dry-run
190
- ```
191
-
192
- The package lives inside the `pi-maestro-flow` monorepo under `packages/` so it resolves `@earendil-works/*` types from the root `node_modules`. It is also an exact-pinned dependency of `pi-maestro-flow` and ships as its own npm package (`pi-cockpit`).
193
-
194
- ## License
195
-
196
- MIT. The split-pane behavior is adapted from `pi-atelier` under its MIT license; attribution is retained in `src/split-pane.ts`.
1
+ # pi-cockpit
2
+
3
+ A responsive **agent cockpit** for [Pi](https://pi.dev): wide terminals get a docked Maestro operations sidebar, narrow terminals automatically fall back to the existing Todo and Agent widgets, and every layout keeps the Starship-style footer.
4
+
5
+ It is the third plugin of the `pi-maestro-flow` project (alongside `pi-maestro-flow` and `pi-maestro-teammate`). It is installed and registered with `pi-maestro-flow`, but it also runs standalone. Current version: **0.9.1**.
6
+
7
+ The dock is a non-capturing top-right overlay. Cockpit reserves its columns by wrapping the active TUI renderer at runtime, so the Pi workspace reflows instead of rendering underneath it. No Pi source files are modified.
8
+
9
+ ```
10
+ ┌─ AGENTS · 2 running ─────────────────────────────┐ ← setWidget(aboveEditor)
11
+ │ ⠋ explorer #a1f3c2 map auth read routes.ts │
12
+ │ ⠋ explorer #b9e014 trace jwt read jwt.ts │
13
+ ├─ TODO · 1/4 ─────────────────────────────────────┤
14
+ │ 01 ✓ map auth entrypoints │
15
+ │ 02 ⠋ trace jwt verify │
16
+ │ 03 · implement refresh │
17
+ │ 04 · add tests │
18
+ └──────────────────────────────────────────────────┘
19
+ > dispatch a goal… ← editor (Pi native)
20
+ pi/stream-70b · ctx [████░░░░] 42% · $0.52 · 01:23 ← setFooter
21
+ ```
22
+
23
+ ## Install
24
+
25
+ `pi-cockpit` comes automatically with the orchestration layer — installing `pi-maestro-flow` pulls `pi-cockpit` and registers it into `settings.packages` on postinstall, no manual setup required. To use it on its own:
26
+
27
+ ```bash
28
+ pi install npm:pi-cockpit@0.9.1 # standalone from npm
29
+ # or, for one run:
30
+ pi -e ./packages/pi-cockpit
31
+ ```
32
+
33
+ ## What it shows
34
+
35
+ - **SIDEBAR** — Workflow Session/Run progress, Goal state and budget, Todo tasks, Teammate roster, background jobs, and read-only Team Swarm progress. Empty sections disappear and constrained heights retain current or failed work before secondary detail.
36
+ - **AGENTS** — every running teammate as a table row (status spinner · role · id · label · live tail), sorted running-first. Completed/failed agents show their total duration. On narrow terminals this returns to the below-editor widget.
37
+ - **TODO** — the active plan as numbered rows with four states (done ✓ / in-progress spinner / blocked ! / pending ·). On narrow terminals this returns to the above-editor widget.
38
+ - **Footer** — `provider/model · context gauge · ↑in ↓out · $cost · elapsed · git branch`. `bash_bg` background-job state lives on a **dedicated second footer row** so it no longer competes with the primary line.
39
+ - **Thinking timer** — while the model is thinking, the folded thinking row shows a spinner and running elapsed time; when the run ends, it settles to the actual duration (e.g. `thoughts · 8.4s`).
40
+ - **Quiet mode** — compresses the seven built-in tool calls (read/bash/edit/write/grep/find/ls) into single-line ✓/✗/⋯ summaries and folds thinking blocks. Two glyph sets available: `check` (✓/✗/⋯) and `dot` (●/○/◌). Toggle with `/cockpit quiet`; turning off requires `/reload` to restore native tool renderers.
41
+
42
+ Toggle each block between list and compact with `/cockpit`.
43
+
44
+ ## Data sources (and the dependency this implies)
45
+
46
+ | Block | Source | Available on bare Pi? |
47
+ |-------|--------|------------------------|
48
+ | MAESTRO | Versioned `cockpit:maestro-query` / `maestro:ui-snapshot` full snapshots from **pi-maestro-flow** | **No** — Workflow, Goal, and Swarm sections stay hidden |
49
+ | AGENTS | `pi.events` channels `teammate:started` / `teammate:message` / `teammate:complete`, broadcast by **pi-maestro-teammate** | **No** — without that extension no events fire, the block stays hidden |
50
+ | TODO | the `todo-state` snapshot the **pi-maestro-flow** `todo` tool persists after every mutation (re-read on each `tool_execution_end` and on `session_start`) | **No** — without the `todo` tool the block stays hidden |
51
+ | Footer | `ctx.model`, `ctx.getContextUsage()`, session usage totals, `footerData.getGitBranch()` | **Yes** |
52
+
53
+ So on a stock Pi the extension loads without error, the Maestro sections stay empty, and the footer remains available. The roster is **self-accumulated from event deltas**; Todo is back-filled from the latest durable `todo-state`. Workflow, Goal, and Swarm use a versioned full-replacement snapshot with generation fencing and a query path for cold-start recovery.
54
+
55
+ Cockpit has no package dependency on `pi-maestro-flow`. It observes optional producers through public event contracts; removing a producer hides only the matching section.
56
+
57
+ ## Configuration
58
+
59
+ `~/.pi/agent/cockpit.json` (created on first run):
60
+
61
+ ```json
62
+ {
63
+ "enabled": true,
64
+ "quietMode": false,
65
+ "quietSymbols": "check",
66
+ "agentsMode": "list",
67
+ "todoMode": "list",
68
+ "todoExpanded": false,
69
+ "stackStyle": "classic",
70
+ "hideNativeAgents": true,
71
+ "icons": { "mode": "auto" },
72
+ "sidebar": {
73
+ "mode": "auto",
74
+ "width": 40,
75
+ "density": "comfortable"
76
+ },
77
+ "title": {
78
+ "enabled": true,
79
+ "showSession": true,
80
+ "showCwd": false,
81
+ "showModel": false,
82
+ "showThinking": false,
83
+ "showGit": false,
84
+ "showMaestro": false,
85
+ "maxLength": 80
86
+ },
87
+ "theme": ""
88
+ }
89
+ ```
90
+
91
+ - `quietMode`: when `true`, compresses built-in tool rendering and folds thinking blocks.
92
+ - `quietSymbols`: `"check"` (✓/✗/⋯) or `"dot"` (●/○/◌) lifecycle glyphs for quiet tool rows.
93
+ - `agentsMode` / `todoMode`: `"list"` or `"compact"`.
94
+ - `todoExpanded`: when `true`, expands the todo widget by default.
95
+ - `stackStyle`: `"classic"` keeps the separate Todo/Agent widgets; `"zen"` projects MISSION, WORK, and ACTORS into one borderless stack. The default remains `"classic"`.
96
+ - `hideNativeAgents`: when `true`, clears the teammate extension's own `teammate-agents` widget (it draws a similar list *below* the editor) so the two don't duplicate. On by default.
97
+ - `sidebar.mode`: `"auto"` or `"on"` enables the dock when at least 72 main columns plus 32 sidebar columns fit; `"off"` always uses widgets.
98
+ - `sidebar.width`: persisted dock width, rounded and clamped to `32..56`; default `40`.
99
+ - `sidebar.density`: `"comfortable"` or `"compact"`.
100
+ - `icons.mode`: `"auto"` (detect Nerd Font), `"nerd"`, or `"ascii"`.
101
+ - `title.enabled`: master switch for the terminal tab title (session summary + working state + opt-in tags). On by default.
102
+ - `title.showSession`: include the session summary (the `session_info` name, else a short session id) right after `pi`. On by default.
103
+ - `title.showCwd`: include the working directory after the session. Off by default — the title stays short, since the tab strip has no room for a wall of tags.
104
+ - `title.showModel`: include the active model tag (`m:gpt-5.6-sol`). Off by default.
105
+ - `title.showThinking`: include the thinking level tag (`t:high`); skipped while off. Off by default.
106
+ - `title.showGit`: include the git branch tag (`git:main`, `git:detached`); read synchronously from `.git/HEAD`, so it costs no process spawn. Off by default.
107
+ - `title.showMaestro`: include the Maestro workflow status tag (`wf:running`, `wf:done`). Off by default.
108
+ - `title.maxLength`: hard cap on the composed title; the middle is ellided to keep the head and the working-state tail. Clamped to `20..200`; default `80`.
109
+ - `title.generationModel`: `"provider/model"` (e.g. `"maestro-qwen/qwen3.8-max"`) of a model registered with the `/api-manager` command (or any other provider pi resolves). When set, the session title is generated by that model over the first completed turn (user prompt + assistant reply) through its OpenAI-compatible endpoint, with a 10s timeout. Empty (default) uses the offline rule-based extractor instead. Generation is best-effort — on any failure the title silently falls back to the rule-based one. Editable from the `/cockpit` settings overlay (`z` or cursor to the `title gen model` row, Enter, type, Enter to save; empty clears back to rule-based).
110
+
111
+ The tab title is `frame + pi - <session> - <working state>`. The session part follows Claude Code's chain (`sessionTitle ?? agentTitle ?? haikuTitle ?? default`): the `/session name` title wins, otherwise the generated title, otherwise a short session id. The frame is Claude Code's title chrome: `⠂`/`⠐` braille spinner while a turn runs, static `✳` when idle; failure replaces it with `✗`. On quit, Cockpit clears the title so no stale tab label lingers (Claude Code's `CLEAR_TERMINAL_TITLE`).
112
+ - `theme`: named theme override; empty string follows the Pi session theme.
113
+
114
+ ## Commands
115
+
116
+ - `/cockpit` — opens the settings overlay.
117
+ - `/maestro-settings` — opens the unified settings shell (Cockpit, Flow, Teammate and integrations).
118
+ - `Alt+J` — opens the background Bash jobs overlay (live status, command, cwd, duration, output tail).
119
+ - `Alt+L` — browses the visible Cockpit surface. In Zen widget mode, arrows or `j/k` select rows, first Enter expands inline details, second Enter opens the entity sheet/overlay, and Esc steps back. In the Agent overlay, `m` makes the selected agent the editor input target.
120
+ - `/cockpit sidebar` — reports the current sidebar mode, width, and density.
121
+ - `/cockpit sidebar auto|on|off` — selects dock behavior.
122
+ - `/cockpit sidebar resize` or `Ctrl+Shift+R` — enters temporary Resize mode. Left/Right adjusts one column, Shift+Left/Shift+Right adjusts four, Enter accepts, and Escape rolls back. Mouse reporting is active only during Resize mode.
123
+ - `/cockpit quiet` — toggles quiet mode; `/cockpit bg` shows background jobs.
124
+ - `/theme` — switch theme with live preview; `/theme <name>` applies directly. `cockpit-zen` is the restrained warm-gold theme designed for the Zen stack; selecting it does not change `stackStyle`. Pi ships no standalone theme command; cockpit provides one.
125
+
126
+ ## Sidebar compatibility
127
+
128
+ The split-pane wrapper depends on Pi's current TUI renderer shape and is verified against Pi `0.83.0`. A render integration failure disables the split and retries the original renderer at full width.
129
+
130
+ Do not enable `pi-cockpit`'s dock and `pi-atelier@0.7.0`'s sidebar together. Both reserve columns by wrapping the same renderer, and `pi-atelier@0.7.0` does not participate in Cockpit's split-owner marker protocol. Use `"sidebar": { "mode": "off" }` when running Atelier.
131
+
132
+ ## Terminal feasibility — what this design deliberately does NOT do
133
+
134
+ The original mockup was a browser page; a terminal is an ANSI stream with no DOM, no CSS, no focus, no hover. Four mockup effects are **out of scope** here, with replacements:
135
+
136
+ | Mockup effect | Why it can't work in a TUI | Replacement |
137
+ |---------------|----------------------------|-------------|
138
+ | Input-focus "power-up" glow (`:has(:focus)`) | no focus pseudo-class; `render(width)` can't see focus | the stack's header dot turns accent-green while an agent is running |
139
+ | Hover-to-expand chips/rows | no hover in a terminal | list mode shows everything; compact mode is one line; `/cockpit` toggles |
140
+ | Scanlines / logo light-up animation | no overlay/animation layer | a braille spinner frame, advanced on each redraw |
141
+ | In-stream thinking-collapse / edit progress bar / colored bash stdout | built-in message & built-in tool rendering is **not** replaceable by extensions (`renderCall`/`renderResult` only apply to tools *you* register) | the conversation stream is left to Pi's native renderer — it is context, not a cockpit deliverable |
142
+
143
+ ## Claude Code-style interactions (all opt-in, all default off)
144
+
145
+ Three independent settings, each stored in `cockpit.json` and exposed bilingually in `/cockpit` and `/maestro-settings`:
146
+
147
+ | Setting | Default | Applies | What it does |
148
+ |---|---|---|---|
149
+ | `doubleEscapeClearInput` | off | after `/reload` | Press Escape twice quickly to clear a non-empty input draft. The first Escape keeps its native meaning; an empty-draft double-Escape stays pi's rewind/tree action. Requires the Cockpit custom editor (`/reload`). |
150
+ | `fullscreenInput` | off | after `/reload` | Alternate screen with the editor fixed at the bottom and an application-scrolled transcript. Wheel scrolls history while the editor stays put; `↑ n new · click to bottom` appears when new output arrives while you are scrolled up. Replaces terminal-native scrollback/search inside fullscreen. Requires the Cockpit custom editor (`/reload`). |
151
+ | `copyOnSelect` | off | live | Drag inside the fullscreen transcript to select; the visible text is copied to the clipboard on release. Effective only while `fullscreenInput` is active. |
152
+
153
+ Interaction rules:
154
+
155
+ - The three settings are independent; `copyOnSelect` is inert without `fullscreenInput`.
156
+ - The legacy `pinEditorBottom` keeps working in normal mode and is ignored inside fullscreen.
157
+ - If another extension already owns a custom editor, `doubleEscapeClearInput` and `fullscreenInput` fail closed with one warning (they share the Cockpit custom editor) and never overwrite it.
158
+ - A clipboard failure shows a warning and keeps the selection so you can retry.
159
+
160
+ ### Terminal capability matrix
161
+
162
+ Fullscreen needs an alternate screen (`?1049`) and SGR mouse reporting (`1006`/`1002`); copy-on-select additionally needs drag selection to be application-owned. No universal support is claimed — this is best effort, opt-in, and `TERM=dumb`/unset is refused with a warning.
163
+
164
+ | Terminal | Alternate screen | SGR mouse | Copy-on-select | Verified |
165
+ |---|---|---|---|---|
166
+ | iTerm2 | ✓ | ✓ | ✓ | expected |
167
+ | Ghostty | ✓ | ✓ | ✓ | expected |
168
+ | WezTerm | ✓ | ✓ | ✓ | expected |
169
+ | kitty | ✓ | ✓ | ✓ | expected |
170
+ | Windows Terminal | ✓ | ✓ | ✓ | expected |
171
+ | VS Code integrated terminal | ✓ | ✓ | ✓ | expected |
172
+ | tmux (no `-T` config) | depends | depends | depends | not claimed |
173
+ | `TERM=dumb` | ✗ | ✗ | ✗ | refused with a warning |
174
+
175
+ ### Manual smoke checklist
176
+
177
+ 1. `/maestro-settings` → Cockpit → enable `doubleEscapeClearInput`, then `/reload`; type a draft and press Escape twice — the draft clears; a single Escape does nothing; with an empty draft two Escapes still open the tree selector.
178
+ 2. Enable `fullscreenInput`, `/reload`; scroll the transcript with the mouse wheel — the editor stays fixed at the bottom; while scrolled up, new output shows the `↑ n new` hint; clicking it returns to the bottom.
179
+ 3. Enable `copyOnSelect` (live, no reload); drag across transcript lines — the visible text lands on the clipboard (verify with `pbpaste` on macOS); a plain click does not copy; a copy failure (e.g. headless) shows a warning and keeps the selection.
180
+ 4. Toggle everything off and `/reload` — behavior returns to stock pi.
181
+ 5. If a terminal ever gets stuck in a blank alternate screen (crash while fullscreen), run `reset` to restore the normal screen.
182
+
183
+ ## Local development
184
+
185
+ ```bash
186
+ cd packages/pi-cockpit
187
+ npm test
188
+ npm run typecheck
189
+ npm pack --dry-run
190
+ ```
191
+
192
+ The package lives inside the `pi-maestro-flow` monorepo under `packages/` so it resolves `@earendil-works/*` types from the root `node_modules`. It is also an exact-pinned dependency of `pi-maestro-flow` and ships as its own npm package (`pi-cockpit`).
193
+
194
+ ## License
195
+
196
+ MIT. The split-pane behavior is adapted from `pi-atelier` under its MIT license; attribution is retained in `src/split-pane.ts`.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-maestro-settings-core",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Versioned Settings and i18n contracts shared by Pi Maestro plugins.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -127,6 +127,48 @@ export interface SettingsInvokeActionResultV1 {
127
127
  message?: string;
128
128
  }
129
129
 
130
+ /**
131
+ * Ask a provider for the values an editor's `optionsSource` names.
132
+ *
133
+ * Carries no abort signal: the protocol travels by event and must stay
134
+ * serializable, so a provider that reaches a slow system bounds the wait
135
+ * itself and reports exhaustion as a failed result.
136
+ */
137
+ export interface SettingsListOptionsRequestV1 {
138
+ context: SettingsContextV1;
139
+ /** The setting being edited. */
140
+ key: string;
141
+ /** The `optionsSource` its editor declared, so one provider can serve several. */
142
+ optionsSource: string;
143
+ }
144
+
145
+ /**
146
+ * The values an options source published, or why it could not answer.
147
+ *
148
+ * A failure is distinct from an empty list. Empty means the source answered and
149
+ * offers nothing; `failure` means it never answered, which the shell shows to
150
+ * the operator instead of an empty picker they would read as "no choices".
151
+ */
152
+ export interface SettingsListOptionsResultV1 {
153
+ options: readonly SettingsSourcedOption[];
154
+ /** Already-localized reason the source could not be read. */
155
+ failure?: string;
156
+ }
157
+
158
+ /**
159
+ * One value an options source published.
160
+ *
161
+ * `label` is raw display text, not a catalogue key: these values come from a
162
+ * system outside this build at runtime, so no translation catalogue can carry
163
+ * them. That is the difference from `SettingsSelectOption`, whose values are
164
+ * declared here and therefore translatable.
165
+ */
166
+ export interface SettingsSourcedOption {
167
+ value: string;
168
+ label: string;
169
+ description?: string;
170
+ }
171
+
130
172
  export interface SettingsRuntimeFailureV1 {
131
173
  key: string;
132
174
  messageKey: string;
@@ -154,4 +196,13 @@ export interface SettingsProviderV1 {
154
196
  rollback?(request: SettingsRollbackRequestV1): MaybePromise<SettingsRollbackResultV1>;
155
197
  applyRuntime?(request: SettingsApplyRuntimeRequestV1): MaybePromise<SettingsApplyRuntimeResultV1>;
156
198
  invokeAction?(request: SettingsInvokeActionRequestV1): MaybePromise<SettingsInvokeActionResultV1>;
199
+ /**
200
+ * Resolve the values an editor's `optionsSource` names.
201
+ *
202
+ * A provider declaring `optionsSource` on any editor must implement this;
203
+ * without it the shell has a picker it can never fill. Unlike `describe`,
204
+ * this may perform I/O and may be slow, so the shell calls it when an
205
+ * operator opens that editor rather than while listing settings.
206
+ */
207
+ listOptions?(request: SettingsListOptionsRequestV1): MaybePromise<SettingsListOptionsResultV1>;
157
208
  }