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.
- package/docs/agent-workflow.md +17 -5
- package/docs/cli.md +281 -17
- package/docs/configuration.md +168 -25
- package/docs/hooks.md +476 -64
- package/docs/install.md +6 -0
- package/docs/keybindings.md +22 -39
- package/docs/notify-os-focus-poc.md +84 -6
- package/docs/notify-queue.md +37 -5
- package/docs/session-restore.md +78 -0
- package/docs/settings-ia.md +54 -0
- package/docs/statusbar.md +13 -4
- package/package.json +5 -5
- package/docs/roadmap.md +0 -109
package/docs/keybindings.md
CHANGED
|
@@ -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 >
|
|
12
|
-
run [`projmux setup`](#diagnose-projmux-setup)
|
|
13
|
-
you exactly which sequences reach the process
|
|
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
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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 >
|
|
129
|
-
|
|
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
|
-
|
|
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.
|
package/docs/notify-queue.md
CHANGED
|
@@ -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)** —
|
|
187
|
-
`notify target gone;
|
|
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
|
-
|
|
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.
|
|
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.
|
|
28
|
-
"@projmux/darwin-x64": "0.
|
|
29
|
-
"@projmux/linux-arm64": "0.
|
|
30
|
-
"@projmux/linux-x64": "0.
|
|
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).
|