projmux 0.4.9 → 0.4.10

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/README-ko.md CHANGED
@@ -27,10 +27,8 @@ notification, settings 사이를 오가고 싶을 때 사용합니다.
27
27
 
28
28
  - [Node.js](https://nodejs.org/)와 npm: 기본 설치 경로에 필요합니다.
29
29
  - [tmux](https://github.com/tmux/tmux/wiki/Installing) **3.4 이상**.
30
- - [fzf](https://github.com/junegunn/fzf#installation) **0.65.0 이상**.
31
30
 
32
- 설치 후 `projmux doctor`로 로컬 runtime을 확인하세요. 필요한 `fzf`는
33
- junegunn/fzf CLI binary입니다. `npm i fzf`는 다른 JavaScript library입니다.
31
+ 설치 후 `projmux doctor`로 로컬 runtime을 확인하세요.
34
32
 
35
33
  ## 설치
36
34
 
package/README.md CHANGED
@@ -27,11 +27,8 @@ keys to move between projects, windows, panes, notifications, and settings.
27
27
 
28
28
  - [Node.js](https://nodejs.org/) and npm, for the main install path.
29
29
  - [tmux](https://github.com/tmux/tmux/wiki/Installing) **3.4 or newer**.
30
- - [fzf](https://github.com/junegunn/fzf#installation) **0.65.0 or newer**.
31
30
 
32
- Run `projmux doctor` after installing to check the local runtime. The `fzf`
33
- requirement is the junegunn/fzf CLI binary; `npm i fzf` is a different
34
- JavaScript library.
31
+ Run `projmux doctor` after installing to check the local runtime.
35
32
 
36
33
  ## Install
37
34
 
@@ -29,7 +29,7 @@
29
29
  - `make fmt`: repository formatting for Go, shell snippets, and generated docs where applicable.
30
30
  - `make fix`: safe automatic fixes such as `go fix` and repository-approved cleanup steps.
31
31
  - `make npm-pack`: local npm binary package staging and `npm pack --dry-run` for the root package plus platform packages.
32
- - `make test`: fast unit coverage for app-layer AI split native agent launch/selective popup-toggle/settings/status/notification parity including AI pane option metadata, watcher metadata bootstrap for existing panes, capture-backed reply detection, missing-pane watcher shutdown, armed focus-only reply badge clearing, busy attention preservation on focus clear, manual AI topic preservation while watcher status still updates, agent-labeled desktop notification message context with pane-title-first body text, global/project-local post-create hook dispatch, and agent-specific notification icons, and scoped even row/column resizing after shell and agent splits, status-bar git/kube segment parity including branch block styling, statusbar pwd click path popup/buffer-copy fallback, isolated `projmux shell` tmux app launch/config generation including home-project default session targeting, app-owned project-name statusbar layout, and distinct project badge color, shell startup update prompt actions for fresh installer-aware cached updates, pane/window keybindings, window rename bindings, pane rename helper/binding, pane-exit rebalance command/hooks, hook-pane and after-select-pane based attention focus hooks, attention badge toggle/clear/list/window rendering, attach/current/kill/pin/preview/prune/sessions/session-popup/settings commands, switch, tag, tmux helper commands, update status/check/apply cache and installer detection including GitHub Release binary asset selection/extraction/replacement, doctor install-missing command selection, and Settings About update status/check action wiring, untitled standalone popup-toggle marker close/config install, direct popup minimum sizing, AI picker minimum width and height, sidebar minimum width and compact badge spacing, preview select writes, popup render output after cycling, switch picker pin action behavior without inline settings rows, nested settings hub sections for AI defaults, project picker filesystem scan/pin actions, Project Root settings source/shadowing/set/current/clear flows, app/keybinding info including Ctrl-M rename forwarding, and About version/source rendering, switch picker focused-session kill, switch picker launcher-key abort bindings, switch explicit project-root, unconfigured-root, and weak managed-root heuristic parity, switch popup hiding new-session candidates while sidebar keeps create-capable rows, switch row project-name display with `~` pinned to the top and live-session-first sorting, pretty-path, preview-context including kube context/namespace, switch settings subcommand flows including add-current-pin, interactive add-pin picker, and settings label/preview polish, fzf preview wiring, fzf baseline surface parity including prompt/footer/header fallback without app-name filler and search-key scoped card matching, sidebar compact action-only key footer, sidebar preview-window/start-position behavior without focus-time session switching, sidebar row/window ANSI styling with pane-aggregated attention badge state and AI topic labels, Alt+2/Alt+3 legacy popup row, preview-window, pane metadata, and pane-snapshot parity, switch read0 card rows with active/inactive title styling, right-side status badges, combined directory/git metadata with muted inactive branch styling, statusbar-matched block window tabs with window attention badges, read0 expect-key action parsing, restored pin/tag card badges, and restrained selected-row marker styling, switch preview metadata without duplicated directory/git rows, preview metadata rendering, popup pane display names for AI agents, AI topics, and shell commands, switch preview cycle bindings, sessions picker preview/cycle/open/kill wiring including attached-session fallback behavior, sessions picker launcher-key abort bindings, popup/switch preview summary formatting, popup sessions tmux entry helpers, switch/popup/session rendering, session identity, candidate discovery, config path derivation, popup preview read-models, and pure state rules including preview, tag, and lifecycle stores.
32
+ - `make test`: fast unit coverage for app-layer AI split native agent launch/selective popup-toggle/settings/status/notification parity including AI pane option metadata, watcher metadata bootstrap for existing panes, capture-backed reply detection, missing-pane watcher shutdown, armed focus-only reply badge clearing, busy attention preservation on focus clear, manual AI topic preservation while watcher status still updates, agent-labeled desktop notification message context with pane-title-first body text, global/project-local lifecycle hook dispatch for post-create/pre-create/pane-startup/post-attach with project hook and .projmux/config.toml trust-store hashing plus env/settings kill-switch gating, declarative startup/hook run precedence, project config env/kube session environment application, pane-startup command capture/send-keys orchestration, pre-create abort behavior, and shared projmux notification icon paths, and scoped even row/column resizing after shell and agent splits, status-bar git/kube segment parity including branch block styling, statusbar pwd display-only native-framed path popup, no clipboard or tmux buffer copy, and simple Enter-close popup command, statusbar usage native HUD popup alignment/threshold/sync-staleness/fallback coverage without raw CLI popup or popup-toggle stacking, statusbar settings click popup fallback, isolated `projmux shell` tmux app launch/config generation including home-project default session targeting, app-owned project-name statusbar layout, and distinct project badge color, first-run/version-bump shell welcome state and inline update handling, shell startup update prompt actions for fresh installer-aware cached updates, pane/window keybindings, keymap.toml tmux override rendering/stale unbinds, Settings Keybindings root/list/detail/typed edit flows including parse-error rows, invalid chord and typed-cancel guards, disable/reset writes, app config regeneration, live tmux source-file reload, and no-live-tmux save behavior, window rename bindings, pane rename helper/binding, pane-exit rebalance command/hooks, hook-pane and after-select-pane based attention focus hooks, attention badge toggle/clear/list/window rendering, attach/current/kill/pin/preview/prune/sessions/session-popup/settings commands, switch, tag, tmux helper commands, update status/check/apply cache and installer detection including GitHub Release binary asset selection/extraction/replacement, doctor install-missing command selection, and Settings About update status/check action wiring, untitled standalone popup-toggle marker close/config install, direct popup minimum sizing, AI picker minimum width and height, sidebar minimum width and compact badge spacing, preview select writes, popup render output after cycling, switch picker pin action behavior without inline settings rows, nested settings hub sections for AI defaults, project picker filesystem scan/pin actions, Project Root settings source/shadowing/set/current/clear flows, app/keybinding info including Ctrl-M rename forwarding, and About version/source rendering, switch picker focused-session kill, switch picker launcher-key abort bindings, switch explicit project-root, unconfigured-root, and weak managed-root heuristic parity, switch popup hiding new-session candidates while sidebar keeps create-capable rows, switch row project-name display with `~` pinned to the top and live-session-first sorting, pretty-path, preview-context including kube context/namespace, switch settings subcommand flows including add-current-pin, interactive add-pin picker, and settings label/preview polish, native preview wiring, baseline picker surface parity including prompt/footer/header fallback without app-name filler and search-key scoped card matching, sidebar compact action-only key footer, sidebar preview-window/start-position behavior without focus-time session switching, sidebar row/window ANSI styling with pane-aggregated attention badge state and AI topic labels, Alt+2/Alt+3 legacy popup row, preview-window, pane metadata, and pane-snapshot parity, switch read0 card rows with active/inactive title styling, right-side status badges, combined directory/git metadata with muted inactive branch styling, statusbar-matched block window tabs with window attention badges, read0 expect-key action parsing, restored pin/tag card badges, and restrained selected-row marker styling, switch preview metadata without duplicated directory/git rows, preview metadata rendering, popup pane display names for AI agents, AI topics, and shell commands, switch preview cycle bindings, sessions picker preview/cycle/open/kill wiring including attached-session fallback behavior, sessions picker launcher-key abort bindings, popup/switch preview summary formatting, popup sessions tmux entry helpers, switch/popup/session rendering, session identity, candidate discovery, config path derivation, popup preview read-models, and pure state rules including preview, tag, and lifecycle stores.
33
33
  - Current focused unit coverage also includes strict notify SOT behavior
34
34
  (TTL does not remove rows, focus success acks, reconcile reports stale
35
35
  rows), `notify list --live` queue/live explanations, notify sidebar
@@ -37,8 +37,8 @@
37
37
  actions, and `focus` dispatch diagnostics for session fallback, unresolved
38
38
  targets, window fallback, pane fallback, explicit id failures, and
39
39
  notify-only fallback.
40
- - Picker focused unit coverage includes backend-neutral picker item/action mapping, fzf adapter output, native title-focused filtering, numeric selection, shared close actions including raw and CSI-u Ctrl-X native custom actions, saved picker backend config, AI picker title chrome and stable search-key ordering, Settings title chrome and root section order, Settings Labs backend switching, environment override precedence, compact multi-line metadata gutters aligned to the project-name column, proportional native scrollbar thumb rendering, multiline partial next-item row rendering with rendered-row scrollbar units, fixed split-preview and sidebar list viewports with scrollbar tracks, native mouse down/follow-drag/release behavior, preview tab/control normalization before width clipping, optional native frame titlebars without same-line rule fill, titled native Alt-1 sidebar chrome, statusbar-preserving sidebar popup height, native-only compact project sidebar popup sizing, and notify sidebar title/popup sizing that preserves the fzf minimum/ratio baseline.
41
- - `make test-integration`: Docker-backed Linux integration smoke with real `tmux`, `fzf`, `git`, and `stty`; covers `doctor`, tmux config print/install/apply, and notify queue CRUD against isolated HOME/XDG paths.
40
+ - Picker focused unit coverage includes backend-neutral picker item/action mapping, native title-focused filtering, numeric selection, shared close actions including raw and CSI-u Ctrl-X native custom actions, deprecated picker backend value normalization, AI picker title chrome and stable search-key ordering, Settings title chrome and root section order, Settings Labs shell without backend choices plus keybinding diagnostic list/detail/probe outcome/init delegation coverage, environment override normalization, compact multi-line metadata gutters with one-column-indented metadata, proportional native scrollbar thumb rendering, multiline partial next/previous item row rendering with rendered-row scrollbar units, fixed split-preview and sidebar list viewports with scrollbar tracks, native up/down-family navigation wrap with empty-list safety and PageUp/PageDown/Home/End clamp regression coverage, native mouse down/follow-drag/release behavior, preview tab/control normalization before width clipping, optional native frame titlebars without same-line rule fill, titled native Alt-1 sidebar chrome, statusbar-preserving sidebar popup height, native-only compact project sidebar popup sizing, and notify sidebar title/popup sizing.
41
+ - `make test-integration`: Docker-backed Linux integration smoke with real `tmux`, `git`, and `stty`; covers `doctor`, tmux config print/install/apply, and notify queue CRUD against isolated HOME/XDG paths.
42
42
  - `make test-install-smoke`: Docker-backed source install smoke; covers `make install`, atomic binary replacement, `tmux apply` against a live `projmux` socket, and post-install notify queue initialization.
43
43
  - `make test-e2e`: Docker-backed real-tmux workflow smoke for session/pane setup, app config sourcing, reply-state notify reconciliation, focus notify fallback, and status notify rendering with contextual project/state/agent badges. Host-only terminal, WSL, macOS, and GUI notification behavior remains outside Docker; see [docs/testing.md](testing.md).
44
44
 
@@ -41,11 +41,10 @@ Responsibilities:
41
41
  - convert failures into typed errors
42
42
 
43
43
  ### 3. UI orchestration
44
- Picker data is modeled independently from fzf rows. The app builds
44
+ Picker data is modeled independently from row rendering. The app builds
45
45
  backend-neutral `picker.Item` values (`Title`, `Value`, `SearchText`,
46
- `MetaLines`, `Badges`, `PreviewTarget`) and then adapts them to the selected
47
- backend. Native is the default backend, while `PROJMUX_PICKER_BACKEND=fzf`
48
- keeps the external fzf backend available as an explicit fallback.
46
+ `MetaLines`, `Badges`, `PreviewTarget`) and renders them through the native
47
+ picker.
49
48
 
50
49
  Responsibilities:
51
50
  - rows for popup and sidebar views
@@ -180,7 +179,7 @@ single `bind -n MouseDown1Status` covers both lines because tmux fires
180
179
  | Range id | Line | Click action | Keybinding |
181
180
  |----------|------|-------------------------------------------|--------------|
182
181
  | session | 0 | popup `projmux sessions --ui=popup` | prefix+s s |
183
- | pwd | 0 | copy pane_current_path and show path popup | prefix+s p |
182
+ | pwd | 0 | show pane_current_path in a display-only path popup | prefix+s p |
184
183
  | kube | 0 | popup `projmux switch --ui=popup` | prefix+s k |
185
184
  | git | 0 | popup `projmux switch --ui=popup` | prefix+s g |
186
185
  | usage | 1 | popup `projmux usage` | prefix+s u |
package/docs/cli.md CHANGED
@@ -60,9 +60,8 @@ sub-verbs are entry hooks invoked by tmux keybindings (e.g.
60
60
  `sidebar-focus` is wired to the sidebar's focus binding so navigation keeps
61
61
  the active session in sync).
62
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.
63
+ Settings > Labs remains available for experimental settings, but picker backend
64
+ selection has been retired. The native picker is always used.
66
65
 
67
66
  ## setup
68
67
 
@@ -102,20 +101,16 @@ projmux doctor [--json]
102
101
  projmux doctor --install-missing [--dry-run] [--include-optional]
103
102
  ```
104
103
 
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.
104
+ Runs a dependency check: `tmux ≥ 3.4`, `git`, `stty` (POSIX only), and
105
+ `kubectl` (optional). Exit code `0` even when optional deps are missing;
106
+ non-zero only when a required dep is missing or stale. `--json` emits a
107
+ machine-readable array; the default is the human report with suggested install
108
+ commands per platform. `--install-missing` is explicit opt-in and runs
109
+ generated install commands only for missing or stale required dependencies.
110
+ `--dry-run` prints those commands without executing them. `--include-optional`
111
+ also includes optional missing dependencies such as `kubectl` when an install
112
+ command is available. Install flags cannot be combined with `--json`. Doctor
113
+ does not diagnose terminal key delivery; use `projmux setup` for that.
119
114
 
120
115
  ## focus
121
116
 
@@ -242,19 +237,20 @@ projmux status notify [--max-width N]
242
237
 
243
238
  ```
244
239
  projmux statusbar click <range-id> [--socket <s>] [--mouse-window <id>]
245
- [--mouse-x N] [--mouse-y N]
240
+ [--client <tty>] [--mouse-x N] [--mouse-y N]
246
241
  ```
247
242
 
248
243
  Click/keyboard dispatcher for the two-line status bar. Implemented range ids:
249
- `session pwd kube git usage notify`. The bare `window` /
244
+ `session pwd kube git usage notify settings`. The bare `window` /
250
245
  `window|<idx>` token (tmux's built-in window-list range) and the empty
251
246
  range fall through to `select-window -t @<mouse_window>` so the native
252
247
  click-to-switch tab affordance is preserved on row 0. Unknown range ids are
253
248
  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.
249
+ popup; `pwd` shows the current pane path in a native-framed display-only
250
+ popup; `kube` and `git` open the project switcher popup;
251
+ `settings` toggles the settings popup for the tmux client; `usage` opens the
252
+ detailed `projmux usage` table popup; `notify` focuses and acks the newest
253
+ actionable queue target.
258
254
  `MouseDown1Status` errors are
259
255
  swallowed and surfaced as `display-message` toasts so a transient
260
256
  failure does not raise a tmux error popup. See [statusbar.md](statusbar.md).
@@ -413,10 +409,9 @@ flags with the top-level `switch` UX:
413
409
  effective root unless saved. `Workdirs` remains separate: those entries are
414
410
  additional search roots, not the primary root. Appearance stores
415
411
  `~/.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;
412
+ `emoji` and updates the live tmux option when available. Labs remains
413
+ available for experimental settings. The About section reads the cached
414
+ update status without network access;
420
415
  selecting Check Updates runs `projmux update check`, and Update Now runs
421
416
  `projmux update apply`. The same About section also lists the keybinding
422
417
  diagnostic path: zero-config first, `setup` for swallowed keys, `init` for
@@ -38,6 +38,68 @@ The saved workdir file is:
38
38
  It stores one absolute path per line. Lines beginning with `#` are comments.
39
39
  The file is read only when no env root list is set.
40
40
 
41
+ ## Keymap File
42
+
43
+ Settings > Keybindings is the normal in-app editor for tmux key chords. It
44
+ lists each action, opens a detail screen, and lets you type `plain` or
45
+ `prefix` tmux chord strings. Saving writes `~/.config/projmux/keymap.toml`,
46
+ rewrites `~/.config/projmux/tmux.conf`, and, when Settings is running inside
47
+ tmux, sources that app config so non-terminal-layer tmux chords take effect
48
+ immediately.
49
+
50
+ Settings > Labs > Diagnose keybindings is the in-app diagnostic/remediation
51
+ surface for the same catalog. It probes one action key at a time through the
52
+ controlling TTY, reports whether the key arrived as a plain tmux chord, CSI-u
53
+ fallback, unexpected sequence, or timeout, and delegates supported terminal
54
+ fallback preview/apply operations to the `projmux init` engine.
55
+
56
+ `~/.config/projmux/keymap.toml` can also be edited by hand. When the file is
57
+ absent, generated tmux config stays on the built-in defaults.
58
+
59
+ Supported schema:
60
+
61
+ ```toml
62
+ [bindings.sessionizer-sidebar]
63
+ plain = "M-a"
64
+ prefix = "A"
65
+
66
+ [bindings.new-window]
67
+ plain = "C-t"
68
+ ```
69
+
70
+ Each table is `[bindings.<action-id>]`. Supported keys are:
71
+
72
+ | Key | Meaning |
73
+ | --- | --- |
74
+ | `plain` | A no-prefix tmux chord such as `M-a`, `C-t`, or `M-S-Left`. |
75
+ | `prefix` | A tmux prefix-table chord such as `A` or `r`. |
76
+
77
+ Set a value to the empty string to disable that chord for the action:
78
+
79
+ ```toml
80
+ [bindings.sessionizer-sidebar]
81
+ plain = ""
82
+ ```
83
+
84
+ In Settings, `Disable Plain/Prefix chord` writes the empty string override.
85
+ `Reset Plain/Prefix chord` removes that override and returns to the built-in
86
+ default. The Settings writer is deterministic and rewrites the supported
87
+ subset only: `[bindings.<action-id>]` tables with `plain` and `prefix` string
88
+ keys. If the existing file has parse errors or unknown action IDs, Settings
89
+ shows the keymap error row and refuses to overwrite it until the file is fixed.
90
+
91
+ The file currently affects generated tmux config from `projmux tmux
92
+ print-config`, `projmux tmux install`, `projmux tmux print-app-config`,
93
+ `projmux tmux install-app`, and `projmux shell`. Terminal init adapters such as
94
+ Ghostty and Windows Terminal still install the built-in CSI-u fallback map.
95
+ Changing those terminal-layer mappings still requires rerunning `projmux init`
96
+ and restarting the terminal where that terminal requires it.
97
+
98
+ When a chord is overridden, projmux emits unbinds for both the stale default
99
+ chord and the replacement before binding the merged action. Popup and floating
100
+ UI actions still route through `tmux popup-toggle`, so pressing the same
101
+ configured key opens and closes the popup.
102
+
41
103
  ## Environment Variables
42
104
 
43
105
  | Variable | Purpose |
@@ -51,7 +113,7 @@ The file is read only when no env root list is set.
51
113
  | `PROJMUX_USAGE_DEBUG` | When non-empty, prints adapter errors from `projmux status usage` to stderr. |
52
114
  | `PROJMUX_USAGE_LIMITS_PATH` | Deprecated. Read but ignored; limits now come from upstream APIs and local Codex rollout state. |
53
115
  | `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. |
116
+ | `PROJMUX_PICKER_BACKEND` | Legacy picker backend override. Any value, including old `fzf` settings, now resolves to the native picker. |
55
117
  | `PROJMUX_INSTALLER` | Installer source hint used by update flows. npm installs set this automatically; advanced release installs can set `github-release`. |
56
118
 
57
119
  Example:
@@ -87,7 +149,8 @@ When the hook is set, projmux invokes it with positional arguments:
87
149
  summary body urgency app-name tag group icon-path
88
150
  ```
89
151
 
90
- Hook details for new-session lifecycle hooks live in [Hooks](hooks.md).
152
+ Hook details for new-session lifecycle hooks and project-local
153
+ `.projmux/config.toml` live in [Hooks](hooks.md).
91
154
 
92
155
  ## Usage Tracking
93
156
 
@@ -100,6 +163,18 @@ ${PROJMUX_USAGE_STATE_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/projmux/usage}/
100
163
  See [Usage tracking](usage-tracking.md) for adapter behavior, throttling, and
101
164
  failure handling.
102
165
 
166
+ ## Shell Welcome State
167
+
168
+ `projmux shell` stores its once-per-version welcome marker under:
169
+
170
+ ```text
171
+ ${XDG_STATE_HOME:-$HOME/.local/state}/projmux/welcomed-v<version>.json
172
+ ```
173
+
174
+ If the marker is missing, the next shell launch shows the welcome again. If the
175
+ marker is corrupt or cannot be written, shell startup continues without the
176
+ welcome.
177
+
103
178
  ## Decoration Mode
104
179
 
105
180
  Settings > Appearance controls optional status and picker decoration:
package/docs/hooks.md CHANGED
@@ -1,89 +1,224 @@
1
1
  # Hooks
2
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.
3
+ projmux runs optional user scripts at selected tmux lifecycle points. Hooks are
4
+ the project-agnostic extension point for behavior projmux itself stays out of:
5
+ injecting per-session env via `tmux set-environment`, selecting repository
6
+ tokens, exporting a Kubernetes context, kicking off a background sync, or
7
+ sending an initial pane command.
9
8
 
10
- ## Where it lives
9
+ projmux-owned internal tmux hooks such as `pane-focus-in`, `pane-focus-out`,
10
+ `after-select-pane`, and `after-kill-pane` are not exposed as user hook events.
11
11
 
12
- Global hook:
12
+ ## Events
13
+
14
+ | Event | When it runs | Failure behavior | Stdout behavior |
15
+ | --- | --- | --- | --- |
16
+ | `pre-create` | Before projmux creates a missing persistent or ephemeral session | Non-zero exit, exec error, or timeout aborts creation | Logged with `[pre-create] ` |
17
+ | `post-create` | After projmux creates a brand-new persistent or ephemeral session | Logged and ignored; creation continues | Logged with `[post-create] ` |
18
+ | `pane-startup` | After `post-create`, once the initial pane of a brand-new session reaches a shell prompt | Logged and ignored; empty output is no-op | Captured as the command to send into the pane |
19
+ | `post-attach` | After projmux switches the current tmux client to an existing session/target from inside tmux | Logged and ignored | Logged with `[post-attach] ` |
20
+
21
+ Deferred Phase A candidates remain future work until their behavior can be
22
+ specified without exposing projmux's internal tmux hook machinery: pane exit,
23
+ window create/rename, and focus-change hook events.
24
+
25
+ ## Where Hooks Live
26
+
27
+ Global hooks live under the XDG config directory:
28
+
29
+ ```text
30
+ ${XDG_CONFIG_HOME:-$HOME/.config}/projmux/hooks/<event>
31
+ ```
32
+
33
+ For example:
13
34
 
14
35
  ```text
15
36
  ${XDG_CONFIG_HOME:-$HOME/.config}/projmux/hooks/post-create
37
+ ${XDG_CONFIG_HOME:-$HOME/.config}/projmux/hooks/pane-startup
16
38
  ```
17
39
 
18
- Project-local hooks, discovered from the new session's `PROJMUX_CWD`:
40
+ Project-local hooks are discovered from the lifecycle context's `PROJMUX_CWD`:
19
41
 
20
42
  ```text
21
- <repo>/.projmux/post-create
22
- <repo>/.projmux/hooks/post-create
43
+ <repo>/.projmux/<event>
44
+ <repo>/.projmux/hooks/<event>
45
+ <repo>/.projmux/config.toml
23
46
  ```
24
47
 
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.
48
+ For example, `pane-startup` discovery checks:
28
49
 
29
- ```sh
30
- mkdir -p ~/.config/projmux/hooks
31
- chmod +x ~/.config/projmux/hooks/post-create
50
+ ```text
51
+ <repo>/.projmux/pane-startup
52
+ <repo>/.projmux/hooks/pane-startup
32
53
  ```
33
54
 
34
- For project-local hooks, projmux runs at most one file: first
35
- `.projmux/post-create` if executable, otherwise `.projmux/hooks/post-create` if
55
+ Each hook file must exist, be a regular file or symlink (not a directory), and
56
+ have the owner-execute bit set. Anything else is silently skipped.
57
+
58
+ For each event, projmux runs at most one project-local file: first
59
+ `.projmux/<event>` if executable, otherwise `.projmux/hooks/<event>` if
36
60
  executable. Discovery does not walk parent directories and does not run hooks
37
61
  from status, preview, or picker hot paths.
38
62
 
39
- ## When it runs
63
+ If both a global hook and a project-local hook exist for an event, projmux runs
64
+ the global hook first, then the project-local hook, then any matching
65
+ declarative config command from `.projmux/config.toml`. For `pane-startup`, the
66
+ last non-empty trimmed stdout wins. Config `pane-startup` commands therefore
67
+ override file hooks deterministically.
68
+
69
+ ## Project Config
70
+
71
+ Repositories may declare hook behavior in `.projmux/config.toml`. projmux
72
+ supports only this narrow TOML subset in Phase B:
73
+
74
+ ```toml
75
+ [startup]
76
+ run = "git status --short"
77
+
78
+ [hooks.pre-create]
79
+ run = "echo checking"
80
+
81
+ [hooks.post-create]
82
+ run = "echo created $PROJMUX_SESSION"
83
+
84
+ [hooks.pane-startup]
85
+ run = "echo make test"
86
+
87
+ [hooks.post-attach]
88
+ run = "echo attached"
89
+
90
+ [env]
91
+ FOO = "bar"
92
+
93
+ [kube]
94
+ context = "dev-cluster"
95
+ namespace = "tools"
96
+ ```
97
+
98
+ Only quoted string values are supported. Unknown sections or keys make the
99
+ config file invalid for this run. Supported hook events are the public lifecycle
100
+ events listed above: `pre-create`, `post-create`, `pane-startup`, and
101
+ `post-attach`. Internal tmux hook names such as `after-select-pane` are rejected.
102
+ Phase C secret interpolation is not implemented; values are used literally.
103
+
104
+ `[hooks.<event>] run` executes through `sh -c` with the same timeout, logging,
105
+ environment, and failure model as file hooks. For `pane-startup`, stdout is
106
+ captured as the pane command.
107
+
108
+ `[startup] run` is a direct shorthand for a startup pane command. It is not
109
+ executed as shell by the hook runner; the string itself is sent to the new pane.
110
+ When both `[hooks.pane-startup] run` and `[startup] run` are present,
111
+ `[startup] run` wins because it is applied last.
112
+
113
+ `[env]` values are added to hook process environments and to newly-created tmux
114
+ session environments. For new sessions, projmux passes them to
115
+ `tmux new-session` as sorted `-e KEY=VALUE` arguments before the first pane is
116
+ created, so the initial shell and `[startup]` command can see them. projmux also
117
+ refreshes the tmux session environment with `tmux set-environment -t <session>
118
+ KEY VALUE` after creation for later panes. projmux's reserved `PROJMUX_*` hook
119
+ variables are appended after `[env]` in hook process environments, so the hook
120
+ contract cannot be overridden by project config.
121
+
122
+ `[kube] context` and `namespace` are reflected as hook and session environment:
123
+ `PROJMUX_KUBE_CONTEXT`, `KUBE_CONTEXT`, `PROJMUX_KUBE_NAMESPACE`, and
124
+ `KUBE_NAMESPACE`. projmux does not synthesize kubeconfig files from these two
125
+ values.
126
+
127
+ ## Trust Model
128
+
129
+ Global hooks under `$XDG_CONFIG_HOME` are prompt-free.
130
+
131
+ Project-local hooks and `.projmux/config.toml` are gated by trust-on-first-use
132
+ for every user-facing hook event in this file. A repository hook file or config
133
+ file must be approved before projmux runs or applies it. Approving "always"
134
+ records the file content hash in:
135
+
136
+ ```text
137
+ ${XDG_STATE_HOME:-$HOME/.local/state}/projmux/trusted-projects.json
138
+ ```
139
+
140
+ The trust key is the absolute repository path and each executable hook or config
141
+ path is stored relative to that repository. Each project entry has a
142
+ `trusted_at` timestamp and a `files` map of relative paths to SHA-256 hashes.
143
+ When file content changes, projmux asks again and shows the old and new SHA-256
144
+ hashes. In non-interactive contexts such as tmux run-shell or CI, untrusted or
145
+ changed project-local files fail closed with a warning.
146
+
147
+ Set `PROJMUX_PROJECT_HOOKS=off` to disable project-local hook discovery
148
+ entirely. Project-local hooks can also be disabled from `projmux settings`
149
+ under Labs. The global hook still runs either way.
150
+
151
+ ## Pane Startup
152
+
153
+ `pane-startup` runs only for the initial pane created with a new
154
+ projmux-managed session. It runs after `post-create` so `post-create` can seed
155
+ session-level tmux environment before the startup command is sent. It does not
156
+ run when projmux attaches to an existing session or target.
157
+
158
+ Before running `pane-startup`, projmux polls tmux `pane_current_command` for the
159
+ new pane and waits until it reports a shell command. This avoids fixed sleeps as
160
+ the primary readiness mechanism.
161
+
162
+ The hook's trimmed stdout is treated as the command to send into the new pane:
163
+
164
+ ```text
165
+ tmux send-keys -t <pane-id> <command> Enter
166
+ ```
167
+
168
+ Empty stdout is a no-op. Hook stderr is still forwarded to projmux's stderr with
169
+ the `[pane-startup] ` prefix. If both global and project-local hooks emit a
170
+ command, the project-local command wins because project hooks run after global
171
+ hooks.
172
+
173
+ ## Pre Create Abort
174
+
175
+ `pre-create` runs before `tmux new-session` on creation paths. A non-zero exit,
176
+ exec error, or timeout aborts the session creation. If a global `pre-create`
177
+ hook aborts, the project-local `pre-create` hook does not run.
40
178
 
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.
179
+ This is the only Phase A hook event that can block creation.
45
180
 
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.
181
+ ## Post Attach
49
182
 
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.
183
+ `post-attach` is supported only when projmux is already running inside tmux and
184
+ uses `switch-client` to move the current client to an existing session or
185
+ target. Outside tmux, `tmux attach-session` blocks until the user detaches, so
186
+ projmux does not run `post-attach` for outside-tmux attach paths in this phase.
53
187
 
54
188
  ## Environment
55
189
 
56
- The hook inherits projmux's environment, plus:
190
+ Hooks inherit projmux's environment, plus:
57
191
 
58
192
  | Variable | Always set | Description |
59
193
  | --- | --- | --- |
60
194
  | `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` |
195
+ | `PROJMUX_CWD` | yes | lifecycle working directory; for creation events this is the new session directory |
196
+ | `PROJMUX_SESSION_KIND` | yes | `persistent` or `ephemeral` for creation events; empty for `post-attach` |
63
197
  | `PROJMUX_VERSION` | yes | projmux version string |
64
198
  | `PROJMUX_SOCKET` | only if projmux used `tmux -L <socket>` | tmux socket name |
199
+ | `PROJMUX_PANE` | only for pane events | tmux pane id such as `%7` |
65
200
 
66
201
  ## Examples
67
202
 
68
- ### Global stub
203
+ ### Global Post Create Stub
69
204
 
70
205
  ```bash
71
206
  #!/usr/bin/env bash
72
207
  echo "session=$PROJMUX_SESSION cwd=$PROJMUX_CWD kind=$PROJMUX_SESSION_KIND"
73
208
  ```
74
209
 
75
- ### Project-local stub
210
+ ### Project Pane Startup Command
76
211
 
77
212
  ```bash
78
213
  mkdir -p .projmux
79
- cat > .projmux/post-create <<'EOF'
214
+ cat > .projmux/pane-startup <<'EOF'
80
215
  #!/usr/bin/env bash
81
- echo "project hook for $PROJMUX_CWD"
216
+ echo "git status --short"
82
217
  EOF
83
- chmod +x .projmux/post-create
218
+ chmod +x .projmux/pane-startup
84
219
  ```
85
220
 
86
- ### Per-session GH_TOKEN by repo
221
+ ### Per Session GH_TOKEN By Repo
87
222
 
88
223
  ```bash
89
224
  #!/usr/bin/env bash
@@ -105,14 +240,21 @@ it does not retroactively change the current shell. Open new panes via tmux
105
240
  ## Troubleshooting
106
241
 
107
242
  - **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
243
+ (`ls -l ~/.config/projmux/hooks/<event>`) or the project hook
244
+ (`ls -l .projmux/<event> .projmux/hooks/<event>`). A missing bit makes
245
+ projmux skip hook files silently by design. `.projmux/config.toml` does not
246
+ need an execute bit.
247
+ - **`project hook ... requires trust; skipping in non-interactive context`** or
248
+ **`project config ... requires trust; skipping in non-interactive context`.**
249
+ Run the same projmux command from an interactive terminal to approve the file,
250
+ or set `PROJMUX_PROJECT_HOOKS=off` if project-local execution should be
251
+ disabled.
252
+ - **`projmux: <event> hook: ... timed out after 5s`.** Long-running work
112
253
  belongs in a backgrounded child (`(slow-thing &) >/dev/null 2>&1`). The hook
113
254
  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.
255
+ - **`projmux: <event> hook: hook ... exited with status N`.** The script
256
+ returned non-zero. For `pre-create`, creation aborts; for other events,
257
+ projmux logs once and moves on.
258
+ - **Lines appear with `[post-create] `, `[pre-create] `, or `[post-attach] `
259
+ prefixes.** Expected; hook stdout/stderr are multiplexed into projmux's
260
+ stderr stream. `pane-startup` stdout is captured as the pane command instead.
package/docs/install.md CHANGED
@@ -18,8 +18,20 @@ After installing, run:
18
18
  projmux doctor
19
19
  ```
20
20
 
21
- `doctor` checks that runtime tools such as `tmux` and the junegunn/fzf CLI are
22
- available and new enough.
21
+ `doctor` checks that runtime tools such as `tmux`, `git`, and `stty` are
22
+ available.
23
+
24
+ Start the tmux app with:
25
+
26
+ ```sh
27
+ projmux shell
28
+ ```
29
+
30
+ The first launch for each projmux version prints a short welcome with the
31
+ current version, detach/exit keys, core app shortcuts, and cached update status
32
+ when available. If an installer-supported update is available, pressing Enter
33
+ at the inline prompt runs `projmux update apply`; answering `n` prints that
34
+ manual command and continues into the shell.
23
35
 
24
36
  ## Runtime Tools
25
37
 
@@ -27,7 +39,6 @@ Normal use needs:
27
39
 
28
40
  - Node.js and npm for the npm install channel.
29
41
  - tmux 3.4 or newer.
30
- - fzf 0.65.0 or newer.
31
42
 
32
43
  Useful optional tools:
33
44