projmux 0.4.4 → 0.4.6

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/cli.md ADDED
@@ -0,0 +1,429 @@
1
+ # CLI Reference
2
+
3
+ Every subcommand exposed by the `projmux` binary as of v0.4.0. Run
4
+ `projmux help` for the live top-level list, or `projmux <cmd> --help` /
5
+ `projmux <cmd> help` for the per-command usage string.
6
+
7
+ Exit codes:
8
+
9
+ - `0` — success.
10
+ - `1` — runtime failure.
11
+ - `2` — usage error (unknown flag, bad enum, missing required flag) or a
12
+ deterministic semantic exit (e.g. `focus` cannot resolve the target).
13
+
14
+ ## Top-level
15
+
16
+ ```
17
+ projmux <command> [args...]
18
+ ```
19
+
20
+ | Command | Purpose |
21
+ | --- | --- |
22
+ | `ai` | Manage tmux AI splits (Codex/Claude) and per-pane status. |
23
+ | `attention` | View and manage live tmux pane attention state. |
24
+ | `attach` | Open tmux lifecycle entry helpers. |
25
+ | `current` | Resolve the active tmux pane path. |
26
+ | `doctor` | Diagnose runtime dependencies. |
27
+ | `focus` | Switch the active client to a session/window/pane target. |
28
+ | `init` | Apply supported terminal keybinding fallbacks. |
29
+ | `kill` | Terminate tagged tmux sessions. |
30
+ | `notify` | Manage the pending AI notify queue (push/list/ack/reconcile). |
31
+ | `pin` | Manage pinned project directories. |
32
+ | `preview` | Manage persisted tmux preview selection. |
33
+ | `prune` | Trim stale tmux lifecycle state. |
34
+ | `sessions` | Pick and open an existing tmux session. |
35
+ | `session-popup` | Read tmux popup preview state. |
36
+ | `settings` | Configure projmux. |
37
+ | `setup` | Probe terminal key delivery for projmux bindings. |
38
+ | `shell` | Open the isolated projmux tmux app. |
39
+ | `status` | Render tmux status bar segments. |
40
+ | `statusbar` | Dispatch projmux status bar clicks and shortcuts. |
41
+ | `switch` | Pick and open a project tmux session. |
42
+ | `tag` | Manage tagged tmux sessions. |
43
+ | `tmux` | Open tmux popup entry helpers / install generated config. |
44
+ | `update` | Check installer-aware GitHub release update status. |
45
+ | `upgrade` | Self-update via `go install`. |
46
+ | `usage` | Report AI token usage across 5h and weekly windows. |
47
+ | `version` | Print the current version. |
48
+
49
+ ## switch
50
+
51
+ ```
52
+ projmux switch [path]
53
+ projmux switch toggle-tag | toggle-pin | kill | settings | preview
54
+ projmux switch cycle-pane | cycle-window | sidebar-focus
55
+ ```
56
+
57
+ Project picker. With no positional argument, opens the fzf-driven popup or
58
+ sidebar (depending on entry helper). With a path, jumps directly. The
59
+ sub-verbs are entry hooks invoked by tmux keybindings (e.g.
60
+ `sidebar-focus` is wired to the sidebar's focus binding so navigation keeps
61
+ the active session in sync).
62
+
63
+ `PROJMUX_PICKER_BACKEND=native` opts the switcher into the experimental native
64
+ picker backend. fzf remains the default and the only backend with full preview
65
+ pane, sidebar focus, and key-action parity. Native currently provides
66
+ multiline card rendering, title-focused search, numeric selection, and the
67
+ shared close-action contract.
68
+
69
+ ## setup
70
+
71
+ ```
72
+ projmux setup [--timeout DURATION] [--non-interactive]
73
+ ```
74
+
75
+ Probes which projmux key sequences (`Alt-1..5`, `Ctrl-N`, `Ctrl-Shift-{R,L,M}`,
76
+ `Ctrl-M`, `Alt-Shift-{Left,Right}`) reach this process and which the
77
+ terminal swallows. Reports `plain`, `csi-u`, `unknown`, or `timeout` for
78
+ each. `--non-interactive` skips the TTY probe and prints the expected key
79
+ map. Default `--timeout` is `5s`. Run it outside tmux after trying
80
+ `projmux shell`; keys reported as `plain` or `csi-u` already work with zero
81
+ terminal config.
82
+
83
+ ## init
84
+
85
+ ```
86
+ projmux init [terminal] [--apply | --dry-run] [--config <path>]
87
+ [--allow-symlink]
88
+ ```
89
+
90
+ Applies a terminal-specific fallback for shortcuts that `projmux setup`
91
+ reports as swallowed. When `terminal` is omitted, autodetects from
92
+ `$TERM_PROGRAM`/`$TERMINAL_EMULATOR`.
93
+ Known terminals: `ghostty`, `windows-terminal`. Default is dry-run; pass
94
+ `--apply` to write (timestamped `.bak.<timestamp>` is created). Refuses to
95
+ write through a symlink unless `--allow-symlink` is passed (dotfiles repos).
96
+ `--config <path>` overrides the candidate list when the adapter has more
97
+ than one default location (Ghostty `config` vs `config.ghostty`). If setup
98
+ shows every key arriving, skip init.
99
+
100
+ ## doctor
101
+
102
+ ```
103
+ projmux doctor [--json]
104
+ projmux doctor --install-missing [--dry-run] [--include-optional]
105
+ ```
106
+
107
+ Runs a dependency check: `tmux ≥ 3.4`, `fzf ≥ 0.65.0`, `git`, `stty` (POSIX
108
+ only), and `kubectl` (optional). Exit code `0` even when optional deps are
109
+ missing; non-zero only when a required dep is missing or stale. `--json`
110
+ emits a machine-readable array; the default is the human report with
111
+ suggested install commands per platform. `--install-missing` is explicit
112
+ opt-in and runs generated install commands only for missing or stale required
113
+ dependencies. `--dry-run` prints those commands without executing them.
114
+ `--include-optional` also includes optional missing dependencies such as
115
+ `kubectl` when an install command is available. Install flags cannot be
116
+ combined with `--json`. Doctor does not diagnose terminal key delivery; use
117
+ `projmux setup` for that. For fzf, doctor requires the junegunn/fzf CLI
118
+ executable on `PATH` to report at least 0.65.0 from `fzf --version`; the npm
119
+ package `fzf` is a JavaScript library and is not a valid CLI install path for
120
+ projmux.
121
+
122
+ ## focus
123
+
124
+ ```
125
+ projmux focus --target SESSION[:WINDOW[.PANE]] [--socket <path>]
126
+ [--source ai|status-bar|external|os-notification]
127
+ [--kind reply-ready|busy-cleared|segment-click|custom]
128
+ [--json]
129
+ ```
130
+
131
+ Unified switch-client dispatch. Resolves the session against the live tmux
132
+ inventory (with prefix/fuzzy fallback), then redirects one suitable attached
133
+ client on the selected socket. It never force-detaches other clients. If no
134
+ client is attached on that socket, it emits the configured desktop
135
+ notification instead. `--socket` is explicit; when omitted, the socket is
136
+ derived from `$TMUX`.
137
+
138
+ Exit codes:
139
+
140
+ - `0` — focused (or the notify-only fallback fired).
141
+ - `2` — target session could not be resolved (`focusExitNotResolved`).
142
+
143
+ `--source`/`--kind` are telemetry labels logged when
144
+ `PROJMUX_FOCUS_DEBUG` is set. `--json` prints a single-line JSON payload
145
+ with `{ok, fallback, target, socket, resolved_session, client, dispatch,
146
+ session_state, window_state, pane_state, reason, note}`. Callers can
147
+ distinguish unresolved sessions (`reason=session-unresolved`, exit 2),
148
+ session rename/prefix fallback (`session_state=fallback`), window index
149
+ fallback (`window_state=index-fallback-session`), pane index fallback
150
+ (`pane_state=index-fallback-window`), and explicit id failures
151
+ (`window-id-unresolved` / `pane-id-unresolved`).
152
+
153
+ ## notify
154
+
155
+ Pending AI notify queue. `attention` is live tmux pane state; `notify` is
156
+ the explicit-ack pending notification source of truth used by the status-bar
157
+ notify segment and notify sidebar. It is not the source of truth for all live pane attention. See
158
+ [notify-queue.md](notify-queue.md) for the full data model.
159
+
160
+ ```
161
+ projmux notify push --text <s> --target <SESSION[:WINDOW[.PANE]]>
162
+ [--socket <s>] [--severity info|warn|critical]
163
+ [--source ai|k8s|git|external] [--ttl <seconds>]
164
+ [--id <s>] [--json]
165
+ projmux notify list [--live] [--json] [--limit N] [--ui table|sidebar]
166
+ [--severity ...] [--source ...]
167
+ projmux notify ack <id> | --all
168
+ projmux notify reconcile [--json]
169
+ ```
170
+
171
+ - `push` — append (or refresh, with `--id`) one entry. `--ttl` defaults to
172
+ `600` seconds as freshness metadata; it does not remove rows from
173
+ `notify list`. `--text` is hard-capped to 80 runes (longer text is
174
+ truncated server-side).
175
+ - `list` — newest-first pending queue table `ID AGE SEV SRC TARGET TEXT`
176
+ (or JSON). `--severity` and `--source` are repeatable filters.
177
+ `--live` adds a non-mutating explanation table (or JSON report) that
178
+ compares queued entries with live pane attention. It calls out manual
179
+ reply badges that do not queue because no AI agent is attached, live AI
180
+ reply panes missing a queue entry, matched AI reply entries, and stale
181
+ queue entries whose live pane no longer matches. `--ui=sidebar` opens the
182
+ compact interactive notify list where Enter focuses and acks a target, `a`
183
+ acks the selected row, and `Ctrl-A` clears all; opening or navigating the
184
+ sidebar does not ack. The sidebar uses two-line cards with notification text
185
+ first and compact age/project/window/pane metadata below, while keeping ids,
186
+ source, severity, and target details searchable.
187
+ - `ack <id>` removes one entry; `--all` flushes the queue.
188
+ - `reconcile` — walks `tmux list-panes -a` and back-fills entries for
189
+ panes whose attention state is `reply` AND whose AI agent option is
190
+ set, reporting stale `ai:` entries that no longer match a live pane without
191
+ acking them.
192
+ Soft-fails (no error, populated `errors` field in the summary) when
193
+ tmux is not running. Use this as the recovery path when the queue and
194
+ live pane state drift.
195
+
196
+ ## usage
197
+
198
+ Authoritative AI token usage. See [usage-tracking.md](usage-tracking.md)
199
+ for adapter detail.
200
+
201
+ ```
202
+ projmux usage [--model codex|claude|all] [--window 5h|weekly|all]
203
+ [--json] [--force|-f]
204
+ ```
205
+
206
+ Renders a tab-aligned `MODEL WINDOW PCT RESETS_AT STALE` table; appends a
207
+ backoff note when an adapter is in 429 cooldown. `--force` clears any
208
+ active backoff and bypasses the per-adapter throttle floor (Claude `5m`,
209
+ Codex shares the global `30s`). `--json` emits the snapshot array; when
210
+ backoff is active the wrapper `{snapshots, backoff}` object is emitted
211
+ instead.
212
+
213
+ ## status
214
+
215
+ Per-segment status-bar renderers. All four are silent on failure — the
216
+ tmux status interval polls them and must never produce a stack trace.
217
+
218
+ ```
219
+ projmux status git [path]
220
+ projmux status kube [session]
221
+ projmux status usage [--max-width N] [--force|-f]
222
+ projmux status notify [--max-width N]
223
+ ```
224
+
225
+ - `git` — `#[bold,fg=colour16,bg=colour45] <branch> #[default]` for the
226
+ pane's `pane_current_path` (or the supplied path). Empty when not in a
227
+ repo.
228
+ - `kube` — `⎈ <context>/<namespace>` segment. Reads
229
+ `~/.cache/tmux/kube-segment-<session>.txt` first (TTL governed by
230
+ `TMUX_KUBE_CACHE_TTL`, default `5s`). Picks up a per-session
231
+ `KUBECONFIG` from `${XDG_RUNTIME_DIR:-~/.cache}/kube-sessions/<session>.yaml`.
232
+ - `usage` — HUD-style `Claude (Nm) 5h [bar] N% · weekly [bar] N% Codex 5h
233
+ [bar] N% · weekly [bar] N%`. Degrades through six tiers as `--max-width`
234
+ shrinks. Triggers an opportunistic, throttled refresh (per-adapter
235
+ throttle, `30s` floor) so a stale cache self-heals.
236
+ - `notify` — newest-first HUD block with project, state, optional agent, text,
237
+ age, and `+<extras>`. Window/pane ids remain routable metadata but are not
238
+ displayed in the compact HUD. Degrades through width tiers; default
239
+ `--max-width` is `200` runes.
240
+
241
+ ## statusbar
242
+
243
+ ```
244
+ projmux statusbar click <range-id> [--socket <s>] [--mouse-window <id>]
245
+ [--mouse-x N] [--mouse-y N]
246
+ ```
247
+
248
+ Click/keyboard dispatcher for the two-line status bar. Implemented range ids:
249
+ `session pwd kube git usage notify`. The bare `window` /
250
+ `window|<idx>` token (tmux's built-in window-list range) and the empty
251
+ range fall through to `select-window -t @<mouse_window>` so the native
252
+ click-to-switch tab affordance is preserved on row 0. Unknown range ids are
253
+ non-specialized placeholders and no-op. `session` opens the existing-session
254
+ popup; `pwd` copies the current pane path to the tmux paste buffer and shows
255
+ it in a compact popup; `kube` and `git` open the project switcher popup;
256
+ `usage` opens the detailed `projmux usage` table popup; `notify` focuses and
257
+ acks the newest actionable queue target.
258
+ `MouseDown1Status` errors are
259
+ swallowed and surfaced as `display-message` toasts so a transient
260
+ failure does not raise a tmux error popup. See [statusbar.md](statusbar.md).
261
+
262
+ ## attention
263
+
264
+ ```
265
+ projmux attention toggle [pane]
266
+ projmux attention clear [pane]
267
+ projmux attention arm [pane]
268
+ projmux attention list [--json] [--all]
269
+ projmux attention window [window]
270
+ ```
271
+
272
+ Toggles the `✳` pane title prefix and the `@projmux_attention_state` pane
273
+ option. `toggle` flips between cleared and `reply`; `clear` always
274
+ clears; `arm` sets a pre-reply armed state used by the AI flow. The
275
+ producer side pushes the matching entry into the notify queue when the pane
276
+ has an associated AI agent option; clearing attention does not ack the queue
277
+ row (manual toggles on shell panes do not push). `list` reads `tmux list-panes -a` and shows live pane
278
+ attention state without reading or mutating the notify queue; by default
279
+ it shows panes with an attention option or title marker, and `--all`
280
+ includes every pane. `window` renders the status-bar window badge for the
281
+ supplied window.
282
+
283
+ ## ai
284
+
285
+ ```
286
+ projmux ai split --inside <right|down> [--agent <name>] ...
287
+ projmux ai picker --inside <right|down>
288
+ projmux ai settings
289
+ projmux ai status set <thinking|waiting|idle> [--pane <id>]
290
+ projmux ai notify <reset|notify> [--pane <id>]
291
+ projmux ai watch-title [--pane <id>]
292
+ projmux ai topic ...
293
+ ```
294
+
295
+ Manages the AI split lifecycle and the per-pane state machine that drives
296
+ the `attention` badge, the `notify` queue producer, and the desktop
297
+ notifier. `status set waiting` is the trigger that flips a pane to the
298
+ reply-ready state — that transition pushes an `ai:<session>:<pane>`
299
+ entry into the notify queue.
300
+
301
+ ## tmux
302
+
303
+ ```
304
+ projmux tmux popup-toggle <mode>
305
+ projmux tmux popup-switch
306
+ projmux tmux popup-sessions
307
+ projmux tmux popup-preview <session>
308
+ projmux tmux rebalance-panes
309
+ projmux tmux rename-pane <pane> <title>
310
+ projmux tmux print-config [--bin <path>]
311
+ projmux tmux print-app-config [--bin <path>]
312
+ projmux tmux install [--bin <path>] [--config <path>] [--include <path>]
313
+ projmux tmux install-app [--bin <path>] [--config <path>]
314
+ projmux tmux apply
315
+ ```
316
+
317
+ Helpers tmux's keybindings and the install pipeline call into. Modes
318
+ accepted by `popup-toggle` mirror the historical sessionizer surface:
319
+ `session-popup`, `sessionizer`, `sessionizer-sidebar`,
320
+ `ai-split-picker-right`, `ai-split-picker-down`, `ai-split-settings`.
321
+ `apply` reloads the live `-L projmux` server's config without restarting
322
+ it; `make install` and `projmux upgrade` invoke it after replacing the
323
+ binary.
324
+
325
+ ## update
326
+
327
+ ```
328
+ projmux update status [--json]
329
+ projmux update check [--json]
330
+ projmux update apply [--dry-run] [--no-apply]
331
+ ```
332
+
333
+ Installer-aware update status foundation. `status` is read-only: it
334
+ prints the current version, cached latest GitHub Release tag when present,
335
+ cache freshness (`fresh`, `stale`, or `unknown`), update state, detected
336
+ installer source, and cache path. It never reaches the network, so it is
337
+ safe for interactive use and shell startup paths.
338
+
339
+ `check` fetches the latest GitHub Release metadata for
340
+ `crevissepartners/projmux`, atomically writes
341
+ `${XDG_CACHE_HOME:-~/.cache}/projmux/update.json`, then prints the concise
342
+ latest/update/cache result. `--json` emits the same machine-readable
343
+ status shape for both subcommands.
344
+
345
+ `projmux shell` reads the same cache before opening the isolated tmux app.
346
+ When the cache is fresh, an update is available, and the installer supports
347
+ `update apply`, shell startup shows a small picker with Update Now, Later,
348
+ and Skip This Version actions. This startup prompt never reaches the network;
349
+ run `projmux update check` first when you want it to see the newest release.
350
+
351
+ Installer detection honors
352
+ `PROJMUX_INSTALLER=npm|go|github-release|source`. When unset or invalid,
353
+ the source is reported as `unknown` with guidance to set the variable.
354
+ `apply` is installer-aware and only runs after explicit user selection.
355
+ For npm installs, it runs `npm update -g projmux` and then
356
+ `projmux tmux apply` unless `--no-apply` is set. For Go installs, it
357
+ delegates to the existing atomic `projmux upgrade` flow. For
358
+ `github-release` installs, it downloads the latest matching
359
+ `projmux_<version>_<goos>_<goarch>.tar.gz` release asset, extracts the
360
+ binary, atomically replaces the current executable, then applies tmux
361
+ configuration unless `--no-apply` is set. `source` installs report an
362
+ actionable error because they must be updated from the source checkout.
363
+
364
+ ## upgrade
365
+
366
+ ```
367
+ projmux upgrade [--ref @latest|@<tag>|@<branch>]
368
+ [--target <path>] [--no-apply] [--dry-run]
369
+ ```
370
+
371
+ `go install`s the binary, atomically replaces the on-disk file, then
372
+ runs `projmux tmux apply` (skipped with `--no-apply`). Reads
373
+ `PROJMUX_PROJDIR` from the calling shell and memoizes the primary entry
374
+ to `~/.config/projmux/projdir`. npm-installed binaries reject this
375
+ command; use `projmux update apply` or `npm update -g projmux` for npm
376
+ installs.
377
+
378
+ ## sessions / session-popup / preview / pin / kill / prune / tag
379
+
380
+ The lifecycle helpers retained from earlier releases. They share their
381
+ flags with the top-level `switch` UX:
382
+
383
+ - `sessions` — list/pick existing tmux sessions, supports `--ui=popup`.
384
+ - `session-popup` — read/write the popup-marker state used during
385
+ preview cycling.
386
+ - `preview` — manage the persisted window/pane preview selection.
387
+ - `pin add|remove|toggle|list|clear` — pin set CRUD, persisted under
388
+ `~/.config/projmux/pins`.
389
+ - `kill <session>` / `kill tagged` — terminate sessions; `tagged`
390
+ consumes the active tagged-selection set.
391
+ - `prune ephemeral` — drop ephemeral sessions older than the configured
392
+ retention window.
393
+ - `tag` — manage the tagged-selection set.
394
+
395
+ ## current / shell / attach / settings
396
+
397
+ - `current` — print `pane_current_path` for the active tmux pane (used
398
+ by the shell jump binding).
399
+ - `shell` — boot the isolated `-L projmux` tmux server with the
400
+ generated config. The generated app config uses absolute `$SHELL` as the
401
+ tmux default shell when set, otherwise `/bin/sh`.
402
+ - `attach auto [--keep=N] [--fallback=home|ephemeral]` — auto-attach to
403
+ the most recent session, with bounded retention and a fallback policy.
404
+ - `settings` — interactive configuration UI for the project picker, AI
405
+ splits, Status Bar cwd/git decoration, Project Root management, the
406
+ switcher's saved workdirs list, and About/Update status. In Project Picker,
407
+ `Project Root` manages the saved
408
+ primary root (`~/.config/projmux/projdir`) and displays whether the effective
409
+ value comes from `PROJMUX_PROJDIR`, tmux `@projmux_projdir`, saved config, or
410
+ no configured source. When no source is configured, the direct-set prompt
411
+ starts with `$HOME` as an editable fallback, but `$HOME` is not used as the
412
+ effective root unless saved. `Workdirs` remains separate: those entries are
413
+ additional search roots, not the primary root. Status Bar stores
414
+ `~/.config/projmux/statusbar-decoration` as `off` (default), `symbol`, or
415
+ `emoji` and updates the live tmux option when available. The About section
416
+ reads the cached update status without network access;
417
+ selecting Check Updates runs `projmux update check`, and Update Now runs
418
+ `projmux update apply`. The same About section also lists the keybinding
419
+ diagnostic path: zero-config first, `setup` for swallowed keys, `init` for
420
+ supported terminal fallbacks, and `doctor` for dependencies.
421
+
422
+ ## See also
423
+
424
+ - [npm-distribution.md](npm-distribution.md) — npm binary package layout.
425
+ - [statusbar.md](statusbar.md) — two-line layout and click range catalogue.
426
+ - [notify-queue.md](notify-queue.md) — queue file format and lifecycle.
427
+ - [usage-tracking.md](usage-tracking.md) — adapter HTTP/file behaviour.
428
+ - [keybindings.md](keybindings.md) — terminal key delivery and CSI-u.
429
+ - [hooks.md](hooks.md) — `post-create` hook contract.
@@ -0,0 +1,127 @@
1
+ # Configuration
2
+
3
+ Most users can configure projmux from `projmux settings`. Environment variables
4
+ are available for repeatable shell setup, managed machines, or advanced
5
+ overrides.
6
+
7
+ ## Project Discovery
8
+
9
+ `projmux switch` combines pinned directories, live tmux sessions, and
10
+ discovered project roots.
11
+
12
+ When no explicit roots are configured, discovery uses weak probes only when
13
+ those folders exist:
14
+
15
+ - `~/source`
16
+ - `~/work`
17
+ - `~/projects`
18
+ - `~/src`
19
+ - `~/code`
20
+
21
+ It does not assume a canonical repo root.
22
+
23
+ Use Settings > Project Picker for the normal interactive flow:
24
+
25
+ - `Project Root` sets, changes, or clears the saved primary root.
26
+ - `+ Add Workdir...` appends one directory to the saved workdirs list.
27
+ - `Workdirs` reviews and removes saved workdirs.
28
+
29
+ `Add Workdir > Type path manually...` skips the filesystem scan and is useful
30
+ for large mounts, WSL paths, NFS paths, or temporary project roots.
31
+
32
+ The saved workdir file is:
33
+
34
+ ```text
35
+ ~/.config/projmux/workdirs
36
+ ```
37
+
38
+ It stores one absolute path per line. Lines beginning with `#` are comments.
39
+ The file is read only when no env root list is set.
40
+
41
+ ## Environment Variables
42
+
43
+ | Variable | Purpose |
44
+ | --- | --- |
45
+ | `PROJMUX_PROJDIR` | Explicit primary project root. Accepts an OS-native PATH-style multi-value: the first non-empty entry is the primary root and later entries are prepended to managed-root discovery. The primary value is memoized to `~/.config/projmux/projdir`. |
46
+ | `PROJMUX_MANAGED_ROOTS` | Search-root override. Uses the OS-native path-list separator and takes priority over the saved workdirs file and default weak probes. |
47
+ | `TMUX_SESSIONIZER_ROOTS` | Legacy alias still honored at runtime for managed roots. |
48
+ | `PROJMUX_NOTIFY_HOOK` | External executable that receives AI desktop notifications instead of the built-in Linux/WSL sender. |
49
+ | `PROJMUX_WSL_TOAST_ICON_DIR` | Directory used when copying the WSL toast icon into a Windows-readable path. |
50
+ | `PROJMUX_USAGE_STATE_DIR` | Override directory for AI usage snapshots. Defaults to `<state>/projmux/usage`. Point this at a synced directory to share authoritative usage across machines. |
51
+ | `PROJMUX_USAGE_DEBUG` | When non-empty, prints adapter errors from `projmux status usage` to stderr. |
52
+ | `PROJMUX_USAGE_LIMITS_PATH` | Deprecated. Read but ignored; limits now come from upstream APIs and local Codex rollout state. |
53
+ | `PROJMUX_FOCUS_DEBUG` | When non-empty, `projmux focus` prints one telemetry line to stderr. |
54
+ | `PROJMUX_PICKER_BACKEND` | Set to `native` to opt into the experimental native picker backend. `fzf` remains the default and stable backend. |
55
+ | `PROJMUX_INSTALLER` | Installer source hint used by update flows. npm installs set this automatically; advanced release installs can set `github-release`. |
56
+
57
+ Example:
58
+
59
+ ```sh
60
+ export PROJMUX_PROJDIR="/main/repos:/srv/work/repos"
61
+ ```
62
+
63
+ On Linux and macOS the separator is `:`. On Windows-style paths the separator
64
+ is `;`.
65
+
66
+ ## tmux Project Root Option
67
+
68
+ The switch command also reads this tmux option:
69
+
70
+ ```tmux
71
+ set-option -g @projmux_projdir /path/to/repos
72
+ ```
73
+
74
+ An env `PROJMUX_PROJDIR` value takes priority over the tmux option. The tmux
75
+ option takes priority over the saved projdir file.
76
+
77
+ ## Notifications
78
+
79
+ When `PROJMUX_NOTIFY_HOOK` is unset, projmux uses:
80
+
81
+ - `notify-send` on Linux.
82
+ - PowerShell toasts on WSL.
83
+
84
+ When the hook is set, projmux invokes it with positional arguments:
85
+
86
+ ```text
87
+ summary body urgency app-name tag group icon-path
88
+ ```
89
+
90
+ Hook details for new-session lifecycle hooks live in [Hooks](hooks.md).
91
+
92
+ ## Usage Tracking
93
+
94
+ `projmux usage` and the status-bar usage segment store snapshots under:
95
+
96
+ ```text
97
+ ${PROJMUX_USAGE_STATE_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/projmux/usage}/snapshots.json
98
+ ```
99
+
100
+ See [Usage tracking](usage-tracking.md) for adapter behavior, throttling, and
101
+ failure handling.
102
+
103
+ ## Status Bar Decoration
104
+
105
+ Settings > Status Bar controls the optional cwd/git leading decoration:
106
+
107
+ - `off` is the default and avoids icon-font assumptions.
108
+ - `symbol` restores the Nerd Font-style folder/git icons.
109
+ - `emoji` uses emoji decorators.
110
+
111
+ The saved value lives at:
112
+
113
+ ```text
114
+ ~/.config/projmux/statusbar-decoration
115
+ ```
116
+
117
+ ## Rare Tunables
118
+
119
+ These are intended for debugging or local policy, not routine setup:
120
+
121
+ | Variable | Purpose |
122
+ | --- | --- |
123
+ | `PROJMUX_TMUX_NOTIFY_DEDUPE_SECONDS` | Collapse window for duplicate AI notifications keyed on `(summary, tag)`. |
124
+ | `PROJMUX_CODEX_TITLE_WATCH_INTERVAL` | Title-watch loop pacing for Codex panes. |
125
+ | `PROJMUX_CODEX_REPLY_SETTLE_LOOPS` | Reply-detection settle-loop pacing for Codex panes. |
126
+ | `TMUX_KUBE_CACHE_TTL` | Kubernetes status segment cache TTL. |
127
+ | `TMUX_KUBE_TIMEOUT` | kubectl invocation budget for the Kubernetes status segment. |
package/docs/hooks.md ADDED
@@ -0,0 +1,118 @@
1
+ # Hooks
2
+
3
+ projmux runs an optional user script when it creates a new tmux session. The
4
+ hook is the project-agnostic extension point for things projmux itself stays out of:
5
+ injecting per-session env via `tmux set-environment`, picking a `GH_TOKEN` for
6
+ the repo, exporting a Kubernetes context, kicking off a background sync, etc.
7
+ projmux never ships behavior specific to any of those — that lives in the
8
+ hook.
9
+
10
+ ## Where it lives
11
+
12
+ Global hook:
13
+
14
+ ```text
15
+ ${XDG_CONFIG_HOME:-$HOME/.config}/projmux/hooks/post-create
16
+ ```
17
+
18
+ Project-local hooks, discovered from the new session's `PROJMUX_CWD`:
19
+
20
+ ```text
21
+ <repo>/.projmux/post-create
22
+ <repo>/.projmux/hooks/post-create
23
+ ```
24
+
25
+ Each hook file must exist, be a regular file or symlink (not a directory), and
26
+ have the owner-execute bit set. Anything else is silently skipped — no warning,
27
+ no log. There is no enable flag.
28
+
29
+ ```sh
30
+ mkdir -p ~/.config/projmux/hooks
31
+ chmod +x ~/.config/projmux/hooks/post-create
32
+ ```
33
+
34
+ For project-local hooks, projmux runs at most one file: first
35
+ `.projmux/post-create` if executable, otherwise `.projmux/hooks/post-create` if
36
+ executable. Discovery does not walk parent directories and does not run hooks
37
+ from status, preview, or picker hot paths.
38
+
39
+ ## When it runs
40
+
41
+ After projmux creates a brand-new tmux session via `EnsureSession` (the
42
+ persistent path used by `current` and `switch`) or `CreateEphemeralSession`
43
+ (used by `attach`). It does **not** run when projmux attaches to an existing
44
+ session.
45
+
46
+ If both a global hook and a project-local hook exist, projmux runs the global
47
+ hook first, then the project-local hook. A failure or timeout in either hook is
48
+ logged once and does not block session creation or the other hook.
49
+
50
+ The hook's exit code is ignored — session creation always succeeds.
51
+ Hook stdout and stderr are forwarded to projmux's stderr line-by-line,
52
+ prefixed with `[post-create] `. The hook is killed after 5 seconds.
53
+
54
+ ## Environment
55
+
56
+ The hook inherits projmux's environment, plus:
57
+
58
+ | Variable | Always set | Description |
59
+ | --- | --- | --- |
60
+ | `PROJMUX_SESSION` | yes | tmux session name |
61
+ | `PROJMUX_CWD` | yes | absolute working directory of the new session |
62
+ | `PROJMUX_SESSION_KIND` | yes | `persistent` or `ephemeral` |
63
+ | `PROJMUX_VERSION` | yes | projmux version string |
64
+ | `PROJMUX_SOCKET` | only if projmux used `tmux -L <socket>` | tmux socket name |
65
+
66
+ ## Examples
67
+
68
+ ### Global stub
69
+
70
+ ```bash
71
+ #!/usr/bin/env bash
72
+ echo "session=$PROJMUX_SESSION cwd=$PROJMUX_CWD kind=$PROJMUX_SESSION_KIND"
73
+ ```
74
+
75
+ ### Project-local stub
76
+
77
+ ```bash
78
+ mkdir -p .projmux
79
+ cat > .projmux/post-create <<'EOF'
80
+ #!/usr/bin/env bash
81
+ echo "project hook for $PROJMUX_CWD"
82
+ EOF
83
+ chmod +x .projmux/post-create
84
+ ```
85
+
86
+ ### Per-session GH_TOKEN by repo
87
+
88
+ ```bash
89
+ #!/usr/bin/env bash
90
+ set -euo pipefail
91
+
92
+ case "$PROJMUX_CWD" in
93
+ "$HOME"/source/repos/personal/*) token=$GH_TOKEN_PERSONAL ;;
94
+ "$HOME"/source/repos/work/*) token=$GH_TOKEN_WORK ;;
95
+ *) exit 0 ;;
96
+ esac
97
+
98
+ tmux set-environment -t "$PROJMUX_SESSION" GH_TOKEN "$token"
99
+ ```
100
+
101
+ `set-environment` only seeds the session env that newly-spawned panes inherit;
102
+ it does not retroactively change the current shell. Open new panes via tmux
103
+ (`Ctrl-b c`, `Ctrl-b "`, etc.) to pick up the value.
104
+
105
+ ## Troubleshooting
106
+
107
+ - **Nothing happens.** Check the execute bit on the global hook
108
+ (`ls -l ~/.config/projmux/hooks/post-create`) or the project hook
109
+ (`ls -l .projmux/post-create .projmux/hooks/post-create`). A missing bit
110
+ makes projmux skip silently by design.
111
+ - **`projmux: post-create hook: ... timed out after 5s`.** Long-running work
112
+ belongs in a backgrounded child (`(slow-thing &) >/dev/null 2>&1`). The hook
113
+ itself must return within 5s or projmux kills it.
114
+ - **`projmux: post-create hook: hook ... exited with status N`.** The script
115
+ returned non-zero. projmux logs it once and moves on; the session is still
116
+ created.
117
+ - **Lines appear with `[post-create] ` prefix.** Expected — that is how the
118
+ hook's stdout/stderr is multiplexed into projmux's stderr stream.