# Changelog

## libtmux-mcp 0.1.x (unreleased)

<!-- KEEP THIS PLACEHOLDER - DO NOT REMOVE OR MODIFY THIS LINE -->
_Notes on upcoming releases will be added here_
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->

## libtmux-mcp 0.1.0a5 (2026-05-06)

_Pane geometry on response models, the `find_pane_by_position`
corner-finder, and `mcp_swap --scope` targeting project or
user/system config scope._

### What's new

**New tools**

- {tooliconl}`find-pane-by-position` — resolve a layout-relative pane
  ("the bottom-right pane", any corner) to a `PaneInfo` in one
  read-only call. Composes the four `pane_at_*` predicates with a
  `pane_left + pane_top` tie-break for ambiguous layouts (e.g.
  single-pane windows that touch every edge). Replaces the
  {tooliconl}`display-message`-and-parse workaround. (#34)

**Pane geometry on response models**

- {class}`~libtmux_mcp.models.PaneInfo`,
  {class}`~libtmux_mcp.models.PaneContentMatch`, and
  {class}`~libtmux_mcp.models.PaneSnapshot` now carry window-relative
  `pane_left` / `pane_top` / `pane_right` / `pane_bottom` (typed
  `int | None`), the four `pane_at_left` / `pane_at_right` /
  `pane_at_top` / `pane_at_bottom` edge predicates (typed
  `bool | None`), and `pane_tty`. Agents reason about layout off the
  returned model instead of expanding `#{pane_at_*}` format strings
  by hand. {tooliconl}`snapshot-pane` fetches the new variables in
  the same `display-message` round-trip it already makes, so there
  is no extra tmux call. The older `pane_width` / `pane_height`
  fields stay `str | None` for now — switching them to `int | None`
  is a breaking schema change deferred as a separate follow-up.
  (#34)

### Development

- `scripts/mcp_swap.py` — `use-local` and `revert` accept
  `--scope {user,project}` so a Claude install can target either the
  top-level `mcpServers` fallback (every project without an override
  picks it up) or the per-project `projects.<abs>.mcpServers` entry.
  Default `project` preserves the previous behaviour. Codex / Cursor /
  Gemini have no per-project layer; the flag is silently coerced to
  `user` for them. A single install can hold both scopes
  simultaneously with independent backups, and `revert --scope user`
  restores just one layer. Full `revert` unwinds in LIFO order via an
  explicit `seq_no` counter on each state entry — no reliance on dict
  iteration through the JSON round-trip. Shape validation is
  centralised in `_claude_user_servers` / `_claude_project_node`
  helpers so read / write / delete fail symmetrically with
  `RuntimeError` on a malformed Claude config. The state-file schema
  is internal — no compatibility contract; running an older `mcp_swap`
  against a newer `state.json` is undefined behaviour and not
  supported. (#35)

### Documentation

- Bump gp-sphinx docs stack to v0.0.1a16 — docs site now renders
  via `gp-furo-theme`, a Tailwind v4 respin of Furo, with
  `sphinx-vite-builder` handling theme-asset builds (#33)
- Eliminate first-paint jank on the docs site: `{mcp-install}` no
  longer flashes when restoring a saved client/method from
  `localStorage` (CLS drops to 0), and the sidebar logo plus all
  bold and italic text no longer pop in late as fonts arrive.
  (#36)
- Discoverability sweep for {tooliconl}`find-pane-by-position`:
  dedicated topic page, homepage `Inspect (readonly)` chain entry,
  `### Inspect` grid card and "Targeting a pane by layout?"
  decision-tree cluster on the tools index, {ref}`concepts`
  Discovery enumeration, a "Spin up the dev server in the
  bottom-right pane" row in the prompting-patterns table, a new
  corner-targeting recipe in {ref}`recipes`, and refreshed example
  responses on {tooliconl}`get-pane-info` and
  {tooliconl}`snapshot-pane` showing the new geometry block.
  {tooliconl}`display-message` "Avoid when" guidance now redirects
  layout-relative use cases to the corner-finder. (#34)

## libtmux-mcp 0.1.0a4 (2026-05-02)

_`respawn_pane`, single-object info reads, and the cross-CLI `mcp_swap` dev script._

### What's new

**New tools**

- {tooliconl}`respawn-pane` — restart a wedged pane in place,
  preserving `pane_id` and the window layout (the alternative
  {tooliconl}`kill-pane` + {tooliconl}`split-window` invalidates pane
  references and rearranges the window). (#27)
- {tooliconl}`get-session-info`, {tooliconl}`get-window-info` —
  single-object metadata reads when you already have an ID. Completes
  the four-level `get_*_info` hierarchy (server / session / window /
  pane); buffers, hooks, and options have existing read paths and are
  deliberately excluded. (#27)

**Dev scripts**

- `scripts/mcp_swap.py`. Point Claude / Codex / Cursor / Gemini at a
  local checkout in one command. `just mcp-use-local` rewrites each
  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, `--dry-run` mode, per-CLI state file, env
  preservation on replacement, layout-shape guard before mutating
  Claude's per-project config. Scope is global configs only — see
  `scripts/README.md`. (#27)

### Documentation

- {ref}`safety` adds a {tooliconl}`respawn-pane` "Footgun" subsection
  alongside {tooliconl}`pipe-pane` and {tooliconl}`set-environment`:
  `kill=true` default, non-idempotent retries, explicit-`pane_id`
  requirement, OS-process-table visibility for `shell` / `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. (#27)
- LLM-facing discoverability tightened: {tooliconl}`display-message`
  retitled "Evaluate tmux Format String" with a docstring that leads
  with read-only format expansion (the tool wraps `display-message -p`
  but `-p` expands rather than displays), {tooliconl}`pipe-pane` leads
  with the `/tmp/pane.log` logging use case, and the server
  instructions explain why hooks are read-only and why there is no
  `list_buffers` tool. The `socket_name` contract is tightened to
  acknowledge {tooliconl}`list-servers` as the documented exception.
  (#27)
- Topic pages added for {tooliconl}`respawn-pane`,
  {tooliconl}`get-session-info`, and {tooliconl}`get-window-info`.
  (#27)

### API decisions (pre-release)

- {tooliconl}`respawn-pane` settles on `shell` (renamed from
  `shell_command`) to align with {tooliconl}`split-window` and
  upstream `Pane.respawn(shell=)`, and drops the `session_name` /
  `session_id` / `window_id` resolver fallbacks — the runtime guard
  rejected any call missing `pane_id` anyway. Validation now lives at
  the FastMCP schema boundary. (#27)

## libtmux-mcp 0.1.0a3 (2026-04-19)

_Post-0.1.0a2 smoke-test fixes and `libtmux` floor bump_

### Dependencies

- Minimum `libtmux>=0.55.1` (was `>=0.55.0`). 0.55.1 ships the
  pytest-plugin socket-reaper fix
  ([tmux-python/libtmux#661](https://github.com/tmux-python/libtmux/pull/661))
  so `libtmux_test*` daemons and socket files no longer leak under
  `/tmp/tmux-<uid>/` during test runs. Downstream effect: no local
  conftest reaper is needed in libtmux-mcp — the upstream fixtures
  self-clean.

### Fixes

- Pane `is_caller` annotation no longer false-positives across tmux
  sockets. `_serialize_pane`, `snapshot_pane`, and `search_panes` all
  compared `pane.pane_id == TMUX_PANE` without verifying the caller's
  socket, so a caller on socket A pane `%0` was marked `is_caller=True`
  for any `%0` on any other server. The annotation now reuses
  `_caller_is_on_server` (the same socket-scoped comparator used by
  the self-kill guard) via the new `_compute_is_caller` helper
  (#22, fixes #19).
- {tooliconl}`wait-for-channel` and {tooliconl}`signal-channel` no
  longer block the FastMCP event loop. Both were sync `def` handlers
  running `subprocess.run(timeout=N)` on the main loop — for the
  duration of the wait the server could not service other tool calls,
  MCP pings, or client cancellations. Ported to `async def` +
  {func}`asyncio.to_thread`, matching the pattern already used by
  {tooliconl}`wait-for-text` / {tooliconl}`wait-for-content-change`
  (#21, fixes #18).

## libtmux-mcp 0.1.0a2 (2026-04-19)

_FastMCP alignment: new tools, prompts, and middleware (#15)_

### Breaking changes

- {tooliconl}`search-panes` now returns
  {class}`~libtmux_mcp.models.SearchPanesResult` instead of
  `list[`{class}`~libtmux_mcp.models.PaneContentMatch``]`. Matches
  moved to `.matches`; new `truncated`, `truncated_panes`,
  `total_panes_matched`, `offset`, `limit` fields enable pagination.
  Migrate `for m in search_panes(...)` →
  `for m in search_panes(...).matches`.
- Minimum `fastmcp>=3.2.4` (was `>=3.1.0`). Required for
  {class}`~libtmux_mcp.middleware.ReadonlyRetryMiddleware` and
  per-parameter input-schema descriptions.

### What's new

**New tools**

- Discovery: {tooliconl}`list-servers`.
- Waits: {tooliconl}`wait-for-text`,
  {tooliconl}`wait-for-content-change`, {tooliconl}`wait-for-channel`,
  {tooliconl}`signal-channel`. All bounded, emit
  `ctx.report_progress` / `ctx.warning`, and propagate
  {exc}`asyncio.CancelledError` cleanly.
- Buffers: {tooliconl}`load-buffer`, {tooliconl}`paste-buffer`,
  {tooliconl}`show-buffer`, {tooliconl}`delete-buffer`.
  Agent-namespaced as `libtmux_mcp_<uuid>_<name>` to prevent
  collisions; leaked buffers GC'd on graceful shutdown.
- Hooks (read-only): {tooliconl}`show-hook`, {tooliconl}`show-hooks`.
- Panes / windows: {tooliconl}`snapshot-pane`, {tooliconl}`pipe-pane`,
  {tooliconl}`display-message`, {tooliconl}`paste-text`,
  {tooliconl}`select-pane`, {tooliconl}`swap-pane`,
  {tooliconl}`select-window`, {tooliconl}`move-window`,
  {tooliconl}`enter-copy-mode`, {tooliconl}`exit-copy-mode`.

**Prompt recipes**

- Four prompts: `run_and_wait`, `diagnose_failing_pane`,
  `build_dev_workspace`, `interrupt_gracefully`. Set
  `LIBTMUX_MCP_PROMPTS_AS_TOOLS=1` to expose them as tools for
  tools-only MCP clients.

**Middleware stack**

- `TimingMiddleware`, `ErrorHandlingMiddleware`,
  {class}`~libtmux_mcp.middleware.AuditMiddleware` (digest-redacted
  argument summaries),
  {class}`~libtmux_mcp.middleware.SafetyMiddleware` (tier-gated tool
  visibility),
  {class}`~libtmux_mcp.middleware.ReadonlyRetryMiddleware`
  (transparent retry of readonly tools on transient
  {exc}`libtmux.exc.LibTmuxException`; mutating tools never retry),
  {class}`~libtmux_mcp.middleware.TailPreservingResponseLimitingMiddleware`
  (trims oversized output from the head so the active prompt
  survives).

**Bounded outputs**

- {tooliconl}`capture-pane`, {tooliconl}`snapshot-pane`,
  {tooliconl}`show-buffer` accept `max_lines` (default 500) with
  tail-preserving truncation and typed `content_truncated` /
  `content_truncated_lines` signals. Pass `max_lines=None` to opt
  out.

**Other**

- Tool input schemas carry per-parameter `description` fields
  auto-extracted from NumPy-style docstrings.
- Lifespan startup probe fails fast with {exc}`RuntimeError` if
  `tmux` is missing on `PATH`.
- {attr}`~libtmux_mcp.models.SessionInfo.active_pane_id` surfaces the
  pane id returned by {tooliconl}`create-session`.

### Fixes

- {tooliconl}`search-panes` neutralizes tmux format-string injection
  in the regex fast path.
- macOS `TMUX_TMPDIR` self-kill guard: resolves the server socket via
  `tmux display-message #{socket_path}` before env-based
  reconstruction; basename-match fallback closes the launchd
  divergence gap.
- `build_dev_workspace` prompt uses the real tool parameter names
  (`session_name=`, `pane_id=`, `direction=`) and no longer waits for
  shell prompts after launching screen-grabbing programs (`vim`,
  `watch`, `tail -f`). New `log_command` parameter replaces the
  Linux-only `/var/log/syslog` default.
- `ReadonlyRetryMiddleware` retry warnings log under
  `libtmux_mcp.retry`.

### Documentation

- New per-tool pages: `buffers.md`, `hooks.md`, `waits.md`,
  `tools/index.md`. `panes.md` documents the `SearchPanesResult`
  migration. `safety.md` covers the macOS `TMUX_TMPDIR` caveat, the
  audit log, `pipe_pane`, and `set_environment`.

## libtmux-mcp 0.1.0a1 (2026-04-13)

### What's new

- New pane tools: `snapshot_pane`, `wait_for_content_change`, `select_pane`, `swap_pane`, `pipe_pane`, `display_message`, `enter_copy_mode`, `exit_copy_mode`, and `paste_text` (#11)
- New session tool: `select_window` — navigate by ID, index, or direction (#11)
- New window tool: `move_window` — reorder within a session or move across sessions (#11)
- New models: `PaneSnapshot` and `ContentChangeResult` (#11)

### Documentation

- Visual improvements to API docs from [gp-sphinx](https://gp-sphinx.git-pull.com)-based Sphinx packages (#10)
- Bump gp-sphinx docs stack to v0.0.1a8 (#14)

## libtmux-mcp 0.1.0a0 (2026-03-22)

### New features

- Initial release as standalone package
- MCP tools across 6 modules (server, session, window, pane, options, environment)
- `tmux://` URI resources for browsing tmux hierarchy
- Safety tier middleware (`readonly`, `mutating`, `destructive`)
- Socket isolation via `LIBTMUX_SOCKET` / `LIBTMUX_SOCKET_PATH` env vars
- Agent self-awareness via `TMUX_PANE` detection
- Server caching with `is_alive()` eviction
- Pydantic models for all tool outputs
- Full type safety (mypy strict)
