projmux 0.4.5 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/cli.md ADDED
@@ -0,0 +1,432 @@
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 configured picker popup
58
+ or 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
+ Settings > Labs > Picker Engine can switch between the default native picker
64
+ backend and the external fzf fallback. `PROJMUX_PICKER_BACKEND=fzf` is supported
65
+ as an environment override and takes priority over the saved Labs setting.
66
+
67
+ ## setup
68
+
69
+ ```
70
+ projmux setup [--timeout DURATION] [--non-interactive]
71
+ ```
72
+
73
+ Probes which projmux key sequences (`Alt-1..5`, `Ctrl-N`, `Ctrl-Shift-{R,L,M}`,
74
+ `Ctrl-M`, `Alt-Shift-{Left,Right}`) reach this process and which the
75
+ terminal swallows. Reports `plain`, `csi-u`, `unknown`, or `timeout` for
76
+ each. `--non-interactive` skips the TTY probe and prints the expected key
77
+ map. Default `--timeout` is `5s`. Run it outside tmux after trying
78
+ `projmux shell`; keys reported as `plain` or `csi-u` already work with zero
79
+ terminal config.
80
+
81
+ ## init
82
+
83
+ ```
84
+ projmux init [terminal] [--apply | --dry-run] [--config <path>]
85
+ [--allow-symlink]
86
+ ```
87
+
88
+ Applies a terminal-specific fallback for shortcuts that `projmux setup`
89
+ reports as swallowed. When `terminal` is omitted, autodetects from
90
+ `$TERM_PROGRAM`/`$TERMINAL_EMULATOR`.
91
+ Known terminals: `ghostty`, `windows-terminal`. Default is dry-run; pass
92
+ `--apply` to write (timestamped `.bak.<timestamp>` is created). Refuses to
93
+ write through a symlink unless `--allow-symlink` is passed (dotfiles repos).
94
+ `--config <path>` overrides the candidate list when the adapter has more
95
+ than one default location (Ghostty `config` vs `config.ghostty`). If setup
96
+ shows every key arriving, skip init.
97
+
98
+ ## doctor
99
+
100
+ ```
101
+ projmux doctor [--json]
102
+ projmux doctor --install-missing [--dry-run] [--include-optional]
103
+ ```
104
+
105
+ Runs a dependency check: `tmux ≥ 3.4`, `fzf ≥ 0.65.0`, `git`, `stty` (POSIX
106
+ only), and `kubectl` (optional). Exit code `0` even when optional deps are
107
+ missing; non-zero only when a required dep is missing or stale. `--json`
108
+ emits a machine-readable array; the default is the human report with
109
+ suggested install commands per platform. `--install-missing` is explicit
110
+ opt-in and runs generated install commands only for missing or stale required
111
+ dependencies. `--dry-run` prints those commands without executing them.
112
+ `--include-optional` also includes optional missing dependencies such as
113
+ `kubectl` when an install command is available. Install flags cannot be
114
+ combined with `--json`. Doctor does not diagnose terminal key delivery; use
115
+ `projmux setup` for that. For fzf, doctor requires the junegunn/fzf CLI
116
+ executable on `PATH` to report at least 0.65.0 from `fzf --version`; the npm
117
+ package `fzf` is a JavaScript library and is not a valid CLI install path for
118
+ projmux.
119
+
120
+ ## focus
121
+
122
+ ```
123
+ projmux focus --target SESSION[:WINDOW[.PANE]] [--socket <path>]
124
+ [--source ai|status-bar|external|os-notification]
125
+ [--kind reply-ready|busy-cleared|segment-click|custom]
126
+ [--json]
127
+ ```
128
+
129
+ Unified switch-client dispatch. Resolves the session against the live tmux
130
+ inventory (with prefix/fuzzy fallback), then redirects one suitable attached
131
+ client on the selected socket. It never force-detaches other clients. If no
132
+ client is attached on that socket, it emits the configured desktop
133
+ notification instead. `--socket` is explicit; when omitted, the socket is
134
+ derived from `$TMUX`.
135
+
136
+ Exit codes:
137
+
138
+ - `0` — focused (or the notify-only fallback fired).
139
+ - `2` — target session could not be resolved (`focusExitNotResolved`).
140
+
141
+ `--source`/`--kind` are telemetry labels logged when
142
+ `PROJMUX_FOCUS_DEBUG` is set. `--json` prints a single-line JSON payload
143
+ with `{ok, fallback, target, socket, resolved_session, client, dispatch,
144
+ session_state, window_state, pane_state, reason, note}`. Callers can
145
+ distinguish unresolved sessions (`reason=session-unresolved`, exit 2),
146
+ session rename/prefix fallback (`session_state=fallback`), window index
147
+ fallback (`window_state=index-fallback-session`), pane index fallback
148
+ (`pane_state=index-fallback-window`), and explicit id failures
149
+ (`window-id-unresolved` / `pane-id-unresolved`).
150
+
151
+ ## notify
152
+
153
+ Pending AI notify queue. `attention` is live tmux pane state; `notify` is
154
+ the explicit-ack pending notification source of truth used by the status-bar
155
+ notify segment and notify sidebar. It is not the source of truth for all live pane attention. See
156
+ [notify-queue.md](notify-queue.md) for the full data model.
157
+
158
+ ```
159
+ projmux notify push --text <s> --target <SESSION[:WINDOW[.PANE]]>
160
+ [--socket <s>] [--severity info|warn|critical]
161
+ [--source ai|k8s|git|external] [--ttl <seconds>]
162
+ [--id <s>] [--json]
163
+ projmux notify list [--live] [--json] [--limit N] [--ui table|sidebar]
164
+ [--severity ...] [--source ...]
165
+ projmux notify ack <id> | --all
166
+ projmux notify reconcile [--json]
167
+ ```
168
+
169
+ - `push` — append (or refresh, with `--id`) one entry. `--ttl` defaults to
170
+ `600` seconds as freshness metadata; it does not remove rows from
171
+ `notify list`. `--text` is hard-capped to 80 runes (longer text is
172
+ truncated server-side).
173
+ - `list` — newest-first pending queue table `ID AGE SEV SRC TARGET TEXT`
174
+ (or JSON). `--severity` and `--source` are repeatable filters.
175
+ `--live` adds a non-mutating explanation table (or JSON report) that
176
+ compares queued entries with live pane attention. It calls out manual
177
+ reply badges that do not queue because no AI agent is attached, live AI
178
+ reply panes missing a queue entry, matched AI reply entries, and stale
179
+ queue entries whose live pane no longer matches. `--ui=sidebar` opens the
180
+ compact interactive notify list where Enter focuses and acks a target, `x`
181
+ acks the selected row, and `Ctrl-X` clears all; opening or navigating the
182
+ sidebar does not ack. The sidebar uses two-line cards with notification text
183
+ first and compact age/project/window/pane metadata below. Hidden queue ids
184
+ remain action values, but the sidebar has no search input.
185
+ - `ack <id>` removes one entry; `--all` flushes the queue.
186
+ - `reconcile` — walks `tmux list-panes -a` and back-fills entries for
187
+ panes whose attention state is `reply` AND whose AI agent option is
188
+ set, reporting stale `ai:` entries that no longer match a live pane without
189
+ acking them.
190
+ Soft-fails (no error, populated `errors` field in the summary) when
191
+ tmux is not running. Use this as the recovery path when the queue and
192
+ live pane state drift.
193
+
194
+ ## usage
195
+
196
+ Authoritative AI token usage. See [usage-tracking.md](usage-tracking.md)
197
+ for adapter detail.
198
+
199
+ ```
200
+ projmux usage [--model codex|claude|all] [--window 5h|weekly|all]
201
+ [--json] [--force|-f]
202
+ ```
203
+
204
+ Renders a tab-aligned `MODEL WINDOW PCT RESETS_AT STALE` table; appends a
205
+ backoff note when an adapter is in 429 cooldown. `--force` clears any
206
+ active backoff and bypasses the per-adapter throttle floor (Claude `5m`,
207
+ Codex shares the global `30s`). `--json` emits the snapshot array; when
208
+ backoff is active the wrapper `{snapshots, backoff}` object is emitted
209
+ instead.
210
+
211
+ ## status
212
+
213
+ Per-segment status-bar renderers. All four are silent on failure — the
214
+ tmux status interval polls them and must never produce a stack trace.
215
+
216
+ ```
217
+ projmux status git [path]
218
+ projmux status kube [session]
219
+ projmux status usage [--max-width N] [--force|-f]
220
+ projmux status notify [--max-width N]
221
+ ```
222
+
223
+ - `git` — `#[bold,fg=colour16,bg=colour45] <branch> <state> #[default]` for
224
+ the pane's `pane_current_path` (or the supplied path). Empty when not in a
225
+ repo. `<state>` is omitted when clean, otherwise it may include `*` for
226
+ local changes, `+N` staged entries, and `↑N`/`↓N` ahead/behind counts, with
227
+ compact per-token colors in tmux output.
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 [--client <key>] <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
+ `notify-sidebar`, `ai-split-picker-right`, `ai-split-picker-down`,
321
+ `ai-split-settings`.
322
+ `apply` reloads the live `-L projmux` server's config without restarting
323
+ it; `make install` and `projmux upgrade` invoke it after replacing the
324
+ binary.
325
+
326
+ ## update
327
+
328
+ ```
329
+ projmux update status [--json]
330
+ projmux update check [--json]
331
+ projmux update apply [--dry-run] [--no-apply]
332
+ ```
333
+
334
+ Installer-aware update status foundation. `status` is read-only: it
335
+ prints the current version, cached latest GitHub Release tag when present,
336
+ cache freshness (`fresh`, `stale`, or `unknown`), update state, detected
337
+ installer source, and cache path. It never reaches the network, so it is
338
+ safe for interactive use and shell startup paths.
339
+
340
+ `check` fetches the latest GitHub Release metadata for
341
+ `crevissepartners/projmux`, atomically writes
342
+ `${XDG_CACHE_HOME:-~/.cache}/projmux/update.json`, then prints the concise
343
+ latest/update/cache result. `--json` emits the same machine-readable
344
+ status shape for both subcommands.
345
+
346
+ `projmux shell` reads the same cache before opening the isolated tmux app.
347
+ When the cache is fresh, an update is available, and the installer supports
348
+ `update apply`, shell startup shows a small picker with Update Now, Later,
349
+ and Skip This Version actions. This startup prompt never reaches the network;
350
+ run `projmux update check` first when you want it to see the newest release.
351
+
352
+ Installer detection honors
353
+ `PROJMUX_INSTALLER=npm|go|github-release|source`. When unset or invalid,
354
+ the source is reported as `unknown` with guidance to set the variable.
355
+ `apply` is installer-aware and only runs after explicit user selection.
356
+ For npm installs, it runs `npm update -g projmux` and then
357
+ `projmux tmux apply` unless `--no-apply` is set. For Go installs, it
358
+ delegates to the existing atomic `projmux upgrade` flow. For
359
+ `github-release` installs, it downloads the latest matching
360
+ `projmux_<version>_<goos>_<goarch>.tar.gz` release asset, extracts the
361
+ binary, atomically replaces the current executable, then applies tmux
362
+ configuration unless `--no-apply` is set. `source` installs report an
363
+ actionable error because they must be updated from the source checkout.
364
+
365
+ ## upgrade
366
+
367
+ ```
368
+ projmux upgrade [--ref @latest|@<tag>|@<branch>]
369
+ [--target <path>] [--no-apply] [--dry-run]
370
+ ```
371
+
372
+ `go install`s the binary, atomically replaces the on-disk file, then
373
+ runs `projmux tmux apply` (skipped with `--no-apply`). Reads
374
+ `PROJMUX_PROJDIR` from the calling shell and memoizes the primary entry
375
+ to `~/.config/projmux/projdir`. npm-installed binaries reject this
376
+ command; use `projmux update apply` or `npm update -g projmux` for npm
377
+ installs.
378
+
379
+ ## sessions / session-popup / preview / pin / kill / prune / tag
380
+
381
+ The lifecycle helpers retained from earlier releases. They share their
382
+ flags with the top-level `switch` UX:
383
+
384
+ - `sessions` — list/pick existing tmux sessions, supports `--ui=popup`.
385
+ - `session-popup` — read/write the popup-marker state used during
386
+ preview cycling.
387
+ - `preview` — manage the persisted window/pane preview selection.
388
+ - `pin add|remove|toggle|list|clear` — pin set CRUD, persisted under
389
+ `~/.config/projmux/pins`.
390
+ - `kill <session>` / `kill tagged` — terminate sessions; `tagged`
391
+ consumes the active tagged-selection set.
392
+ - `prune ephemeral` — drop ephemeral sessions older than the configured
393
+ retention window.
394
+ - `tag` — manage the tagged-selection set.
395
+
396
+ ## current / shell / attach / settings
397
+
398
+ - `current` — print `pane_current_path` for the active tmux pane (used
399
+ by the shell jump binding).
400
+ - `shell` — boot the isolated `-L projmux` tmux server with the
401
+ generated config. The generated app config uses absolute `$SHELL` as the
402
+ tmux default shell when set, otherwise `/bin/sh`.
403
+ - `attach auto [--keep=N] [--fallback=home|ephemeral]` — auto-attach to
404
+ the most recent session, with bounded retention and a fallback policy.
405
+ - `settings` — interactive configuration UI for the project picker, AI
406
+ splits, Icons & Decorations mode, Project Root management, the
407
+ switcher's saved workdirs list, Labs, and About/Update status. In Project
408
+ Picker, `Project Root` manages the saved
409
+ primary root (`~/.config/projmux/projdir`) and displays whether the effective
410
+ value comes from `PROJMUX_PROJDIR`, tmux `@projmux_projdir`, saved config, or
411
+ no configured source. When no source is configured, the direct-set prompt
412
+ starts with `$HOME` as an editable fallback, but `$HOME` is not used as the
413
+ effective root unless saved. `Workdirs` remains separate: those entries are
414
+ additional search roots, not the primary root. Icons & Decorations stores
415
+ `~/.config/projmux/statusbar-decoration` as `off` (default), `symbol`, or
416
+ `emoji` and updates the live tmux option when available. Labs stores
417
+ `~/.config/projmux/picker-backend` as `native` (default) or `fzf` and updates
418
+ the live tmux `PROJMUX_PICKER_BACKEND` environment when available. The About
419
+ section reads the cached update status without network access;
420
+ selecting Check Updates runs `projmux update check`, and Update Now runs
421
+ `projmux update apply`. The same About section also lists the keybinding
422
+ diagnostic path: zero-config first, `setup` for swallowed keys, `init` for
423
+ supported terminal fallbacks, and `doctor` for dependencies.
424
+
425
+ ## See also
426
+
427
+ - [npm-distribution.md](npm-distribution.md) — npm binary package layout.
428
+ - [statusbar.md](statusbar.md) — two-line layout and click range catalogue.
429
+ - [notify-queue.md](notify-queue.md) — queue file format and lifecycle.
430
+ - [usage-tracking.md](usage-tracking.md) — adapter HTTP/file behaviour.
431
+ - [keybindings.md](keybindings.md) — terminal key delivery and CSI-u.
432
+ - [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` | Override the picker backend. Native is the default; set `fzf` to use the external fzf 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
+ ## Decoration Mode
104
+
105
+ Settings > Icons & Decorations controls optional status and picker decoration:
106
+
107
+ - `off` is the default and avoids icon-font assumptions.
108
+ - `symbol` restores the Nerd Font-style folder, GitHub, and bell icons.
109
+ - `emoji` uses emoji decorators, including the notify sidebar header bell.
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.