Commit 6534251
authored
feat: respawn_pane, get_*_info reads, mcp_swap dev script (#27)
Three follow-up tracks ship together: a destructive-but-recoverable pane
lifecycle tool, single-object info reads that complete the
get_*_info hierarchy, and a cross-CLI dev script that points Claude /
Codex / Cursor / Gemini at a local checkout in one command. Server
instructions, audit redaction, and the safety topic are extended in
lockstep.
**New tools**
- **`respawn_pane`** — restart a wedged pane in place, preserving
`pane_id` and the window layout where the kill-and-resplit
alternative would invalidate references and rearrange the layout.
Refuses to respawn the MCP server's own pane (mirroring the
existing `kill_*` self-guards), requires explicit `pane_id` (no
hierarchical fallback), and carries `destructiveHint=true` /
`idempotentHint=false` annotations while staying in the
`mutating` tier so default-profile agents can still reach it for
shell recovery.
- **`get_session_info`**, **`get_window_info`** — targeted
single-object metadata reads, completing the four-level
`get_*_info` hierarchy alongside `get_server_info` /
`get_pane_info`. Buffers, hooks, and options have existing read
paths and are intentionally excluded.
**New dev script: `scripts/mcp_swap.py`**
- One command (`just mcp-use-local`) rewrites each detected CLI's
global config to run the local repo via `uv`; `just mcp-revert`
restores from a timestamped backup. `just mcp-detect` and
`just mcp-status` report install state.
- Atomic writes (tempfile + `os.replace`) with re-validation,
layout-shape guard before mutating Claude's per-project config,
per-CLI state file at `$XDG_STATE_HOME/libtmux-mcp-dev/swap/`,
env preservation on replacement, `--dry-run` mode that prints a
unified diff. Scope is global configs only — workspace configs
remain the CLI's native responsibility (`cursor mcp add`,
`gemini mcp add`).
**Audit redaction**
`shell` (string) and `environment` (dict) on `respawn_pane` join the
existing redaction set. Dict-shaped values keep their *keys* visible
(env var names like `DATABASE_URL` are operator-debug-useful) but
digest each *value* via `{len, sha256_prefix}`. Documented limitation:
values may briefly appear in the OS process table while tmux spawns
the new process — the audit log redaction does not cover that
window.
**LLM discoverability and server instructions**
- `display_message` retitled to "Evaluate tmux Format String" with a
docstring leading with read-only format expansion. Eliminates the
naming clash with tmux's `display-message` verb that pushed agents
toward the wrong affordance.
- `pipe_pane` docstring leads with the concrete
`output_path="/tmp/pane.log"` logging use case.
- `_BASE_INSTRUCTIONS` decomposed into named segments with
module-level guidance on when to add a new gap-explainer vs. push
the explanation into a tool docstring. New segments: hooks are
read-only by design (`HOOKS ARE READ-ONLY`), buffer lifecycle plus
why `list_buffers` is intentionally absent, `is_caller` workflow
inside tmux, qualified `socket_name` contract that names
`list_servers` as the documented exception. A contract test
enforces that every registered tool except `list_servers` accepts
`socket_name` so prose and signatures cannot drift.
**Documentation**
- Topic pages added for `respawn-pane`, `get-session-info`, and
`get-window-info`.
- `safety.md` adds a `respawn-pane` Footgun section alongside
`pipe-pane` and `set-environment`. The macOS `TMUX_TMPDIR`
caveat is rewritten to reflect shipped behaviour — tmux's
three-step socket-path resolution is documented, so operators
no longer need to set `TMUX_TMPDIR` explicitly to chase a
problem that's already solved.
- README tool index updated for the three new tools.
**Pre-release decisions (no migration path needed for users)**
- `respawn_pane` parameter renamed `shell_command` → `shell` to
align with `split_window(shell=…)` and the upstream
`Pane.respawn(shell=…)` API.
- `respawn_pane` drops the `session_id` / `session_name` /
`window_id` resolver fallbacks. The runtime guard rejected any
call missing `pane_id` anyway; validation now lives at the
FastMCP schema boundary.
**Stopgap noted in source**
`respawn_pane` emits a literal `pane.cmd("respawn-pane", *argv)`
because libtmux 0.55.x has no `Pane.respawn()` yet. The argv shape
mirrors the upstream `tmux-parity` branch: `-k`, `-c <dir>`,
repeated `-eKEY=VALUE` (joined form), then optional trailing
shell. When the libtmux release line picks it up, swap to
`pane.respawn(...)` and drop the stderr branch.
**State path**
`scripts/mcp_swap.py` writes to
`$XDG_STATE_HOME/libtmux-mcp-dev/swap/state.json` (default
`~/.local/state/libtmux-mcp-dev/swap/state.json`). The `-dev`
namespace makes the dev-tooling status loud and avoids the
collision with the runtime package name. No migration shim —
pre-alpha, dev-only, single-user surface.38 files changed
Lines changed: 2411 additions & 68 deletions
File tree
- docs
- _ext
- _widgets/mcp-install
- tools
- pane
- session
- window
- topics
- scripts
- src/libtmux_mcp
- tools
- pane_tools
- tests
- docs
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
9 | 33 | | |
10 | 34 | | |
11 | 35 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
20 | | - | |
21 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
22 | 23 | | |
23 | | - | |
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
43 | | - | |
| 43 | + | |
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
9 | 11 | | |
10 | 12 | | |
11 | 13 | | |
| |||
33 | 35 | | |
34 | 36 | | |
35 | 37 | | |
36 | | - | |
37 | | - | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
81 | 81 | | |
82 | 82 | | |
83 | 83 | | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
84 | 88 | | |
85 | 89 | | |
86 | 90 | | |
| |||
110 | 114 | | |
111 | 115 | | |
112 | 116 | | |
| 117 | + | |
113 | 118 | | |
114 | 119 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
12 | 16 | | |
13 | 17 | | |
14 | 18 | | |
| |||
32 | 36 | | |
33 | 37 | | |
34 | 38 | | |
| 39 | + | |
35 | 40 | | |
36 | 41 | | |
37 | 42 | | |
| |||
0 commit comments