projmux 0.6.6 → 0.7.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.
@@ -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
 
@@ -92,18 +106,56 @@ table `ID AGE SEV SRC TARGET TEXT`. `--severity` / `--source` are
92
106
  repeatable filters. Without `--live`, this command reads only the queue and
93
107
  preserves the stable JSON array used by scripts.
94
108
 
95
- `--ui=sidebar` opens the notify queue as an interactive right-side list when
96
- run inside the tmux popup surface. Selecting a row focuses the selected target
97
- pane and acks the row after focus succeeds. The surface actions
98
- `NotifySidebar:Ack`, `NotifySidebar:ClearNonCritical`, and
99
- `NotifySidebar:ClearAll` are internal picker actions; direct launch aliases
100
- are edited in Settings, while internal picker aliases are adjusted in
101
- `keymap.toml` when needed. Runtime footer key guides read the merged keymap
102
- 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
104
- notification text first, then age, project, window, and pane metadata; hidden
105
- queue ids remain action values but the sidebar has no search input and
106
- intentionally does not expose a separate metadata detail view.
109
+ `--ui=sidebar` opens the notify queue as an interactive right-side pane/session
110
+ inbox when run inside the tmux popup surface. The queue source of truth remains
111
+ flat; the sidebar builds a read-only grouped view for display. The first screen
112
+ shows collapsed group rows keyed by pane when available, then window, then
113
+ session/external fallback. Each group row is a fixed three-line card: line 1
114
+ keeps project/session plus agent/provider with newest age, line 2 keeps
115
+ topic/pane-title/task context plus severity/live-state aggregate
116
+ metadata, and line 3 keeps the latest notification preview. Collapsed group
117
+ cards do not promote window/pane ids as primary information. A `+N` badge is
118
+ shown only when the group can unfold, and `N` is the number of child
119
+ notification rows that will appear; one-notification group headers omit both
120
+ the count badge and strong fold marker. Right/Left show and hide child rows for
121
+ foldable groups inside the native sidebar only; this fold state is
122
+ session-local and is not persisted. Right on a childless group refreshes
123
+ without adding rows. Enter on a group row, whether folded or expanded, focuses
124
+ the group's representative pane and acknowledges every visible notification in
125
+ that group only after focus succeeds. Inactive means an `ai:` queue entry points
126
+ to a pane that no longer matches live reply+agent state; it is not a time-age
127
+ TTL state, and Enter still focuses the target when it is routable. If the
128
+ representative target is gone/unroutable, Enter treats the selected pane inbox
129
+ as explicit cleanup and acknowledges/prunes the visible group without focusing,
130
+ including critical notifications. If a live- or inactive-looking representative target
131
+ disappears during focus, Enter uses the same gone-group cleanup policy. Other
132
+ focus failures keep the group pending, show a clear message, and refresh/prune
133
+ the list. Expanded child notification rows are compact event rows with age,
134
+ message preview, and severity/state while keeping the existing focus/ack-one
135
+ behavior. The surface actions
136
+ `NotifySidebar:Ack`, `NotifySidebar:AckGroup`,
137
+ `NotifySidebar:ClearNonCritical`, and `NotifySidebar:ClearAll` are internal
138
+ picker actions; direct launch aliases are edited in Settings, while internal
139
+ picker aliases are adjusted in `keymap.toml` when needed.
140
+ `NotifySidebar:AckGroup` defaults to uppercase `A` and explicitly
141
+ acknowledges every visible notification in the selected group, including
142
+ critical notifications. Runtime footer key guides read the merged keymap and
143
+ show the default alias when present, otherwise the first configured alias, so
144
+ custom aliases do not make the UI stale.
145
+ `NotifySidebar:Ack`, `NotifySidebar:AckGroup`, and
146
+ `NotifySidebar:ClearNonCritical` refresh rows, live state, and selection inside
147
+ the same native picker session; `NotifySidebar:ClearAll` still closes the
148
+ popup and prints a summary. Rows are intentionally compact: hidden queue ids
149
+ remain action values but the sidebar has no search input and intentionally does
150
+ not expose a separate metadata detail view.
151
+
152
+ When a new pending notification is successfully pushed by any app producer
153
+ (`notify push`, reply-ready, reconcile backfill, or bell fallback), open native
154
+ notify sidebars receive a best-effort queue-write event and rerun the same
155
+ `DeferredUpdate` row/live-state refresh path used by `a` ack and `x`
156
+ non-critical clear. Event delivery errors are ignored after the queue write:
157
+ the push still succeeds, and reopening the sidebar remains the recovery path
158
+ for seeing the latest queue.
107
159
 
108
160
  `--live` adds a non-mutating explanation view that reads
109
161
  `tmux list-panes -a` and compares the queue with live reply-state panes. It
@@ -117,11 +169,20 @@ output becomes `{queue, live, rows, errors}`. Typical states:
117
169
  queue entry.
118
170
  - `live-ai-reply-missing-queue` — a live AI reply pane lacks the derived
119
171
  queue entry; run `projmux notify reconcile` to back-fill it.
120
- - `queue-stale` — an `ai:` queue entry exists, but the live pane no longer
121
- matches reply+agent state; it remains pending until explicit ack. Surfaced
122
- in the sidebar/statusbar as `STALE` / `STL`.
123
- - `queue-gone` — a queue entry has no routable target (empty session); it
124
- can only be ack'd. Surfaced as `GONE` / `GON`.
172
+ - `queue-stale` — preserved machine-readable state for an inactive target:
173
+ an `ai:` queue entry whose pane still EXISTS in the live tmux pane inventory,
174
+ but no longer matches reply+agent state. It is not TTL/time age. Surfaced in
175
+ the sidebar/statusbar as `INACTIVE` / `INA`; Enter still focuses and acks if
176
+ the target is routable.
177
+ - `queue-gone` — the queue entry's target is gone. This is now determined two
178
+ ways: (a) the entry has no routable target (empty session), or (b) the entry
179
+ carries a pane target whose pane id is absent from the real tmux live pane
180
+ inventory (`tmux list-panes -a`). Surfaced as `GONE` / `GON`, and Enter/ack
181
+ cleans it up without focusing. The inventory check is best-effort: when the
182
+ pane inventory cannot be read (tmux error, or an empty/unrecognized reply),
183
+ membership-based GONE is skipped so a missing tmux server never falsely
184
+ dims/gones every row, and only pane-target rows are eligible (window/session-
185
+ only rows keep the empty-session check only).
125
186
  - `queue-only` — a non-AI/external queue entry is pending and has no live AI
126
187
  reply-pane requirement.
127
188
 
@@ -151,7 +212,11 @@ path), then:
151
212
  - pushes/refreshes one `ai:<session>:<pane>` entry for every pane
152
213
  whose attention state is `reply` AND whose agent option is non-empty;
153
214
  - reports every existing queue entry whose id starts with `ai:` and whose
154
- pane no longer matches that condition as stale, without acking it.
215
+ pane no longer matches that condition as inactive/`queue-stale`, without
216
+ acking it.
217
+
218
+ Successful backfill pushes publish the same best-effort open-sidebar refresh
219
+ event as other pending queue additions.
155
220
 
156
221
  Soft-fails when tmux is not running (returns a populated `errors`
157
222
  field rather than a non-zero exit) so the post-install hook does not
@@ -180,7 +245,10 @@ an entry with:
180
245
  When the pane leaves the reply state (manual `attention clear`,
181
246
  `status set idle`, or a window close), `AckReplyReady` intentionally does not
182
247
  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.
248
+ swallowed so the live tmux UI never blocks on disk IO. After a successful
249
+ queue write and same-pane non-critical compaction, the producer publishes the
250
+ same best-effort notify-sidebar queue-write refresh event used by
251
+ `projmux notify push`.
184
252
 
185
253
  Manual `projmux attention toggle` on a pane without an agent option
186
254
  does NOT push — the queue is intentionally AI-driven; reconcile honours
@@ -200,7 +268,9 @@ tmux and writes an info/source-ai row with:
200
268
  Unlike reply-ready reconcile, bell ingest does not require AI pane metadata.
201
269
  It is intentionally available for arbitrary CLIs that only signal attention
202
270
  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.
271
+ seconds before a later bell refreshes the stable queue id. Successful
272
+ non-deduped bell queue writes publish the same best-effort open-sidebar
273
+ refresh event as other pending queue additions.
204
274
 
205
275
  ## Consumer (status-bar click)
206
276
 
@@ -230,8 +300,11 @@ Outcomes:
230
300
 
231
301
  The same consume policy is shared by notify-sidebar Enter and OS
232
302
  click-to-focus Toast callbacks after a real tmux focus dispatch succeeds.
303
+ OS Toast click-to-focus is only registered when Desktop notification mode is
304
+ `raise`; the in-app sidebar/statusbar consume path works in every mode.
233
305
  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
306
+ not ack the notify queue; their response-complete badge consume is limited to
307
+ live tmux pane badge/state options. Non-critical AI completion producers also compact
235
308
  older same-pane non-critical AI rows after replacing/pushing their latest row,
236
309
  so reply-ready/stop/bell-style completion rows stay latest-state centered
237
310
  without changing the queue schema or TTL contract.
@@ -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.
@@ -24,16 +24,22 @@ view-first layout:
24
24
  commands, `Pane navigation`, `Window navigation`, and `Rename` groups or
25
25
  equivalent searchable rows.
26
26
  - `Settings > Keybindings > Action` keeps the user-facing edit path small:
27
- action, current keybinding/aliases, `Add alias`, and reset. It does not offer
28
- replace-primary, disable-default, typed-fallback, terminal mapping preview, or
29
- terminal mapping apply rows.
27
+ action label, state, a flat Keys list, Options, and a collapsed
28
+ Troubleshooting row. Keys shows only currently active/effective keys plus
29
+ `+ Add key`. Pressing a key row opens key detail, where Remove key and Test
30
+ key live. Options offers Unbind and Reset to default/Use default when
31
+ state-appropriate. Add key opens the default Press a key flow with Cancel and
32
+ Advanced...; typed key-name entry and raw diagnostics live under Advanced. It
33
+ does not expose Default key, Apply State, Delivery, Advanced Delivery,
34
+ key-role replacement, terminal mapping preview, or terminal mapping apply
35
+ rows as always-visible sections.
30
36
  - Terminal delivery remediation lives outside Settings primary flow. The
31
37
  supported order is `projmux shell` first, then `projmux setup`, then
32
38
  `projmux init` for supported terminal adapters.
33
39
  - Rows that cannot safely be edited still stay visible. Mark diagnostic-only
34
40
  rows with the delivery path and reason instead of hiding them or turning them
35
- into unsupported editable aliases. Transport-dependent rows stay visible with
36
- a separate default transport key and additive plain-alias entry; replacing or
41
+ into unsupported editable keys. Transport-dependent rows stay visible with
42
+ their default transport key and additive custom-key entry; replacing or
37
43
  disabling the transport default is not exposed.
38
44
  - `Alt-1..5` are the only guaranteed zero-config launch defaults. `UserN` and
39
45
  `CSI-u` are legacy/removal/unsupported targets, not supported fallback
@@ -62,6 +68,9 @@ view-first layout:
62
68
  runtime action values and writes only
63
69
  `${XDG_CONFIG_HOME:-$HOME/.config}/projmux/ai-hook-actions.json`. It does not
64
70
  edit catalog `install` values or run agent install/remove commands.
71
+ - `Settings > Session State > Sidebar startup picker` controls the Alt-1
72
+ project-open startup selector. The saved file remains
73
+ `${XDG_CONFIG_HOME:-$HOME/.config}/projmux/sidebar-startup-picker`.
65
74
  - `Settings > Labs` keeps experimental toggles, but keybindings no longer have a
66
75
  visible Labs row. The hidden compatibility action redirects to the
67
76
  `Settings > Keybindings` action list, not to a diagnostic default.
@@ -107,5 +116,6 @@ Shell bootstrap UX is phase-split:
107
116
  - `projmux welcome` remains the stdout revisit command.
108
117
  - `Settings > About > Welcome` opens a visible native viewer independent of
109
118
  shell skip state.
110
- - Shell `skip_version` state applies only to the automatic `projmux shell`
111
- prompt; it does not hide manual revisit surfaces.
119
+ - Legacy shell `skip_version` state remains readable for compatibility but no
120
+ longer suppresses the automatic `projmux shell` prompt; release skips live in
121
+ `update-skip.json` and do not hide manual revisit surfaces.
package/docs/statusbar.md CHANGED
@@ -46,6 +46,9 @@ row 1 [#S] #{pane_current_path} ⎈ <ctx>/<ns> <git>  %H:%M
46
46
  notify queue segment and from notify queue severity or desktop notification
47
47
  urgency. For example, an approval request may remain a critical queued
48
48
  notification while its live status badge renders action-required amber-orange.
49
+ Pane focus hooks and `projmux attention clear` consume only the
50
+ response-complete live badge, including stale `@projmux_ai_state=waiting`
51
+ fallback state; action-required and in-progress live badges remain visible.
49
52
  Window-list badges and app pane-border badges use the same semantic priority,
50
53
  with display style controlled by Settings > Appearance > AI badge style and persisted in
51
54
  `~/.config/projmux/ai-badge-style`. The default is `dot`; `emoji` renders
@@ -138,13 +141,35 @@ last collect timestamp when present, falls back to the cache file mtime when
138
141
  needed, and keeps stale sync metadata muted instead of escalating it to a
139
142
  warning color.
140
143
  The notification HUD detail surface opens the right-side notification popup
141
- through the notify sidebar action, with newest-first rows and an
142
- attention-tinted title. When notification icon decoration is `symbol` or
143
- `emoji`, the bell appears before the title text.
144
- Selecting a row still focuses and acknowledges that notification. Internal
145
- notify commands use `NotifySidebar:*` IDs in `keymap.toml`; runtime footers
146
- render key guides from the merged keymap and prefer the default alias when it
147
- is still configured.
144
+ through the notify sidebar action, showing the grouped pane/session inbox with
145
+ collapsed group rows and the same attention-tinted title. When notification
146
+ icon decoration is `symbol` or `emoji`, the bell appears before the title text.
147
+ Foldable group rows show `+N`, where `N` is the number of child notification
148
+ rows shown after Right. One-notification group headers omit the count and do
149
+ not render as strongly foldable. Right/Left show and hide child rows locally
150
+ inside the native sidebar; Right on a childless group refreshes without adding
151
+ rows. Enter on a group row, whether folded or expanded, focuses the group's
152
+ representative pane and acknowledges every visible notification in that group
153
+ only after focus succeeds. Inactive means an `ai:` queue entry no longer
154
+ matches live reply+agent state (its pane still EXISTS in tmux), not that the
155
+ row is old; if the target remains routable, Enter and statusbar clicks still
156
+ focus and then ack. Gone means the target is unroutable (empty session) or the
157
+ row's pane id is absent from the real tmux live pane inventory
158
+ (`tmux list-panes -a`). The inventory check is best-effort: an unreadable or
159
+ empty/unrecognized tmux reply is treated as "unavailable", so a missing tmux
160
+ server never falsely gones routable rows. If the
161
+ representative target is gone/unroutable, Enter cleans up the selected group
162
+ without focusing and acknowledges every visible notification in that group,
163
+ including critical notifications. A
164
+ target-gone focus race follows the same cleanup policy; other focus failures
165
+ keep the group pending and show a clear message before refresh/prune. Enter on
166
+ a child notification preserves the existing focus and ack-one behavior.
167
+ `NotifySidebar:AckGroup` remains the explicit group ack action and acknowledges
168
+ every visible notification in the selected group, including critical
169
+ notifications.
170
+ Internal notify commands use `NotifySidebar:*` IDs in `keymap.toml`; runtime
171
+ footers render key guides from the merged keymap and prefer the default alias
172
+ when it is still configured.
148
173
 
149
174
  Empty `#{mouse_status_range}` (a click on whitespace) falls through to
150
175
  `select-window -t @<mouse_window>` when `--mouse-window` is non-empty,
@@ -177,6 +202,8 @@ them as `display-message` toasts:
177
202
 
178
203
  - `notify` click whose focus dispatch exits 2 (target unresolved):
179
204
  ack the entry, toast `notify target gone; cleared`.
205
+ - `notify` click whose AI target is inactive because it no longer matches live
206
+ reply+agent state: focus and ack when the target is still routable.
180
207
  - Any other focus failure: keep the entry, toast `focus failed:
181
208
  <reason>`.
182
209
  - `session`, `kube`, or `git` popup launch failure: toast
package/docs/testing.md CHANGED
@@ -21,6 +21,10 @@ and humans run the same entrypoints.
21
21
  `test/e2e/linux-smoke.sh`. It validates a minimal real-tmux workflow:
22
22
  sessions, panes, config sourcing, reply-state notify reconciliation, focus
23
23
  notify fallback, and status notify rendering.
24
+ - `make deadcode` runs `go tool deadcode` (pinned via the go.mod tool
25
+ directive) over the module and reports unreachable functions, filtering out
26
+ the intentional/MUST-KEEP baseline in `.deadcode-allowlist.txt`; it fails
27
+ only on NEW dead code, and `make fix` runs it after `go fix`.
24
28
 
25
29
  ## Docker-Covered Checks
26
30
 
@@ -90,7 +94,7 @@ Observe:
90
94
 
91
95
  - `Alt-1` opens the project sidebar.
92
96
  - `Alt-2` opens the notification sidebar.
93
- - `Alt-3` opens the existing-session picker.
97
+ - `Alt-3` opens Recent Windows.
94
98
  - `Alt-4` opens the AI split picker.
95
99
  - `Alt-5` opens Settings.
96
100
  - Pressing the same launch key again closes the popup instead of typing escape
@@ -117,12 +121,15 @@ Observe:
117
121
  `"reason":"no-attached-client"`.
118
122
  - Windows shows a short projmux toast with `session ready:
119
123
  projmux-host-smoke`.
124
+ - In `notify` mode, the toast has no click-to-focus action and should not
125
+ auto-raise the host terminal.
120
126
  - No visible PowerShell or console window remains open after the toast.
121
127
 
122
128
  If the PR changes click-to-focus behavior, repeat with
123
129
  `PROJMUX_DESKTOP_NOTIFY_MODE=raise`, click the toast, and record whether the
124
- host terminal returns to the target. Otherwise leave click callbacks marked as
125
- manual/not run.
130
+ host terminal returns to the target. `raise` should also be the only mode where
131
+ `projmux focus` performs post-switch osfocus. Otherwise leave click callbacks
132
+ marked as manual/not run.
126
133
 
127
134
  ### macOS GUI Notification
128
135
 
package/docs/upgrading.md CHANGED
@@ -3,13 +3,14 @@
3
3
  projmux has two update surfaces:
4
4
 
5
5
  - `projmux shell` reads the cached release status before opening the app. When
6
- the cache is fresh and a newer release is available, startup shows a picker
7
- with Update Now, Later, and Skip This Version actions.
6
+ the cache is missing or stale, startup attempts a short best-effort refresh
7
+ and continues if it fails. When a newer release is available, the shell
8
+ welcome offers Continue, Upgrade, and Skip until next actions.
8
9
  - Settings > About > Update shows the current version, detected installer,
9
10
  cached latest version, Check Updates, and Update Now actions.
10
11
 
11
- Startup never reaches the network. Refresh the cache explicitly when you want
12
- projmux to check GitHub Releases:
12
+ Refresh the cache explicitly when you want a full foreground GitHub Releases
13
+ check:
13
14
 
14
15
  ```sh
15
16
  projmux update check
@@ -24,6 +25,11 @@ projmux update apply
24
25
  Use `--dry-run` to see the planned action and `--no-apply` to skip reloading
25
26
  the live tmux config after the binary changes.
26
27
 
28
+ Shell Upgrade invokes only `projmux update apply`. Shell Skip until next stores
29
+ the current latest release tag in `update-skip.json`; the prompt appears again
30
+ when the cached latest tag changes. For `source` and unknown installer sources,
31
+ Upgrade prints guidance and continues shell entry without applying anything.
32
+
27
33
  ## npm Installs
28
34
 
29
35
  The recommended install path is:
@@ -1,10 +1,26 @@
1
1
  # Usage tracking
2
2
 
3
3
  `projmux usage` and `projmux status usage` report authoritative 5-hour
4
- and weekly utilisation for both Claude Code and the Codex CLI. Both
5
- adapters read from the upstream's own view of the account so the
4
+ and weekly utilisation for enabled AI agents. `--model all`, the tmux
5
+ HUD, and the statusbar usage popup use Settings > AI Settings > Enabled
6
+ agents as the source of truth, so disabled Claude/Codex providers are
7
+ not refreshed or rendered on ambient/all surfaces. Explicit read-only
8
+ requests such as `projmux usage --model claude` or `--model codex`
9
+ still collect and render that provider even when it is disabled.
10
+
11
+ Both adapters read from the upstream's own view of the account so the
6
12
  percentages match what `claude /usage` and `codex` show natively.
7
13
 
14
+ Antigravity is intentionally not registered as a 5-hour/weekly quota adapter.
15
+ The only stable Phase 0b usage signal is statusline `context_window`, which is
16
+ conversation context-window usage, not account quota usage. `projmux usage
17
+ --model antigravity` and ambient all-model table output therefore render an
18
+ explicit unsupported note when Antigravity is enabled. The statusbar usage
19
+ popup shows an `Antigravity ctx ... unsupported` row, while the compact tmux
20
+ status segment stays silent unless Claude/Codex quota rows exist. Projmux does
21
+ not infer quota, reset timestamps, or account limits from screen scraping,
22
+ tokens, history, OAuth/cache files, or binary strings.
23
+
8
24
  ## Adapters
9
25
 
10
26
  ### Claude (`internal/core/usage/adapters/claude`)
@@ -79,12 +95,13 @@ across machines (Dropbox, iCloud Drive).
79
95
  ### `projmux usage`
80
96
 
81
97
  ```
82
- projmux usage [--model codex|claude|all] [--window 5h|weekly|all]
98
+ projmux usage [--model codex|claude|antigravity|all] [--window 5h|weekly|all]
83
99
  [--json] [--force|-f]
84
100
  ```
85
101
 
86
- Calls `Manager.Collect` (or `ForceCollect` with `--force`), filters by
87
- model/window, and renders the tab-aligned table:
102
+ For `--model all`, calls `Manager.Collect` (or `ForceCollect` with
103
+ `--force`) only for providers enabled in Settings > AI Settings >
104
+ Enabled agents, filters by window, and renders the tab-aligned table:
88
105
 
89
106
  ```
90
107
  MODEL WINDOW PCT RESETS_AT STALE
@@ -103,16 +120,27 @@ instead. A backoff note is appended to the human table:
103
120
  claude is in backoff, try again in 30m (use --force to bypass)
104
121
  ```
105
122
 
123
+ When no AI agents are enabled, all-model table output contains no
124
+ provider rows and prints a short Settings hint. `--json` returns an
125
+ empty array. Explicit `--model claude` and `--model codex` bypass the
126
+ enabled-agent filter for read-only inspection and collect/render only
127
+ the requested adapter. Explicit `--model antigravity` renders the same
128
+ unsupported/context-window-only note even when Antigravity is disabled, because
129
+ there is no supported Antigravity quota adapter to collect.
130
+
106
131
  ### `projmux status usage`
107
132
 
108
133
  ```
109
134
  projmux status usage [--max-width N] [--force|-f]
110
135
  ```
111
136
 
112
- The HUD bar wired to the tmux status interval. Triggers an
113
- opportunistic refresh: `MaybeCollect(throttle=30s)` (subject to
114
- per-adapter throttle and active backoff). Errors are swallowed unless
115
- `PROJMUX_USAGE_DEBUG` is set. Then loads the cache and renders.
137
+ The HUD bar wired to the tmux status interval. It scopes the registry to
138
+ enabled AI agents, then triggers an opportunistic refresh:
139
+ `MaybeCollect(throttle=30s)` (subject to per-adapter throttle and active
140
+ backoff). Disabled providers are not refreshed just to be hidden. Errors
141
+ are swallowed unless `PROJMUX_USAGE_DEBUG` is set. Then it loads the
142
+ cache, filters to the same enabled-agent scope, and renders. If no AI
143
+ agents are enabled, the status segment emits nothing.
116
144
 
117
145
  Output degrades through six tiers as `--max-width` shrinks:
118
146
 
@@ -132,10 +160,11 @@ because the rollout file is always near-current (no throttle gap to report).
132
160
  ### Statusbar usage popup
133
161
 
134
162
  `projmux statusbar click usage` renders a native-framed popup from the same
135
- cache instead of shelling out to `projmux usage`. This keeps `projmux usage
136
- --json` backwards-compatible for CLI consumers while giving the tmux click path
137
- a structured table with aligned rows, right-aligned numeric values, dim
138
- unavailable cells, amber usage at 80%, and red usage at 95%.
163
+ cache instead of shelling out to `projmux usage`. The popup filters rows and
164
+ sync metadata to the same enabled-agent scope as the ambient HUD. This keeps
165
+ `projmux usage --json` backwards-compatible for CLI consumers while giving the
166
+ tmux click path a structured table with aligned rows, right-aligned numeric
167
+ values, dim unavailable cells, amber usage at 80%, and red usage at 95%.
139
168
 
140
169
  The popup sync line uses the maximum authoritative `LastCollect` timestamp from
141
170
  the cache. If that field is unavailable, it falls back to the snapshots file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projmux",
3
- "version": "0.6.6",
3
+ "version": "0.7.0",
4
4
  "description": "tmux project session manager",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/crevissepartners/projmux#readme",
@@ -28,9 +28,9 @@
28
28
  "package:npm:pack": "scripts/package-npm.sh --pack"
29
29
  },
30
30
  "optionalDependencies": {
31
- "@projmux/linux-x64": "0.6.6",
32
- "@projmux/linux-arm64": "0.6.6",
33
- "@projmux/darwin-x64": "0.6.6",
34
- "@projmux/darwin-arm64": "0.6.6"
31
+ "@projmux/linux-x64": "0.7.0",
32
+ "@projmux/linux-arm64": "0.7.0",
33
+ "@projmux/darwin-x64": "0.7.0",
34
+ "@projmux/darwin-arm64": "0.7.0"
35
35
  }
36
36
  }
@@ -1,97 +0,0 @@
1
- # README Hero GIF Recording
2
-
3
- This recipe records the README hero GIF:
4
-
5
- - `docs/assets/projmux-ai-attention.gif`
6
-
7
- The maintained recorder is kept in the local dotfiles checkout at:
8
-
9
- ```sh
10
- /home/es5h/dotfiles/bin/projmux/projmux-record-readme-gifs.py
11
- ```
12
-
13
- ## Prerequisites
14
-
15
- Install or verify these local tools before recording:
16
-
17
- - `python3`
18
- - `git`
19
- - `tmux`
20
- - `ffmpeg` and `ffprobe`
21
- - `Xvfb`
22
- - `openbox`
23
- - `ghostty`
24
- - `xdotool`
25
- - `xwininfo`
26
- - `script` from util-linux
27
- - authenticated `codex`
28
-
29
- Build the local projmux binary first:
30
-
31
- ```sh
32
- make build
33
- ```
34
-
35
- The script expects `.bin/projmux` by default. Override paths only when needed:
36
-
37
- ```sh
38
- PROJMUX_RECORD_REPO=/path/to/projmux \
39
- PROJMUX_RECORD_BIN=/path/to/projmux \
40
- PROJMUX_RECORD_DISPLAY=117 \
41
- PROJMUX_RECORD_SCREEN=2560x1440x24 \
42
- PROJMUX_RECORD_KEEP_TMP=1 \
43
- python3 /home/es5h/dotfiles/bin/projmux/projmux-record-readme-gifs.py
44
- ```
45
-
46
- For the normal checkout, run:
47
-
48
- ```sh
49
- python3 /home/es5h/dotfiles/bin/projmux/projmux-record-readme-gifs.py
50
- ```
51
-
52
- ## Scenario Contract
53
-
54
- The recording uses a private demo home, XDG config/state dirs, demo git
55
- projects, and an isolated `CODEX_HOME`. It copies the local Codex auth/config
56
- into that isolated home, trusts the demo projects/hooks, and seeds usage cache
57
- data so the tmux status line shows the Codex HUD during the capture.
58
-
59
- The scenario records the AI attention flow:
60
-
61
- 1. Start in `mobile-client` with no pending notification.
62
- 2. Open the AI picker and launch a real Codex pane.
63
- 3. Ask Codex to do a short task.
64
- 4. Use the projmux sessionizer sidebar to move to `atlas-api`.
65
- 5. Keep working in zsh while Codex finishes in the previous project.
66
- 6. When the Codex completion notification exists, open the notification sidebar.
67
- 7. Select the notification and return focus to the original Codex pane.
68
-
69
- ## Visual Guardrails
70
-
71
- - Keep the native picker UI native. The script runs picker commands through
72
- `script(1)` with a fixed PTY size so fzf/terminal UI rendering is captured
73
- instead of degraded line-mode output.
74
- - Keep the terminal in zsh with the demo prompt so the project and git branch
75
- are visible.
76
- - Keep the Codex usage HUD visible in the tmux status line.
77
- - Use the sessionizer sidebar for project movement in 4a.
78
- - Capture the Ghostty X11 window geometry with `xwininfo` and feed that exact
79
- rectangle to ffmpeg. This prevents the GIF from drifting away from `(0, 0)`.
80
-
81
- ## Verification
82
-
83
- After recording, inspect the resulting streams:
84
-
85
- ```sh
86
- ffprobe -v error -select_streams v:0 \
87
- -show_entries stream=width,height,nb_frames,duration \
88
- -of default=noprint_wrappers=1 \
89
- docs/assets/projmux-ai-attention.gif
90
- ```
91
-
92
- Expected final shape is roughly 1110px wide and about 16-18 seconds, with the
93
- native picker, zsh prompt, Codex pane, notification sidebar, and usage HUD
94
- visible in the relevant frames.
95
-
96
- Set `PROJMUX_RECORD_KEEP_TMP=1` when you need to inspect intermediate MP4s,
97
- Ghostty logs, or palette files under `/tmp/projmux-readme-record-*`.