projmux 0.12.2 → 0.13.1
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.md +3 -0
- package/docs/agent-workflow.md +277 -20
- package/docs/ai-agent-shortcuts.md +22 -3
- package/docs/architecture.md +1291 -90
- package/docs/cli-guide.md +572 -69
- package/docs/cli.md +498 -67
- package/docs/configuration.md +220 -35
- package/docs/hooks.md +17 -3
- package/docs/install.md +23 -7
- package/docs/keybindings.md +5 -3
- package/docs/legacy-cli-retirement.md +37 -8
- package/docs/operational-diagnostics.md +58 -2
- package/docs/session-restore.md +73 -157
- package/docs/settings-ia.md +48 -24
- package/docs/statusbar.md +10 -2
- package/docs/testing.md +93 -10
- package/docs/troubleshooting.md +157 -0
- package/docs/upgrading.md +266 -11
- package/docs/usage-tracking.md +56 -10
- package/package.json +5 -5
package/docs/cli-guide.md
CHANGED
|
@@ -63,6 +63,28 @@ rendered from the same command manifest the binary renders `projmux help`
|
|
|
63
63
|
from and is verified against it on every `make test`, so it cannot drift.
|
|
64
64
|
Nothing in this guide restates it.
|
|
65
65
|
|
|
66
|
+
Every executable route and root-level parser bridge in that same graph has one
|
|
67
|
+
**selectorless authority** class. The label describes what omission means; it
|
|
68
|
+
does not prevent a route from accepting an explicit selector:
|
|
69
|
+
|
|
70
|
+
- `natural-omitted` — omission resolves one predictable current resource or a
|
|
71
|
+
documented contextual read/scope, such as an active-root inventory or picker;
|
|
72
|
+
supplying any selector replaces that natural target or scope instead of
|
|
73
|
+
blending with it. Current Pane/Window mutations still require one exact
|
|
74
|
+
resource.
|
|
75
|
+
- `explicit-target` — the route or its generated caller must name the exact
|
|
76
|
+
target; ambient tmux context cannot supply or narrow it.
|
|
77
|
+
- `refusal` — the node has no safe selectorless action, so omission refuses
|
|
78
|
+
before output or mutation. Namespace nodes use this class when only a child
|
|
79
|
+
route is executable.
|
|
80
|
+
- `explicit-fan-out` — the route spelling is an intentional global or
|
|
81
|
+
whole-set operation. Resource mutations never enter this class merely
|
|
82
|
+
because a selector happened to match several rows.
|
|
83
|
+
|
|
84
|
+
The generated reference and `projmux <route> --help` print the class projected
|
|
85
|
+
from the graph. Completeness tests census every graph node plus bare/help/version
|
|
86
|
+
root bridges and reject missing, duplicate, conflicting, or unknown rows.
|
|
87
|
+
|
|
66
88
|
## Resource selectors and the active target
|
|
67
89
|
|
|
68
90
|
The resource routes (`get`, `describe`, `create`, `rename`, `rebind`, `delete`,
|
|
@@ -111,13 +133,16 @@ The contract:
|
|
|
111
133
|
--name current` succeeds — so a sentinel token would silently shadow a real
|
|
112
134
|
resource.
|
|
113
135
|
- **"No selector at all" means exactly that.** Any positional `<ref>`, any
|
|
114
|
-
`--project`/`-p`, `--window`/`-w`, `--pane`, or any `--selector` label keeps
|
|
115
|
-
|
|
116
|
-
partially specified selector.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
136
|
+
`--project`/`-p`, `--window`/`-w`, `--pane`, or any `--selector` label keeps
|
|
137
|
+
picking the target itself. The active *target* is never blended into a
|
|
138
|
+
partially specified selector. The active *Project* is a separate rule and does
|
|
139
|
+
apply to a reference -- see [Reference scope](#reference-scope-the-active-project-namespace)
|
|
140
|
+
below.
|
|
141
|
+
- **Only the singular routes and `create`.** The plural reads (`get
|
|
142
|
+
projects|windows|panes|agents`) stay 0..N inventories over their whole scope,
|
|
143
|
+
and `delete` is unchanged. `create` has its own spelling of the same rule --
|
|
144
|
+
see [Create scope](#create-scope) -- because a create resolves a scope to put
|
|
145
|
+
something *into* rather than a target to act *on*.
|
|
121
146
|
- **Inside tmux is decided by `$TMUX_PANE` plus `$TMUX`**, not by whether a tmux
|
|
122
147
|
server answers. A bare `display-message` from outside a client still succeeds
|
|
123
148
|
and answers for the most-recently-used session; projmux never uses that.
|
|
@@ -136,6 +161,170 @@ and `@projmux_window_uid` on its window — and derives every ancestor from
|
|
|
136
161
|
registry `ownerRef`. The session-scoped `@projmux_project_uid` is deliberately
|
|
137
162
|
not consulted.
|
|
138
163
|
|
|
164
|
+
### Plural read scope: the active managed root
|
|
165
|
+
|
|
166
|
+
Inside tmux, selector-omitted `get windows|panes|agents` derives one exact
|
|
167
|
+
managed root from the active Window's Registry `ownerRef`:
|
|
168
|
+
|
|
169
|
+
- a Project-owned Window lists only that Project's descendants;
|
|
170
|
+
- a ControlSession-owned Window, including Home, lists only that
|
|
171
|
+
ControlSession's descendants;
|
|
172
|
+
- a missing Window mirror, a foreign Window uid, or a Window without one exact
|
|
173
|
+
existing Project or ControlSession owner is exit `2` with zero stdout. It
|
|
174
|
+
never falls through to the whole Registry.
|
|
175
|
+
|
|
176
|
+
The default narrows the Window universe and does not pick a target. Name and
|
|
177
|
+
label selectors keep their existing meaning inside that root. An applicable
|
|
178
|
+
explicit `uid:` occurrence is opaque Registry-global authority: like explicit
|
|
179
|
+
`--project`/`-p` and `--all-projects`/`-A`, it bypasses active-target observation
|
|
180
|
+
entirely, including from a foreign tmux Pane. The two whole-set spellings retain
|
|
181
|
+
their existing meaning and include ControlSession descendants. Outside tmux, an
|
|
182
|
+
omitted root keeps the historical whole-Registry inventory without probing a
|
|
183
|
+
default server.
|
|
184
|
+
|
|
185
|
+
### Reference scope: the active Project namespace
|
|
186
|
+
|
|
187
|
+
A `metadata.name` is unique inside its owner scope, never across the registry: a
|
|
188
|
+
Window name is unique inside its Project, a Pane name inside its Window or
|
|
189
|
+
Agent, an Agent name inside its Window. So inside a managed Project a reference
|
|
190
|
+
is resolved inside the Project that owns the active Window, which is the same
|
|
191
|
+
universe the plural reads use in that context:
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
projmux describe window zsh # the Window named zsh in *this* Project
|
|
195
|
+
projmux describe pane log # the Pane named log in this Project
|
|
196
|
+
projmux describe agent codex # the Agent named codex in this Project
|
|
197
|
+
projmux rename window zsh --name review
|
|
198
|
+
projmux rename pane log --name build
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The Project-only read matrix is `describe window|pane|agent`. Generic
|
|
202
|
+
`rename window|pane|agent` uses the same narrowing rule but accepts either the
|
|
203
|
+
active Project or ControlSession as the exact owner root. `describe project`
|
|
204
|
+
and `rename project` are not in either matrix — a Project has no enclosing
|
|
205
|
+
root — and `delete`, `rebind`, and `agent resume` keep their selector meaning.
|
|
206
|
+
|
|
207
|
+
The contract:
|
|
208
|
+
|
|
209
|
+
- **It narrows the search, it does not pick the target.** The Project fixes the
|
|
210
|
+
universe and nothing else. The active Window and the active Pane are not used
|
|
211
|
+
to break a tie, so two same-named resources inside the one Project stay the
|
|
212
|
+
ordinary bounded `matched N ..., want exactly one` ambiguity. Resolving that
|
|
213
|
+
is what `--window`/`--pane` and a `uid:` reference are for.
|
|
214
|
+
- **Explicit `--project`/`-p` wins, with zero observations.** Naming a Project
|
|
215
|
+
never costs a tmux round trip and never depends on the pane you are sitting
|
|
216
|
+
in.
|
|
217
|
+
- **A `uid:` reference is scoped too.** A uid that belongs to another Project is
|
|
218
|
+
a no-match, not a cross-Project hit. Pass `--project` to address it.
|
|
219
|
+
- **Outside tmux nothing changes.** The whole registry is searched and the
|
|
220
|
+
previous `matched N ..., want exactly one` ambiguity is unchanged. No default
|
|
221
|
+
tmux server is probed.
|
|
222
|
+
- **Inside tmux a broken owner chain refuses.** A pane carrying no
|
|
223
|
+
`@projmux_window_uid`, a mirrored Window uid the registry does not hold, or a
|
|
224
|
+
Window with no exact existing Project or ControlSession owner is exit `2`
|
|
225
|
+
with zero bytes on stdout and zero mutations for generic rename. It is never
|
|
226
|
+
a silent fallback to the whole registry.
|
|
227
|
+
|
|
228
|
+
### Create scope
|
|
229
|
+
|
|
230
|
+
`create window|pane|agent|<provider>` is resource-backed on every spelling.
|
|
231
|
+
There is no mode flag and no second parser: the same argv means the same thing
|
|
232
|
+
whether or not `--project` is present, and `-w`, `--create-window`, `--pane`,
|
|
233
|
+
`--selector`, `--placement`, `--name`, `--label`, `--cwd`, `--add-dir`, `-o`,
|
|
234
|
+
and the `--` payload all reach the same parser either way.
|
|
235
|
+
|
|
236
|
+
The scope resolves in two branches:
|
|
237
|
+
|
|
238
|
+
- **Explicit `--project`/`-p` wins**, inside tmux and outside it. The active
|
|
239
|
+
tmux target is not consulted at all.
|
|
240
|
+
- **With no explicit scope occurrence, the Project comes from the active managed runtime**:
|
|
241
|
+
the `@projmux_window_uid` mirrored on the pane you are in, and that Window's
|
|
242
|
+
registry `ownerRef`. This is the same seam the empty-selector reads use.
|
|
243
|
+
|
|
244
|
+
The Window and the anchor Pane follow the *whole* scope rather than the Project
|
|
245
|
+
flag alone:
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
projmux create codex # active Project, active Window, split from the active Pane
|
|
249
|
+
projmux create codex -p alpha -w hi --create-window # exact Project, new Window "hi"
|
|
250
|
+
projmux create codex -p beta -w main # everything explicit
|
|
251
|
+
projmux create pane -p alpha # every Window of alpha; a deliberate fan-out
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
One explicit scope occurrence (`--project`, `--window`, `--pane`, or
|
|
255
|
+
`--selector`) makes the whole scope explicit, so naming a Window never picks up
|
|
256
|
+
an anchor from somewhere you did not address. With a scope but no `--pane`, the
|
|
257
|
+
split anchor is the target Window's role-agnostic `spec.anchorPaneRef`. A
|
|
258
|
+
shell-required offline operation may plan a lazy direct
|
|
259
|
+
`spec.defaultShellPaneRef` without replacing an Agent anchor. A missing or stale
|
|
260
|
+
anchor is exit `2` rather than a silent alternate-Pane repair.
|
|
261
|
+
|
|
262
|
+
An exact existing Window or Pane can reveal its owner Project through Registry
|
|
263
|
+
`ownerRef`. A Window named with `--create-window` does not exist yet and cannot;
|
|
264
|
+
without `--project` that spelling refuses and names `--project <ref>` as the
|
|
265
|
+
remedy, even when the invocation happens inside a managed Pane.
|
|
266
|
+
|
|
267
|
+
Refusals are exit `2` with zero Registry writes and zero tmux mutations, and
|
|
268
|
+
they name `--project` as the fix:
|
|
269
|
+
|
|
270
|
+
- outside tmux with no `--project` — no default server is probed;
|
|
271
|
+
- inside Home, a control session, an unattributed pane, or a foreign pane —
|
|
272
|
+
none of those carry a managed identity, and projmux never invents a Project
|
|
273
|
+
from `$HOME`, a session name, or a cwd;
|
|
274
|
+
- a mirrored uid the Registry does not hold, or a Window whose owning Project is
|
|
275
|
+
gone — a `recoverable` runtime is reported, never adopted.
|
|
276
|
+
|
|
277
|
+
Every create is **detached**: no create moves the client. Use `focus pane` or
|
|
278
|
+
`-o pane-id` when you want to end up in the new pane. A natural create validates
|
|
279
|
+
the inherited exact route and Pane containment. An explicit resource scope
|
|
280
|
+
binds the selected app resource route without letting unrelated inherited
|
|
281
|
+
`TMUX`/`TMUX_PANE` choose or change the resource target; exact Project plus
|
|
282
|
+
`--create-window` therefore uses the validated app logical `-L` route on its
|
|
283
|
+
first attempt, with no `env -u` workaround. Runtime safety remains independent
|
|
284
|
+
and may refuse before mutation. An inherited app-owned `TMUX` socket/PID stays
|
|
285
|
+
route evidence: projmux validates its exact `-S` path, ownership/logical
|
|
286
|
+
markers, logical `-L` alias, and PID while ignoring unrelated `TMUX_PANE`
|
|
287
|
+
containment. Outside tmux it validates the default app `-L` route. Marker,
|
|
288
|
+
physical-socket, server-PID, generation, and owner reobservation remain
|
|
289
|
+
mandatory. Commands that explicitly select an existing live `--socket-path`
|
|
290
|
+
keep that exact `-S` route unchanged.
|
|
291
|
+
|
|
292
|
+
#### Splits started from a popup
|
|
293
|
+
|
|
294
|
+
The split UI's own pickers (`M-7`, `M-4`/`C-r`, the pane context menu, and the
|
|
295
|
+
default split key when the saved mode is `selective` or `resume`) run inside a
|
|
296
|
+
`display-popup`. tmux exports `$TMUX` to a popup job and deliberately exports no
|
|
297
|
+
`$TMUX_PANE`, because a popup is not a pane — so the picker has no inherited
|
|
298
|
+
target of its own while still knowing, from the keypress that opened it, which
|
|
299
|
+
pane the operator was in. That pane travels on the create intent as an explicit
|
|
300
|
+
anchor and resolves Project, Window and split anchor through the same identity
|
|
301
|
+
mirror a pane-hosted invocation reads.
|
|
302
|
+
|
|
303
|
+
The anchor is something the split UI hands to `create`, never something `create`
|
|
304
|
+
reads from the environment. `$TMUX_SPLIT_TARGET_PANE` is not a scope override:
|
|
305
|
+
typing `projmux create pane --placement right` inside a popup is still an
|
|
306
|
+
invocation with no target and still refuses with the `--project` usage error
|
|
307
|
+
above, and no read, rename, or delete verb consults it.
|
|
308
|
+
|
|
309
|
+
#### Managed pane context menu
|
|
310
|
+
|
|
311
|
+
The generated `MouseDown3Pane` menu treats its Horizontal Split, Vertical
|
|
312
|
+
Split, and Kill entries as Projmux resource actions. Both splits pass the exact
|
|
313
|
+
clicked pane through the popup-origin anchor above and reach the same canonical
|
|
314
|
+
`create pane` materializer as the CLI, so the new pane receives a Registry uid.
|
|
315
|
+
Kill resolves that anchor's mirrored uid and reaches canonical `delete pane`,
|
|
316
|
+
including its printed delete result. The menu never falls back to a raw tmux
|
|
317
|
+
mutation when either route refuses. The reason is displayed on the exact client
|
|
318
|
+
that opened the menu instead of being lost as a `run-shell` exit code.
|
|
319
|
+
|
|
320
|
+
tmux Respawn has no equivalent in the current resource model: it preserves the
|
|
321
|
+
same pane handle, layout, Registry uid, and original command, while canonical
|
|
322
|
+
delete plus create removes that identity and creates another one (and may end
|
|
323
|
+
the Window or Project session when it deletes the last pane). The generated
|
|
324
|
+
menu therefore omits Respawn entirely and does not expose a refusal handler or
|
|
325
|
+
invent a replace operation. User-authored tmux bindings remain outside this
|
|
326
|
+
managed-menu contract.
|
|
327
|
+
|
|
139
328
|
### Rename and rebind live convergence
|
|
140
329
|
|
|
141
330
|
`rename project|window|pane` commits the selected Registry `metadata.name` and
|
|
@@ -166,6 +355,26 @@ duplicate UID claims remain fail-closed.
|
|
|
166
355
|
|
|
167
356
|
### Agent topic, interaction, activation, and workspace
|
|
168
357
|
|
|
358
|
+
`agent turn start`, `agent turn steer`, `agent turn interrupt`, and
|
|
359
|
+
`agent approval review` use the live Codex app-server connection only when the
|
|
360
|
+
selected Agent, its owned Pane, activation generation, thread, current turn,
|
|
361
|
+
and connection epoch still match exactly. `start` sends only the exact thread
|
|
362
|
+
id and one text input; `steer` supplies the current expected turn id; and
|
|
363
|
+
`interrupt` supplies that exact turn id. These commands never install sticky
|
|
364
|
+
model, effort, cwd, sandbox, permission, or collaboration overrides.
|
|
365
|
+
|
|
366
|
+
Approval review shows only the safe one-shot intersection supplied by the
|
|
367
|
+
exact pending request. Command, file, and network requests are limited to
|
|
368
|
+
`accept`, `decline`, and `cancel`; permission grants echo the received supported
|
|
369
|
+
profile with `scope=turn` and `strictAutoReview=null`. Session grants, policy or
|
|
370
|
+
network amendments, unstable root grants, legacy approval mutations, and
|
|
371
|
+
automatic approval are unavailable. The request envelope and raw JSON-RPC id
|
|
372
|
+
remain only in connection memory; reconnect, resolution, ambiguity, or any
|
|
373
|
+
identity mismatch produces no provider write. Approval queue rows advertise
|
|
374
|
+
`Review pending approval` only while that responder exists, otherwise they
|
|
375
|
+
advertise the exact-Agent `Open Codex` focus fallback; resolution removes the
|
|
376
|
+
row. Neither route stores prompt, command, path, permission, or request content.
|
|
377
|
+
|
|
169
378
|
`agent topic get|set|clear` and `agent status get|set` resolve exactly one
|
|
170
379
|
Agent, either from an explicit Agent reference or from the Agent-owned active
|
|
171
380
|
managed Pane. Topic is a non-identifying Registry annotation. Interaction is a
|
|
@@ -193,6 +402,40 @@ and shell Pane manual attention share the existing priority reducer, while
|
|
|
193
402
|
`dot`/`emoji`/`off`, glyphs, colors, and the Window aggregate are never stored
|
|
194
403
|
in resource metadata.
|
|
195
404
|
|
|
405
|
+
A clean managed process exit is topology authority only through the exact
|
|
406
|
+
generated `pane-exited` hook. The supervisor must have durably journaled a
|
|
407
|
+
same-generation `normal` receipt, the hook must name the exact `%N` Pane and
|
|
408
|
+
the owner Window must carry its exact last-positive `$N/@N` binding on the same
|
|
409
|
+
socket, and fresh preflight plus locked observations must still resolve the
|
|
410
|
+
same Registry owner chain. A non-last Pane is removed while its directly owning
|
|
411
|
+
Agent is retained Offline with its conversation identity. For a last Pane, that evidence is retained until a
|
|
412
|
+
matching `window-unlinked` hook removes the Window; a final Project Window also
|
|
413
|
+
removes its Window descendants while retaining the exact Project uid, root,
|
|
414
|
+
reservation, pins, snapshots, and external assets as a valid zero-Window
|
|
415
|
+
Project. Managed runtime Stop is different: it stops only the exact runtime and
|
|
416
|
+
keeps the complete desired Project/Window/Pane graph closed for a same-UID
|
|
417
|
+
Continue. Shell and Claude/Codex clean exit have the same result; `/exit`, pane
|
|
418
|
+
content, prompt, history, and transcript are never parsed. `abnormal`,
|
|
419
|
+
`killed`, `unknown`, stale/resumed bindings, empty or unavailable inventory,
|
|
420
|
+
permission failure, and foreign-host/window observations keep their diagnostic
|
|
421
|
+
rows and produce no automatic delete plan.
|
|
422
|
+
|
|
423
|
+
For `pane-exited`, tmux supplies `%N` as `#{hook_pane}`. Its current-context
|
|
424
|
+
session/window formats may already name a surviving client Window, so the owner
|
|
425
|
+
pair comes from the Window's last live Registry observation. The separate
|
|
426
|
+
`window-unlinked` hook supplies exact `#{hook_session}` and `#{hook_window}`;
|
|
427
|
+
only a matching causal pair authorizes last-Pane Window deletion.
|
|
428
|
+
|
|
429
|
+
An offline historical Window with no stored causal last-Pane receipt is not
|
|
430
|
+
absence-only migration authority and is never auto-deleted. Recover it with the
|
|
431
|
+
canonical exact route, `projmux delete window uid:<window-uid> --socket <name>
|
|
432
|
+
--yes` (or the corresponding `--socket-path`/inherited absolute `$TMUX` route).
|
|
433
|
+
Deleting a Project's last exact Window retains the Project with zero Windows;
|
|
434
|
+
within the runtime/startup lifecycle table, explicit `delete project --yes` is
|
|
435
|
+
the unregister operation. Runtime absence, managed Stop, ordinary Window close,
|
|
436
|
+
and Fresh never invoke the separately scoped filesystem-missing `prune project`
|
|
437
|
+
administrative policy.
|
|
438
|
+
|
|
196
439
|
Resource-backed Agent create accepts provider-neutral `--cwd <absolute>` and
|
|
197
440
|
repeatable `--add-dir <absolute>`. Explicit paths must exist, resolve without a
|
|
198
441
|
symlink escape, and remain inside a registered Project tree; only Codex and
|
|
@@ -210,8 +453,15 @@ owner Project root from `get`/`describe`, and a successful resume persists that
|
|
|
210
453
|
normalized effective workspace without changing Window Project ownership.
|
|
211
454
|
|
|
212
455
|
When `create agent -- <initial-prompt>` is used, normal resource creation and
|
|
213
|
-
provider activation are distinct. Projmux waits for
|
|
214
|
-
|
|
456
|
+
provider activation are distinct. Projmux first waits up to five seconds for an
|
|
457
|
+
exact provider `SessionStart`; that readiness evidence leaves activation
|
|
458
|
+
`pending` and opens an independent five-second initial-task acknowledgement
|
|
459
|
+
window. A `UserPromptSubmit` acknowledgement may also arrive directly before
|
|
460
|
+
the readiness observer sees `SessionStart`. The two stages are independently
|
|
461
|
+
bounded, so provider startup plus an acknowledgement later than two seconds may
|
|
462
|
+
take more than five but never more than ten seconds. Neither stage captures pane
|
|
463
|
+
content or stores the prompt. Acknowledgement returns success and the requested
|
|
464
|
+
exact `%N` output. If
|
|
215
465
|
activation cannot be confirmed, the command exits nonzero while naming the
|
|
216
466
|
exact Agent UID and Pane plus safe provider retry and `delete agent ... --yes`
|
|
217
467
|
cleanup options. The live resources remain explicit and retryable rather than
|
|
@@ -219,7 +469,13 @@ being reported as an ordinary success.
|
|
|
219
469
|
|
|
220
470
|
Activation metadata is bounded to provider-hook provenance and fixed
|
|
221
471
|
acknowledged/timed-out/failed diagnostics. Provider error strings and initial
|
|
222
|
-
prompt text are never stored.
|
|
472
|
+
prompt text are never stored. `pending` can become `unconfirmed` and a later
|
|
473
|
+
exact hook can refine either state to `acknowledged`; acknowledged never moves
|
|
474
|
+
backward. Late refinement must quote the same Agent, Pane uid, activation
|
|
475
|
+
generation, and recorded live `%N`, so a hook from a replaced materialization
|
|
476
|
+
cannot acknowledge its replacement. Codex and Claude initial payloads use the
|
|
477
|
+
same acknowledgement and result contract; provider-specific prompt content is
|
|
478
|
+
not evidence. Resource-backed Agent create and resume do not
|
|
223
479
|
start the legacy title/content watcher; that watcher remains only for legacy
|
|
224
480
|
non-resource panes and exits before reading title or capture content if resource
|
|
225
481
|
identity appears.
|
|
@@ -315,14 +571,23 @@ client only after it converges; a refusal, a failed preflight, or a rolled-back
|
|
|
315
571
|
partial leaves the client where it was and reports the exact stage. The
|
|
316
572
|
activation is pinned to the session the open targets, so a Project whose
|
|
317
573
|
Registry projects a different session name is refused instead of populating a
|
|
318
|
-
session the open never reaches.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
574
|
+
session the open never reaches. The closed-Project startup screen has exactly
|
|
575
|
+
two neutral actions. `Continue project` materializes current Registry desired
|
|
576
|
+
state with the same Project UID. A retained graph keeps descendant UIDs; a
|
|
577
|
+
zero-Window Project atomically receives a new canonical Window/shell UID chain.
|
|
578
|
+
A deleted Project may use only the exact usable snapshot compatibility path;
|
|
579
|
+
an unavailable Continue is an explicit zero-write refusal with no Fresh
|
|
580
|
+
fallback. `Open fresh` is one step with no danger styling, confirmation, or
|
|
581
|
+
delete counts: it atomically replaces the same-root graph with a new Project
|
|
582
|
+
UID and new canonical Window/shell UIDs, then hands off only after ordinary
|
|
583
|
+
materialization. A repeat replaces identity again. The root, git/worktrees,
|
|
584
|
+
trust decision, unrelated roots, and snapshot bytes remain unchanged.
|
|
585
|
+
|
|
586
|
+
Materialization launches or resumes declared Agents through the canonical
|
|
587
|
+
provider/trust path and creates their managed Agent-owned Panes. An individual
|
|
588
|
+
Agent refusal preserves the committed desired Registry and emits an item notice
|
|
589
|
+
for retry. It never directly executes `Pane.spec.command`; that field remains a
|
|
590
|
+
one-time name seed. A new Window binds only its own tmux-created primary Pane. On an existing Window,
|
|
326
591
|
every pre-existing uid-less Pane is refused rather than adopted; foreign,
|
|
327
592
|
duplicate, wrong-owner, or otherwise ambiguous UID state is refused before the
|
|
328
593
|
first create. Layout uses the existing deterministic right-axis equalizer and does
|
|
@@ -334,6 +599,193 @@ Registry-only: its complete Agent/Pane cascade is shown under `--dry-run`, no
|
|
|
334
599
|
tmux object is killed, and unrelated live objects and sockets are untouched. A
|
|
335
600
|
unique live mirror keeps the exact-kill path, while duplicate, foreign,
|
|
336
601
|
stale-owner, inventory-failure, and revalidation-race states are refused.
|
|
602
|
+
The selected Window's Registry `ownerRef` is authoritative in that preflight:
|
|
603
|
+
a Project uses its `status.session` plus the optional matching Project uid
|
|
604
|
+
mirror, while a ControlSession uses its exact `spec.session` and requires that
|
|
605
|
+
no Project uid mirror contaminate the control session. Window, Pane, and Agent
|
|
606
|
+
deletes preserve that `(root kind, root uid)` chain in the signed live plan and
|
|
607
|
+
report a final-window cascade with its actual root kind.
|
|
608
|
+
|
|
609
|
+
Pane and Agent Registry-only deletion is deliberately narrower. It accepts
|
|
610
|
+
only an explicit exact `uid:` selector: a Pane must carry durable
|
|
611
|
+
`MissingRuntime=True/RuntimeUnbound` evidence, and an Agent must be `Offline`
|
|
612
|
+
with no `paneRef` (with every retained descendant Pane also marked
|
|
613
|
+
`MissingRuntime`). The exact routed server must answer with a non-empty socket
|
|
614
|
+
identity and a non-empty Pane inventory that proves the target has zero mirrors.
|
|
615
|
+
A missing server, empty or failed inventory, unavailable or permission-denied
|
|
616
|
+
transport, implicit/name/scope/`--all` selection, and duplicate or foreign
|
|
617
|
+
mirrors are not absence authority and make zero writes. Dry-run and apply sign
|
|
618
|
+
the same socket, owner/root chain, lifecycle evidence, Pane activation
|
|
619
|
+
generation, and Agent binding; locked revalidation refuses zero-to-live,
|
|
620
|
+
live-to-zero, owner, generation, duplicate, or foreign changes. A successful
|
|
621
|
+
Registry-only result reports that no tmux Pane was killed, preserves the owning
|
|
622
|
+
Window/root/socket and all siblings, and repeating the exact apply returns the
|
|
623
|
+
ordinary no-match result.
|
|
624
|
+
|
|
625
|
+
`delete window|pane|agent` names the server its live half addresses the same
|
|
626
|
+
way `reconcile resources` does: `--socket <name>`, `--socket-path <absolute>`,
|
|
627
|
+
or the inherited absolute `$TMUX`. Outside tmux with neither flag it refuses
|
|
628
|
+
rather than reaching for the app's own socket, so a delete issued against an
|
|
629
|
+
isolated server can never inventory one host and kill objects on another.
|
|
630
|
+
|
|
631
|
+
Before it kills anything, a delete commits an intentional termination receipt
|
|
632
|
+
against every Pane whose process it is about to end, in its own Registry
|
|
633
|
+
transaction. If that write fails, nothing live is touched; if the delete then
|
|
634
|
+
refuses for any other reason, the receipt is withdrawn again. The receipt is
|
|
635
|
+
what tells a later reader that a process disappeared because someone asked for
|
|
636
|
+
it, rather than because it crashed.
|
|
637
|
+
|
|
638
|
+
## get runtime
|
|
639
|
+
|
|
640
|
+
```text
|
|
641
|
+
projmux get runtime sessions|windows|panes [--socket <name> | --socket-path <absolute>] [-o json|none]
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
`get runtime` is the read-only escape hatch onto one exact tmux server. The
|
|
645
|
+
resource reads (`get projects|windows|panes|agents`) enumerate the Registry;
|
|
646
|
+
this one enumerates the machine, including everything projmux does not own, and
|
|
647
|
+
it accepts no selector because most of what it reports has no name to resolve.
|
|
648
|
+
Its kinds are tmux object kinds, not resource kinds, and they have no singular
|
|
649
|
+
spelling for the same reason.
|
|
650
|
+
|
|
651
|
+
Every row carries the attribution the resolved resource graph decided from exact
|
|
652
|
+
evidence -- `managed`, `recoverable`, `control`, `ephemeral`, `unattributed`,
|
|
653
|
+
`foreign`, `conflict` -- the reason for it, the stable tmux id, the fully
|
|
654
|
+
qualified coordinate (`<session>`, `<session>:@N`, `<session>:@N.%N`), and, for a
|
|
655
|
+
managed object, the Registry resource it is bound to. A refused object is named
|
|
656
|
+
and explained and is never handed a resource identity.
|
|
657
|
+
|
|
658
|
+
Socket selection is the same fail-closed rule `reconcile resources` uses, with
|
|
659
|
+
one difference at the end:
|
|
660
|
+
|
|
661
|
+
- `--socket <name>` means exactly `tmux -L <name>`.
|
|
662
|
+
- `--socket-path <absolute>` means exactly `tmux -S <absolute>`.
|
|
663
|
+
- The two flags are mutually exclusive.
|
|
664
|
+
- With neither flag, an invocation inside tmux inherits only the absolute socket
|
|
665
|
+
path from `$TMUX` and uses `-S`.
|
|
666
|
+
- With neither flag outside tmux the read still succeeds. It returns the
|
|
667
|
+
unavailable projection: no items, every scope reported unobservable with a
|
|
668
|
+
reason, and zero tmux calls. There is no default-socket guess, and a sibling
|
|
669
|
+
socket is never read.
|
|
670
|
+
|
|
671
|
+
The default projection is a table preceded by a header line naming the host mode
|
|
672
|
+
and the exact transport, plus one line per scope that could not be observed. The
|
|
673
|
+
header is always printed, even when the table is empty: "no sessions" is only
|
|
674
|
+
trustworthy next to which server was asked and whether the answer could be taken
|
|
675
|
+
at all. `-o json` emits the same data as a stable `Runtime{Session,Window,Pane}List`
|
|
676
|
+
envelope; `-o none` prints nothing. The Registry projections (`uid`, `name`,
|
|
677
|
+
`ref`, `metadata`) are deliberately not offered, because most of what this route
|
|
678
|
+
returns has none of them.
|
|
679
|
+
|
|
680
|
+
The read writes nothing: the Registry is opened without being created, the
|
|
681
|
+
observation issues one option probe and three list queries whatever the size of
|
|
682
|
+
the server, and no write verb is ever sent.
|
|
683
|
+
|
|
684
|
+
## runtime diagnostics
|
|
685
|
+
|
|
686
|
+
```text
|
|
687
|
+
projmux runtime diagnostics [--socket <name> | --socket-path <absolute>] [--ui=popup|sidebar]
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
The interactive half of the same read. It lists every tmux object on the exact
|
|
691
|
+
server in containment order with an attribution tally, and it is deliberately
|
|
692
|
+
separate from `projmux runtime sessions`: that picker lists recent sessions so
|
|
693
|
+
you can open one, this one lists everything so you can understand it.
|
|
694
|
+
|
|
695
|
+
Selecting a row opens its action menu, which offers only routes that already
|
|
696
|
+
exist:
|
|
697
|
+
|
|
698
|
+
- **Focus** hands `projmux focus` the row's exact coordinate and the server's own
|
|
699
|
+
`#{socket_path}`. It moves a client and never materializes anything.
|
|
700
|
+
- **Attach** forwards to `projmux attach project uid:<uid>`, and is offered only
|
|
701
|
+
for a session bound to a Registry Project while you are outside tmux.
|
|
702
|
+
- **Open Resource Inspector** opens `projmux resources` unchanged.
|
|
703
|
+
|
|
704
|
+
An action that does not apply is listed with the reason instead of being hidden:
|
|
705
|
+
"no Registry Project claims this session; diagnostics never adopts one" is the
|
|
706
|
+
diagnostic. There is no adopt, import, rename, or kill action, and opening the
|
|
707
|
+
surface writes nothing.
|
|
708
|
+
|
|
709
|
+
## reconcile registry
|
|
710
|
+
|
|
711
|
+
```text
|
|
712
|
+
projmux reconcile registry [--dry-run] [--source <name|absolute-path>] [--expect-source-checksum <sha256:hex>] [--expect-current-checksum <sha256:hex>] [--socket <name> | --socket-path <absolute>] [-o json]
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
`reconcile registry` is the recovery boundary for the Registry itself. It is a
|
|
716
|
+
sibling of `reconcile resources`, not a stronger version of it: `reconcile
|
|
717
|
+
resources` converges a Registry that loads, and this route runs when the
|
|
718
|
+
Registry is the thing that is wrong.
|
|
719
|
+
|
|
720
|
+
Planning writes nothing. With no `--source` — and with `--dry-run` at any time —
|
|
721
|
+
the command reads the current `registry.json`, the `registry.initialized`
|
|
722
|
+
marker, and the bounded copies under `recovery/`, then reports:
|
|
723
|
+
|
|
724
|
+
- the current state as `valid`, `first-use`, `missing`, `empty`, `malformed`,
|
|
725
|
+
`schema-too-new`, `invalid`, or `unreadable`, with a `sha256:` digest of the
|
|
726
|
+
exact bytes;
|
|
727
|
+
- every candidate newest first, each marked `eligible` or `rejected` with the
|
|
728
|
+
reason, its digest, size, mtime, schema version, and the
|
|
729
|
+
projects/windows/panes/agents/reservations it holds;
|
|
730
|
+
- the exact guarded command that would restore the candidate it suggests.
|
|
731
|
+
|
|
732
|
+
No lock is taken, no permission is repaired, and `<state>/projmux/metadata/` is
|
|
733
|
+
not created — a preview is safe against a first-use state directory and against
|
|
734
|
+
one nobody should be writing to yet.
|
|
735
|
+
|
|
736
|
+
Restoring requires `--source`. There is no "restore the newest" mode: which copy
|
|
737
|
+
is the truth is a judgment about which mutations were wanted. A source is an
|
|
738
|
+
exact copy name, a unique fragment of one, or an absolute path to a copy carried
|
|
739
|
+
from elsewhere; a fragment matching several copies is refused rather than ranked.
|
|
740
|
+
An explicit path gets exactly the same verification as a bounded copy.
|
|
741
|
+
|
|
742
|
+
Verification is fail closed. Malformed JSON, an empty file, an envelope newer
|
|
743
|
+
than this build, and a graph with a duplicate uid, a dangling `ownerRef`, or a
|
|
744
|
+
broken name reservation are all refused with the current Registry byte-identical.
|
|
745
|
+
The verified bytes are then published **verbatim**, so uids, owner relations, and
|
|
746
|
+
name reservations are preserved exactly rather than re-encoded, and a
|
|
747
|
+
known-older-but-valid envelope stays readable through the normal safe read and
|
|
748
|
+
migrates on the next semantic write.
|
|
749
|
+
|
|
750
|
+
The bytes being replaced are kept first, at
|
|
751
|
+
`recovery/replaced-<stamp>-<seq>.json`. Unlike the write-side copies this keeps
|
|
752
|
+
content that does not verify: a damaged Registry is the only remaining evidence
|
|
753
|
+
if the restore turns out to be the wrong call, and the preserved copy is offered
|
|
754
|
+
back as a candidate. Replaced copies are their own bounded family, so a restore
|
|
755
|
+
never consumes the automatic write history.
|
|
756
|
+
|
|
757
|
+
Race guards are the operator's tie to the plan they read:
|
|
758
|
+
|
|
759
|
+
- `--expect-source-checksum <sha256:hex>` refuses unless the source still hashes
|
|
760
|
+
to that digest.
|
|
761
|
+
- `--expect-current-checksum <sha256:hex>` refuses unless the current Registry
|
|
762
|
+
still does.
|
|
763
|
+
- Both are what the printed `next:` command already carries, so copy-pasting the
|
|
764
|
+
preview's suggestion is guarded by construction.
|
|
765
|
+
|
|
766
|
+
Underneath, the source is re-read and re-verified under the store lock, the
|
|
767
|
+
staged copy is re-validated, and both inputs are re-hashed immediately before the
|
|
768
|
+
single atomic rename. Anything that moved refuses with nothing published, no
|
|
769
|
+
preserved copy, and no staged file left behind, and says to re-run the preview. A
|
|
770
|
+
repeat restore is a byte no-op: no rename, no preserved copy, no marker write.
|
|
771
|
+
Restoring into a state directory with no marker publishes one, so a later loss on
|
|
772
|
+
that machine reads as state loss rather than as a fresh first use.
|
|
773
|
+
|
|
774
|
+
When recovery is needed and no verified copy exists, the report adds a mirror
|
|
775
|
+
diagnostic — and it is **only** a diagnostic. It reports the Projmux identity the
|
|
776
|
+
one exact tmux server still carries (Project/Window/Pane uids, mirrored names,
|
|
777
|
+
the Project root, and containment resolved from stable tmux ids) beside a fixed
|
|
778
|
+
statement of what no mirror can return: offline resources, every Agent (no tmux
|
|
779
|
+
option carries an Agent uid), an Agent-owned Pane's `ownerRef`, the name
|
|
780
|
+
reservation table, `spec.anchorPaneRef`, `spec.defaultShellPaneRef`, and labels/annotations/timestamps/
|
|
781
|
+
status. Panes carrying a provider option are counted as proof that Agents existed
|
|
782
|
+
whose uids are nowhere on the server. Nothing is imported and no Registry is
|
|
783
|
+
generated from fragments. Socket selection follows the same `--socket` /
|
|
784
|
+
`--socket-path` / inherited-`$TMUX` rule as `reconcile resources`, except that
|
|
785
|
+
having no exact target is reported as a reason rather than being a usage error:
|
|
786
|
+
a restore is a filesystem operation, so recovery must work on a machine with no
|
|
787
|
+
tmux server. The diagnostic is skipped entirely when a verified copy exists or
|
|
788
|
+
the Registry is healthy.
|
|
337
789
|
|
|
338
790
|
## Internal plumbing (`projmux internal ...`)
|
|
339
791
|
|
|
@@ -854,6 +1306,13 @@ projmux attention window [window]
|
|
|
854
1306
|
Toggles the `✳` pane title prefix and the `@projmux_attention_state` pane
|
|
855
1307
|
option. `toggle` flips between cleared and `reply`; `clear` always
|
|
856
1308
|
clears; `arm` sets a pre-reply armed state used by the AI flow. The
|
|
1309
|
+
optional pane is the exact pane invoking the command: when it is omitted,
|
|
1310
|
+
Projmux requires inherited `$TMUX` plus an exact `$TMUX_PANE=%N` and verifies
|
|
1311
|
+
that same pane with a targeted tmux read before changing attention state. From
|
|
1312
|
+
outside tmux, or when that evidence is missing, malformed, or stale, pass an
|
|
1313
|
+
explicit pane target instead; the command fails without writing attention
|
|
1314
|
+
state. Explicit targets used by generated focus hooks keep their existing
|
|
1315
|
+
meaning.
|
|
857
1316
|
producer side pushes the matching entry into the notify queue when the pane
|
|
858
1317
|
has an associated AI agent option; clearing attention does not ack the queue
|
|
859
1318
|
row (manual toggles on shell panes do not push). `list` reads `tmux list-panes -a` and shows live pane
|
|
@@ -865,8 +1324,8 @@ supplied window.
|
|
|
865
1324
|
## Agent creation and hook ingress
|
|
866
1325
|
|
|
867
1326
|
```
|
|
868
|
-
projmux create agent --provider <claude|codex|antigravity> [--placement right|down] ...
|
|
869
|
-
projmux create pane [--placement right|down] ...
|
|
1327
|
+
projmux create agent --provider <claude|codex|antigravity> [--project <ref>] [--window <ref>]... [--create-window] [--placement right|down] ...
|
|
1328
|
+
projmux create pane [--project <ref>] [--window <ref>]... [--create-window] [--placement right|down] ...
|
|
870
1329
|
projmux config edit [--get|--set <mode>]
|
|
871
1330
|
projmux agent status set <thinking|waiting|idle> [pane]
|
|
872
1331
|
projmux agent topic ...
|
|
@@ -897,34 +1356,60 @@ yellow respectively. That palette is independent from notify queue
|
|
|
897
1356
|
can still render a non-red action-required status badge.
|
|
898
1357
|
|
|
899
1358
|
`create agent --provider ...` selects a provider without changing the saved
|
|
900
|
-
default. Concrete provider invocations create a new managed
|
|
901
|
-
|
|
902
|
-
not selected or reused
|
|
903
|
-
|
|
904
|
-
|
|
1359
|
+
default. Concrete provider invocations create a new Agent and a new managed
|
|
1360
|
+
Pane every time; existing managed AI panes in the same project/session are
|
|
1361
|
+
not selected or reused, and rebinding an existing conversation is `agent
|
|
1362
|
+
resume`, a different verb. The scope of the new resources follows
|
|
1363
|
+
[Create scope](#create-scope). The provider picker remains available through
|
|
1364
|
+
`internal agent-pane picker`. Arguments after `--` are extra arguments appended to
|
|
905
1365
|
the resolved `claude`, `codex`, or `agy` executable inside the managed wrapper;
|
|
906
|
-
projmux still sets the context directory, tmux title, AI pane metadata,
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
Automation callers
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1366
|
+
projmux still sets the context directory, tmux title, AI pane metadata, and
|
|
1367
|
+
split layout.
|
|
1368
|
+
|
|
1369
|
+
Automation callers get the new pane's handle from `-o pane-id` on the canonical
|
|
1370
|
+
create routes: `projmux create agent --provider <p> --placement right -o pane-id`
|
|
1371
|
+
and `projmux create pane --placement right -o pane-id` each print exactly the
|
|
1372
|
+
managed Pane's `%N` followed by one newline. See
|
|
1373
|
+
[AI Agent Shortcuts](ai-agent-shortcuts.md) for the shortcut spellings.
|
|
1374
|
+
|
|
1375
|
+
Every Projmux split surface produces the same canonical create intent. The
|
|
1376
|
+
default `ai-split-right/down` binding reads the saved split mode and turns it
|
|
1377
|
+
into one intent -- a provider Agent, a shell Pane, or one of the two pickers --
|
|
1378
|
+
and the `Alt-7` picker and the resume picker do the same with what the operator
|
|
1379
|
+
selected. Only the create route's materializer runs tmux's `split-window`, so a
|
|
1380
|
+
pane opened from the UI is a Registry resource on the same terms as one asked for
|
|
1381
|
+
by name, and a failed launch leaves zero Registry and zero tmux mutations. A raw
|
|
1382
|
+
unmanaged split exists only where you make one yourself.
|
|
1383
|
+
|
|
923
1384
|
The resume picker lists the newest deduplicated Claude, Codex, and Antigravity
|
|
924
1385
|
resume sessions for the current project, with `[+ New Session]` pinned first.
|
|
925
1386
|
If there are no resume sessions it goes straight to the existing selective
|
|
926
|
-
picker. Selecting a row
|
|
927
|
-
`
|
|
1387
|
+
picker. Selecting a row creates a managed Agent whose pane joins that
|
|
1388
|
+
conversation -- `claude --resume <id>`, `codex resume <id>`, or
|
|
1389
|
+
`agy --conversation <uuid>`. Rebinding an Agent the Registry already has is
|
|
1390
|
+
`projmux agent resume`, a different verb that never falls back to a fresh
|
|
1391
|
+
conversation.
|
|
1392
|
+
|
|
1393
|
+
Codex rows come from one source per picker invocation. A healthy app-server is
|
|
1394
|
+
primary: `thread/list` is paged with opaque cursors, non-archived and explicit
|
|
1395
|
+
`cli`/`vscode`/`appServer` source filters, provider recency ordering, and exact
|
|
1396
|
+
cwd/depth filtering. Rows preserve the exact thread id plus provider name,
|
|
1397
|
+
branch, and runtime status; an unnamed thread uses only its short id, never its
|
|
1398
|
+
prompt preview. The row suffix displays native or rollout source, confidence,
|
|
1399
|
+
status, and any closed fallback reason. Unsupported, unavailable, protocol, or
|
|
1400
|
+
malformed-pagination results discard the native partial result and run the
|
|
1401
|
+
existing rollout scan once. Selecting a native row resumes that same thread id
|
|
1402
|
+
through the native lane; selecting a fallback row keeps the current CLI lane.
|
|
1403
|
+
Claude and Antigravity discovery and launch semantics are unchanged.
|
|
1404
|
+
|
|
1405
|
+
`projmux agent review [<agent-ref>]` starts a native Codex review for
|
|
1406
|
+
uncommitted changes by default. Use exactly one of `--base <branch>`, `--commit
|
|
1407
|
+
<sha>`, or `--instructions <text>` to choose another review target. The action
|
|
1408
|
+
is available only when the selected Running Codex Agent still has an exact live
|
|
1409
|
+
Pane/thread binding and the current app-server supports `review/start`; every
|
|
1410
|
+
other case reports review as unavailable without changing the Agent. This route
|
|
1411
|
+
projects only the initial response into interaction status. It does not claim
|
|
1412
|
+
the later notification-driven completion lifecycle.
|
|
928
1413
|
|
|
929
1414
|
Live Antigravity hook/session-state resume metadata remains a separate,
|
|
930
1415
|
high-confidence lane; it is not enumerated from disk by the picker. Within the
|
|
@@ -944,10 +1429,8 @@ Settings > AI Settings > Enabled agents controls Claude/Codex/Antigravity launch
|
|
|
944
1429
|
visibility. Disabled agents are hidden from the selective picker and from the
|
|
945
1430
|
default-mode picker. A saved default that later becomes disabled fails clearly
|
|
946
1431
|
instead of falling back to another agent. Direct
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
launch, pass `--force-agent`; picker and saved default paths do not use this
|
|
950
|
-
override. If all AI agents are disabled, the selective picker still offers the
|
|
1432
|
+
Canonical `create agent --provider <p>` launches and the provider shortcuts also
|
|
1433
|
+
fail when disabled. If all AI agents are disabled, the selective picker still offers the
|
|
951
1434
|
plain `shell` split and shows guidance to re-enable Claude/Codex/Antigravity.
|
|
952
1435
|
For user-level skill, slash-command, editor, or launcher registrations that
|
|
953
1436
|
call this contract, see [AI Agent Shortcuts](ai-agent-shortcuts.md).
|
|
@@ -1283,8 +1766,8 @@ keymap action is no longer accepted: replace a stale
|
|
|
1283
1766
|
The `projmux agent topic set/clear` commands keep
|
|
1284
1767
|
AI topic ownership separate from the user pane label and raw pane title.
|
|
1285
1768
|
`apply` regenerates the app tmux config and reloads the live `-L projmux`
|
|
1286
|
-
server without restarting it. `make install` and `projmux update apply` invoke
|
|
1287
|
-
|
|
1769
|
+
server without restarting it. `make install` and `projmux update apply` invoke
|
|
1770
|
+
it before binary publication and again afterward for verification. Settings > Keybindings normally runs the same
|
|
1288
1771
|
save/config/reload flow automatically; use `projmux config apply` (or its
|
|
1289
1772
|
hidden equivalent `projmux internal tmux apply`) as the CLI recovery or sync path after
|
|
1290
1773
|
hand-editing `keymap.toml`, after saving Settings outside tmux, or after
|
|
@@ -1373,15 +1856,21 @@ binary in `$GOBIN`/`$GOPATH/bin`/`~/go/bin` as `go`, and a local `go build`
|
|
|
1373
1856
|
still require an explicit `PROJMUX_INSTALLER=github-release`. Anything else is
|
|
1374
1857
|
reported as `unknown` with guidance.
|
|
1375
1858
|
`apply` is installer-aware and only runs after explicit user selection.
|
|
1376
|
-
For npm installs,
|
|
1859
|
+
For npm installs, the current binary first runs `config apply --bin` with the
|
|
1860
|
+
exact published target. Only after that succeeds does it run
|
|
1861
|
+
`npm install -g projmux@latest` (which reliably
|
|
1377
1862
|
crosses minor/major versions where `npm update -g` does not, and re-resolves
|
|
1378
1863
|
the per-platform optional dependency) and then runs the new binary's
|
|
1379
|
-
`projmux config apply
|
|
1864
|
+
`projmux config apply` as post-publication verification. A failed preparation
|
|
1865
|
+
does not invoke the installer; later failures are non-zero and print the exact
|
|
1866
|
+
`projmux config apply --socket projmux` recovery. With `--no-apply`, the
|
|
1867
|
+
pre-publication live convergence is omitted and the post-update step uses
|
|
1380
1868
|
`--no-reload`: it still migrates marker-owned files and writes generated
|
|
1381
|
-
configuration without accessing live tmux
|
|
1869
|
+
configuration without accessing live tmux, then explicitly reports that live
|
|
1870
|
+
apply remains required. For Go installs, it uses the same ordering around the existing atomic
|
|
1382
1871
|
replacement implementation. For `github-release` installs, it downloads the latest
|
|
1383
1872
|
matching `projmux_<version>_<goos>_<goarch>.tar.gz` release asset, extracts the
|
|
1384
|
-
binary, atomically replaces the current executable, then performs the same
|
|
1873
|
+
binary, pre-converges, atomically replaces the current executable, then performs the same
|
|
1385
1874
|
apply/`--no-reload` convergence. `source` installs report an
|
|
1386
1875
|
actionable error to update the checkout with `git pull --ff-only && make install`.
|
|
1387
1876
|
|
|
@@ -1403,7 +1892,8 @@ returns a usage error.
|
|
|
1403
1892
|
|
|
1404
1893
|
The live tmux inventory is under `runtime`: `runtime sessions`, `runtime
|
|
1405
1894
|
attach`, `runtime stop`, `runtime tag`, and `runtime prune`. Project pins use
|
|
1406
|
-
`pin project list|add|remove|toggle|clear
|
|
1895
|
+
`pin project list|add|remove|toggle|clear|migrate`; `list` takes `--kind
|
|
1896
|
+
project|candidate` and `migrate` takes `--dry-run`. Resource retention uses `prune
|
|
1407
1897
|
project|snapshot`, while explicit snapshot deletion uses `delete snapshot`.
|
|
1408
1898
|
|
|
1409
1899
|
Popup-marker, preview, status, and tmux configuration plumbing is hidden under
|
|
@@ -1417,20 +1907,33 @@ human configuration work should prefer `config render` and `config apply`.
|
|
|
1417
1907
|
generated config. The generated app config uses absolute `$SHELL` as the
|
|
1418
1908
|
tmux default shell when set, otherwise `/bin/sh`. `shell` starts or attaches
|
|
1419
1909
|
the app session directly after resolving the target app session name and
|
|
1420
|
-
startup directory. Alt-1 sidebar project open defaults to `
|
|
1421
|
-
which materializes the Project's Registry Windows
|
|
1422
|
-
before the client moves
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1910
|
+
startup directory. Alt-1 sidebar project open defaults to `Continue project`,
|
|
1911
|
+
which materializes the Project's Registry Windows, shell Panes, and Agents
|
|
1912
|
+
before the client moves. When the startup picker is enabled it contains exactly
|
|
1913
|
+
`Continue project` and `Open fresh`; Esc returns to Projects. `Continue
|
|
1914
|
+
project` restores a deleted Project only from its usable exact snapshot and
|
|
1915
|
+
otherwise refuses with zero Registry writes. `Open fresh` is a neutral,
|
|
1916
|
+
confirmation-free one-step action that atomically replaces the Project with
|
|
1917
|
+
a new Project/Window/shell UID chain and one same-root claimant. Repeating it
|
|
1918
|
+
allocates another new identity. Neither action modifies snapshot
|
|
1919
|
+
bytes, the project directory, git/worktrees, unrelated roots, or trust state.
|
|
1920
|
+
- `quit` — open an action picker with `Save Project snapshots and quit`, `Quit
|
|
1921
|
+
without saving`, and `Cancel`. The safe first action takes one complete,
|
|
1922
|
+
exact-socket Registry/resource-graph observation, freezes every live managed
|
|
1923
|
+
Project session in Project UID/session order, and captures each latest
|
|
1924
|
+
snapshot even when auto-save is off. Home/control, ephemeral, unattributed,
|
|
1925
|
+
recoverable, foreign, and offline sessions are excluded and reported as
|
|
1926
|
+
bounded class counts. Every target is attempted. A failed capture leaves the
|
|
1927
|
+
successful per-session atomic files in place, reports the exact failed
|
|
1928
|
+
session, and does not stop the app; retry captures every target again. Only an
|
|
1929
|
+
all-success ledger reaches the existing physical-socket, app-marker, and
|
|
1930
|
+
logical-route guarded shutdown. Named snapshots and Registry bytes are never
|
|
1931
|
+
written, and the batch is not a multi-file transaction or topology freeze.
|
|
1932
|
+
`Quit without saving` preserves the earlier guarded shutdown behavior:
|
|
1933
|
+
missing servers and runtimes without the app marker are no-ops. Existing
|
|
1934
|
+
non-interactive `--yes` and `--force` callers retain that same snapshot-free
|
|
1935
|
+
behavior and exact shutdown route; the default command always uses the
|
|
1936
|
+
action picker.
|
|
1434
1937
|
- `attach project <ref>` — enter a Project runtime from outside tmux.
|
|
1435
1938
|
Automatic live-runtime attachment is `runtime attach`.
|
|
1436
1939
|
- `settings` — interactive configuration UI for the project picker, AI
|