projmux 0.8.4 → 0.10.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.
@@ -4,6 +4,24 @@ Most users can configure projmux from `projmux settings`. Environment variables
4
4
  are available for repeatable shell setup, managed machines, or advanced
5
5
  overrides.
6
6
 
7
+ ## Operational diagnostics state
8
+
9
+ Projmux keeps a private bounded operational journal at:
10
+
11
+ ```text
12
+ ${XDG_STATE_HOME:-$HOME/.local/state}/projmux/logs/operations.jsonl
13
+ ```
14
+
15
+ There is no configuration switch for arbitrary fields or remote delivery. The
16
+ `projmux` state and `logs` directories are created/repaired to mode `0700`, and
17
+ the JSONL file is created/repaired to `0600` on POSIX systems. At more than
18
+ 5 MiB, the writer atomically retains about the newest 2 MiB of complete valid
19
+ records. Lock ownership is maintained by the OS and acquisition waits no more
20
+ than 200 ms before the best-effort write is abandoned. See
21
+ [operational-diagnostics.md](operational-diagnostics.md) for safe field and
22
+ best-effort behavior. Existing `ai-ingest.log` and
23
+ `PROJMUX_*_DEBUG` settings remain separate and unchanged.
24
+
7
25
  ## Project Discovery
8
26
 
9
27
  `projmux switch` combines pinned directories, live tmux sessions, and
@@ -97,8 +115,8 @@ keybinding as saved; failures identify the stage that failed.
97
115
  Raw sequences that cannot be safely represented as a direct keybinding are not
98
116
  persisted. Use Settings to save a custom key. When key delivery needs
99
117
  terminal-layer remediation, first try the key in `projmux shell`, then run
100
- `projmux setup` from the raw terminal, then use `projmux init` for supported
101
- terminal adapters.
118
+ `projmux setup` from the raw terminal, then use `projmux setup terminal` for
119
+ supported terminal adapters.
102
120
 
103
121
  `~/.config/projmux/keymap.toml` can also be edited by hand. When the file is
104
122
  absent, generated tmux config stays on the built-in defaults.
@@ -114,6 +132,9 @@ keys = ["C-t"]
114
132
 
115
133
  [bindings."Sidebar:PinProject"]
116
134
  keys = ["M-p", "p"]
135
+
136
+ [bindings."Resources:Open"]
137
+ keys = ["M-u"]
117
138
  ```
118
139
 
119
140
  Each table is `[bindings.<action-id>]`. Supported keys are:
@@ -150,17 +171,23 @@ such as `ProjectSidebarToggle`, `NotifySidebarToggle`, `SessionPopupToggle`,
150
171
  popup commands use `Surface:Action` IDs and have surface-local conflict
151
172
  domains and remain visible in Settings when catalogued.
152
173
 
174
+ `Resources:Open` is a user-configurable direct popup action with no built-in
175
+ shortcut. Every configured alias renders the canonical client-scoped body
176
+ `projmux tmux popup-toggle --client #{client_tty} resource-inspector`; pressing
177
+ the same alias again closes only that client's popup. It remains available on
178
+ Linux/tmux even when the Labs live-resource status segment is off.
179
+
153
180
  The Settings writer is deterministic and rewrites the supported saved subset
154
181
  only. If the existing file has parse errors or unknown action IDs, Settings
155
182
  shows the keymap error row and refuses to overwrite it until the file is fixed.
156
183
 
157
184
  The file currently affects generated tmux config from `projmux tmux
158
185
  print-config`, `projmux tmux install`, `projmux tmux print-app-config`,
159
- `projmux tmux install-app`, and `projmux shell`. Terminal init adapters such as
186
+ `projmux tmux install-app`, and `projmux shell`. Terminal remediation adapters such as
160
187
  Ghostty and Windows Terminal install built-in plain-byte mappings where needed;
161
188
  they do not read `keymap.toml` or copy saved keys into terminal configs.
162
- Changing terminal-layer mappings still requires rerunning `projmux init` and
163
- restarting the terminal where that terminal requires it.
189
+ Changing terminal-layer mappings still requires rerunning `projmux setup
190
+ terminal` and restarting the terminal where that terminal requires it.
164
191
 
165
192
  When a chord is overridden, projmux emits unbinds for both the stale default
166
193
  chord and the replacement before binding the merged action. Popup and floating
@@ -334,9 +361,9 @@ user/global preference in this release.
334
361
  ## AI Resume Picker
335
362
 
336
363
  The AI resume picker (`projmux ai split --agent resume`) lists the most recent
337
- deduplicated Claude/Codex resume sessions. The number of rows it shows and how
338
- far below the current directory it scans are both configurable; the defaults are
339
- 30 rows and depth 0 (the current directory only).
364
+ deduplicated Claude/Codex/Antigravity resume sessions. The number of rows it
365
+ shows and how far below the current directory it scans are both configurable;
366
+ the defaults are 30 rows and depth 0 (the current directory only).
340
367
 
341
368
  Preferred interactive path:
342
369
 
@@ -375,6 +402,21 @@ column (`./`, `./web`, `./api`) so child-directory sessions are easy to tell
375
402
  apart. A missing or zero depth is identical to the historical behavior. Settings
376
403
  edits write the global config.
377
404
 
405
+ Antigravity uses the upstream v1.1.12 current-storage boundary before its
406
+ legacy history fallback. `cache/last_conversations.json` contributes the latest
407
+ UUID mapped to a matching workspace; `cache/conversation_metadata.json`
408
+ contributes only rows that carry a valid UUID, workspace URI/path, and summary.
409
+ Both require an exact regular `conversations/<uuid>.db` and use only its
410
+ existence/mtime. They do not open SQLite content and do not treat `.db-wal`,
411
+ `.db-shm`, symlinks, or arbitrary paths as conversations. The cache provides a
412
+ latest-session floor rather than complete history. Missing/malformed cache,
413
+ workspace-less metadata, and stale mappings degrade to legacy `history.jsonl`
414
+ without changing the shared exact/depth/sort/cap behavior.
415
+ Live hook/session-state resume metadata is a separate high-confidence lane and
416
+ is not a disk-picker candidate. When a disk picker selection creates a pane,
417
+ its source is persisted so Session State preview and doctor can report medium
418
+ confidence for DB-validated cache sources or low confidence for legacy history.
419
+
378
420
  ## Environment Variables
379
421
 
380
422
  | Variable | Purpose |
@@ -397,7 +439,6 @@ edits write the global config.
397
439
  | `PROJMUX_SESSIONSTATE_AUTOSAVE` | Session snapshot autosave override for the global fallback. Values such as `off`, `false`, or `0` disable autosave for projects that inherit the global setting; explicit project auto-save `on`/`off` still takes precedence. |
398
440
  | `PROJMUX_SESSIONSTATE_DEBUG` | When non-empty, quiet autosave surfaces suppressed session-state errors to stderr. |
399
441
  | `PROJMUX_FOCUS_DEBUG` | When non-empty, `projmux focus` prints one telemetry line to stderr. |
400
- | `PROJMUX_PICKER_BACKEND` | Legacy picker backend override. Any value, including old `fzf` settings, now resolves to the native picker. |
401
442
  | `PROJMUX_INSTALLER` | Installer source hint used by update flows. npm installs set this automatically; advanced release installs can set `github-release`. |
402
443
  | `PROJMUX_SHELL_UPDATE_CHECK_TIMEOUT_MS` | Timeout in milliseconds for the best-effort release check attempted by `projmux shell` when the update cache is missing or stale. Invalid, zero, or negative values use the default. |
403
444
 
@@ -785,7 +826,25 @@ statistics and `hw.memsize`. Neither path launches `top`, `vm_stat`, `free`,
785
826
  PowerShell, or another metrics process. On macOS, available memory is free plus
786
827
  inactive pages, matching the reclaimable-memory intent of Linux
787
828
  `MemAvailable`. In WSL the values describe the Linux guest/VM view, not total
788
- Windows host utilization. The CPU delta cache is internal state at
829
+ Windows host utilization. These are host-scoped values and do not attribute
830
+ usage to a pane, window, project, or session.
831
+
832
+ When enabled, the compact segment is also the clickable `resources` statusbar
833
+ range and opens the Resource Inspector. Turning the Lab off hides only this
834
+ segment; it does not disable `projmux resources` or a custom-bound
835
+ `Resources:Open` action. Inspector samples are memory-only for the popup
836
+ lifetime and are unrelated to the status segment's host CPU reference cache.
837
+
838
+ The display policy is fixed rather than configurable: CPU is normal below 70%,
839
+ warning at 70–89%, and critical at 90% or above; memory is normal below 75%,
840
+ warning at 75–89%, and critical at 90% or above. The two values are classified
841
+ and styled independently. Normal and unavailable (`--`) values use the
842
+ secondary status-text theme role, warnings use the warning role, and critical
843
+ values use the bold critical role. Visible severity words are omitted and each
844
+ percent uses a fixed four-column slot, including `%`, so metric transitions do
845
+ not resize the segment. No threshold values are stored in config.
846
+
847
+ The CPU delta cache is internal state at
789
848
  `${XDG_STATE_HOME:-~/.local/state}/projmux/live-resources-sample.json`.
790
849
  CPU reference samples older than 30 seconds are ignored and replaced on the
791
850
  next refresh.
@@ -87,7 +87,7 @@ Files:
87
87
 
88
88
  - `internal/ui/projmuxpicker/*`
89
89
  - `internal/ui/render/*`
90
- - `docs/native-picker-no-fzf-poc.md`
90
+ - `docs/native-picker.md`
91
91
 
92
92
  Classification:
93
93
 
@@ -124,7 +124,7 @@ Classification:
124
124
 
125
125
  Do not translate these families:
126
126
 
127
- - Product and agent names: `Codex`, `Claude`, `projmux`, `tmux`, `psmux`,
127
+ - Product and agent names: `Codex`, `Claude`, `projmux`, `tmux`,
128
128
  `GitHub`, `npm`.
129
129
  - Terminal and app names: `Windows Terminal`, `Ghostty`, `WezTerm`, `Kitty`,
130
130
  `iTerm2`, `Alacritty`, `Foot`.
package/docs/hooks.md CHANGED
@@ -207,8 +207,9 @@ If a `send-noti` hook itself calls `projmux notify push`, projmux sees
207
207
 
208
208
  `Settings > Notifications > Delivery sources` surfaces the active Codex hooks,
209
209
  Claude, and tmux AI notify diagnostics: status, conflicts, config paths, and
210
- copyable CLI install/remove/dry-run commands. It does not install or remove
211
- external Codex, Claude, or tmux settings.
210
+ copyable CLI install/remove/dry-run commands. It also shows whether
211
+ `PROJMUX_NOTIFY_HOOK` overrides the built-in desktop sender. It does not install
212
+ or remove external Codex, Claude, or tmux settings.
212
213
 
213
214
  `PROJMUX_NOTIFY_HOOK` is separate from `[hooks.send-noti]`: it replaces the
214
215
  desktop sender and receives positional arguments
@@ -617,37 +618,79 @@ catalog `install` field for installed hook events.
617
618
 
618
619
  ## Antigravity Hook Ingest
619
620
 
620
- `projmux ai ingest antigravity-hook < payload.json` is available for manual
621
- Antigravity CLI `agy` hook/statusline payloads. Projmux does not provide
622
- `projmux ai integrate antigravity`, does not install Antigravity hooks, and
623
- does not mutate Antigravity user config. The Delivery sources diagnostic
624
- therefore reports Antigravity as a read-only unsupported/manual row. If users
625
- wire it by hand, use an absolute `projmux` command path or a known cwd because
626
- relative command paths failed the Phase 0b smoke.
627
-
628
- The default known Antigravity catalog records only observed Phase 0b signals
629
- and marks them `install: false`:
621
+ `projmux ai ingest antigravity-hook --event <event> < payload.json` accepts
622
+ Antigravity CLI `agy` hook payloads. Antigravity v1.1.12 stdin does
623
+ not include the event name, so the command's `--event` value is authoritative.
624
+ Payload event aliases remain a compatibility fallback when `--event` is
625
+ omitted. `projmux ai integrate antigravity [--dry-run|--remove]` owns exactly
626
+ the named `projmux` entry in `~/.gemini/config/hooks.json` and separately owns
627
+ only `statusLine` in `~/.gemini/antigravity-cli/settings.json`. Other named hooks,
628
+ their fields, and unknown JSON values remain untouched. The generated commands
629
+ use the stable absolute projmux executable because Antigravity runs handlers
630
+ with the config directory as cwd. The command refuses unmanaged name/command
631
+ conflicts, malformed JSON, symlink paths, and permission failures with an
632
+ actionable diagnostic. Doctor/Settings distinguish installed, missing,
633
+ conflicting, and stale managed entries; stale covers executable, event/schema,
634
+ or stdout-fallback drift and is refreshed by the install command.
635
+ The statusline object uses the official v1.1.12 command shape with
636
+ `enabled=true` and `stack_with_default=true`. Its direct explicit `Statusline`
637
+ ingest command emits empty stdout, preserving the built-in line. Existing
638
+ custom statusline commands are conflicts and are never wrapped or chained.
639
+
640
+ The default Antigravity catalog records the five official v1.1.12 events and
641
+ installs four non-permission events:
630
642
 
631
643
  | Event/signal | Behavior |
632
644
  | --- | --- |
633
- | `PostInvocation` | marks the matched pane hook-active and writes a quiet ingest diagnostic; no notify queue entry is pushed |
634
- | `Stop` | pushes a completion row, or a critical error row when `error` is present or `terminationReason` is non-normal |
635
- | `Statusline` with `tool_confirmation_pending=true` | pushes a critical approval row; this is only active when a statusline/manual status payload is wired to ingest |
636
- | `Statusline` without `tool_confirmation_pending=true` | marks the matched pane hook-active and writes a quiet ingest diagnostic |
645
+ | `PreToolUse` | known permission-changing event; never installed and no permission decision is synthesized |
646
+ | `PreInvocation` | marks the matched pane hook-active and moves it to thinking/busy; no notify queue entry is pushed |
647
+ | `PostInvocation` | marks the matched pane hook-active and writes a quiet bookkeeping diagnostic; no notify queue entry is pushed |
648
+ | `PostToolUse` | marks the matched pane hook-active, retains tool error metadata in quiet diagnostics, and pushes no notify queue entry |
649
+ | `Stop` | pushes an info completion unless an explicit error signal requires a critical error row |
650
+ | `Statusline` with `tool_confirmation_pending=true` | pushes/replaces a deduped critical approval-required row outside the hook catalog |
651
+ | `Statusline` with `agent_state=thinking|working|tool_use` | moves the matched pane to thinking/busy without notifying, unless a terminal completion/approval state must be preserved from a late refresh; a new `PreInvocation` resets the next generation to busy |
652
+ | `Statusline` with `agent_state=idle` or `tool_confirmation_pending=false` | quiet update; does not clear completion/approval attention and creates no notification |
637
653
  | unknown events | mark the matched pane hook-active and write quiet ingest diagnostics only |
638
654
 
639
- Antigravity notify rows use `agent=antigravity` metadata. Accepted fields
640
- include `conversationId`/`conversation_id`, `cwd`, `workspace.path`,
641
- `transcriptPath`, `terminationReason`, `error`, `fullyIdle`, `agent_state`,
642
- `context_window`, and nested `statusline.tool_confirmation_pending`.
655
+ Antigravity notify rows use `agent=antigravity` metadata. The v1.1.12 parser
656
+ retains common camelCase `conversationId`, `workspacePaths`, `transcriptPath`,
657
+ `artifactDirectoryPath`, and `modelName`; invocation `invocationNum` and
658
+ `initialNumSteps`; post-tool `toolCall`, `stepIdx`, and `error`; and Stop
659
+ `executionNum`, `terminationReason`, `error`, and `fullyIdle`. Existing aliases
660
+ such as `conversation_id`, `cwd`, `workspace.path`, `agent_state`, and nested
661
+ `statusline.tool_confirmation_pending` remain accepted. The first non-empty
662
+ `workspacePaths` value is only a cwd fallback candidate; empty/absent arrays do
663
+ not become the process cwd, and inherited `$TMUX_PANE` still wins attribution.
664
+ `NO_TOOL_CALL`, `MODEL_STOP`, and known normal reasons are info completions.
665
+ Non-empty `error`, explicit `ERROR`, and `MAX_STEPS_EXCEEDED` families are
666
+ critical; unknown reasons retain diagnostic metadata but default to info.
667
+ Explicit non-permission hook ingest writes valid hook stdout: `{}` for
668
+ invocation/post-tool events and `{"decision":"stop"}` for Stop. The latter
669
+ allows the requested stop to complete; projmux does not emit `continue` or a
670
+ `PreToolUse` permission decision.
671
+ Each managed command includes its explicit event selector and a valid stdout
672
+ fallback: `{}` for invocation/post-tool events and `{"decision":"stop"}` for
673
+ Stop. The fallback is non-blocking and never returns `continue`,
674
+ `allow`, `deny`, or `ask`.
675
+
676
+ The named entry in `hooks.json` remains the install source of truth. Use
677
+ `agy -p '/hooks' --output-format json` only as a read-only runtime diagnosis of
678
+ loaded sources/events; its result is never used to generate or rewrite config.
643
679
  Antigravity ingest uses `conversationId` as pane thread metadata for matching
644
680
  and as session-state resume metadata. Session restore uses
645
681
  `agy --conversation <uuid>` only when that id is present and UUID-shaped;
646
- otherwise preview and doctor render `resume unavailable`. The statusline
647
- `context_window` percentage is persisted to the usage state dir on ingest so
648
- the usage HUD can surface it as a `context-window-only` row — Antigravity has
649
- no 5-hour/weekly quota contract, so no quota bars are emitted. Raw payloads or
650
- transcript contents are not stored.
682
+ otherwise preview and doctor render `resume unavailable`. Official snake_case
683
+ `cwd`, `conversation_id`, `transcript_path`, `agent_state`,
684
+ `tool_confirmation_pending`, and structured `context_window.used_percentage`
685
+ plus token fields are parsed directly. The structured percentage is persisted
686
+ with conversation identity to the usage state dir; the legacy string percentage
687
+ remains a fallback so the usage HUD can surface it as the conversation-local
688
+ `context` row. The official top-level `quota` map is persisted independently:
689
+ valid `remaining_fraction` values become used percent, exact upstream bucket
690
+ IDs render as separate `quota/<bucket>` account rows, and `reset_time` plus
691
+ optional `reset_in_seconds` retain their independent meanings. Invalid,
692
+ disabled, missing, or empty quota shapes degrade without inventing a cadence or
693
+ reinterpreting context. Raw payloads or transcript contents are not stored.
651
694
 
652
695
  ## Ingest Debug Log
653
696
 
package/docs/install.md CHANGED
@@ -18,8 +18,8 @@ After installing, run:
18
18
  projmux doctor
19
19
  ```
20
20
 
21
- `doctor` checks that runtime tools such as `tmux`, `git`, and `stty` are
22
- available.
21
+ `doctor` performs read-only diagnostics for runtime tools such as `tmux`,
22
+ `git`, and `stty`.
23
23
 
24
24
  Start the tmux app with:
25
25
 
@@ -17,7 +17,7 @@ The recommended path when a key does not fire:
17
17
  `projmux shell` key adapter, then retry the physical key.
18
18
  3. Run `projmux setup` outside tmux to see which bytes reach the process.
19
19
  4. For supported terminals outside the native macOS app socket, preview
20
- `projmux init [terminal]`; add `--apply`
20
+ `projmux setup terminal [terminal]`; add `--apply`
21
21
  only after reviewing the merge.
22
22
  5. For unsupported terminals, configure plain Meta bytes or add a custom key in
23
23
  Settings > Keybindings.
@@ -56,6 +56,13 @@ These shortcuts are the guaranteed launch defaults. They need no tmux prefix.
56
56
  to the selected live tmux window using that window's current active pane; it is
57
57
  separate from `last-pane` and from the existing-session popup.
58
58
 
59
+ `Resources:Open` is listed in Settings > Keybindings without a default key.
60
+ Adding a safe direct chord opens the Linux/tmux Resource Inspector through the
61
+ canonical client-scoped popup-toggle path; pressing the same custom chord again
62
+ closes it without touching another client's popup. The action remains usable
63
+ when Settings > Labs > Live system resources is off—the Lab controls only
64
+ statusbar visibility.
65
+
59
66
  The tmux prefix remains the upstream default `Ctrl-b`. Inside a running
60
67
  session, `Ctrl-b ?` lists the live tmux bindings.
61
68
 
@@ -142,11 +149,11 @@ Advanced typed entry remains available from Action detail for literal
142
149
  risky/reserved key copy, and raw diagnostics. Advanced delivery is still owned
143
150
  by the selected Projmux action. The native macOS app-socket adapter reads the
144
151
  same safe chords directly; supported Ghostty and Windows Terminal mappings for
145
- other paths are previewed/applied through `projmux init`, not by storing raw
152
+ other paths are previewed/applied through `projmux setup terminal`, not by storing raw
146
153
  sequences in the primary keymap. Options covers unbinding the action and
147
154
  reset/use-default flows. Diagnostic/probe/init workflows are not first-class
148
155
  Settings tabs; use `projmux setup` and, where the native adapter does not apply,
149
- `projmux init` from the terminal when key delivery needs remediation.
156
+ `projmux setup terminal` from the terminal when key delivery needs remediation.
150
157
 
151
158
  Optional direct keys can be added for actions such as:
152
159
 
@@ -187,7 +194,7 @@ Settings > Keybindings stays a discovery surface. It must continue to expose
187
194
  launch toggles, sidebar keymap actions, picker-local actions, pane switching,
188
195
  window switching, and rename actions. The basic Settings flow is not the
189
196
  terminal remediation surface: key-role replacement, disable-default, typed
190
- fallback, terminal mapping preview/apply, and init execution rows stay out of
197
+ fallback and terminal mapping preview/apply rows stay out of
191
198
  the action detail.
192
199
 
193
200
  The product model does not support `UserN` or `CSI-u` as fallback guidance.
@@ -235,7 +242,10 @@ Settings is the default apply path for key edits: it writes the key list,
235
242
  refreshes the generated config, and reloads the running tmux session when
236
243
  possible. Use `projmux tmux apply` as a CLI recovery/sync command after editing
237
244
  the keymap file by hand, after an outside-tmux Settings save, or after resolving
238
- a reported generated-config or live-reload failure.
245
+ a reported generated-config or live-reload failure. Generated config first
246
+ unbinds the known retired `C-t` pane-label chord, then installs the current
247
+ keymap; an explicit current `C-t` assignment therefore wins without retaining
248
+ the retired command body. Apply does not rewrite `keymap.toml`.
239
249
 
240
250
  ## Keymap File
241
251
 
@@ -299,29 +309,29 @@ projmux setup --timeout 10s
299
309
  projmux setup --non-interactive
300
310
  ```
301
311
 
302
- ## Auto-Config: `projmux init`
312
+ ## Terminal remediation: `projmux setup terminal`
303
313
 
304
- `projmux init` previews and optionally applies supported terminal mappings.
305
- Default mode is dry-run; pass `--apply` to write changes with a timestamped
314
+ `projmux setup terminal` previews and optionally applies supported terminal
315
+ mappings. Default mode is a read-only preview; pass `--apply` to write changes with a timestamped
306
316
  backup.
307
317
 
308
318
  ```sh
309
- projmux init
310
- projmux init ghostty
311
- projmux init ghostty --apply
312
- projmux init windows-terminal --apply
313
- projmux init --config /path/to/file
314
- projmux init --allow-symlink
319
+ projmux setup terminal
320
+ projmux setup terminal ghostty
321
+ projmux setup terminal ghostty --apply
322
+ projmux setup terminal windows-terminal --apply
323
+ projmux setup terminal --config /path/to/file
324
+ projmux setup terminal --allow-symlink
315
325
  ```
316
326
 
317
327
  The merge is idempotent: matching bindings are no-ops, missing bindings are
318
328
  added, and keys already mapped to a different user action are skipped with a
319
- warning. `projmux init` does not read `keymap.toml`; direct tmux keys still
329
+ warning. `projmux setup terminal` does not read `keymap.toml`; direct tmux keys still
320
330
  belong in Settings > Keybindings or the keymap file.
321
331
 
322
332
  ### Ghostty
323
333
 
324
- `projmux init ghostty` emits plain Meta bytes for `Alt-1` through `Alt-5` in
334
+ `projmux setup terminal ghostty` emits plain Meta bytes for `Alt-1` through `Alt-5` in
325
335
  the managed block:
326
336
 
327
337
  ```text
@@ -341,7 +351,7 @@ refused by default; pass `--allow-symlink` to write through the link.
341
351
 
342
352
  ### Windows Terminal
343
353
 
344
- `projmux init windows-terminal` merges `sendInput` actions identified by the
354
+ `projmux setup terminal windows-terminal` merges `sendInput` actions identified by the
345
355
  `User.projmux*` ID prefix. The generated inputs use plain Meta bytes, tmux
346
356
  prefix sequences for split actions, and xterm modifier-arrow sequences for
347
357
  previous/next window:
@@ -0,0 +1,103 @@
1
+ # Native Picker
2
+
3
+ The native picker is the product picker for every interactive selection flow.
4
+ There is no runtime backend selection, saved picker selector, or external
5
+ picker process. `internal/ui/picker` owns the interaction contract and
6
+ `internal/ui/projmuxpicker` owns its visual composition.
7
+
8
+ ## Product Contract
9
+
10
+ | Area | Behavior | Primary evidence |
11
+ | --- | --- | --- |
12
+ | Items and results | Structured items keep display labels, stable values, optional search text, multi-line metadata, and action results separate. `internal/ui/pickercompat` maps older internal option/result shapes into this contract; it is not a runtime backend. | `TestPickerOptionsFromCompatPickerMapsCandidatesWhenEntriesAreEmpty`; `TestPickerOptionsFromCompatPickerPreservesTheme` |
13
+ | Search | Lower-case queries are case-insensitive, uppercase queries are case-sensitive, hidden values remain searchable when no explicit search key exists, explicit search-key lists preserve caller order, and simple rows receive fuzzy ranking and match highlighting. | `TestFilterItemsUsesSmartCase`; `TestFilterItemsSearchesHiddenValueWhenNoSearchKey`; `TestFilterItemsPreservesSearchKeyOrder`; `TestFilterItemsRanksBetterMatchesFirst` |
14
+ | Navigation | Up/Down, Ctrl-J/Ctrl-N, and Ctrl-K/Ctrl-P wrap selection; PageUp/PageDown and Home/End clamp or jump; empty lists remain safe. Ctrl-N and Ctrl-P remain real navigation keys unless a caller claims the key as a custom action. | `TestNativeInteractiveSupportsControlNavigationKeys`; `TestNativeInteractiveWrapsPreviousNavigationKeys`; `TestNativeInteractiveWrapsNextNavigationKeys`; `TestNativeInteractiveNavigationKeysAreNoopForEmptyList`; `TestNativeInteractiveJumpNavigationRemainsClamped` |
15
+ | Editing | Left/Right, Ctrl-A/E, Delete, Backspace, Ctrl-U/W, printable input, accept-query mode, and a visible query cursor are supported. | `TestNativeInteractiveEditsTypedQueryAtCursor`; `TestNativeInteractiveSupportsQueryLineEditingKeys`; `TestNativeInteractiveCtrlUDeletesBeforeCursor`; `TestNativeRunnerAcceptsTypedQuery` |
16
+ | Actions | Enter accepts, shared close actions abort, printable/control expect keys return stable action keys, command actions can refresh previews, and mutable actions can update items without restarting the picker. | `TestNativeRunnerUsesSharedCloseActions`; `TestNativeInteractiveSupportsPrintableExpectKeys`; `TestNativeInteractiveRunsCustomActionCommandAndRefreshes`; `TestNativeInteractiveCustomActionMutatesItemsAndRefreshes` |
17
+ | Deferred state | Deferred and event-triggered updates preserve query and selection by value, can repeat, and may explicitly choose a new focus value. | `TestNativeInteractiveDeferredUpdateTriggerRefreshesRepeatedly`; switch and notify sidebar mutable-refresh app tests |
18
+ | Preview | Popup previews use a right split, sidebar previews use a bottom split, control bytes and tabs are normalized before clipping, and preview scrolling/cycling rerenders in place. | `TestNativeInteractiveRendersWidePreviewBesideList`; `TestNativeInteractiveRendersDownPreviewBelowList`; `TestRenderSplitPreviewRowsNormalizesPreviewTabsBeforeTruncating`; `TestNativeInteractiveRendersPreviewOffset` |
19
+ | Mouse | SGR mouse input focuses on primary down, follows drag, accepts on matching release, and scrolls with the wheel. | `TestNativeInteractiveSelectsOnMouseRelease`; `TestNativeInteractiveMouseDragFollowsSelection`; `TestNativeInteractiveSupportsMouseWheelSelection` |
20
+ | Lifecycle | Interactive runs use the alternate screen, synchronized/coalesced frame updates, controlling-TTY fallback, and deterministic reader cleanup. | `TestNativeInteractiveUsesAlternateScreen`; `TestNativeInteractiveWrapsRedrawsInSynchronizedUpdates`; picker/setup lifecycle tests in `docs/agent-workflow.md` |
21
+
22
+ ## Rendering And Popup Chrome
23
+
24
+ The renderer owns rounded frames, optional titlebars and chips, search/header
25
+ separators, footer placement, selected-row styling, proportional scrollbars,
26
+ multi-line gaps, preview geometry, ANSI restoration, and terminal-cell width for
27
+ CJK text, emoji, and combining marks. Frame-owned cells inherit the effective
28
+ theme's `surface` and `chrome_foreground` values, including padding after
29
+ embedded resets.
30
+
31
+ Picker popups are always borderless tmux popups (`display-popup -B`) so the
32
+ native renderer owns the visible frame. The popup body receives a per-command
33
+ style derived from the effective theme; no global popup, pane, window, shell,
34
+ or status style is mutated. Project sidebars use a `20%` width with a `40`
35
+ column minimum, notification sidebars use `24%` with a `64` column minimum,
36
+ and both reserve the two statusbar rows when client height is known.
37
+
38
+ Primary evidence includes `TestAppRunTmuxPopupToggleUsesBorderlessNativePopup`,
39
+ `TestAppRunTmuxPopupToggleUsesNativePopupBodyStyleFromGlobalTheme`,
40
+ `TestBuildPopupToggleAppliesNativeBodyStyle`,
41
+ `TestSessionizerSidebarWidthUsesCompactMinimum`,
42
+ `TestNotifySidebarWidthUsesProductContract`, and
43
+ `TestSidebarPopupHeightLeavesStatusbarRows`.
44
+
45
+ ## Fuzzy Scoring Provenance
46
+
47
+ The native fuzzy scorer intentionally follows the fzf V2 dynamic-scoring
48
+ algorithm for the maintained reference cases in `internal/ui/picker/backend_test.go`.
49
+ `TestFuzzyScoreMatchesFZFV2ReferenceScores` and
50
+ `TestFuzzyScoreRejectsFZFV2ReferenceNonMatches` are provenance fixtures; keep
51
+ their expected scores stable when changing filtering internals.
52
+
53
+ ## Retired Artifact Contract
54
+
55
+ The retired picker selector environment variable and saved selector filename
56
+ are not runtime inputs. Their presence must not cause lookup, file reads,
57
+ warnings, deletion, rewriting, propagation to child popups, or any change to
58
+ the native path. There is deliberately no alias, migration, or stale-value
59
+ cleanup behavior.
60
+
61
+ Coverage is split by boundary:
62
+
63
+ - `TestRunNativePickerOptionDoesNotObserveRetiredBackendArtifacts` guards the
64
+ direct picker path, fails if the retired env name is queried, and verifies a
65
+ stale file is neither deleted nor replaced.
66
+ - `TestAppRunSwitchUsesNativePickerWithoutBackendLookup` guards the ordinary
67
+ switch flow.
68
+ - `TestAppRunTmuxPopupToggleIgnoresRetiredBackendArtifacts` and
69
+ `TestBuildPopupTogglePropagatesNativePickerEnvironmentWithoutRetiredBackend`
70
+ guard popup chrome and child-environment construction.
71
+ - `test/e2e/linux-smoke.sh` launches the ordinary attached-client Settings
72
+ popup with stale env/file values, completes the recorder flow, verifies the
73
+ file inode/content are unchanged, and rejects visible compatibility output.
74
+
75
+ ## Removed PoC Coverage Mapping
76
+
77
+ The retired standalone dependency sandbox and focused smoke duplicated product
78
+ coverage or tested a dependency policy that no longer exists. Its valid
79
+ behavior assertions remain covered as follows:
80
+
81
+ | Removed focused assertion | Maintained coverage |
82
+ | --- | --- |
83
+ | Settings and Labs render through the native picker | `TestSettingsUsesNativePicker`; `TestSettingsHubKeepsLabsSectionWithoutRetiredPickerChoices`; ordinary attached-client Settings recorder in `test/e2e/linux-smoke.sh` |
84
+ | Search chrome, smart-case filtering, native frame ownership, and full-height rendering | `TestNativeInteractiveSeparatesSearchHeaderFromList`; `TestFilterItemsUsesSmartCase`; `TestNativeInteractiveRendersBorderFrame`; `TestNativeInteractiveUsesAvailableHeightForSimpleLists` |
85
+ | AI Settings maps a typed selection to the saved mode | `TestAISettingsPickerSetsSelectedMode`; native picker option/result mapping tests |
86
+ | Switch popup/sidebar filtering, selection, preview cycling, and initial focus | `TestAppRunSwitchDefaultsToPopupAndOpensSelectedSession`; `TestSwitchCommandSupportsSidebarUI`; preview cycle and initial-position tests in `internal/app/switch_test.go` |
87
+ | Sessions popup filtering, selection, and preview cycling | `TestAppRunSessionsDefaultsToPopupAndOpensSelectedSession`; session popup preview/cycle tests in `internal/app/session_popup_test.go` |
88
+ | Notification sidebar navigation and mutable actions | `TestNotifySidebarUsesNativePicker`; notify sidebar ack/clear/deferred-refresh app tests |
89
+ | Launch-key close behavior and Ctrl-N/Ctrl-P navigation | `TestNativeInteractiveClosesOnMatchingLaunchCloseKey`; `TestNativeInteractiveSupportsControlNavigationKeys`; empty-list and wrap tests |
90
+ | Mouse selection and alternate-screen restoration | native mouse and lifecycle tests listed above |
91
+ | Generated shell config and the Alt-1 project-sidebar popup path | `TestShellWritesAppConfigAndRunsIsolatedTmux`; `TestAppRunTmuxPopupToggleOpensStandaloneSidebar`; popup frame/body-style tests listed above |
92
+ | Stale selector values do not affect the product | retained and strengthened in the unit/app/e2e negative coverage listed in the previous section |
93
+
94
+ The dependency-absence assertion is now a source-residue property rather than
95
+ a separate container scenario: production code contains no selector, resolver,
96
+ saved-selector access, propagation, or external picker launch path.
97
+
98
+ ## Maintenance
99
+
100
+ Update this document and the maintained list in
101
+ [`docs/agent-workflow.md`](agent-workflow.md) whenever picker behavior changes
102
+ coverage level, gains a new product flow, or changes input/render/action
103
+ semantics.
@@ -70,12 +70,13 @@ exit code 2.
70
70
  routing/debug context such as `agent`, `thread_id`, `turn_id`, `cwd`,
71
71
  `model`, and `client`; Claude hook rows also carry event-specific keys such as
72
72
  `tool_name`, `tool_input.command`, `error_type`, `subagent_type`, and
73
- `teammate_name`. Antigravity manual hook rows carry `agent=antigravity`,
73
+ `teammate_name`. Antigravity hook rows carry `agent=antigravity`,
74
74
  `conversation_id`, `termination_reason`, `fully_idle`,
75
75
  `tool_confirmation_pending`, `agent_state`, and `context_window` when present.
76
76
  The same `conversation_id` can seed session-state restore via
77
- `agy --conversation <uuid>` when it is UUID-shaped; Antigravity quota usage
78
- remains unsupported because `context_window` is not 5-hour/weekly quota data.
77
+ `agy --conversation <uuid>` when it is UUID-shaped. Antigravity
78
+ account quota remains outside notify attention semantics: `context_window` is a
79
+ separate conversation-local gauge and is never treated as quota data.
79
80
  Tmux bell fallback rows carry `agent=bell`, `event=bell`, and tmux target
80
81
  context such as pane title, command, session, window, pane, and socket.
81
82
  `notify list --json` includes this metadata as the structured data channel
@@ -18,7 +18,7 @@ The shim sets `PROJMUX_INSTALLER=npm` before executing the real binary so
18
18
  `projmux update status` and the Settings About screen can present
19
19
  npm-specific guidance. npm is only an update/install source label here; the
20
20
  keybinding flow remains `projmux shell` first, then `projmux setup` and
21
- `projmux init` only for terminals that swallow shortcuts.
21
+ `projmux setup terminal` only for terminals that swallow shortcuts.
22
22
 
23
23
  ## Local Packaging
24
24
 
@@ -116,5 +116,5 @@ completed update never leaves stale-path `run-shell` commands failing with
116
116
 
117
117
  The npm installer must not install system dependencies, edit shell startup
118
118
  files, or mutate tmux config. Those actions stay behind explicit
119
- `projmux doctor`, `projmux init`, Settings About update actions, or future
119
+ `projmux doctor`, `projmux setup terminal`, Settings About update actions, or future
120
120
  opt-in install commands.