projmux 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/agent-workflow.md +19 -14
- package/docs/architecture.md +2 -2
- package/docs/cli.md +147 -51
- package/docs/configuration.md +4 -3
- package/docs/globalization.md +1 -1
- package/docs/keybindings.md +5 -2
- package/docs/native-picker.md +103 -0
- package/docs/operational-diagnostics.md +47 -6
- package/docs/pr-guideline.md +1 -1
- package/docs/resource-attribution.md +61 -4
- package/docs/settings-ia.md +2 -2
- package/docs/statusbar.md +16 -5
- package/docs/tmux-surface-inventory.md +0 -1
- package/docs/upgrading.md +21 -0
- package/docs/usage-tracking.md +63 -19
- package/package.json +5 -5
- package/docs/native-picker-no-fzf-poc.md +0 -227
- package/docs/native-picker-parity.md +0 -155
- package/docs/picker-ui-plan.md +0 -91
package/docs/globalization.md
CHANGED
package/docs/keybindings.md
CHANGED
|
@@ -194,7 +194,7 @@ Settings > Keybindings stays a discovery surface. It must continue to expose
|
|
|
194
194
|
launch toggles, sidebar keymap actions, picker-local actions, pane switching,
|
|
195
195
|
window switching, and rename actions. The basic Settings flow is not the
|
|
196
196
|
terminal remediation surface: key-role replacement, disable-default, typed
|
|
197
|
-
fallback
|
|
197
|
+
fallback and terminal mapping preview/apply rows stay out of
|
|
198
198
|
the action detail.
|
|
199
199
|
|
|
200
200
|
The product model does not support `UserN` or `CSI-u` as fallback guidance.
|
|
@@ -242,7 +242,10 @@ Settings is the default apply path for key edits: it writes the key list,
|
|
|
242
242
|
refreshes the generated config, and reloads the running tmux session when
|
|
243
243
|
possible. Use `projmux tmux apply` as a CLI recovery/sync command after editing
|
|
244
244
|
the keymap file by hand, after an outside-tmux Settings save, or after resolving
|
|
245
|
-
a reported generated-config or live-reload failure.
|
|
245
|
+
a reported generated-config or live-reload failure. Generated config first
|
|
246
|
+
unbinds the known retired `C-t` pane-label chord, then installs the current
|
|
247
|
+
keymap; an explicit current `C-t` assignment therefore wins without retaining
|
|
248
|
+
the retired command body. Apply does not rewrite `keymap.toml`.
|
|
246
249
|
|
|
247
250
|
## Keymap File
|
|
248
251
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Native Picker
|
|
2
|
+
|
|
3
|
+
The native picker is the product picker for every interactive selection flow.
|
|
4
|
+
There is no runtime backend selection, saved picker selector, or external
|
|
5
|
+
picker process. `internal/ui/picker` owns the interaction contract and
|
|
6
|
+
`internal/ui/projmuxpicker` owns its visual composition.
|
|
7
|
+
|
|
8
|
+
## Product Contract
|
|
9
|
+
|
|
10
|
+
| Area | Behavior | Primary evidence |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| Items and results | Structured items keep display labels, stable values, optional search text, multi-line metadata, and action results separate. `internal/ui/pickercompat` maps older internal option/result shapes into this contract; it is not a runtime backend. | `TestPickerOptionsFromCompatPickerMapsCandidatesWhenEntriesAreEmpty`; `TestPickerOptionsFromCompatPickerPreservesTheme` |
|
|
13
|
+
| Search | Lower-case queries are case-insensitive, uppercase queries are case-sensitive, hidden values remain searchable when no explicit search key exists, explicit search-key lists preserve caller order, and simple rows receive fuzzy ranking and match highlighting. | `TestFilterItemsUsesSmartCase`; `TestFilterItemsSearchesHiddenValueWhenNoSearchKey`; `TestFilterItemsPreservesSearchKeyOrder`; `TestFilterItemsRanksBetterMatchesFirst` |
|
|
14
|
+
| Navigation | Up/Down, Ctrl-J/Ctrl-N, and Ctrl-K/Ctrl-P wrap selection; PageUp/PageDown and Home/End clamp or jump; empty lists remain safe. Ctrl-N and Ctrl-P remain real navigation keys unless a caller claims the key as a custom action. | `TestNativeInteractiveSupportsControlNavigationKeys`; `TestNativeInteractiveWrapsPreviousNavigationKeys`; `TestNativeInteractiveWrapsNextNavigationKeys`; `TestNativeInteractiveNavigationKeysAreNoopForEmptyList`; `TestNativeInteractiveJumpNavigationRemainsClamped` |
|
|
15
|
+
| Editing | Left/Right, Ctrl-A/E, Delete, Backspace, Ctrl-U/W, printable input, accept-query mode, and a visible query cursor are supported. | `TestNativeInteractiveEditsTypedQueryAtCursor`; `TestNativeInteractiveSupportsQueryLineEditingKeys`; `TestNativeInteractiveCtrlUDeletesBeforeCursor`; `TestNativeRunnerAcceptsTypedQuery` |
|
|
16
|
+
| Actions | Enter accepts, shared close actions abort, printable/control expect keys return stable action keys, command actions can refresh previews, and mutable actions can update items without restarting the picker. | `TestNativeRunnerUsesSharedCloseActions`; `TestNativeInteractiveSupportsPrintableExpectKeys`; `TestNativeInteractiveRunsCustomActionCommandAndRefreshes`; `TestNativeInteractiveCustomActionMutatesItemsAndRefreshes` |
|
|
17
|
+
| Deferred state | Deferred and event-triggered updates preserve query and selection by value, can repeat, and may explicitly choose a new focus value. | `TestNativeInteractiveDeferredUpdateTriggerRefreshesRepeatedly`; switch and notify sidebar mutable-refresh app tests |
|
|
18
|
+
| Preview | Popup previews use a right split, sidebar previews use a bottom split, control bytes and tabs are normalized before clipping, and preview scrolling/cycling rerenders in place. | `TestNativeInteractiveRendersWidePreviewBesideList`; `TestNativeInteractiveRendersDownPreviewBelowList`; `TestRenderSplitPreviewRowsNormalizesPreviewTabsBeforeTruncating`; `TestNativeInteractiveRendersPreviewOffset` |
|
|
19
|
+
| Mouse | SGR mouse input focuses on primary down, follows drag, accepts on matching release, and scrolls with the wheel. | `TestNativeInteractiveSelectsOnMouseRelease`; `TestNativeInteractiveMouseDragFollowsSelection`; `TestNativeInteractiveSupportsMouseWheelSelection` |
|
|
20
|
+
| Lifecycle | Interactive runs use the alternate screen, synchronized/coalesced frame updates, controlling-TTY fallback, and deterministic reader cleanup. | `TestNativeInteractiveUsesAlternateScreen`; `TestNativeInteractiveWrapsRedrawsInSynchronizedUpdates`; picker/setup lifecycle tests in `docs/agent-workflow.md` |
|
|
21
|
+
|
|
22
|
+
## Rendering And Popup Chrome
|
|
23
|
+
|
|
24
|
+
The renderer owns rounded frames, optional titlebars and chips, search/header
|
|
25
|
+
separators, footer placement, selected-row styling, proportional scrollbars,
|
|
26
|
+
multi-line gaps, preview geometry, ANSI restoration, and terminal-cell width for
|
|
27
|
+
CJK text, emoji, and combining marks. Frame-owned cells inherit the effective
|
|
28
|
+
theme's `surface` and `chrome_foreground` values, including padding after
|
|
29
|
+
embedded resets.
|
|
30
|
+
|
|
31
|
+
Picker popups are always borderless tmux popups (`display-popup -B`) so the
|
|
32
|
+
native renderer owns the visible frame. The popup body receives a per-command
|
|
33
|
+
style derived from the effective theme; no global popup, pane, window, shell,
|
|
34
|
+
or status style is mutated. Project sidebars use a `20%` width with a `40`
|
|
35
|
+
column minimum, notification sidebars use `24%` with a `64` column minimum,
|
|
36
|
+
and both reserve the two statusbar rows when client height is known.
|
|
37
|
+
|
|
38
|
+
Primary evidence includes `TestAppRunTmuxPopupToggleUsesBorderlessNativePopup`,
|
|
39
|
+
`TestAppRunTmuxPopupToggleUsesNativePopupBodyStyleFromGlobalTheme`,
|
|
40
|
+
`TestBuildPopupToggleAppliesNativeBodyStyle`,
|
|
41
|
+
`TestSessionizerSidebarWidthUsesCompactMinimum`,
|
|
42
|
+
`TestNotifySidebarWidthUsesProductContract`, and
|
|
43
|
+
`TestSidebarPopupHeightLeavesStatusbarRows`.
|
|
44
|
+
|
|
45
|
+
## Fuzzy Scoring Provenance
|
|
46
|
+
|
|
47
|
+
The native fuzzy scorer intentionally follows the fzf V2 dynamic-scoring
|
|
48
|
+
algorithm for the maintained reference cases in `internal/ui/picker/backend_test.go`.
|
|
49
|
+
`TestFuzzyScoreMatchesFZFV2ReferenceScores` and
|
|
50
|
+
`TestFuzzyScoreRejectsFZFV2ReferenceNonMatches` are provenance fixtures; keep
|
|
51
|
+
their expected scores stable when changing filtering internals.
|
|
52
|
+
|
|
53
|
+
## Retired Artifact Contract
|
|
54
|
+
|
|
55
|
+
The retired picker selector environment variable and saved selector filename
|
|
56
|
+
are not runtime inputs. Their presence must not cause lookup, file reads,
|
|
57
|
+
warnings, deletion, rewriting, propagation to child popups, or any change to
|
|
58
|
+
the native path. There is deliberately no alias, migration, or stale-value
|
|
59
|
+
cleanup behavior.
|
|
60
|
+
|
|
61
|
+
Coverage is split by boundary:
|
|
62
|
+
|
|
63
|
+
- `TestRunNativePickerOptionDoesNotObserveRetiredBackendArtifacts` guards the
|
|
64
|
+
direct picker path, fails if the retired env name is queried, and verifies a
|
|
65
|
+
stale file is neither deleted nor replaced.
|
|
66
|
+
- `TestAppRunSwitchUsesNativePickerWithoutBackendLookup` guards the ordinary
|
|
67
|
+
switch flow.
|
|
68
|
+
- `TestAppRunTmuxPopupToggleIgnoresRetiredBackendArtifacts` and
|
|
69
|
+
`TestBuildPopupTogglePropagatesNativePickerEnvironmentWithoutRetiredBackend`
|
|
70
|
+
guard popup chrome and child-environment construction.
|
|
71
|
+
- `test/e2e/linux-smoke.sh` launches the ordinary attached-client Settings
|
|
72
|
+
popup with stale env/file values, completes the recorder flow, verifies the
|
|
73
|
+
file inode/content are unchanged, and rejects visible compatibility output.
|
|
74
|
+
|
|
75
|
+
## Removed PoC Coverage Mapping
|
|
76
|
+
|
|
77
|
+
The retired standalone dependency sandbox and focused smoke duplicated product
|
|
78
|
+
coverage or tested a dependency policy that no longer exists. Its valid
|
|
79
|
+
behavior assertions remain covered as follows:
|
|
80
|
+
|
|
81
|
+
| Removed focused assertion | Maintained coverage |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| Settings and Labs render through the native picker | `TestSettingsUsesNativePicker`; `TestSettingsHubKeepsLabsSectionWithoutRetiredPickerChoices`; ordinary attached-client Settings recorder in `test/e2e/linux-smoke.sh` |
|
|
84
|
+
| Search chrome, smart-case filtering, native frame ownership, and full-height rendering | `TestNativeInteractiveSeparatesSearchHeaderFromList`; `TestFilterItemsUsesSmartCase`; `TestNativeInteractiveRendersBorderFrame`; `TestNativeInteractiveUsesAvailableHeightForSimpleLists` |
|
|
85
|
+
| AI Settings maps a typed selection to the saved mode | `TestAISettingsPickerSetsSelectedMode`; native picker option/result mapping tests |
|
|
86
|
+
| Switch popup/sidebar filtering, selection, preview cycling, and initial focus | `TestAppRunSwitchDefaultsToPopupAndOpensSelectedSession`; `TestSwitchCommandSupportsSidebarUI`; preview cycle and initial-position tests in `internal/app/switch_test.go` |
|
|
87
|
+
| Sessions popup filtering, selection, and preview cycling | `TestAppRunSessionsDefaultsToPopupAndOpensSelectedSession`; session popup preview/cycle tests in `internal/app/session_popup_test.go` |
|
|
88
|
+
| Notification sidebar navigation and mutable actions | `TestNotifySidebarUsesNativePicker`; notify sidebar ack/clear/deferred-refresh app tests |
|
|
89
|
+
| Launch-key close behavior and Ctrl-N/Ctrl-P navigation | `TestNativeInteractiveClosesOnMatchingLaunchCloseKey`; `TestNativeInteractiveSupportsControlNavigationKeys`; empty-list and wrap tests |
|
|
90
|
+
| Mouse selection and alternate-screen restoration | native mouse and lifecycle tests listed above |
|
|
91
|
+
| Generated shell config and the Alt-1 project-sidebar popup path | `TestShellWritesAppConfigAndRunsIsolatedTmux`; `TestAppRunTmuxPopupToggleOpensStandaloneSidebar`; popup frame/body-style tests listed above |
|
|
92
|
+
| Stale selector values do not affect the product | retained and strengthened in the unit/app/e2e negative coverage listed in the previous section |
|
|
93
|
+
|
|
94
|
+
The dependency-absence assertion is now a source-residue property rather than
|
|
95
|
+
a separate container scenario: production code contains no selector, resolver,
|
|
96
|
+
saved-selector access, propagation, or external picker launch path.
|
|
97
|
+
|
|
98
|
+
## Maintenance
|
|
99
|
+
|
|
100
|
+
Update this document and the maintained list in
|
|
101
|
+
[`docs/agent-workflow.md`](agent-workflow.md) whenever picker behavior changes
|
|
102
|
+
coverage level, gains a new product flow, or changes input/render/action
|
|
103
|
+
semantics.
|
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Projmux records a small local-only operational journal so command failures and
|
|
4
4
|
state changes can be inspected after the originating process exits. It does
|
|
5
|
-
not upload the journal,
|
|
6
|
-
|
|
5
|
+
not upload the journal, contact an issue tracker, or provide a background
|
|
6
|
+
telemetry service. A support archive is created only by an explicit
|
|
7
|
+
`projmux diagnostics report` invocation and is never transmitted.
|
|
7
8
|
|
|
8
9
|
## Safe event contract
|
|
9
10
|
|
|
10
11
|
Each JSONL record has a closed schema: `at`, `level`, `component`, `event`,
|
|
11
12
|
`result`, `duration_ms`, `run_id`, `version`, `mux_backend`, and optional
|
|
12
13
|
allowlisted `command`, `subcommand`, `kind`, and sanitized `message`. There is
|
|
13
|
-
no generic metadata map.
|
|
14
|
+
no generic metadata map. Runtime lifecycle records add only closed
|
|
15
|
+
`operation` and `code` enums. The allowed operations are session create,
|
|
16
|
+
attach, switch, kill, and tmux apply; codes are stable failure/health
|
|
17
|
+
classifications and never carry routing identity or subprocess details.
|
|
14
18
|
|
|
15
19
|
Command and subcommand names come from static allowlists. Unknown argv values,
|
|
16
20
|
paths, flags, and arguments are dropped. Messages have control/format
|
|
@@ -25,6 +29,28 @@ pane captures/output/title/topic/content, transcripts, raw hook payloads,
|
|
|
25
29
|
configuration secrets, or arbitrary environment values. Phase 0 also does not
|
|
26
30
|
add session/window/pane or other routing identifiers.
|
|
27
31
|
|
|
32
|
+
One explicit state-changing command owns at most one lifecycle pair. Its
|
|
33
|
+
`lifecycle.start` and `lifecycle.outcome` share the process `run_id`, and a
|
|
34
|
+
composite create-then-attach/switch flow keeps the first real mutation as its
|
|
35
|
+
operation instead of recording nested outcomes. Lifecycle ownership replaces
|
|
36
|
+
the generic top-level `command.outcome`; it never duplicates it. Start/outcome
|
|
37
|
+
append failures are ignored and do not change the command result.
|
|
38
|
+
|
|
39
|
+
The diagnostics package exposes a typed `ReadRuntimeHealth` projection for
|
|
40
|
+
read-only Doctor consumers. It reports the fixed `tmux` backend, latest
|
|
41
|
+
socket/apply state, and a bounded tail/count of safe failures using only
|
|
42
|
+
`Store.ReadOnly`; it does not create, chmod, lock, truncate, apply, restart, or
|
|
43
|
+
repair anything. Doctor schema 2 consumes that seam for its `logs` findings
|
|
44
|
+
and adds one fixed-argv, one-second `tmux -L projmux show-options` probe for
|
|
45
|
+
actual socket/config health. The probe neither generates nor applies config.
|
|
46
|
+
Its captured output is capped at 4 KiB. Doctor reads only a pre-existing
|
|
47
|
+
regular generated config (at most 1 MiB) without following symlinks, and the
|
|
48
|
+
shared read-only journal seam rejects non-regular inputs and files above 5 MiB.
|
|
49
|
+
These conditions degrade to typed findings rather than blocking or repairing
|
|
50
|
+
the source. Windows ACL privacy is reported as unverified because `os.FileMode`
|
|
51
|
+
cannot prove it; a separate finding preserves the metadata-only writability
|
|
52
|
+
result, and Doctor does not modify ACLs.
|
|
53
|
+
|
|
28
54
|
## Storage and retention
|
|
29
55
|
|
|
30
56
|
The path is
|
|
@@ -54,9 +80,13 @@ arm`, `attention clear`, `attention window`, `tmux autosave-session-state`, and
|
|
|
54
80
|
to the journal; an error from any of them still records exactly one safe error
|
|
55
81
|
outcome. Explicit user mutations such as `attention toggle` retain their
|
|
56
82
|
state-changing success record. Direct top-level help and explicit preview-only intents (`upgrade
|
|
57
|
-
--dry-run`, `update apply --dry-run`,
|
|
58
|
-
|
|
59
|
-
|
|
83
|
+
--dry-run`, `update apply --dry-run`, AI integration dry-runs, and the
|
|
84
|
+
currently preview-only session restore) are also read-only. Doctor is a stricter
|
|
85
|
+
boundary: successes and errors never append to this journal, so diagnostics do
|
|
86
|
+
not make its filesystem contract self-defeating. Support report success and
|
|
87
|
+
errors likewise never append; its strict reader shares the viewer's tolerant
|
|
88
|
+
decoder but never creates/locks/chmods/repairs/truncates the source journal.
|
|
89
|
+
Multi-mode commands such as AI status/topic,
|
|
60
90
|
terminal apply, snapshot delete, update check, and welcome popup inspect only
|
|
61
91
|
allowlisted mode/flag names; boolean `=false` values retain mutation-capable
|
|
62
92
|
classification, and no flag values are ever recorded. Help-looking tokens
|
|
@@ -78,3 +108,14 @@ recursion loop.
|
|
|
78
108
|
The older bounded `ai-ingest.log` and subsystem-specific `PROJMUX_*_DEBUG`
|
|
79
109
|
surfaces retain their current paths, formats, and behavior. They are not
|
|
80
110
|
migrated by this foundation.
|
|
111
|
+
|
|
112
|
+
## Explicit support report
|
|
113
|
+
|
|
114
|
+
`projmux diagnostics report [--output <path>]` previews and then atomically
|
|
115
|
+
publishes a private local `tar.gz`; see [cli.md](cli.md#diagnostics). The
|
|
116
|
+
manifest records report schema version 2, `default-hash-v1` redaction, every
|
|
117
|
+
included entry, and stable missing/corrupt/permission omission reasons. Doctor
|
|
118
|
+
JSON schema version 2 and the bounded operations decoder are reused rather than
|
|
119
|
+
duplicated. AI ingest contributes count-only allowlisted source/result rows,
|
|
120
|
+
never raw legacy lines. Existing output files survive collisions and partial
|
|
121
|
+
temporary archives are removed.
|
package/docs/pr-guideline.md
CHANGED
|
@@ -24,7 +24,7 @@ feat(ai): add codex split picker keybinding
|
|
|
24
24
|
fix(ai): prepend agent bin dir to PATH so node-managed CLIs find node
|
|
25
25
|
docs(readme): drop Releases and Configuration sections
|
|
26
26
|
chore: bump release-please manifest to 0.3.0
|
|
27
|
-
refactor(picker):
|
|
27
|
+
refactor(picker): simplify native picker bootstrap code
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Rules:
|
|
@@ -90,11 +90,18 @@ The opt-in read-only test below observes an existing socket and emits counts
|
|
|
90
90
|
only. It neither captures pane content nor reads process command lines.
|
|
91
91
|
|
|
92
92
|
```text
|
|
93
|
-
PROJMUX_RESOURCE_TMUX_SOCKET=projmux
|
|
93
|
+
PROJMUX_RESOURCE_TMUX_SOCKET=projmux \
|
|
94
|
+
PROJMUX_RESOURCE_EXPECT_PROJECT_ROOT=/path/to/project go test \
|
|
94
95
|
-run TestResourceAttributionRealTmuxReadOnlySmoke -v \
|
|
95
96
|
./internal/integrations/tmux
|
|
96
97
|
```
|
|
97
98
|
|
|
99
|
+
When `PROJMUX_RESOURCE_EXPECT_PROJECT_ROOT` is set, the smoke additionally
|
|
100
|
+
requires at least one blank explicit anchor to resolve from its pane current
|
|
101
|
+
path and requires the resulting project bucket to contain a pane. Output stays
|
|
102
|
+
bounded to the expected project path and aggregate counts; it does not emit
|
|
103
|
+
session names, pane content, prompts, transcripts, or process command lines.
|
|
104
|
+
|
|
98
105
|
2026-08-12 result: `panes=8`, `pane_pid_eq_sid=8`, `missing_pids=0`,
|
|
99
106
|
`attributed_processes=13`, `escaped_boundary=0`, `sampled=474`, `skipped=0`,
|
|
100
107
|
`race=0`, `permission=0`, `status=ready`. A separate tmux format-only count
|
|
@@ -119,6 +126,18 @@ PROJMUX_RESOURCE_TRANSIENT_SMOKE=1 go test \
|
|
|
119
126
|
The pane shell remained attributed while its real `setsid` child was counted
|
|
120
127
|
at the escaped/Other boundary, without reading the child command line.
|
|
121
128
|
|
|
129
|
+
The current-path fallback itself has a separate isolated real-tmux smoke. It
|
|
130
|
+
starts with inherited `TMUX`/`TMUX_PANE` removed, uses a dedicated
|
|
131
|
+
`TMUX_TMPDIR` plus `-L` socket, verifies the actual socket path is below that
|
|
132
|
+
temporary root before exact cleanup, and confirms the blank tmux project
|
|
133
|
+
option remains blank after in-memory attribution:
|
|
134
|
+
|
|
135
|
+
```text
|
|
136
|
+
PROJMUX_RESOURCE_PROJECT_FALLBACK_SMOKE=1 go test \
|
|
137
|
+
-run TestResourceProjectFallbackTransientSmoke -v \
|
|
138
|
+
./internal/integrations/tmux
|
|
139
|
+
```
|
|
140
|
+
|
|
122
141
|
## Phase 1 inspector
|
|
123
142
|
|
|
124
143
|
The popup retains warming/partial/unavailable and overage states, renders RSS
|
|
@@ -127,6 +146,44 @@ samples when it closes. Its non-overlapping default cadence is two seconds;
|
|
|
127
146
|
Ctrl-R shares the same scan gate. Selection and query survive refresh by stable
|
|
128
147
|
row identity, while a vanished row clamps to the nearest valid neighbor.
|
|
129
148
|
Display labels use label → agent topic → known interactive shell → raw title,
|
|
130
|
-
but those values never become ownership keys.
|
|
131
|
-
|
|
132
|
-
|
|
149
|
+
but those values never become ownership keys. Pane rows and detail reuse that
|
|
150
|
+
identity plus the tmux current command, PID/SID, pane id, and TTY; pane rows
|
|
151
|
+
show attributed process counts while project/window rows retain pane counts.
|
|
152
|
+
Right/Enter move forward, Left moves back (and is a root no-op), and Esc closes
|
|
153
|
+
at every depth. Unsupported platforms show an unavailable reason, not zero
|
|
154
|
+
metrics. PSS, non-Linux collectors, process-list drill-down, history, and
|
|
155
|
+
resource mutation remain outside this contract.
|
|
156
|
+
|
|
157
|
+
Host and attributed CPU/memory use the same semantic classifier as the live
|
|
158
|
+
statusbar: CPU is normal below 70%, warning at 70–89.9%, and critical at 90%
|
|
159
|
+
or above; memory is normal below 75%, warning at 75–89.9%, and critical at 90%
|
|
160
|
+
or above. Values retain the resolved semantic role but omit visible severity
|
|
161
|
+
words. Unknown is rendered as `--`, never as zero; Sample lifecycle and
|
|
162
|
+
freshness remain explicit text.
|
|
163
|
+
|
|
164
|
+
The first paint is a non-actionable warming surface. Completed samples report
|
|
165
|
+
age and fresh/stale state; partial and overage callouts stay bounded to counts
|
|
166
|
+
and aggregate values. Empty and gone scopes are read-only and explain what the
|
|
167
|
+
latest complete sample can no longer open. Automatic refresh runs every two
|
|
168
|
+
seconds; Ctrl-R reports in-progress state while retaining the last complete
|
|
169
|
+
sample. Both paths preserve scope, breadcrumb, query, selection, and the row
|
|
170
|
+
order last computed by Tab. The default order is Name; Tab computes CPU,
|
|
171
|
+
Memory, or Name once from the current sample, while later refreshes update row
|
|
172
|
+
values without silently moving focus. Native synchronized frame diffs repaint
|
|
173
|
+
only changed rows and update state/footer chrome together.
|
|
174
|
+
|
|
175
|
+
The live summary is a fixed five-row bottom dock below the search/list surface:
|
|
176
|
+
one renderer-owned theme-aware divider, then Host, Attributed, Coverage, and
|
|
177
|
+
Sample. It does not scroll or filter with rows. Coverage owns the non-drillable
|
|
178
|
+
Other or current-scope empty/gone explanation; bounded partial/overage details
|
|
179
|
+
stay on Sample. The action footer remains below the dock with its own chrome
|
|
180
|
+
boundary, so diagnostic values and key hints never share a role. The 80x24
|
|
181
|
+
layout retains a navigable list viewport without clipping, border bleed, or a
|
|
182
|
+
second dock divider.
|
|
183
|
+
|
|
184
|
+
Project rows label project paths explicitly. The two attribution buckets keep
|
|
185
|
+
their stable core keys but display `No project match` and `Multiple project
|
|
186
|
+
matches` with bounded explanations. Pane primary identity follows the shared
|
|
187
|
+
label → agent-only AI topic → interactive shell → raw title resolver; pane id,
|
|
188
|
+
process id, and TTY remain labeled secondary details and stable keys are
|
|
189
|
+
unchanged.
|
package/docs/settings-ia.md
CHANGED
|
@@ -85,8 +85,8 @@ view-first layout:
|
|
|
85
85
|
`${XDG_CONFIG_HOME:-$HOME/.config}/projmux/sidebar-startup-picker`.
|
|
86
86
|
- `Settings > Labs` contains only Live system resources and Project Hooks.
|
|
87
87
|
Keybindings live at `Settings > Keybindings`; Labs has no visible or hidden
|
|
88
|
-
keybindings redirect.
|
|
89
|
-
render picker source
|
|
88
|
+
keybindings redirect. The native picker is the product picker, so Labs does
|
|
89
|
+
not render picker source information.
|
|
90
90
|
- `Settings > Labs > Live system resources` is a direct global on/off toggle
|
|
91
91
|
for the macOS/Linux/WSL lower-status-row `CPU N% MEM N%` segment. It defaults
|
|
92
92
|
off, updates live tmux state when toggled, and renders unavailable on
|
package/docs/statusbar.md
CHANGED
|
@@ -68,7 +68,8 @@ row 1 [#S] #{pane_current_path} ⎈ <ctx>/<ns> <git> CPU 12% MEM 41%
|
|
|
68
68
|
without dominating the status row. Window tab indexes stay left of each tab,
|
|
69
69
|
and tab titles are centered in a fixed-width trim so long active pane names
|
|
70
70
|
do not resize the status row.
|
|
71
|
-
- `Settings > Labs > Live system resources` adds the compact
|
|
71
|
+
- `Settings > Labs > Live system resources` adds the compact
|
|
72
|
+
`CPU N% MEM N%`
|
|
72
73
|
segment between git and the clock on macOS, Linux, and WSL. It is global,
|
|
73
74
|
default off, and updates with tmux's existing five-second status interval.
|
|
74
75
|
CPU and memory are host-scoped telemetry, not pane, window, project, or
|
|
@@ -76,8 +77,13 @@ row 1 [#S] #{pane_current_path} ⎈ <ctx>/<ns> <git> CPU 12% MEM 41%
|
|
|
76
77
|
normal below 70%, warning at 70–89%, and critical at 90% or above; memory is
|
|
77
78
|
normal below 75%, warning at 75–89%, and critical at 90% or above. Normal and
|
|
78
79
|
unavailable (`--`) values use the secondary status-text role, warnings use
|
|
79
|
-
the warning role, and critical values use the bold critical role.
|
|
80
|
-
value
|
|
80
|
+
the warning role, and critical values use the bold critical role. Severity
|
|
81
|
+
words are omitted. Each percent value, including `%`, occupies one fixed
|
|
82
|
+
four-column slot (` 9%`, ` 15%`, `100%`, or ` --%`), so styling or changing
|
|
83
|
+
either metric cannot move the following segment. Styling one value never
|
|
84
|
+
promotes the other value. The Resource Inspector uses this same classifier
|
|
85
|
+
and semantic roles for host and attributed CPU/memory while rendering
|
|
86
|
+
unavailable metrics as `--` without severity suffixes.
|
|
81
87
|
Linux CPU is the aggregate delta from `/proc/stat`; memory is
|
|
82
88
|
`(MemTotal - MemAvailable) / MemTotal` from `/proc/meminfo`. macOS CPU uses
|
|
83
89
|
the aggregate Mach host tick delta; memory is total physical memory minus
|
|
@@ -155,7 +161,11 @@ Antigravity rows keep conversation-local `context` separate from account
|
|
|
155
161
|
`quota/<exact upstream bucket ID>` rows; the popup displays an absolute reset
|
|
156
162
|
when provided and otherwise the exact optional relative reset seconds. Opaque
|
|
157
163
|
bucket IDs are escaped for terminal/tmux safety and are never assigned a
|
|
158
|
-
`5h`/`weekly` cadence.
|
|
164
|
+
`5h`/`weekly` cadence. Claude retains aggregate `5h`/`weekly` rows alongside
|
|
165
|
+
typed named/model `limits[]` rows in this popup: model-scoped rows display the
|
|
166
|
+
exact upstream group plus model display identity with a bounded terminal-safe
|
|
167
|
+
label, reset, and per-row age. The compact status line excludes every Claude
|
|
168
|
+
named/model row and continues to use only the aggregate official windows.
|
|
159
169
|
Session State inspection lives under `Projects > Sessions > State`; global
|
|
160
170
|
Settings > Session State is settings-only and the statusbar no longer exposes a
|
|
161
171
|
duplicate State button.
|
|
@@ -169,7 +179,8 @@ does not leave terminal key state behind. The usage popup uses the same
|
|
|
169
179
|
single-payload print and plain Enter-close pattern. It shows the authoritative
|
|
170
180
|
last collect timestamp when present, falls back to the cache file mtime when
|
|
171
181
|
needed, and keeps stale sync metadata muted instead of escalating it to a
|
|
172
|
-
warning color.
|
|
182
|
+
warning color. Percent-only named rows do not synthesize `USED`, `LIMIT`, or
|
|
183
|
+
`LEFT` counts.
|
|
173
184
|
The notification HUD detail surface opens the right-side notification popup
|
|
174
185
|
through the notify sidebar action, showing the grouped pane/session inbox with
|
|
175
186
|
collapsed group rows and the same attention-tinted title. When notification
|
package/docs/upgrading.md
CHANGED
|
@@ -40,6 +40,27 @@ still requires an explicit `PROJMUX_INSTALLER=github-release`.
|
|
|
40
40
|
|
|
41
41
|
## Behavior Changes
|
|
42
42
|
|
|
43
|
+
### Terminal init command removed
|
|
44
|
+
|
|
45
|
+
The deprecated top-level `projmux init` command and its legacy-only
|
|
46
|
+
`--dry-run` flag have been removed. Use the exact replacement
|
|
47
|
+
`projmux setup terminal`; it previews by default, and accepts `--apply`,
|
|
48
|
+
`--config <path>`, and `--allow-symlink` when those behaviors are needed.
|
|
49
|
+
|
|
50
|
+
### Pane rename keymap action ID removed
|
|
51
|
+
|
|
52
|
+
The deprecated `rename-pane-topic` keybinding action ID has been removed. If
|
|
53
|
+
`~/.config/projmux/keymap.toml` still contains
|
|
54
|
+
`[bindings.rename-pane-topic]`, rename that table to
|
|
55
|
+
`[bindings.rename-pane-label]` before running Settings or
|
|
56
|
+
`projmux tmux apply`. Projmux now rejects the stale table with that exact
|
|
57
|
+
replacement instead of silently applying its keys to the user-label action.
|
|
58
|
+
|
|
59
|
+
This removal does not change `projmux ai topic set/clear`: those advanced CLI
|
|
60
|
+
commands continue to write only AI topic and manual-ownership state. User pane
|
|
61
|
+
rename continues to write only the pane label, and raw pane title remains an
|
|
62
|
+
independent fallback.
|
|
63
|
+
|
|
43
64
|
### Theme is now global-only
|
|
44
65
|
|
|
45
66
|
Theme is a global user preference. The effective theme resolves from the global
|
package/docs/usage-tracking.md
CHANGED
|
@@ -11,12 +11,17 @@ requests such as `projmux usage --model claude`, `--model codex`, or
|
|
|
11
11
|
still collect and render that provider even when it is disabled.
|
|
12
12
|
|
|
13
13
|
Claude and Codex adapters read the upstream's own account view. Antigravity
|
|
14
|
-
reads only the official managed statusline payload: `context_window` remains
|
|
15
|
-
conversation-local
|
|
16
|
-
account row. Projmux preserves the upstream bucket ID and
|
|
17
|
-
undocumented ID means `5h` or `weekly`. It does not infer
|
|
18
|
-
timestamps, or account limits from screen scraping,
|
|
19
|
-
OAuth/cache files, or binary strings.
|
|
14
|
+
reads only the official managed statusline payload: `context_window` remains
|
|
15
|
+
private conversation-local diagnostic metadata, while each valid `quota` map
|
|
16
|
+
entry is a separate account row. Projmux preserves the upstream bucket ID and
|
|
17
|
+
never guesses that an undocumented ID means `5h` or `weekly`. It does not infer
|
|
18
|
+
quota, cadence, reset timestamps, or account limits from screen scraping,
|
|
19
|
+
tokens, history, OAuth/cache files, or binary strings.
|
|
20
|
+
|
|
21
|
+
Claude keeps the canonical aggregate `five_hour` and `seven_day` rows and also
|
|
22
|
+
preserves structurally valid typed `limits[]` rows as named account quotas for
|
|
23
|
+
inspection surfaces. These named rows never participate in the ambient status
|
|
24
|
+
projection.
|
|
20
25
|
|
|
21
26
|
## Adapters
|
|
22
27
|
|
|
@@ -45,6 +50,23 @@ floor.
|
|
|
45
50
|
- A clean 200 resets the consecutive counter.
|
|
46
51
|
- `--force` (BackoffResetter) clears the persisted state and attempts
|
|
47
52
|
the call regardless of streak.
|
|
53
|
+
- Canonical `five_hour` and `seven_day` blocks remain `5h` and `weekly`.
|
|
54
|
+
Each valid typed `limits[]` row becomes `window=quota` with the exact opaque
|
|
55
|
+
`group` copied to `bucket`. The snapshot also preserves `kind`, `severity`,
|
|
56
|
+
`is_active`, and nullable `scope`, model ID, and surface metadata; percent and
|
|
57
|
+
reset remain the authoritative common snapshot fields.
|
|
58
|
+
- A model-scoped quota renders as `quota/<group> · <model display name>` in
|
|
59
|
+
text and popup inspection. Control characters and tmux format introducers are
|
|
60
|
+
escaped and the display label is bounded, while the stored identity remains
|
|
61
|
+
byte-for-byte unchanged. No model-family inference, aliasing, aggregation, or
|
|
62
|
+
percentage-to-count derivation is performed.
|
|
63
|
+
- `limits[]` is capped at 64 rows and required field presence/types are checked
|
|
64
|
+
explicitly. A malformed container or row fails that adapter collection so the
|
|
65
|
+
manager retains the complete last-known-good Claude slice. A valid
|
|
66
|
+
aggregate-only response succeeds and therefore removes obsolete named rows.
|
|
67
|
+
- Null legacy top-level model hints and unknown experiment keys are ignored.
|
|
68
|
+
Billing/credit blocks such as `extra_usage` and `spend` are not ingested or
|
|
69
|
+
rendered.
|
|
48
70
|
|
|
49
71
|
### Codex (`internal/core/usage/adapters/codex`)
|
|
50
72
|
|
|
@@ -74,12 +96,12 @@ read.
|
|
|
74
96
|
|
|
75
97
|
Local managed-statusline sidecars. No network or credential reads.
|
|
76
98
|
|
|
77
|
-
- `context_window.used_percentage`
|
|
78
|
-
|
|
79
|
-
percentage remains a
|
|
99
|
+
- `context_window.used_percentage` and its conversation ID remain in the
|
|
100
|
+
private context sidecar for hook/notify diagnostics. They do not become
|
|
101
|
+
Usage snapshots. The legacy string percentage remains a writer fallback.
|
|
80
102
|
- The official `quota` map is sorted by its exact bucket ID. Each valid bucket
|
|
81
103
|
becomes `window=quota`, `bucket=<upstream ID>` and renders as
|
|
82
|
-
`quota/<upstream ID>`
|
|
104
|
+
`quota/<upstream ID>` on account-inspection surfaces.
|
|
83
105
|
- Used percent is `100 * (1 - remaining_fraction)`. Non-finite or values
|
|
84
106
|
outside `[0,1]`, empty IDs, null/disabled entries, and negative relative
|
|
85
107
|
resets are ignored safely.
|
|
@@ -89,8 +111,8 @@ Local managed-statusline sidecars. No network or credential reads.
|
|
|
89
111
|
- Context and quota use independent private sidecars. A context-only payload
|
|
90
112
|
does not erase the last quota observation. An explicit empty/null quota map
|
|
91
113
|
records no buckets; the manager's existing rule still preserves prior model
|
|
92
|
-
rows when an adapter returns zero total rows
|
|
93
|
-
|
|
114
|
+
rows when an adapter returns zero total rows. Context never participates in
|
|
115
|
+
that account-row replacement decision.
|
|
94
116
|
|
|
95
117
|
## Snapshot store
|
|
96
118
|
|
|
@@ -101,7 +123,9 @@ ${PROJMUX_USAGE_STATE_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/projmux/usage}/
|
|
|
101
123
|
JSON document keyed by adapter, recording:
|
|
102
124
|
|
|
103
125
|
- per-window `Snapshot{Model, Window, Bucket, Pct, Limit, ResetsAt,
|
|
104
|
-
ResetInSeconds, UpdatedAt}`; `Bucket` is populated only for
|
|
126
|
+
ResetInSeconds, UpdatedAt, NamedQuota}`; `Bucket` is populated only for
|
|
127
|
+
`window=quota`, and `NamedQuota` is populated only when an upstream typed
|
|
128
|
+
named-quota contract supplies the metadata
|
|
105
129
|
- per-adapter `last_collect` timestamp (drives the throttle)
|
|
106
130
|
- per-adapter `Backoff{Until, Consecutive}` (drives the cooldown)
|
|
107
131
|
|
|
@@ -126,8 +150,8 @@ Enabled agents, filters by window, and renders the tab-aligned table:
|
|
|
126
150
|
```
|
|
127
151
|
MODEL WINDOW PCT RESETS_AT RESET_IN STALE
|
|
128
152
|
claude 5h 80% 2026-05-07T14:00:00+09:00 -
|
|
129
|
-
antigravity context 14% - -
|
|
130
153
|
antigravity quota/gemini-weekly 6% 2026-07-06T16:50:32+09:00 560580s
|
|
154
|
+
claude quota/group-redacted · Model Redacted Alpha 38% 2031-02-03T15:05:06+09:00 - *
|
|
131
155
|
```
|
|
132
156
|
|
|
133
157
|
`STALE` is `*` when `now - UpdatedAt > 10m`. A `--json` payload returns
|
|
@@ -144,9 +168,11 @@ provider rows and prints a short Settings hint. `--json` returns an
|
|
|
144
168
|
empty array. Explicit `--model claude`, `--model codex` and
|
|
145
169
|
`--model antigravity` bypass the enabled-agent filter for read-only
|
|
146
170
|
inspection and collect/render only the requested adapter. Antigravity reports
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
`--window weekly` never matches an opaque quota bucket named
|
|
171
|
+
zero or more account `quota/<bucket-id>` rows. Legacy cached `window=context`
|
|
172
|
+
rows are suppressed in text and JSON output. `--window quota` selects only
|
|
173
|
+
account buckets; `--window weekly` never matches an opaque quota bucket named
|
|
174
|
+
`weekly`. `--window context` remains an accepted compatibility filter and
|
|
175
|
+
returns no Usage rows.
|
|
150
176
|
|
|
151
177
|
### `projmux status usage`
|
|
152
178
|
|
|
@@ -162,12 +188,19 @@ are swallowed unless `PROJMUX_USAGE_DEBUG` is set. Then it loads the
|
|
|
162
188
|
cache, filters to the same enabled-agent scope, and renders. If no AI
|
|
163
189
|
agents are enabled, the status segment emits nothing.
|
|
164
190
|
|
|
191
|
+
The HUD first derives an ambient projection separate from lossless account
|
|
192
|
+
snapshots. Canonical `5h`/`weekly` rows are eligible for every provider;
|
|
193
|
+
Antigravity's exact `quota/gemini-weekly` identity is projected as `weekly`
|
|
194
|
+
without rewriting the cache. Context, `3p-weekly`, and unknown quota buckets
|
|
195
|
+
do not participate in status width. Claude `limits[]` named/model rows are also
|
|
196
|
+
excluded; only its aggregate official `5h` and `weekly` rows reach the HUD.
|
|
197
|
+
|
|
165
198
|
Output degrades through six tiers as `--max-width` shrinks:
|
|
166
199
|
|
|
167
200
|
1. Long form with last-sync age + bars: `Claude (3m) 5h [████████░░]
|
|
168
|
-
80% · weekly [...] Antigravity
|
|
201
|
+
80% · weekly [...] Antigravity weekly [...]`
|
|
169
202
|
2. Drop the age indicator (legacy long form).
|
|
170
|
-
3.
|
|
203
|
+
3. Keep one primary bar per provider (`5h`, or `weekly` when 5h is absent).
|
|
171
204
|
4. Drop bars entirely (`Claude 5h:80% weekly:30%`).
|
|
172
205
|
5. Single-letter labels (`C 5h:80% weekly:30%`).
|
|
173
206
|
6. Hard rune-truncate with trailing `…`.
|
|
@@ -186,6 +219,17 @@ sync metadata to the same enabled-agent scope as the ambient HUD. This keeps
|
|
|
186
219
|
tmux click path a structured table with aligned rows, right-aligned numeric
|
|
187
220
|
values, dim unavailable cells, amber usage at 80%, and red usage at 95%.
|
|
188
221
|
|
|
222
|
+
The popup suppresses legacy cached context rows while preserving every valid
|
|
223
|
+
named quota ID, reset, and freshness value. Its columns are data-driven: when
|
|
224
|
+
no displayed row has authoritative absolute token counts, `USED`, `LIMIT`, and
|
|
225
|
+
`LEFT` are omitted together. If any row has real counts, all three columns are
|
|
226
|
+
shown; percent-only rows use unavailable cells. Counts are never derived from
|
|
227
|
+
percentages.
|
|
228
|
+
|
|
229
|
+
Named Claude rows use the same bounded, injection-safe label as text output and
|
|
230
|
+
include their reset plus per-row `AGE`. JSON retains exact opaque group/model
|
|
231
|
+
identity, nullable scope fields, `updated_at`, and the derived `stale` flag.
|
|
232
|
+
|
|
189
233
|
The popup sync line uses the maximum authoritative `LastCollect` timestamp from
|
|
190
234
|
the cache. If that field is unavailable, it falls back to the snapshots file
|
|
191
235
|
mtime. The sync line turns amber when the timestamp is more than 60 seconds
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projmux",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "tmux project session manager",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/crevissepartners/projmux#readme",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"package:npm:pack": "scripts/package-npm.sh --pack"
|
|
29
29
|
},
|
|
30
30
|
"optionalDependencies": {
|
|
31
|
-
"@projmux/linux-x64": "0.
|
|
32
|
-
"@projmux/linux-arm64": "0.
|
|
33
|
-
"@projmux/darwin-x64": "0.
|
|
34
|
-
"@projmux/darwin-arm64": "0.
|
|
31
|
+
"@projmux/linux-x64": "0.10.0",
|
|
32
|
+
"@projmux/linux-arm64": "0.10.0",
|
|
33
|
+
"@projmux/darwin-x64": "0.10.0",
|
|
34
|
+
"@projmux/darwin-arm64": "0.10.0"
|
|
35
35
|
}
|
|
36
36
|
}
|