projmux 0.8.4 → 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/README-ko.md +2 -2
- package/README.md +6 -5
- package/docs/agent-workflow.md +33 -18
- package/docs/ai-agent-shortcuts.md +25 -0
- package/docs/architecture.md +32 -10
- package/docs/cli.md +342 -78
- package/docs/configuration.md +69 -10
- package/docs/globalization.md +2 -2
- package/docs/hooks.md +68 -25
- package/docs/install.md +2 -2
- package/docs/keybindings.md +27 -17
- package/docs/native-picker.md +103 -0
- package/docs/notify-queue.md +4 -3
- package/docs/npm-distribution.md +2 -2
- package/docs/operational-diagnostics.md +121 -0
- package/docs/pr-guideline.md +1 -1
- package/docs/resource-attribution.md +189 -0
- package/docs/session-restore.md +31 -6
- package/docs/settings-ia.md +52 -12
- package/docs/statusbar.md +31 -4
- package/docs/testing.md +1 -1
- package/docs/tmux-surface-inventory.md +108 -775
- package/docs/upgrading.md +21 -0
- package/docs/usage-tracking.md +91 -28
- package/package.json +5 -5
- package/docs/native-picker-no-fzf-poc.md +0 -227
- package/docs/native-picker-parity.md +0 -153
- package/docs/picker-ui-plan.md +0 -91
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Operational Diagnostics and Privacy
|
|
2
|
+
|
|
3
|
+
Projmux records a small local-only operational journal so command failures and
|
|
4
|
+
state changes can be inspected after the originating process exits. It does
|
|
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.
|
|
8
|
+
|
|
9
|
+
## Safe event contract
|
|
10
|
+
|
|
11
|
+
Each JSONL record has a closed schema: `at`, `level`, `component`, `event`,
|
|
12
|
+
`result`, `duration_ms`, `run_id`, `version`, `mux_backend`, and optional
|
|
13
|
+
allowlisted `command`, `subcommand`, `kind`, and sanitized `message`. There is
|
|
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.
|
|
18
|
+
|
|
19
|
+
Command and subcommand names come from static allowlists. Unknown argv values,
|
|
20
|
+
paths, flags, and arguments are dropped. Messages have control/format
|
|
21
|
+
characters removed, whitespace normalized, the current home path abbreviated
|
|
22
|
+
to `~`, and length capped at 512 Unicode code points. Top-level outcomes never
|
|
23
|
+
copy `error.Error()` into the journal: their message is one of three stable,
|
|
24
|
+
lossy phrases (`command failed`, `invalid command usage`, or a classified
|
|
25
|
+
non-success status). Error `kind` is stored separately from that phrase.
|
|
26
|
+
|
|
27
|
+
The journal must never contain raw argv, stdin, prompts, notification bodies,
|
|
28
|
+
pane captures/output/title/topic/content, transcripts, raw hook payloads,
|
|
29
|
+
configuration secrets, or arbitrary environment values. Phase 0 also does not
|
|
30
|
+
add session/window/pane or other routing identifiers.
|
|
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
|
+
|
|
54
|
+
## Storage and retention
|
|
55
|
+
|
|
56
|
+
The path is
|
|
57
|
+
`${XDG_STATE_HOME:-$HOME/.local/state}/projmux/logs/operations.jsonl`.
|
|
58
|
+
On POSIX systems the `projmux` state and `logs` directories are private
|
|
59
|
+
(`0700`) and the journal is private (`0600`); accesses make a best-effort
|
|
60
|
+
repair of older permissive modes.
|
|
61
|
+
|
|
62
|
+
Append and trim share an OS-owned advisory inter-process lock. The kernel
|
|
63
|
+
releases ownership when a process exits, so an orphaned lock path needs no
|
|
64
|
+
path deletion or stale-owner reclamation and cannot race a successor owner.
|
|
65
|
+
Lock acquisition has an explicit 200 ms total budget so this side channel
|
|
66
|
+
cannot materially delay the original command result. When the file exceeds
|
|
67
|
+
5 MiB, a platform-specific atomic replacement retains approximately the
|
|
68
|
+
newest 2 MiB, beginning at a complete valid record; Windows uses replace-
|
|
69
|
+
existing semantics rather than plain rename. A trailing partial record is
|
|
70
|
+
discarded before the next append, and the reader skips malformed or truncated
|
|
71
|
+
records.
|
|
72
|
+
|
|
73
|
+
Classification is intentionally conservative for mutation-capable interactive
|
|
74
|
+
commands: opening session/project/settings/popup flows is treated as changing
|
|
75
|
+
even when a user cancels. Explicit read variants (`status`, `list`, `get`,
|
|
76
|
+
`preview`, config printing, plain welcome, and the diagnostics viewer) remain
|
|
77
|
+
read-only. The successful automatic hook/poll paths `ai ingest`, `attention
|
|
78
|
+
arm`, `attention clear`, `attention window`, `tmux autosave-session-state`, and
|
|
79
|
+
`window record` are also read-only so high-frequency operation does not append
|
|
80
|
+
to the journal; an error from any of them still records exactly one safe error
|
|
81
|
+
outcome. Explicit user mutations such as `attention toggle` retain their
|
|
82
|
+
state-changing success record. Direct top-level help and explicit preview-only intents (`upgrade
|
|
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,
|
|
90
|
+
terminal apply, snapshot delete, update check, and welcome popup inspect only
|
|
91
|
+
allowlisted mode/flag names; boolean `=false` values retain mutation-capable
|
|
92
|
+
classification, and no flag values are ever recorded. Help-looking tokens
|
|
93
|
+
after the direct command position stay conservatively mutation-capable because
|
|
94
|
+
they may be values rather than help intent.
|
|
95
|
+
|
|
96
|
+
Failures to resolve the path, create/repair permissions, lock, append, or trim
|
|
97
|
+
are ignored by the top-level command boundary. They do not change the original
|
|
98
|
+
command's stdout, stderr, exit code, or success/failure meaning, and journal
|
|
99
|
+
failures are never recursively journaled.
|
|
100
|
+
|
|
101
|
+
## Inspecting records
|
|
102
|
+
|
|
103
|
+
Use `projmux diagnostics log`; see [cli.md](cli.md#diagnostics). All text,
|
|
104
|
+
JSONL, tail, and filter views consume the same tolerant reader. A successful
|
|
105
|
+
viewer read is excluded from success logging, so inspection does not create a
|
|
106
|
+
recursion loop.
|
|
107
|
+
|
|
108
|
+
The older bounded `ai-ingest.log` and subsystem-specific `PROJMUX_*_DEBUG`
|
|
109
|
+
surfaces retain their current paths, formats, and behavior. They are not
|
|
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:
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Linux resource attribution core
|
|
2
|
+
|
|
3
|
+
Phase 0 provides the read-only attribution contract consumed by the Resource
|
|
4
|
+
Inspector shipped in Phase 1. `projmux resources`, the client-scoped
|
|
5
|
+
`resource-inspector` popup, the statusbar range, and `Resources:Open` all keep
|
|
6
|
+
the snapshot in memory only for the interactive process lifetime; it remains
|
|
7
|
+
outside Session State.
|
|
8
|
+
|
|
9
|
+
## Identity and inventory
|
|
10
|
+
|
|
11
|
+
`tmux.Client.ListResourcePanes` reads a resource-specific inventory containing
|
|
12
|
+
socket path, session id/name, window id, pane id, pane PID/TTY, and the session
|
|
13
|
+
`@projmux_project_path` anchor. This is deliberately separate from the general
|
|
14
|
+
`tmux.Pane` inventory so the resource contract requires PID, TTY, and project
|
|
15
|
+
anchor data without weakening other pane consumers.
|
|
16
|
+
|
|
17
|
+
The ownership key is `(socket, pane_id)`. Process identity is `(PID,
|
|
18
|
+
/proc/<pid>/stat starttime)` and a process is attributed only when its POSIX
|
|
19
|
+
SID maps to exactly one unique pane PID. Pane labels, AI topics, raw titles,
|
|
20
|
+
current commands, and cwd-derived names are not ownership inputs.
|
|
21
|
+
|
|
22
|
+
Linked appearances of one pane are deduplicated. Multiple non-empty project
|
|
23
|
+
anchors become `Shared / ambiguous`; no anchor becomes `Unassigned`. Processes
|
|
24
|
+
that use `setsid` or otherwise leave the pane SID remain host-only and are
|
|
25
|
+
counted at the escaped boundary instead of guessed back onto a pane.
|
|
26
|
+
|
|
27
|
+
## Sampling and read model
|
|
28
|
+
|
|
29
|
+
The Linux collector enumerates `/proc` once per sample, then reads only
|
|
30
|
+
`/proc/stat`, `/proc/meminfo`, and numeric `/proc/<pid>/stat` files. It never
|
|
31
|
+
reads command lines, environment, prompts, pane content, transcripts, memory
|
|
32
|
+
maps, SQLite, protobuf, or remote telemetry.
|
|
33
|
+
|
|
34
|
+
- CPU needs two samples with the same positive logical CPU count. Primary CPU
|
|
35
|
+
is process tick delta divided by aggregate host tick delta (host capacity
|
|
36
|
+
share, normally comparable on a 0–100% scale); secondary CPU is that share
|
|
37
|
+
multiplied by logical CPUs (core-equivalent). First sample, invalid/reset
|
|
38
|
+
deltas, PID reuse, and logical CPU changes remain unknown/partial, never zero
|
|
39
|
+
or silently clamped.
|
|
40
|
+
- Memory is summed RSS bytes plus `RSS / MemTotal`. RSS is a per-process sum;
|
|
41
|
+
shared pages may therefore be counted more than once.
|
|
42
|
+
- Pane values are aggregated into unique window and project rows. Tests enforce
|
|
43
|
+
that window/project totals equal the same set of unique pane totals.
|
|
44
|
+
- `Attributed` is not host total. `Other / unattributed` is a separate host
|
|
45
|
+
remainder. When process-delta timing or RSS sharing makes attributed values
|
|
46
|
+
exceed the host comparison sample, the model exposes an overage and leaves
|
|
47
|
+
remainder unknown instead of clamping it to zero.
|
|
48
|
+
- Snapshot state is `warming`, `ready`, `partial`, or `unavailable`. Bounded
|
|
49
|
+
diagnostics contain scan duration and sampled/skipped/race/permission counts,
|
|
50
|
+
plus identity/delta quality counts; they contain no user payload.
|
|
51
|
+
|
|
52
|
+
## Measurements
|
|
53
|
+
|
|
54
|
+
Measured 2026-08-12 on Linux amd64, Intel Core Ultra 5 125U. Collector cases
|
|
55
|
+
used generated procfs directory fixtures and `-benchtime=20x`; aggregation used
|
|
56
|
+
`-benchtime=100x`. Commands:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
go test -run '^$' -bench BenchmarkCollectorScan -benchtime=20x -count=1 ./internal/integrations/procfsresources
|
|
60
|
+
go test -run '^$' -bench BenchmarkBuildSnapshot -benchtime=100x -count=1 ./internal/core/resources
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Panes | Processes | Procfs scan | Scan allocation | Aggregation | Aggregation allocation |
|
|
64
|
+
|---:|---:|---:|---:|---:|---:|
|
|
65
|
+
| 10 | 50 | 0.407 ms | 80.6 KB | 0.026 ms | 32.1 KB |
|
|
66
|
+
| 10 | 200 | 0.927 ms | 313.0 KB | 0.074 ms | 75.0 KB |
|
|
67
|
+
| 10 | 1000 | 6.332 ms | 1.56 MB | 0.230 ms | 481.6 KB |
|
|
68
|
+
| 50 | 50 | 0.407 ms | 80.6 KB | 0.084 ms | 90.4 KB |
|
|
69
|
+
| 50 | 200 | 0.927 ms | 313.0 KB | 0.111 ms | 133.2 KB |
|
|
70
|
+
| 50 | 1000 | 6.332 ms | 1.56 MB | 0.261 ms | 539.9 KB |
|
|
71
|
+
|
|
72
|
+
The scan is process-count-bound and does not multiply by pane count. This
|
|
73
|
+
supports the planned 2-second popup cadence without a daemon or persistent
|
|
74
|
+
history. A separate count-only live cost probe (`PROJMUX_RESOURCE_PSS_MEASURE=1
|
|
75
|
+
go test -run TestPSSReadCostMeasurement -v
|
|
76
|
+
./internal/integrations/procfsresources`) attempted 200 host processes: 53
|
|
77
|
+
`smaps_rollup` reads succeeded and 147 were skipped because of access
|
|
78
|
+
restrictions. Those reads took 92.865058 ms, versus 7.323994 ms for the matching
|
|
79
|
+
one-pass RSS/stat scan. This is not a universal cost multiplier: accessibility,
|
|
80
|
+
process shape, kernel state, and cache effects differ by host. It does show the
|
|
81
|
+
structural cost of a separate `smaps_rollup` read and kernel page accounting per
|
|
82
|
+
accessible process, whereas RSS comes from the stat file already needed for
|
|
83
|
+
CPU/identity. PSS is therefore **deferred**. It may be reconsidered only as an
|
|
84
|
+
on-demand pane-detail measurement with a concrete consumer; it is not part of
|
|
85
|
+
continuous refresh.
|
|
86
|
+
|
|
87
|
+
## Sanitized real tmux smoke
|
|
88
|
+
|
|
89
|
+
The opt-in read-only test below observes an existing socket and emits counts
|
|
90
|
+
only. It neither captures pane content nor reads process command lines.
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
PROJMUX_RESOURCE_TMUX_SOCKET=projmux \
|
|
94
|
+
PROJMUX_RESOURCE_EXPECT_PROJECT_ROOT=/path/to/project go test \
|
|
95
|
+
-run TestResourceAttributionRealTmuxReadOnlySmoke -v \
|
|
96
|
+
./internal/integrations/tmux
|
|
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
|
+
|
|
105
|
+
2026-08-12 result: `panes=8`, `pane_pid_eq_sid=8`, `missing_pids=0`,
|
|
106
|
+
`attributed_processes=13`, `escaped_boundary=0`, `sampled=474`, `skipped=0`,
|
|
107
|
+
`race=0`, `permission=0`, `status=ready`. A separate tmux format-only count
|
|
108
|
+
showed four shell panes, three direct agent panes, and one launcher pane. The
|
|
109
|
+
zero escaped count is a valid observed boundary; the setsid fixture test pins
|
|
110
|
+
the non-attribution behavior deterministically. Race and permission states are
|
|
111
|
+
also deterministic fixtures: the collector test injects `fs.ErrNotExist` and
|
|
112
|
+
`fs.ErrPermission` for numeric proc entries and checks the separate counts.
|
|
113
|
+
|
|
114
|
+
A positive real-kernel setsid boundary is covered by an isolated transient
|
|
115
|
+
smoke. It creates and removes its own tmux socket/server and never touches the
|
|
116
|
+
existing production socket:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
PROJMUX_RESOURCE_TRANSIENT_SMOKE=1 go test \
|
|
120
|
+
-run TestResourceAttributionTransientSetsidSmoke -v \
|
|
121
|
+
./internal/integrations/tmux
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
2026-08-12 result: `panes=1`, `attributed_processes=1`,
|
|
125
|
+
`escaped_boundary=1`, `sampled=480`, `skipped=0`, `race=0`, `permission=0`.
|
|
126
|
+
The pane shell remained attributed while its real `setsid` child was counted
|
|
127
|
+
at the escaped/Other boundary, without reading the child command line.
|
|
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
|
+
|
|
141
|
+
## Phase 1 inspector
|
|
142
|
+
|
|
143
|
+
The popup retains warming/partial/unavailable and overage states, renders RSS
|
|
144
|
+
explicitly as a sum, keeps `Other / unattributed` non-drillable, and discards
|
|
145
|
+
samples when it closes. Its non-overlapping default cadence is two seconds;
|
|
146
|
+
Ctrl-R shares the same scan gate. Selection and query survive refresh by stable
|
|
147
|
+
row identity, while a vanished row clamps to the nearest valid neighbor.
|
|
148
|
+
Display labels use label → agent topic → known interactive shell → raw title,
|
|
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/session-restore.md
CHANGED
|
@@ -11,9 +11,10 @@ projmux session-state restore --dry-run [--session <name>]
|
|
|
11
11
|
projmux session-state delete [--session <name>]
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Snapshots preserve source metadata, not a
|
|
15
|
-
keep `window_name`; pane records keep `pane_title`, recipe
|
|
16
|
-
metadata (`@projmux_ai_topic`
|
|
14
|
+
Snapshots preserve source metadata, not a resolved display label. Window records
|
|
15
|
+
keep `window_name`; pane records keep the user label, raw `pane_title`, recipe
|
|
16
|
+
fields, AI topic and manual-ownership metadata (`@projmux_ai_topic` and
|
|
17
|
+
`@projmux_ai_topic_manual`), and resume metadata when available. There is no
|
|
17
18
|
`display_label` field in the snapshot schema. After restore, pane borders and
|
|
18
19
|
app window tabs are display-time tmux policy: the app config derives both from
|
|
19
20
|
the active pane's visible label expression, while raw shell or terminal titles
|
|
@@ -48,11 +49,21 @@ unknown sources are low or none. The old statusbar Session State shortcut has
|
|
|
48
49
|
been removed; use `Projects > Sessions > State` or the `projmux session-state`
|
|
49
50
|
CLI for inspection/actions.
|
|
50
51
|
|
|
52
|
+
Session snapshots capture each pane's user-owned `label` separately from its
|
|
53
|
+
raw `title` and agent recipe `topic`. Older snapshots decode with an empty
|
|
54
|
+
label and no manual topic ownership; no title/topic equality heuristic is
|
|
55
|
+
applied. Replay explicitly sets or clears the label, startup recipe fields, AI
|
|
56
|
+
agent/topic/ownership/resume fields, and finally the raw title on the pane id
|
|
57
|
+
returned by tmux creation. It does not derive a target or identity from pane
|
|
58
|
+
order, a visible title, or equality between saved fields.
|
|
59
|
+
|
|
51
60
|
Agent restore direct-starts supported resume commands when creating fresh tmux
|
|
52
61
|
panes, matching the `projmux ai split` wrapper shape: the wrapper prepends the
|
|
53
|
-
agent binary directory to `PATH`, changes to the saved cwd,
|
|
54
|
-
|
|
55
|
-
`
|
|
62
|
+
agent binary directory to `PATH`, changes to the saved cwd, then execs
|
|
63
|
+
`codex resume <id>`, `claude --resume <id>`, or
|
|
64
|
+
`agy --conversation <uuid>`. It does not copy the saved topic into OSC or raw
|
|
65
|
+
tmux title state; replay restores the final raw title only from `Pane.Title`.
|
|
66
|
+
Antigravity restore
|
|
56
67
|
uses only the stable statusline `conversation_id` or hook `conversationId`
|
|
57
68
|
metadata captured as the pane resume id; missing or non-UUID Antigravity ids
|
|
58
69
|
render as `resume unavailable` rather than falling back silently to a shell
|
|
@@ -63,6 +74,20 @@ environment, shell functions, aliases, or live process state. Startup recipes
|
|
|
63
74
|
continue to use their saved `send-keys` command replay, and shell recipes only
|
|
64
75
|
restore cwd/layout.
|
|
65
76
|
|
|
77
|
+
This live capture lane remains distinct from resume-picker disk discovery and
|
|
78
|
+
has high confidence (`hook`/`session-id`). When an Antigravity picker row starts
|
|
79
|
+
a pane, its source is captured too: a UUID verified by an exact regular
|
|
80
|
+
`conversations/<uuid>.db` through `last_conversations` or workspace-bearing
|
|
81
|
+
summarized metadata has medium confidence, while a legacy `history.jsonl` row
|
|
82
|
+
has low confidence. Preview and doctor report that source/confidence as stored;
|
|
83
|
+
they do not claim that the upstream cache exposes complete history. Disk
|
|
84
|
+
discovery does not replace an existing live hook source, and it never opens a
|
|
85
|
+
conversation database or reads prompt/transcript content.
|
|
86
|
+
Bounded Session State agent-pane previews place resume health before the full
|
|
87
|
+
resume id, topic, and title so status, confidence, and source remain visible;
|
|
88
|
+
the underlying snapshot and unbounded preview model retain those identity and
|
|
89
|
+
context fields unchanged. Non-agent pane preview ordering is unchanged.
|
|
90
|
+
|
|
66
91
|
Settings > Session State is global settings only: global auto-save, auto-save
|
|
67
92
|
interval, and storage/retention policy. It does not show the current
|
|
68
93
|
snapshot tree. Delete for current-session snapshots and destructive restore
|
package/docs/settings-ia.md
CHANGED
|
@@ -11,6 +11,10 @@ view-first layout:
|
|
|
11
11
|
current state, source, and expected rendered result before offering mutation
|
|
12
12
|
rows. If a detail opens a dedicated `Change` page, that page is mutation-only
|
|
13
13
|
and does not repeat the same read-only view rows.
|
|
14
|
+
- Every rendered non-empty row value is classified as navigation, actionable,
|
|
15
|
+
or passive information/disabled state and is mapped to a closed owner-loop
|
|
16
|
+
contract before rendering. An unowned value is a Settings error; Enter on a
|
|
17
|
+
passive row is consumed as a no-op.
|
|
14
18
|
- `Settings > Project Picker > Workdirs` is the list/overview entry. Add/remove
|
|
15
19
|
actions live inside that view.
|
|
16
20
|
- `Settings > Project Picker > Project Root` shows effective and saved values
|
|
@@ -35,7 +39,7 @@ view-first layout:
|
|
|
35
39
|
rows as always-visible sections.
|
|
36
40
|
- Terminal delivery remediation lives outside Settings primary flow. The
|
|
37
41
|
supported order is `projmux shell` first, then `projmux setup`, then
|
|
38
|
-
`projmux
|
|
42
|
+
`projmux setup terminal` for supported terminal adapters.
|
|
39
43
|
- Rows that cannot safely be edited still stay visible. Mark diagnostic-only
|
|
40
44
|
rows with the delivery path and reason instead of hiding them or turning them
|
|
41
45
|
into unsupported editable keys. Transport-dependent rows stay visible with
|
|
@@ -57,9 +61,10 @@ view-first layout:
|
|
|
57
61
|
`[theme]` is never resolved or shown here.
|
|
58
62
|
- `Settings > Notifications` owns notification delivery IA. Desktop notification
|
|
59
63
|
mode, AI desktop notification dedupe duration, delivery source diagnostics,
|
|
60
|
-
AI hook quiet policy
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
and AI hook quiet policy live together without mixing mutation boundaries.
|
|
65
|
+
The in-app queue is consumed from the statusbar/sidebar, not from a standalone
|
|
66
|
+
Settings row. `PROJMUX_NOTIFY_HOOK` override presence is folded into Delivery
|
|
67
|
+
sources summary/detail instead of appearing as a separate root row.
|
|
63
68
|
- `Settings > Notifications > Desktop notifications` owns the desktop
|
|
64
69
|
notification mode. The detail choices are `none`, `notify`, and `raise`.
|
|
65
70
|
- `Settings > Notifications > AI notification dedupe` owns the duplicate
|
|
@@ -67,10 +72,10 @@ view-first layout:
|
|
|
67
72
|
the effective source; `PROJMUX_TMUX_NOTIFY_DEDUPE_SECONDS` remains the top
|
|
68
73
|
override. The tmux bell fallback keeps its fixed 5 second window.
|
|
69
74
|
- `Settings > Notifications > Delivery sources` shows Codex hooks, Claude, and
|
|
70
|
-
tmux producer diagnostics
|
|
71
|
-
Settings copies command text only;
|
|
72
|
-
notify wiring. The legacy Codex notify
|
|
73
|
-
Settings.
|
|
75
|
+
tmux producer diagnostics, the effective desktop sender override state, and
|
|
76
|
+
copyable install/remove/dry-run commands. Settings copies command text only;
|
|
77
|
+
it does not install or remove external notify wiring. The legacy Codex notify
|
|
78
|
+
source is intentionally omitted from Settings.
|
|
74
79
|
- `Settings > Notifications > Hook quiet policy` shows Codex/Claude hook
|
|
75
80
|
runtime action values and writes only
|
|
76
81
|
`${XDG_CONFIG_HOME:-$HOME/.config}/projmux/ai-hook-actions.json`. It does not
|
|
@@ -78,13 +83,17 @@ view-first layout:
|
|
|
78
83
|
- `Settings > Session State > Sidebar startup picker` controls the Alt-1
|
|
79
84
|
project-open startup selector. The saved file remains
|
|
80
85
|
`${XDG_CONFIG_HOME:-$HOME/.config}/projmux/sidebar-startup-picker`.
|
|
81
|
-
- `Settings > Labs`
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
- `Settings > Labs` contains only Live system resources and Project Hooks.
|
|
87
|
+
Keybindings live at `Settings > Keybindings`; Labs has no visible or hidden
|
|
88
|
+
keybindings redirect. The native picker is the product picker, so Labs does
|
|
89
|
+
not render picker source information.
|
|
84
90
|
- `Settings > Labs > Live system resources` is a direct global on/off toggle
|
|
85
91
|
for the macOS/Linux/WSL lower-status-row `CPU N% MEM N%` segment. It defaults
|
|
86
92
|
off, updates live tmux state when toggled, and renders unavailable on
|
|
87
|
-
unsupported platforms.
|
|
93
|
+
unsupported platforms. CPU and memory use fixed independent semantic
|
|
94
|
+
thresholds (CPU warning/critical at 70/90; memory at 75/90); the toggle does
|
|
95
|
+
not expose threshold customization. WSL values describe the Linux guest/VM
|
|
96
|
+
view.
|
|
88
97
|
- `Settings > Labs > Project Hooks` is overview-first. The Labs root opens the
|
|
89
98
|
overview, and the on/off mutation rows live one level deeper.
|
|
90
99
|
- `Settings > AI Settings` is view-first. The root contains `Default split
|
|
@@ -106,6 +115,37 @@ view-first layout:
|
|
|
106
115
|
`ko-KR`. When `auto` is active it must show the detected source (`LC_ALL`,
|
|
107
116
|
`LC_MESSAGES`, `LANG`, or fallback). Unsupported locale tags must remain
|
|
108
117
|
visible as warnings and fall back to `en-US`.
|
|
118
|
+
- Global root descriptions keep ownership explicit: Appearance owns language,
|
|
119
|
+
AI badge style, and status/notification icon decoration; Theme owns presets,
|
|
120
|
+
color tokens, and font hints. About describes only the surface it retains.
|
|
121
|
+
- `Settings > About` is intentionally compact: Version, Source, update
|
|
122
|
+
status/actions (including Latest, Update state, Installer, and Release notes
|
|
123
|
+
when available), Welcome, and Quit. It does not reproduce static key,
|
|
124
|
+
terminal, dependency, terminal-emulator, or documentation guides. Key
|
|
125
|
+
delivery discovery lives in `projmux setup`, supported terminal remediation
|
|
126
|
+
in `projmux setup terminal`, read-only dependency/runtime diagnostics in
|
|
127
|
+
`projmux doctor`, and broader orientation in Welcome and maintained docs.
|
|
128
|
+
- Without an actionable project context, the Project surface renders one
|
|
129
|
+
passive context-guidance row instead of repeating the same disabled reason
|
|
130
|
+
for Trust, Hooks, Project recipe, and Effective merge view. With project
|
|
131
|
+
context, those four rows and Session State retain their existing actions.
|
|
132
|
+
|
|
133
|
+
Settings mutation feedback follows one transient contract. The next picker
|
|
134
|
+
frame inserts one passive `Feedback` row after Back; the row uses the catalogued
|
|
135
|
+
`settingsNoopValue`, so Enter cannot create an unknown action. Selecting another
|
|
136
|
+
navigation/action clears the old row before that operation runs, and a handled
|
|
137
|
+
result replaces it. The inventory includes AI defaults/enabled agents/resume
|
|
138
|
+
limits, notification modes/dedupe/hook policy, Appearance and locale choices,
|
|
139
|
+
Labs toggles, project roots/workdirs/pins, project hooks/recipe/trust, Theme,
|
|
140
|
+
Session State, direct keybinding reset/remove/toggle operations, and About
|
|
141
|
+
update apply/check. Typed validation and staged apply failures stay in the
|
|
142
|
+
popup instead of being visible only on stdout/stderr.
|
|
143
|
+
|
|
144
|
+
The generic feedback inventory deliberately excludes Welcome, Quit,
|
|
145
|
+
read-only hook/effective/notification diagnostics, Session State preview, and
|
|
146
|
+
key capture/probe/diagnostic bodies. Those flows own a viewer, confirmation, or
|
|
147
|
+
multi-step output surface; only an actual Settings write at their boundary is
|
|
148
|
+
eligible for transient mutation feedback.
|
|
109
149
|
|
|
110
150
|
Hooks remain the reference pattern for this IA:
|
|
111
151
|
|
package/docs/statusbar.md
CHANGED
|
@@ -68,9 +68,22 @@ 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.
|
|
75
|
+
CPU and memory are host-scoped telemetry, not pane, window, project, or
|
|
76
|
+
session attribution. Each value has an independent semantic style: CPU is
|
|
77
|
+
normal below 70%, warning at 70–89%, and critical at 90% or above; memory is
|
|
78
|
+
normal below 75%, warning at 75–89%, and critical at 90% or above. Normal and
|
|
79
|
+
unavailable (`--`) values use the secondary status-text role, warnings use
|
|
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.
|
|
74
87
|
Linux CPU is the aggregate delta from `/proc/stat`; memory is
|
|
75
88
|
`(MemTotal - MemAvailable) / MemTotal` from `/proc/meminfo`. macOS CPU uses
|
|
76
89
|
the aggregate Mach host tick delta; memory is total physical memory minus
|
|
@@ -82,6 +95,10 @@ row 1 [#S] #{pane_current_path} ⎈ <ctx>/<ns> <git> CPU 12% MEM 41%
|
|
|
82
95
|
re-enabling after a pause starts at `CPU --%` instead of showing a long-term
|
|
83
96
|
average. Missing or malformed procfs data degrades to `--` or an empty segment
|
|
84
97
|
without producing a tmux error popup.
|
|
98
|
+
The complete live segment is wrapped in `#[range=user|resources]...#[norange]`.
|
|
99
|
+
Clicking it opens the same canonical client-scoped `resource-inspector`
|
|
100
|
+
popup as the `Resources:Open` keybinding action. Disabling the Lab hides only
|
|
101
|
+
the segment; it does not disable a custom action or `projmux resources`.
|
|
85
102
|
- The settings chip keeps its label padding inside the `settings` range
|
|
86
103
|
and inside the chip background. The compact app chip renders `` with
|
|
87
104
|
the extra right-side icon padding painted by the same background, while
|
|
@@ -112,6 +129,7 @@ bind-key -n MouseDown1Status if-shell -F "#{==:#{mouse_status_range},window}" \
|
|
|
112
129
|
| `kube` | 0 | `projmux tmux popup-toggle sessionizer` | `prefix s k` |
|
|
113
130
|
| `git` | 0 | `projmux tmux popup-toggle sessionizer` | `prefix s g` |
|
|
114
131
|
| `settings` | 0 | `projmux tmux popup-toggle --client <tty> ai-split-settings` | mouse only; `prefix s s` remains `session` |
|
|
132
|
+
| `resources` | 0 | `projmux tmux popup-toggle --client <tty> resource-inspector` | mouse or custom `Resources:Open`; no default key |
|
|
115
133
|
| `usage` | 1 | show a native-framed usage HUD popup from cached usage state | `prefix s u` |
|
|
116
134
|
| `notify` | 1 | `projmux focus --target <newest> --source status-bar --kind segment-click [--client <tty>]`, then ack on focus success | `prefix s n` |
|
|
117
135
|
|
|
@@ -136,10 +154,18 @@ hard-truncate path still closes with `#[default]` so later status segments do
|
|
|
136
154
|
not inherit notification styling. That dotless narrow fallback applies only to
|
|
137
155
|
the queued notify segment, not to the separate window-list live attention badge.
|
|
138
156
|
`usage` opens a native-framed detail HUD for the compact usage bar. It reads
|
|
139
|
-
the cached usage state in-process
|
|
140
|
-
output shape unchanged for external consumers, aligns model/window rows with
|
|
157
|
+
the cached usage state in-process and aligns model/window rows with
|
|
141
158
|
right-aligned numeric values, dims unavailable values, keeps stale sync/age
|
|
142
159
|
metadata muted, and colors only threshold values: amber at 80% and red at 95%.
|
|
160
|
+
Antigravity rows keep conversation-local `context` separate from account
|
|
161
|
+
`quota/<exact upstream bucket ID>` rows; the popup displays an absolute reset
|
|
162
|
+
when provided and otherwise the exact optional relative reset seconds. Opaque
|
|
163
|
+
bucket IDs are escaped for terminal/tmux safety and are never assigned a
|
|
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.
|
|
143
169
|
Session State inspection lives under `Projects > Sessions > State`; global
|
|
144
170
|
Settings > Session State is settings-only and the statusbar no longer exposes a
|
|
145
171
|
duplicate State button.
|
|
@@ -153,7 +179,8 @@ does not leave terminal key state behind. The usage popup uses the same
|
|
|
153
179
|
single-payload print and plain Enter-close pattern. It shows the authoritative
|
|
154
180
|
last collect timestamp when present, falls back to the cache file mtime when
|
|
155
181
|
needed, and keeps stale sync metadata muted instead of escalating it to a
|
|
156
|
-
warning color.
|
|
182
|
+
warning color. Percent-only named rows do not synthesize `USED`, `LIMIT`, or
|
|
183
|
+
`LEFT` counts.
|
|
157
184
|
The notification HUD detail surface opens the right-side notification popup
|
|
158
185
|
through the notify sidebar action, showing the grouped pane/session inbox with
|
|
159
186
|
collapsed group rows and the same attention-tinted title. When notification
|
package/docs/testing.md
CHANGED
|
@@ -77,7 +77,7 @@ Observe:
|
|
|
77
77
|
- `Alt-1` through `Alt-5` report `OK plain`. These are the guaranteed
|
|
78
78
|
zero-config launch defaults.
|
|
79
79
|
- If a guaranteed key reports `MISS timeout`, preview a supported terminal
|
|
80
|
-
mapping with `projmux
|
|
80
|
+
mapping with `projmux setup terminal ghostty` or `projmux setup terminal windows-terminal`,
|
|
81
81
|
apply it with the same command plus `--apply`, restart that terminal if
|
|
82
82
|
required, and rerun `projmux setup --timeout 10s`.
|
|
83
83
|
- Optional direct aliases and transport-dependent chords may be reported by
|