projmux 0.6.2 → 0.6.4

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.
@@ -0,0 +1,794 @@
1
+ # tmux Surface Inventory
2
+
3
+ Phase 0 artifact for the Mux substrate independence roadmap.
4
+
5
+ This document inventories the tmux command, format variable, hook, and option
6
+ surface that projmux currently calls or generates. It is intentionally an
7
+ inventory, not a backend abstraction design. Runtime behavior must remain on
8
+ the existing tmux path until a later phase extracts a mux contract.
9
+
10
+ ## Scope And Method
11
+
12
+ Primary production sources:
13
+
14
+ - `internal/integrations/tmux/`
15
+ - `internal/integrations/sessionstate/`
16
+ - `internal/app/`
17
+ - Generated tmux config from `projmux tmux print-config`,
18
+ `projmux tmux print-app-config`, and `projmux shell`
19
+
20
+ Support and fixture sources:
21
+
22
+ - `test/`
23
+ - `scripts/poc-native-picker-no-fzf-*.sh`
24
+ - Existing docs that describe generated tmux snippets
25
+
26
+ Searches used for this inventory:
27
+
28
+ - `rg 'run\("tmux"|read\("tmux"|exec\.Command\("tmux"' .`
29
+ - `rg 'tmux ' .`
30
+ - `rg 'display-message -p' .`
31
+ - `rg 'display-popup' .`
32
+ - `rg 'capture-pane' .`
33
+ - `rg 'set-hook' .`
34
+ - `rg '@projmux_' .`
35
+ - `rg '#\{[^}]+\}' .`
36
+
37
+ The tables below separate production runtime surface from e2e/support fixtures
38
+ and legacy cleanup candidates. Test fixture command strings are used to confirm
39
+ the production contract, but they are not treated as separate product surface
40
+ unless the corresponding production path exists.
41
+
42
+ ## Phase 1 Runner Boundary
43
+
44
+ Phase 1 introduces `internal/integrations/mux` as a thin command-runner
45
+ boundary over the existing tmux backend. New app-layer subprocess calls to the
46
+ `tmux` binary should use `mux.Run`, `mux.Read`, or `mux.ReadTrimmed` so raw
47
+ binary invocation does not spread further. Existing typed tmux clients and
48
+ test-injected runner fields remain valid and do not need a broad rewrite in
49
+ this phase.
50
+
51
+ ## Phase 2A Semantic Mux Reads And Pane Options
52
+
53
+ Phase 2A adds semantic helpers in `internal/integrations/mux` for the pane
54
+ metadata/read surfaces that psmux must eventually model:
55
+
56
+ - `SetPaneOption`, `UnsetPaneOption`, and `ShowPaneOption` cover
57
+ `set-option -p` writes/unsets and `display-message -p "#{@...}"` reads.
58
+ - `DisplayMessage` and `DisplayMessageTrimmed` cover `display-message -p`
59
+ format reads while preserving fake-runner injection through app-owned mux
60
+ runners.
61
+ - `TmuxFormat`, `PaneOptionFormat`, and `JoinFormats` centralize common tmux
62
+ format assembly so converted call sites do not hand-build `#{...}` strings.
63
+
64
+ Phase 2A intentionally does not introduce list-pane/list-window, popup, focus,
65
+ capture, lifecycle, or psmux backend behavior.
66
+
67
+ ## Phase 2B Pane And Window Inventory Reads
68
+
69
+ Phase 2B adds semantic structured inventory helpers in
70
+ `internal/integrations/mux` while keeping tmux as the only production backend:
71
+
72
+ - `ListPanes` covers fixed-format `list-panes -F` reads for pane inventory.
73
+ - `ListWindows` covers fixed-format `list-windows -F` reads for window
74
+ inventory.
75
+ - `DisplayPaneFields` covers one-pane `display-message -p` field reads.
76
+ - `FieldDelimiter` and `ParseFormatRows` centralize delimiter choice, escaped
77
+ unit-separator compatibility, malformed-row skipping, and field trimming.
78
+
79
+ Converted app-layer read paths include AI hook pane matching, tmux bell pane
80
+ field reads, `attention list`/window badge reads, and notify queue reconcile.
81
+ The session-state typed tmux client remains on its existing capture path in this
82
+ phase so replay schema and generation logic stay unchanged.
83
+
84
+ ### Phase 2B psmux Audit Capability Mapping
85
+
86
+ The table below maps the tmux format variables used by Phase 2B inventory reads
87
+ to the semantic capability that a future psmux audit/backend must provide.
88
+
89
+ | Capability | Current tmux read | Format variables / options | Converted read paths |
90
+ | --- | --- | --- | --- |
91
+ | `ListPanes` pane identity | `list-panes -a -F` | `#{session_name}`, `#{window_id}`, `#{pane_id}`, `#{pane_active}`, `#{socket_path}` | `attention list`, notify reconcile |
92
+ | `ListPanes` pane labels/state | `list-panes -a -F`, `list-panes -t <window> -F` | `#{pane_title}`, `#{@projmux_attention_state}`, `#{@projmux_ai_state}`, `#{@projmux_ai_agent}`, `#{@projmux_ai_topic}` | `attention list`, attention window badge, notify reconcile |
93
+ | `ListPanes` AI hook match fields | `list-panes -a -F` | `#{pane_id}`, `#{pane_current_path}`, `#{@projmux_ai_thread_id}`, `#{@projmux_ai_session_id}` | AI hook pane matching |
94
+ | `DisplayPaneFields` bell pane fields | `display-message -p -t <pane>` | `#{session_name}`, `#{window_id}`, `#{window_name}`, `#{pane_id}`, `#{pane_title}`, `#{pane_current_command}`, `#{socket_path}` | tmux bell ingest |
95
+ | `ListWindows` window inventory | `list-windows -F` | `#{window_index}`, `#{window_id}`, `#{window_name}`, `#{window_layout}`, `#{window_panes}`, `#{pane_current_path}` | API available for the existing typed tmux window reads; conversion deferred outside Phase 2B app read slice |
96
+
97
+ ## Phase 2C Interactive Command Slice
98
+
99
+ Phase 2C adds semantic helpers in `internal/integrations/mux` for the visible
100
+ interactive tmux commands that a future backend must either support or report
101
+ as unsupported:
102
+
103
+ - `DisplayPopup` covers `display-popup` launch args while preserving the
104
+ existing tmux option ordering from `BuildDisplayPopupArgs`.
105
+ - `ClosePopup` covers scoped `display-popup [-c <client>] [-t <pane>] -C`
106
+ close behavior.
107
+ - `CapturePane` covers both `capture-pane -p -J -S <n> -t <pane>` and
108
+ `capture-pane -p -t <pane> -S <n>` forms.
109
+ - `SwitchClient`, `SelectPane`, and `SelectWindow` cover focus/statusbar
110
+ navigation commands, including optional `-S <socket>` and `-c <client>`
111
+ targeting where the current tmux paths use them.
112
+
113
+ Converted app-layer paths include popup-toggle open/close (including notify
114
+ sidebar popup close-by-client), statusbar path/usage popups and window-list
115
+ passthrough, focus switch/window/pane selection, and AI watch-title joined
116
+ capture.
117
+
118
+ ### Phase 2C psmux Audit Capability Mapping
119
+
120
+ | Capability | Current tmux command | Converted paths |
121
+ | --- | --- | --- |
122
+ | `DisplayPopup` | `display-popup [-c <client>] [-t <pane>] [-E] [-B] [-d <cwd>] [-e KEY=VALUE] [-x <x>] [-y <y>] [-w <w>] [-h <h>] [-T <title>] <command>` | `tmux popup-toggle`, statusbar path popup, statusbar usage popup |
123
+ | `ClosePopup` | `display-popup [-c <client>] [-t <pane>] -C` | `tmux popup-toggle` close path, including notify sidebar client-scoped close |
124
+ | `CapturePane` | `capture-pane -p -J -S -80 -t <pane>`; `capture-pane -p -t <pane> -S <start>` | AI watch-title capture path converted; generic typed tmux client helper remains a tmux client path |
125
+ | `SwitchClient` | `[-S <socket>] switch-client [-c <client>] -t <session>` | `projmux focus` dispatch |
126
+ | `SelectPane` | `[-S <socket>] select-pane [-T <title>] -t <target>` | `projmux focus` pane selection; title form available for existing pane-title surfaces |
127
+ | `SelectWindow` | `[-S <socket>] select-window -t <target>` | `projmux focus` window selection, statusbar window-list passthrough |
128
+
129
+ ## Phase 2D Lifecycle / Split / Hook Slice
130
+
131
+ Phase 2D adds semantic helpers in `internal/integrations/mux` for tmux
132
+ lifecycle, pane split, hook, and global/session option commands:
133
+
134
+ - `NewSession` covers `new-session` lifecycle args, including detached or
135
+ attach/create mode, socket/config prefixes, session name, cwd, sorted
136
+ environment injection, optional pane id return via `-P -F "#{pane_id}"`, and
137
+ an optional command tail.
138
+ - `SplitWindow` covers `split-window -h/-v`, detached splits, target, cwd,
139
+ optional pane id return via `-P -F "#{pane_id}"`, and command tail ordering.
140
+ - `SetHook` covers global append and unset forms used by tmux bell integration.
141
+ - `SetOption` and `ShowOption` cover global/session option writes and reads
142
+ where runtime code needs structured option commands rather than raw argv.
143
+
144
+ Converted paths include project session creation, AI agent/shell split, and
145
+ tmux bell integration option/hook install and removal. Session-state replay
146
+ creation remains on the existing typed tmux path in this phase.
147
+
148
+ ### Phase 2D psmux Audit Capability Mapping
149
+
150
+ | Capability | Current tmux command | Converted paths |
151
+ | --- | --- | --- |
152
+ | `NewSession` | `[-S <socket>] [-f <config>] new-session [-d] [-A] [-s <session>] [-c <cwd>] [-e KEY=VALUE] [-P -F "#{pane_id}"] [command...]` | Project session create, including lifecycle startup pane id capture |
153
+ | `SplitWindow` | `split-window [-d] [-P -F "#{pane_id}"] [-h/-v] [-t <target>] [-c <cwd>] [command...]` | AI agent split, AI shell split |
154
+ | `SetHook` | `set-hook -ag <hook> <command>`, `set-hook -gu <hook[index]>` | `projmux ai integrate tmux-bell` install/remove |
155
+ | `SetOption` | `set-option -g <option> <value>`, `set-option -t <session> -q <option> <value>` | tmux bell integration option install; API available for session-scoped markers |
156
+ | `ShowOption` | `show-options [-g] [-q] [-v] [-t <target>] <option>` | API available for global/session option reads; converted low-risk call sites use the plural spelling for psmux parity |
157
+
158
+ ## Phase 3 psmux MVP Direction
159
+
160
+ Phase 3 should bring up a psmux backend before solving rich pane metadata.
161
+ psmux 3.3.4 supports global and session user options, but pane-scoped custom
162
+ `@...` options are not persisted by `set-option -p` in current smoke results.
163
+ The psmux MVP therefore treats pane user options as an unsupported capability
164
+ instead of a launch blocker.
165
+
166
+ Phase 3A0 should bootstrap native Windows `projmux.exe` execution before the
167
+ psmux backend is wired into product flows. Split it into explicit subphases so
168
+ native PowerShell work does not get conflated with the later psmux backend:
169
+
170
+ - **Phase 3A0-1: Windows build unblock.** Make `GOOS=windows GOARCH=amd64 go
171
+ build ./cmd/projmux` succeed. WSL-only focus code, POSIX process attributes,
172
+ and shell assumptions must be build-tagged, stubbed, or moved behind
173
+ platform adapters.
174
+ - **Phase 3A0-2: Pure PowerShell CLI smoke.** Run `projmux.exe --version` and
175
+ other mux-free diagnostics/config commands from PowerShell. This phase should
176
+ prove that startup, argument parsing, config/state path calculation, and
177
+ read-only commands do not require tmux, WSL, or POSIX shell tools.
178
+ - **Phase 3A0-3: Windows dependency and path policy.** Teach diagnostics to
179
+ distinguish native Windows/psmux from WSL/Linux/tmux. `tmux`, `stty`, `/bin/sh`,
180
+ and WSL-only helpers must not be required for a native psmux track.
181
+ - **Phase 3A0-4: PowerShell process-launch and quoting policy.** Define how
182
+ generated psmux config invokes `projmux.exe` and child commands without
183
+ POSIX quoting. This must be settled before hooks, status commands, or popup
184
+ launchers call back into projmux on Windows. Policy/helper coverage now
185
+ lives in `internal/integrations/psmux`: generated psmux config renders
186
+ PowerShell-native command lines with `& '<projmux.exe>' '<arg>'...`, while
187
+ native process-launch code keeps argv structured or uses the Windows
188
+ CreateProcess command-line renderer only at that boundary.
189
+ - **Phase 3A0-5: Native shell entry smoke.** Add the first limited
190
+ `projmux.exe shell` PowerShell smoke that reaches a psmux app runtime, with
191
+ rich pane metadata still disabled.
192
+
193
+ ### Generated psmux Command Rendering Policy
194
+
195
+ psmux-native generated config must not reuse `tmuxShellQuote`,
196
+ `tmuxConfigQuote`, `sh -c`, POSIX environment-prefix syntax, or cmd.exe
197
+ metacharacter escaping. Those helpers remain tmux/POSIX-only.
198
+
199
+ When generated psmux config needs to call back into `projmux.exe`, render a
200
+ PowerShell-native invocation from an argv model:
201
+
202
+ - executable: first token after PowerShell's call operator, e.g.
203
+ `& 'C:\Program Files\projmux\projmux.exe'`;
204
+ - arguments: one single-quoted literal per argv element;
205
+ - single quote inside an argv element: doubled (`'can''t'`);
206
+ - spaces, backtick, `$`, `&`, `|`, `<`, `>`, `;`, parentheses, braces, caret,
207
+ percent, and exclamation are data inside single-quoted literals;
208
+ - NUL, CR, and LF are rejected for generated one-line config commands.
209
+
210
+ When psmux backend code can launch a process directly, it should keep
211
+ `Executable` plus `Args` structured until the native process boundary. If a
212
+ single Windows command-line string is unavoidable for `CreateProcess`, use the
213
+ psmux Windows command-line renderer for direct CreateProcess/C-runtime argv
214
+ parsing only; do not feed that string through PowerShell or `cmd.exe /c`.
215
+ If future code must launch through `cmd.exe /c`, add a separate cmd.exe
216
+ renderer with tests for cmd metacharacters instead of extending the
217
+ CreateProcess helper.
218
+
219
+ Phase 3A0-5 acceptance/checklist:
220
+
221
+ - `projmux.exe shell` chooses the psmux path only on native Windows/PowerShell;
222
+ tmux/POSIX `projmux shell` behavior stays unchanged.
223
+ - generated psmux config uses the PowerShell argv renderer for all callbacks to
224
+ `projmux.exe` and child commands.
225
+ - Windows paths and args containing spaces, single quote, double quote,
226
+ backtick, dollar, ampersand, pipe, redirect characters, semicolon,
227
+ parentheses, braces, caret, percent, and exclamation survive a smoke launch.
228
+ - the smoke reaches a minimal psmux app runtime without pane-scoped metadata,
229
+ replay, or backend capability redesign.
230
+ - unsupported psmux capabilities fail or degrade explicitly in the smoke notes.
231
+
232
+ Phase 3A0-5 result:
233
+
234
+ - native Windows `projmux.exe shell` now writes a separate generated
235
+ `psmux.conf` and launches `psmux -L projmux -f <config> new-session -A -s
236
+ <session> [-c <cwd>]`.
237
+ - Linux, WSL, and macOS continue to write the existing tmux app config and
238
+ launch `tmux -L projmux -f <config> new-session -A -s <session> [-c <cwd>]`.
239
+ - the generated psmux config is intentionally minimal: app marker, mouse,
240
+ history, basic status, automatic rename, and `C-n` new-window. Pane-scoped
241
+ metadata, popups, rich hooks, notify sidebar, and session-state replay remain
242
+ disabled/follow-up.
243
+ - the psmux status callback uses
244
+ `internal/integrations/psmux.ProjmuxCommand(...).PowerShell()` and renders
245
+ as `#(& '<projmux.exe>' 'status' 'git' '#{pane_current_path}')`; POSIX tmux
246
+ quoting helpers are not used for callback argv.
247
+
248
+ Native Windows PowerShell smoke commands for PR/archive notes:
249
+
250
+ ```powershell
251
+ $Projmux = "$env:TEMP\projmux.exe"
252
+ go build -o $Projmux .\cmd\projmux
253
+ $env:PROJMUX_WELCOME = "0"
254
+ $Session = 'projmux smoke chars '' " ` $ & | < > ; ( ) { } ^ % !'
255
+ & $Projmux shell --config "$env:TEMP\projmux-psmux-smoke.conf" --session $Session --bin $Projmux
256
+ psmux -L projmux list-sessions
257
+ psmux -L projmux show-options -gqv @projmux_app
258
+ psmux -L projmux kill-server
259
+ Remove-Item Env:\PROJMUX_WELCOME -ErrorAction SilentlyContinue
260
+ ```
261
+
262
+ Expected command shape from the implementation:
263
+
264
+ ```text
265
+ psmux -L projmux -f <generated config> new-session -A -s <session> [-c <cwd>]
266
+ ```
267
+
268
+ Phase 3A should add an explicit mux capability contract:
269
+
270
+ - `PaneUserOptions=false` for psmux until upstream parity or a projmux metadata
271
+ store exists.
272
+ - `GlobalUserOptions=true` and `SessionUserOptions=true` when backed by
273
+ `show-options` / `set-option`.
274
+ - Unsupported capabilities must fail or degrade explicitly; they must not
275
+ silently pretend pane metadata was stored.
276
+
277
+ Phase 3B is audit-only. It should complete the public psmux parity matrix,
278
+ record the Phase 2A-2D and Phase 3A0 smoke results, and identify the remaining
279
+ native Windows PowerShell checks. It must not implement psmux backend/runtime
280
+ work, sidecar metadata, session-state schema changes, replay behavior, or
281
+ distribution work.
282
+
283
+ Phase 4 should report degraded psmux capabilities explicitly while keeping the
284
+ MVP narrow:
285
+
286
+ - pane user options are missing in psmux 3.3.4, so pane topic, AI state,
287
+ attention ack, notification dedupe, and agent resume metadata must be
288
+ disabled or shown as unsupported until a later implementation phase.
289
+ - notify/status/session-state code paths should report missing pane metadata
290
+ capability rather than relying on empty `@projmux_*` reads.
291
+ - `doctor` and settings diagnostics should identify the active backend and its
292
+ missing metadata capabilities.
293
+
294
+ ### Phase 4 MVP Subset
295
+
296
+ This section is a Phase 3B scope boundary only; Phase 4 implementation remains
297
+ out of scope for the audit branch.
298
+
299
+ Phase 4 should implement only the smallest psmux-capable product slice needed
300
+ to prove native Windows project navigation and basic AI pane launch:
301
+
302
+ - app shell launch on the psmux backend with explicit degraded capability
303
+ reporting.
304
+ - project session create, attach/open, and switch.
305
+ - right/down split plus Codex/Claude pane launch when command-tail split works.
306
+ - basic pane identity/context reads: pane id, window id, session name, current
307
+ path, current command, title, and socket path where available.
308
+ - global/session options via `set-option` and `show-options`; pane-scoped
309
+ custom `@projmux_*` options stay degraded/unsupported.
310
+ - minimal generated status line: app marker, basic status row, and
311
+ PowerShell-rendered callbacks only.
312
+ - minimal project switch sidebar: open the sidebar, list projects/sessions,
313
+ select a project/session, then open or switch to it.
314
+ - Phase 4B-F1 generated psmux config uses an explicit command tail for the
315
+ project popup (`display-popup ... -E powershell -NoProfile -Command
316
+ <PowerShell script>`) instead of passing the PowerShell script itself as the
317
+ `-E` argument. The popup is left anchored with `-x 0 -y 0 -w 42% -h 100%` as
318
+ the closest sidebar-like psmux fallback; exact tmux sidebar parity remains out
319
+ of scope and needs native Windows visual confirmation.
320
+ - basic preview if the psmux surface can provide it cheaply; no preview is an
321
+ acceptable MVP fallback.
322
+ - a psmux-capable popup or native picker path for the minimal project switch
323
+ sidebar only.
324
+
325
+ Phase 4 explicitly excludes notify sidebar work, rich sidebar preview, pane
326
+ metadata badges, AI pane topic/state/notify/session-state resume metadata
327
+ recovery, statusbar mouse integration, advanced focus/ack/consume semantics,
328
+ full tmux sidebar parity, full hook parity, session-state replay,
329
+ session-state schema changes, sidecar metadata storage, pane option workaround
330
+ implementation, and Phase 5 packaging/distribution work.
331
+
332
+ ### Phase 4A App/Session Foundation
333
+
334
+ Phase 4A adds only the psmux app/session foundation:
335
+
336
+ - backend selection uses `PROJMUX_MUX_BACKEND=tmux|psmux`, with native Windows
337
+ defaulting to psmux and non-Windows hosts defaulting to tmux.
338
+ - `projmux shell` continues to use the app socket/session naming
339
+ `-L projmux` / `home` and writes `psmux.conf` only for the psmux backend.
340
+ - project session create, attach/open, switch, recent-session summaries, and
341
+ preview/session inventory can use `psmux -L projmux`.
342
+ - psmux inventory is based on `list-sessions`, `list-windows`, and
343
+ `list-panes` core fields only. Pane-scoped `@projmux_*` metadata is left
344
+ empty/degraded; no sidecar store or pane-option workaround is introduced.
345
+ - `projmux quit` keeps the `@projmux_app=1` global marker guard before
346
+ `kill-server` on the selected app runtime backend.
347
+
348
+ Phase 4A still excludes the Phase 4B project switch sidebar/keybinding UI,
349
+ Phase 4C splits/agents, notify sidebar, rich preview, metadata recovery,
350
+ statusbar mouse integration, advanced focus/ack/consume, full hook parity, and
351
+ session-state restore parity.
352
+
353
+ Rich metadata should be revisited only after the psmux MVP is usable. The two
354
+ acceptable later directions are upstream psmux parity for pane-scoped custom
355
+ user options, or a projmux-owned sidecar pane metadata store with reconcile,
356
+ locking, stale-record cleanup, and pane-id reuse protection.
357
+
358
+ ## Classification Key
359
+
360
+ | Class | Meaning |
361
+ | --- | --- |
362
+ | `required MVP` | Needed for baseline session create/list/open, app shell, AI split, and project navigation. |
363
+ | `interactive UI` | Needed for popups, statusbar clicks, key bindings, picker launch, visible messages, or clipboard helpers. |
364
+ | `hooks/status` | Needed for generated tmux hooks, AI/attention/status segments, notification queue reconciliation, or settings-driven live options. |
365
+ | `e2e/support` | Used by install/apply flows, smoke scripts, POCs, or test harnesses. |
366
+ | `legacy/cleanup candidate` | Backward compatibility, migration markers, old option aliases, or code paths called out as future cleanup candidates. |
367
+
368
+ ## Command Inventory
369
+
370
+ ### Session And App Runtime
371
+
372
+ | Command | Class | Production call sites | Purpose / notes |
373
+ | --- | --- | --- | --- |
374
+ | `has-session -t <target>` | `required MVP` | `Client.sessionExists`, `tmuxSessionExists` | Check whether a project/app session exists before create/open. |
375
+ | `new-session -d -s <session> -c <cwd> [-e KEY=VALUE]` | `required MVP` | `Client.createDetachedSession` | Create detached project sessions. With lifecycle hooks enabled, adds `-P -F "#{pane_id}"` to capture the first pane id. Phase 2D mux API: `NewSession`. |
376
+ | `new-session -A -s <session> [-c <cwd>]` | `required MVP` | `projmux shell` | Attach/create the isolated app session. Wrapped with `-L <socket> -f <config>`. Phase 2D mux API available: `NewSession`. |
377
+ | `attach-session -t <target>` | `required MVP` | `Client.OpenSession`, `Client.OpenSessionTarget` | Outside-tmux open path. Pane targets degrade to session/window where attach cannot select a pane. |
378
+ | `switch-client [-c <client>] -t <target>` | `required MVP`, `focus` | `Client.OpenSession`, `focusCommand`, generated status key table | Inside-tmux open/focus path. `PROJMUX_SWITCH_TARGET_CLIENT` can force the originating client. Phase 2C mux API: `SwitchClient`. |
379
+ | `list-sessions -F ...` | `required MVP`, `focus`, `e2e/support` | `RecentSessions`, `RecentSessionSummaries`, `ListEphemeralSessions`, `focus`, `tmux apply` | Drives session picker rows, focus fallback, app reload probe, and ephemeral cleanup. |
380
+ | `list-windows -F ...` | `required MVP`, `session-state`, `hooks/status` | `ListSessionWindows`, `runRebalancePanes`, session-state capture/replay | Reads window inventory, window ids, pane counts, layouts. |
381
+ | `list-panes -F ...` | `required MVP`, `hooks/status`, `session-state` | `ListAllPanes`, AI matching, attention list, notify reconcile, session-state capture | Main pane inventory primitive. |
382
+ | `set-environment -t <session> KEY VALUE` | `required MVP`, `hooks/status` | `Client.applyProjectSessionEnv` | Applies project hook environment to newly-created sessions. |
383
+ | `kill-session -t <session>` | `required MVP`, `legacy/cleanup candidate` | `Client.KillSession`, session-state live replay cleanup | User/session lifecycle and destructive live replay cleanup. |
384
+ | `kill-server` | `required MVP` for app quit | `quitCommand` | Quits only an app-owned `tmux -L projmux` server after `@projmux_app=1` verification. |
385
+
386
+ ### Popup And Interactive UI
387
+
388
+ | Command | Class | Production call sites | Purpose / notes |
389
+ | --- | --- | --- | --- |
390
+ | `display-popup ... <command>` | `interactive UI` | `tmux popup-*`, AI picker, hook trust prompt, welcome popup, statusbar pwd/usage popups | Native tmux popup surface. Uses `-E`, `-B`, `-c`, `-t`, `-d`, `-e`, `-x`, `-y`, `-w`, `-h`, `-T` depending on mode. Phase 2C mux API: `DisplayPopup`. |
391
+ | `display-popup [-c <client>] [-t <pane>] -C` | `interactive UI` | `tmux popup-toggle` | Closes a scoped popup. Notify sidebar close targets client instead of origin pane. Phase 2C mux API: `ClosePopup`. |
392
+ | `display-message [message]` | `interactive UI`, `hooks/status` | AI/status/settings/attention/statusbar fallback paths | User-visible toasts and error fallbacks that avoid tmux `run-shell` error popups. |
393
+ | `select-pane -T <title> -t <pane>` | `interactive UI`, `hooks/status`, `session-state` | attention toggle/clear, AI topic/title, `tmux rename-pane`, replay shell wrapper | Sets pane title and topic-adjacent UI metadata. Phase 2C mux API: `SelectPane`. |
394
+ | `select-pane -t <target>` | `focus`, `session-state` | `focusCommand`, session-state replay | Selects target pane after focus or replay. Phase 2C mux API: `SelectPane`. |
395
+ | `select-window -t <target>` | `interactive UI`, `focus`, `session-state` | statusbar window-list passthrough, focus, live replay | Restores native window click behavior and selects replay/focus targets. Phase 2C mux API: `SelectWindow`. |
396
+ | `split-window [-h|-v] [-P -F "#{pane_id}"] [-t <pane>] [-c <cwd>] <cmd>` | `required MVP`, `interactive UI` | AI split, session-state replay | Creates AI/shell panes. AI agent split reads the new pane id from `-P -F`. Phase 2D mux API: `SplitWindow` for AI splits; session-state replay remains on the typed tmux path. |
397
+ | `resize-pane -t <pane> -x|-y <size>` | `interactive UI` | AI split layout rebalance | Best-effort equal sizing after AI split. |
398
+ | `set-buffer -w -- <text>` | `interactive UI` | Settings copy helpers | Copies generated install/remove/dry-run commands to the tmux clipboard. |
399
+ | `command-prompt` | `interactive UI` | generated keymap | Prompt-based rename/topic actions in generated config. |
400
+ | `bind-key`, `unbind-key`, `switch-client -T` | `interactive UI`, `hooks/status` | generated config | Installs popup/statusbar/keybinding UX, including `MouseDown1Status` and the `projmux-status` key table. |
401
+ | `new-window -c "#{pane_current_path}"` | `interactive UI` | generated keymap | App keybinding for opening a new shell window in the current pane path. |
402
+ | `previous-window`, `next-window`, `select-pane -L/-R/-U/-D` | `interactive UI` | generated app keymap | Navigation bindings inside app config. |
403
+
404
+ ### Hook, Status, And Notification Surface
405
+
406
+ | Command | Class | Production call sites | Purpose / notes |
407
+ | --- | --- | --- | --- |
408
+ | `set-hook -g pane-focus-out run-shell -b ...` | `hooks/status` | generated standalone/app config | Arms attention focus state on pane focus out. Uses `#{hook_pane}`. |
409
+ | `set-hook -g pane-focus-in run-shell -b ...` | `hooks/status` | generated standalone/app config | Clears attention state when pane receives focus. Uses `#{hook_pane}`. |
410
+ | `set-hook -g after-select-pane run-shell -b ...` | `hooks/status` | generated standalone/app config | Clears attention on selected pane. Uses `#{pane_id}`. |
411
+ | `set-hook -g pane-exited` / `after-kill-pane` | `hooks/status` | generated standalone/app config | Calls `projmux tmux rebalance-panes` after pane removal. |
412
+ | `set-hook -g client-attached` | `hooks/status` | generated app config | Runs the legacy welcome popup helper; with the current shell prompt policy it normally no-ops unless a pending marker exists. |
413
+ | `set-hook -ag alert-bell ...` | `hooks/status` | `projmux ai integrate tmux-bell` | Installs bell fallback to `projmux ai ingest bell --pane "#{pane_id}"`. Phase 2D mux API: `SetHook`. |
414
+ | `set-hook -gu alert-bell[...]` | `legacy/cleanup candidate`, `hooks/status` | `projmux ai integrate tmux-bell --remove` | Removes projmux-managed alert-bell entries. Phase 2D mux API: `SetHook`. |
415
+ | `show-hooks -g alert-bell` | `hooks/status` | tmux bell integration planning | Detects existing managed bell fallback. |
416
+ | `run-shell -b <command>` | `hooks/status`, `interactive UI` | generated hooks, generated statusbar binds, AI watch-title | Async hook/status dispatch and title watcher launch. |
417
+ | `show-options -gqv <option>` | `hooks/status`, `legacy/cleanup candidate` | notification mode, statusbar decoration, `@projmux_projdir`, app ownership | Reads global user options and generated app markers. The plural form works on tmux and psmux; avoid tmux's singular alias in shared surfaces. |
418
+ | `show-options -gv @projmux_app` | `required MVP` for app quit | `quitCommand` | Confirms a socket is app-owned before `kill-server`. Phase 2D mux API available: `ShowOption`. |
419
+ | `set-option -g <option> <value>` | `hooks/status` | settings, notification registration, generated config, tmux bell integration | Writes statusbar decoration, desktop notify mode markers, toast URI markers, and tmux bell options. Phase 2D mux API: `SetOption` for tmux bell integration. |
420
+ | `set-option -g -u <option>` | `legacy/cleanup candidate` | notification URI migration | Unsets older URI registration markers. |
421
+ | `set-option -p [-u] -t <pane> <option> [value]` | `hooks/status`, `required MVP` for AI | AI state, attention, topics, notification dedupe, session-state recipe metadata | Core pane metadata storage. Phase 2A mux API: `SetPaneOption`, `UnsetPaneOption`. |
422
+ | `set-option -t <session> -q <option> <value>` | `session-state`, `required MVP` | session autosave/source, ephemeral sessions | Stores session-level live markers. |
423
+ | `source-file <config>` | `e2e/support`, app runtime support | `tmux apply`, install smoke | Reloads generated app config into live `-L projmux` server. |
424
+
425
+ ### Capture And Replay
426
+
427
+ | Command | Class | Production call sites | Purpose / notes |
428
+ | --- | --- | --- | --- |
429
+ | `capture-pane -p -J -S -80 -t <pane>` | `capture`, `hooks/status` | AI watch-title/notification inference | Reads recent pane text joined into logical lines. Phase 2C mux API: `CapturePane`. |
430
+ | `capture-pane -p -t <pane> -S <n>` | `capture`, `required MVP` if generic pane viewer is used | `Client.CapturePane` | Generic typed tmux client capture helper. Phase 2C mux API supports this form; typed client conversion remains deferred. |
431
+ | `rename-window -t <target> <name>` | `session-state` | session-state replay | Restores first window name. |
432
+ | `new-window -d -t <target> -c <cwd> [-n <name>] [cmd...]` | `session-state` | session-state replay | Recreates additional windows. |
433
+ | `select-layout -t <target> <layout>` | `session-state` | session-state replay | Restores captured window layouts. |
434
+ | `move-window -d -k -s <window-id> -t <target>` | `legacy/cleanup candidate`, `session-state` | destructive live replay primitive | Used by `ApplyToExistingSession`, called out in roadmap backlog as possible cleanup. |
435
+ | `kill-window -t <window-id>` | `legacy/cleanup candidate`, `session-state` | destructive live replay primitive | Removes live windows not present in staged snapshot. |
436
+ | `send-keys -t <target> <command> Enter` | `required MVP`, `session-state` | startup commands and replay recipes | Runs startup or agent resume commands in target panes. |
437
+
438
+ ### E2E, POC, And Support Scripts
439
+
440
+ | Command | Class | Source | Purpose / notes |
441
+ | --- | --- | --- | --- |
442
+ | `tmux -L <socket> new-session -d ...` | `e2e/support` | integration/install smoke scripts | Creates isolated smoke servers. |
443
+ | `tmux -L <socket> show-options -gqv @projmux_app` | `e2e/support` | install/integration/e2e smoke scripts | Confirms generated app config was sourced. |
444
+ | `tmux set-option -p ... @projmux_ai_*` | `e2e/support` | e2e smoke | Seeds AI/status metadata for visual smoke. |
445
+ | `tmux display-message -p ...`, `list-panes`, `list-windows` | `e2e/support` | native picker POC scripts | Verifies tmux state during native picker POC runs. |
446
+ | `set-environment -g PROJMUX_*` in generated POC config | `e2e/support` | native picker POC scripts | Seeds environment for sandboxed native picker tests. |
447
+
448
+ ## `display-message -p` Format Variable Inventory
449
+
450
+ This section lists the format variables read through `tmux display-message -p`
451
+ only. Other `-F` uses in `list-*` commands are covered in the next section.
452
+
453
+ | Format | Class | Read by | Purpose / notes |
454
+ | --- | --- | --- | --- |
455
+ | `#{pane_current_path}` | `required MVP`, `interactive UI`, `hooks/status` | tmux client, AI split, status git, statusbar pwd, popup context | CWD for project switch, popup launch, status segments, and AI context. |
456
+ | `#{session_name}` | `required MVP`, `session-state` | tmux client, autosave, settings/sessionstate | Current session identity. |
457
+ | `#S` | `hooks/status`, `focus` | AI notify, status kube, popup context, focus URI translation | Short session name alias used in legacy/direct paths. |
458
+ | `#W` | `hooks/status` | AI desktop notification | Window name in notification body. |
459
+ | `#I` | `focus` | focus URI translation | Window index when converting a pane id from a toast URI. |
460
+ | `#{pane_id}` | `required MVP`, `interactive UI`, `hooks/status`, `focus` | popup context, AI split, watch-title gate, notify producer | Resolves target/origin pane ids and checks pane liveness. |
461
+ | `#{pane_title}` | `hooks/status`, `interactive UI` | attention, AI watch-title, notify producer | Pane label/topic evidence and attention title cleanup. |
462
+ | `#{pane_current_command}` | `required MVP`, `session-state`, `hooks/status` | startup wait, AI watch-title, bell ingest | Shell readiness and AI/bell classification. |
463
+ | `#{socket_path}` | `hooks/status`, `focus` | notification toast, attention/notify producer, bell ingest | Carries socket path into notify queue and toast focus URI. |
464
+ | `#{window_id}` | `hooks/status` | notify producer, bell ingest | Stable window target for notification rows. |
465
+ | `#{window_name}` | `hooks/status` | bell ingest | Bell notification fallback context. |
466
+ | `#{client_tty}` | `interactive UI`, `statusbar` | popup-toggle, statusbar generated bindings | Scopes popup markers and statusbar click origin. |
467
+ | `#{client_pid}` | `interactive UI` | popup context fallback | Fallback popup marker key when `client_tty` is empty. |
468
+ | `#{client_width}` / `#{client_height}` | `interactive UI` | popup sizing | Calculates popup dimensions for picker/status surfaces. |
469
+ | `#{@projmux_statusbar_decoration}` | `interactive UI`, `hooks/status` | popup context | Live fallback for popup/statusbar decoration mode. |
470
+ | `#{@projmux_sessionstate_autosave_at}` | `session-state` | autosave debounce | Per-session autosave gate timestamp. |
471
+ | `#{@projmux_sessionstate_source}` | `session-state` | session-state source check | Marks fresh/restored/autosave source. |
472
+ | `#{@projmux_attention_state}` | `hooks/status` | attention, AI, notify | Pane reply/busy state. |
473
+ | `#{@projmux_attention_ack}` | `hooks/status` | AI watch-title | Reply acknowledgement state. |
474
+ | `#{@projmux_attention_focus_armed}` | `hooks/status` | attention | Focus-gated attention clear behavior. |
475
+ | `#{@projmux_ai_agent}` | `required MVP`, `hooks/status`, `session-state` | AI/notify/session-state | Agent kind metadata. |
476
+ | `#{@projmux_ai_context}` | `hooks/status` | AI watch-title | AI context directory metadata. |
477
+ | `#{@projmux_ai_topic}` | `hooks/status`, `session-state` | AI/status/session-state | Display topic and restore recipe label. |
478
+ | `#{@projmux_ai_topic_manual}` | `hooks/status` | AI watch-title | Blocks automatic topic overwrite. |
479
+ | `#{@projmux_ai_state}` | `hooks/status` | attention/AI/notify | AI thinking/waiting/idle state. |
480
+ | `#{@projmux_ai_hook_active}` | `hooks/status` | AI watch-title gate | Prevents title watcher from overriding hook-driven metadata. |
481
+ | `#{@projmux_desktop_notification_key}` | `hooks/status` | AI notification dedupe | Dedupe key for desktop notifications. |
482
+ | `#{@projmux_desktop_notification_at}` | `hooks/status` | AI notification dedupe | Dedupe timestamp. |
483
+ | `#{@projmux_desktop_notified}` | `legacy/cleanup candidate`, `hooks/status` | AI notification reset/tests | Older notification marker still reset/written. |
484
+
485
+ ## `list-* -F` Format Variable Inventory
486
+
487
+ | Command | Formats | Class | Purpose |
488
+ | --- | --- | --- | --- |
489
+ | `list-sessions -F` | `#{session_activity}`, `#{session_name}`, `#{session_attached}`, `#{session_windows}` | `required MVP` | Recent session ordering and picker summaries. |
490
+ | `list-sessions -F` | `#{session_name}`, `#{session_attached}`, `#{session_last_attached}`, `#{@projmux_ephemeral}` | `required MVP` | Ephemeral lifecycle inventory. |
491
+ | `list-sessions -F` | `#{session_id}` | `e2e/support` | `tmux apply` live server probe/count. |
492
+ | `list-sessions -F` | `#{session_activity}`, `#{session_name}`, `#{session_attached}` | `focus` | Focus fallback inventory. |
493
+ | `list-clients -F` | `#{client_active_pane}` | `hooks/status` | Detect whether a pane is visible to any attached client. |
494
+ | `list-clients -F` | `#{client_activity}`, `#{session_id}` | `required MVP` | Outside-tmux AI split target fallback. |
495
+ | `list-clients -F` | `#{client_name}`, `#{client_session}` | `focus` | Pick a client for focus dispatch. |
496
+ | `list-windows -F` | `#{window_index}`, `#{?window_active,1,0}`, `#{window_name}`, `#{window_panes}`, `#{pane_current_path}` | `required MVP` | Session preview and window inventory. |
497
+ | `list-windows -F` | `#{window_id}`, `#{window_panes}` | `hooks/status` | Pane rebalance after exits. |
498
+ | `list-windows -F` | `#{window_index}`, `#{window_name}`, `#{window_layout}` | `session-state` | Snapshot capture. |
499
+ | `list-windows -F` | `#{window_id}`, `#{window_index}` | `session-state` | Staged/live replay window mapping. |
500
+ | `list-panes -a -F` | `#{session_name}`, `#{pane_id}`, `#{window_index}`, `#{pane_index}`, `#{?pane_active,1,0}`, `#{pane_title}`, `#{@projmux_attention_state}`, `#{@projmux_ai_state}`, `#{@projmux_ai_agent}`, `#{@projmux_ai_topic}`, `#{@projmux_attention_ack}`, `#{@projmux_attention_focus_armed}`, `#{pane_current_command}`, `#{pane_current_path}` | `required MVP`, `hooks/status` | Full pane inventory. |
501
+ | `list-panes -a -F` | `#{session_name}`, `#{window_id}`, `#{pane_id}`, `#{pane_active}`, `#{pane_title}`, `#{@projmux_attention_state}`, `#{@projmux_ai_state}`, `#{@projmux_ai_agent}`, `#{@projmux_ai_topic}`, `#{socket_path}` | `hooks/status` | `attention list` inventory. |
502
+ | `list-panes -a -F` | `#{session_name}`, `#{window_id}`, `#{pane_id}`, `#{@projmux_attention_state}`, `#{@projmux_ai_state}`, `#{@projmux_ai_agent}`, `#{@projmux_ai_topic}`, `#{socket_path}` | `hooks/status` | Notify queue reconcile. |
503
+ | `list-panes -a -F` | `#{pane_id}`, `#{pane_current_path}`, `#{@projmux_ai_thread_id}`, `#{@projmux_ai_session_id}` | `hooks/status` | AI hook payload to live pane matching. |
504
+ | `list-panes -t <window> -F` | `#{pane_title}`, `#{@projmux_attention_state}` | `hooks/status` | Window badge rendering. |
505
+ | `list-panes -t <target> -F` | `#{pane_id}`, `#{pane_left}`, `#{pane_top}`, `#{pane_width}`, `#{pane_height}` | `interactive UI` | AI split post-layout equalization. |
506
+ | `list-panes -s -t <session> -F` | `#{window_index}`, `#{pane_index}`, `#{pane_title}`, `#{?pane_active,1,0}`, `#{pane_current_path}`, `#{@projmux_recipe_kind}`, `#{@projmux_startup_command}`, `#{@projmux_ai_managed}`, `#{@projmux_ai_agent}`, `#{@projmux_ai_topic}`, `#{@projmux_ai_resume_id}`, `#{@projmux_ai_resume_source}`, `#{@projmux_ai_resume_updated_at}` | `session-state` | Snapshot pane capture. |
507
+ | `list-panes -s -t <session> -F` | `#{pane_id}`, `#{pane_current_path}`, `#{@projmux_ai_managed}`, `#{@projmux_ai_agent}`, `#{@projmux_ai_session_id}`, `#{@projmux_ai_resume_id}`, `#{@projmux_ai_transcript_path}` | `session-state` | Refresh AI resume metadata before save. |
508
+
509
+ ## Option And Hook Surface
510
+
511
+ ### Projmux User Options
512
+
513
+ | Option | Scope | Class | Read / write purpose |
514
+ | --- | --- | --- | --- |
515
+ | `@projmux_app` | global | `required MVP` | Generated app config sets `1`; quit/install smoke verify before app runtime shutdown. |
516
+ | `@projmux_projdir` | global | `required MVP` | Declarative project root source read by `projmux switch` when inside tmux. |
517
+ | `@projmux_ephemeral` | session | `required MVP` | Marks ephemeral sessions for lifecycle inventory. |
518
+ | `@projmux_statusbar_decoration` | global | `hooks/status` | Legacy/fallback status decoration. |
519
+ | `@projmux_statusbar_decoration_cwd` | global | `hooks/status` | CWD segment decoration mode. |
520
+ | `@projmux_statusbar_decoration_git` | global | `hooks/status` | Git segment decoration mode. |
521
+ | `@projmux_statusbar_decoration_notify` | global | `hooks/status` | Notify segment decoration mode. |
522
+ | `@projmux_desktop_notify_mode` | global | `hooks/status` | Current 3-way desktop notify mode. |
523
+ | `@projmux_desktop_notify` | global | `legacy/cleanup candidate` | Legacy boolean desktop notify mode still read for compatibility. |
524
+ | `@projmux_uri_protocol_registered_v6` | global | `hooks/status` | WSL toast URI registration marker. |
525
+ | `@projmux_uri_protocol_registered` through `_v5` | global | `legacy/cleanup candidate` | Old URI markers unset during v6 registration. |
526
+ | `@projmux_legacy_appid_cleaned` | global | `legacy/cleanup candidate` | One-shot marker for old WSL toast AppID cleanup. |
527
+ | `@projmux_attention_state` | pane | `hooks/status` | `busy` / `reply` state for statusbar, notify, and attention UX. |
528
+ | `@projmux_attention_ack` | pane | `hooks/status` | Acknowledgement gate for AI reply detection. |
529
+ | `@projmux_attention_focus_armed` | pane | `hooks/status` | Prevents background focus from clearing pending reply too early. |
530
+ | `@projmux_ai_managed` | pane | `required MVP`, `session-state` | Marks projmux-managed AI panes. |
531
+ | `@projmux_ai_agent` | pane | `required MVP`, `session-state` | Agent kind: codex/claude/shell-derived metadata. |
532
+ | `@projmux_ai_context` | pane | `hooks/status` | AI context directory. |
533
+ | `@projmux_ai_state` | pane | `hooks/status` | thinking/waiting/idle status. |
534
+ | `@projmux_ai_topic` | pane | `hooks/status`, `session-state` | Pane topic shown in status/pane border and saved in snapshots. |
535
+ | `@projmux_ai_topic_manual` | pane | `hooks/status` | Manual topic overwrite guard. |
536
+ | `@projmux_ai_hook_active` | pane | `hooks/status` | Marks hook-driven panes. |
537
+ | `@projmux_ai_thread_id` | pane | `hooks/status` | Codex hook matching metadata. |
538
+ | `@projmux_ai_session_id` | pane | `hooks/status`, `session-state` | Hook/session resume matching metadata. |
539
+ | `@projmux_ai_transcript_path` | pane | `session-state` | Claude transcript fallback for resume id refresh. |
540
+ | `@projmux_ai_resume_id` | pane | `session-state` | Restore resume identifier. |
541
+ | `@projmux_ai_resume_source` | pane | `session-state` | Source of resume id: hook/session-id/transcript/log. |
542
+ | `@projmux_ai_resume_updated_at` | pane | `session-state` | Resume metadata freshness timestamp. |
543
+ | `@projmux_ai_bell_notified_at` | pane | `hooks/status` | Bell notification dedupe timestamp. |
544
+ | `@projmux_desktop_notified` | pane | `legacy/cleanup candidate` | Older desktop notification marker still written/reset. |
545
+ | `@projmux_desktop_notification_key` | pane | `hooks/status` | Desktop notification dedupe key. |
546
+ | `@projmux_desktop_notification_at` | pane | `hooks/status` | Desktop notification dedupe timestamp. |
547
+ | `@projmux_recipe_kind` | pane | `session-state` | Startup recipe marker. |
548
+ | `@projmux_startup_command` | pane | `session-state` | Startup recipe command. |
549
+ | `@projmux_sessionstate_source` | session | `session-state` | Fresh/restored/autosave source marker. |
550
+ | `@projmux_sessionstate_autosave_at` | session | `session-state` | Autosave debounce timestamp. |
551
+
552
+ ### Generated tmux Options
553
+
554
+ The app and standalone generated configs also own normal tmux options. psmux
555
+ does not need to emulate every cosmetic option for an MVP, but it must account
556
+ for options that affect visible product behavior:
557
+
558
+ - App identity/runtime: `default-terminal`, `default-shell`,
559
+ `default-command`, `update-environment`, `history-limit`, `set-clipboard`.
560
+ - Input behavior: `mouse`, `mode-keys`, `status-keys`, `escape-time`.
561
+ - Statusbar behavior: `status`, `status-position`, `status-interval`,
562
+ `status-left`, `status-right`, `status-left-length`,
563
+ `status-right-length`, `status-format[0]`, `status-format[1]`,
564
+ `status-format[2]` unset.
565
+ - Window/pane labels: `automatic-rename`, `automatic-rename-format`,
566
+ `window-status-format`, `window-status-current-format`,
567
+ `window-status-separator`, `pane-border-status`, `pane-border-format`,
568
+ pane/status/message style options.
569
+ - Bell fallback: `allow-passthrough`, `monitor-bell`, `bell-action`.
570
+
571
+ ### Hook Names
572
+
573
+ | Hook | Class | Installed by | Payload |
574
+ | --- | --- | --- | --- |
575
+ | `pane-focus-out` | `hooks/status` | generated config | `run-shell -b '<bin> attention arm #{hook_pane}'` |
576
+ | `pane-focus-in` | `hooks/status` | generated config | `run-shell -b '<bin> attention clear #{hook_pane}'` |
577
+ | `after-select-pane` | `hooks/status` | generated config | `run-shell -b '<bin> attention clear #{pane_id}'` |
578
+ | `pane-exited` | `hooks/status` | generated config | `run-shell -b 'sleep 0.05; <bin> tmux rebalance-panes'` |
579
+ | `after-kill-pane` | `hooks/status` | generated config | `run-shell -b 'sleep 0.05; <bin> tmux rebalance-panes'` |
580
+ | `client-attached` | `hooks/status` | app generated config | `run-shell -b '<bin> welcome --popup >/dev/null 2>&1'` (legacy pending-marker fallback; normally no-op after shell prompt) |
581
+ | `alert-bell` | `hooks/status` | `projmux ai integrate tmux-bell` | `run-shell -b 'projmux ai ingest bell --pane "#{pane_id}" ...'` |
582
+
583
+ ## Surface-Specific Command Sets
584
+
585
+ ### Popup
586
+
587
+ Required tmux commands:
588
+
589
+ - `display-message -p -F #{client_tty|client_pid|pane_id|#S|pane_current_path|client_width|client_height|@projmux_statusbar_decoration}`
590
+ - `display-popup` with sizing, target/client, env, cwd, border, title, and close-on-exit flags.
591
+ - `display-popup -C` to close toggle popups.
592
+ - Phase 2C mux APIs: `DisplayPopup`, `ClosePopup`.
593
+
594
+ Dependent generated commands:
595
+
596
+ - `bind-key ... run-shell '<bin> tmux popup-toggle --client #{client_tty} <mode>'`
597
+ - `run-shell` from statusbar key table and mouse handlers.
598
+
599
+ ### Statusbar
600
+
601
+ Required tmux commands/config:
602
+
603
+ - `set -g status 2`
604
+ - `set -g status-left`, `status-right`, `status-format[0]`, `status-format[1]`
605
+ - `#[range=user|...]`, `#[align=...]`, `#(<bin> status ...)`, and native window list `#{W:...}` format support.
606
+ - `bind-key -n MouseDown1Status if-shell -F "#{==:#{mouse_status_range},window}" { select-window -t = } { run-shell ... }`
607
+ - `bind-key s switch-client -T projmux-status`
608
+ - `bind-key -T projmux-status <key> run-shell ...`
609
+ - Runtime handlers: `display-message`, `display-popup`, `select-window`, `show-options`, `list-panes`.
610
+ - Phase 2C mux APIs used by runtime handlers: `DisplayPopup`, `SelectWindow`.
611
+
612
+ ### Hook
613
+
614
+ Required tmux commands:
615
+
616
+ - `set-hook -g` for generated focus/pane/client hooks.
617
+ - `set-hook -ag`, `set-hook -gu`, `show-hooks -g` for the optional bell fallback.
618
+ - `run-shell -b` payload execution.
619
+ - Hook format vars: `#{hook_pane}`, `#{pane_id}`.
620
+ - Pane option reads/writes via `display-message -p` and `set-option -p`.
621
+
622
+ ### Capture
623
+
624
+ Required tmux commands:
625
+
626
+ - AI title/watch inference: `capture-pane -p -J -S -80 -t <pane>`.
627
+ - Generic tmux client helper: `capture-pane -p -t <pane> -S <start>`.
628
+ - Phase 2C mux API: `CapturePane`.
629
+
630
+ psmux audit should check both forms separately because joined-line capture
631
+ (`-J`) is product-visible for AI topic/notification inference.
632
+
633
+ ### Focus
634
+
635
+ Required tmux commands:
636
+
637
+ - `list-sessions -F "#{session_activity}<sep>#{session_name}<sep>#{session_attached}"`
638
+ - `list-clients -F "#{client_name}<sep>#{client_session}"`
639
+ - `switch-client [-c <client>] -t <session>`
640
+ - `select-window -t <session>:<window>`
641
+ - `select-pane -t <session>:<window>.<pane>`
642
+ - URI translation: `display-message -p -t <pane-id> "#S<sep>#I"`
643
+ - Optional socket wrapper: `tmux -S <socket> ...`
644
+ - Phase 2C mux APIs: `SwitchClient`, `SelectWindow`, `SelectPane`.
645
+
646
+ ### Session-State
647
+
648
+ Required tmux commands:
649
+
650
+ - Capture: `list-windows`, `list-panes -s`, `display-message -p`, `set-option`.
651
+ - Save refresh: `list-panes -s`, pane `set-option` resume metadata.
652
+ - Replay: `new-session`, `rename-window`, `new-window`, `split-window`,
653
+ `select-layout`, `select-pane`, `send-keys`.
654
+ - Live overwrite primitive: `move-window`, `kill-window`, `kill-session`,
655
+ `select-window`. This is classified as a legacy/cleanup candidate because
656
+ the roadmap already tracks unused destructive live overwrite removal.
657
+
658
+ ## Legacy And Cleanup Candidates
659
+
660
+ These items should not block Phase 1, but they are useful pressure points when
661
+ raw tmux calls are centralized:
662
+
663
+ - Global option reads should use the mux `ShowOption` helper or spell the
664
+ command as `show-options`; tmux accepts the singular alias, but psmux 3.3.4
665
+ does not.
666
+ - Legacy desktop notify option `@projmux_desktop_notify` remains a read alias
667
+ for old boolean state.
668
+ - URI registration markers `@projmux_uri_protocol_registered` through `_v5`
669
+ are explicitly unset after successful v6 registration.
670
+ - `@projmux_desktop_notified` is still written/reset, but dedupe uses
671
+ `@projmux_desktop_notification_key` and `_at`.
672
+ - Session-state parser accepts an older 11-field pane format while the current
673
+ capture format emits 13 fields.
674
+ - `ApplyToExistingSession` uses destructive live overwrite commands
675
+ (`move-window`, `kill-window`, temp `kill-session`) and is already tracked in
676
+ the roadmap backlog as a possible removal.
677
+ - Native picker POC scripts contain raw tmux setup and assertions; keep them in
678
+ e2e/support unless they graduate into production paths.
679
+
680
+ ## psmux Parity Audit
681
+
682
+ Phase 3B owns audit, matrix, and documentation only. It records the current
683
+ psmux parity result after Phase 2A-2D and Phase 3A0. It must not implement
684
+ psmux backend/runtime work, session-state replay or schema changes, sidecar
685
+ metadata, or Phase 5 distribution.
686
+
687
+ Observed private audit environment: Windows Terminal plus PowerShell, psmux
688
+ 3.3.4. Public docs should keep the reproducible command shape and status
689
+ classification here; raw command output belongs in private audit notes.
690
+
691
+ Status values for Phase 3: `pass`, `partial`, `missing`, `unknown`.
692
+ For rows with split behavior, the status records the product-facing aggregate
693
+ and the notes identify the passing minimal slice separately from richer parity
694
+ gaps.
695
+
696
+ | Capability | tmux command / format surface | Current class | psmux status | Audit notes |
697
+ | --- | --- | --- | --- | --- |
698
+ | Create detached project session | `new-session -d -s -c [-e] [-P -F "#{pane_id}"]` | `required MVP` | `pass` | Detached create, cwd, and `-P -F "#{pane_id}"` return passed. Phase 2D mux API: `NewSession`. |
699
+ | Attach or switch to session | `attach-session`, `switch-client [-c] -t` | `required MVP`, `focus` | `partial` | Basic target selection passed in smoke. Complex cross-client and cross-session focus in Windows Terminal remains follow-up; use the `switch-client` line in the native smoke block. Phase 2C mux API: `SwitchClient`. |
700
+ | App shell server | `tmux -L <socket> -f <config> new-session -A -s` / `psmux -L <socket> -f <config> new-session -A -s` | `required MVP` | `partial` | Minimal native PowerShell shell-entry passed: `psmux -L projmux` exposes sessions/panes and `@projmux_app=1`. Richer app runtime remains partial because sidebar/keybinding, full statusbar, notify, and metadata surfaces are not implemented. Phase 2D mux API: `NewSession`. |
701
+ | Session inventory | `list-sessions -F` plus session formats | `required MVP`, `focus` | `unknown` | No Phase 3B result has been recorded for full session activity, attached count, windows, and ids. Run the `list-sessions` line in the native smoke block. |
702
+ | Window inventory | `list-windows -F` plus window formats | `required MVP`, `session-state` | `partial` | Basic index, id, name, and pane-count passed. Layout was not fully audited and matters only if session restore enters psmux scope. |
703
+ | Pane inventory | `list-panes -a/-s -F` plus pane/user-option formats | `required MVP`, `hooks/status`, `session-state` | `partial` | Core fields passed in the app-shell smoke, including session, window, pane, cwd, command, and title. Custom `@projmux_*` fields are empty because pane user options are missing. |
704
+ | Popup launch | `display-popup` with target/client/env/cwd/size/border/title/close flags | `interactive UI` | `partial` | Command returned no error. Visual rendering, focus behavior, close semantics, and option coverage need user visual confirmation; run the popup lines in the native smoke block. Phase 2C mux API: `DisplayPopup`. |
705
+ | Popup close | `display-popup -C` | `interactive UI` | `partial` | Command returned no error. Toggle/close semantics need user visual confirmation with the popup lines in the native smoke block. Phase 2C mux API: `ClosePopup`. |
706
+ | Current context formats | `display-message -p -F #{pane_current_path}`, `#{pane_id}`, `#{session_name}`, `#{pane_current_command}`, `#{pane_title}` | `interactive UI` | `pass` | Target-scoped `DisplayMessage` and `DisplayMessageTrimmed` passed for pane id, session name, current path, current command, and title. Phase 2A mux APIs: `DisplayMessage`, `DisplayMessageTrimmed`. |
707
+ | Display pane fields | `display-message -p -t <pane> "#{pane_id}|#{window_id}|#{socket_path}"` | `focus` | `pass` | Pane id, window id, and socket path tuple passed. |
708
+ | Focus URI translation | `display-message -p -t %N "#S<sep>#I"` | `focus` | `unknown` | Pane-to-session/window mapping for toast click focus was not fully recorded. Run the target-scoped `display-message` lines in the native smoke block before enabling focus UX. |
709
+ | Client inventory | `list-clients -F #{client_name} #{client_session} #{client_active_pane}` | `focus`, `hooks/status` | `unknown` | Needed for focus and reply auto-ack correctness. Run from inside an attached psmux client with the `list-clients` line in the native smoke block. |
710
+ | New window | `new-window [-d] [-n] [-c] [-P -F "#{pane_id}"] [command...]` | `required MVP`, `session-state` | `pass` | Name and cwd create passed. Command tail remains follow-up only if session-state restore enters psmux scope. |
711
+ | Pane split | `split-window -h/-v [-P -F "#{pane_id}"] [-t] [-c] <cmd>` | `required MVP` | `partial` | Horizontal split with pane-id return, cwd, and command tail passed. Vertical split remains a follow-up smoke if product scope requires it. Phase 2D mux API: `SplitWindow`. |
712
+ | Resize panes | `resize-pane -x/-y` | `interactive UI` | `unknown` | Not recorded in Phase 3B. This can remain outside the MVP if default popup/native picker sizing is acceptable; run the `resize-pane` line in the native smoke block before adding resize-dependent UX. |
713
+ | Pane title | `select-pane -T` and `#{pane_title}` | `interactive UI`, `hooks/status` | `pass` | Basic `SelectPane` target selection and title format read passed. Phase 2C mux API: `SelectPane`. |
714
+ | Pane options | `set-option -p`, `display-message -p "#{@...}"`, `show-options -p` | `required MVP`, `hooks/status`, `session-state` | `missing` | `set-option -p` is accepted, but custom `@projmux_audit_key` is not visible through `display-message "#{@...}"` or `show-options -p`. Classify `SetPaneOption`, `UnsetPaneOption`, and `ShowPaneOption` as degraded/unsupported for Phase 4, not a Phase 4 blocker. |
715
+ | Global/session set options | `set-option -g`, `set-option -t -q` | `hooks/status`, `session-state` | `pass` | Global and session custom option writes passed. Phase 2D mux API: `SetOption`. |
716
+ | Global/session show options | `show-options [-g] [-q] [-v] [-t <target>] <option>` | `hooks/status`, `session-state` | `partial` | Plural `show-options` works for global/session reads. psmux 3.3.4 lacks tmux's singular `show-option` alias, so adapters and shared surfaces must use `show-options` plural. Phase 2D mux API: `ShowOption`. |
717
+ | Generated minimal statusbar | `status`, basic `status-left` / `status-right`, `#(...)` callback | `hooks/status` | `partial` | Minimal generated psmux status options were present, including `@projmux_app=1` and a `status-right` PowerShell-rendered `projmux.exe status git #{pane_current_path}` callback. Full status ranges, mouse dispatch, and HUD rows remain future scope. |
718
+ | PowerShell callback rendering | `#(& '<projmux.exe>' 'status' 'git' '#{pane_current_path}')` | `required MVP`, `hooks/status` | `pass` | Observed generated psmux config renders the `status git` callback through the Phase 3A0 PowerShell argv renderer rather than POSIX quoting. |
719
+ | Project switch sidebar / keybinding | sidebar or picker open, project/session list, select then open/switch | `required MVP`, `interactive UI` | `missing` | Current generated psmux config has no projmux sidebar/keybinding yet (`list-keys` has no projmux entry). Keep a narrow minimal project switch sidebar in Phase 4 MVP scope. Notify sidebar is deferred and out of Phase 4 MVP. |
720
+ | Statusbar mouse/key dispatch | `MouseDown1Status`, `if-shell -F`, `switch-client -T`, `run-shell` | `interactive UI`, `hooks/status` | `missing` | Current generated psmux config has no projmux statusbar mouse/key dispatch. Defer from Phase 4 MVP except any minimum picker/sidebar binding needed for project switch. |
721
+ | Hooks | `set-hook`, `show-hooks`, `run-shell -b`, `#{hook_pane}` | `hooks/status` | `partial` | Custom hook set/show/unset passed. Real Projmux hook names, events, payload execution, and hook format vars need targeted follow-up only if hooks enter psmux scope. Phase 2D mux API: `SetHook`. |
722
+ | Bell fallback | `monitor-bell`, `bell-action`, `alert-bell`, `#{pane_id}` | `hooks/status` | `unknown` | Not recorded. Defer from Phase 4 MVP; run targeted bell smoke before supporting unknown AI-tool notification fallback. Phase 2D mux APIs: `SetOption`, `SetHook`. |
723
+ | Capture pane | `capture-pane -p`, `capture-pane -p -J` | `capture` | `partial` | Raw capture passed. Joined `-J` needs explicit follow-up smoke if AI title inference enters psmux scope. Phase 2C mux API: `CapturePane`. |
724
+ | Select window | `select-window -t <session>:<window>` | `focus`, `session-state` | `pass` | Basic target selection passed. Complex cross-client focus remains follow-up. Phase 2C mux API: `SelectWindow`. |
725
+ | Session-state replay | `new-window`, `split-window`, `rename-window`, `select-layout`, `select-pane`, `send-keys` | `session-state` | `unknown` | Out of Phase 3B and Phase 4 MVP scope except the already-audited create/split primitives. Run targeted replay smoke only if session restore later enters psmux scope. |
726
+ | Live overwrite replay | `move-window`, `kill-window`, `select-window` | `legacy/cleanup candidate` | `unknown` | Out of Phase 3B and Phase 4 MVP scope; candidate to exclude from psmux and possibly remove. Phase 2C mux API covers `SelectWindow` only. |
727
+ | Config reload | `source-file`, generated config file semantics | `e2e/support` | `unknown` | Generated config load via `psmux -L projmux -f <config> new-session ...` passed for the app shell path. `source-file` reload parity has not been recorded; run `psmux -L $Socket source-file $Config` from the native smoke block before depending on reload behavior. |
728
+ | Clipboard helper | `set-buffer -w` | `interactive UI` | `unknown` | Not recorded. Settings copy helper can fall back to OS clipboard later; run a targeted `set-buffer -w` smoke before adding psmux clipboard UX. |
729
+ | Socket targeting | `-L <socket>`, `-S <socket>` | `required MVP`, `focus` | `partial` | `psmux -L projmux` passed for the minimal app shell server. Keep `-S` focus socket behavior as follow-up. |
730
+ | Quoting/process launch | PowerShell callbacks, generated config quoting, child command launch | `required MVP`, `interactive UI` | `partial` | PowerShell callback rendering passed for the observed `status git` callback, and Phase 3A0-5 psmux shell entry passed from PowerShell. Broader child command launch remains follow-up. |
731
+
732
+ Additional native Windows PowerShell follow-up smoke block:
733
+
734
+ ```powershell
735
+ # Run from the repo root in Windows PowerShell with Go and psmux 3.3.4 on PATH.
736
+ # Keep raw command output in private audit notes, not this public document.
737
+ $ErrorActionPreference = 'Stop'
738
+ $Socket = 'projmux-audit'
739
+ $Session = 'projmux-audit'
740
+ $Config = Join-Path $env:TEMP 'projmux-psmux-audit.conf'
741
+ $Projmux = Join-Path $env:TEMP 'projmux.exe'
742
+ $Repo = (Get-Location).Path
743
+
744
+ go build -o $Projmux .\cmd\projmux
745
+ psmux -L $Socket kill-server 2>$null
746
+
747
+ Set-Content -Encoding UTF8 -Path $Config -Value @"
748
+ set -g @projmux_app 1
749
+ set -g status on
750
+ set -g status-left 'projmux '
751
+ set -g status-right '#(& '$($Projmux -replace "'", "''")' 'status' 'git' '#{pane_current_path}')'
752
+ "@
753
+
754
+ psmux -L $Socket -f $Config new-session -d -s $Session -c $Repo -P -F '#{pane_id}'
755
+ psmux -L $Socket source-file $Config
756
+ psmux -L $Socket list-sessions -F '#{session_name}|#{session_id}|#{session_windows}|#{session_attached}'
757
+ psmux -L $Socket list-windows -t $Session -F '#{window_index}|#{window_id}|#{window_name}|#{window_panes}|#{window_layout}'
758
+ psmux -L $Socket list-panes -a -F '#{session_name}|#{window_id}|#{pane_id}|#{pane_current_path}|#{pane_current_command}|#{pane_title}|#{@projmux_audit_key}'
759
+ psmux -L $Socket display-message -p -t "$Session:0.0" '#{pane_id}|#{window_id}|#{socket_path}|#{session_name}|#{pane_current_path}|#{pane_current_command}|#{pane_title}'
760
+
761
+ psmux -L $Socket set-option -g '@projmux_audit_global' ok
762
+ psmux -L $Socket show-options -gqv '@projmux_audit_global'
763
+ psmux -L $Socket set-option -t $Session -q '@projmux_audit_session' ok
764
+ psmux -L $Socket show-options -t $Session -qv '@projmux_audit_session'
765
+ psmux -L $Socket show-option -gqv '@projmux_audit_global'
766
+
767
+ psmux -L $Socket set-option -p -t "$Session:0.0" '@projmux_audit_key' ok
768
+ psmux -L $Socket display-message -p -t "$Session:0.0" '#{@projmux_audit_key}'
769
+ psmux -L $Socket show-options -p -t "$Session:0.0"
770
+
771
+ psmux -L $Socket new-window -d -t $Session -n audit-window -c $Repo -P -F '#{pane_id}'
772
+ psmux -L $Socket split-window -h -d -t "$Session:0.0" -c $Repo -P -F '#{pane_id}' powershell -NoLogo -NoExit
773
+ psmux -L $Socket split-window -v -d -t "$Session:0.0" -c $Repo -P -F '#{pane_id}' powershell -NoLogo -NoExit
774
+ psmux -L $Socket select-window -t "$Session:1"
775
+ psmux -L $Socket select-pane -t "$Session:0.0"
776
+ psmux -L $Socket select-pane -T 'audit title' -t "$Session:0.0"
777
+ psmux -L $Socket resize-pane -t "$Session:0.0" -x 100 -y 30
778
+
779
+ psmux -L $Socket capture-pane -p -t "$Session:0.0"
780
+ psmux -L $Socket capture-pane -p -J -S -80 -t "$Session:0.0"
781
+
782
+ psmux -L $Socket set-hook -g 'projmux-audit-hook' 'display-message audit-hook'
783
+ psmux -L $Socket show-hooks -g 'projmux-audit-hook'
784
+ psmux -L $Socket set-hook -gu 'projmux-audit-hook'
785
+
786
+ psmux -L $Socket display-popup -t "$Session:0.0" -T 'projmux audit popup' -w 60 -h 10 -E powershell -NoProfile -Command 'Write-Host "confirm popup rendering, focus, and close"; Start-Sleep -Seconds 3'
787
+ psmux -L $Socket display-popup -C
788
+
789
+ # Run these from inside an attached psmux client before enabling focus UX:
790
+ psmux -L $Socket list-clients -F '#{client_name}|#{client_session}|#{client_active_pane}'
791
+ psmux -L $Socket switch-client -t $Session
792
+
793
+ psmux -L $Socket kill-server
794
+ ```