projmux 0.6.5 → 0.6.7

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.
@@ -87,16 +87,17 @@ language.
87
87
  ## Keymap File
88
88
 
89
89
  Settings > Keybindings is the normal in-app editor for action keys. It lists
90
- user-configurable direct bindings, opens a detail screen, and offers `Add
91
- alias`, `Replace primary`, `Disable default`, `Reset`, `Press new key`, and
92
- `Type key chord`. Saving writes safe tmux plain chords to
90
+ actions with current keybinding summaries, opens a simple detail screen, and
91
+ offers `Add alias` plus reset. Saving writes safe tmux plain chords to
93
92
  `~/.config/projmux/keymap.toml`, rewrites `~/.config/projmux/tmux.conf`, and,
94
93
  when Settings is running inside tmux, sources that app config so tmux-level
95
94
  chords take effect immediately.
96
95
 
97
96
  Raw sequences that cannot be safely represented as a tmux plain chord are not
98
- persisted. Use Settings to save a safe direct alias, or use `projmux init` for
99
- the supported terminal mappings.
97
+ persisted. Use Settings to save a safe direct alias. When key delivery needs
98
+ terminal-layer remediation, first try the key in `projmux shell`, then run
99
+ `projmux setup` from the raw terminal, then use `projmux init` for supported
100
+ terminal adapters.
100
101
 
101
102
  `~/.config/projmux/keymap.toml` can also be edited by hand. When the file is
102
103
  absent, generated tmux config stays on the built-in defaults.
@@ -126,21 +127,27 @@ do not break. Settings preserves existing prefix entries when rewriting the
126
127
  file, but does not create new prefix keys, and generated tmux config no longer
127
128
  binds the old action prefix chords.
128
129
 
129
- Use an empty `keys` list to disable direct plain aliases for the action:
130
+ Settings > Keybindings names `AISplitPickerToggle` as the AI split popup
131
+ picker toggle. That action opens or closes the picker UI and is separate from
132
+ the direct AI pane actions, `ai-split-right` and `ai-split-down`. The direct
133
+ actions create a new managed AI pane each time they run, leaving existing AI
134
+ panes in place. `right` and `down` choose where the new pane is created.
135
+
136
+ Use an empty `keys` list to disable direct plain aliases for the action when
137
+ editing the file by hand:
130
138
 
131
139
  ```toml
132
140
  [bindings.ProjectSidebarToggle]
133
141
  keys = []
134
142
  ```
135
143
 
136
- In Settings, `Disable default` writes `keys = []`. `Reset` removes the saved
137
- override and returns to the built-in default. Legacy popup IDs such as
144
+ In Settings, reset removes the saved override and returns to the built-in
145
+ default. Legacy popup IDs such as
138
146
  `sessionizer-sidebar` still read, but new writes use canonical toggle names
139
147
  such as `ProjectSidebarToggle`, `NotifySidebarToggle`, `SessionPopupToggle`,
140
148
  `AISplitPickerToggle`, `SettingsToggle`, and `ProjectSwitcherToggle`. Internal
141
149
  popup commands use `Surface:Action` IDs and have surface-local conflict
142
- domains; those are manual `keymap.toml` entries, not Settings list/edit
143
- targets.
150
+ domains and remain visible in Settings when catalogued.
144
151
 
145
152
  The Settings writer is deterministic and rewrites the supported saved subset
146
153
  only. If the existing file has parse errors or unknown action IDs, Settings
@@ -149,7 +156,8 @@ shows the keymap error row and refuses to overwrite it until the file is fixed.
149
156
  The file currently affects generated tmux config from `projmux tmux
150
157
  print-config`, `projmux tmux install`, `projmux tmux print-app-config`,
151
158
  `projmux tmux install-app`, and `projmux shell`. Terminal init adapters such as
152
- Ghostty and Windows Terminal install built-in plain-byte mappings where needed.
159
+ Ghostty and Windows Terminal install built-in plain-byte mappings where needed;
160
+ they do not read `keymap.toml` or copy saved aliases into terminal configs.
153
161
  Changing terminal-layer mappings still requires rerunning `projmux init` and
154
162
  restarting the terminal where that terminal requires it.
155
163
 
@@ -176,9 +184,18 @@ field with source labels: `project`, `global`, or `fallback`.
176
184
  Renderer adapters can apply an already resolved `EffectiveTheme` to native
177
185
  picker frame background/foreground SGR and tmux status/window `colourN`
178
186
  background tokens. Settings and native project picker surfaces load global and
179
- project `[theme]` values through the shared effective-theme source. Fallback
180
- renderer output intentionally keeps the existing palette constants byte for
181
- byte.
187
+ project `[theme]` values through the shared effective-theme source. Native
188
+ picker frames also apply the built-in fallback `background`/`foreground` tokens
189
+ so picker-owned padding, empty rows, footer rows, and preview gaps do not
190
+ inherit the terminal default background.
191
+
192
+ Native picker popups launched through `projmux tmux popup-toggle` also pass a
193
+ per-popup tmux 3.4 `display-popup -s` body style using the effective theme
194
+ `background`/`foreground` tmux tokens. This styles only the tmux popup body
195
+ before the native renderer draws. It does not set global `popup-style` or
196
+ `popup-border-style`, and it does not change shell pane backgrounds,
197
+ `default-style`, `window-style`, OSC terminal backgrounds, or the general
198
+ status/window palette.
182
199
 
183
200
  Resolver schema shape:
184
201
 
@@ -270,11 +287,12 @@ user/global preference in this release.
270
287
  | `PROJMUX_FOCUS_DEBUG` | When non-empty, `projmux focus` prints one telemetry line to stderr. |
271
288
  | `PROJMUX_PICKER_BACKEND` | Legacy picker backend override. Any value, including old `fzf` settings, now resolves to the native picker. |
272
289
  | `PROJMUX_INSTALLER` | Installer source hint used by update flows. npm installs set this automatically; advanced release installs can set `github-release`. |
290
+ | `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. |
273
291
 
274
292
  ## Welcome State
275
293
 
276
- `projmux shell` stores per-version welcome state under the projmux state
277
- directory, normally:
294
+ `projmux shell` still reads and rewrites legacy per-version welcome state under
295
+ the projmux state directory for attach-popup compatibility, normally:
278
296
 
279
297
  ```text
280
298
  ${XDG_STATE_HOME:-$HOME/.local/state}/projmux/welcomed-v<version>.json
@@ -292,11 +310,21 @@ The current schema is:
292
310
  }
293
311
  ```
294
312
 
295
- `skip_version` is the only field that suppresses the shell welcome. When it
296
- matches the current projmux version, `projmux shell` skips the welcome. When it
297
- is absent or names a different version, the welcome is shown again. Older state
298
- files that contain only `last_welcomed_version` remain readable, but that field
299
- does not count as a skip.
313
+ This file no longer suppresses the shell-entry welcome. `skip_version` and
314
+ `last_welcomed_version` remain readable for legacy state and pending attach
315
+ popup compatibility, but the automatic shell prompt now uses release skip state
316
+ instead.
317
+
318
+ Update prompt skips are stored by latest release tag under the update cache
319
+ directory:
320
+
321
+ ```text
322
+ ${XDG_CACHE_HOME:-$HOME/.cache}/projmux/update-skip.json
323
+ ```
324
+
325
+ When `tag_name` matches the fresh cached latest release tag, `projmux shell`
326
+ continues without offering the update actions. A newer latest tag makes the
327
+ prompt eligible again.
300
328
 
301
329
  Example:
302
330
 
@@ -398,11 +426,13 @@ only a generic in-app queue/sidebar/statusbar row; it does not fire OS toast,
398
426
  The OS-level dispatch carries three modes. The in-app notify queue, the
399
427
  statusbar segment, and the attention badge stay live regardless of which
400
428
  mode is active — only the toast / notify-send / auto-raise fan-out is
401
- gated here.
429
+ gated here. The same mode also gates `projmux focus` post-switch osfocus
430
+ dispatch: only `raise` asks the host terminal to come forward after a
431
+ successful tmux focus.
402
432
 
403
433
  | Mode | On push | On click |
404
434
  | --- | --- | --- |
405
- | `none` | no toast | n/a |
435
+ | `off` / `none` | no toast | n/a |
406
436
  | `notify` | toast / notify-send fires | no click action |
407
437
  | `raise` | toast / notify-send fires AND the host terminal is auto-raised via the osfocus chain | toast click invokes `projmux focus --uri` via the `projmux://` handler |
408
438
 
@@ -410,30 +440,37 @@ Click activation is wired only for `raise`. The `projmux://` URI handler is
410
440
  registered on the first `raise` Notify of each tmux server (gated by the
411
441
  `@projmux_uri_protocol_registered_v6` marker). The
412
442
  mode only controls whether a toast fires at all and whether to follow it
413
- up with an on-push auto-raise.
443
+ up with an on-push auto-raise. `off` / `none` and `notify` also suppress
444
+ the focus-triggered osfocus raise after `projmux focus`.
414
445
 
415
446
  Resolution order (highest priority first):
416
447
 
417
- 1. `PROJMUX_DESKTOP_NOTIFY_MODE` env (`none` / `notify` / `raise`).
448
+ 1. `PROJMUX_DESKTOP_NOTIFY_MODE` env (`off` / `none` / `notify` / `raise`).
418
449
  2. `PROJMUX_DESKTOP_NOTIFY` env (legacy `on` / `off`; `on` → `notify`,
419
450
  `off` → `none`).
420
- 3. Tmux global option `@projmux_desktop_notify_mode`.
421
- 4. Tmux global option `@projmux_desktop_notify` (legacy `1` / `0`, same
451
+ 3. Saved Settings config
452
+ `${XDG_CONFIG_HOME:-$HOME/.config}/projmux/desktop-notify-mode`
453
+ (`off` / `notify` / `raise`).
454
+ 4. Tmux global option `@projmux_desktop_notify_mode`.
455
+ 5. Tmux global option `@projmux_desktop_notify` (legacy `1` / `0`, same
422
456
  mapping as the env above).
423
- 5. Default = `raise` when running inside WSL with `$WT_SESSION` set
457
+ 6. Default = `raise` when running inside WSL with `$WT_SESSION` set
424
458
  (Windows Terminal × WSL is the measured-working cell for osfocus
425
459
  raise today); otherwise `notify`.
426
460
 
427
461
  Migration is intentionally read-time. Users with the previous legacy
428
462
  toggle set keep their behavior — `@projmux_desktop_notify=0` resolves to
429
463
  `none`, `@projmux_desktop_notify=1` resolves to `notify`. The first
430
- Settings press through the new row writes the new key and the legacy key
431
- goes unused. No eager rewrite of tmux state.
464
+ Settings press through the new row writes `desktop-notify-mode`, mirrors the
465
+ new value into `@projmux_desktop_notify_mode` when tmux is live, and leaves the
466
+ legacy key unused. No eager rewrite of tmux state.
432
467
 
433
468
  Toggle from Settings > Notifications > `Desktop notifications`. The
434
469
  Settings info row labels the effective source as `env`, `env (legacy)`,
435
470
  `setting`, `setting (legacy)`, or `default` so users see which rung of
436
- the cascade pinned the value.
471
+ the cascade pinned the value. `projmux tmux apply` regenerates the live tmux
472
+ option from the saved Settings file; when that file is missing, apply writes
473
+ only the default for the current host.
437
474
 
438
475
  Hook details for new-session lifecycle hooks and project-local
439
476
  `.projmux/config.toml` live in [Hooks](hooks.md).
@@ -542,8 +579,8 @@ while `on` and `off` take precedence. Auto-save only updates the latest
542
579
  snapshot. Named snapshots are manual and are never updated by auto-save.
543
580
 
544
581
  Project open from the Alt-1 sidebar defaults to opening a closed project as an
545
- `Empty session`. The optional `Settings > Labs > Sidebar startup picker` toggle
546
- enables the native sidebar `Start project` step. Rows appear as `Latest
582
+ `Empty session`. The optional `Settings > Session State > Sidebar startup
583
+ picker` toggle enables the native sidebar `Start project` step. Rows appear as `Latest
547
584
  snapshot`, named snapshot rows, `Empty session`, and `Back`. `Latest snapshot`
548
585
  is the snapshot auto-save that changes as auto-save runs; named snapshots are
549
586
  fixed snapshots. Rows include saved-at date/time metadata when projmux can
@@ -562,7 +599,7 @@ Default `projmux shell` no longer opens a startup picker or replays session-stat
562
599
  snapshots before attach. It still derives the default app session identity and
563
600
  startup directory from the current project context when available; otherwise it
564
601
  uses the `home` target and home directory. Session-state restore selection is
565
- limited to the Labs sidebar startup picker.
602
+ limited to the Session State sidebar startup picker.
566
603
 
567
604
  Settings > Session State is global settings only: global auto-save, auto-save
568
605
  interval, and storage/retention policy. Settings > Project > Session State
package/docs/hooks.md CHANGED
@@ -206,7 +206,11 @@ desktop sender and receives positional arguments
206
206
  `summary body urgency app-name tag group icon-path`. That `urgency` value is
207
207
  the OS notification urgency, not the notify-queue severity. AI approval,
208
208
  input, selection, and confirmation rows can stay critical in the queue and UI
209
- while the desktop notification hook receives `normal`.
209
+ while the desktop notification hook receives `normal`. Live AI status badges
210
+ are a third surface: permission/input-required panes use the action-required
211
+ amber-orange status role, response-complete panes use success green, and
212
+ in-progress panes use progress yellow. They do not inherit the critical queue
213
+ severity, and permission/input status badges do not use red.
210
214
 
211
215
  ## Codex Hooks Engine
212
216
 
@@ -602,6 +606,39 @@ Codex and are managed from `Settings > Notifications > Hook quiet policy`.
602
606
  They only affect ingest delivery; `projmux ai integrate claude` still uses the
603
607
  catalog `install` field for installed hook events.
604
608
 
609
+ ## Antigravity Hook Ingest
610
+
611
+ `projmux ai ingest antigravity-hook < payload.json` is available for manual
612
+ Antigravity CLI `agy` hook/statusline payloads. Projmux does not provide
613
+ `projmux ai integrate antigravity`, does not install Antigravity hooks, and
614
+ does not mutate Antigravity user config. The Delivery sources diagnostic
615
+ therefore reports Antigravity as a read-only unsupported/manual row. If users
616
+ wire it by hand, use an absolute `projmux` command path or a known cwd because
617
+ relative command paths failed the Phase 0b smoke.
618
+
619
+ The default known Antigravity catalog records only observed Phase 0b signals
620
+ and marks them `install: false`:
621
+
622
+ | Event/signal | Behavior |
623
+ | --- | --- |
624
+ | `PostInvocation` | marks the matched pane hook-active and writes a quiet ingest diagnostic; no notify queue entry is pushed |
625
+ | `Stop` | pushes a completion row, or a critical error row when `error` is present or `terminationReason` is non-normal |
626
+ | `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 |
627
+ | `Statusline` without `tool_confirmation_pending=true` | marks the matched pane hook-active and writes a quiet ingest diagnostic |
628
+ | unknown events | mark the matched pane hook-active and write quiet ingest diagnostics only |
629
+
630
+ Antigravity notify rows use `agent=antigravity` metadata. Accepted fields
631
+ include `conversationId`/`conversation_id`, `cwd`, `workspace.path`,
632
+ `transcriptPath`, `terminationReason`, `error`, `fullyIdle`, `agent_state`,
633
+ `context_window`, and nested `statusline.tool_confirmation_pending`.
634
+ Antigravity ingest uses `conversationId` as pane thread metadata for matching
635
+ and as session-state resume metadata. Session restore uses
636
+ `agy --conversation <uuid>` only when that id is present and UUID-shaped;
637
+ otherwise preview and doctor render `resume unavailable`. Antigravity usage
638
+ quota HUD support remains unsupported because the stable usage signal is
639
+ `context-window-only` statusline data, not 5-hour/weekly quota data. Raw
640
+ payloads or transcript contents are not stored.
641
+
605
642
  ## Ingest Debug Log
606
643
 
607
644
  Every `projmux ai ingest ...` path appends compact JSONL diagnostics to
package/docs/install.md CHANGED
@@ -28,14 +28,14 @@ projmux shell
28
28
  ```
29
29
 
30
30
  Each `projmux shell` launch prints a short welcome with the current version,
31
- detach/exit keys, core app shortcuts, and cached update status when available.
32
- Press Enter to continue for this run, or press `s` to skip the welcome for the
33
- current projmux version. The next projmux version shows the welcome again.
34
-
35
- If an installer-supported update is available, the same prompt keeps update
36
- actions separate from welcome skip: press `u` to run `projmux update apply`,
37
- `n` to print the manual update command, or `d` to skip daily update prompts for
38
- that release.
31
+ detach/exit keys, a bootstrap reminder, and cached release status when
32
+ available. Press Enter to continue into the shell.
33
+
34
+ If an update is available, the same shell-entry prompt uses one action
35
+ vocabulary: Enter continues, `u` upgrades by invoking `projmux update apply`,
36
+ and `s` skips that latest release tag until a newer tag appears. For `source`
37
+ or unknown installer sources, `u` prints installer guidance and then continues
38
+ shell entry.
39
39
 
40
40
  To revisit the guide later, run `projmux welcome`, or use Settings > About >
41
41
  Welcome inside the app to open it in a visible viewer. Set `PROJMUX_WELCOME=off` before
@@ -12,8 +12,7 @@ supported fallback guidance.
12
12
  The recommended path when a key does not fire:
13
13
 
14
14
  1. Try the key inside `projmux shell`.
15
- 2. Open Settings > Keybindings > Diagnostic, or run `projmux setup` outside
16
- tmux, to see which bytes reach the process.
15
+ 2. Run `projmux setup` outside tmux to see which bytes reach the process.
17
16
  3. For supported terminals, preview `projmux init [terminal]`; add `--apply`
18
17
  only after reviewing the merge.
19
18
  4. For unsupported terminals, configure plain Meta bytes or add a tmux alias in
@@ -34,7 +33,7 @@ These shortcuts are the guaranteed launch defaults. They need no tmux prefix.
34
33
  | `Alt-1` | Project sidebar |
35
34
  | `Alt-2` | Notify sidebar |
36
35
  | `Alt-3` | Existing session popup |
37
- | `Alt-4` | AI split picker |
36
+ | `Alt-4` | AI split popup picker |
38
37
  | `Alt-5` | Settings |
39
38
 
40
39
  The tmux prefix remains the upstream default `Ctrl-b`. Inside a running
@@ -47,14 +46,31 @@ keymap actions, pane switching, window switching, and rename actions remain
47
46
  visible. Transport-dependent rows show the default transport key separately
48
47
  from editable plain aliases.
49
48
 
49
+ The Settings flow is intentionally simple: the root is one action list with
50
+ current key summaries, and each action detail shows the action, current
51
+ keybinding/aliases, `Add alias`, and reset. Diagnostic/probe/init workflows are
52
+ not first-class Settings tabs; use `projmux setup` and `projmux init` from the
53
+ terminal when key delivery needs remediation.
54
+
50
55
  Optional direct aliases can be added for actions such as:
51
56
 
52
57
  | Canonical action | Meaning |
53
58
  | --- | --- |
54
59
  | `ProjectSwitcherToggle` | Project switcher popup |
60
+ | `AISplitPickerToggle` | AI split popup picker; pressing again closes the picker popup |
61
+ | `ai-split-right` | Open a new direct AI split to the right |
62
+ | `ai-split-down` | Open a new direct AI split below |
55
63
  | `new-window` | New tmux window in the current pane directory |
56
64
  | `rename-window` | Rename the current tmux window |
57
65
 
66
+ `AISplitPickerToggle` is the `Alt-4` popup picker toggle. It opens or closes
67
+ the picker UI where the user chooses the AI split mode. It is separate from
68
+ the direct `ai-split-right` and `ai-split-down` actions.
69
+
70
+ The direct AI split actions create a new managed AI pane each time they run.
71
+ Existing AI panes are left in place; the requested direction controls where the
72
+ new pane is created.
73
+
58
74
  Pane switching is catalogued as transport-dependent and the generated app tmux
59
75
  config binds `M-Left`, `M-Right`, `M-Up`, and `M-Down` to `select-pane`
60
76
  movement. Previous/next window remain transport-dependent and the generated app
@@ -67,19 +83,20 @@ without storing or replacing the transport default. Rename actions no longer
67
83
  have a built-in terminal fallback; use tmux's prefix rename flow or configure
68
84
  an explicit safe alias where the action is editable.
69
85
 
70
- ## Roadmap Requirements
86
+ ## Product Requirements
71
87
 
72
- Follow-up Phase 2 keeps Settings > Keybindings as a discovery surface. It must
73
- continue to expose launch toggles, sidebar keymap actions, picker-local actions,
74
- pane switching, window switching, and rename actions. Transport-dependent rows
75
- should explain the default transport key and offer only additive safe plain
76
- aliases; diagnostic-only rows should explain why they are not editable.
88
+ Settings > Keybindings stays a discovery surface. It must continue to expose
89
+ launch toggles, sidebar keymap actions, picker-local actions, pane switching,
90
+ window switching, and rename actions. The primary Settings flow is not the
91
+ terminal remediation surface: replace-primary, disable-default, typed fallback,
92
+ terminal mapping preview/apply, and init execution rows stay out of the action
93
+ detail.
77
94
 
78
- Follow-up Phase 3 removes the `UserN` / `CSI-u` route from the product model.
79
- Windows Terminal and Ghostty-centered replacements should use plain
80
- Meta/control chords or xterm modifier sequences where possible. If a key cannot
81
- be represented that way, leave it as a non-editable unsupported or diagnostic
82
- row instead of preserving a User-key or CSI-u fallback.
95
+ The product model does not support `UserN` or `CSI-u` as fallback guidance.
96
+ Windows Terminal and Ghostty adapters use built-in plain Meta/control bytes or
97
+ xterm modifier sequences where possible. If a key cannot be represented that
98
+ way, leave it as a non-editable unsupported or diagnostic row instead of
99
+ preserving a User-key or CSI-u fallback.
83
100
 
84
101
  ## Picker Actions
85
102
 
@@ -126,13 +143,15 @@ Legacy popup IDs such as `sessionizer-sidebar`, `notify-sidebar`,
126
143
  `session-popup`, `ai-split-picker-right`, `ai-split-settings`, and
127
144
  `sessionizer` still read. Settings and new docs show the canonical toggle
128
145
  names: `ProjectSidebarToggle`, `NotifySidebarToggle`, `SessionPopupToggle`,
129
- `AISplitPickerToggle`, `SettingsToggle`, and `ProjectSwitcherToggle`.
146
+ `AISplitPickerToggle`, `SettingsToggle`, and `ProjectSwitcherToggle`. Direct
147
+ AI split actions keep their command IDs, `ai-split-right` and `ai-split-down`,
148
+ so Settings can distinguish them from the `Alt-4` popup picker toggle.
130
149
 
131
150
  ## Diagnose: `projmux setup`
132
151
 
133
152
  Run `projmux setup` outside tmux to find out which projmux keys reach the raw
134
- terminal. The same diagnostic is available in-app at Settings > Keybindings >
135
- Diagnostic.
153
+ terminal. Settings > Keybindings remains the action/alias editor; setup is the
154
+ terminal delivery diagnostic.
136
155
 
137
156
  | Status | Meaning |
138
157
  | --- | --- |
@@ -32,6 +32,7 @@ native picker engine and is not a public dependency-policy change.
32
32
  | close `--bind key:abort` | Esc, Ctrl-C, Alt-N, Ctrl-Alt-S variants | Covered | `CloseActions`; `TestNativeRunnerUsesSharedCloseActions` |
33
33
  | terminal modified-key encoding | legacy parser fixtures, Ghostty/kitty-style modified keys | Covered | native handles app-specific parser fixtures, generic modified letters/digits, and non-text keys such as Enter/Esc/Backspace/Tab; this is backend parity, not product fallback guidance; `TestNativeInteractiveSupportsCSIuAppKeyBindings` |
34
34
  | `execute-silent(...)+refresh-preview` | switch/session preview cycling | Covered for command execution and rerender loop | `pickercompat.PickerOptions`/`pickercompat.OptionsFromPicker`; `TestNativeInteractiveRunsCustomActionCommandAndRefreshes`; `TestPickerOptionsMapsCompatBindingsToContractActions`; Docker no-fzf e2e sends `Right` and `Alt-Down` before selection |
35
+ | action-local/event-backed mutable refresh | notify sidebar `a` ack and `x` non-critical clear; notify sidebar queue-write event refresh; switch sidebar `Ctrl-X` kill | Covered for in-session row/live-state refresh without picker restart | `picker.Action.Mutate` returns a `DeferredUpdate`, notify queue-write events trigger the same `DeferredUpdate` path after an event arrives, and both reuse the native frame diff renderer plus value-then-clamp selection preservation; `TestNativeInteractiveCustomActionMutatesItemsAndRefreshes`; `TestNativeInteractiveCustomActionRefreshPreservesSelectedValue`; `TestNativeInteractiveDeferredUpdateTriggerRefreshesRepeatedly`; notify sidebar app tests assert one picker invocation with refreshed rows/live state and event subscription; switch sidebar kill app tests assert one native picker invocation with refreshed rows/preview and previous-live-session guard preservation |
35
36
  | `focus:execute-silent(...)` | switch sidebar focus | Covered | native renders the selection frame diff before running sidebar focus commands so movement stays visible before tmux focus side effects; `runNativeFocusAction`; `TestNativeInteractiveRunsFocusActionOnSelectionChange` |
36
37
  | `start:pos(N)` | switch sidebar initial row | Covered | `pickercompat.PickerOptions`/`pickercompat.OptionsFromPicker`; `TestPickerOptionsFromCompatPickerMapsStartPosToInitialIndex`; `TestPickerOptionsMapsCompatBindingsToContractActions` |
37
38
  | `--preview` | switch, sessions | Covered by command output | `nativePreviewLines`; `TestNativeInteractiveRendersSelectedPreview` |
@@ -46,8 +47,9 @@ native picker engine and is not a public dependency-policy change.
46
47
  | fzf navigation keys | interactive selection in searchable lists | Covered | native maps modified-key `Ctrl-J` plus `Ctrl-N` to down and `Ctrl-P`/`Ctrl-K` to up when not claimed by a custom action; up/down-family movement is safe on empty lists; raw LF remains Enter for PTY compatibility; `TestNativeInteractiveSupportsFZFNavigationKeys`; `TestNativeInteractiveNavigationKeysAreNoopForEmptyList` |
47
48
  | alternate-screen lifecycle | fzf fullscreen picker screen restore | Covered | native frame updates and screen exit return to column 0 before terminal control sequences, screen exit resets styles plus clears the alternate buffer from the home cursor before restore, and real TTY restores get a short settle window before caller handoff; `nativeScreenEnter`; `TestNativeInteractiveUsesAlternateScreen`; `TestRenderFullFrameUpdateAlwaysHomesAndWritesFrame` |
48
49
  | frame content width | fzf border inner width | Covered | `ContentLayout` uses the frame inner width so separators and rows reach the right border; `TestRendererContentLayoutUsesFrameInnerWidth` |
49
- | tmux popup frame interaction | native picker popups launched through `popup-toggle` | Covered for native backend popups | `popup-toggle` passes tmux `display-popup -B` when `PROJMUX_PICKER_BACKEND=native`, so the native picker owns the visible frame instead of double-drawing with the tmux popup border; native Alt-1 uses a compact native-only minimum while fzf keeps the previous project sidebar minimum; Alt-1/Alt-2 sidebar heights reserve two bottom statusbar rows; Alt-2 notify sidebar keeps the fzf-like `24%` / min `64` baseline; `TestAppRunTmuxPopupToggleUsesBorderlessPopupForNativeBackend`; `TestSessionizerSidebarWidthKeepsFZFMinimum`; `TestSidebarPopupHeightLeavesStatusbarRows`; `TestNotifySidebarWidthMatchesFZFBaseline`; `TestAppRunTmuxPopupToggleKeepsNotifySidebarFZFSizingForNative` |
50
- | optional native titlebar | native picker popup frame | Covered for empty-title compatibility and opt-in titles | `picker.Options.Title`; `RenderFrameWithTitle`; `ContentLayoutWithTitle`; empty titles keep the prior frame unchanged, non-empty titles render in a picker-owned section below the top border with neutral titlebar background, rule fill, and a divider above search/content, and the native Alt-1 project sidebar opts into `Projects`; `TestRendererRenderFrameWithTitleKeepsDefaultWhenTitleEmpty`; `TestRendererRenderFrameWithTitleUsesTitlebarRow`; `TestRendererContentLayoutWithTitleReservesTitlebarRow`; `TestNativeInteractiveRendersOptionalTitlebar`; `TestSwitchCommandNativeSidebarSetsTitle` |
50
+ | picker-owned app background | native picker frame interior | Covered for renderer-owned cells | `ThemeFromEffective` applies built-in fallback and explicit effective background/foreground SGR to the native frame, and frame rows resume the app style after embedded resets so content padding, empty no-footer rows, footer rows, scrollbars, and preview gaps do not leak terminal default background; `TestThemeFromEffectiveFallbackPaintsFrameBackground`; `TestRendererFrameBackgroundResumesAfterContentResetBeforePadding`; `TestNativeInteractiveNoFooterBlankRowsUseThemeBackground`; `TestNativeInteractiveSplitPreviewGapsUseThemeBackground`; `TestNativeInteractiveSettingsAIBadgeStyleLongPreviewClampsFrameRows` |
51
+ | tmux popup frame interaction | native picker popups launched through `popup-toggle` | Covered for native backend popups | `popup-toggle` passes tmux `display-popup -B` when `PROJMUX_PICKER_BACKEND=native`, so the native picker owns the visible frame instead of double-drawing with the tmux popup border; tmux 3.4 per-command `display-popup -s` is used for the popup body style, setting `bg`/`fg` from the same effective theme tmux background/foreground tokens so tmux's blank/pre-draw popup body matches the native picker surface before the app renderer paints; no global `popup-style`, `popup-border-style`, shell pane background, `default-style`, `window-style`, OSC background, or status/window palette options are changed; native Alt-1 uses a compact native-only minimum while fzf keeps the previous project sidebar minimum; Alt-1/Alt-2 sidebar heights reserve two bottom statusbar rows; Alt-2 notify sidebar keeps the fzf-like `24%` / min `64` baseline; `TestAppRunTmuxPopupToggleUsesBorderlessPopupForNativeBackend`; `TestAppRunTmuxPopupToggleUsesNativePopupBodyStyleFromEffectiveTheme`; `TestBuildPopupToggleWithPickerBackendStylesNativeOnly`; `TestBuildDisplayPopupArgsAddsBodyStyle`; `TestSessionizerSidebarWidthKeepsFZFMinimum`; `TestSidebarPopupHeightLeavesStatusbarRows`; `TestNotifySidebarWidthMatchesFZFBaseline`; `TestAppRunTmuxPopupToggleKeepsNotifySidebarFZFSizingForNative` |
52
+ | optional native titlebar | native picker popup frame | Covered for empty-title compatibility and opt-in titles | `picker.Options.Title`; `RenderFrameWithTitle`; `ContentLayoutWithTitle`; empty titles keep the prior frame unchanged, non-empty titles render in a picker-owned section below the top border, titlebar text/divider/chip gaps inherit the frame background/foreground instead of a separate titlebar overlay ANSI layer, and the native Alt-1 project sidebar opts into `Projects`; `TestRendererRenderFrameWithTitleKeepsDefaultWhenTitleEmpty`; `TestRendererRenderFrameWithTitleUsesTitlebarRow`; `TestRendererContentLayoutWithTitleReservesTitlebarRow`; `TestNativeInteractiveRendersOptionalTitlebar`; `TestSwitchCommandNativeSidebarSetsTitle` |
51
53
  | redraw flicker/top clipping | keyboard navigation in exact-height tmux popup | Partially covered | native redraws use synchronized updates plus coalesced row diffs after the first frame, skip unchanged frames, render frame diffs before sidebar focus commands, frame rendering avoids trailing bottom-border CRLF, and screen exit clears the alternate buffer before restore; `TestNativeInteractiveWrapsRedrawsInSynchronizedUpdates`; `TestFrameUpdateRendererSkipsUnchangedFrame`; `TestFrameUpdateRendererCoalescesEachFrameUpdate`; `TestRendererRenderFrameUsesCRLFRowsForRawTTY`; `TestNativeInteractiveUsesAlternateScreen` |
52
54
 
53
55
  ## Native Surface Architecture
@@ -72,9 +74,11 @@ native picker engine and is not a public dependency-policy change.
72
74
  ## Frame Chrome ANSI
73
75
 
74
76
  Native picker frame chrome is normalized in `internal/ui/projmuxpicker`.
75
- Titlebars restore the titlebar style after embedded ANSI resets, search prompt
76
- and footer separators fill the available frame width, and header, row, footer,
77
- and preview lines close any active SGR style before padding or frame borders can
77
+ Titlebar text, title dividers, and chip-strip gaps inherit the frame
78
+ background/foreground instead of applying a second titlebar overlay ANSI layer;
79
+ chip bodies can still carry active/inactive/disabled tones. Search prompt and
80
+ footer separators fill the available frame width, and header, row, footer, and
81
+ preview lines close any active SGR style before padding or frame borders can
78
82
  inherit it. This phase does not add popup modes or change the `popup-toggle`
79
83
  contract; native popups still rely on the existing borderless tmux popup path.
80
84
 
@@ -107,8 +111,10 @@ contract; native popups still rely on the existing borderless tmux popup path.
107
111
  the right-side preview layout instead of inline preview, asserts the stored
108
112
  preview cursor, selects `bravo-web`, and asserts tmux reports the selected
109
113
  session's active target on the expected window with the expected pane path.
110
- - `notify sidebar`: native routing is unit-covered; Docker no-fzf e2e pushes a
111
- notification, presses printable expect key `a`, and verifies the row is acked.
114
+ - `notify sidebar`: native routing is unit-covered; app tests cover
115
+ queue-write event subscription and picker tests cover repeated
116
+ event-triggered deferred refresh. Docker no-fzf e2e pushes a notification,
117
+ presses printable expect key `a`, and verifies the row is acked.
112
118
 
113
119
  ## Experimental Boundaries
114
120
 
@@ -13,6 +13,7 @@ via `projmux focus`, and feeds the HUD pill rendered by
13
13
  ```
14
14
  ${XDG_STATE_HOME:-$HOME/.local/state}/projmux/notify.json
15
15
  ${XDG_STATE_HOME:-$HOME/.local/state}/projmux/notify.json.lock
16
+ ${XDG_STATE_HOME:-$HOME/.local/state}/projmux/notify-queue-events/refresh-*.sock
16
17
  ```
17
18
 
18
19
  The lock file is acquired via `O_CREATE|O_EXCL` with bounded retry
@@ -24,6 +25,13 @@ The queue file is a pretty-printed JSON array of `Notification`
24
25
  objects, sorted newest-first on read. `expires_at` is freshness metadata;
25
26
  expired entries are not filtered or deleted by `list`.
26
27
 
28
+ Open native notify sidebars also create per-process Unix datagram sockets for
29
+ queue-write refresh events. If the state-dir socket path would exceed Unix
30
+ socket path limits, projmux uses a short per-state-dir temp runtime path for
31
+ the socket directory. These sockets are transient UI delivery endpoints only:
32
+ they are not queue state, do not change the JSON schema, and are removed when
33
+ the sidebar exits.
34
+
27
35
  ## Data model
28
36
 
29
37
  `internal/core/notify`:
@@ -58,11 +66,17 @@ exit code 2.
58
66
  routing/debug context such as `agent`, `thread_id`, `turn_id`, `cwd`,
59
67
  `model`, and `client`; Claude hook rows also carry event-specific keys such as
60
68
  `tool_name`, `tool_input.command`, `error_type`, `subagent_type`, and
61
- `teammate_name`. Tmux bell fallback rows carry `agent=bell`, `event=bell`,
62
- and tmux target context such as pane title, command, session, window, pane, and
63
- socket. `notify list --json` includes this metadata as the structured data
64
- channel while human table/sidebar output keeps the compact text body. Existing
65
- entries without metadata remain valid.
69
+ `teammate_name`. Antigravity manual hook rows carry `agent=antigravity`,
70
+ `conversation_id`, `termination_reason`, `fully_idle`,
71
+ `tool_confirmation_pending`, `agent_state`, and `context_window` when present.
72
+ The same `conversation_id` can seed session-state restore via
73
+ `agy --conversation <uuid>` when it is UUID-shaped; Antigravity quota usage
74
+ remains unsupported because `context_window` is not 5-hour/weekly quota data.
75
+ Tmux bell fallback rows carry `agent=bell`, `event=bell`, and tmux target
76
+ context such as pane title, command, session, window, pane, and socket.
77
+ `notify list --json` includes this metadata as the structured data channel
78
+ while human table/sidebar output keeps the compact text body. Existing entries
79
+ without metadata remain valid.
66
80
 
67
81
  ## CLI surface
68
82
 
@@ -100,11 +114,22 @@ pane and acks the row after focus succeeds. The surface actions
100
114
  are edited in Settings, while internal picker aliases are adjusted in
101
115
  `keymap.toml` when needed. Runtime footer key guides read the merged keymap
102
116
  and show the default alias when present, otherwise the first configured alias,
103
- so custom aliases do not make the UI stale. Rows are intentionally compact: the visible label keeps
117
+ so custom aliases do not make the UI stale. `NotifySidebar:Ack` and
118
+ `NotifySidebar:ClearNonCritical` refresh rows, live state, and selection inside
119
+ the same native picker session; `NotifySidebar:ClearAll` still closes the
120
+ popup and prints a summary. Rows are intentionally compact: the visible label keeps
104
121
  notification text first, then age, project, window, and pane metadata; hidden
105
122
  queue ids remain action values but the sidebar has no search input and
106
123
  intentionally does not expose a separate metadata detail view.
107
124
 
125
+ When a new pending notification is successfully pushed by any app producer
126
+ (`notify push`, reply-ready, reconcile backfill, or bell fallback), open native
127
+ notify sidebars receive a best-effort queue-write event and rerun the same
128
+ `DeferredUpdate` row/live-state refresh path used by `a` ack and `x`
129
+ non-critical clear. Event delivery errors are ignored after the queue write:
130
+ the push still succeeds, and reopening the sidebar remains the recovery path
131
+ for seeing the latest queue.
132
+
108
133
  `--live` adds a non-mutating explanation view that reads
109
134
  `tmux list-panes -a` and compares the queue with live reply-state panes. It
110
135
  does not push, ack, or otherwise repair anything. Human output keeps the
@@ -153,6 +178,9 @@ path), then:
153
178
  - reports every existing queue entry whose id starts with `ai:` and whose
154
179
  pane no longer matches that condition as stale, without acking it.
155
180
 
181
+ Successful backfill pushes publish the same best-effort open-sidebar refresh
182
+ event as other pending queue additions.
183
+
156
184
  Soft-fails when tmux is not running (returns a populated `errors`
157
185
  field rather than a non-zero exit) so the post-install hook does not
158
186
  break. Run this as the recovery path when the on-disk queue has drifted
@@ -180,7 +208,10 @@ an entry with:
180
208
  When the pane leaves the reply state (manual `attention clear`,
181
209
  `status set idle`, or a window close), `AckReplyReady` intentionally does not
182
210
  remove the entry. The user consumes it through explicit ack. Store errors are
183
- swallowed so the live tmux UI never blocks on disk IO.
211
+ swallowed so the live tmux UI never blocks on disk IO. After a successful
212
+ queue write and same-pane non-critical compaction, the producer publishes the
213
+ same best-effort notify-sidebar queue-write refresh event used by
214
+ `projmux notify push`.
184
215
 
185
216
  Manual `projmux attention toggle` on a pane without an agent option
186
217
  does NOT push — the queue is intentionally AI-driven; reconcile honours
@@ -200,7 +231,9 @@ tmux and writes an info/source-ai row with:
200
231
  Unlike reply-ready reconcile, bell ingest does not require AI pane metadata.
201
232
  It is intentionally available for arbitrary CLIs that only signal attention
202
233
  through BEL or OSC 9. Repeated bells from the same pane are suppressed for 5
203
- seconds before a later bell refreshes the stable queue id.
234
+ seconds before a later bell refreshes the stable queue id. Successful
235
+ non-deduped bell queue writes publish the same best-effort open-sidebar
236
+ refresh event as other pending queue additions.
204
237
 
205
238
  ## Consumer (status-bar click)
206
239
 
@@ -230,8 +263,11 @@ Outcomes:
230
263
 
231
264
  The same consume policy is shared by notify-sidebar Enter and OS
232
265
  click-to-focus Toast callbacks after a real tmux focus dispatch succeeds.
266
+ OS Toast click-to-focus is only registered when Desktop notification mode is
267
+ `raise`; the in-app sidebar/statusbar consume path works in every mode.
233
268
  Pane focus hooks and attention clear paths remain live-attention-only and do
234
- not ack the notify queue. Non-critical AI completion producers also compact
269
+ not ack the notify queue; their response-complete badge consume is limited to
270
+ live tmux pane badge/state options. Non-critical AI completion producers also compact
235
271
  older same-pane non-critical AI rows after replacing/pushing their latest row,
236
272
  so reply-ready/stop/bell-style completion rows stay latest-state centered
237
273
  without changing the queue schema or TTL contract.
@@ -31,13 +31,15 @@ make npm-pack
31
31
  or:
32
32
 
33
33
  ```bash
34
- scripts/package-npm.sh --version 0.4.0 --out /tmp/projmux-npm --pack
34
+ scripts/package-npm.sh --version 1.2.3 --out /tmp/projmux-npm --pack
35
35
  ```
36
36
 
37
37
  The script stages package directories under `dist/npm` by default. It builds
38
38
  the Go binary for each supported platform, copies package metadata and docs,
39
- updates package versions in the staged copies, then runs `npm pack --dry-run`
40
- when `--pack` is set.
39
+ updates package versions in the staged copies, generates root
40
+ `optionalDependencies` for the supported platform packages using the same
41
+ version, verifies the staged metadata is internally consistent, then runs
42
+ `npm pack --dry-run` when `--pack` is set.
41
43
 
42
44
  ## Publish Order
43
45
 
@@ -51,8 +51,12 @@ CLI for inspection/actions.
51
51
  Agent restore direct-starts supported resume commands when creating fresh tmux
52
52
  panes, matching the `projmux ai split` wrapper shape: the wrapper prepends the
53
53
  agent binary directory to `PATH`, changes to the saved cwd, sets the terminal
54
- and tmux pane title from the saved agent topic, then execs `codex resume <id>`
55
- or `claude --resume <id>`. This avoids typing agent resumes with
54
+ and tmux pane title from the saved agent topic, then execs `codex resume <id>`,
55
+ `claude --resume <id>`, or `agy --conversation <uuid>`. Antigravity restore
56
+ uses only the stable statusline `conversation_id` or hook `conversationId`
57
+ metadata captured as the pane resume id; missing or non-UUID Antigravity ids
58
+ render as `resume unavailable` rather than falling back silently to a shell
59
+ recipe. This avoids typing agent resumes with
56
60
  `tmux send-keys`. The restore wrapper is still a non-interactive shell command
57
61
  tail, so it does not replay the original pane's interactive shell startup,
58
62
  environment, shell functions, aliases, or live process state. Startup recipes
@@ -74,7 +78,7 @@ when building `Named snapshot` candidates; new primary surfaces should describe
74
78
  the restore unit as a snapshot, not as a separate layout or preset feature.
75
79
 
76
80
  Project open from the Alt-1 sidebar defaults to opening a closed project as an
77
- `Empty session`. `Settings > Labs > Sidebar startup picker` is an opt-in toggle;
81
+ `Empty session`. `Settings > Session State > Sidebar startup picker` is an opt-in toggle;
78
82
  when it is on, closed project open advances inside the sidebar to the native
79
83
  `Start project` step. Rows are ordered `Latest snapshot`, named snapshot rows,
80
84
  `Empty session`, then `Back`. `Latest snapshot` is the auto-saved snapshot that
@@ -98,5 +102,5 @@ directly without a startup picker or trust gate.
98
102
  Default `projmux shell` no longer opens a compatibility startup picker and no
99
103
  longer accepts startup selector flags for session-state restore. It always
100
104
  follows the normal empty attach path after resolving the target app session name
101
- and startup directory. Use `Settings > Labs > Sidebar startup picker` for
105
+ and startup directory. Use `Settings > Session State > Sidebar startup picker` for
102
106
  interactive Latest snapshot / Named snapshot / Empty session selection.