projmux 0.5.2 → 0.6.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.
@@ -8,10 +8,10 @@ The recommended path when a key does not fire:
8
8
 
9
9
  1. Press the key inside `projmux shell` and see what works on your terminal
10
10
  out of the box ([Quick start](#quick-start-no-setup)).
11
- 2. If something is swallowed, open Settings > Labs > Diagnose keybindings, or
12
- run [`projmux setup`](#diagnose-projmux-setup) outside tmux. Both paths tell
13
- you exactly which sequences reach the process and which the terminal is
14
- eating.
11
+ 2. If something is swallowed, open Settings > Keybindings and select the
12
+ action to capture the key, or run [`projmux setup`](#diagnose-projmux-setup)
13
+ outside tmux. Both paths tell you exactly which sequences reach the process
14
+ and which the terminal is eating.
15
15
  3. For terminals projmux knows how to configure, run
16
16
  [`projmux init [terminal]`](#auto-config-projmux-init) as the fallback:
17
17
  preview first, then add `--apply` to merge the right bindings into your
@@ -20,23 +20,20 @@ The recommended path when a key does not fire:
20
20
  hand), use the [Manual fallback / advanced (CSI-u)](#manual-fallback--advanced-csi-u)
21
21
  section.
22
22
 
23
- To change projmux's tmux-level chords, open Settings > Keybindings. The
24
- in-app editor writes `~/.config/projmux/keymap.toml`, regenerates
25
- `~/.config/projmux/tmux.conf`, and hot-reloads the live tmux config when
26
- Settings is running inside tmux. Terminal fallback mappings are separate:
27
- after changing fallback keys, rerun `projmux init` and restart the terminal
28
- where applicable.
29
-
30
- Settings > Labs > Diagnose keybindings reuses the same probe and terminal
31
- fallback engines from `projmux setup` and `projmux init`. It lists the
32
- keybinding catalog, lets you press one action key at a time from inside the
33
- app, distinguishes plain / CSI-u / unexpected / timeout outcomes, and exposes
34
- preview/apply rows for supported terminal fallbacks. When an unexpected
35
- sequence can be safely read as a tmux plain chord, the Lab shows an explicit
36
- `Save as plain override` row with the suggested chord; it never overwrites
37
- `keymap.toml` from an unexpected sequence unless you select that confirmation
38
- row. The Lab also shows whether the detected terminal can reload config after
39
- fallback apply or needs a restart/manual reload.
23
+ To change a user-facing key, open Settings > Keybindings, select an action,
24
+ then choose `Press new key`. Settings captures one keypress through the same
25
+ TTY probe path used by `projmux setup`, writes safe tmux plain chords to
26
+ `~/.config/projmux/keymap.toml`, regenerates `~/.config/projmux/tmux.conf`,
27
+ and hot-reloads the live tmux config when Settings is running inside tmux.
28
+ CSI-u/User-key captures are reported as terminal fallback delivery and do not
29
+ need a keymap write. Raw sequences that cannot be represented safely as a tmux
30
+ plain chord are not persisted; configure terminal fallback with `projmux init`
31
+ instead.
32
+
33
+ The Settings surface no longer exposes plain/prefix tmux chord fields. Legacy
34
+ `prefix = ...` entries in an existing `keymap.toml` still parse during
35
+ migration, but Settings does not write new prefix entries and generated tmux
36
+ config no longer binds the old action prefix chords.
40
37
 
41
38
  > 한국어 요약: 대부분의 터미널은 `projmux shell` 만으로 아래 키가 바로 동작합니다.
42
39
  > 동작하지 않으면 `projmux setup` 으로 어떤 키가 막혔는지 진단하고,
@@ -71,15 +68,6 @@ These open the projmux popups and the sidebar. No prefix needed.
71
68
  | `Alt-5` | Settings |
72
69
  | `Alt-6` | Project switcher popup |
73
70
 
74
- The same surfaces are also available from the prefix table:
75
-
76
- | Shortcut | Action |
77
- | --- | --- |
78
- | `Prefix F` | Project sidebar |
79
- | `Prefix b` | Existing session popup |
80
- | `Prefix f` | Project switcher popup |
81
- | `Prefix g` | Jump to the current pane's project session |
82
-
83
71
  ### Windows, panes, AI splits
84
72
 
85
73
  | Shortcut | Action |
@@ -88,9 +76,6 @@ The same surfaces are also available from the prefix table:
88
76
  | `Alt-Shift-Left` / `Alt-Shift-Right` | Previous / next window |
89
77
  | `Alt-Left` / `Right` / `Up` / `Down` | Move focus between panes |
90
78
  | `Alt-r` | Rename the current window |
91
- | `Prefix R` | Rename the current window |
92
- | `Prefix r` | Open an AI split to the right |
93
- | `Prefix l` | Open an AI split below |
94
79
 
95
80
  When a pane closes, projmux re-spreads remaining panes so the surviving split
96
81
  does not stretch lopsided.
@@ -125,8 +110,8 @@ which projmux keys actually reach the process. The command auto-detects your
125
110
  terminal, then asks you to press each shortcut in turn and classifies the
126
111
  result:
127
112
 
128
- The same diagnostic is available in-app at Settings > Labs > Diagnose
129
- keybindings. The Lab flow reads the controlling TTY directly, so it can probe a
113
+ The same diagnostic is available in-app at Settings > Keybindings > Diagnostic.
114
+ The flow reads the controlling TTY directly, so it can probe a
130
115
  key while Settings itself is running inside tmux.
131
116
 
132
117
  | Status | Meaning |
@@ -140,10 +125,8 @@ The summary at the end lists the failing keys and a remediation hint
140
125
  tailored to the detected terminal (Ghostty, WezTerm, kitty, iTerm2,
141
126
  Alacritty, Windows Terminal, foot, VS Code, …). When projmux ships an init
142
127
  adapter for the terminal, the summary gives both the dry-run preview and the
143
- exact apply command, e.g. `projmux init ghostty --apply`.
144
- Settings > Labs also includes a concise after-apply hint: Ghostty/WezTerm/kitty
145
- can reload config, Windows Terminal and iTerm2 generally need a restarted tab
146
- or session, and unknown terminals are marked manual.
128
+ exact apply command, e.g. `projmux init ghostty --apply`. Settings >
129
+ Keybindings shows the same delivery categories after a capture.
147
130
 
148
131
  Useful flags:
149
132
 
@@ -72,14 +72,14 @@ a different command worked better.
72
72
  |---|---|---|---|---|
73
73
  | Ghostty | macOS | `osascript -e 'tell application "Ghostty" to activate'` + window-id match | | [ ] |
74
74
  | Ghostty | Linux X11 | `wmctrl -ia <wid>` | | [ ] |
75
- | Ghostty | Linux Wayland | Compositor-specific: gnome `dbus` (`org.gnome.Shell.Eval`), KDE `kdotool`, sway `swaymsg` | | [ ] |
76
- | Kitty | Any | `kitty @ focus-window --match <expr>` | | [ ] |
77
- | WezTerm | Any | `wezterm cli activate-pane --pane-id <id>` | | [ ] |
75
+ | Ghostty | Linux Wayland | Compositor-specific: gnome `dbus` (`org.gnome.Shell.Eval`), KDE `kdotool`, sway `swaymsg` | No — Ubuntu GNOME 50 Wayland with Ghostty 1.3.1 exposes `GHOSTTY_*` detection signals, but Ghostty has no raise/focus CLI action and GNOME Shell `Eval` returns `(false, '')` outside unsafe mode. | [x] |
76
+ | Kitty | Any | `kitty @ focus-window --match <expr>` | Partial — Ubuntu GNOME Wayland default kitty 0.45.0 attach exposes `KITTY_WINDOW_ID`/`KITTY_PID`, but no `KITTY_LISTEN_ON`; external `kitty @ ls` fails without a controlling TTY unless kitty is launched with a remote-control socket. | [x] |
77
+ | WezTerm | Any | `wezterm cli activate-pane --pane-id <id>` | Partial — WezTerm 20240203 crashes on native GNOME 50 Wayland in this host, but the X11 fallback (`WAYLAND_DISPLAY` unset) attaches and `wezterm cli activate-pane --pane-id 0` succeeds. | [x] |
78
78
  | Windows Terminal | Windows | `wt -w <id> focus-tab -t <n>` (same instance only) | | [ ] |
79
79
  | Windows Terminal | WSL → Windows | WSL interop + `wt.exe -w <id> focus-tab` | OK — `wt.exe -w 0 focus-tab -t 0` raises the WT window (verified WSL2 Ubuntu-24.04, WT host) | [x] |
80
80
  | iTerm2 | macOS | AppleScript (`tell application "iTerm" ...`) or Python API | | [ ] |
81
- | Alacritty | Any | No remote IPC. OS-window focus only — fall back to the table below. | | [ ] |
82
- | Foot | Linux | `footclient` is limited. OS-window focus only — fall back to the table below. | | [ ] |
81
+ | Alacritty | Any | No remote IPC. OS-window focus only — fall back to the table below. | No — Alacritty 0.16.1 IPC socket is reachable and can create windows/read config, but exposes no existing-window focus or raise command. | [x] |
82
+ | Foot | Linux | `footclient` is limited. OS-window focus only — fall back to the table below. | No — foot 1.25.0 server/footclient can create a new terminal window, but exposes no existing-window focus or raise command. | [x] |
83
83
  | VS Code embedded | Any | Best-effort via `vscode://` URL handler or `code --command` | | [ ] |
84
84
 
85
85
  ## OS-level window activation matrix
@@ -91,7 +91,7 @@ Independent of the terminal: "raise this app to the foreground." Fill the
91
91
  |---|---|---|---|---|
92
92
  | macOS | `osascript -e 'tell application "<AppName>" to activate'` | Stable; no special permission. | | [ ] |
93
93
  | Linux X11 | `wmctrl -a <window-name>` or `xdotool windowactivate <wid>` | Requires `wmctrl` / `xdotool` installed; standard. | | [ ] |
94
- | Linux Wayland (GNOME) | `gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell --method org.gnome.Shell.Eval ...` | Depends on shell version; recent GNOME locks `Eval` outside dev mode. | | [ ] |
94
+ | Linux Wayland (GNOME) | `gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell --method org.gnome.Shell.Eval ...` | Depends on shell version; recent GNOME locks `Eval` outside dev mode. | No — GNOME Shell 50 returns `(false, '')` for `Eval`; X11 fallbacks also do not see native Wayland Ghostty windows (`wmctrl -lx` empty, `xdotool` cannot resolve title/pid). | [x] |
95
95
  | Linux Wayland (KDE) | `kdotool windowactivate <wid>` | KWin script bridge; requires `kdotool`. | | [ ] |
96
96
  | Linux Wayland (sway) | `swaymsg '[con_id=<id>] focus'` | Works for sway / wlroots compositors. | | [ ] |
97
97
  | Windows | `SetForegroundWindow` via PowerShell + P/Invoke | Foreground-lock policy: if the target hasn't recently been active, the OS will only flash the taskbar entry. | | [ ] |
@@ -155,6 +155,84 @@ Notes from Test 3 (`wt.exe -w 0` bare, no subcommand):
155
155
  (Test 1) to raise without creating a tab. Treat the bare form as
156
156
  reserved for the "open new tab" case only.
157
157
 
158
+ Notes from Test 4 (Ubuntu GNOME Wayland + Ghostty):
159
+
160
+ - Environment: Ubuntu GNOME Wayland (`XDG_SESSION_TYPE=wayland`,
161
+ `XDG_CURRENT_DESKTOP=ubuntu:GNOME`, `WAYLAND_DISPLAY=wayland-0`) with
162
+ Ghostty 1.3.1 (`GHOSTTY_RESOURCES_DIR`, `GHOSTTY_BIN_DIR` set). Inside tmux,
163
+ `TERM_PROGRAM=tmux`, so Ghostty detection must use the `GHOSTTY_*` signals.
164
+ - Ghostty 1.3.1 exposes helper actions such as `+new-window`,
165
+ `+show-config`, and `+list-actions`, but no CLI action that raises an
166
+ existing window, focuses a tab, or activates a split. Treat Ghostty on
167
+ native Linux Wayland as OS-window-focus-only unless a future Ghostty IPC
168
+ surface lands.
169
+ - GNOME Shell `Eval` is not usable as a default adapter path on GNOME 50:
170
+ `gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell
171
+ --method org.gnome.Shell.Eval 'global.display.focus_window ?
172
+ global.display.focus_window.get_title() : ""'` returned `(false, '')`.
173
+ - X11 fallbacks are insufficient for native Wayland windows in this
174
+ configuration: `wmctrl -lx` returned an empty list, and `xdotool
175
+ getactivewindow getwindowname getwindowpid` could only see an Xwayland
176
+ window id with no associated pid/title.
177
+
178
+ Notes from Test 5 (Ubuntu GNOME Wayland + kitty default attach):
179
+
180
+ - Environment: Ubuntu GNOME Wayland with kitty 0.45.0 attached to the projmux
181
+ tmux server (`client_termname=xterm-kitty`).
182
+ - The tmux client process inherited `KITTY_WINDOW_ID=1`, `KITTY_PID=<pid>`,
183
+ `TERM=xterm-kitty`, and `COLORTERM=truecolor`, but not `KITTY_LISTEN_ON`.
184
+ - From a non-interactive external process, `kitty @ ls` failed with
185
+ `open /dev/tty: no such device or address`. That confirms the default
186
+ no-socket launch cannot be driven by an osfocus adapter running outside the
187
+ kitty window.
188
+ - The candidate command remains viable only when kitty is launched with a
189
+ remote-control surface (`--listen-on ...` or `listen_on` plus
190
+ `allow_remote_control`) and the adapter can discover that address.
191
+
192
+ Notes from Test 6 (Ubuntu GNOME Wayland + kitty socket attach):
193
+
194
+ - Launching kitty with `--listen-on unix:/tmp/projmux-kitty.sock --override
195
+ allow_remote_control=yes` passed `KITTY_LISTEN_ON` through to the tmux client
196
+ and allowed an external process to run `kitty @ --to
197
+ unix:/tmp/projmux-kitty.sock ls`.
198
+ - `kitty @ --to unix:/tmp/projmux-kitty.sock focus-window --match id:1`
199
+ exited successfully. The adapter path is viable when the launch/config
200
+ exposes a discoverable remote-control socket.
201
+
202
+ Notes from Test 7 (Ubuntu GNOME Wayland + WezTerm):
203
+
204
+ - Native Wayland launch failed on GNOME 50 with `wl_surface ... Buffer size
205
+ ... must be an integer multiple of the buffer_scale (2)`.
206
+ - X11 fallback launch (`WAYLAND_DISPLAY` unset) attached to tmux successfully
207
+ (`client_termname=xterm-256color`).
208
+ - `wezterm cli list` failed while `WAYLAND_DISPLAY` pointed at the stale
209
+ Wayland socket, but `env -u WAYLAND_DISPLAY wezterm cli list` found the X11
210
+ GUI socket and reported `PANEID 0`.
211
+ - `env -u WAYLAND_DISPLAY wezterm cli activate-pane --pane-id 0` exited
212
+ successfully. A WezTerm adapter on GNOME Wayland must avoid stale Wayland
213
+ socket selection when the GUI is actually running on X11.
214
+
215
+ Notes from Test 8 (Ubuntu GNOME Wayland + Alacritty):
216
+
217
+ - Alacritty 0.16.1 daemon mode created an IPC socket at
218
+ `/run/user/1000/Alacritty-wayland-0-<pid>.sock`; `alacritty msg --socket
219
+ ... create-window -e tmux -L projmux attach -t repos-projmux` attached a new
220
+ tmux client (`client_termname=alacritty`).
221
+ - `alacritty msg --socket ... get-config` succeeded, confirming that the
222
+ socket is externally reachable.
223
+ - `alacritty msg` exposes `create-window`, `config`, and `get-config`; it does
224
+ not expose an existing-window focus/raise command. Treat Alacritty as
225
+ OS-window-focus-only for this roadmap item.
226
+
227
+ Notes from Test 9 (Ubuntu GNOME Wayland + foot):
228
+
229
+ - Starting `foot-server.socket` and then running `footclient -N ... tmux -L
230
+ projmux attach -t repos-projmux` attached a new tmux client
231
+ (`client_termname=foot`).
232
+ - The footclient surface is useful for creating terminals against the server,
233
+ but does not provide an IPC command to focus or raise an existing window.
234
+ Treat foot as OS-window-focus-only.
235
+
158
236
  If multiple signals are present (e.g. tmux inside VS Code's embedded
159
237
  terminal), the adapter chain picks the innermost match the IPC can actually
160
238
  talk to and falls through to the next on detect failure.
@@ -34,6 +34,7 @@ type Notification struct {
34
34
  Text string // capped at 80 runes
35
35
  Severity string // info | warn | critical
36
36
  Source string // ai | k8s | git | external
37
+ Metadata map[string]string // optional producer metadata
37
38
  CreatedAt time.Time
38
39
  ExpiresAt time.Time
39
40
  Target // session, window, pane, socket
@@ -53,6 +54,16 @@ freshness/display field, not a removal condition. `Severity` and
53
54
  returns `ErrInvalidSeverity` / `ErrInvalidSource` which the CLI maps to
54
55
  exit code 2.
55
56
 
57
+ `Metadata` is optional and is omitted when empty. Hook producers use it for
58
+ routing/debug context such as `agent`, `thread_id`, `turn_id`, `cwd`,
59
+ `model`, and `client`; Claude hook rows also carry event-specific keys such as
60
+ `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.
66
+
56
67
  ## CLI surface
57
68
 
58
69
  ### push
@@ -72,7 +83,7 @@ number of seconds. `--json` prints `{id, queued}` for scripting.
72
83
  ### list
73
84
 
74
85
  ```
75
- projmux notify list [--live] [--json] [--limit N] [--ui table|sidebar]
86
+ projmux notify list [--live] [--json] [--limit N] [--ui table|sidebar] [--client <tty>]
76
87
  [--severity ...] [--source ...]
77
88
  ```
78
89
 
@@ -87,7 +98,7 @@ acks the row after focus succeeds. `x` acks the selected row. `Ctrl-X` clears
87
98
  all rows via `notify ack --all`. Rows are intentionally compact: the visible
88
99
  label keeps notification text first, then age, project, window, and pane
89
100
  metadata; hidden queue ids remain action values but the sidebar has no search
90
- input.
101
+ input and intentionally does not expose a separate metadata detail view.
91
102
 
92
103
  `--live` adds a non-mutating explanation view that reads
93
104
  `tmux list-panes -a` and compares the queue with live reply-state panes. It
@@ -170,21 +181,42 @@ Manual `projmux attention toggle` on a pane without an agent option
170
181
  does NOT push — the queue is intentionally AI-driven; reconcile honours
171
182
  the same contract.
172
183
 
184
+ ## Producer (tmux bell fallback)
185
+
186
+ `projmux ai ingest bell --pane <pane_id>` is the opt-in fallback producer
187
+ installed by `projmux ai integrate tmux-bell`. It reads the target pane from
188
+ tmux and writes an info/source-ai row with:
189
+
190
+ - id: `ai:bell:<session>:<pane>`
191
+ - text: `bell · <pane title>` with command/window fallback context
192
+ - metadata: `agent=bell`, `event=bell`, pane/session/window/socket fields
193
+ - freshness TTL: 10 minutes
194
+
195
+ Unlike reply-ready reconcile, bell ingest does not require AI pane metadata.
196
+ It is intentionally available for arbitrary CLIs that only signal attention
197
+ through BEL or OSC 9. Repeated bells from the same pane are suppressed for 5
198
+ seconds before a later bell refreshes the stable queue id.
199
+
173
200
  ## Consumer (status-bar click)
174
201
 
175
202
  `internal/app/statusbar.go::handleNotify`. A click on the notify range
176
203
  or the `prefix s n` chord reads the newest queue entry and dispatches:
177
204
 
178
205
  ```
179
- projmux focus --target <target> --source status-bar --kind segment-click [--socket <s>]
206
+ projmux focus --target <target> --source status-bar --kind segment-click [--socket <s>] [--client <tty>]
180
207
  ```
181
208
 
209
+ The status bar passes the clicked tmux client as `--client` when tmux provides
210
+ it. The notify sidebar does the same for row selection. If that origin client
211
+ is no longer attached, `projmux focus` falls back to the existing target-session
212
+ client selection policy.
213
+
182
214
  Outcomes:
183
215
 
184
216
  - **Focus succeeded** — ack the entry. Focus is the single consume path for
185
217
  routed notification clicks.
186
- - **Focus exited 2 (target unresolved)** — keep the entry pending and toast
187
- `notify target gone; ack to clear`.
218
+ - **Focus exited 2 (target unresolved)** — ack the entry and toast
219
+ `notify target gone; cleared`.
188
220
  - **Other failure** — keep the entry, toast `focus failed: <reason>`
189
221
  so the user can retry without losing the row.
190
222
 
@@ -0,0 +1,78 @@
1
+ # Session Restore
2
+
3
+ Session snapshots store tmux layout metadata and replay recipes for shell,
4
+ startup, and supported agent panes. Manual CLI actions remain available through:
5
+
6
+ ```sh
7
+ projmux session-state status [--session <name>]
8
+ projmux session-state save
9
+ projmux session-state preview [--session <name>]
10
+ projmux session-state restore --dry-run [--session <name>]
11
+ projmux session-state delete [--session <name>]
12
+ ```
13
+
14
+ The primary inspection surface is `Projects > Sessions > State`. It shows a
15
+ read-only overview first: latest snapshot status, named snapshots, window ->
16
+ pane structure, cwd, recipe, and agent resume health. Mutation belongs one
17
+ level deeper in explicit actions; the overview does not immediately save,
18
+ delete, preview, or restore.
19
+
20
+ Project Session State actions are scoped to the project-derived session
21
+ identity, not the currently attached tmux session. `Save latest snapshot`
22
+ captures the live project session when that session exists. `Save named
23
+ snapshot` captures the same live project session into a project-local named
24
+ snapshot and stores cwd values with portable project-root placeholders. Closed
25
+ project sessions disable save actions with the live-session reason. `Preview
26
+ restore` prints the read-only dry-run restore plan for the project snapshot, and
27
+ `Delete snapshot` requires a confirmation picker before removing the project
28
+ snapshot. Destructive restore execution remains outside Settings in this slice.
29
+
30
+ Agent panes in Settings, the statusbar popup, and restore dry-run previews show
31
+ resume metadata health next to the pane recipe. `available` means a resume id is
32
+ present and recent enough for replay, `stale` means the stored id exists but the
33
+ metadata is missing or older than the snapshot policy, and `unavailable`
34
+ means projmux cannot safely resume that agent pane from the snapshot. Confidence
35
+ is derived from the metadata source: direct session ids and hook ingest are high
36
+ confidence, transcript/log fallbacks are medium confidence, and missing or
37
+ unknown sources are low or none.
38
+
39
+ The statusbar Session State popup remains a secondary shortcut backed by the
40
+ same read model and CLI behavior:
41
+
42
+ - `Save snapshot` captures the current tmux session, bypassing the autosave debounce
43
+ just like `projmux session-state save`.
44
+ - `Preview restore` prints the dry-run restore plan and does not execute tmux
45
+ replay commands.
46
+
47
+ Settings > Session State is global settings only: global auto-save, auto-save
48
+ interval, and storage/retention policy. It does not show the current
49
+ snapshot tree. Delete for current-session snapshots and destructive restore
50
+ execution stay deferred until those actions have a dedicated safe policy for
51
+ existing or non-empty live sessions.
52
+
53
+ Named snapshots may currently be backed by legacy project files in
54
+ `<project>/.projmux/layouts/*.toml`. They reuse the same window, pane, cwd, and
55
+ startup recipe concepts as session snapshots, but the user-facing restore model
56
+ is still `Latest snapshot`, `Named snapshot`, or `Empty session`. The legacy
57
+ files are imported read-only by Project open
58
+ when building `Named snapshot` candidates; new primary surfaces should describe
59
+ the restore unit as a snapshot, not as a separate layout or preset feature.
60
+
61
+ Project open from the Alt-1 sidebar defaults to opening a closed project as an
62
+ `Empty session`. `Settings > Labs > Sidebar startup picker` is an opt-in toggle;
63
+ when it is on, closed project open advances inside the sidebar to the native
64
+ `Start project` step. Rows are ordered `Latest snapshot`, named snapshot rows,
65
+ `Empty session`, then `Back`. `Latest snapshot` is the auto-saved snapshot that
66
+ keeps changing as auto-save runs. `Named snapshot` is a fixed, user-named
67
+ snapshot and is not updated by auto-save. Rows include saved-at date/time
68
+ metadata when available. `Back` returns to the project list without creating,
69
+ replaying, or opening a session. After the startup mode is selected, project
70
+ hook/config trust is evaluated if needed; approval continues the selected path
71
+ and deny/cancel aborts before session create, snapshot replay, or startup
72
+ command. Existing sessions switch directly without a startup picker.
73
+
74
+ Default `projmux shell` no longer opens a compatibility startup picker and no
75
+ longer accepts startup selector flags for session-state restore. It always
76
+ follows the normal empty attach path after resolving the target app session name
77
+ and startup directory. Use `Settings > Labs > Sidebar startup picker` for
78
+ interactive Latest snapshot / Named snapshot / Empty session selection.
@@ -0,0 +1,54 @@
1
+ # Settings Information Architecture
2
+
3
+ This branch finishes the current Settings/onboarding roadmap slice with a
4
+ view-first layout:
5
+
6
+ - `Settings > Project Picker > Workdirs` is the list/overview entry. Add/remove
7
+ actions live inside that view.
8
+ - `Settings > Project Picker > Project Root` shows effective and saved values
9
+ first, then the edit actions, then the explanatory hints.
10
+ - `Settings > Keybindings` is the single entry point for keybinding work. The
11
+ page is split into four chips: `Bindings`, `Diagnostic`, `Probe`, and `Init`.
12
+ - `Settings > Notifications` owns notification delivery IA. Desktop notification
13
+ mode, delivery source diagnostics, in-app queue status, and
14
+ `PROJMUX_NOTIFY_HOOK` visibility live together without mixing mutation
15
+ boundaries.
16
+ - `Settings > Notifications > Desktop notifications` owns the desktop
17
+ notification mode. The detail choices are `none`, `notify`, and `raise`.
18
+ - `Settings > Notifications > Delivery sources` shows Codex hooks, Claude, and
19
+ tmux producer diagnostics plus copyable install/remove/dry-run commands.
20
+ Settings copies command text only; it does not install or remove external
21
+ notify wiring. The legacy Codex notify source is intentionally omitted from
22
+ Settings.
23
+ - `Settings > Labs` keeps experimental toggles, but keybindings no longer have a
24
+ visible Labs row. The hidden compatibility action still redirects to the
25
+ unified Keybindings page.
26
+ - `Settings > Labs > Project Hooks` is overview-first. The Labs root opens the
27
+ overview, and the on/off mutation rows live one level deeper.
28
+ - `Settings > AI Settings` is view-first. The root contains `Default split
29
+ mode`; the detail contains the `Claude`, `Codex`, and `Shell` choices.
30
+ - `Settings > Project > Project recipe` is the functional label for
31
+ `.projmux/config.toml`. Search still matches `config.toml` as an alias.
32
+ - `Settings > Project > Project recipe` is view-first. The root contains section
33
+ rows such as `Startup command`, `Kube`, and `Environment`; mutation actions
34
+ such as `Set startup command...`, `Clear startup command`, kube edits, and env
35
+ add/remove rows live inside those details.
36
+
37
+ Hooks remain the reference pattern for this IA:
38
+
39
+ - project-scoped rows stay editable in-app
40
+ - global/system rows are read-only in-app
41
+ - project overrides are created intentionally from the project surface or
42
+ `projmux hook edit <event> --project`
43
+ - `Settings > Project > Hooks` lists hook events only. It does not nest the
44
+ Project recipe row; Project recipe belongs under the Project tab.
45
+ - The Settings hook event list includes `send-noti` for both global and project
46
+ hook views.
47
+
48
+ Shell bootstrap UX is phase-split:
49
+
50
+ - Phase 1 is complete in this branch: `projmux welcome`, the About-screen
51
+ Welcome entry, and `pending_attach_welcome` state make the guide revisit-able.
52
+ - Phase 2 is complete in this branch: the generated projmux shell tmux config
53
+ runs the low-noise `projmux welcome --popup` attach hook, which claims the
54
+ pending marker once and displays the welcome guide after attach.
package/docs/statusbar.md CHANGED
@@ -10,7 +10,7 @@ segment only requires one wiring point.
10
10
  ```
11
11
  row 0 [#S] #{pane_current_path} ⎈ <ctx>/<ns> <git>  %H:%M
12
12
  └────────── native tmux window list (one entry per window) ──────────┘
13
- row 1 #[range=user|notify] <notify HUD pill> #[norange]
13
+ row 1 #[range=user|notify] <notify HUD pill> #[norange] #[range=user|sessionstate]state#[norange]
14
14
  #[range=user|usage] <usage HUD bar> #[norange]
15
15
  ```
16
16
 
@@ -37,7 +37,7 @@ row 1 #[range=user|notify] <notify HUD pill> #[norange]
37
37
  existing branch block background.
38
38
  - Row 1 splits the line with `#[align=left]` (the pending AI notify
39
39
  queue, capped at 80
40
- cells) and `#[align=right]` (usage, capped at 120 cells). `notify` is the
40
+ cells, plus the compact `state` entrypoint) and `#[align=right]` (usage, capped at 120 cells). `notify` is the
41
41
  explicit-ack pending queue; live pane attention badges are a separate
42
42
  state surface. Both
43
43
  segments degrade gracefully when the cell budget is tight; see
@@ -64,8 +64,9 @@ bind-key -n MouseDown1Status if-shell -F "#{==:#{mouse_status_range},window}" \
64
64
  | `kube` | 0 | `projmux tmux popup-toggle sessionizer` | `prefix s k` |
65
65
  | `git` | 0 | `projmux tmux popup-toggle sessionizer` | `prefix s g` |
66
66
  | `settings` | 0 | `projmux tmux popup-toggle --client <tty> ai-split-settings` | mouse only; `prefix s s` remains `session` |
67
+ | `sessionstate` | 1 | show a secondary Session State snapshot status and restore preview popup; primary inspection lives under Projects > Sessions > State | `prefix s r` |
67
68
  | `usage` | 1 | show a native-framed usage HUD popup from cached usage state | `prefix s u` |
68
- | `notify` | 1 | `projmux focus --target <newest> --source status-bar --kind segment-click`, then ack on focus success | `prefix s n` |
69
+ | `notify` | 1 | `projmux focus --target <newest> --source status-bar --kind segment-click [--client <tty>]`, then ack on focus success | `prefix s n` |
69
70
 
70
71
  `notify` reads the pending queue only. For a live pane-state view that is
71
72
  independent of queued reminders, use `projmux attention list`. To explain why
@@ -79,6 +80,11 @@ the cached usage state in-process, keeps the existing `projmux usage` CLI
79
80
  output shape unchanged for external consumers, aligns model/window rows with
80
81
  right-aligned numeric values, dims unavailable values, and colors rows at the
81
82
  same alert thresholds as the popup: amber at 80% and red at 95%.
83
+ `sessionstate` opens a secondary Session State popup for the current tmux
84
+ session. Its action label is `Save snapshot`, which captures the current tmux
85
+ session as the latest snapshot and bypasses the autosave debounce. Primary
86
+ state inspection lives under `Projects > Sessions > State`; global Settings >
87
+ Session State is settings-only.
82
88
 
83
89
  The path popup uses the native picker frame chrome, a one-line title,
84
90
  the full wrapped current path, cheap project/git metadata when available, and
@@ -110,6 +116,7 @@ bind-key -T projmux-status n run-shell '#{q:projmux} statusbar click notify'
110
116
  bind-key -T projmux-status g run-shell '#{q:projmux} statusbar click git'
111
117
  bind-key -T projmux-status k run-shell '#{q:projmux} statusbar click kube'
112
118
  bind-key -T projmux-status p run-shell '#{q:projmux} statusbar click pwd'
119
+ bind-key -T projmux-status r run-shell '#{q:projmux} statusbar click sessionstate'
113
120
  bind-key -T projmux-status s run-shell '#{q:projmux} statusbar click session'
114
121
  ```
115
122
 
@@ -126,7 +133,7 @@ click. Each handler therefore swallows runtime failures and surfaces
126
133
  them as `display-message` toasts:
127
134
 
128
135
  - `notify` click whose focus dispatch exits 2 (target unresolved):
129
- keep the entry pending, toast `notify target gone; ack to clear`.
136
+ ack the entry, toast `notify target gone; cleared`.
130
137
  - Any other focus failure: keep the entry, toast `focus failed:
131
138
  <reason>`.
132
139
  - `session`, `kube`, or `git` popup launch failure: toast
@@ -134,6 +141,8 @@ them as `display-message` toasts:
134
141
  - `settings` popup launch failure: toast `statusbar settings: popup failed`.
135
142
  - `pwd` path popup failure: fall back to a short `display-message`
136
143
  containing the current path.
144
+ - `sessionstate` popup failure: fall back to a compact snapshot status
145
+ `display-message`.
137
146
  - `usage` popup failure: fall back to a compact usage summary
138
147
  `display-message`.
139
148
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projmux",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "tmux project session manager",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/crevissepartners/projmux#readme",
@@ -24,10 +24,10 @@
24
24
  "LICENSE"
25
25
  ],
26
26
  "optionalDependencies": {
27
- "@projmux/darwin-arm64": "0.5.2",
28
- "@projmux/darwin-x64": "0.5.2",
29
- "@projmux/linux-arm64": "0.5.2",
30
- "@projmux/linux-x64": "0.5.2"
27
+ "@projmux/darwin-arm64": "0.6.0",
28
+ "@projmux/darwin-x64": "0.6.0",
29
+ "@projmux/linux-arm64": "0.6.0",
30
+ "@projmux/linux-x64": "0.6.0"
31
31
  },
32
32
  "scripts": {
33
33
  "package:npm": "scripts/package-npm.sh",
package/docs/roadmap.md DELETED
@@ -1,109 +0,0 @@
1
- # Roadmap
2
-
3
- ## Done (0.4.x)
4
-
5
- The 0.4 line filled in the operational surface around the session-management
6
- core that 0.3 had landed.
7
-
8
- ### Setup and install
9
-
10
- - `projmux setup` — TTY raw-mode probe that reports which projmux key
11
- sequences (`Alt-1..5`, `Ctrl-N`, `Ctrl-Shift-{R,L,M}`, `Ctrl-M`,
12
- `Alt-Shift-{Left,Right}`) reach the process and which the terminal
13
- swallows.
14
- - `projmux init [terminal]` — auto-merges projmux's CSI-u + chord
15
- bindings into a terminal config. Adapters: Ghostty (with the
16
- `config` / `config.ghostty` candidate split and symlink guard) and
17
- Windows Terminal (WSL + native).
18
-
19
- ### Diagnostics
20
-
21
- - `projmux doctor` — runtime dependency report. Enforces minimum tmux
22
- 3.4 and checks workflow dependencies such as `git` and `stty`.
23
-
24
- ### Focus
25
-
26
- - `projmux focus` — unified switch-client dispatch. Resolves a target
27
- session against the live tmux inventory, redirects an existing client
28
- if one is attached, otherwise emits a desktop notification. Used by
29
- the status-bar notify click and by the AI reply-ready handler.
30
-
31
- ### Notify queue
32
-
33
- - `projmux notify push|list|ack` — persistent JSON-backed queue at
34
- `<state>/projmux/notify.json` with TTL, severity, source, and target
35
- metadata.
36
- - `projmux notify reconcile` — back-fills the queue from live pane
37
- state by walking `tmux list-panes -a`.
38
- - Producer wired to the attention state machine: a pane transitioning
39
- to `reply` with an AI agent option set pushes an `ai:<session>:<pane>`
40
- entry; the matching `clear` acks it.
41
-
42
- ### Usage tracking
43
-
44
- - `projmux usage` (and `status usage`) — authoritative 5h + weekly
45
- utilisation for both Claude (OAuth `api/oauth/usage` endpoint with
46
- 401 token refresh) and Codex (latest rollout `rate_limits` JSONL).
47
- - Per-adapter throttle (Claude `5m`, default `30s`), 429 backoff
48
- (`30m`–`60m` exponential), `--force` to bypass both. Snapshots
49
- preserved on failure so a 429 does not erase prior rows.
50
-
51
- ### Statusbar and HUD
52
-
53
- - Two-line clickable status bar: row 0 is the existing
54
- session/window/path/git/kube row, row 1 splits notify (left) and
55
- usage (right), with a row-0 settings click fallback.
56
- - `projmux statusbar click` — single dispatcher for both mouse clicks
57
- and the `prefix s {u,n,g,k,p,s}` keyboard chord. Window-list clicks
58
- on tabs short-circuit to native `select-window`.
59
- - `pwd` status click copies the current pane path into the tmux paste
60
- buffer and shows a compact path popup instead of a transient
61
- warning-coloured toast.
62
- - HUD-style notify segment with severity+agent badge, midpoint dot
63
- separators, and an age field.
64
- - HUD-style usage segment with bars, last-sync age indicator (Claude),
65
- and graceful degradation through six tiers as `--max-width` shrinks.
66
-
67
- ## Next (0.5+)
68
-
69
- Carried forward from earlier milestones — items still outstanding when
70
- v0.4 shipped.
71
-
72
- ### Picker UI
73
-
74
- - Picker-domain model separate from row rendering. Done in the 0.5 picker
75
- contract slice.
76
- - Native picker backend for multi-line card rows and title-focused search.
77
- Done in the 0.5 picker contract slice, later promoted to the only picker
78
- backend.
79
- - Port switcher popup/sidebar surfaces after parity tests cover
80
- selection, preview, and key actions.
81
-
82
- ### Picker dismissal
83
-
84
- - Picker-agnostic popup close/toggle handling so AI picker dismissal
85
- does not depend on backend-specific key bindings. Done in the 0.5 picker
86
- contract slice; the native runner consumes shared close action keys.
87
-
88
- ### Keybindings
89
-
90
- - In-app Settings editor for tmux keymap entries. Done in the 0.5 keybinding
91
- M4 slice: Settings > Keybindings lists actions, opens action detail rows,
92
- accepts typed `plain`/`prefix` tmux chord strings, supports disable/reset,
93
- writes `~/.config/projmux/keymap.toml`, regenerates the app tmux config, and
94
- hot-reloads the live tmux config when Settings is running inside tmux.
95
- - M6 progress: Settings > Labs now exposes a Keybinding Lab that reuses the
96
- setup probe and init merge engines for per-action diagnostics, single-key
97
- plain/CSI-u/unknown/timeout outcomes, and supported terminal fallback
98
- preview/apply actions.
99
- - Still outstanding for later milestones: broader terminal-specific fallback
100
- remediation for changed/custom fallback mappings beyond the supported init
101
- adapters.
102
-
103
- ### Docker install and E2E harness
104
-
105
- - Initial Docker-backed Linux smoke suites are available through
106
- `make test-integration`, `make test-install-smoke`, and `make test-e2e`.
107
- They cover install/runtime substrate checks against real `tmux`; host-only
108
- terminal, WSL, macOS, and GUI checks remain separate in
109
- [docs/testing.md](testing.md).